from PyQt5 import QtCore, QtWidgets from PyQt5.QtCore import Qt from model.task import Task import time import math import util.array import util.range columns = 4 headers = ['Age', 'Name', 'Duration', 'Tag'] default_sort = (0, Qt.AscendingOrder) class TableModel(QtCore.QAbstractTableModel): def __init__(self, tasks): super(TableModel, self).__init__() self._tasks = tasks def headerData(self, section, orientation, role): if role == Qt.DisplayRole and orientation == Qt.Horizontal: return headers[section] elif role == Qt.DisplayRole and orientation == Qt.Vertical: return section + 1 else: return QtCore.QVariant() def data(self, index, role): if role == Qt.DisplayRole: task = self._tasks[index.row()] if index.column() == 0: return age_since(task.created_at) elif index.column() == 1: return task.name elif index.column() == 2: return pp_duration(task.duration) elif index.column() == 3: return task.tag else: return QtCore.QVariant() def rowCount(self, index): return len(self._tasks) def columnCount(self, index): return columns def get_at(self, row): if row >= 0 and row < len(self._tasks): return self._tasks[row] def insert_task(self, header: QtWidgets.QHeaderView, task: Task) -> int: at = self.insert_position(header, task) self.beginInsertRows(QtCore.QModelIndex(), at, at) self._tasks.insert(at, task) self.endInsertRows() return at def insert_position(self, header: QtWidgets.QHeaderView, task: Task) -> int: row = header.sortIndicatorSection() order = header.sortIndicatorOrder() return util.array.insert_position( sort_key(task, row), [sort_key(t, row) for t in self._tasks], is_reversed(row, order)) def update_task(self, header: QtWidgets.QHeaderView, row, task: Task) -> int: self.delete_task_range(row, 1) return self.insert_task(header, task) def delete_tasks(self, indexes): for range in reversed(util.range.from_indexes(indexes)): self.delete_task_range(range.start, range.length) return True def delete_task_range(self, row, rows): self.beginRemoveRows(QtCore.QModelIndex(), row, row + rows - 1) self._tasks = self._tasks[:row] + self._tasks[row + rows:] self.endRemoveRows() return True def row_ids(self, rows): return [task.id for i, task in enumerate(self._tasks) if i in rows] def sort(self, row: int, order: Qt.SortOrder): self.layoutAboutToBeChanged.emit() self._tasks = sorted( self._tasks, key = lambda task: sort_key(task, row), reverse = is_reversed(row, order)) self.layoutChanged.emit() def age_since(timestamp): diff = int(time.time()) - timestamp if diff >= 60 * 60 * 24: return '' + str(math.floor(diff / 60 / 60 / 24)) + 'd' elif diff >= 60 * 60: return '' + str(math.floor(diff / 60 / 60)) + 'h' elif diff >= 60: return '' + str(math.floor(diff / 60)) + 'm' else: return '1m' def pp_duration(minutes: int): if minutes >= 60 * 24: return '' + str(math.floor(minutes / 60 / 24)) + 'd' elif minutes >= 60: return '' + str(math.floor(minutes / 60)) + 'h' else: return '' + str(minutes) + 'm' def sort_key(task: Task, row: int): if row == 0: return task.created_at elif row == 1: return task.name.lower() elif row == 2: return task.duration elif row == 3: return task.tag.lower() def is_reversed(row: int, order: Qt.SortOrder) -> bool: if row == 0: return order == Qt.AscendingOrder else: return order == Qt.DescendingOrder