1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
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
|