From 38ce3aefe53492e71e92e46f170f6b5b1d0dac1d Mon Sep 17 00:00:00 2001 From: disrupted Date: Sun, 15 Dec 2024 12:57:13 +0100 Subject: [PATCH] fix: do not increment index before update --- tests/test_things.py | 17 ++++++++++++----- tests/test_todo.py | 6 +++++- things_cloud/api/client.py | 5 +---- things_cloud/models/todo.py | 33 +++++++++++++++++++++++++-------- 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/tests/test_things.py b/tests/test_things.py index 0513528..ab3af95 100644 --- a/tests/test_things.py +++ b/tests/test_things.py @@ -11,6 +11,7 @@ from things_cloud.api.account import Account, Credentials from things_cloud.api.client import HistoryResponse, ThingsClient from things_cloud.models.todo import ( + XX, Destination, EditBody, NewBody, @@ -99,6 +100,7 @@ def task() -> TodoItem: return task +@freeze_time(datetime(2024, 12, 9, 12, 31, 47, 123456, tzinfo=timezone.utc)) def test_create( things: ThingsClient, task: TodoItem, account_id: uuid.UUID, httpx_mock: HTTPXMock ): @@ -116,7 +118,7 @@ def test_create( assert request.method == "POST" assert ( request.url - == f"https://cloud.culturedcode.com/version/1/history/{account_id}/commit?ancestor-index=124&_cnt=1" + == f"https://cloud.culturedcode.com/version/1/history/{account_id}/commit?ancestor-index={start_idx}&_cnt=1" ) assert json.loads(request.content) == { "tiqzvgT8m7ME4gooPqtdq3": { @@ -126,8 +128,8 @@ def test_create( "tt": "test_create", "ss": 0, "st": 0, - "cd": 1733747506, - "md": 1733747506.919961, + "cd": 1733747506.919961, + "md": 1733747507.123456, "sr": None, "tir": None, "sp": None, @@ -154,7 +156,8 @@ def test_create( "rp": None, "acrd": None, "rr": None, - "nt": {"_t": "tx", "ch": 0, "v": "", "t": 0}, + "nt": {"_t": "tx", "ch": 0, "v": "", "t": 1}, + "xx": {"sn": {}, "_t": "oo"}, }, "e": "Task6", } @@ -233,7 +236,7 @@ def history_data_new() -> dict[str, Any]: "ti": 0, "tg": [], "icp": False, - "nt": {"ch": 0, "_t": "tx", "t": 0, "v": ""}, + "nt": {"ch": 0, "_t": "tx", "t": 1, "v": ""}, "do": 0, "dl": [], "lai": None, @@ -248,6 +251,7 @@ def history_data_new() -> dict[str, Any]: "ato": None, "sb": 0, "agr": [], + "xx": {"sn": {}, "_t": "oo"}, }, "e": "Task6", "t": 0, @@ -317,6 +321,7 @@ def test_history_new(things: ThingsClient, history_new: HistoryResponse): assert todo.after_completion_reference_date is None assert todo.recurrence_rule is None assert todo.note == Note() + assert todo.xx == XX() # assert not todo._changes @@ -420,6 +425,7 @@ def history_data_mixed() -> dict[str, Any]: "ato": None, "sb": 0, "agr": [], + "xx": {"sn": {}, "_t": "oo"}, }, "e": "Task6", "t": 0, @@ -462,6 +468,7 @@ def history_data_mixed() -> dict[str, Any]: "ato": None, "sb": 0, "agr": [], + "xx": {"sn": {}, "_t": "oo"}, }, "e": "Task6", "t": 0, diff --git a/tests/test_todo.py b/tests/test_todo.py index 6c4d12d..790d616 100644 --- a/tests/test_todo.py +++ b/tests/test_todo.py @@ -4,6 +4,7 @@ from freezegun import freeze_time from things_cloud.models.todo import ( + XX, Destination, Note, Status, @@ -309,7 +310,7 @@ def test_serde(): "ti": 0, "tg": [], "icp": False, - "nt": {"ch": 0, "_t": "tx", "t": 0, "v": ""}, + "nt": {"ch": 0, "_t": "tx", "t": 1, "v": ""}, "do": 0, "dl": [], "lai": None, @@ -324,6 +325,7 @@ def test_serde(): "ato": 43200, "sb": 0, "agr": [], + "xx": {"sn": {}, "_t": "oo"}, } todo = TodoApiObject.model_validate(api_object) timestamp_rounded = datetime(2022, 1, 3, 18, 29, 27, tzinfo=timezone.utc) @@ -402,6 +404,7 @@ def test_todo_from_api_object(): "after_completion_reference_date": None, "recurrence_rule": None, "note": Note(), + "xx": XX(), } ) todo = api_object.to_todo() @@ -438,6 +441,7 @@ def test_todo_from_api_object(): assert todo.after_completion_reference_date is None assert todo.recurrence_rule is None assert todo.note == Note() + assert todo.xx == XX() assert todo._synced_state == api_object diff --git a/things_cloud/api/client.py b/things_cloud/api/client.py index 294741a..ce7e704 100644 --- a/things_cloud/api/client.py +++ b/things_cloud/api/client.py @@ -121,14 +121,11 @@ def today(self) -> list[TodoItem]: ] def __commit(self, update: Update) -> CommitResponse: - index = self._offset - if update.body.type is UpdateType.NEW: - index += 1 response = self.__request( method="POST", endpoint="/commit", params={ - "ancestor-index": str(index), + "ancestor-index": str(self._offset), "_cnt": "1", }, json=update.to_api_payload(), diff --git a/things_cloud/models/todo.py b/things_cloud/models/todo.py index 81094dd..898ab0a 100644 --- a/things_cloud/models/todo.py +++ b/things_cloud/models/todo.py @@ -69,6 +69,7 @@ class UpdateType(IntEnum): class EntityType(StrEnum): TASK_6 = "Task6" + CHECKLIST_ITEM_3 = "ChecklistItem3" class NewBody(pydantic.BaseModel): @@ -118,10 +119,15 @@ class Status(IntEnum): class Note(pydantic.BaseModel): - t_: str = pydantic.Field(alias="_t", default="tx") + t_: Annotated[str, pydantic.Field(alias="_t")] = "tx" ch: int = 0 - v: str = "" # value - t: int = 0 + value: Annotated[str, pydantic.Field(alias="v")] = "" # value + t: int = 1 + + +class XX(pydantic.BaseModel): + sn: dict = {} + t_: Annotated[str, pydantic.Field(alias="_t")] = "oo" class TodoApiObject(pydantic.BaseModel): @@ -131,8 +137,8 @@ class TodoApiObject(pydantic.BaseModel): title: Annotated[str, pydantic.Field(alias="tt")] status: Annotated[Status, pydantic.Field(alias="ss")] destination: Annotated[Destination, pydantic.Field(alias="st")] - creation_date: Annotated[TimestampInt | None, pydantic.Field(alias="cd")] - modification_date: Annotated[TimestampFloat | None, pydantic.Field(alias="md")] + creation_date: Annotated[TimestampFloat, pydantic.Field(alias="cd")] + modification_date: Annotated[TimestampFloat, pydantic.Field(alias="md")] scheduled_date: Annotated[TimestampInt | None, pydantic.Field(alias="sr")] today_index_reference_date: Annotated[ TimestampInt | None, pydantic.Field(alias="tir") @@ -174,6 +180,13 @@ class TodoApiObject(pydantic.BaseModel): ] recurrence_rule: Annotated[str | None, pydantic.Field(alias="rr")] note: Annotated[Note, pydantic.Field(alias="nt")] + xx: Annotated[XX, pydantic.Field(alias="xx")] + # task: Annotated[ + # list[ShortUUID] | None, + # pydantic.Field( + # alias="ts", description="for checklist items, references parent task" + # ), + # ] = None # exclude otherwise def to_todo(self) -> TodoItem: todo = TodoItem( @@ -222,7 +235,7 @@ class TodoDeltaApiObject(pydantic.BaseModel): title: Annotated[str | None, pydantic.Field(alias="tt")] = None status: Annotated[Status | None, pydantic.Field(alias="ss")] = None destination: Annotated[Destination | None, pydantic.Field(alias="st")] = None - creation_date: Annotated[TimestampInt | None, pydantic.Field(alias="cd")] = None + creation_date: Annotated[TimestampFloat | None, pydantic.Field(alias="cd")] = None modification_date: Annotated[TimestampFloat, pydantic.Field(alias="md")] = ( pydantic.Field(default_factory=Util.now) ) @@ -263,6 +276,7 @@ class TodoDeltaApiObject(pydantic.BaseModel): ] = None recurrence_rule: Annotated[str | None, pydantic.Field(alias="rr")] = None note: Annotated[Note | None, pydantic.Field(alias="nt")] = None + xx: Annotated[XX | None, pydantic.Field(alias="xx")] = None def apply_edits(self, todo: TodoItem) -> None: keys = self.model_dump(by_alias=False, exclude_none=True).keys() @@ -285,8 +299,8 @@ class TodoItem(pydantic.BaseModel): title: str _status: Status = pydantic.PrivateAttr(default=Status.TODO) _destination: Destination = pydantic.PrivateAttr(default=Destination.INBOX) - creation_date: datetime | None = pydantic.Field(default_factory=Util.now) - modification_date: datetime | None = pydantic.Field(default_factory=Util.now) + creation_date: datetime = pydantic.Field(default_factory=Util.now) + modification_date: datetime = pydantic.Field(default_factory=Util.now) scheduled_date: datetime | None = pydantic.Field(default=None) today_index_reference_date: datetime | None = pydantic.Field( default=None, repr=False @@ -316,6 +330,7 @@ class TodoItem(pydantic.BaseModel): after_completion_reference_date: datetime | None = pydantic.Field(default=None) recurrence_rule: str | None = pydantic.Field(default=None) # TODO: weird XML values note: Note = pydantic.Field(default_factory=Note) + xx: XX = pydantic.Field(default_factory=XX) _synced_state: TodoApiObject | None = pydantic.PrivateAttr(default=None) def to_update(self) -> Update: @@ -336,6 +351,7 @@ def _to_new(self) -> TodoApiObject: ) raise ValueError(msg) + self.modification_date = Util.now() return TodoApiObject( index=self.index, title=self.title, @@ -370,6 +386,7 @@ def _to_new(self) -> TodoApiObject: after_completion_reference_date=self.after_completion_reference_date, recurrence_rule=self.recurrence_rule, note=self.note, + xx=self.xx, ) def _to_edit(self) -> TodoDeltaApiObject: