From bdb8b22ef41824cd52efb8535fa411b07a2b9703 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 8 Oct 2023 20:38:25 -0500 Subject: [PATCH] Add some support for datasync, and deleting times from Harvest --- rattail_harvest/db/model/__init__.py | 5 +-- rattail_harvest/db/model/harvest.py | 45 ---------------------- rattail_harvest/harvest/importing/model.py | 12 +++++- rattail_harvest/harvest/webapi.py | 26 ++++++++++++- 4 files changed, 35 insertions(+), 53 deletions(-) diff --git a/rattail_harvest/db/model/__init__.py b/rattail_harvest/db/model/__init__.py index 0d58bc1..86a099f 100644 --- a/rattail_harvest/db/model/__init__.py +++ b/rattail_harvest/db/model/__init__.py @@ -26,7 +26,4 @@ Harvest integration data models from .harvest import (HarvestCacheUser, HarvestCacheClient, HarvestCacheProject, HarvestCacheTask, - HarvestCacheTimeEntry, - # TODO: deprecate / remove these - HarvestUser, HarvestClient, HarvestProject, - HarvestTask, HarvestTimeEntry) + HarvestCacheTimeEntry) diff --git a/rattail_harvest/db/model/harvest.py b/rattail_harvest/db/model/harvest.py index f3fec9b..4b015d2 100644 --- a/rattail_harvest/db/model/harvest.py +++ b/rattail_harvest/db/model/harvest.py @@ -111,15 +111,6 @@ class HarvestCacheUser(model.Base): return normalize_full_name(self.first_name, self.last_name) -class HarvestUser(HarvestCacheUser): - """ DEPRECATED """ - - def __init__(self, *args, **kwargs): - warnings.warn("HarvestUser class is deprecated; " - "please use HarvestCacheUser instead", - DeprecationWarning, stacklevel=2) - - class HarvestCacheClient(model.Base): """ Represents a client record in Harvest. @@ -152,15 +143,6 @@ class HarvestCacheClient(model.Base): return self.name or '' -class HarvestClient(HarvestCacheClient): - """ DEPRECATED """ - - def __init__(self, *args, **kwargs): - warnings.warn("HarvestClient class is deprecated; " - "please use HarvestCacheClient instead", - DeprecationWarning, stacklevel=2) - - class HarvestCacheProject(model.Base): """ Represents a project record in Harvest. @@ -234,15 +216,6 @@ class HarvestCacheProject(model.Base): return self.name or '' -class HarvestProject(HarvestCacheProject): - """ DEPRECATED """ - - def __init__(self, *args, **kwargs): - warnings.warn("HarvestProject class is deprecated; " - "please use HarvestCacheProject instead", - DeprecationWarning, stacklevel=2) - - class HarvestCacheTask(model.Base): """ Represents a task record in Harvest. @@ -277,15 +250,6 @@ class HarvestCacheTask(model.Base): return self.name or '' -class HarvestTask(HarvestCacheTask): - """ DEPRECATED """ - - def __init__(self, *args, **kwargs): - warnings.warn("HarvestTask class is deprecated; " - "please use HarvestCacheTask instead", - DeprecationWarning, stacklevel=2) - - class HarvestCacheTimeEntry(model.Base): """ Represents a time entry record in Harvest. @@ -361,12 +325,3 @@ class HarvestCacheTimeEntry(model.Base): def __str__(self): return str(self.spent_date or '') - - -class HarvestTimeEntry(HarvestCacheTimeEntry): - """ DEPRECATED """ - - def __init__(self, *args, **kwargs): - warnings.warn("HarvestTimeEntry class is deprecated; " - "please use HarvestCacheTimeEntry instead", - DeprecationWarning, stacklevel=2) diff --git a/rattail_harvest/harvest/importing/model.py b/rattail_harvest/harvest/importing/model.py index 611680d..eacbeb0 100644 --- a/rattail_harvest/harvest/importing/model.py +++ b/rattail_harvest/harvest/importing/model.py @@ -31,7 +31,14 @@ from rattail_harvest.harvest.webapi import make_harvest_webapi class ToHarvest(importing.Importer): def setup(self): - super(ToHarvest, self).setup() + super().setup() + self.setup_webapi() + + def datasync_setup(self): + super().datasync_setup() + self.setup_webapi() + + def setup_webapi(self): self.webapi = make_harvest_webapi(self.config) @@ -158,4 +165,5 @@ class TimeEntryImporter(ToHarvest): if self.dry_run: return True - raise NotImplementedError + self.webapi.delete_time_entry(entry['id']) + return True diff --git a/rattail_harvest/harvest/webapi.py b/rattail_harvest/harvest/webapi.py index 03e57bd..fafdcb8 100644 --- a/rattail_harvest/harvest/webapi.py +++ b/rattail_harvest/harvest/webapi.py @@ -61,6 +61,9 @@ class HarvestWebAPI(object): elif request_method == 'PATCH': response = requests.patch('{}/{}'.format(self.base_url, api_method), headers=headers, params=params) + elif request_method == 'DELETE': + response = requests.delete('{}/{}'.format(self.base_url, api_method), + headers=headers, params=params) else: raise NotImplementedError("unknown request method: {}".format( request_method)) @@ -85,6 +88,12 @@ class HarvestWebAPI(object): """ return self._request('PATCH', api_method, params=params) + def delete(self, api_method, params=None): + """ + Perform a DELETE request for the given API method, and return the response. + """ + return self._request('DELETE', api_method, params=params) + def get_company(self): """ Retrieves the company for the currently authenticated user. @@ -170,8 +179,13 @@ class HarvestWebAPI(object): https://help.getharvest.com/api-v2/timesheets-api/timesheets/time-entries/#retrieve-a-time-entry """ - response = self.get('/time_entries/{}'.format(time_entry_id)) - return response.json() + try: + response = self.get('/time_entries/{}'.format(time_entry_id)) + except requests.exceptions.HTTPError as error: + if error.response.status_code != 404: + raise + else: + return response.json() def create_time_entry(self, **kwargs): """ @@ -208,6 +222,14 @@ class HarvestWebAPI(object): response = self.patch('/time_entries/{}'.format(time_entry_id), params=kwargs) return response.json() + def delete_time_entry(self, time_entry_id, **kwargs): + """ + Delete a time entry. + + https://help.getharvest.com/api-v2/timesheets-api/timesheets/time-entries/#delete-a-time-entry + """ + self.delete('/time_entries/{}'.format(time_entry_id), params=kwargs) + def make_harvest_webapi(config): access_token = config.require('harvest', 'api.access_token')