commit d73b0d2765ec641e5e3d232733d91de07c5fdf97
parent 9ef47194b8cf04fa013c4b497bb2675f77605626
Author: Carlosokumu <carlosokumu254@gmail.com>
Date: Sat, 23 Aug 2025 16:03:02 +0300
extend IcalManager:
- update generate_uuid to generate a globally unique UID using UUID + domain format for new event entries.
- update load_ical_file to return a calendar instance if the the provided ical_path does not exist.
- add check_existing_event to check if an event with same summary,start and end already exists.
Diffstat:
1 file changed, 55 insertions(+), 7 deletions(-)
diff --git a/calendarapp/ical/ical_manager.py b/calendarapp/ical/ical_manager.py
@@ -1,8 +1,11 @@
import os
-from icalendar import Calendar, Event, vDuration
+import uuid
+from zoneinfo import ZoneInfo
+from icalendar import Calendar, Event
from datetime import datetime, timedelta, timezone
from typing import Dict, Any
+
class ICalManager:
"""Handles parsing, representing, and writing iCalendar files"""
@@ -39,7 +42,7 @@ class ICalManager:
if 'uid' in event_data:
event.add('uid', event_data['uid'])
else:
- event.add('uid', self._generate_uid(start_dt))
+ event.add('uid', self._generate_uid())
return event
@@ -103,14 +106,21 @@ class ICalManager:
return timedelta(hours=hours, minutes=minutes)
- def _generate_uid(self, dtstart: datetime) -> str:
- """Generate a unique ID based on event start time."""
- return f"{dtstart.strftime('%Y%m%dT%H%M%S')}@calendarapp"
+
+ def _generate_uid(self) -> str:
+ """Generate a globally unique UID using UUID + domain style."""
+ domain = "calendarapp.org" # you can set this to your app's domain
+ return f"{uuid.uuid4()}@{domain}"
+
def load_ical_file(self, filename: str) -> Calendar:
- """Load an iCalendar file from disk with error handling."""
+ """Load an iCalendar file from disk, or return a new Calendar if missing."""
if not os.path.exists(filename):
- raise FileNotFoundError(f"iCalendar file '{filename}' does not exist.")
+ ## return a new Calendar instance if the provided ical_path is missing
+ cal = Calendar()
+ cal.add('VERSION', '2.0')
+ cal.add('PRODID', '-//CalendarApp//mxm.dk//')
+ return cal
try:
with open(filename, 'rb') as f:
@@ -139,6 +149,44 @@ class ICalManager:
if component.name == "VEVENT":
return component
return None
+
+
+ def check_existing_event(self,cal: Calendar,event_data: Event) -> bool:
+ tzid = event_data.get("tzid")
+
+ candidate_key = (
+ self.normalize_ical_field(event_data.get("start"), tzid),
+ self.normalize_ical_field(event_data.get("end"), tzid),
+ self.normalize_ical_field(event_data.get("summary")),
+ )
+ for component in cal.walk("VEVENT"):
+ existing_key = (
+ self.normalize_ical_field(component.get("DTSTART"), tzid),
+ self.normalize_ical_field(component.get("DTEND"), tzid),
+ self.normalize_ical_field(component.get("SUMMARY")),
+ )
+ if existing_key == candidate_key:
+ return True
+ return False
+
+
+ def normalize_ical_field(self, value, tzid: str | None = None):
+ if isinstance(value, list):
+ value = value[0]
+ if hasattr(value, "dt"):
+ value = value.dt
+
+ if isinstance(value, datetime):
+ if value.tzinfo is None and tzid:
+ return value.replace(tzinfo=ZoneInfo(tzid))
+ return value
+
+ if hasattr(value, "to_ical"):
+ return value.to_ical().decode()
+
+ return str(value)
+
+
def save_ical_file(self, event: Event, filename: str) -> None:
"""Save calendar with event to .ics file."""