diff --git a/hosts/www/caldav-export.py b/hosts/www/caldav-export.py index 814a54b..8ef1130 100644 --- a/hosts/www/caldav-export.py +++ b/hosts/www/caldav-export.py @@ -1,4 +1,5 @@ import argparse +import os import re from pathlib import Path @@ -11,12 +12,7 @@ def safe_filename(name: str) -> str: return re.sub(r"[^a-zA-Z0-9._-]+", "_", name) -def export_calendar( - url: str, - username: str, - password: str, - calendar: str, -) -> Calendar: +def export_calendar(url: str, username: str, password: str, calendar: str) -> Calendar: client = DAVClient( url=url, username=username, @@ -62,45 +58,13 @@ def export_calendar( cal = Calendar.from_ical(data) - # Only copy top-level components. - # - # Do NOT use cal.walk() here. walk() also returns the - # STANDARD/DAYLIGHT components inside VTIMEZONE. - for component in cal.subcomponents: - result.add_component(component) + for component in cal.walk(): + if component.name != "VCALENDAR": + result.add_component(component) return result -def add_normalized_components( - target: Calendar, - source: Calendar, - timezones: dict[str, object], -) -> None: - """ - Add components from source to target while collecting VTIMEZONE - components separately. - - Exactly one VTIMEZONE is retained for each TZID. - """ - - for component in source.subcomponents: - if component.name == "VTIMEZONE": - tzid = component.get("TZID") - - if tzid is None: - print(" warning: ignoring VTIMEZONE without TZID") - continue - - tzid = str(tzid) - - if tzid not in timezones: - timezones[tzid] = component - - else: - target.add_component(component) - - def main(): parser = argparse.ArgumentParser() parser.add_argument("config") @@ -117,16 +81,12 @@ def main(): output_dir = Path(args.output) output_dir.mkdir(parents=True, exist_ok=True) - username = config["username"] - password = config["password"] - combined = Calendar() combined.add("prodid", "-//Combined calendar//") combined.add("version", "2.0") - # TZID -> VTIMEZONE component - timezones = {} - + username = config["username"] + password = config["password"] for entry in config["calendars"]: cal = export_calendar( entry["url"], @@ -137,37 +97,19 @@ def main(): filename = output_dir / f"{safe_filename(entry['calendar'])}.ics" - # Individual calendar export stays as-is. with open(filename, "wb") as f: f.write(cal.to_ical()) print(f" -> {filename}") - # Normalize components for the combined calendar. - add_normalized_components( - combined, - cal, - timezones, - ) - - # Add each VTIMEZONE exactly once. - # - # Put them before events/components in the resulting VCALENDAR. - normalized = Calendar() - normalized.add("prodid", "-//Combined calendar//") - normalized.add("version", "2.0") - - for tzid, timezone in timezones.items(): - print(f" timezone: {tzid}") - normalized.add_component(timezone) - - for component in combined.subcomponents: - normalized.add_component(component) + for component in cal.walk(): + if component.name != "VCALENDAR": + combined.add_component(component) combined_file = output_dir / "all.ics" with open(combined_file, "wb") as f: - f.write(normalized.to_ical()) + f.write(combined.to_ical()) print() print(f"Combined export: {combined_file}")