class TogglApi: def __init__(self): self.config = load_config() api_token = self.config['toggl']['token'] self.toggl = Toggl() self.toggl.setAPIKey(api_token) def preset_projects(self): return self.config['toggl']['preset_projects'] def current_timer(self): timer = self.toggl.currentRunningTimeEntry()['data'] if timer: if 'pid' in timer: project = self.get_project_name(timer['pid']) else: project = ['Kein Projekt', (244, 244, 6)] timer_dic = { 'name': timer.get('description', ""), 'id': timer['id'], 'start_time': datetime.fromtimestamp(int(timer['duration']) * -1), 'project_name': project[0], 'project_color': project[1] } # start = datetime.strptime(timer['start'],"%Y-%m-%dT%H:%M:%S+00:00") return timer_dic else: return { 'name': "", 'id': "1234", 'start_time': datetime.fromtimestamp(1542385078), 'project_name': "Kein Projekt", 'project_color': (30, 0, 0) } def get_project_name(self, pid): project = self.toggl.getProject(pid)['data'] name = project['name'] try: color = self.hex_to_rgb(project['hex_color']) color = (int(color[0] / 2), int(color[1] / 2), int(color[2] / 2)) except (): color = (200, 200, 200) return name, color def start_timer(self, project_name, description): print(self.config['toggl']['project_ids'][project_name]) self.toggl.startTimeEntry( description, self.config['toggl']['project_ids'][project_name]) def stop_timer(self): current_timer_id = self.current_timer()['id'] if current_timer_id: self.toggl.stopTimeEntry(current_timer_id) return True return False @staticmethod def hex_to_rgb(hex_str): hex = hex_str[1:] # remove pound sign # https://stackoverflow.com/questions/29643352/converting-hex-to-rgb-value-in-python return tuple(int(hex[i:i + 2], 16) for i in (0, 2, 4))
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
else: continue # %% # chech if now have tasks running. if last_entry['duration'] < 0: # notifi_title = 'Having Running' notifi_content = '⚠Now Have Running Task: ' + last_entry.get( 'description', 'No Description') else: try: description = last_entry.get('description', None) pid = last_entry.get('pid', None) tid = last_entry.get('tid', None) started_entry = toggl.startTimeEntry(description=description, pid=pid, tid=tid) # notifi_title = 'Continue Toggl Success!' notifi_content = '✅Continue Last Entry: ' + started_entry['data'].get( 'description', 'No Description') except HTTPError as e: # notifi_title = 'Continue Toggl Failed' notifi_content = '❌' + e.message print(notifi_content) # os.system(""" # osascript -e 'display notification "{}" with title "{}"' # """.format(notifi_title, notifi_content))
def print_post(): write_log("Received: " + str(dict(request.args))) if "action" not in request.args or len( request.args["action"]) == 0 or request.args["action"] not in [ "start", "stop" ]: err_msg = "ERR: Action not provided or is not 'start' or 'stop'", write_log(err_msg) return err_msg, 200 # 400 if "toggl_api_token" not in request.args or len( request.args["toggl_api_token"]) == 0: err_msg = "ERR: Toggl API token not provided" write_log(err_msg) return err_msg, 200 # 400 action = request.args["action"] api_token = request.args["toggl_api_token"] toggl = Toggl() toggl.setAPIKey(api_token) if action == "stop": currentTimer = toggl.currentRunningTimeEntry() response = toggl.stopTimeEntry(currentTimer['data']['id']) write_log("Received from toggl:" + str(response)) return response, 200 if "desc" not in request.args or len(request.args["desc"]) == 0: err_msg = "ERR: Description not provided" write_log(err_msg) return err_msg, 200 # 400 if "project" not in request.args or len(request.args["project"]) == 0: err_msg = "ERR: Project not provided" write_log(err_msg) return err_msg, 200 # 400 desc = request.args["desc"] project_name = request.args["project"] write_log("Sending request for workspaces") workspaces = toggl.request("https://api.track.toggl.com/api/v8/workspaces") all_projects = [] for w in workspaces: workspace_id = w["id"] resp = toggl.request( f"https://api.track.toggl.com/api/v8/workspaces/{workspace_id}/projects" ) # Resp can be none if a workspace has no projects if resp: all_projects += resp all_project_names = [p["name"] for p in all_projects] if project_name not in all_project_names: err_msg = f"ERR: Project with name `{project_name}` not found." write_log(err_msg) return err_msg, 200 # 404 project_id = -1 for p in all_projects: if p["name"] == project_name: project_id = p["id"] if project_id == -1: err_msg = f"ERR: No project ID can be found for project with name `{project_name}`." write_log(err_msg) return err_msg, 200 # 404 write_log(f"Performing action {action} on entry.") response = toggl.startTimeEntry(desc, project_id) write_log("Received from toggl:" + str(response)) return response, 200