diff --git a/favro_sync/__main__.py b/favro_sync/__main__.py index 783ddf9..0aa000e 100644 --- a/favro_sync/__main__.py +++ b/favro_sync/__main__.py @@ -1,9 +1,10 @@ import secret_loader +import requests_cache +import tempfile -from .favro_client import FavroClient, SeqId +from .favro_client import FavroClient, SeqId, OrganizationId from .favro_fuse import start_favro_fuse -# Authentication def main(): secrets = secret_loader.SecretLoader() @@ -11,12 +12,15 @@ def main(): favro_username = secrets.load_or_fail('FAVRO_USERNAME') favro_password = secrets.load_or_fail('FAVRO_PASSWORD') - client = FavroClient(favro_org_id=favro_org_id, favro_username=favro_username, favro_password=favro_password) - #card_id = client.get_card_id(SeqId(4714)) - #description = 'TEST' - #client.update_card_description(card_id, description) + with tempfile.TemporaryDirectory(prefix='favro_sync-') as tmpdirname: + session = requests_cache.CachedSession(tmpdirname + '/http-cache.sqlite', expire_after=360) - start_favro_fuse(client) + client = FavroClient(favro_org_id=OrganizationId(favro_org_id), + favro_username=favro_username, + favro_password=favro_password, session=session) + + 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 386355d..29dacb7 100644 --- a/favro_sync/favro_client.py +++ b/favro_sync/favro_client.py @@ -1,5 +1,6 @@ import requests import dataclasses +import datetime from typing import Any import functools from collections.abc import Iterator @@ -16,21 +17,73 @@ class SeqId: 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 seq_id: SeqId + common_id: CommonId + organization_id: OrganizationId + is_archived: bool + name: str + dependencies: list[None] # TODO + tags: list[None] # TODO + todo_list_user_id: UserId | None + todo_list_completed: bool | None + creator_user_id: UserId + creation_date: datetime.datetime + + detailed_description: str | None + + # Derived + seq_id_with_prefix: str - detailed_description: str + + + ''' TODO, fieds: + 'position': -399 + 'listPosition': -399 + + 'isLane': False + 'assignments': [{'userId': 'Faieomp8fuS8DrnyP' 'completed': True}] + 'tasksTotal': 0 + 'tasksDone': 0 + 'attachments': [] + 'customFields': + 'timeOnBoard': None + 'timeOnColumns': None + 'favroAttachments': [] + ''' @staticmethod def from_json(json: dict[str, Any]) -> 'Card': - print(json) return Card( card_id = CardId(json['cardId']), - seq_id = SeqId(json['cardSequentialId']), - seq_id_with_prefix = PREFIX + json['cardSequentialId'], - detailed_description = json['detailedDescription'], + 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']), + todo_list_completed = json['todoListCompleted'], + dependencies = json['dependencies'], + tags = json['tags'], + creator_user_id = UserId(json['createdByUserId']), + creation_date = datetime.datetime.fromisoformat(json['createdAt']), + + seq_id_with_prefix = PREFIX + str(json['sequentialId']), ) # Endpoints @@ -40,27 +93,35 @@ URL_UPDATE_CARD = URL_API_ROOT+'/cards/{card_id}' class FavroClient: - def __init__(self, *, favro_org_id: str, favro_username: str, favro_password: str, + def __init__(self, *, favro_org_id: OrganizationId, favro_username: str, favro_password: str, session: requests.Session | None = None): + + assert favro_org_id is not None + assert favro_username is not None + assert favro_password is not None + # Setup session self.session = session or requests.Session() - self.session.auth = (favro_username, favro_username) + self.session.auth = (favro_username, favro_password) self.session.headers.update({ - 'organizationId': favro_org_id, + 'organizationId': favro_org_id.raw_id, 'content-type': 'application/json', }) + def check_logged_in(self) -> None: + next(self.get_todo_list_cards()) + def get_todo_list_cards(self) -> Iterator[Card]: yield from self.get_cards(todo_list=True) def get_cards(self, *, seqid: SeqId | None = None, todo_list=True) -> Iterator[Card]: # Determine params for get_cards - params = {} + params = {'descriptionFormat': 'markdown'} if seqid: - params['cardSequentialId']= seqid.raw_id + params['cardSequentialId']= str(seqid.raw_id) if todo_list: - params['todoList'] = True + params['todoList'] = 'true' # Run query response = self.session.get(URL_GET_ALL_CARDS, params = params) @@ -75,10 +136,9 @@ class FavroClient: def get_card(self, seqid: SeqId) -> Card: return next(self.get_cards(seqid=seqid)) - @functools.cache def get_card_id(self, seqid: SeqId) -> CardId: - json = self.get_card_json(seqid) - return CardId(json['cardId']) + first_card = next(self.get_cards(seqid = seqid)) + return first_card.card_id def update_card_description(self, card_id: CardId, description: str) -> Card: """Returns updated Card.""" diff --git a/favro_sync/favro_fuse.py b/favro_sync/favro_fuse.py index 4f043c6..b66d54f 100644 --- a/favro_sync/favro_fuse.py +++ b/favro_sync/favro_fuse.py @@ -46,7 +46,7 @@ class FavroFuse(fuse.Fuse): yield fuse.Direntry('..') for card in self.favro_client.get_todo_list_cards(): - yield fuse.Direntry(card.seqid_with_prefix) + yield fuse.Direntry(card.seq_id_with_prefix) def open(self, path: str, flags) -> int: if path != hello_path: