fromtypingimportOptionalfromicalendarimportCalendar,Eventfromx_wr_timezoneimportto_standarddefgenerate_default_prodid()->str:"""Generate a default PRODID."""return"-//mergecal.org//MergeCal//EN"
[docs]classCalendarMerger:"""Merge multiple calendars into one."""def__init__(self,calendars:list[Calendar],prodid:Optional[str]=None,version:str="2.0",method:Optional[str]=None,):self.merged_calendar=Calendar()# Set required propertiesself.merged_calendar.add("prodid",prodidorgenerate_default_prodid())self.merged_calendar.add("version",version)# Set optional properties if providedifmethod:self.merged_calendar.add("method",method)self.calendars:list[Calendar]=[]forcalendarincalendars:self.add_calendar(calendar)
[docs]defadd_calendar(self,calendar:Calendar)->None:"""Add a calendar to be merged."""self.calendars.append(to_standard(calendar,add_timezone_component=True))
[docs]defmerge(self)->Calendar:"""Merge the calendars."""existing_uids:set[tuple[Optional[str],int,Optional[str]]]=set()no_uid_events:list[Event]=[]tzids:set[str]=set()forcalinself.calendars:fortimezoneincal.timezones:iftimezone.tz_namenotintzids:self.merged_calendar.add_component(timezone)tzids.add(timezone.tz_name)foreventincal.events:uid=event.get("uid",None)sequence=event.get("sequence",0)recurrence_id=event.get("recurrence-id",None)# Create a unique identifier for the componentcomponent_id=(uid,sequence,recurrence_id)ifuidisNone:ifeventinno_uid_events:continueno_uid_events.append(event)elifcomponent_idinexisting_uids:continueexisting_uids.add(component_id)self.merged_calendar.add_component(event)returnself.merged_calendar
[docs]defmerge_calendars(calendars:list[Calendar],**kwargs:object)->Calendar:"""Convenience function to merge calendars."""merger=CalendarMerger(calendars,**kwargs)# type: ignorereturnmerger.merge()