1
0

Ruff
Some checks failed
Test Python / Test (push) Failing after 26s

This commit is contained in:
Jon Michael Aanes 2024-10-10 14:02:02 +02:00
parent e594dff149
commit cbb0cba076
6 changed files with 76 additions and 40 deletions

View File

@ -4,21 +4,21 @@ Implements methods for interacting with the [Favro API](https://favro.com/devel
""" """
import dataclasses import dataclasses
import datetime
from collections.abc import Iterator from collections.abc import Iterator
from logging import getLogger from logging import getLogger
import datetime
import requests_util
import requests import requests
import requests_util
from .favro_data_model import ( from .favro_data_model import (
Card, Card,
CardId, CardId,
Collection, Collection,
CollectionId,
CustomFieldId, CustomFieldId,
CustomFieldInfo, CustomFieldInfo,
OrganizationId, OrganizationId,
CollectionId,
SeqId, SeqId,
TagId, TagId,
TagInfo, TagInfo,
@ -107,9 +107,15 @@ class FavroClient:
self.card_cache = CardCache() self.card_cache = CardCache()
# Setup caching # Setup caching
requests_util.setup_limiter(self.session, URL_API_ROOT, datetime.timedelta(days=7)) requests_util.setup_limiter(
requests_util.setup_limiter(self.session, URL_GET_CARDS, datetime.timedelta(minutes=10)) self.session, URL_API_ROOT, datetime.timedelta(days=7),
requests_util.setup_limiter(self.session, URL_GET_TASKS, datetime.timedelta(minutes=10)) )
requests_util.setup_limiter(
self.session, URL_GET_CARDS, datetime.timedelta(minutes=10),
)
requests_util.setup_limiter(
self.session, URL_GET_TASKS, datetime.timedelta(minutes=10),
)
def check_logged_in(self) -> None: def check_logged_in(self) -> None:
next(self.get_todo_list_cards()) next(self.get_todo_list_cards())
@ -124,7 +130,9 @@ class FavroClient:
collection_id: CollectionId | None = None, collection_id: CollectionId | None = None,
todo_list=False, todo_list=False,
) -> Iterator[Card]: ) -> Iterator[Card]:
request = self._get_cards_request(seq_id=seq_id,todo_list=todo_list,collection_id=collection_id) request = self._get_cards_request(
seq_id=seq_id, todo_list=todo_list, collection_id=collection_id,
)
for card in self._get_paginated(request, Card.from_json): for card in self._get_paginated(request, Card.from_json):
self.card_cache.add_card(card) self.card_cache.add_card(card)
yield card yield card
@ -134,9 +142,10 @@ class FavroClient:
yield from self._get_paginated(request, Collection.from_json) yield from self._get_paginated(request, Collection.from_json)
def _get_cards_prepared_request( def _get_cards_prepared_request(
self, **kwargs, self,
**kwargs,
) -> requests.PreparedRequest: ) -> requests.PreparedRequest:
request = self._get_cards_request(**kwargs) request = self._get_cards_request(**kwargs)
return self.session.prepare_request(request) return self.session.prepare_request(request)
def _get_cards_request( def _get_cards_request(
@ -234,7 +243,9 @@ class FavroClient:
self._invalidate_cache(card_id) self._invalidate_cache(card_id)
return self.update_card_contents_locally(card_id, card_contents) return self.update_card_contents_locally(card_id, card_contents)
def _get_paginated(self, base_request: requests.Request, entity_from_json) -> Iterator: def _get_paginated(
self, base_request: requests.Request, entity_from_json,
) -> Iterator:
page = 0 page = 0
request_id = None request_id = None
num_pages = 1 num_pages = 1

View File

@ -136,8 +136,8 @@ class CustomFieldItem:
@staticmethod @staticmethod
def from_json(json: dict[str, Any]) -> 'CustomFieldItem': def from_json(json: dict[str, Any]) -> 'CustomFieldItem':
return CustomFieldItem( return CustomFieldItem(
custom_field_item_id=CustomFieldItemId(json['customFieldItemId']), custom_field_item_id=CustomFieldItemId(json['customFieldItemId']),
name=json['name'], name=json['name'],
) )
@ -163,7 +163,9 @@ class CustomFieldInfo:
enabled: bool enabled: bool
custom_field_items: list[CustomFieldItem] custom_field_items: list[CustomFieldItem]
def get_field_item(self, field_item_id: CustomFieldItemId) -> CustomFieldItem | None: def get_field_item(
self, field_item_id: CustomFieldItemId,
) -> CustomFieldItem | None:
for item in self.custom_field_items: for item in self.custom_field_items:
if item.custom_field_item_id == field_item_id: if item.custom_field_item_id == field_item_id:
return item return item
@ -174,11 +176,13 @@ class CustomFieldInfo:
return CustomFieldInfo( return CustomFieldInfo(
organization_id=OrganizationId(json['organizationId']), organization_id=OrganizationId(json['organizationId']),
custom_field_id=CustomFieldId(json['customFieldId']), custom_field_id=CustomFieldId(json['customFieldId']),
widget_common_id=map_opt(WidgetCommonId,json.get('widgetCommonId')), widget_common_id=map_opt(WidgetCommonId, json.get('widgetCommonId')),
type=json['type'], type=json['type'],
name=json['name'], name=json['name'],
enabled=json['enabled'], enabled=json['enabled'],
custom_field_items=[CustomFieldItem.from_json(f) for f in json.get('customFieldItems', [])], custom_field_items=[
CustomFieldItem.from_json(f) for f in json.get('customFieldItems', [])
],
) )
@ -198,9 +202,9 @@ class CustomField:
else: else:
typed_value = [CustomFieldItemId(v) for v in value] typed_value = [CustomFieldItemId(v) for v in value]
return CustomField( return CustomField(
custom_field_id = CustomFieldId(json['customFieldId']), custom_field_id=CustomFieldId(json['customFieldId']),
value = typed_value, value=typed_value,
color = json.get('color'), color=json.get('color'),
) )
@ -243,12 +247,13 @@ class Task:
position=json['position'], position=json['position'],
) )
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class Collection: class Collection:
collection_id: CollectionId collection_id: CollectionId
organization_id: OrganizationId organization_id: OrganizationId
name: str name: str
shared_to_users: list[dict[str,str]] # TODO shared_to_users: list[dict[str, str]] # TODO
public_sharing: str public_sharing: str
background: str background: str
is_archived: bool is_archived: bool
@ -257,16 +262,17 @@ class Collection:
@staticmethod @staticmethod
def from_json(json: dict[str, Any]) -> 'Collection': def from_json(json: dict[str, Any]) -> 'Collection':
return Collection( return Collection(
collection_id=CollectionId(json['collectionId']), collection_id=CollectionId(json['collectionId']),
organization_id=OrganizationId(json['collectionId']), organization_id=OrganizationId(json['collectionId']),
name=json['name'], name=json['name'],
shared_to_users=json['sharedToUsers'], shared_to_users=json['sharedToUsers'],
public_sharing=json['publicSharing'], public_sharing=json['publicSharing'],
background=json['background'], background=json['background'],
is_archived=json['archived'], is_archived=json['archived'],
widget_common_id=map_opt(WidgetCommonId,json.get('widgetCommonId')), widget_common_id=map_opt(WidgetCommonId, json.get('widgetCommonId')),
) )
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class Card: class Card:
card_id: CardId card_id: CardId

View File

@ -8,7 +8,13 @@ from logging import getLogger
import fuse import fuse
from .favro_client import FavroClient from .favro_client import FavroClient
from .favro_data_model import Card, SeqId, CustomFieldInfo, CustomFieldItemId, CustomField from .favro_data_model import (
Card,
CustomField,
CustomFieldInfo,
CustomFieldItemId,
SeqId,
)
from .favro_markdown import CardContents, CardFileFormatter from .favro_markdown import CardContents, CardFileFormatter
################################################################################ ################################################################################
@ -26,9 +32,12 @@ CARD_FILENAME_REGEX = r'^PAR\-(\d+)\.md$'
################################################################################ ################################################################################
# Formatting # Formatting
def to_custom_field_value(custom_field: CustomField, field_def: CustomFieldInfo) -> str | None:
def to_custom_field_value(
custom_field: CustomField, field_def: CustomFieldInfo,
) -> str | None:
value: CustomFieldItemId | list[CustomFieldItemId] = custom_field.value value: CustomFieldItemId | list[CustomFieldItemId] = custom_field.value
if field_def.type in {'Single select','Multiple select'}: if field_def.type in {'Single select', 'Multiple select'}:
items = [field_def.get_field_item(item_id) for item_id in value] items = [field_def.get_field_item(item_id) for item_id in value]
items = [i for i in items if i] items = [i for i in items if i]
return items[0].name return items[0].name
@ -36,7 +45,8 @@ def to_custom_field_value(custom_field: CustomField, field_def: CustomFieldInfo)
return custom_field.color return custom_field.color
return None return None
def to_custom_fields(card: Card, favro_client: FavroClient) -> dict[str,str]:
def to_custom_fields(card: Card, favro_client: FavroClient) -> dict[str, str]:
custom_fields = {} custom_fields = {}
for field_assignment in card.custom_fields: for field_assignment in card.custom_fields:
field_def = favro_client.get_custom_field(field_assignment.custom_field_id) field_def = favro_client.get_custom_field(field_assignment.custom_field_id)
@ -46,6 +56,7 @@ def to_custom_fields(card: Card, favro_client: FavroClient) -> dict[str,str]:
del field_assignment, str_value del field_assignment, str_value
return custom_fields return custom_fields
def to_card_contents(card: Card, favro_client: FavroClient) -> CardContents: def to_card_contents(card: Card, favro_client: FavroClient) -> CardContents:
tags = [favro_client.get_tag(tag_id).name for tag_id in card.tags] tags = [favro_client.get_tag(tag_id).name for tag_id in card.tags]
assignments = [ assignments = [
@ -80,6 +91,7 @@ def to_card_contents(card: Card, favro_client: FavroClient) -> CardContents:
################################################################################ ################################################################################
# FUSE # FUSE
class FavroStat(fuse.Stat): class FavroStat(fuse.Stat):
def __init__(self): def __init__(self):
self.st_mode = 0 self.st_mode = 0
@ -101,7 +113,6 @@ class FileSystemItem:
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class RootFileSystemItem(FileSystemItem): class RootFileSystemItem(FileSystemItem):
@staticmethod @staticmethod
def from_path_segment(_segment: str) -> 'RootFileSystemItem': def from_path_segment(_segment: str) -> 'RootFileSystemItem':
return RootFileSystemItem() return RootFileSystemItem()
@ -109,6 +120,7 @@ class RootFileSystemItem(FileSystemItem):
def __str__(self): def __str__(self):
return '/' return '/'
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class CollectionFileSystemItem(FileSystemItem): class CollectionFileSystemItem(FileSystemItem):
collection_name: str collection_name: str
@ -123,6 +135,7 @@ class CollectionFileSystemItem(FileSystemItem):
def __str__(self): def __str__(self):
return self.collection_name return self.collection_name
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class CardFileSystemItem(FileSystemItem): class CardFileSystemItem(FileSystemItem):
seq_id: SeqId seq_id: SeqId
@ -137,7 +150,9 @@ class CardFileSystemItem(FileSystemItem):
return CARD_FILENAME_FORMAT.format(seq_id=self.seq_id.raw_id) return CARD_FILENAME_FORMAT.format(seq_id=self.seq_id.raw_id)
def path_to_file_system_item(path_str: str, path_components: list[type[FileSystemItem]]) -> FileSystemItem | None: def path_to_file_system_item(
path_str: str, path_components: list[type[FileSystemItem]],
) -> FileSystemItem | None:
path = re.findall(r'[^/]+', path_str) path = re.findall(r'[^/]+', path_str)
component = path_components[len(path)] component = path_components[len(path)]
return component.from_path_segment(path[-1] if path else None) return component.from_path_segment(path[-1] if path else None)
@ -155,7 +170,11 @@ class FavroFuse(fuse.Fuse):
self.favro_client = favro_client self.favro_client = favro_client
self.formatter = formatter self.formatter = formatter
self.wiped_cards = set() self.wiped_cards = set()
self.path_components = [RootFileSystemItem, CollectionFileSystemItem, CardFileSystemItem] self.path_components = [
RootFileSystemItem,
CollectionFileSystemItem,
CardFileSystemItem,
]
super().__init__(**kwargs) super().__init__(**kwargs)
def getattr(self, path: str) -> FavroStat | int: def getattr(self, path: str) -> FavroStat | int:
@ -190,7 +209,6 @@ class FavroFuse(fuse.Fuse):
del collection del collection
elif isinstance(file_system_item, CollectionFileSystemItem): elif isinstance(file_system_item, CollectionFileSystemItem):
# TODO: move into own function # TODO: move into own function
for collection in self.favro_client.get_collections(): for collection in self.favro_client.get_collections():
if collection.name.replace('/', '') == file_system_item.collection_name: if collection.name.replace('/', '') == file_system_item.collection_name:

View File

@ -35,7 +35,7 @@ class CardContents:
is_archived: bool is_archived: bool
start_date: datetime.date | None start_date: datetime.date | None
due_date: datetime.date | None due_date: datetime.date | None
custom_fields: dict[str,str] custom_fields: dict[str, str]
def format_obsidian_link(text: str) -> str: def format_obsidian_link(text: str) -> str:
@ -173,5 +173,5 @@ class CardFileFormatter:
is_archived=is_archived, is_archived=is_archived,
start_date=start_date, start_date=start_date,
due_date=due_date, due_date=due_date,
custom_fields={}, # TODO custom_fields={}, # TODO
) )

View File

@ -1,6 +1,6 @@
import pytest import pytest
import requests_cache import requests_cache
from favro_sync import secrets from favro_sync import secrets
from favro_sync.favro_client import FavroClient, OrganizationId, SeqId from favro_sync.favro_client import FavroClient, OrganizationId, SeqId
@ -50,6 +50,7 @@ def test_get_cards():
for card in client.get_cards(todo_list=True): for card in client.get_cards(todo_list=True):
assert_valid_card(card) assert_valid_card(card)
@needs_secrets @needs_secrets
def test_get_collections(): def test_get_collections():
client = create_client() client = create_client()
@ -62,6 +63,7 @@ def test_get_collections():
assert collection.is_archived is not None assert collection.is_archived is not None
assert collection.widget_common_id is None assert collection.widget_common_id is None
def create_client(): def create_client():
session = requests_cache.CachedSession('output/test-http-cache.sqlite') session = requests_cache.CachedSession('output/test-http-cache.sqlite')
return FavroClient( return FavroClient(

View File

@ -1,9 +1,8 @@
from favro_sync.favro_client import SeqId
from favro_sync.favro_fuse import to_card_contents from favro_sync.favro_fuse import to_card_contents
from favro_sync.favro_markdown import CardFileFormatter
from .test_client import create_client, needs_secrets from .test_client import create_client, needs_secrets
@needs_secrets @needs_secrets
def test_format_all_cards(): def test_format_all_cards():
client = create_client() client = create_client()