1.1 --- a/imiptools/content.py Wed Mar 04 18:03:35 2015 +0100
1.2 +++ b/imiptools/content.py Thu Mar 05 01:01:44 2015 +0100
1.3 @@ -182,7 +182,7 @@
1.4 if self.recurrenceid:
1.5 remove_affected_period(freebusy, self.uid, self.recurrenceid)
1.6
1.7 - def _update_freebusy(self, freebusy, periods, recurrenceid, transp=None):
1.8 + def _update_freebusy(self, freebusy, periods, recurrenceid, transp=None, replace_all=False):
1.9
1.10 """
1.11 Update the 'freebusy' collection with the given 'periods', indicating an
1.12 @@ -191,18 +191,18 @@
1.13 """
1.14
1.15 update_freebusy(freebusy, periods, transp or self.obj.get_value("TRANSP"),
1.16 - self.uid, recurrenceid)
1.17 + self.uid, recurrenceid, replace_all)
1.18
1.19 - def update_freebusy(self, freebusy, periods, transp=None):
1.20 + def update_freebusy(self, freebusy, periods, transp=None, replace_all=False):
1.21
1.22 """
1.23 Update the 'freebusy' collection for this event with the given
1.24 'periods'.
1.25 """
1.26
1.27 - self._update_freebusy(freebusy, periods, self.recurrenceid, transp)
1.28 + self._update_freebusy(freebusy, periods, self.recurrenceid, transp, replace_all)
1.29
1.30 - def update_freebusy_for_participant(self, freebusy, periods, attr, for_organiser=False):
1.31 + def update_freebusy_for_participant(self, freebusy, periods, attr, for_organiser=False, has_moved=False):
1.32
1.33 """
1.34 Update the 'freebusy' collection using the given 'periods', subject to
1.35 @@ -213,13 +213,13 @@
1.36 # Organisers employ a special transparency.
1.37
1.38 if for_organiser or attr.get("PARTSTAT") != "DECLINED":
1.39 - self.update_freebusy(freebusy, periods, transp=(for_organiser and "ORG" or None))
1.40 + self.update_freebusy(freebusy, periods, transp=(for_organiser and "ORG" or None), replace_all=has_moved)
1.41 else:
1.42 self.remove_from_freebusy(freebusy)
1.43
1.44 # Convenience methods for updating stored free/busy information.
1.45
1.46 - def update_freebusy_from_participant(self, user, participant_item, for_organiser):
1.47 + def update_freebusy_from_participant(self, user, participant_item, for_organiser, has_moved=False):
1.48
1.49 """
1.50 For the given 'user', record the free/busy information for the
1.51 @@ -240,19 +240,20 @@
1.52 # Record in the free/busy details unless a non-participating attendee.
1.53
1.54 self.update_freebusy_for_participant(freebusy, periods, participant_attr,
1.55 - for_organiser and self.is_not_attendee(participant, self.obj))
1.56 + for_organiser and self.is_not_attendee(participant, self.obj),
1.57 + has_moved)
1.58
1.59 self.remove_freebusy_for_original_recurrence(freebusy)
1.60 self.store.set_freebusy_for_other(user, freebusy, participant)
1.61
1.62 - def update_freebusy_from_organiser(self, attendee, organiser_item):
1.63 + def update_freebusy_from_organiser(self, attendee, organiser_item, has_moved=False):
1.64
1.65 """
1.66 For the 'attendee', record free/busy information from the
1.67 'organiser_item' (a value plus attributes).
1.68 """
1.69
1.70 - self.update_freebusy_from_participant(attendee, organiser_item, True)
1.71 + self.update_freebusy_from_participant(attendee, organiser_item, True, has_moved)
1.72
1.73 def update_freebusy_from_attendees(self, organiser, attendees):
1.74
1.75 @@ -511,6 +512,28 @@
1.76 sequence = self.obj.get_value("SEQUENCE") or "0"
1.77 self.obj["SEQUENCE"] = [(str(int(sequence) + (increment and 1 or 0)), {})]
1.78
1.79 + # Store modifications.
1.80 +
1.81 + def handle_moved_event(self, identity):
1.82 +
1.83 + """
1.84 + Determine whether a complete event is being moved, removing special
1.85 + recurrences in anticipation of their republication.
1.86 + """
1.87 +
1.88 + if self.recurrenceid:
1.89 + return False
1.90 +
1.91 + obj = self.get_object(identity)
1.92 +
1.93 + if obj.get_utc_datetime("DTSTART") == self.obj.get_utc_datetime("DTSTART"):
1.94 + return False
1.95 +
1.96 + # Remove recurrences associated with old recurrence identifiers.
1.97 +
1.98 + self.store.remove_recurrences(identity, self.uid)
1.99 + return True
1.100 +
1.101 def detach_recurrence(self, identity):
1.102
1.103 "Detach the current object from its parent if it is a recurrence."
2.1 --- a/imiptools/handlers/person.py Wed Mar 04 18:03:35 2015 +0100
2.2 +++ b/imiptools/handlers/person.py Thu Mar 05 01:01:44 2015 +0100
2.3 @@ -20,7 +20,7 @@
2.4 """
2.5
2.6 from imiptools.content import Handler
2.7 -from imiptools.data import get_uri
2.8 +from imiptools.data import get_uri, get_window_end
2.9 from imiptools.dates import format_datetime
2.10 from imiptools.handlers.common import CommonFreebusy
2.11 from imiptools.period import replace_overlapping
2.12 @@ -49,7 +49,11 @@
2.13 if not self.have_new_object(attendee):
2.14 continue
2.15
2.16 - # Set the complete event if not an additional occurrence.
2.17 + # Remove additional occurrences if a complete event is moved.
2.18 +
2.19 + has_moved = self.handle_moved_event(attendee)
2.20 +
2.21 + # Set the complete event or an additional occurrence.
2.22
2.23 self.store.set_event(attendee, self.uid, self.recurrenceid, self.obj.to_node())
2.24
2.25 @@ -61,21 +65,37 @@
2.26
2.27 if queue:
2.28 self.store.queue_request(attendee, self.uid, self.recurrenceid)
2.29 +
2.30 + # Cancel any request.
2.31 +
2.32 elif cancel:
2.33 self.store.cancel_event(attendee, self.uid, self.recurrenceid)
2.34
2.35 - # No return message will occur to update the free/busy
2.36 - # information, so this is done here.
2.37 + # Update free/busy details.
2.38 +
2.39 + if cancel or has_moved:
2.40 +
2.41 + # Upon cancellation, no return message will occur to update
2.42 + # the free/busy information, so this is done here.
2.43
2.44 freebusy = self.store.get_freebusy(attendee)
2.45 - self.remove_from_freebusy(freebusy)
2.46 +
2.47 + # Moved events must update the free/busy details, removing
2.48 + # obsolete detached recurrences.
2.49 +
2.50 + if has_moved:
2.51 + tzid = self.get_tzid(attendee)
2.52 + periods = self.obj.get_periods_for_freebusy(tzid, get_window_end(tzid))
2.53 + self.update_freebusy(freebusy, periods, replace_all=True)
2.54 + else:
2.55 + self.remove_from_freebusy(freebusy)
2.56
2.57 self.store.set_freebusy(attendee, freebusy)
2.58
2.59 if self.publisher:
2.60 self.publisher.set_freebusy(attendee, freebusy)
2.61
2.62 - self.update_freebusy_from_organiser(attendee, organiser_item)
2.63 + self.update_freebusy_from_organiser(attendee, organiser_item, has_moved)
2.64
2.65 # As organiser, update attendance.
2.66
3.1 --- a/imiptools/handlers/person_outgoing.py Wed Mar 04 18:03:35 2015 +0100
3.2 +++ b/imiptools/handlers/person_outgoing.py Thu Mar 05 01:01:44 2015 +0100
3.3 @@ -57,11 +57,16 @@
3.4
3.5 if from_organiser:
3.6
3.7 + # Remove additional occurrences if a complete event is moved.
3.8 +
3.9 + has_moved = self.handle_moved_event(identity)
3.10 +
3.11 # Set the complete event or an additional occurrence.
3.12
3.13 self.store.set_event(identity, self.uid, self.recurrenceid, self.obj.to_node())
3.14
3.15 else:
3.16 + has_moved = False
3.17 organiser_item, attendees = self.require_organiser_and_attendees(from_organiser)
3.18 self.merge_attendance(attendees, identity)
3.19
3.20 @@ -96,7 +101,8 @@
3.21 periods = obj.get_periods_for_freebusy(tzid, get_window_end(tzid))
3.22
3.23 self.update_freebusy_for_participant(freebusy, periods, attr,
3.24 - from_organiser and self.is_not_attendee(identity, obj))
3.25 + from_organiser and self.is_not_attendee(identity, obj),
3.26 + has_moved)
3.27
3.28 self.remove_freebusy_for_original_recurrence(freebusy)
3.29 self.store.set_freebusy(identity, freebusy)
4.1 --- a/imiptools/handlers/resource.py Wed Mar 04 18:03:35 2015 +0100
4.2 +++ b/imiptools/handlers/resource.py Thu Mar 05 01:01:44 2015 +0100
4.3 @@ -82,6 +82,10 @@
4.4
4.5 self.update_dtstamp()
4.6
4.7 + # Remove additional occurrences if a complete event is moved.
4.8 +
4.9 + has_moved = self.handle_moved_event(attendee)
4.10 +
4.11 # Set the complete event or an additional occurrence.
4.12
4.13 event = self.obj.to_node()
4.14 @@ -94,7 +98,7 @@
4.15 # Only update free/busy details if the event is scheduled.
4.16
4.17 if scheduled:
4.18 - self.update_freebusy(freebusy, periods)
4.19 + self.update_freebusy(freebusy, periods, replace_all=has_moved)
4.20 else:
4.21 self.remove_from_freebusy(freebusy)
4.22
5.1 --- a/imiptools/period.py Wed Mar 04 18:03:35 2015 +0100
5.2 +++ b/imiptools/period.py Thu Mar 05 01:01:44 2015 +0100
5.3 @@ -67,7 +67,7 @@
5.4
5.5 insort_left(freebusy, period)
5.6
5.7 -def remove_period(freebusy, uid, recurrenceid=None):
5.8 +def remove_period(freebusy, uid, recurrenceid=None, remove_all=False):
5.9
5.10 """
5.11 Remove from 'freebusy' all periods associated with 'uid' and 'recurrenceid'
5.12 @@ -77,7 +77,7 @@
5.13 i = 0
5.14 while i < len(freebusy):
5.15 t = freebusy[i]
5.16 - if len(t) >= 5 and t[2] == uid and t[4] == recurrenceid:
5.17 + if len(t) >= 5 and t[2] == uid and (remove_all or t[4] == recurrenceid):
5.18 del freebusy[i]
5.19 else:
5.20 i += 1
5.21 @@ -403,14 +403,14 @@
5.22
5.23 return start, end, uid, recurrenceid, key
5.24
5.25 -def update_freebusy(freebusy, periods, transp, uid, recurrenceid):
5.26 +def update_freebusy(freebusy, periods, transp, uid, recurrenceid, replace_all=False):
5.27
5.28 """
5.29 Update the free/busy details with the given 'periods', 'transp' setting and
5.30 'uid' plus 'recurrenceid'.
5.31 """
5.32
5.33 - remove_period(freebusy, uid, recurrenceid)
5.34 + remove_period(freebusy, uid, recurrenceid, replace_all)
5.35
5.36 for start, end in periods:
5.37 insert_period(freebusy, (start, end, uid, transp, recurrenceid))