# HG changeset patch # User Paul Boddie # Date 1425513704 -3600 # Node ID b5744872031ac879950dc3c899a5b202158fbd6b # Parent 7e3930192655c34c2e45ca9059a6146960c80616 Introduced some support for rescheduled/moved events where recurrences are obsoleted because the original event periods have changed, and where new additional or modified recurrences are received to replace them. diff -r 7e3930192655 -r b5744872031a imiptools/content.py --- a/imiptools/content.py Wed Mar 04 18:03:35 2015 +0100 +++ b/imiptools/content.py Thu Mar 05 01:01:44 2015 +0100 @@ -182,7 +182,7 @@ if self.recurrenceid: remove_affected_period(freebusy, self.uid, self.recurrenceid) - def _update_freebusy(self, freebusy, periods, recurrenceid, transp=None): + def _update_freebusy(self, freebusy, periods, recurrenceid, transp=None, replace_all=False): """ Update the 'freebusy' collection with the given 'periods', indicating an @@ -191,18 +191,18 @@ """ update_freebusy(freebusy, periods, transp or self.obj.get_value("TRANSP"), - self.uid, recurrenceid) + self.uid, recurrenceid, replace_all) - def update_freebusy(self, freebusy, periods, transp=None): + def update_freebusy(self, freebusy, periods, transp=None, replace_all=False): """ Update the 'freebusy' collection for this event with the given 'periods'. """ - self._update_freebusy(freebusy, periods, self.recurrenceid, transp) + self._update_freebusy(freebusy, periods, self.recurrenceid, transp, replace_all) - def update_freebusy_for_participant(self, freebusy, periods, attr, for_organiser=False): + def update_freebusy_for_participant(self, freebusy, periods, attr, for_organiser=False, has_moved=False): """ Update the 'freebusy' collection using the given 'periods', subject to @@ -213,13 +213,13 @@ # Organisers employ a special transparency. if for_organiser or attr.get("PARTSTAT") != "DECLINED": - self.update_freebusy(freebusy, periods, transp=(for_organiser and "ORG" or None)) + self.update_freebusy(freebusy, periods, transp=(for_organiser and "ORG" or None), replace_all=has_moved) else: self.remove_from_freebusy(freebusy) # Convenience methods for updating stored free/busy information. - def update_freebusy_from_participant(self, user, participant_item, for_organiser): + def update_freebusy_from_participant(self, user, participant_item, for_organiser, has_moved=False): """ For the given 'user', record the free/busy information for the @@ -240,19 +240,20 @@ # Record in the free/busy details unless a non-participating attendee. self.update_freebusy_for_participant(freebusy, periods, participant_attr, - for_organiser and self.is_not_attendee(participant, self.obj)) + for_organiser and self.is_not_attendee(participant, self.obj), + has_moved) self.remove_freebusy_for_original_recurrence(freebusy) self.store.set_freebusy_for_other(user, freebusy, participant) - def update_freebusy_from_organiser(self, attendee, organiser_item): + def update_freebusy_from_organiser(self, attendee, organiser_item, has_moved=False): """ For the 'attendee', record free/busy information from the 'organiser_item' (a value plus attributes). """ - self.update_freebusy_from_participant(attendee, organiser_item, True) + self.update_freebusy_from_participant(attendee, organiser_item, True, has_moved) def update_freebusy_from_attendees(self, organiser, attendees): @@ -511,6 +512,28 @@ sequence = self.obj.get_value("SEQUENCE") or "0" self.obj["SEQUENCE"] = [(str(int(sequence) + (increment and 1 or 0)), {})] + # Store modifications. + + def handle_moved_event(self, identity): + + """ + Determine whether a complete event is being moved, removing special + recurrences in anticipation of their republication. + """ + + if self.recurrenceid: + return False + + obj = self.get_object(identity) + + if obj.get_utc_datetime("DTSTART") == self.obj.get_utc_datetime("DTSTART"): + return False + + # Remove recurrences associated with old recurrence identifiers. + + self.store.remove_recurrences(identity, self.uid) + return True + def detach_recurrence(self, identity): "Detach the current object from its parent if it is a recurrence." diff -r 7e3930192655 -r b5744872031a imiptools/handlers/person.py --- a/imiptools/handlers/person.py Wed Mar 04 18:03:35 2015 +0100 +++ b/imiptools/handlers/person.py Thu Mar 05 01:01:44 2015 +0100 @@ -20,7 +20,7 @@ """ from imiptools.content import Handler -from imiptools.data import get_uri +from imiptools.data import get_uri, get_window_end from imiptools.dates import format_datetime from imiptools.handlers.common import CommonFreebusy from imiptools.period import replace_overlapping @@ -49,7 +49,11 @@ if not self.have_new_object(attendee): continue - # Set the complete event if not an additional occurrence. + # Remove additional occurrences if a complete event is moved. + + has_moved = self.handle_moved_event(attendee) + + # Set the complete event or an additional occurrence. self.store.set_event(attendee, self.uid, self.recurrenceid, self.obj.to_node()) @@ -61,21 +65,37 @@ if queue: self.store.queue_request(attendee, self.uid, self.recurrenceid) + + # Cancel any request. + elif cancel: self.store.cancel_event(attendee, self.uid, self.recurrenceid) - # No return message will occur to update the free/busy - # information, so this is done here. + # Update free/busy details. + + if cancel or has_moved: + + # Upon cancellation, no return message will occur to update + # the free/busy information, so this is done here. freebusy = self.store.get_freebusy(attendee) - self.remove_from_freebusy(freebusy) + + # Moved events must update the free/busy details, removing + # obsolete detached recurrences. + + if has_moved: + tzid = self.get_tzid(attendee) + periods = self.obj.get_periods_for_freebusy(tzid, get_window_end(tzid)) + self.update_freebusy(freebusy, periods, replace_all=True) + else: + self.remove_from_freebusy(freebusy) self.store.set_freebusy(attendee, freebusy) if self.publisher: self.publisher.set_freebusy(attendee, freebusy) - self.update_freebusy_from_organiser(attendee, organiser_item) + self.update_freebusy_from_organiser(attendee, organiser_item, has_moved) # As organiser, update attendance. diff -r 7e3930192655 -r b5744872031a imiptools/handlers/person_outgoing.py --- a/imiptools/handlers/person_outgoing.py Wed Mar 04 18:03:35 2015 +0100 +++ b/imiptools/handlers/person_outgoing.py Thu Mar 05 01:01:44 2015 +0100 @@ -57,11 +57,16 @@ if from_organiser: + # Remove additional occurrences if a complete event is moved. + + has_moved = self.handle_moved_event(identity) + # Set the complete event or an additional occurrence. self.store.set_event(identity, self.uid, self.recurrenceid, self.obj.to_node()) else: + has_moved = False organiser_item, attendees = self.require_organiser_and_attendees(from_organiser) self.merge_attendance(attendees, identity) @@ -96,7 +101,8 @@ periods = obj.get_periods_for_freebusy(tzid, get_window_end(tzid)) self.update_freebusy_for_participant(freebusy, periods, attr, - from_organiser and self.is_not_attendee(identity, obj)) + from_organiser and self.is_not_attendee(identity, obj), + has_moved) self.remove_freebusy_for_original_recurrence(freebusy) self.store.set_freebusy(identity, freebusy) diff -r 7e3930192655 -r b5744872031a imiptools/handlers/resource.py --- a/imiptools/handlers/resource.py Wed Mar 04 18:03:35 2015 +0100 +++ b/imiptools/handlers/resource.py Thu Mar 05 01:01:44 2015 +0100 @@ -82,6 +82,10 @@ self.update_dtstamp() + # Remove additional occurrences if a complete event is moved. + + has_moved = self.handle_moved_event(attendee) + # Set the complete event or an additional occurrence. event = self.obj.to_node() @@ -94,7 +98,7 @@ # Only update free/busy details if the event is scheduled. if scheduled: - self.update_freebusy(freebusy, periods) + self.update_freebusy(freebusy, periods, replace_all=has_moved) else: self.remove_from_freebusy(freebusy) diff -r 7e3930192655 -r b5744872031a imiptools/period.py --- a/imiptools/period.py Wed Mar 04 18:03:35 2015 +0100 +++ b/imiptools/period.py Thu Mar 05 01:01:44 2015 +0100 @@ -67,7 +67,7 @@ insort_left(freebusy, period) -def remove_period(freebusy, uid, recurrenceid=None): +def remove_period(freebusy, uid, recurrenceid=None, remove_all=False): """ Remove from 'freebusy' all periods associated with 'uid' and 'recurrenceid' @@ -77,7 +77,7 @@ i = 0 while i < len(freebusy): t = freebusy[i] - if len(t) >= 5 and t[2] == uid and t[4] == recurrenceid: + if len(t) >= 5 and t[2] == uid and (remove_all or t[4] == recurrenceid): del freebusy[i] else: i += 1 @@ -403,14 +403,14 @@ return start, end, uid, recurrenceid, key -def update_freebusy(freebusy, periods, transp, uid, recurrenceid): +def update_freebusy(freebusy, periods, transp, uid, recurrenceid, replace_all=False): """ Update the free/busy details with the given 'periods', 'transp' setting and 'uid' plus 'recurrenceid'. """ - remove_period(freebusy, uid, recurrenceid) + remove_period(freebusy, uid, recurrenceid, replace_all) for start, end in periods: insert_period(freebusy, (start, end, uid, transp, recurrenceid))