22 |
22 |
23 class TimeTracker(QObject): |
23 class TimeTracker(QObject): |
24 """ |
24 """ |
25 Class implementing the time tracker object. |
25 Class implementing the time tracker object. |
26 """ |
26 """ |
|
27 |
27 FileName = "TimeTracker.ttj" |
28 FileName = "TimeTracker.ttj" |
28 |
29 |
29 def __init__(self, plugin, iconSuffix, parent=None): |
30 def __init__(self, plugin, iconSuffix, parent=None): |
30 """ |
31 """ |
31 Constructor |
32 Constructor |
32 |
33 |
33 @param plugin reference to the plugin object |
34 @param plugin reference to the plugin object |
34 @type TimeTrackerPlugin |
35 @type TimeTrackerPlugin |
35 @param iconSuffix suffix for the icons |
36 @param iconSuffix suffix for the icons |
36 @type str |
37 @type str |
37 @param parent parent |
38 @param parent parent |
38 @type QObject |
39 @type QObject |
39 """ |
40 """ |
40 QObject.__init__(self, parent) |
41 QObject.__init__(self, parent) |
41 |
42 |
42 self.__plugin = plugin |
43 self.__plugin = plugin |
43 self.__iconSuffix = iconSuffix |
44 self.__iconSuffix = iconSuffix |
44 self.__ui = parent |
45 self.__ui = parent |
45 |
46 |
46 self.__ericProject = ericApp().getObject("Project") |
47 self.__ericProject = ericApp().getObject("Project") |
47 |
48 |
48 def __initialize(self): |
49 def __initialize(self): |
49 """ |
50 """ |
50 Private slot to initialize some member variables. |
51 Private slot to initialize some member variables. |
51 """ |
52 """ |
52 self.__projectPath = '' |
53 self.__projectPath = "" |
53 self.__trackerFilePath = '' |
54 self.__trackerFilePath = "" |
54 self.__projectOpen = False |
55 self.__projectOpen = False |
55 |
56 |
56 self.__entries = {} # key: entry ID, value tracker entry |
57 self.__entries = {} # key: entry ID, value tracker entry |
57 self.__currentEntry = None |
58 self.__currentEntry = None |
58 |
59 |
59 self.__widget.clear() |
60 self.__widget.clear() |
60 self.__widget.setEnabled(False) |
61 self.__widget.setEnabled(False) |
61 |
62 |
62 def activate(self): |
63 def activate(self): |
63 """ |
64 """ |
64 Public method to activate the time tracker. |
65 Public method to activate the time tracker. |
65 """ |
66 """ |
66 from .TimeTrackerWidget import TimeTrackerWidget |
67 from .TimeTrackerWidget import TimeTrackerWidget |
67 |
68 |
68 self.__widget = TimeTrackerWidget(self) |
69 self.__widget = TimeTrackerWidget(self) |
69 iconName = ( |
70 iconName = ( |
70 "sbTimeTracker96" |
71 "sbTimeTracker96" |
71 if self.__ui.getLayoutType() == "Sidebars" else |
72 if self.__ui.getLayoutType() == "Sidebars" |
72 "clock-{0}".format(self.__iconSuffix) |
73 else "clock-{0}".format(self.__iconSuffix) |
73 ) |
74 ) |
74 self.__ui.addSideWidget( |
75 self.__ui.addSideWidget( |
75 self.__ui.BottomSide, self.__widget, |
76 self.__ui.BottomSide, |
76 UI.PixmapCache.getIcon( |
77 self.__widget, |
77 os.path.join("TimeTracker", "icons", iconName) |
78 UI.PixmapCache.getIcon(os.path.join("TimeTracker", "icons", iconName)), |
78 ), |
79 self.tr("Time Tracker"), |
79 self.tr("Time Tracker")) |
80 ) |
80 |
81 |
81 self.__activateAct = EricAction( |
82 self.__activateAct = EricAction( |
82 self.tr('Time Tracker'), |
83 self.tr("Time Tracker"), |
83 self.tr('T&ime Tracker'), |
84 self.tr("T&ime Tracker"), |
84 QKeySequence(self.tr("Alt+Shift+I")), |
85 QKeySequence(self.tr("Alt+Shift+I")), |
85 0, self, |
86 0, |
86 'time_tracker_activate') |
87 self, |
87 self.__activateAct.setStatusTip(self.tr( |
88 "time_tracker_activate", |
88 "Switch the input focus to the Time Tracker window.")) |
89 ) |
89 self.__activateAct.setWhatsThis(self.tr( |
90 self.__activateAct.setStatusTip( |
90 """<b>Activate Time Tracker</b>""" |
91 self.tr("Switch the input focus to the Time Tracker window.") |
91 """<p>This switches the input focus to the Time Tracker""" |
92 ) |
92 """ window.</p>""" |
93 self.__activateAct.setWhatsThis( |
93 )) |
94 self.tr( |
|
95 """<b>Activate Time Tracker</b>""" |
|
96 """<p>This switches the input focus to the Time Tracker""" |
|
97 """ window.</p>""" |
|
98 ) |
|
99 ) |
94 self.__activateAct.triggered.connect(self.__activateWidget) |
100 self.__activateAct.triggered.connect(self.__activateWidget) |
95 |
101 |
96 self.__ui.addEricActions([self.__activateAct], 'ui') |
102 self.__ui.addEricActions([self.__activateAct], "ui") |
97 menu = self.__ui.getMenu("subwindow") |
103 menu = self.__ui.getMenu("subwindow") |
98 menu.addAction(self.__activateAct) |
104 menu.addAction(self.__activateAct) |
99 |
105 |
100 self.__initialize() |
106 self.__initialize() |
101 |
107 |
102 def deactivate(self): |
108 def deactivate(self): |
103 """ |
109 """ |
104 Public method to deactivate the time tracker. |
110 Public method to deactivate the time tracker. |
105 """ |
111 """ |
106 menu = self.__ui.getMenu("subwindow") |
112 menu = self.__ui.getMenu("subwindow") |
107 menu.removeAction(self.__activateAct) |
113 menu.removeAction(self.__activateAct) |
108 self.__ui.removeEricActions([self.__activateAct], 'ui') |
114 self.__ui.removeEricActions([self.__activateAct], "ui") |
109 self.__ui.removeSideWidget(self.__widget) |
115 self.__ui.removeSideWidget(self.__widget) |
110 |
116 |
111 def projectOpened(self): |
117 def projectOpened(self): |
112 """ |
118 """ |
113 Public slot to handle the projectOpened signal. |
119 Public slot to handle the projectOpened signal. |
114 """ |
120 """ |
115 if self.__projectOpen: |
121 if self.__projectOpen: |
116 self.projectClosed() |
122 self.projectClosed() |
117 |
123 |
118 self.__projectOpen = True |
124 self.__projectOpen = True |
119 self.__projectPath = self.__ericProject.getProjectPath() |
125 self.__projectPath = self.__ericProject.getProjectPath() |
120 self.__trackerFilePath = os.path.join( |
126 self.__trackerFilePath = os.path.join( |
121 self.__ericProject.getProjectManagementDir(), |
127 self.__ericProject.getProjectManagementDir(), TimeTracker.FileName |
122 TimeTracker.FileName) |
128 ) |
123 |
129 |
124 self.__readTrackerEntries() |
130 self.__readTrackerEntries() |
125 self.__widget.showTrackerEntries(sorted(self.__entries.values(), |
131 self.__widget.showTrackerEntries(sorted(self.__entries.values(), reverse=True)) |
126 reverse=True)) |
|
127 self.__widget.setEnabled(True) |
132 self.__widget.setEnabled(True) |
128 |
133 |
129 self.startTrackerEntry() |
134 self.startTrackerEntry() |
130 |
135 |
131 def projectClosed(self): |
136 def projectClosed(self): |
132 """ |
137 """ |
133 Public slot to handle the projectClosed signal. |
138 Public slot to handle the projectClosed signal. |
134 """ |
139 """ |
135 if self.__projectOpen: |
140 if self.__projectOpen: |
136 self.stopTrackerEntry() |
141 self.stopTrackerEntry() |
137 self.saveTrackerEntries() |
142 self.saveTrackerEntries() |
138 self.__initialize() |
143 self.__initialize() |
139 |
144 |
140 def __readTrackerEntries(self): |
145 def __readTrackerEntries(self): |
141 """ |
146 """ |
142 Private slot to read the time tracker entries from a file. |
147 Private slot to read the time tracker entries from a file. |
143 """ |
148 """ |
144 if os.path.exists(self.__trackerFilePath): |
149 if os.path.exists(self.__trackerFilePath): |
148 entriesDataList = json.loads(jsonString) |
153 entriesDataList = json.loads(jsonString) |
149 except (OSError, json.JSONDecodeError) as err: |
154 except (OSError, json.JSONDecodeError) as err: |
150 EricMessageBox.critical( |
155 EricMessageBox.critical( |
151 self.__ui, |
156 self.__ui, |
152 self.tr("Read Time Tracker File"), |
157 self.tr("Read Time Tracker File"), |
153 self.tr("""<p>The time tracker file <b>{0}</b> could""" |
158 self.tr( |
154 """ not be read.</p><p>Reason: {1}</p>""") |
159 """<p>The time tracker file <b>{0}</b> could""" |
155 .format(self.__trackerFilePath, str(err))) |
160 """ not be read.</p><p>Reason: {1}</p>""" |
|
161 ).format(self.__trackerFilePath, str(err)), |
|
162 ) |
156 return |
163 return |
157 |
164 |
158 from .TimeTrackEntry import TimeTrackEntry |
165 from .TimeTrackEntry import TimeTrackEntry |
159 |
166 |
160 invalidCount = 0 |
167 invalidCount = 0 |
161 for data in entriesDataList: |
168 for data in entriesDataList: |
162 entry = TimeTrackEntry(self.__plugin) |
169 entry = TimeTrackEntry(self.__plugin) |
163 eid = entry.fromDict(data) |
170 eid = entry.fromDict(data) |
164 if eid > -1: |
171 if eid > -1: |
165 self.__entries[eid] = entry |
172 self.__entries[eid] = entry |
166 else: |
173 else: |
167 invalidCount += 1 |
174 invalidCount += 1 |
168 |
175 |
169 if invalidCount: |
176 if invalidCount: |
170 EricMessageBox.information( |
177 EricMessageBox.information( |
171 self.__ui, |
178 self.__ui, |
172 self.tr("Read Time Tracker File"), |
179 self.tr("Read Time Tracker File"), |
173 self.tr("""<p>The time tracker file <b>{0}</b>""" |
180 self.tr( |
174 """ contained %n invalid entries. These""" |
181 """<p>The time tracker file <b>{0}</b>""" |
175 """ have been discarded.</p>""", "", |
182 """ contained %n invalid entries. These""" |
176 invalidCount).format(self.__trackerFilePath)) |
183 """ have been discarded.</p>""", |
177 |
184 "", |
|
185 invalidCount, |
|
186 ).format(self.__trackerFilePath), |
|
187 ) |
|
188 |
178 def saveTrackerEntries(self, filePath="", ids=None): |
189 def saveTrackerEntries(self, filePath="", ids=None): |
179 """ |
190 """ |
180 Public slot to save the tracker entries to a file. |
191 Public slot to save the tracker entries to a file. |
181 |
192 |
182 @param filePath path and name of the file to write the entries to |
193 @param filePath path and name of the file to write the entries to |
183 @type str |
194 @type str |
184 @param ids list of entry IDs to be written |
195 @param ids list of entry IDs to be written |
185 @type list of int |
196 @type list of int |
186 """ |
197 """ |
187 if not filePath: |
198 if not filePath: |
188 filePath = self.__trackerFilePath |
199 filePath = self.__trackerFilePath |
189 entriesDataList = ( |
200 entriesDataList = ( |
190 [self.__entries[eid].toDict() |
201 [self.__entries[eid].toDict() for eid in ids if eid in self.__entries] |
191 for eid in ids if eid in self.__entries] |
202 if ids |
192 if ids else |
203 else [e.toDict() for e in self.__entries.values()] |
193 [e.toDict() for e in self.__entries.values()] |
|
194 ) |
204 ) |
195 try: |
205 try: |
196 jsonString = json.dumps(entriesDataList, indent=2) |
206 jsonString = json.dumps(entriesDataList, indent=2) |
197 with open(filePath, "w") as f: |
207 with open(filePath, "w") as f: |
198 f.write(jsonString) |
208 f.write(jsonString) |
199 except (TypeError, OSError) as err: |
209 except (TypeError, OSError) as err: |
200 EricMessageBox.critical( |
210 EricMessageBox.critical( |
201 self.__ui, |
211 self.__ui, |
202 self.tr("Save Time Tracker File"), |
212 self.tr("Save Time Tracker File"), |
203 self.tr("""<p>The time tracker file <b>{0}</b> could""" |
213 self.tr( |
204 """ not be saved.</p><p>Reason: {1}</p>""") |
214 """<p>The time tracker file <b>{0}</b> could""" |
205 .format(self.__trackerFilePath, str(err))) |
215 """ not be saved.</p><p>Reason: {1}</p>""" |
206 |
216 ).format(self.__trackerFilePath, str(err)), |
|
217 ) |
|
218 |
207 def importTrackerEntries(self, fname): |
219 def importTrackerEntries(self, fname): |
208 """ |
220 """ |
209 Public slot to import tracker entries from a file. |
221 Public slot to import tracker entries from a file. |
210 |
222 |
211 @param fname name of the file to import (string) |
223 @param fname name of the file to import (string) |
212 """ |
224 """ |
213 try: |
225 try: |
214 with open(fname, "r", encoding="utf-8") as f: |
226 with open(fname, "r", encoding="utf-8") as f: |
215 jsonString = f.read() |
227 jsonString = f.read() |
216 entriesDataList = json.loads(jsonString) |
228 entriesDataList = json.loads(jsonString) |
217 except (OSError, json.JSONDecodeError) as err: |
229 except (OSError, json.JSONDecodeError) as err: |
218 EricMessageBox.critical( |
230 EricMessageBox.critical( |
219 self.__ui, |
231 self.__ui, |
220 self.tr("Import Time Tracker File"), |
232 self.tr("Import Time Tracker File"), |
221 self.tr("""<p>The time tracker file <b>{0}</b> could""" |
233 self.tr( |
222 """ not be read.</p><p>Reason: {1}</p>""") |
234 """<p>The time tracker file <b>{0}</b> could""" |
223 .format(fname, str(err))) |
235 """ not be read.</p><p>Reason: {1}</p>""" |
|
236 ).format(fname, str(err)), |
|
237 ) |
224 return |
238 return |
225 |
239 |
226 from .TimeTrackEntry import TimeTrackEntry |
240 from .TimeTrackEntry import TimeTrackEntry |
227 |
241 |
228 invalidCount = 0 |
242 invalidCount = 0 |
229 duplicateCount = 0 |
243 duplicateCount = 0 |
230 entries = [] |
244 entries = [] |
231 |
245 |
232 for data in entriesDataList: |
246 for data in entriesDataList: |
233 entry = TimeTrackEntry(self.__plugin) |
247 entry = TimeTrackEntry(self.__plugin) |
234 eid = entry.fromDict(data) |
248 eid = entry.fromDict(data) |
235 if eid > -1: |
249 if eid > -1: |
236 entries.append(entry) |
250 entries.append(entry) |
237 else: |
251 else: |
238 invalidCount += 1 |
252 invalidCount += 1 |
239 |
253 |
240 if not self.__plugin.getPreferences("AllowDuplicates"): |
254 if not self.__plugin.getPreferences("AllowDuplicates"): |
241 startDateTimes = [ |
255 startDateTimes = [e.getStartDateTime() for e in self.__entries.values()] |
242 e.getStartDateTime() for e in self.__entries.values()] |
|
243 for entry in entries[:]: |
256 for entry in entries[:]: |
244 if entry.getStartDateTime() in startDateTimes: |
257 if entry.getStartDateTime() in startDateTimes: |
245 entries.remove(entry) |
258 entries.remove(entry) |
246 duplicateCount += 1 |
259 duplicateCount += 1 |
247 |
260 |
248 start = ( |
261 start = max(self.__entries.keys()) + 1 if len(self.__entries.keys()) else 0 |
249 max(self.__entries.keys()) + 1 |
|
250 if len(self.__entries.keys()) else |
|
251 0 |
|
252 ) |
|
253 for nextID, entry in enumerate(entries, start=start): |
262 for nextID, entry in enumerate(entries, start=start): |
254 entry.setID(nextID) |
263 entry.setID(nextID) |
255 self.__entries[nextID] = entry |
264 self.__entries[nextID] = entry |
256 |
265 |
257 if self.__plugin.getPreferences("AutoSave"): |
266 if self.__plugin.getPreferences("AutoSave"): |
258 self.saveTrackerEntries() |
267 self.saveTrackerEntries() |
259 |
268 |
260 if invalidCount != 0 or duplicateCount != 0: |
269 if invalidCount != 0 or duplicateCount != 0: |
261 if invalidCount != 0 and duplicateCount != 0: |
270 if invalidCount != 0 and duplicateCount != 0: |
262 msg = self.tr( |
271 msg = self.tr( |
263 """<p>The time tracker file <b>{0}</b> contained""" |
272 """<p>The time tracker file <b>{0}</b> contained""" |
264 """ %n invalid entries.""", |
273 """ %n invalid entries.""", |
265 "", invalidCount).format(fname) |
274 "", |
|
275 invalidCount, |
|
276 ).format(fname) |
266 msg += " " + self.tr( |
277 msg += " " + self.tr( |
267 """ %n duplicate entries were detected.""", "", |
278 """ %n duplicate entries were detected.""", "", duplicateCount |
268 duplicateCount) |
279 ) |
269 elif duplicateCount != 0: |
280 elif duplicateCount != 0: |
270 msg = self.tr( |
281 msg = self.tr( |
271 """<p>The time tracker file <b>{0}</b> contained""" |
282 """<p>The time tracker file <b>{0}</b> contained""" |
272 """ %n duplicate entries.""", |
283 """ %n duplicate entries.""", |
273 "", duplicateCount).format(fname) |
284 "", |
|
285 duplicateCount, |
|
286 ).format(fname) |
274 elif invalidCount != 0: |
287 elif invalidCount != 0: |
275 msg = self.tr( |
288 msg = self.tr( |
276 """<p>The time tracker file <b>{0}</b> contained""" |
289 """<p>The time tracker file <b>{0}</b> contained""" |
277 """ %n invalid entries.""", |
290 """ %n invalid entries.""", |
278 "", invalidCount).format(fname) |
291 "", |
|
292 invalidCount, |
|
293 ).format(fname) |
279 msg += " " + self.tr( |
294 msg += " " + self.tr( |
280 """ %n entries have been ignored.</p>""", |
295 """ %n entries have been ignored.</p>""", |
281 "", invalidCount + duplicateCount) |
296 "", |
|
297 invalidCount + duplicateCount, |
|
298 ) |
282 EricMessageBox.information( |
299 EricMessageBox.information( |
283 self.__ui, |
300 self.__ui, self.tr("Import Time Tracker File"), msg |
284 self.tr("Import Time Tracker File"), |
301 ) |
285 msg) |
302 |
286 |
|
287 self.__widget.clear() |
303 self.__widget.clear() |
288 self.__widget.showTrackerEntries(sorted(self.__entries.values(), |
304 self.__widget.showTrackerEntries(sorted(self.__entries.values(), reverse=True)) |
289 reverse=True)) |
|
290 self.__widget.setCurrentEntry(self.__currentEntry) |
305 self.__widget.setCurrentEntry(self.__currentEntry) |
291 |
306 |
292 def addTrackerEntry(self, startDateTime, duration, task, comment): |
307 def addTrackerEntry(self, startDateTime, duration, task, comment): |
293 """ |
308 """ |
294 Public method to add a new tracker entry based on the given data. |
309 Public method to add a new tracker entry based on the given data. |
295 |
310 |
296 @param startDateTime start date and time |
311 @param startDateTime start date and time |
297 @type QDateTime |
312 @type QDateTime |
298 @param duration duration in minutes |
313 @param duration duration in minutes |
299 @type int |
314 @type int |
300 @param task task description |
315 @param task task description |
302 @param comment comment |
317 @param comment comment |
303 @type str |
318 @type str |
304 """ |
319 """ |
305 if not self.__plugin.getPreferences("AllowDuplicates"): |
320 if not self.__plugin.getPreferences("AllowDuplicates"): |
306 startDateTimes = [ |
321 startDateTimes = [ |
307 entry.getStartDateTime() for entry in self.__entries.values()] |
322 entry.getStartDateTime() for entry in self.__entries.values() |
|
323 ] |
308 if startDateTime in startDateTimes: |
324 if startDateTime in startDateTimes: |
309 return |
325 return |
310 |
326 |
311 if duration < self.__plugin.getPreferences("MinimumDuration"): |
327 if duration < self.__plugin.getPreferences("MinimumDuration"): |
312 return |
328 return |
313 |
329 |
314 nextID = ( |
330 nextID = max(self.__entries.keys()) + 1 if len(self.__entries.keys()) else 0 |
315 max(self.__entries.keys()) + 1 |
331 |
316 if len(self.__entries.keys()) else |
|
317 0 |
|
318 ) |
|
319 |
|
320 from .TimeTrackEntry import TimeTrackEntry |
332 from .TimeTrackEntry import TimeTrackEntry |
321 |
333 |
322 entry = TimeTrackEntry(self.__plugin) |
334 entry = TimeTrackEntry(self.__plugin) |
323 entry.setID(nextID) |
335 entry.setID(nextID) |
324 entry.setStartDateTime(startDateTime) |
336 entry.setStartDateTime(startDateTime) |
325 entry.setDuration(duration) |
337 entry.setDuration(duration) |
326 entry.setTask(task) |
338 entry.setTask(task) |
327 entry.setComment(comment) |
339 entry.setComment(comment) |
328 self.__entries[nextID] = entry |
340 self.__entries[nextID] = entry |
329 |
341 |
330 self.__widget.clear() |
342 self.__widget.clear() |
331 self.__widget.showTrackerEntries( |
343 self.__widget.showTrackerEntries(sorted(self.__entries.values(), reverse=True)) |
332 sorted(self.__entries.values(), reverse=True)) |
|
333 self.__widget.setCurrentEntry(self.__currentEntry) |
344 self.__widget.setCurrentEntry(self.__currentEntry) |
334 |
345 |
335 def pauseTrackerEntry(self): |
346 def pauseTrackerEntry(self): |
336 """ |
347 """ |
337 Public method to pause the current tracker entry. |
348 Public method to pause the current tracker entry. |
338 """ |
349 """ |
339 self.__currentEntry.pause() |
350 self.__currentEntry.pause() |
340 |
351 |
341 def continueTrackerEntry(self): |
352 def continueTrackerEntry(self): |
342 """ |
353 """ |
343 Public method to continue the current tracker entry. |
354 Public method to continue the current tracker entry. |
344 """ |
355 """ |
345 self.__currentEntry.continue_() |
356 self.__currentEntry.continue_() |
346 |
357 |
347 def stopTrackerEntry(self): |
358 def stopTrackerEntry(self): |
348 """ |
359 """ |
349 Public method to stop the current tracker entry. |
360 Public method to stop the current tracker entry. |
350 |
361 |
351 @return tuple of the ID assigned to the stopped tracker entry and |
362 @return tuple of the ID assigned to the stopped tracker entry and |
352 the duration |
363 the duration |
353 @rtype tuple of (int, int) |
364 @rtype tuple of (int, int) |
354 """ |
365 """ |
355 duration = 0 |
366 duration = 0 |
356 nextID = -1 |
367 nextID = -1 |
357 if self.__currentEntry is not None: |
368 if self.__currentEntry is not None: |
358 self.__currentEntry.stop() |
369 self.__currentEntry.stop() |
359 if self.__currentEntry.isValid(): |
370 if self.__currentEntry.isValid(): |
360 nextID = ( |
371 nextID = ( |
361 max(self.__entries.keys()) + 1 |
372 max(self.__entries.keys()) + 1 if len(self.__entries.keys()) else 0 |
362 if len(self.__entries.keys()) else |
|
363 0 |
|
364 ) |
373 ) |
365 self.__currentEntry.setID(nextID) |
374 self.__currentEntry.setID(nextID) |
366 self.__entries[nextID] = self.__currentEntry |
375 self.__entries[nextID] = self.__currentEntry |
367 if self.__plugin.getPreferences("AutoSave"): |
376 if self.__plugin.getPreferences("AutoSave"): |
368 self.saveTrackerEntries() |
377 self.saveTrackerEntries() |
369 duration = self.__currentEntry.getDuration() |
378 duration = self.__currentEntry.getDuration() |
370 self.__currentEntry = None |
379 self.__currentEntry = None |
371 |
380 |
372 return nextID, duration |
381 return nextID, duration |
373 |
382 |
374 def startTrackerEntry(self): |
383 def startTrackerEntry(self): |
375 """ |
384 """ |
376 Public method to start a new tracker entry. |
385 Public method to start a new tracker entry. |
377 """ |
386 """ |
378 from .TimeTrackEntry import TimeTrackEntry |
387 from .TimeTrackEntry import TimeTrackEntry |
379 |
388 |
380 self.__currentEntry = TimeTrackEntry(self.__plugin) |
389 self.__currentEntry = TimeTrackEntry(self.__plugin) |
381 self.__currentEntry.start() |
390 self.__currentEntry.start() |
382 self.__widget.setCurrentEntry(self.__currentEntry) |
391 self.__widget.setCurrentEntry(self.__currentEntry) |
383 |
392 |
384 def getCurrentEntry(self): |
393 def getCurrentEntry(self): |
385 """ |
394 """ |
386 Public method to get a reference to the current tracker entry. |
395 Public method to get a reference to the current tracker entry. |
387 |
396 |
388 @return reference to the current entry |
397 @return reference to the current entry |
389 @rtype TimeTrackEntry |
398 @rtype TimeTrackEntry |
390 """ |
399 """ |
391 return self.__currentEntry |
400 return self.__currentEntry |
392 |
401 |
393 def getEntry(self, eid): |
402 def getEntry(self, eid): |
394 """ |
403 """ |
395 Public method to get a tracker entry given its ID. |
404 Public method to get a tracker entry given its ID. |
396 |
405 |
397 @param eid ID of the tracker entry |
406 @param eid ID of the tracker entry |
398 @type int |
407 @type int |
399 @return entry for the given ID or None |
408 @return entry for the given ID or None |
400 @rtype TimeTrackEntry |
409 @rtype TimeTrackEntry |
401 """ |
410 """ |
402 if eid in self.__entries: |
411 if eid in self.__entries: |
403 return self.__entries[eid] |
412 return self.__entries[eid] |
404 else: |
413 else: |
405 return None |
414 return None |
406 |
415 |
407 def deleteTrackerEntry(self, eid): |
416 def deleteTrackerEntry(self, eid): |
408 """ |
417 """ |
409 Public method to delete a tracker entry given its ID. |
418 Public method to delete a tracker entry given its ID. |
410 |
419 |
411 @param eid ID of the tracker entry |
420 @param eid ID of the tracker entry |
412 @type int |
421 @type int |
413 """ |
422 """ |
414 if eid in self.__entries: |
423 if eid in self.__entries: |
415 del self.__entries[eid] |
424 del self.__entries[eid] |
416 |
425 |
417 def removeDuplicateTrackerEntries(self): |
426 def removeDuplicateTrackerEntries(self): |
418 """ |
427 """ |
419 Public slot to remove duplicate time tracker entries. |
428 Public slot to remove duplicate time tracker entries. |
420 |
429 |
421 If entries with the identical start date and time are found, the one |
430 If entries with the identical start date and time are found, the one |
422 with the longest duration is kept. |
431 with the longest duration is kept. |
423 """ |
432 """ |
424 entries = {} |
433 entries = {} |
425 for entry in self.__entries.values(): |
434 for entry in self.__entries.values(): |
427 if dt in entries: |
436 if dt in entries: |
428 if entry.getDuration() > entries[dt].getDuration(): |
437 if entry.getDuration() > entries[dt].getDuration(): |
429 entries[dt] = entry |
438 entries[dt] = entry |
430 else: |
439 else: |
431 entries[dt] = entry |
440 entries[dt] = entry |
432 |
441 |
433 self.__entries = {} |
442 self.__entries = {} |
434 for nextID, entry in enumerate(sorted(entries.values())): |
443 for nextID, entry in enumerate(sorted(entries.values())): |
435 entry.setID(nextID) |
444 entry.setID(nextID) |
436 self.__entries[nextID] = entry |
445 self.__entries[nextID] = entry |
437 |
446 |
438 if self.__plugin.getPreferences("AutoSave"): |
447 if self.__plugin.getPreferences("AutoSave"): |
439 self.saveTrackerEntries() |
448 self.saveTrackerEntries() |
440 |
449 |
441 self.__widget.clear() |
450 self.__widget.clear() |
442 self.__widget.showTrackerEntries( |
451 self.__widget.showTrackerEntries(sorted(self.__entries.values(), reverse=True)) |
443 sorted(self.__entries.values(), reverse=True)) |
|
444 self.__widget.setCurrentEntry(self.__currentEntry) |
452 self.__widget.setCurrentEntry(self.__currentEntry) |
445 |
453 |
446 def mergeDuplicateTrackerEntries(self): |
454 def mergeDuplicateTrackerEntries(self): |
447 """ |
455 """ |
448 Public slot to merge duplicate time tracker entries. |
456 Public slot to merge duplicate time tracker entries. |
449 |
457 |
450 If entries with the identical start date and time are found, the |
458 If entries with the identical start date and time are found, the |
451 durations of these entries are added. |
459 durations of these entries are added. |
452 """ |
460 """ |
453 entries = {} |
461 entries = {} |
454 for entry in self.__entries.values(): |
462 for entry in self.__entries.values(): |
455 dt = entry.getStartDateTime() |
463 dt = entry.getStartDateTime() |
456 if dt in entries: |
464 if dt in entries: |
457 entries[dt].addDuration(entry.getDuration()) |
465 entries[dt].addDuration(entry.getDuration()) |
458 else: |
466 else: |
459 entries[dt] = entry |
467 entries[dt] = entry |
460 |
468 |
461 self.__entries = {} |
469 self.__entries = {} |
462 for nextID, entry in enumerate(sorted(entries.values())): |
470 for nextID, entry in enumerate(sorted(entries.values())): |
463 entry.setID(nextID) |
471 entry.setID(nextID) |
464 self.__entries[nextID] = entry |
472 self.__entries[nextID] = entry |
465 |
473 |
466 if self.__plugin.getPreferences("AutoSave"): |
474 if self.__plugin.getPreferences("AutoSave"): |
467 self.saveTrackerEntries() |
475 self.saveTrackerEntries() |
468 |
476 |
469 self.__widget.clear() |
477 self.__widget.clear() |
470 self.__widget.showTrackerEntries( |
478 self.__widget.showTrackerEntries(sorted(self.__entries.values(), reverse=True)) |
471 sorted(self.__entries.values(), reverse=True)) |
|
472 self.__widget.setCurrentEntry(self.__currentEntry) |
479 self.__widget.setCurrentEntry(self.__currentEntry) |
473 |
480 |
474 def entryChanged(self): |
481 def entryChanged(self): |
475 """ |
482 """ |
476 Public method to indicate an external change to any of the entries. |
483 Public method to indicate an external change to any of the entries. |
477 """ |
484 """ |
478 if self.__plugin.getPreferences("AutoSave"): |
485 if self.__plugin.getPreferences("AutoSave"): |
479 self.saveTrackerEntries() |
486 self.saveTrackerEntries() |
480 |
487 |
481 def getPreferences(self, key): |
488 def getPreferences(self, key): |
482 """ |
489 """ |
483 Public method to retrieve the various settings. |
490 Public method to retrieve the various settings. |
484 |
491 |
485 @param key key of the value to get |
492 @param key key of the value to get |
486 @type str |
493 @type str |
487 @return value of the requested setting |
494 @return value of the requested setting |
488 @rtype Any |
495 @rtype Any |
489 """ |
496 """ |
490 return self.__plugin.getPreferences(key) |
497 return self.__plugin.getPreferences(key) |
491 |
498 |
492 def __activateWidget(self): |
499 def __activateWidget(self): |
493 """ |
500 """ |
494 Private slot to handle the activation of the time tracker widget. |
501 Private slot to handle the activation of the time tracker widget. |
495 """ |
502 """ |
496 uiLayoutType = self.__ui.getLayoutType() |
503 uiLayoutType = self.__ui.getLayoutType() |
497 |
504 |
498 if uiLayoutType == "Toolboxes": |
505 if uiLayoutType == "Toolboxes": |
499 self.__ui.hToolboxDock.show() |
506 self.__ui.hToolboxDock.show() |
500 self.__ui.hToolbox.setCurrentWidget(self.__widget) |
507 self.__ui.hToolbox.setCurrentWidget(self.__widget) |
501 elif uiLayoutType == "Sidebars": |
508 elif uiLayoutType == "Sidebars": |
502 self.__ui.bottomSidebar.show() |
509 self.__ui.bottomSidebar.show() |