Writing implemented for description only. It doesn't work with tasks or titles
Some checks failed
Test Python / Test (push) Failing after 23s
Some checks failed
Test Python / Test (push) Failing after 23s
This commit is contained in:
parent
cd3d1e3f33
commit
15906dac21
|
@ -12,6 +12,8 @@ def main():
|
||||||
favro_username = secrets.load_or_fail('FAVRO_USERNAME')
|
favro_username = secrets.load_or_fail('FAVRO_USERNAME')
|
||||||
favro_password = secrets.load_or_fail('FAVRO_PASSWORD')
|
favro_password = secrets.load_or_fail('FAVRO_PASSWORD')
|
||||||
|
|
||||||
|
read_only = False
|
||||||
|
|
||||||
#with tempfile.TemporaryDirectory(prefix='favro_sync_') as tmpdirname:
|
#with tempfile.TemporaryDirectory(prefix='favro_sync_') as tmpdirname:
|
||||||
tmpdirname = './output' # TODO
|
tmpdirname = './output' # TODO
|
||||||
if True:
|
if True:
|
||||||
|
@ -19,7 +21,10 @@ def main():
|
||||||
|
|
||||||
client = FavroClient(favro_org_id=OrganizationId(favro_org_id),
|
client = FavroClient(favro_org_id=OrganizationId(favro_org_id),
|
||||||
favro_username=favro_username,
|
favro_username=favro_username,
|
||||||
favro_password=favro_password, session=session)
|
favro_password=favro_password,
|
||||||
|
session=session,
|
||||||
|
read_only=read_only,
|
||||||
|
)
|
||||||
|
|
||||||
client.check_logged_in()
|
client.check_logged_in()
|
||||||
start_favro_fuse(client)
|
start_favro_fuse(client)
|
||||||
|
|
|
@ -4,8 +4,9 @@ import datetime
|
||||||
from typing import Any
|
from typing import Any
|
||||||
import functools
|
import functools
|
||||||
from collections.abc import Iterator
|
from collections.abc import Iterator
|
||||||
|
from logging import getLogger
|
||||||
|
|
||||||
PREFIX = 'PAR-' # TODO: Make configurable
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
# Types
|
# Types
|
||||||
|
|
||||||
|
@ -71,8 +72,8 @@ class Card:
|
||||||
is_archived = json['archived'],
|
is_archived = json['archived'],
|
||||||
organization_id = OrganizationId(json['organizationId']),
|
organization_id = OrganizationId(json['organizationId']),
|
||||||
name = json['name'],
|
name = json['name'],
|
||||||
todo_list_user_id = UserId(json['todoListUserId']),
|
todo_list_user_id = UserId(json['todoListUserId' ]) if 'todoListUserId' in json else None,
|
||||||
todo_list_completed = json['todoListCompleted'],
|
todo_list_completed = json.get('todoListCompleted'),
|
||||||
dependencies = json['dependencies'],
|
dependencies = json['dependencies'],
|
||||||
tags = json['tags'],
|
tags = json['tags'],
|
||||||
creator_user_id = UserId(json['createdByUserId']),
|
creator_user_id = UserId(json['createdByUserId']),
|
||||||
|
@ -87,7 +88,7 @@ URL_UPDATE_CARD = URL_API_ROOT+'/cards/{card_id}'
|
||||||
class FavroClient:
|
class FavroClient:
|
||||||
|
|
||||||
def __init__(self, *, favro_org_id: OrganizationId, favro_username: str, favro_password: str,
|
def __init__(self, *, favro_org_id: OrganizationId, favro_username: str, favro_password: str,
|
||||||
session: requests.Session | None = None):
|
session: requests.Session | None = None, read_only=True):
|
||||||
|
|
||||||
assert favro_org_id is not None
|
assert favro_org_id is not None
|
||||||
assert favro_username is not None
|
assert favro_username is not None
|
||||||
|
@ -100,6 +101,10 @@ class FavroClient:
|
||||||
'organizationId': favro_org_id.raw_id,
|
'organizationId': favro_org_id.raw_id,
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
})
|
})
|
||||||
|
self.read_only = read_only
|
||||||
|
|
||||||
|
self.card_id_to_seq_id: dict[CardId, SeqId] = {}
|
||||||
|
self.seq_id_to_card_id: dict[SeqId, CardId] = {}
|
||||||
|
|
||||||
def check_logged_in(self) -> None:
|
def check_logged_in(self) -> None:
|
||||||
next(self.get_todo_list_cards())
|
next(self.get_todo_list_cards())
|
||||||
|
@ -108,39 +113,68 @@ class FavroClient:
|
||||||
yield from self.get_cards(todo_list=True)
|
yield from self.get_cards(todo_list=True)
|
||||||
|
|
||||||
|
|
||||||
def get_cards(self, *, seq_id: SeqId | None = None, todo_list=True) -> Iterator[Card]:
|
def get_cards(self, *, seq_id: SeqId | None = None, todo_list=False) -> Iterator[Card]:
|
||||||
# Determine params for get_cards
|
# Determine params for get_cards
|
||||||
params = {'descriptionFormat': 'markdown'}
|
request = self._get_cards_request(seq_id, todo_list)
|
||||||
if seq_id:
|
|
||||||
params['cardSequentialId']= str(seq_id.raw_id)
|
|
||||||
if todo_list:
|
|
||||||
params['todoList'] = 'true'
|
|
||||||
|
|
||||||
# Run query
|
# Run query
|
||||||
response = self.session.get(URL_GET_ALL_CARDS, params = params)
|
response = self.session.send(request)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
json = response.json()
|
json = response.json()
|
||||||
|
|
||||||
# TODO: Pageination
|
# TODO: Pageination
|
||||||
for entity_json in json['entities']:
|
for entity_json in json['entities']:
|
||||||
yield Card.from_json(entity_json)
|
card = Card.from_json(entity_json)
|
||||||
|
self.card_id_to_seq_id[card.card_id] = card.seq_id
|
||||||
|
self.seq_id_to_card_id[card.seq_id] = card.card_id
|
||||||
|
yield card
|
||||||
del entity_json
|
del entity_json
|
||||||
|
|
||||||
|
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)
|
||||||
|
if todo_list is True:
|
||||||
|
params['todoList'] = 'true'
|
||||||
|
|
||||||
|
request = requests.Request('GET', URL_GET_ALL_CARDS, params = params)
|
||||||
|
return self.session.prepare_request(request)
|
||||||
|
|
||||||
def get_card(self, seq_id: SeqId) -> Card:
|
def get_card(self, seq_id: SeqId) -> Card:
|
||||||
return next(self.get_cards(seq_id=seq_id))
|
return next(self.get_cards(seq_id=seq_id))
|
||||||
|
|
||||||
def get_card_id(self, seq_id: SeqId) -> CardId:
|
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
|
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])
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
def update_card_description(self, card_id: CardId, description: str) -> Card:
|
def update_card_description(self, card_id: CardId, description: str) -> Card:
|
||||||
"""Returns updated 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
|
||||||
|
elif self.read_only is True:
|
||||||
|
raise Exception('FavroClient is read only')
|
||||||
|
|
||||||
json_body = {
|
json_body = {
|
||||||
'detailedDescription': description,
|
'detailedDescription': description,
|
||||||
'descriptionFormat': 'markdown',
|
'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()
|
response.raise_for_status()
|
||||||
|
logger.warning("Response: %s", response.json())
|
||||||
|
self._invalidate_cache(card_id)
|
||||||
|
|
||||||
return Card.from_json(response.json())
|
return Card.from_json(response.json())
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,6 @@ class FavroFuse(fuse.Fuse):
|
||||||
|
|
||||||
for card in self.favro_client.get_todo_list_cards():
|
for card in self.favro_client.get_todo_list_cards():
|
||||||
yield fuse.Direntry(CARD_FILENAME_FORMAT.format(seq_id=card.seq_id.raw_id))
|
yield fuse.Direntry(CARD_FILENAME_FORMAT.format(seq_id=card.seq_id.raw_id))
|
||||||
return # TODO
|
|
||||||
|
|
||||||
def open(self, path: str, flags) -> int | None:
|
def open(self, path: str, flags) -> int | None:
|
||||||
thing = Thing.from_path(path)
|
thing = Thing.from_path(path)
|
||||||
|
@ -138,7 +137,6 @@ class FavroFuse(fuse.Fuse):
|
||||||
return len(written_buffer)
|
return len(written_buffer)
|
||||||
|
|
||||||
def truncate(self, path: str, new_size: int):
|
def truncate(self, path: str, new_size: int):
|
||||||
print('Trunca', path, new_size)
|
|
||||||
# Check that this is a card thing.
|
# Check that this is a card thing.
|
||||||
thing = Thing.from_path(path)
|
thing = Thing.from_path(path)
|
||||||
if not isinstance(thing, CardThing):
|
if not isinstance(thing, CardThing):
|
||||||
|
@ -149,7 +147,9 @@ class FavroFuse(fuse.Fuse):
|
||||||
# Splice contents
|
# Splice contents
|
||||||
contents_str = card_to_contents(card)
|
contents_str = card_to_contents(card)
|
||||||
contents = bytes(contents_str, 'utf8')
|
contents = bytes(contents_str, 'utf8')
|
||||||
contents = contents[0:new_size]
|
old_size = len(contents)
|
||||||
|
contents = contents[0:new_size] + b' ' * (old_size - new_size)
|
||||||
|
assert len(contents) == old_size
|
||||||
contents_str = contents.decode('utf8')
|
contents_str = contents.decode('utf8')
|
||||||
|
|
||||||
# Write to favro
|
# Write to favro
|
||||||
|
|
Loading…
Reference in New Issue
Block a user