Personal noctalia plugins collection
1#!/usr/bin/env python3
2"""Update or delete a VEVENT item via Evolution Data Server."""
3
4import argparse
5import json
6import sys
7from datetime import datetime, timezone
8
9import gi
10gi.require_version("ECal", "2.0")
11gi.require_version("EDataServer", "1.2")
12gi.require_version("ICalGLib", "3.0")
13from gi.repository import ECal, EDataServer, ICalGLib
14
15
16def find_calendar_source(registry, calendar_uid):
17 source = registry.ref_source(calendar_uid)
18 if source and source.has_extension(EDataServer.SOURCE_EXTENSION_CALENDAR):
19 return source
20 for src in registry.list_sources(EDataServer.SOURCE_EXTENSION_CALENDAR):
21 if src.get_display_name() == calendar_uid or src.get_uid() == calendar_uid:
22 return src
23 return None
24
25
26def remove_property(comp, kind):
27 """Remove all properties of the given kind from a component."""
28 prop = comp.get_first_property(kind)
29 while prop:
30 comp.remove_property(prop)
31 prop = comp.get_first_property(kind)
32
33
34def main():
35 parser = argparse.ArgumentParser(description="Update/delete EDS VEVENT item")
36 parser.add_argument("--calendar", required=True, help="Calendar source UID")
37 parser.add_argument("--uid", required=True, help="VEVENT UID")
38 parser.add_argument("--action", required=True, choices=["delete", "update"],
39 help="Action to perform")
40 parser.add_argument("--summary", help="New event summary")
41 parser.add_argument("--location", help="New event location")
42 parser.add_argument("--description", help="New event description")
43 parser.add_argument("--start", type=int, help="New start time (unix timestamp)")
44 parser.add_argument("--end", type=int, help="New end time (unix timestamp)")
45 args = parser.parse_args()
46
47 try:
48 registry = EDataServer.SourceRegistry.new_sync(None)
49 source = find_calendar_source(registry, args.calendar)
50 if not source:
51 print(json.dumps({"success": False, "error": f"Calendar not found: {args.calendar}"}))
52 sys.exit(1)
53
54 client = ECal.Client.connect_sync(
55 source, ECal.ClientSourceType.EVENTS, 1, None
56 )
57
58 if args.action == "delete":
59 client.remove_object_sync(args.uid, None, ECal.ObjModType.ALL, ECal.OperationFlags.NONE, None)
60 print(json.dumps({"success": True}))
61 return
62
63 # For update, fetch the existing component first
64 success, comp = client.get_object_sync(args.uid, None, None)
65 if not success or not comp:
66 print(json.dumps({"success": False, "error": "VEVENT not found"}))
67 sys.exit(1)
68
69 ical = comp.get_icalcomponent()
70
71 if args.summary is not None:
72 ical.set_summary(args.summary)
73
74 if args.location is not None:
75 ical.set_location(args.location)
76
77 if args.description is not None:
78 ical.set_description(args.description)
79
80 if args.start is not None:
81 dt = datetime.fromtimestamp(args.start, tz=timezone.utc)
82 t = ICalGLib.Time.new_null_time()
83 t.set_date(dt.year, dt.month, dt.day)
84 t.set_time(dt.hour, dt.minute, dt.second)
85 t.set_timezone(ICalGLib.Timezone.get_utc_timezone())
86 ical.set_dtstart(t)
87
88 if args.end is not None:
89 dt = datetime.fromtimestamp(args.end, tz=timezone.utc)
90 t = ICalGLib.Time.new_null_time()
91 t.set_date(dt.year, dt.month, dt.day)
92 t.set_time(dt.hour, dt.minute, dt.second)
93 t.set_timezone(ICalGLib.Timezone.get_utc_timezone())
94 ical.set_dtend(t)
95
96 client.modify_object_sync(comp, ECal.ObjModType.ALL, ECal.OperationFlags.NONE, None)
97 print(json.dumps({"success": True}))
98
99 except Exception as e:
100 print(json.dumps({"success": False, "error": str(e)}))
101 sys.exit(1)
102
103
104if __name__ == "__main__":
105 main()