import struct from toggl.TogglPy import Toggl from zei.ZeiDelegate import ZeiDelegate from datetime import datetime, timezone from Mapping import Mapping from plyer import notification from os.path import dirname, realpath, join import dateutil.parser import logging _log = logging.getLogger(__name__) _log.addHandler(logging.StreamHandler()) _log.setLevel(logging.INFO) class TogglDelegate(ZeiDelegate): def __init__(self, periph, config): self.config = config self.toggl = Toggl() self.toggl.setAPIKey(self.config["toggl"]["settings"]["token"]) self._populateProjects() self._populateMappings(self.config["mappings"]) super().__init__(periph) def handleNotification(self, cHandle, data): if cHandle == 38: # Side Change Notification side = struct.unpack("B", data)[0] self._trackProjectByMapping( self.mappings[side] if side in self.mappings else self.mappings[0] ) else: _log.info("Notification from hndl: %s - %r", cHandle, data) def _trackProjectByMapping(self, mapping: Mapping): self._trackProject( description=mapping.description, pid=mapping.id, tags=mapping.tags ) def _trackProject(self, description: str, pid: int, tags: list): current = self.toggl.currentRunningTimeEntry()["data"] if current is not None: if ( datetime.now(timezone.utc) - dateutil.parser.isoparse(current["start"]) ).total_seconds() < 20: # Delete entry if not older than 20s _log.info("Abort currently running entry") self.toggl.deleteTimeEntry(current["id"]) else: _log.info("Stopping currently running entry") self.toggl.stopTimeEntry(current["id"]) if pid not in self.projects: _log.info("Project not found, aborting") return _log.info( "Now tracking project %s: %s (%s)", self.projects[pid]["name"], description, ", ".join(tags if tags else []), ) notification.notify( title="Toggl", message=f"Now tracking project {self.projects[pid]['name']} {description} ({', '.join(tags) if tags else ''})", app_name="Toggl", app_icon=join(dirname(realpath(__file__)), "icons/toggl.png"), timeout=5, ) if pid == 0: return self.toggl.startTimeEntry(description, pid=pid, tags=tags) def _populateMappings(self, mappings: dict): self.mappings = {0: Mapping(0, 0)} for i in mappings: self.mappings[int(i)] = Mapping(int(i), int(mappings[i]["id"])) if "description" in mappings[i]: self.mappings[int(i)].description = mappings[i]["description"] if "tags" in mappings[i]: self.mappings[int(i)].tags = mappings[i]["tags"] def _populateProjects(self): self.projects = {} proj = self.toggl.getWorkspaceProjects( self.config["toggl"]["settings"]["workspace_id"] ) NoneProj = { "id": 0, "wid": int(self.config["toggl"]["settings"]["workspace_id"]), "name": "None", "billable": False, "is_private": True, "active": True, "template": False, "at": "2020-06-09T04:02:38+00:00", "created_at": "2019-12-09T16:36:28+00:00", "color": "9", "auto_estimates": False, "actual_hours": 0, "hex_color": "#990099", } self.projects[0] = NoneProj for i in proj: self.projects[i["id"]] = i