From 03c46b149db9058a71106444b81d1044706e81c6 Mon Sep 17 00:00:00 2001 From: Jan Kubant Date: Thu, 16 Feb 2023 09:56:11 +0100 Subject: [PATCH] Add conda to project and add a setting to allow to set billable tag based on project property. And couple of renamings. --- ClockifyAPI.py | 44 ++++++++++++++++++++++++-------------------- Makefile | 4 ++++ README.md | 3 +-- conda.yml | 20 ++++++++++++++++++++ main.py | 12 +++++++++--- 5 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 Makefile create mode 100644 conda.yml diff --git a/ClockifyAPI.py b/ClockifyAPI.py index 2d955fa..c38e82b 100644 --- a/ClockifyAPI.py +++ b/ClockifyAPI.py @@ -270,7 +270,7 @@ def getTasksOnProject(self, workspace, projectName): self._loadAdmin() wsId = self.getWorkspaceID(workspace) - pId = self.getProjectID(projectName, workspace) + pId = self.get_project_id(projectName, workspace) url = self.url + "/workspaces/%s/projects/%s/tasks" % (wsId, pId) self.pTasks = self.multiGetRequest(url) @@ -303,16 +303,16 @@ def getClientID(self, client, workspace, skipCliQuery=False): raise RuntimeError("Client %s not found in workspace %s" % (client, workspace)) return clId - def getProjects(self, workspace, skipPrjQuery=False): - if self._syncProjects == True: - curUser = self._loadedUserEmail + def getProjects(self, workspace_name: str, skipPrjQuery=False): + if self._syncProjects: + cur_user = self._loadedUserEmail self.projects = [] for user in self._APIusers: self.logger.info("synchronizing clockify projects for user %s..." % user["email"]) self._loadUser(user["email"]) - wsId = self.getWorkspaceID(workspace) + wsId = self.getWorkspaceID(workspace_name) url = self.url + "/workspaces/%s/projects" % wsId projects = self.multiGetRequest(url) self.projects.extend(projects) @@ -321,7 +321,7 @@ def getProjects(self, workspace, skipPrjQuery=False): f = open("clockify_projects.json", "w") f.write(json.dumps(self.projects, indent=2)) f.close() - self._loadUser(curUser) + self._loadUser(cur_user) self._syncProjects = False return self.projects @@ -351,19 +351,23 @@ def getWorkspaceProjects(self, workspace, skipPrjQuery=False): return self.projects - def getProjectID(self, project, workspace, skipPrjQuery=False): - pId = None - if skipPrjQuery: + def get_project(self, project_name: str, workspace_name: str, skip_prj_query=False): + if skip_prj_query: projects = self.projects else: - projects = self.getProjects(workspace, skipPrjQuery) + projects = self.getProjects(workspace_name, skip_prj_query) + + project_candidates = [] + for project in projects: + if project_name == project["name"]: + project_candidates.append(project) + + if len(project_candidates) == 1: + return project_candidates[0] + raise ValueError(f"project {project_name} not found or multiple projects with same name found") - for p in projects: - if p["name"] == project: - pId = p["id"] - if pId == None: - raise RuntimeError("Project %s not found in workspace %s" % (project, workspace)) - return pId + def get_project_id(self, project_name, workspace, skip_prj_query=False): + return self.get_project(project_name, workspace, skip_prj_query)["id"] def getUsers(self, workspace): if self._syncUsers == True: @@ -669,7 +673,7 @@ def addEntry(self, start, description, projectName, userMail, workspace, url = self.url + "/workspaces/%s/time-entries" % wsId if projectName != None: - projectId = self.getProjectID(projectName, workspace, skipPrjQuery=self._syncProjects) + projectId = self.get_project_id(projectName, workspace, skip_prj_query=self._syncProjects) if taskName != None: pTasks = self.getTasksOnProject(workspace, projectName) taskId = self.getTaskIdFromTasks(taskName, pTasks) @@ -770,7 +774,7 @@ def getTimeEntryForUser(self, userMail, workspace, description, uId = self.userID if projectName != None: - prjID = self.getProjectID(projectName, workspace) + prjID = self.get_project_id(projectName, workspace) if start != None: start = start.strftime('%Y-%m-%dT%H:%M:%SZ') @@ -795,7 +799,7 @@ def getTimeEntryForUser(self, userMail, workspace, description, def archiveProject(self, projectName, workspace, skipPrjQuery=False): wsId = self.getWorkspaceID(workspace) - pID = self.getProjectID(projectName, workspace, skipPrjQuery=skipPrjQuery) + pID = self.get_project_id(projectName, workspace, skip_prj_query=skipPrjQuery) url = "https://api.clockify.me/api/workspaces/%s/projects/%s/archive" % (wsId, pID) rv = self._request(url, typ="GET") if rv.status_code == 200: @@ -841,7 +845,7 @@ def deleteEntry(self, entryID, workspace): def deleteProject(self, projectName, workspace, skipPrjQuery=False): wsId = self.getWorkspaceID(workspace) - projectID = self.getProjectID(projectName, workspace, skipPrjQuery) + projectID = self.get_project_id(projectName, workspace, skipPrjQuery) url = self.url + "/workspaces/%s/projects/%s" % (wsId, projectID) rv = self._request(url, typ="DELETE") if rv.ok: diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e07ce25 --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +CONDA_ENV=clockify-automation + +conda-create: + conda env create -f conda.yml --name $(CONDA_ENV) \ No newline at end of file diff --git a/README.md b/README.md index 512923f..a9cefe2 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ Credits to Markus Proeller, markus.proeller@pieye.org. ## Setup -* Create virtual env: `python -m venv venv` and activate it: `. ./venv/bin/activate `. -* Install requirements: `pip install -r requirements.txt`. +* Create virtual env in conda, use `make conda-create` then activate the environment via `conda activate clockify-automation` * Set proper values in `config.json`, use `config_example.json` as a base. * Run it: `python main.py` diff --git a/conda.yml b/conda.yml new file mode 100644 index 0000000..872ce07 --- /dev/null +++ b/conda.yml @@ -0,0 +1,20 @@ +name: txmatching +channels: + - conda-forge +dependencies: + - autopep8=2.0.0 + - pip=22.3.1 + - python=3.10.5 + - pip: + - arrow==0.17.0 + - certifi==2020.12.5 + - chardet==4.0.0 + - click==7.1.2 + - idna==2.10 + - python-dateutil==2.8.1 + - pytz==2020.5 + - requests==2.25.1 + - six==1.15.0 + - slumber==0.7.1 + - TogglPy==0.1.1 + - urllib3==1.26.5 \ No newline at end of file diff --git a/main.py b/main.py index 1e2019b..61973c7 100644 --- a/main.py +++ b/main.py @@ -123,7 +123,7 @@ def main(): ) clockify = ClockifyAPI(clockify_settings.token, clockify_settings.email, reqTimeout=1) - clockify.getProjects(workspace=clockify_settings.workspace) + clockify.getProjects(workspace_name=clockify_settings.workspace) toggle_base_url = 'https://api.track.toggl.com/api/v9' @@ -177,8 +177,14 @@ def main(): tags = [tag.strip() for tag in row['tags'] if tag.strip() != ''] - # tag billable if there's a tag billable - billable = 'billable' in tags + # tag billable if there's a tag else set default for the project + project = clockify.get_project(row['project_name'], clockify_settings.workspace) + if 'billable' in tags: + billable = True + elif 'non-billable' in tags: + billable = False + else: + billable = project["billable"] # remove billable and non-billable tags as we don't need them anymore tags = [tag for tag in tags if tag not in {'non-billable', 'billable'}]