diff -r 09f25e6d99ee -r 702f47d3f794 TimeTracker/TimeTracker.py --- a/TimeTracker/TimeTracker.py Thu Dec 30 12:38:13 2021 +0100 +++ b/TimeTracker/TimeTracker.py Tue Sep 20 19:10:20 2022 +0200 @@ -24,12 +24,13 @@ """ Class implementing the time tracker object. """ + FileName = "TimeTracker.ttj" - + def __init__(self, plugin, iconSuffix, parent=None): """ Constructor - + @param plugin reference to the plugin object @type TimeTrackerPlugin @param iconSuffix suffix for the icons @@ -38,96 +39,100 @@ @type QObject """ QObject.__init__(self, parent) - + self.__plugin = plugin self.__iconSuffix = iconSuffix self.__ui = parent - + self.__ericProject = ericApp().getObject("Project") - + def __initialize(self): """ Private slot to initialize some member variables. """ - self.__projectPath = '' - self.__trackerFilePath = '' + self.__projectPath = "" + self.__trackerFilePath = "" self.__projectOpen = False - - self.__entries = {} # key: entry ID, value tracker entry + + self.__entries = {} # key: entry ID, value tracker entry self.__currentEntry = None - + self.__widget.clear() self.__widget.setEnabled(False) - + def activate(self): """ Public method to activate the time tracker. """ from .TimeTrackerWidget import TimeTrackerWidget - + self.__widget = TimeTrackerWidget(self) iconName = ( "sbTimeTracker96" - if self.__ui.getLayoutType() == "Sidebars" else - "clock-{0}".format(self.__iconSuffix) + if self.__ui.getLayoutType() == "Sidebars" + else "clock-{0}".format(self.__iconSuffix) ) self.__ui.addSideWidget( - self.__ui.BottomSide, self.__widget, - UI.PixmapCache.getIcon( - os.path.join("TimeTracker", "icons", iconName) - ), - self.tr("Time Tracker")) - + self.__ui.BottomSide, + self.__widget, + UI.PixmapCache.getIcon(os.path.join("TimeTracker", "icons", iconName)), + self.tr("Time Tracker"), + ) + self.__activateAct = EricAction( - self.tr('Time Tracker'), - self.tr('T&ime Tracker'), + self.tr("Time Tracker"), + self.tr("T&ime Tracker"), QKeySequence(self.tr("Alt+Shift+I")), - 0, self, - 'time_tracker_activate') - self.__activateAct.setStatusTip(self.tr( - "Switch the input focus to the Time Tracker window.")) - self.__activateAct.setWhatsThis(self.tr( - """<b>Activate Time Tracker</b>""" - """<p>This switches the input focus to the Time Tracker""" - """ window.</p>""" - )) + 0, + self, + "time_tracker_activate", + ) + self.__activateAct.setStatusTip( + self.tr("Switch the input focus to the Time Tracker window.") + ) + self.__activateAct.setWhatsThis( + self.tr( + """<b>Activate Time Tracker</b>""" + """<p>This switches the input focus to the Time Tracker""" + """ window.</p>""" + ) + ) self.__activateAct.triggered.connect(self.__activateWidget) - - self.__ui.addEricActions([self.__activateAct], 'ui') + + self.__ui.addEricActions([self.__activateAct], "ui") menu = self.__ui.getMenu("subwindow") menu.addAction(self.__activateAct) - + self.__initialize() - + def deactivate(self): """ Public method to deactivate the time tracker. """ menu = self.__ui.getMenu("subwindow") menu.removeAction(self.__activateAct) - self.__ui.removeEricActions([self.__activateAct], 'ui') + self.__ui.removeEricActions([self.__activateAct], "ui") self.__ui.removeSideWidget(self.__widget) - + def projectOpened(self): """ Public slot to handle the projectOpened signal. """ if self.__projectOpen: self.projectClosed() - + self.__projectOpen = True self.__projectPath = self.__ericProject.getProjectPath() self.__trackerFilePath = os.path.join( - self.__ericProject.getProjectManagementDir(), - TimeTracker.FileName) - + self.__ericProject.getProjectManagementDir(), TimeTracker.FileName + ) + self.__readTrackerEntries() - self.__widget.showTrackerEntries(sorted(self.__entries.values(), - reverse=True)) + self.__widget.showTrackerEntries(sorted(self.__entries.values(), reverse=True)) self.__widget.setEnabled(True) - + self.startTrackerEntry() - + def projectClosed(self): """ Public slot to handle the projectClosed signal. @@ -136,7 +141,7 @@ self.stopTrackerEntry() self.saveTrackerEntries() self.__initialize() - + def __readTrackerEntries(self): """ Private slot to read the time tracker entries from a file. @@ -150,13 +155,15 @@ EricMessageBox.critical( self.__ui, self.tr("Read Time Tracker File"), - self.tr("""<p>The time tracker file <b>{0}</b> could""" - """ not be read.</p><p>Reason: {1}</p>""") - .format(self.__trackerFilePath, str(err))) + self.tr( + """<p>The time tracker file <b>{0}</b> could""" + """ not be read.</p><p>Reason: {1}</p>""" + ).format(self.__trackerFilePath, str(err)), + ) return - + from .TimeTrackEntry import TimeTrackEntry - + invalidCount = 0 for data in entriesDataList: entry = TimeTrackEntry(self.__plugin) @@ -165,20 +172,24 @@ self.__entries[eid] = entry else: invalidCount += 1 - + if invalidCount: EricMessageBox.information( self.__ui, self.tr("Read Time Tracker File"), - self.tr("""<p>The time tracker file <b>{0}</b>""" - """ contained %n invalid entries. These""" - """ have been discarded.</p>""", "", - invalidCount).format(self.__trackerFilePath)) - + self.tr( + """<p>The time tracker file <b>{0}</b>""" + """ contained %n invalid entries. These""" + """ have been discarded.</p>""", + "", + invalidCount, + ).format(self.__trackerFilePath), + ) + def saveTrackerEntries(self, filePath="", ids=None): """ Public slot to save the tracker entries to a file. - + @param filePath path and name of the file to write the entries to @type str @param ids list of entry IDs to be written @@ -187,10 +198,9 @@ if not filePath: filePath = self.__trackerFilePath entriesDataList = ( - [self.__entries[eid].toDict() - for eid in ids if eid in self.__entries] - if ids else - [e.toDict() for e in self.__entries.values()] + [self.__entries[eid].toDict() for eid in ids if eid in self.__entries] + if ids + else [e.toDict() for e in self.__entries.values()] ) try: jsonString = json.dumps(entriesDataList, indent=2) @@ -200,14 +210,16 @@ EricMessageBox.critical( self.__ui, self.tr("Save Time Tracker File"), - self.tr("""<p>The time tracker file <b>{0}</b> could""" - """ not be saved.</p><p>Reason: {1}</p>""") - .format(self.__trackerFilePath, str(err))) - + self.tr( + """<p>The time tracker file <b>{0}</b> could""" + """ not be saved.</p><p>Reason: {1}</p>""" + ).format(self.__trackerFilePath, str(err)), + ) + def importTrackerEntries(self, fname): """ Public slot to import tracker entries from a file. - + @param fname name of the file to import (string) """ try: @@ -218,17 +230,19 @@ EricMessageBox.critical( self.__ui, self.tr("Import Time Tracker File"), - self.tr("""<p>The time tracker file <b>{0}</b> could""" - """ not be read.</p><p>Reason: {1}</p>""") - .format(fname, str(err))) + self.tr( + """<p>The time tracker file <b>{0}</b> could""" + """ not be read.</p><p>Reason: {1}</p>""" + ).format(fname, str(err)), + ) return - + from .TimeTrackEntry import TimeTrackEntry - + invalidCount = 0 duplicateCount = 0 entries = [] - + for data in entriesDataList: entry = TimeTrackEntry(self.__plugin) eid = entry.fromDict(data) @@ -236,63 +250,64 @@ entries.append(entry) else: invalidCount += 1 - + if not self.__plugin.getPreferences("AllowDuplicates"): - startDateTimes = [ - e.getStartDateTime() for e in self.__entries.values()] + startDateTimes = [e.getStartDateTime() for e in self.__entries.values()] for entry in entries[:]: if entry.getStartDateTime() in startDateTimes: entries.remove(entry) duplicateCount += 1 - - start = ( - max(self.__entries.keys()) + 1 - if len(self.__entries.keys()) else - 0 - ) + + start = max(self.__entries.keys()) + 1 if len(self.__entries.keys()) else 0 for nextID, entry in enumerate(entries, start=start): entry.setID(nextID) self.__entries[nextID] = entry - + if self.__plugin.getPreferences("AutoSave"): self.saveTrackerEntries() - + if invalidCount != 0 or duplicateCount != 0: if invalidCount != 0 and duplicateCount != 0: msg = self.tr( """<p>The time tracker file <b>{0}</b> contained""" """ %n invalid entries.""", - "", invalidCount).format(fname) + "", + invalidCount, + ).format(fname) msg += " " + self.tr( - """ %n duplicate entries were detected.""", "", - duplicateCount) + """ %n duplicate entries were detected.""", "", duplicateCount + ) elif duplicateCount != 0: msg = self.tr( """<p>The time tracker file <b>{0}</b> contained""" """ %n duplicate entries.""", - "", duplicateCount).format(fname) + "", + duplicateCount, + ).format(fname) elif invalidCount != 0: msg = self.tr( """<p>The time tracker file <b>{0}</b> contained""" """ %n invalid entries.""", - "", invalidCount).format(fname) + "", + invalidCount, + ).format(fname) msg += " " + self.tr( """ %n entries have been ignored.</p>""", - "", invalidCount + duplicateCount) + "", + invalidCount + duplicateCount, + ) EricMessageBox.information( - self.__ui, - self.tr("Import Time Tracker File"), - msg) - + self.__ui, self.tr("Import Time Tracker File"), msg + ) + self.__widget.clear() - self.__widget.showTrackerEntries(sorted(self.__entries.values(), - reverse=True)) + self.__widget.showTrackerEntries(sorted(self.__entries.values(), reverse=True)) self.__widget.setCurrentEntry(self.__currentEntry) - + def addTrackerEntry(self, startDateTime, duration, task, comment): """ Public method to add a new tracker entry based on the given data. - + @param startDateTime start date and time @type QDateTime @param duration duration in minutes @@ -304,21 +319,18 @@ """ if not self.__plugin.getPreferences("AllowDuplicates"): startDateTimes = [ - entry.getStartDateTime() for entry in self.__entries.values()] + entry.getStartDateTime() for entry in self.__entries.values() + ] if startDateTime in startDateTimes: return - + if duration < self.__plugin.getPreferences("MinimumDuration"): return - - nextID = ( - max(self.__entries.keys()) + 1 - if len(self.__entries.keys()) else - 0 - ) - + + nextID = max(self.__entries.keys()) + 1 if len(self.__entries.keys()) else 0 + from .TimeTrackEntry import TimeTrackEntry - + entry = TimeTrackEntry(self.__plugin) entry.setID(nextID) entry.setStartDateTime(startDateTime) @@ -326,28 +338,27 @@ entry.setTask(task) entry.setComment(comment) self.__entries[nextID] = entry - + self.__widget.clear() - self.__widget.showTrackerEntries( - sorted(self.__entries.values(), reverse=True)) + self.__widget.showTrackerEntries(sorted(self.__entries.values(), reverse=True)) self.__widget.setCurrentEntry(self.__currentEntry) - + def pauseTrackerEntry(self): """ Public method to pause the current tracker entry. """ self.__currentEntry.pause() - + def continueTrackerEntry(self): """ Public method to continue the current tracker entry. """ self.__currentEntry.continue_() - + def stopTrackerEntry(self): """ Public method to stop the current tracker entry. - + @return tuple of the ID assigned to the stopped tracker entry and the duration @rtype tuple of (int, int) @@ -358,9 +369,7 @@ self.__currentEntry.stop() if self.__currentEntry.isValid(): nextID = ( - max(self.__entries.keys()) + 1 - if len(self.__entries.keys()) else - 0 + max(self.__entries.keys()) + 1 if len(self.__entries.keys()) else 0 ) self.__currentEntry.setID(nextID) self.__entries[nextID] = self.__currentEntry @@ -368,32 +377,32 @@ self.saveTrackerEntries() duration = self.__currentEntry.getDuration() self.__currentEntry = None - + return nextID, duration - + def startTrackerEntry(self): """ Public method to start a new tracker entry. """ from .TimeTrackEntry import TimeTrackEntry - + self.__currentEntry = TimeTrackEntry(self.__plugin) self.__currentEntry.start() self.__widget.setCurrentEntry(self.__currentEntry) - + def getCurrentEntry(self): """ Public method to get a reference to the current tracker entry. - + @return reference to the current entry @rtype TimeTrackEntry """ return self.__currentEntry - + def getEntry(self, eid): """ Public method to get a tracker entry given its ID. - + @param eid ID of the tracker entry @type int @return entry for the given ID or None @@ -403,21 +412,21 @@ return self.__entries[eid] else: return None - + def deleteTrackerEntry(self, eid): """ Public method to delete a tracker entry given its ID. - + @param eid ID of the tracker entry @type int """ if eid in self.__entries: del self.__entries[eid] - + def removeDuplicateTrackerEntries(self): """ Public slot to remove duplicate time tracker entries. - + If entries with the identical start date and time are found, the one with the longest duration is kept. """ @@ -429,24 +438,23 @@ entries[dt] = entry else: entries[dt] = entry - + self.__entries = {} for nextID, entry in enumerate(sorted(entries.values())): entry.setID(nextID) self.__entries[nextID] = entry - + if self.__plugin.getPreferences("AutoSave"): self.saveTrackerEntries() - + self.__widget.clear() - self.__widget.showTrackerEntries( - sorted(self.__entries.values(), reverse=True)) + self.__widget.showTrackerEntries(sorted(self.__entries.values(), reverse=True)) self.__widget.setCurrentEntry(self.__currentEntry) - + def mergeDuplicateTrackerEntries(self): """ Public slot to merge duplicate time tracker entries. - + If entries with the identical start date and time are found, the durations of these entries are added. """ @@ -457,44 +465,43 @@ entries[dt].addDuration(entry.getDuration()) else: entries[dt] = entry - + self.__entries = {} for nextID, entry in enumerate(sorted(entries.values())): entry.setID(nextID) self.__entries[nextID] = entry - + if self.__plugin.getPreferences("AutoSave"): self.saveTrackerEntries() - + self.__widget.clear() - self.__widget.showTrackerEntries( - sorted(self.__entries.values(), reverse=True)) + self.__widget.showTrackerEntries(sorted(self.__entries.values(), reverse=True)) self.__widget.setCurrentEntry(self.__currentEntry) - + def entryChanged(self): """ Public method to indicate an external change to any of the entries. """ if self.__plugin.getPreferences("AutoSave"): self.saveTrackerEntries() - + def getPreferences(self, key): """ Public method to retrieve the various settings. - + @param key key of the value to get @type str @return value of the requested setting @rtype Any """ return self.__plugin.getPreferences(key) - + def __activateWidget(self): """ Private slot to handle the activation of the time tracker widget. """ uiLayoutType = self.__ui.getLayoutType() - + if uiLayoutType == "Toolboxes": self.__ui.hToolboxDock.show() self.__ui.hToolbox.setCurrentWidget(self.__widget)