Personal noctalia plugins collection
at 2b08e6ad749a4073455246675bfd7021a9c3d054 127 lines 5.2 kB view raw
1#!/usr/bin/env python3 2"""Update or delete a VTODO 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_task_source(registry, task_list_uid): 17 source = registry.ref_source(task_list_uid) 18 if source and source.has_extension(EDataServer.SOURCE_EXTENSION_TASK_LIST): 19 return source 20 for src in registry.list_sources(EDataServer.SOURCE_EXTENSION_TASK_LIST): 21 if src.get_display_name() == task_list_uid or src.get_uid() == task_list_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 VTODO item") 36 parser.add_argument("--task-list", required=True, help="Task list UID") 37 parser.add_argument("--uid", required=True, help="VTODO UID") 38 parser.add_argument("--action", required=True, choices=["complete", "uncomplete", "delete", "update"], 39 help="Action to perform") 40 parser.add_argument("--summary", help="New task summary (for update)") 41 parser.add_argument("--description", help="New task description (for update)") 42 parser.add_argument("--due", type=int, help="New due date as unix timestamp (for update)") 43 parser.add_argument("--priority", type=int, help="New priority 0-9 (for update)") 44 args = parser.parse_args() 45 46 try: 47 registry = EDataServer.SourceRegistry.new_sync(None) 48 source = find_task_source(registry, args.task_list) 49 if not source: 50 print(json.dumps({"success": False, "error": f"Task list not found: {args.task_list}"})) 51 sys.exit(1) 52 53 client = ECal.Client.connect_sync( 54 source, ECal.ClientSourceType.TASKS, 1, None 55 ) 56 57 if args.action == "delete": 58 client.remove_object_sync(args.uid, None, ECal.ObjModType.ALL, ECal.OperationFlags.NONE, None) 59 print(json.dumps({"success": True})) 60 return 61 62 # For complete/uncomplete, fetch the existing component first 63 success, comp = client.get_object_sync(args.uid, None, None) 64 if not success or not comp: 65 print(json.dumps({"success": False, "error": "VTODO not found"})) 66 sys.exit(1) 67 68 ical = comp.get_icalcomponent() 69 70 if args.action == "complete": 71 ical.set_status(ICalGLib.PropertyStatus.COMPLETED) 72 73 # Set PERCENT-COMPLETE to 100 74 remove_property(ical, ICalGLib.PropertyKind.PERCENTCOMPLETE_PROPERTY) 75 prop = ICalGLib.Property.new_percentcomplete(100) 76 ical.add_property(prop) 77 78 # Set COMPLETED timestamp 79 remove_property(ical, ICalGLib.PropertyKind.COMPLETED_PROPERTY) 80 now = datetime.now(timezone.utc) 81 completed_time = ICalGLib.Time.new_null_time() 82 completed_time.set_date(now.year, now.month, now.day) 83 completed_time.set_time(now.hour, now.minute, now.second) 84 completed_time.set_timezone(ICalGLib.Timezone.get_utc_timezone()) 85 prop = ICalGLib.Property.new_completed(completed_time) 86 ical.add_property(prop) 87 88 elif args.action == "uncomplete": 89 ical.set_status(ICalGLib.PropertyStatus.NEEDSACTION) 90 91 # Set PERCENT-COMPLETE to 0 92 remove_property(ical, ICalGLib.PropertyKind.PERCENTCOMPLETE_PROPERTY) 93 prop = ICalGLib.Property.new_percentcomplete(0) 94 ical.add_property(prop) 95 96 # Remove COMPLETED timestamp 97 remove_property(ical, ICalGLib.PropertyKind.COMPLETED_PROPERTY) 98 99 elif args.action == "update": 100 if args.summary is not None: 101 ical.set_summary(args.summary) 102 if args.description is not None: 103 ical.set_description(args.description) 104 if args.due is not None: 105 dt = datetime.fromtimestamp(args.due, tz=timezone.utc) 106 t = ICalGLib.Time.new_null_time() 107 t.set_date(dt.year, dt.month, dt.day) 108 t.set_time(dt.hour, dt.minute, dt.second) 109 t.set_timezone(ICalGLib.Timezone.get_utc_timezone()) 110 remove_property(ical, ICalGLib.PropertyKind.DUE_PROPERTY) 111 prop = ICalGLib.Property.new_due(t) 112 ical.add_property(prop) 113 if args.priority is not None: 114 remove_property(ical, ICalGLib.PropertyKind.PRIORITY_PROPERTY) 115 prop = ICalGLib.Property.new_priority(args.priority) 116 ical.add_property(prop) 117 118 client.modify_object_sync(comp, ECal.ObjModType.ALL, ECal.OperationFlags.NONE, None) 119 print(json.dumps({"success": True})) 120 121 except Exception as e: 122 print(json.dumps({"success": False, "error": str(e)})) 123 sys.exit(1) 124 125 126if __name__ == "__main__": 127 main()