diff --git a/favro_sync/favro_client.py b/favro_sync/favro_client.py index ebfe7ca..b41a0e3 100644 --- a/favro_sync/favro_client.py +++ b/favro_sync/favro_client.py @@ -102,32 +102,47 @@ class FavroClient: seq_id: SeqId | None = None, todo_list=False, ) -> Iterator[Card]: - # Determine params for get_cards - request = self._get_cards_request(seq_id, todo_list) + page = 0 + request_id = None + num_pages = 1 - # Run query - logger.warning('Sending request: %s', request.url) - response = self.session.send(request) - response.raise_for_status() - json = response.json() + while page < num_pages: + # Determine params for get_cards + request = self._get_cards_request(seq_id, todo_list, page=page, request_id=request_id) - # TODO: Add support for pageination - for entity_json in json['entities']: - card = Card.from_json(entity_json) - self.cache.add_card(card) - yield card - del entity_json + # Run query + logger.warning('Sending request: %s', request.url) + response = self.session.send(request) + response.raise_for_status() + json = response.json() + + for entity_json in json['entities']: + card = Card.from_json(entity_json) + self.cache.add_card(card) + yield card + del entity_json + + # Pagination bookkeeping + page = json['page'] + 1 + request_id = json['requestId'] + num_pages = json['pages'] def _get_cards_request( self, seq_id: SeqId | None = None, - todo_list=False, + todo_list: bool=False, + request_id: None | str = None, + page: None | int = None, ) -> requests.PreparedRequest: params = {'descriptionFormat': 'markdown'} if seq_id is not None: params['cardSequentialId'] = str(seq_id.raw_id) if todo_list is True: params['todoList'] = 'true' + if request_id: + params['requestId'] = request_id + if page: + params['page'] = page request = requests.Request('GET', URL_GET_ALL_CARDS, params=params) return self.session.prepare_request(request) @@ -192,6 +207,7 @@ class FavroClient: 'name': card_contents.name, 'detailedDescription': card_contents.description, 'descriptionFormat': 'markdown', + 'archived': card_contents.archived, } url = URL_UPDATE_CARD.format(card_id=card_id.raw_id) diff --git a/favro_sync/favro_data_model.py b/favro_sync/favro_data_model.py index 2fab314..cb6468a 100644 --- a/favro_sync/favro_data_model.py +++ b/favro_sync/favro_data_model.py @@ -94,13 +94,13 @@ class TagInfo: @dataclasses.dataclass(frozen=True) class CustomField: custom_field_id: CustomFieldId - value: list[str] # TODO + value: list[str] | str @staticmethod def from_json(json: dict[str, Any]) -> 'CustomField': return CustomField( CustomFieldId(json['customFieldId']), - json['value'], # TODO + json.get('value'), ) @@ -137,14 +137,14 @@ class Card: todo_list_completed: bool | None creator_user_id: UserId creation_date: datetime.datetime + start_date: datetime.date | None + due_date: datetime.date | None attachments: list[dict] detailed_description: str | None + archived: bool @staticmethod def from_json(json: dict[str, Any]) -> 'Card': - todo_list_user_id = ( - UserId(json['todoListUserId']) if 'todoListUserId' in json else None - ) return Card( card_id=CardId(json['cardId']), seq_id=SeqId(json['sequentialId']), @@ -153,17 +153,25 @@ class Card: is_archived=json['archived'], organization_id=OrganizationId(json['organizationId']), name=json['name'], - todo_list_user_id=todo_list_user_id, + todo_list_user_id=map_opt(UserId, json.get('todoListUserId')), todo_list_completed=json.get('todoListCompleted'), dependencies=[ CardDependency.from_json(dep) for dep in json['dependencies'] ], tags=[TagId(tag) for tag in json['tags']], creator_user_id=UserId(json['createdByUserId']), - creation_date=datetime.datetime.fromisoformat(json['createdAt']), + creation_date=map_opt(datetime.datetime.fromisoformat,json.get('createdAt')), + start_date=map_opt(datetime.datetime.fromisoformat,json.get('startDate')), + due_date=map_opt(datetime.datetime.fromisoformat,json.get('dueDate')), assignments=[CardAssignment.from_json(ass) for ass in json['assignments']], custom_fields=[ CustomField.from_json(field) for field in json['customFields'] ], + archived=json['archived'], attachments=json['attachments'], # TODO ) + +def map_opt(mapper, value): + if value is not None: + return mapper(value) + return None diff --git a/favro_sync/favro_fuse.py b/favro_sync/favro_fuse.py index a85ae68..9288f9e 100644 --- a/favro_sync/favro_fuse.py +++ b/favro_sync/favro_fuse.py @@ -201,6 +201,8 @@ class FavroFuse(fuse.Fuse): seq_id=card.seq_id.raw_id, ), todo_list_completed = card.todo_list_completed, + start_date= card.start_date, + due_date= card.due_date, ) return self.formatter.format_card_contents(card_contents) diff --git a/favro_sync/favro_markdown.py b/favro_sync/favro_markdown.py index d3b10e2..bd12034 100644 --- a/favro_sync/favro_markdown.py +++ b/favro_sync/favro_markdown.py @@ -1,5 +1,6 @@ import dataclasses import re +import datetime import frontmatter import marko @@ -14,6 +15,9 @@ FM_KEY_ASSIGNMENTS = 'assignments' FM_KEY_DEPENDENCIES = 'dependencies' FM_KEY_URL = 'url' FM_KEY_ALIASES = 'aliases' +FM_KEY_ARCHIVED = 'archived' +FM_KEY_DUE_DATE = 'due' +FM_KEY_START_DATE = 'start-date' ################################################################################ @@ -27,6 +31,9 @@ class CardContents: card_dependencies: list[str] url: str todo_list_completed: bool | None + archived: bool + start_date: datetime.date | None + due_date: datetime.date | None def format_obsidian_link(text: str) -> str: @@ -87,6 +94,12 @@ class CardFileFormatter: ] if card.todo_list_completed: frontmatter_data[FM_KEY_TODO_LIST_COMPLETED] = card.todo_list_completed + if card.archived: + frontmatter_data[FM_KEY_ARCHIVED] = card.archived + if card.due_date: + frontmatter_data[FM_KEY_DUE_DATE] = card.due_date + if card.start_date: + frontmatter_data[FM_KEY_DUE_DATE] = card.start_date # Card name ls = [] @@ -134,6 +147,10 @@ class CardFileFormatter: url: list[str] = fm.metadata.get(FM_KEY_URL) todo_list_completed: bool | None = fm.metadata.get(FM_KEY_TODO_LIST_COMPLETED) + archived: bool = fm.metadata.get(FM_KEY_ARCHIVED) + + start_date: datetime.date = fm.metadata.get(FM_KEY_START_DATE) + due_date: datetime.date = fm.metadata.get(FM_KEY_DUE_DATE) description = self.renderer.render_children(document).strip() return CardContents( @@ -145,4 +162,7 @@ class CardFileFormatter: card_dependencies=card_dependencies, url=url, todo_list_completed=todo_list_completed, + archived=archived, + start_date=start_date, + due_date=due_date, ) diff --git a/test/test_client.py b/test/test_client.py index 4bca231..ba111af 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -20,15 +20,19 @@ def test_create_client(): def test_get_card(): client = create_client() card = client.get_card(SeqId(11368)) - print(card) - assert card is not None - assert card.name is not None + assert_valid_card(card) + assert card.detailed_description is not None assert len(card.dependencies) == 1 assert len(card.attachments) == 0 assert len(card.custom_fields) == 2 +@needs_secrets +def test_get_cards(): + client = create_client() + for card in client.get_cards(todo_list=True): + assert_valid_card(card) def create_client(): return FavroClient( @@ -37,3 +41,7 @@ def create_client(): favro_password=secrets.favro_password(), read_only=True, ) + +def assert_valid_card(card): + assert card is not None + assert card.name is not None