|
@@ -82,6 +82,10 @@ def update_page(page_id, properties, icon=None):
|
|
|
return _request("PATCH", f"/pages/{page_id}", body)
|
|
return _request("PATCH", f"/pages/{page_id}", body)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+def archive_page(page_id):
|
|
|
|
|
+ return _request("PATCH", f"/pages/{page_id}", {"archived": True})
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
# ---------------------------------------------------------------------------
|
|
# ---------------------------------------------------------------------------
|
|
|
# Property extraction
|
|
# Property extraction
|
|
|
# ---------------------------------------------------------------------------
|
|
# ---------------------------------------------------------------------------
|
|
@@ -294,6 +298,7 @@ def sync():
|
|
|
created = 0
|
|
created = 0
|
|
|
updated = 0
|
|
updated = 0
|
|
|
skipped = 0
|
|
skipped = 0
|
|
|
|
|
+ desired_keys = set()
|
|
|
|
|
|
|
|
for page in recurring_pages:
|
|
for page in recurring_pages:
|
|
|
cfg = parse_recurring_event(page)
|
|
cfg = parse_recurring_event(page)
|
|
@@ -306,6 +311,7 @@ def sync():
|
|
|
f"{len(occurrences)} occurrence(s) computed.")
|
|
f"{len(occurrences)} occurrence(s) computed.")
|
|
|
for occ in occurrences:
|
|
for occ in occurrences:
|
|
|
key = (cfg["name"], occ.isoformat())
|
|
key = (cfg["name"], occ.isoformat())
|
|
|
|
|
+ desired_keys.add(key)
|
|
|
props = _build_calendar_properties(cfg, occ, today)
|
|
props = _build_calendar_properties(cfg, occ, today)
|
|
|
icon = cfg["icon"]
|
|
icon = cfg["icon"]
|
|
|
if key in existing:
|
|
if key in existing:
|
|
@@ -315,7 +321,36 @@ def sync():
|
|
|
create_page(props, icon=icon)
|
|
create_page(props, icon=icon)
|
|
|
created += 1
|
|
created += 1
|
|
|
|
|
|
|
|
- print(f"Done. Created {created}, updated {updated}, skipped {skipped}.")
|
|
|
|
|
|
|
+ # Purge stale recurring entries: any calendar page marked as recurring
|
|
|
|
|
+ # that no longer corresponds to a desired (name, date) occurrence.
|
|
|
|
|
+ archived = _purge_stale(calendar_pages, desired_keys)
|
|
|
|
|
+
|
|
|
|
|
+ print(f"Done. Created {created}, updated {updated}, archived {archived}, "
|
|
|
|
|
+ f"skipped {skipped}.")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _purge_stale(calendar_pages, desired_keys):
|
|
|
|
|
+ archived = 0
|
|
|
|
|
+ for page in calendar_pages:
|
|
|
|
|
+ # Skip pages that are already archived / in trash.
|
|
|
|
|
+ if page.get("archived") or page.get("in_trash"):
|
|
|
|
|
+ continue
|
|
|
|
|
+ props = page["properties"]
|
|
|
|
|
+ # Only consider entries flagged as recurring — manual entries are
|
|
|
|
|
+ # always left untouched.
|
|
|
|
|
+ is_recurring = props.get("Is Recurring", {}).get("checkbox", False)
|
|
|
|
|
+ if not is_recurring:
|
|
|
|
|
+ continue
|
|
|
|
|
+ name = _title_text(props["Event Name"])
|
|
|
|
|
+ d = _date_start(props["Date"])
|
|
|
|
|
+ if not name or not d:
|
|
|
|
|
+ continue
|
|
|
|
|
+ key = (name, d.isoformat())
|
|
|
|
|
+ if key not in desired_keys:
|
|
|
|
|
+ archive_page(page["id"])
|
|
|
|
|
+ archived += 1
|
|
|
|
|
+ print(f" Archived stale occurrence: {name} @ {d.isoformat()}")
|
|
|
|
|
+ return archived
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if __name__ == "__main__":
|