diff --git a/favro_sync/__main__.py b/favro_sync/__main__.py index 57e9c9a..a322f99 100644 --- a/favro_sync/__main__.py +++ b/favro_sync/__main__.py @@ -6,7 +6,7 @@ import requests_cache from .favro_client import FavroClient, OrganizationId from .favro_fuse import start_favro_fuse -import secrets +from . import secrets def main(): diff --git a/favro_sync/favro_client.py b/favro_sync/favro_client.py index 83cfce0..e402e58 100644 --- a/favro_sync/favro_client.py +++ b/favro_sync/favro_client.py @@ -4,7 +4,8 @@ from logging import getLogger import requests import dataclasses -from .favro_data_model import Card, CardId, OrganizationId, SeqId +from .favro_data_model import (Card, CardId, OrganizationId, SeqId, UserInfo, + UserId, UserInfo, TagId, TagInfo) from .favro_markdown import CardContents logger = getLogger(__name__) @@ -13,6 +14,8 @@ logger = getLogger(__name__) 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_USER = URL_API_ROOT + '/users/{user_id}' +URL_GET_TAG = URL_API_ROOT + '/tags/{tag_id}' class CardCache: @@ -27,16 +30,19 @@ class CardCache: for card in reversed(self.cards): if card.card_id == card_id: return card + return None def get_card_by_seq_id(self, seq_id: SeqId) -> Card | None: for card in reversed(self.cards): if card.seq_id == seq_id: return card + return None def remove(self, card_id: CardId) -> Card | None: while card := self.get_card_by_card_id(card_id): self.cards.remove(card) return card + return None class FavroClient: def __init__( @@ -85,7 +91,6 @@ class FavroClient: # TODO: Add support for pageination for entity_json in json['entities']: - print(entity_json) card = Card.from_json(entity_json) self.cache.add_card(card) yield card @@ -108,6 +113,14 @@ class FavroClient: return card return next(self.get_cards(seq_id=seq_id)) + def get_user(self, user_id: UserId) -> UserInfo: + response = self.session.get(URL_GET_USER.format(user_id=user_id.raw_id)) + return UserInfo.from_json(response.json()) + + def get_tag(self, tag_id: TagId) -> TagInfo: + response = self.session.get(URL_GET_TAG.format(tag_id=tag_id.raw_id)) + return TagInfo.from_json(response.json()) + def _invalidate_cache(self, card_id: CardId) -> None: card = self.cache.remove(card_id) if card: @@ -125,12 +138,12 @@ class FavroClient: """Returns updated Card.""" if self.read_only == 'silent': logger.warning( - 'FavroClient is silent read only: Discarding card description update of length %d', - len(description), + 'FavroClient is silent read only: Discarding card description update', ) return None # TODO - elif self.read_only is True: - raise Exception('FavroClient is read only') + if self.read_only is True: + msg = 'FavroClient is read only' + raise Exception(msg) json_body = { 'name': card_contents.name, diff --git a/favro_sync/favro_data_model.py b/favro_sync/favro_data_model.py index c47da6e..a0bbceb 100644 --- a/favro_sync/favro_data_model.py +++ b/favro_sync/favro_data_model.py @@ -40,10 +40,42 @@ class CardAssignment: json['completed'], ) +@dataclasses.dataclass(frozen=True) +class UserInfo: + user_id: UserId + name: str + email: str + organization_role: str + + @staticmethod + def from_json(json: dict[str, Any]) -> 'UserInfo': + return UserInfo( + UserId(json['userId']), + json['name'], + json['email'], + json['organizationRole'], + ) + @dataclasses.dataclass(frozen=True) class TagId: raw_id: str +@dataclasses.dataclass(frozen=True) +class TagInfo: + tag_id: TagId + organization_id: OrganizationId + name: str + color: str | None + + @staticmethod + def from_json(json: dict[str, Any]) -> 'TagInfo': + return TagInfo( + TagId(json['tagId']), + OrganizationId(json['organizationId']), + json['name'], + json.get('color'), + ) + @dataclasses.dataclass(frozen=True) class CardDependency: card_id: CardId diff --git a/favro_sync/favro_fuse.py b/favro_sync/favro_fuse.py index 09db0b3..4200060 100644 --- a/favro_sync/favro_fuse.py +++ b/favro_sync/favro_fuse.py @@ -73,7 +73,7 @@ class FavroFuse(fuse.Fuse): st.st_mode = stat.S_IFREG | 0o666 st.st_nlink = 1 - st.st_size = len(self.formatter.format_card(card)) + st.st_size = len(self._format_card_file(card)) st.st_ctime = int(card.creation_date.timestamp()) st.st_mtime = st.st_ctime # TODO else: @@ -88,7 +88,7 @@ class FavroFuse(fuse.Fuse): for card in self.favro_client.get_todo_list_cards(): yield fuse.Direntry(CARD_FILENAME_FORMAT.format(seq_id=card.seq_id.raw_id)) - def open(self, path: str, flags) -> int | None: + def open(self, path: str, flags: int) -> int | None: thing = Thing.from_path(path) if not isinstance(thing, CardThing): return -errno.ENOENT @@ -102,7 +102,7 @@ class FavroFuse(fuse.Fuse): card = self.favro_client.get_card(thing.seq_id) - contents_str = self.formatter.format_card(card) + contents_str = self._format_card_file(card) contents = bytes(contents_str, 'utf8') slen = len(contents) @@ -123,7 +123,7 @@ class FavroFuse(fuse.Fuse): card = self.favro_client.get_card(thing.seq_id) # Splice contents - contents_str = self.formatter.format_card(card) + contents_str = self._format_card_file(card) contents = bytes(contents_str, 'utf8') contents = splice(contents, written_buffer, offset) contents_str = contents.decode('utf8') @@ -144,7 +144,7 @@ class FavroFuse(fuse.Fuse): card = self.favro_client.get_card(thing.seq_id) # Splice contents - contents_str = self.formatter.format_card(card) + contents_str = self._format_card_file(card) contents = bytes(contents_str, 'utf8') old_size = len(contents) contents = contents[0:new_size] + b' ' * (old_size - new_size) @@ -158,6 +158,16 @@ class FavroFuse(fuse.Fuse): # Return amount written return 0 + def _format_card_file(self, card: Card) -> str: + tags = [self.favro_client.get_tag(tag_id).name for tag_id in card.tags] + assignments = [self.favro_client.get_user(assignment.user).name for assignment in card.assignments] + card_contents = CardContents( + card.name, + card.detailed_description, + tags, + assignments, + ) + return self.formatter.format_card_contents(card_contents) def splice(original_buffer: bytes, input_buffer: bytes, offset: int) -> bytes: return ( diff --git a/favro_sync/favro_markdown.py b/favro_sync/favro_markdown.py index 3810086..5a3b094 100644 --- a/favro_sync/favro_markdown.py +++ b/favro_sync/favro_markdown.py @@ -19,7 +19,7 @@ class CardContents: assignments: list[str] class CardFileFormatter: - """Component for formatting and parsing card files """ + """Component for formatting and parsing card files.""" def __init__(self, obsidian_mode = True): self.obsidian_mode = obsidian_mode @@ -90,5 +90,3 @@ class CardFileFormatter: assignments = [], ) - def format_card(self, card: Card) -> str: - return self.format_card_contents(CardContents(card.name, card.detailed_description))