From 4bd388ca3a76f6eb3d9178d5d8d0d2968f79afe1 Mon Sep 17 00:00:00 2001 From: Kingsley Yung Date: Sun, 28 Apr 2024 17:33:39 +0800 Subject: [PATCH] Change CalDAV description format Old mappings: - TW `annotations`, `uuid` <-> Caldav `DESCRIPTION` When a TW item is converted to caldav item, the `annotations` and `uuid` of TW item are encoded into the following format, and stored in `DESCRIPTION` of caldav. ```plaintext IMPORTED FROM TASKWARRIOR * Annotation 1: first annotation * Annotation 2: second annotation * Annotation 3: third annotation * uuid: 12345678-123-1234-1234-1234567890ab ``` New mappings: - TW `uuid` <-> Caldav `X-SYNCALL-TW-UUID` - TW `annotations` <-> Caldav `DESCRIPTION` (each annotation <-> a line in description) In this new mappings, the `uuid` of TW item will be mapped to a non-standard iCalendar field `X-SYNCALL-TW-UUID`, allowed by RFC 5545. The `annotation` of TW item will be mapped to `DESCRIPTION` of caldav item. Each line in `DESCRIPTION` is corresponding to an annotation. --- docs/readme-tw-caldav.md | 3 ++- syncall/caldav/caldav_side.py | 2 ++ syncall/caldav/caldav_utils.py | 1 + syncall/tw_caldav_utils.py | 19 ++++++++------ tests/test_data/sample_items.yaml | 41 +++++++++---------------------- 5 files changed, 29 insertions(+), 37 deletions(-) diff --git a/docs/readme-tw-caldav.md b/docs/readme-tw-caldav.md index f586f54..6e662f0 100644 --- a/docs/readme-tw-caldav.md +++ b/docs/readme-tw-caldav.md @@ -29,7 +29,8 @@ TW <-> Caldav will make the following mappings between items: - `L` <-> 9 - `M` <-> 5 - `H` <-> 1 -- TW `annotations`, `uuid` <-> `DESCRIPTION` +- TW `annotations` <-> `DESCRIPTION` (one annotation <-> one line in description) +- TW `uuid` <-> `X-SYNCALL-TW-UUID` - TW `tags` <-> `CATEGORIES` ### Current limitations diff --git a/syncall/caldav/caldav_side.py b/syncall/caldav/caldav_side.py index d864634..5dce5f4 100644 --- a/syncall/caldav/caldav_side.py +++ b/syncall/caldav/caldav_side.py @@ -27,6 +27,7 @@ class CaldavSide(SyncSide): "status", "summary", "due", + "x-syncall-tw-uuid", ) _date_keys: tuple[str] = ("end", "start", "last-modified") @@ -144,6 +145,7 @@ def add_item(self, item): categories=item.get("categories"), created=item.get("created"), completed=item.get("completed"), + x_syncall_tw_uuid=item.get("x-syncall-tw-uuid"), ) return map_ics_to_item(icalendar_component(todo)) diff --git a/syncall/caldav/caldav_utils.py b/syncall/caldav/caldav_utils.py index 2d22a81..40f5752 100644 --- a/syncall/caldav/caldav_utils.py +++ b/syncall/caldav/caldav_utils.py @@ -46,6 +46,7 @@ def _convert_one(name: str) -> str: todo_item[name] = _convert_one(name).lower() for name in ["description", "summary"]: todo_item[name] = _convert_one(name) + todo_item["uuid"] = _convert_one("x-syncall-tw-uuid") for date_field in ("due", "created", "completed", "last-modified"): if vtodo.get(date_field): diff --git a/syncall/tw_caldav_utils.py b/syncall/tw_caldav_utils.py index 06ad116..0f13ac5 100644 --- a/syncall/tw_caldav_utils.py +++ b/syncall/tw_caldav_utils.py @@ -37,13 +37,11 @@ def convert_tw_to_caldav(tw_item: Item) -> Item: caldav_item["summary"] = tw_item["description"] # description - caldav_item["description"] = "IMPORTED FROM TASKWARRIOR\n" - if "annotations" in tw_item: - for i, annotation in enumerate(tw_item["annotations"]): - caldav_item["description"] += f"\n* Annotation {i + 1}: {annotation}" + if "annotations" in tw_item.keys(): + caldav_item["description"] = "\n".join(tw_item["annotations"]) - caldav_item["description"] += "\n" - caldav_item["description"] += f'\n* uuid: {tw_item["uuid"]}' + # uuid + caldav_item["x-syncall-tw-uuid"] = f'{tw_item["uuid"]}' # Status caldav_item["status"] = aliases_tw_caldav_status[tw_item["status"]] @@ -77,7 +75,7 @@ def convert_tw_to_caldav(tw_item: Item) -> Item: def convert_caldav_to_tw(caldav_item: Item) -> Item: - # Parse the description + # Parse the description by old format annotations, uuid = parse_caldav_item_desc(caldav_item) assert isinstance(annotations, list) assert isinstance(uuid, UUID) or uuid is None @@ -93,6 +91,13 @@ def convert_caldav_to_tw(caldav_item: Item) -> Item: tw_item["annotations"] = annotations if uuid is not None: tw_item["uuid"] = uuid + else: # if uuid is not found, try new format + if "description" in caldav_item.keys(): + tw_item["annotations"] = [ + line.strip() for line in caldav_item["description"].split("\n") if line + ] + if "x-syncall-tw-uuid" in caldav_item.keys(): + tw_item["uuid"] = UUID(caldav_item["x-syncall-tw-uuid"]) # Status tw_item["status"] = aliases_caldav_tw_status[caldav_item["status"]] diff --git a/tests/test_data/sample_items.yaml b/tests/test_data/sample_items.yaml index efc2b36..a3f1e2d 100644 --- a/tests/test_data/sample_items.yaml +++ b/tests/test_data/sample_items.yaml @@ -35,35 +35,25 @@ tw_item_w_due: - This is the annotation text - This is the other annotation text caldav_item_expected: - description: 'IMPORTED FROM TASKWARRIOR - - - * Annotation 1: This is the annotation text - - * Annotation 2: This is the other annotation text - - - * uuid: 00208973-20da-4988-ae3e-58ef3650c363' + description: |- + This is the annotation text + This is the other annotation text summary: Kalimera! last-modified: 2019-03-05 00:03:09 status: 'needs-action' categories: ['remindme'] + x-syncall-tw-uuid: '00208973-20da-4988-ae3e-58ef3650c363' caldav_item_w_date_expected: - description: 'IMPORTED FROM TASKWARRIOR - - - * Annotation 1: This is the annotation text - - * Annotation 2: This is the other annotation text - - - * uuid: 00208973-20da-4988-ae3e-58ef3650c363' + description: |- + This is the annotation text + This is the other annotation text due: 2019-03-09 00:00:00 start: 2019-03-08 23:00:00 summary: Kalimera! last-modified: 2019-03-05 00:03:09 status: 'needs-action' categories: ['remindme'] + x-syncall-tw-uuid: '00208973-20da-4988-ae3e-58ef3650c363' gcal_item_expected: description: 'IMPORTED FROM TASKWARRIOR @@ -150,17 +140,9 @@ gcal_item: summary: another summary goes here updated: '2019-03-08T00:29:06.602Z' caldav_item: - description: ' - - The same old description - - - * Annotation 1: kalimera - - * Annotation 2: kalinuxta kai kali vradia - - - * uuid: 00208973-20da-4988-ae3e-58ef3650c363' + description: |- + kalimera + kalinuxta kai kali vradia due: 2019-03-04 05:00:00 id: 52ggba92l9ph4d0ghabi3ohdh7 start: 2019-03-04 04:00:00 @@ -168,6 +150,7 @@ caldav_item: summary: another summary goes here last-modified: 2019-03-08 00:29:06.602 priority: '' + x-syncall-tw-uuid: '00208973-20da-4988-ae3e-58ef3650c363' tw_item_expected: syncallduration: "PT3600S" annotations: