from PyQt5 import QtCore, QtWidgets, QtGui from PyQt5.QtCore import Qt from model.task import Task from model.difficulty import Difficulty from model.priority import Priority from model import difficulty, priority import time import math import util.array import util.range import gui.tasks.duration import gui.color columns = 6 headers = ['Age', 'Name', 'Duration', 'Difficulty', 'Priority', '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): task = self._tasks[index.row()] if role == Qt.DisplayRole: if index.column() == 0: return age_since(task.created_at) elif index.column() == 1: return task.name elif index.column() == 2: return gui.tasks.duration.format(task.duration) elif index.column() == 3: return difficulty.format(task.difficulty) elif index.column() == 4: return priority.format(task.priority) elif index.column() == 5: return task.tag elif role == Qt.ForegroundRole: if index.column() == 2: return QtGui.QBrush(gui.tasks.duration.color(task.duration)) elif index.column() == 3: return QtGui.QBrush(difficulty_color(task.difficulty)) elif index.column() == 4: return QtGui.QBrush(priority_color(task.priority)) 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() is_rev = is_reversed(row, order) return util.array.insert_position( sort_key(task, row, is_rev), [sort_key(t, row, is_rev) for t in self._tasks], is_rev) 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() is_rev = is_reversed(row, order) self._tasks = sorted( self._tasks, key = lambda task: sort_key(task, row, is_rev), reverse = is_rev) 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 sort_key(task: Task, row: int, is_reversed: bool): if row == 0: return task.created_at elif row == 1: return task.name.lower() elif row == 2: if is_reversed: return task.duration else: return (task.duration == 0, task.duration) elif row == 3: return task.difficulty elif row == 4: return task.priority elif row == 5: if is_reversed: return task.tag.lower() else: return (task.tag == '', task.tag.lower()) def is_reversed(row: int, order: Qt.SortOrder) -> bool: if row == 0: return order == Qt.AscendingOrder else: return order == Qt.DescendingOrder def difficulty_color(d: Difficulty) -> QtGui.QColor: if d == Difficulty.EASY: return gui.color.easy_difficulty elif d == Difficulty.NORMAL: return gui.color.normal_difficulty elif d == Difficulty.HARD: return gui.color.hard_difficulty def priority_color(p: Priority) -> QtGui.QColor: if p == Priority.LOW: return gui.color.low_priority elif p == Priority.MIDDLE: return gui.color.middle_priority elif p == Priority.HIGH: return gui.color.high_priority