-
Notifications
You must be signed in to change notification settings - Fork 98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Delete recurring event? #35
Comments
Sorry the slow response. Deleting a single instance of a recurring event is slightly nontrivial, see https://blog.jonudell.net/2008/08/28/specifying-exceptions-to-recurring-calendar-events/amp/ for details. It is within the scope of the caldav library to support this somehow, but I will not have capacity to look into it anytime soon. |
It's probably several years still until I get time to deal with this. The problem is as such: when running a But what does one actually want to do when running DELETE on a recurrence instance?
Probably it's needed to specify with a separate optional parameter how to deal with recurrence instances - probably with the default being "raise an error". And yes, this is a bit related to #379, because maybe it's needed to load the full recurrence set and put it back to the server to get things done correctly on all servers. |
I don't really know the subject but when I looked the references I saw that |
Commenting on:
I have a recurrent series of events but attendees are assigned only on specific instances of the series. The use case is, that I want to remove the assignment of the attendee(s). I came across the issue because I wanted to delete the event in this case. However, this is not needed, I can simply remove the attendee(s) an leave the event without attendees in the calendar. |
There seems to be a deeper issue here. On a related discussion from sabre dav https://groups.google.com/g/sabredav-discuss/c/M82DQRJTr4A?pli=1 they suggest to work on the level of 'calendar' rather than 'event' objects. Rather than fetching single events, icalendar.Calendar object can be fetched and modified. This solves the problem of deleting single events from a recurrent series of events. Simply get the calendar object which contains the recurrent series and all possible modifications of single events. Then add/remove/update the corresponding items, then put the calendar object back. Important, there are 2 concepts of calendars.
python-caldav does not seem to support working with calendar objects directly, but it is easy to work around this. Here is a working example. Create a series where just one item has an attendee. In this example we first create the 2 corresponding events wrapped by a calendar object. Notice their identical uid. Tested with SOGo. The will be located at <base_url>/6B-664A5280-F-5B831280.ics. data = """
BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
SUMMARY:recurrence with attendee one single item
DTSTART;TZID=Europe/Zurich:20240101T090000
DTEND;TZID=Europe/Zurich:20240101T180000
UID:6B-664A5280-F-5B831280
DESCRIPTION:this is the recurrent series
TRANSP:OPAQUE
RRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH
END:VEVENT
BEGIN:VEVENT
SUMMARY:single item
DTSTART;TZID=Europe/Zurich:20240605T090000
DTEND;TZID=Europe/Zurich:20240605T170000
UID:6B-664A5280-F-5B831280
DESCRIPTION:this is the single item assigning a attendee to just one event
ATTENDEE:foo.bar@corge.baz
RECURRENCE-ID:20240605T070000Z
END:VEVENT
END:VCALENDAR
"""
parent = caldav.DAVClient(...).principal().calendar()
ical_calendar = icalendar.Calendar.from_ical(data)
events = [event for event in ical_calendar.subcomponents if isinstance(event,icalendar.Event)]
assert len(events) == 2
assert events[0].get('RECURRENCE-ID') is None
assert events[1].get('RECURRENCE-ID').dt == datetime.datetime(2024,6,5,7,0,tzinfo=datetime.timezone.utc)
caldav.CalendarObjectResource(
client=parent.client,
data=ical_calendar.to_ical().decode('utf-8'),
parent=parent
).save()
# Important, do not provide a comp_class here. If we put comp_class='Event' we will only receive
# one of the original 2 events
object_by_id = calendar_.calendar.object_by_uid('6B-664A5280-F-5B831280',comp_class=None)
# object_by_id is an event rather than a Calendar,
# but no problem, the desired calendar is present in the underlying data
ical_calendar_ = icalendar.Calendar.from_ical(object_by_id.data)
events = [event for event in ical_calendar_.subcomponents if isinstance(event,icalendar.Event)]
assert len(events) == 2
assert events[0].get('RECURRENCE-ID') is None
assert events[1].get('RECURRENCE-ID').dt == datetime.datetime(2024, 6, 5, 7, 0, tzinfo=datetime.timezone.utc)
ical_calendar__ = icalendar.Calendar()
ical_calendar__.add_component(events[0])
caldav.CalendarObjectResource(
client=parent.client,
data=ical_calendar__.to_ical().decode('utf-8'),
parent=parent
).save()
object_by_id = calendar_.calendar.object_by_uid('6B-664A5280-F-5B831280', comp_class=None)
ical_calendar_ = icalendar.Calendar.from_ical(object_by_id.data)
events = [event for event in ical_calendar_.subcomponents if isinstance(event, icalendar.Event)]
assert len(events) == 1 python-caldav would definitely benefit from a better support for icalendar.calendar objects, but this is another issue. |
A "calendar" is a collection of events/tasks/journals. In the CalDAV specifications, it's defined to have a CalDAV URL and it supports operations like search, hence the What is probably poorly documented is that a caldav.Event-object in some cases may be a recurrent event with all the recurrence instance data included (and sometimes with the recurrence instance data autogenerated, either at the server side or client side), other times such an object may contain only a recurrence instance, and yet in other cases it may contain only the definition of the recurring event. The
This sounds like a bug. I should look into it when I'm more awake.
This should be equivalent to: ical_calendar_ = object_by_id.icalendar_instance I'm recommending to do this in the documentation: ical_calendar_ = object_by_id.icalendar_component Which will give the icalendar component object (event/task/journal) rather than the icalendar calendar object. and hence throw away all the recurrence-data fra
I disagree on that one - but the possibility to edit overridden recurrence instances (and handling them at all) is probably not very well thought-through in the python-caldav library. |
Great explanation. So it all boils down to the following: VCALENDAR objects can be used to fully control recurring events with multiple single instances. Add, modify or delete all or some of the individual instances. I can confirm the issue with # Important, do not provide a comp_class here. If we put comp_class='Event' we will only receive
# one of the original 2 events
object_by_id = calendar_.calendar.object_by_uid('6B-664A5280-F-5B831280',comp_class=caldav.Event)
# object_by_id is an event rather than a Calendar,
# but no problem, the desired calendar is present in the underlying data
ical_calendar_ = object_by_id.icalendar_instance
events = [event for event in ical_calendar_.subcomponents if isinstance(event,icalendar.Event)]
assert len(events) == 1 # expecting 2 |
Yes. By fetching it through There also exists higher-level methods for editing participants in the caldav library - though, it's very poorly tested as it was done as part of a project that lost traction at some point.
I will fork it out as a separate issue. |
Also, I think I've concluded that an |
Also, @ptrba (sorry for the noise here) - why did you consider removing all participants is a better idea than setting status=cancelled? |
Probably equivalent. Have not tried. I went into the direction of editing the events in Calendar object because I have a lot of changes being performed on the object and I wanted to have full control over the entries. Cancelling events will mean they will be dangling around. May or not be problem for the performance, but definitely creates cognitive overhead. The solution presented here works flawlessly and it is clean. What bothered me most was the following Problem (obfuscated and simplified, but there is a real use case behind it). I have a recurrent series of events, say lunch from Monday til Friday from 12-13 pm. I have a special date where I add an attendee, say 1st of May. This will create an clone with the recurrence-id on 20xx0501-1200. Now, we decide to change lunch time from 12 to 12.15. This should affect the regular recurrent series but also the special date (it is only special because it has another attendee). Now I need to change the recurrence-id of the special event. I can do this by either:
wrt to your issue #398: I do not fully understand the role of expansion here. But this will of course depend on the use case. In my use case I use a client side representation which is very close to the original icalendar events. |
Honestly, I have no idea what is best - and what is best may depend quite a bit on he client and server. Actually I believe the CalDAV and icalendar standards are a mess. I started using the CalDAV library because I didn't want to get my hands dirty with low-lever work on those protocols, but had to fix some bugs in the library and soon enough the owner of the project put the maintainer-hat on my head :-) That being said, I believe that according to the standards 12:15 is not a valid time for the As for "expansion", say one searches for "all events between 10:00 and 14:00 on Wednesday in a week", how should the lunch be returned? With |
How to delete only a single occurrence of a recurring event? The
delete()
-method deletes every occurrence.The text was updated successfully, but these errors were encountered: