fix path
This commit is contained in:
parent
289e502d6a
commit
c79c0d12e5
1 changed files with 0 additions and 0 deletions
110
hosts/www/caldav-export.py
Normal file
110
hosts/www/caldav-export.py
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
import argparse
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
from caldav import DAVClient
|
||||
from icalendar import Calendar
|
||||
|
||||
|
||||
def safe_filename(name):
|
||||
return re.sub(r"[^a-zA-Z0-9._-]+", "_", name)
|
||||
|
||||
|
||||
def export_calendar(url, username, password, output):
|
||||
client = DAVClient(
|
||||
url=url,
|
||||
username=username,
|
||||
password=password,
|
||||
)
|
||||
|
||||
principal = client.principal()
|
||||
|
||||
calendars = principal.calendars()
|
||||
|
||||
target = None
|
||||
for cal in calendars:
|
||||
if cal.name == output["calendar"]:
|
||||
target = cal
|
||||
break
|
||||
|
||||
if target is None:
|
||||
raise RuntimeError(
|
||||
f"Calendar '{output['calendar']}' not found for {username}"
|
||||
)
|
||||
|
||||
print(f"Exporting {username}/{target.name}")
|
||||
|
||||
result = Calendar()
|
||||
result.add("prodid", "-//CalDAV Export//")
|
||||
result.add("version", "2.0")
|
||||
|
||||
objects = target.objects()
|
||||
|
||||
print(f" {len(objects)} objects")
|
||||
|
||||
for obj in objects:
|
||||
cal = Calendar.from_ical(obj.data)
|
||||
|
||||
for component in cal.walk():
|
||||
if component.name != "VCALENDAR":
|
||||
result.add_component(component)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("config")
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
default="/srv/http/ics",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.config) as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
output_dir = Path(args.output)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
combined = Calendar()
|
||||
combined.add("prodid", "-//CalDAV Export Combined//")
|
||||
combined.add("version", "2.0")
|
||||
|
||||
for entry in config["calendars"]:
|
||||
username = entry["username"]
|
||||
password = entry["password"]
|
||||
|
||||
cal = export_calendar(
|
||||
entry["url"],
|
||||
username,
|
||||
password,
|
||||
entry,
|
||||
)
|
||||
|
||||
filename = output_dir / f"{safe_filename(entry['calendar'])}.ics"
|
||||
|
||||
with open(filename, "wb") as f:
|
||||
f.write(cal.to_ical())
|
||||
|
||||
print(f" -> {filename}")
|
||||
|
||||
for component in cal.walk():
|
||||
if component.name != "VCALENDAR":
|
||||
combined.add_component(component)
|
||||
|
||||
combined_file = output_dir / "all-calendars.ics"
|
||||
|
||||
with open(combined_file, "wb") as f:
|
||||
f.write(combined.to_ical())
|
||||
|
||||
print()
|
||||
print(f"Combined export: {combined_file}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Loading…
Reference in a new issue