From b2c32881df38fc3f62db4c0010ee14007976095a Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Thu, 26 Sep 2024 23:08:37 +0200 Subject: [PATCH] Ruff --- favro_sync/__main__.py | 30 ++++++++------ favro_sync/favro_client.py | 75 +++++++++++++++++++--------------- favro_sync/favro_data_model.py | 47 ++++++++++----------- favro_sync/favro_fuse.py | 62 ++++++++++++++++++---------- setup.py | 3 +- 5 files changed, 124 insertions(+), 93 deletions(-) diff --git a/favro_sync/__main__.py b/favro_sync/__main__.py index 3c2311b..67c735c 100644 --- a/favro_sync/__main__.py +++ b/favro_sync/__main__.py @@ -1,8 +1,8 @@ -import secret_loader -import requests_cache -import tempfile -from .favro_client import FavroClient, SeqId, OrganizationId +import requests_cache +import secret_loader + +from .favro_client import FavroClient, OrganizationId from .favro_fuse import start_favro_fuse @@ -14,20 +14,24 @@ def main(): read_only = False - #with tempfile.TemporaryDirectory(prefix='favro_sync_') as tmpdirname: - tmpdirname = './output' # TODO + # with tempfile.TemporaryDirectory(prefix='favro_sync_') as tmpdirname: + tmpdirname = './output' # TODO if True: - session = requests_cache.CachedSession(tmpdirname + '/http-cache.sqlite', expire_after=360) + session = requests_cache.CachedSession( + tmpdirname + '/http-cache.sqlite', expire_after=360, + ) - client = FavroClient(favro_org_id=OrganizationId(favro_org_id), - favro_username=favro_username, - favro_password=favro_password, - session=session, - read_only=read_only, - ) + client = FavroClient( + favro_org_id=OrganizationId(favro_org_id), + favro_username=favro_username, + favro_password=favro_password, + session=session, + read_only=read_only, + ) client.check_logged_in() start_favro_fuse(client) + if __name__ == '__main__': main() diff --git a/favro_sync/favro_client.py b/favro_sync/favro_client.py index 4df4d6a..c3fc5f6 100644 --- a/favro_sync/favro_client.py +++ b/favro_sync/favro_client.py @@ -1,25 +1,28 @@ -import requests -import dataclasses -import datetime -from typing import Any -import functools from collections.abc import Iterator from logging import getLogger -from .favro_data_model import Card, CardId, SeqId, OrganizationId +import requests + +from .favro_data_model import Card, CardId, OrganizationId, SeqId logger = getLogger(__name__) # Endpoints URL_API_ROOT = 'https://favro.com/api/v1' -URL_GET_ALL_CARDS = URL_API_ROOT+'/cards' -URL_UPDATE_CARD = URL_API_ROOT+'/cards/{card_id}' +URL_GET_ALL_CARDS = URL_API_ROOT + '/cards' +URL_UPDATE_CARD = URL_API_ROOT + '/cards/{card_id}' + class FavroClient: - - def __init__(self, *, favro_org_id: OrganizationId, favro_username: str, favro_password: str, - session: requests.Session | None = None, read_only=True): - + def __init__( + self, + *, + favro_org_id: OrganizationId, + favro_username: str, + favro_password: str, + session: requests.Session | None = None, + read_only=True, + ): assert favro_org_id is not None assert favro_username is not None assert favro_password is not None @@ -27,10 +30,12 @@ class FavroClient: # Setup session self.session = session or requests.Session() self.session.auth = (favro_username, favro_password) - self.session.headers.update({ + self.session.headers.update( + { 'organizationId': favro_org_id.raw_id, 'content-type': 'application/json', - }) + }, + ) self.read_only = read_only self.card_id_to_seq_id: dict[CardId, SeqId] = {} @@ -42,8 +47,9 @@ class FavroClient: def get_todo_list_cards(self) -> Iterator[Card]: yield from self.get_cards(todo_list=True) - - def get_cards(self, *, seq_id: SeqId | None = None, todo_list=False) -> Iterator[Card]: + def get_cards( + self, *, seq_id: SeqId | None = None, todo_list=False, + ) -> Iterator[Card]: # Determine params for get_cards request = self._get_cards_request(seq_id, todo_list) @@ -60,14 +66,16 @@ class FavroClient: yield card del entity_json - def _get_cards_request(self, seq_id: SeqId | None = None, todo_list=False) -> requests.PreparedRequest: + def _get_cards_request( + self, seq_id: SeqId | None = None, todo_list=False, + ) -> requests.PreparedRequest: params = {'descriptionFormat': 'markdown'} if seq_id is not None: - params['cardSequentialId']= str(seq_id.raw_id) + params['cardSequentialId'] = str(seq_id.raw_id) if todo_list is True: params['todoList'] = 'true' - request = requests.Request('GET', URL_GET_ALL_CARDS, params = params) + request = requests.Request('GET', URL_GET_ALL_CARDS, params=params) return self.session.prepare_request(request) def get_card(self, seq_id: SeqId) -> Card: @@ -76,35 +84,36 @@ class FavroClient: def get_card_id(self, seq_id: SeqId) -> CardId: if card_id := self.seq_id_to_card_id[seq_id]: return card_id - first_card = next(self.get_cards(seq_id = seq_id)) + first_card = next(self.get_cards(seq_id=seq_id)) return first_card.card_id def _invalidate_cache(self, card_id: CardId) -> None: - self.session.cache.delete(requests=[ - self._get_cards_request(seq_id=self.card_id_to_seq_id[card_id]) - ]) - + self.session.cache.delete( + requests=[self._get_cards_request(seq_id=self.card_id_to_seq_id[card_id])], + ) def update_card_description(self, card_id: CardId, description: str) -> Card: """Returns updated Card.""" if self.read_only == 'silent': - logger.warning('FavroClient is silent read only: Discarding card description update of length %d', - len(description)) - return None # TODO + logger.warning( + 'FavroClient is silent read only: Discarding card description update of length %d', + len(description), + ) + return None # TODO elif self.read_only is True: raise Exception('FavroClient is read only') json_body = { - 'detailedDescription': description, - 'descriptionFormat': 'markdown', + 'detailedDescription': description, + 'descriptionFormat': 'markdown', } logger.warning('Sending body: %s', json_body) - response = self.session.put(URL_UPDATE_CARD.format(card_id=card_id.raw_id), json=json_body) + response = self.session.put( + URL_UPDATE_CARD.format(card_id=card_id.raw_id), json=json_body, + ) response.raise_for_status() - logger.warning("Response: %s", response.json()) + logger.warning('Response: %s', response.json()) self._invalidate_cache(card_id) return Card.from_json(response.json()) - - diff --git a/favro_sync/favro_data_model.py b/favro_sync/favro_data_model.py index 21d7301..2f3125d 100644 --- a/favro_sync/favro_data_model.py +++ b/favro_sync/favro_data_model.py @@ -1,32 +1,33 @@ -import requests import dataclasses import datetime from typing import Any -import functools -from collections.abc import Iterator -from logging import getLogger @dataclasses.dataclass(frozen=True) class SeqId: raw_id: int + @dataclasses.dataclass(frozen=True) class CardId: raw_id: str + @dataclasses.dataclass(frozen=True) class CommonId: raw_id: str + @dataclasses.dataclass(frozen=True) class UserId: raw_id: str + @dataclasses.dataclass(frozen=True) class OrganizationId: raw_id: str + @dataclasses.dataclass(frozen=True) class Card: card_id: CardId @@ -35,8 +36,8 @@ class Card: organization_id: OrganizationId is_archived: bool name: str - dependencies: list[None] # TODO - tags: list[None] # TODO + dependencies: list[None] # TODO + tags: list[None] # TODO todo_list_user_id: UserId | None todo_list_completed: bool | None creator_user_id: UserId @@ -44,7 +45,7 @@ class Card: detailed_description: str | None - ''' TODO, fieds: + """ TODO, fieds: 'position': -399 'listPosition': -399 @@ -57,24 +58,24 @@ class Card: 'timeOnBoard': None 'timeOnColumns': None 'favroAttachments': [] - ''' + """ @staticmethod def from_json(json: dict[str, Any]) -> 'Card': return Card( - card_id = CardId(json['cardId']), - seq_id = SeqId(json['sequentialId']), - common_id = CommonId(json['cardCommonId']), - detailed_description = json.get('detailedDescription'), - is_archived = json['archived'], - organization_id = OrganizationId(json['organizationId']), - name = json['name'], - todo_list_user_id = UserId(json['todoListUserId' ]) if 'todoListUserId' in json else None, - todo_list_completed = json.get('todoListCompleted'), - dependencies = json['dependencies'], - tags = json['tags'], - creator_user_id = UserId(json['createdByUserId']), - creation_date = datetime.datetime.fromisoformat(json['createdAt']), + card_id=CardId(json['cardId']), + seq_id=SeqId(json['sequentialId']), + common_id=CommonId(json['cardCommonId']), + detailed_description=json.get('detailedDescription'), + is_archived=json['archived'], + organization_id=OrganizationId(json['organizationId']), + name=json['name'], + todo_list_user_id=UserId(json['todoListUserId']) + if 'todoListUserId' in json + else None, + todo_list_completed=json.get('todoListCompleted'), + dependencies=json['dependencies'], + tags=json['tags'], + creator_user_id=UserId(json['createdByUserId']), + creation_date=datetime.datetime.fromisoformat(json['createdAt']), ) - - diff --git a/favro_sync/favro_fuse.py b/favro_sync/favro_fuse.py index e03e6df..4a2f0e3 100644 --- a/favro_sync/favro_fuse.py +++ b/favro_sync/favro_fuse.py @@ -1,17 +1,20 @@ -import os, stat, errno, fuse -from pathlib import Path -import re import dataclasses +import errno +import re +import stat from collections.abc import Iterator -from .favro_data_model import CardId, SeqId, Card +import fuse + from .favro_client import FavroClient +from .favro_data_model import Card, SeqId fuse.fuse_python_api = (0, 2) hello_path = '/hello' hello_str = b'Hello World!\n' + class MyStat(fuse.Stat): def __init__(self): self.st_mode = 0 @@ -25,12 +28,13 @@ class MyStat(fuse.Stat): self.st_mtime = 0 self.st_ctime = 0 + CARD_FILENAME_FORMAT = 'PAR-{seq_id}.md' CARD_FILENAME_REGEX = r'^\/PAR\-(\d+)\.md$' + @dataclasses.dataclass(frozen=True) class Thing: - @staticmethod def from_path(path_str: str) -> 'Thing | None': if path_str == '/': @@ -39,25 +43,28 @@ class Thing: return CardThing(SeqId(int(m.group(1)))) return None + @dataclasses.dataclass(frozen=True) class RootThing(Thing): pass + @dataclasses.dataclass(frozen=True) class CardThing(Thing): seq_id: SeqId + def card_to_contents(card: Card) -> str: ls = [] - #ls.append('# ') - #ls.append(card.name) - #ls.append('\n\n') + # ls.append('# ') + # ls.append(card.name) + # ls.append('\n\n') ls.append(card.detailed_description or '') return ''.join(ls) + class FavroFuse(fuse.Fuse): - '''Favro Filesystem in Userspace. - ''' + """Favro Filesystem in Userspace.""" def __init__(self, favro_client: FavroClient, **kwargs): self.favro_client = favro_client @@ -77,7 +84,7 @@ class FavroFuse(fuse.Fuse): st.st_nlink = 1 st.st_size = len(card_to_contents(card)) st.st_ctime = int(card.creation_date.timestamp()) - st.st_mtime = st.st_ctime # TODO + st.st_mtime = st.st_ctime # TODO else: return -errno.ENOENT return st @@ -93,8 +100,8 @@ class FavroFuse(fuse.Fuse): thing = Thing.from_path(path) if not isinstance(thing, CardThing): return -errno.ENOENT - #accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR - #if (flags & accmode) != os.O_RDONLY: return -errno.EACCES + # accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR + # if (flags & accmode) != os.O_RDONLY: return -errno.EACCES return None def read(self, path: str, size: int, offset: int) -> bytes | int: @@ -112,7 +119,7 @@ class FavroFuse(fuse.Fuse): if offset < slen: if offset + size > slen: size = slen - offset - buf = contents[offset:offset+size] + buf = contents[offset : offset + size] else: buf = b'' return buf @@ -159,20 +166,31 @@ class FavroFuse(fuse.Fuse): # Return amount written return 0 -def splice(original_buffer: bytes, input_buffer: bytes, offset: int) -> bytes: - return original_buffer[0:offset-1] + input_buffer + original_buffer[offset+len(input_buffer)+1:len(original_buffer)] -HELP=""" +def splice(original_buffer: bytes, input_buffer: bytes, offset: int) -> bytes: + return ( + original_buffer[0 : offset - 1] + + input_buffer + + original_buffer[offset + len(input_buffer) + 1 : len(original_buffer)] + ) + + +HELP = ( + """ Userspace hello example -""" + fuse.Fuse.fusage +""" + + fuse.Fuse.fusage +) + def start_favro_fuse(favro_client: FavroClient): # TODO: server = FavroFuse( - favro_client = favro_client, - version='%prog ' + fuse.__version__, - usage=HELP, - dash_s_do='setsingle') + favro_client=favro_client, + version='%prog ' + fuse.__version__, + usage=HELP, + dash_s_do='setsingle', + ) server.parse(errex=1) server.main() diff --git a/setup.py b/setup.py index 6585d92..e0c504f 100644 --- a/setup.py +++ b/setup.py @@ -38,8 +38,7 @@ REQUIREMENTS_MAIN = [ 'requests', 'secret_loader @ git+https://gitfub.space/Jmaa/secret_loader', ] -REQUIREMENTS_TEST = [ -] +REQUIREMENTS_TEST = [] setup(