diff --git a/hamstertools/app.py b/hamstertools/app.py index a8fe020..80e101f 100644 --- a/hamstertools/app.py +++ b/hamstertools/app.py @@ -73,9 +73,7 @@ class ListScreen(Screen): class ActivityEditScreen(ModalScreen): - BINDINGS = [ - ("escape", "cancel", "Cancel") - ] + BINDINGS = [("escape", "cancel", "Cancel")] def compose(self) -> ComposeResult: yield Grid( @@ -93,7 +91,7 @@ class ActivityMappingScreen(ModalScreen): BINDINGS = [ ("ctrl+g", "global", "Toggle global"), ("ctrl+s", "save", "Save"), - ("escape", "cancel", "Cancel") + ("escape", "cancel", "Cancel"), ] customer_id = None @@ -111,12 +109,8 @@ class ActivityMappingScreen(ModalScreen): return sorted(matches, key=lambda v: v.main.plain.startswith(value.lower())) def _get_customers(self, input_state): - customers = [ - DropdownItem(c.name, str(c.id)) - for c in KimaiCustomer.select() - ] - return ActivityMappingScreen._filter_dropdowns(customers, - input_state.value) + customers = [DropdownItem(c.name, str(c.id)) for c in KimaiCustomer.select()] + return ActivityMappingScreen._filter_dropdowns(customers, input_state.value) def _get_projects(self, input_state): projects = [ @@ -125,24 +119,21 @@ class ActivityMappingScreen(ModalScreen): KimaiProject.customer_id == self.customer_id ) ] - return ActivityMappingScreen._filter_dropdowns(projects, - input_state.value) + return ActivityMappingScreen._filter_dropdowns(projects, input_state.value) def _get_activities(self, input_state): activities = KimaiActivity.select() - if self.query_one('#global').value: + if self.query_one("#global").value: activities = activities.where( KimaiActivity.project_id.is_null(), ) else: - activities = activities.where( - KimaiActivity.project_id == self.project_id - ) + activities = activities.where(KimaiActivity.project_id == self.project_id) - return ActivityMappingScreen._filter_dropdowns([ - DropdownItem(a.name, str(a.id)) - for a in activities], input_state.value) + return ActivityMappingScreen._filter_dropdowns( + [DropdownItem(a.name, str(a.id)) for a in activities], input_state.value + ) def compose(self) -> ComposeResult: yield Vertical( @@ -154,78 +145,88 @@ class ActivityMappingScreen(ModalScreen): AutoComplete( Input(placeholder="Type to search...", id="customer"), Dropdown(items=self._get_customers), - ) + ), ), Horizontal( Label("Project"), AutoComplete( - Input(placeholder="Type to search...", id='project'), + Input(placeholder="Type to search...", id="project"), Dropdown(items=self._get_projects), - ) + ), ), Horizontal( Label("Activity"), AutoComplete( - Input(placeholder="Type to search...", id='activity'), + Input(placeholder="Type to search...", id="activity"), Dropdown(items=self._get_activities), - ) + ), ), Horizontal( Label("Description"), - Input(id='description'), + Input(id="description"), ), Horizontal( Label("Tags"), - Input(id='tags'), + Input(id="tags"), ), - Horizontal(Checkbox("Global", id='global')), + Horizontal(Checkbox("Global", id="global")), ) - @on(Input.Submitted, '#customer') + @on(Input.Submitted, "#customer") def customer_submitted(self, event): if event.control.parent.dropdown.selected_item is not None: - self.customer_id = str(event.control.parent.dropdown.selected_item.left_meta) - self.query_one('#project').focus() + self.customer_id = str( + event.control.parent.dropdown.selected_item.left_meta + ) + self.query_one("#project").focus() - @on(DescendantBlur, '#customer') + @on(DescendantBlur, "#customer") def customer_blur(self, event): if event.control.parent.dropdown.selected_item is not None: - self.customer_id = str(event.control.parent.dropdown.selected_item.left_meta) + self.customer_id = str( + event.control.parent.dropdown.selected_item.left_meta + ) - @on(Input.Submitted, '#project') + @on(Input.Submitted, "#project") def project_submitted(self, event): if event.control.parent.dropdown.selected_item is not None: self.project_id = str(event.control.parent.dropdown.selected_item.left_meta) - self.query_one('#activity').focus() + self.query_one("#activity").focus() - @on(DescendantBlur, '#project') + @on(DescendantBlur, "#project") def project_blur(self, event): if event.control.parent.dropdown.selected_item is not None: self.project_id = str(event.control.parent.dropdown.selected_item.left_meta) - @on(Input.Submitted, '#activity') + @on(Input.Submitted, "#activity") def activity_submitted(self, event): if event.control.parent.dropdown.selected_item is not None: - self.activity_id = str(event.control.parent.dropdown.selected_item.left_meta) - self.query_one('#activity').focus() + self.activity_id = str( + event.control.parent.dropdown.selected_item.left_meta + ) + self.query_one("#activity").focus() - @on(DescendantBlur, '#activity') + @on(DescendantBlur, "#activity") def activity_blur(self, event): if event.control.parent.dropdown.selected_item is not None: - self.activity_id = str(event.control.parent.dropdown.selected_item.left_meta) + self.activity_id = str( + event.control.parent.dropdown.selected_item.left_meta + ) def action_global(self): - self.query_one('#global').value = not self.query_one('#global').value + self.query_one("#global").value = not self.query_one("#global").value def action_save(self): - self.dismiss({ - 'kimai_customer_id': self.customer_id, - 'kimai_project_id': self.project_id, - 'kimai_activity_id': self.activity_id, - 'kimai_description': self.query_one('#description').value, - 'kimai_tags': self.query_one('#tags').value, - 'global': self.query_one('#global').value, - }) + self.dismiss( + { + "kimai_customer_id": self.customer_id, + "kimai_project_id": self.project_id, + "kimai_activity_id": self.activity_id, + "kimai_description": self.query_one("#description").value, + "kimai_tags": self.query_one("#tags").value, + "global": self.query_one("#global").value, + } + ) def action_cancel(self): self.dismiss(None) @@ -365,27 +366,38 @@ class ActivityListScreen(ListScreen): self.app.push_screen(ActivityEditScreen(), handle_edit) def action_mapping(self): - selected_activity = HamsterActivity.select( - HamsterActivity, - fn.COALESCE(HamsterCategory.name, "None").alias("category_name"), - ).join(HamsterCategory, JOIN.LEFT_OUTER).where( - HamsterActivity.id == self.table.get_cell_at( - Coordinate(self.table.cursor_coordinate.row, 2), + selected_activity = ( + HamsterActivity.select( + HamsterActivity, + fn.COALESCE(HamsterCategory.name, "None").alias("category_name"), ) - ).get() + .join(HamsterCategory, JOIN.LEFT_OUTER) + .where( + HamsterActivity.id + == self.table.get_cell_at( + Coordinate(self.table.cursor_coordinate.row, 2), + ) + ) + .get() + ) def handle_mapping(mapping): if mapping is None: return - m = HamsterKimaiMapping.create(hamster_activity=selected_activity, **mapping) + m = HamsterKimaiMapping.create( + hamster_activity=selected_activity, **mapping + ) m.save() filter_input = self.query_one("#filter") self._refresh(filter_input.value) - self.app.push_screen(ActivityMappingScreen( - category=selected_activity.category_name, - activity=selected_activity.name - ), handle_mapping) + self.app.push_screen( + ActivityMappingScreen( + category=selected_activity.category_name, + activity=selected_activity.name, + ), + handle_mapping, + ) class CategoryListScreen(ListScreen): diff --git a/hamstertools/kimai.py b/hamstertools/kimai.py index 8c0f51d..a36aeff 100644 --- a/hamstertools/kimai.py +++ b/hamstertools/kimai.py @@ -9,24 +9,24 @@ class NotFound(Exception): class KimaiAPI(object): # temporary hardcoded config - KIMAI_API_URL = 'https://kimai.autonomic.zone/api' + KIMAI_API_URL = "https://kimai.autonomic.zone/api" - KIMAI_USERNAME = '3wordchant' - KIMAI_API_KEY = os.environ['KIMAI_API_KEY'] + KIMAI_USERNAME = "3wordchant" + KIMAI_API_KEY = os.environ["KIMAI_API_KEY"] - auth_headers = { - 'X-AUTH-USER': KIMAI_USERNAME, - 'X-AUTH-TOKEN': KIMAI_API_KEY - } + auth_headers = {"X-AUTH-USER": KIMAI_USERNAME, "X-AUTH-TOKEN": KIMAI_API_KEY} def __init__(self): - requests_cache.install_cache('kimai', backend='sqlite', expire_after=1800) + requests_cache.install_cache("kimai", backend="sqlite", expire_after=1800) self.customers_json = requests.get( - f'{self.KIMAI_API_URL}/customers?visible=3', headers=self.auth_headers).json() + f"{self.KIMAI_API_URL}/customers?visible=3", headers=self.auth_headers + ).json() self.projects_json = requests.get( - f'{self.KIMAI_API_URL}/projects?visible=3', headers=self.auth_headers).json() + f"{self.KIMAI_API_URL}/projects?visible=3", headers=self.auth_headers + ).json() self.activities_json = requests.get( - f'{self.KIMAI_API_URL}/activities?visible=3', headers=self.auth_headers).json() + f"{self.KIMAI_API_URL}/activities?visible=3", headers=self.auth_headers + ).json() class BaseAPI(object): @@ -41,19 +41,17 @@ class Customer(BaseAPI): @staticmethod def list(api): - return [ - Customer(api, c['id'], c['name']) for c in api.customers_json - ] + return [Customer(api, c["id"], c["name"]) for c in api.customers_json] @staticmethod def get_by_id(api, id): for value in api.customers_json: - if value['id'] == id: - return Customer(api, value['id'], value['name']) + if value["id"] == id: + return Customer(api, value["id"], value["name"]) raise NotFound() def __repr__(self): - return f'Customer (id={self.id}, name={self.name})' + return f"Customer (id={self.id}, name={self.name})" class Project(BaseAPI): @@ -63,20 +61,25 @@ class Project(BaseAPI): @staticmethod def list(api): return [ - Project(api, p['id'], p['name'], Customer.get_by_id(api, p['customer'])) for p in api.projects_json + Project(api, p["id"], p["name"], Customer.get_by_id(api, p["customer"])) + for p in api.projects_json ] @staticmethod def get_by_id(api, id, none=False): for value in api.projects_json: - if value['id'] == id: - return Project(api, value['id'], value['name'], - Customer.get_by_id(api, value['customer'])) + if value["id"] == id: + return Project( + api, + value["id"], + value["name"], + Customer.get_by_id(api, value["customer"]), + ) if not none: raise NotFound() def __repr__(self): - return f'Project (id={self.id}, name={self.name}, customer={self.customer})' + return f"Project (id={self.id}, name={self.name}, customer={self.customer})" class Activity(BaseAPI): @@ -86,20 +89,24 @@ class Activity(BaseAPI): @staticmethod def list(api): return [ - Activity(api, a['id'], a['name'], Project.get_by_id(api, - a['project'], - none=True)) + Activity( + api, a["id"], a["name"], Project.get_by_id(api, a["project"], none=True) + ) for a in api.activities_json ] @staticmethod def get_by_id(api, id, none=False): for value in api.activities_json: - if value['id'] == id: - return Activity(api, value['id'], value['name'], - Project.get_by_id(api, value['project'])) + if value["id"] == id: + return Activity( + api, + value["id"], + value["name"], + Project.get_by_id(api, value["project"]), + ) if not none: raise NotFound() def __repr__(self): - return f'Activity (id={self.id}, name={self.name}, project={self.project})' + return f"Activity (id={self.id}, name={self.name}, project={self.project})"