|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2005 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a class to store task data. |
|
8 """ |
|
9 |
|
10 import contextlib |
|
11 import enum |
|
12 import os |
|
13 import time |
|
14 |
|
15 from PyQt6.QtCore import Qt, QUuid |
|
16 from PyQt6.QtWidgets import QTreeWidgetItem |
|
17 |
|
18 import UI.PixmapCache |
|
19 import Preferences |
|
20 |
|
21 |
|
22 class TaskType(enum.IntEnum): |
|
23 """ |
|
24 Class defining the task types. |
|
25 """ |
|
26 NONE = 255 |
|
27 FIXME = 0 |
|
28 TODO = 1 |
|
29 WARNING = 2 |
|
30 NOTE = 3 |
|
31 TEST = 4 |
|
32 DOCU = 5 |
|
33 |
|
34 |
|
35 class TaskPriority(enum.IntEnum): |
|
36 """ |
|
37 Class defining the task priorities. |
|
38 """ |
|
39 HIGH = 0 |
|
40 NORMAL = 1 |
|
41 LOW = 2 |
|
42 |
|
43 |
|
44 class Task(QTreeWidgetItem): |
|
45 """ |
|
46 Class implementing the task data structure. |
|
47 """ |
|
48 TaskType2IconName = { |
|
49 TaskType.FIXME: "taskFixme", # __NO-TASK__ |
|
50 TaskType.TODO: "taskTodo", # __NO-TASK__ |
|
51 TaskType.WARNING: "taskWarning", # __NO-TASK__ |
|
52 TaskType.NOTE: "taskNote", # __NO-TASK__ |
|
53 TaskType.TEST: "taskTest", # __NO-TASK__ |
|
54 TaskType.DOCU: "taskDocu", # __NO-TASK__ |
|
55 } |
|
56 TaskType2ColorName = { |
|
57 TaskType.FIXME: "TasksFixmeColor", # __NO-TASK__ |
|
58 TaskType.TODO: "TasksTodoColor", # __NO-TASK__ |
|
59 TaskType.WARNING: "TasksWarningColor", # __NO-TASK__ |
|
60 TaskType.NOTE: "TasksNoteColor", # __NO-TASK__ |
|
61 TaskType.TEST: "TasksTestColor", # __NO-TASK__ |
|
62 TaskType.DOCU: "TasksDocuColor", # __NO-TASK__ |
|
63 } |
|
64 TaskType2MarkersName = { |
|
65 TaskType.FIXME: "TasksFixmeMarkers", # __NO-TASK__ |
|
66 TaskType.TODO: "TasksTodoMarkers", # __NO-TASK__ |
|
67 TaskType.WARNING: "TasksWarningMarkers", # __NO-TASK__ |
|
68 TaskType.NOTE: "TasksNoteMarkers", # __NO-TASK__ |
|
69 TaskType.TEST: "TasksTestMarkers", # __NO-TASK__ |
|
70 TaskType.DOCU: "TasksDocuMarkers", # __NO-TASK__ |
|
71 } |
|
72 |
|
73 def __init__(self, summary, priority=TaskPriority.NORMAL, filename="", |
|
74 lineno=0, completed=False, _time=0, isProjectTask=False, |
|
75 taskType=TaskType.TODO, project=None, description="", |
|
76 uid="", parentUid=""): |
|
77 """ |
|
78 Constructor |
|
79 |
|
80 @param summary summary text of the task |
|
81 @type str |
|
82 @param priority priority of the task |
|
83 @type TaskPriority |
|
84 @param filename filename containing the task |
|
85 @type str |
|
86 @param lineno line number containing the task |
|
87 @type int |
|
88 @param completed flag indicating completion status |
|
89 @type bool |
|
90 @param _time creation time of the task (if 0 use current time) |
|
91 @type float |
|
92 @param isProjectTask flag indicating a task related to the current |
|
93 project |
|
94 @type bool |
|
95 @param taskType type of the task |
|
96 @type TaskType |
|
97 @param project reference to the project object |
|
98 @type Project |
|
99 @param description explanatory text of the task |
|
100 @type str |
|
101 @param uid unique id of the task |
|
102 @type str |
|
103 @param parentUid unique id of the parent task |
|
104 @type str |
|
105 """ |
|
106 super().__init__() |
|
107 |
|
108 self.summary = summary |
|
109 self.description = description |
|
110 self.filename = filename |
|
111 self.lineno = lineno |
|
112 self.completed = completed |
|
113 self.created = _time and _time or time.time() |
|
114 self._isProjectTask = isProjectTask |
|
115 self.project = project |
|
116 if uid: |
|
117 self.uid = uid |
|
118 else: |
|
119 self.uid = QUuid.createUuid().toString() |
|
120 self.parentUid = parentUid |
|
121 |
|
122 if isProjectTask: |
|
123 self.filename = self.project.getRelativePath(self.filename) |
|
124 |
|
125 self.setData(0, Qt.ItemDataRole.DisplayRole, "") |
|
126 self.setData(1, Qt.ItemDataRole.DisplayRole, "") |
|
127 self.setData(2, Qt.ItemDataRole.DisplayRole, self.summary) |
|
128 self.setData(3, Qt.ItemDataRole.DisplayRole, self.filename) |
|
129 self.setData(4, Qt.ItemDataRole.DisplayRole, self.lineno or "") |
|
130 |
|
131 if self.completed: |
|
132 self.setIcon(0, UI.PixmapCache.getIcon("taskCompleted")) |
|
133 strikeOut = True |
|
134 else: |
|
135 self.setIcon(0, UI.PixmapCache.getIcon("empty")) |
|
136 strikeOut = False |
|
137 for column in range(2, 5): |
|
138 f = self.font(column) |
|
139 f.setStrikeOut(strikeOut) |
|
140 self.setFont(column, f) |
|
141 |
|
142 self.setPriority(priority) |
|
143 |
|
144 self.setTaskType(taskType) |
|
145 self.setTextAlignment(4, Qt.AlignmentFlag.AlignRight) |
|
146 |
|
147 def colorizeTask(self): |
|
148 """ |
|
149 Public slot to set the colors of the task item. |
|
150 """ |
|
151 boldFont = self.font(0) |
|
152 boldFont.setBold(True) |
|
153 nonBoldFont = self.font(0) |
|
154 nonBoldFont.setBold(False) |
|
155 for col in range(5): |
|
156 with contextlib.suppress(KeyError): |
|
157 self.setBackground( |
|
158 col, Preferences.getTasks( |
|
159 Task.TaskType2ColorName[self.taskType])) |
|
160 |
|
161 if self._isProjectTask: |
|
162 self.setFont(col, boldFont) |
|
163 else: |
|
164 self.setFont(col, nonBoldFont) |
|
165 |
|
166 def setSummary(self, summary): |
|
167 """ |
|
168 Public slot to update the description. |
|
169 |
|
170 @param summary summary text of the task (string) |
|
171 """ |
|
172 self.summary = summary |
|
173 self.setText(2, self.summary) |
|
174 |
|
175 def setDescription(self, description): |
|
176 """ |
|
177 Public slot to update the description field. |
|
178 |
|
179 @param description descriptive text of the task |
|
180 @type str |
|
181 """ |
|
182 self.description = description |
|
183 |
|
184 def setPriority(self, priority): |
|
185 """ |
|
186 Public slot to update the priority. |
|
187 |
|
188 @param priority priority of the task |
|
189 @type TaskPriority |
|
190 """ |
|
191 self.priority = priority |
|
192 |
|
193 if self.priority == TaskPriority.NORMAL: |
|
194 self.setIcon(1, UI.PixmapCache.getIcon("empty")) |
|
195 elif self.priority == TaskPriority.HIGH: |
|
196 self.setIcon(1, UI.PixmapCache.getIcon("taskPrioHigh")) |
|
197 elif self.priority == TaskPriority.LOW: |
|
198 self.setIcon(1, UI.PixmapCache.getIcon("taskPrioLow")) |
|
199 else: |
|
200 self.setIcon(1, UI.PixmapCache.getIcon("empty")) |
|
201 |
|
202 def setTaskType(self, taskType): |
|
203 """ |
|
204 Public method to update the task type. |
|
205 |
|
206 @param taskType type of the task |
|
207 @type TaskType |
|
208 """ |
|
209 self.taskType = taskType |
|
210 |
|
211 try: |
|
212 self.setIcon(2, UI.PixmapCache.getIcon( |
|
213 Task.TaskType2IconName[self.taskType])) |
|
214 except KeyError: |
|
215 self.setIcon(2, UI.PixmapCache.getIcon("empty")) |
|
216 |
|
217 self.colorizeTask() |
|
218 |
|
219 def setCompleted(self, completed): |
|
220 """ |
|
221 Public slot to update the completed flag. |
|
222 |
|
223 @param completed flag indicating completion status (boolean) |
|
224 """ |
|
225 self.completed = completed |
|
226 if self.completed: |
|
227 self.setIcon(0, UI.PixmapCache.getIcon("taskCompleted")) |
|
228 strikeOut = True |
|
229 else: |
|
230 self.setIcon(0, UI.PixmapCache.getIcon("empty")) |
|
231 strikeOut = False |
|
232 for column in range(2, 5): |
|
233 f = self.font(column) |
|
234 f.setStrikeOut(strikeOut) |
|
235 self.setFont(column, f) |
|
236 |
|
237 # set the completion status for all children |
|
238 for index in range(self.childCount()): |
|
239 self.child(index).setCompleted(completed) |
|
240 |
|
241 def isCompleted(self): |
|
242 """ |
|
243 Public slot to return the completion status. |
|
244 |
|
245 @return flag indicating the completion status (boolean) |
|
246 """ |
|
247 return self.completed |
|
248 |
|
249 def getFilename(self): |
|
250 """ |
|
251 Public method to retrieve the task's filename. |
|
252 |
|
253 @return filename (string) |
|
254 """ |
|
255 if self._isProjectTask and self.filename: |
|
256 return os.path.join(self.project.getProjectPath(), self.filename) |
|
257 else: |
|
258 return self.filename |
|
259 |
|
260 def isFileTask(self): |
|
261 """ |
|
262 Public slot to get an indication, if this task is related to a file. |
|
263 |
|
264 @return flag indicating a file task (boolean) |
|
265 """ |
|
266 return self.filename != "" |
|
267 |
|
268 def getLineno(self): |
|
269 """ |
|
270 Public method to retrieve the task's linenumber. |
|
271 |
|
272 @return linenumber (integer) |
|
273 """ |
|
274 return self.lineno |
|
275 |
|
276 def getUuid(self): |
|
277 """ |
|
278 Public method to get the task's uid. |
|
279 |
|
280 @return uid (string) |
|
281 """ |
|
282 return self.uid |
|
283 |
|
284 def getParentUuid(self): |
|
285 """ |
|
286 Public method to get the parent task's uid. |
|
287 |
|
288 @return parent uid (string) |
|
289 """ |
|
290 return self.parentUid |
|
291 |
|
292 def setProjectTask(self, pt): |
|
293 """ |
|
294 Public method to set the project relation flag. |
|
295 |
|
296 @param pt flag indicating a project task (boolean) |
|
297 """ |
|
298 self._isProjectTask = pt |
|
299 self.colorizeTask() |
|
300 |
|
301 def isProjectTask(self): |
|
302 """ |
|
303 Public slot to return the project relation status. |
|
304 |
|
305 @return flag indicating the project relation status (boolean) |
|
306 """ |
|
307 return self._isProjectTask |
|
308 |
|
309 def isProjectFileTask(self): |
|
310 """ |
|
311 Public slot to get an indication, if this task is related to a |
|
312 project file. |
|
313 |
|
314 @return flag indicating a project file task (boolean) |
|
315 """ |
|
316 return self._isProjectTask and self.filename != "" |
|
317 |
|
318 def toDict(self): |
|
319 """ |
|
320 Public method to convert the task data to a dictionary. |
|
321 |
|
322 @return dictionary containing the task data |
|
323 @rtype dict |
|
324 """ |
|
325 return { |
|
326 "summary": self.summary.strip(), |
|
327 "description": self.description.strip(), |
|
328 "priority": self.priority.value, |
|
329 "lineno": self.lineno, |
|
330 "completed": self.completed, |
|
331 "created": self.created, |
|
332 "type": self.taskType.value, |
|
333 "uid": self.uid, |
|
334 "parent_uid": self.parentUid, |
|
335 "expanded": self.isExpanded(), |
|
336 "filename": self.getFilename(), |
|
337 } |