1.1 --- a/imiptools/client.py Sat Oct 17 19:08:30 2015 +0200
1.2 +++ b/imiptools/client.py Sat Oct 17 19:15:22 2015 +0200
1.3 @@ -23,7 +23,7 @@
1.4 from imiptools import config
1.5 from imiptools.data import Object, get_address, get_uri, get_window_end, \
1.6 is_new_object, make_freebusy, to_part, \
1.7 - uri_dict, uri_items, uri_parts, uri_values
1.8 + uri_dict, uri_item, uri_items, uri_parts, uri_values
1.9 from imiptools.dates import check_permitted_values, format_datetime, get_default_timezone, \
1.10 get_duration, get_timestamp
1.11 from imiptools.period import can_schedule, remove_period, \
1.12 @@ -291,6 +291,79 @@
1.13
1.14 update_freebusy(freebusy, periods, transp, uid, recurrenceid, summary, organiser, expires)
1.15
1.16 + # Preparation of messages communicating the state of events.
1.17 +
1.18 + def get_message_parts(self, obj, method, attendee=None):
1.19 +
1.20 + """
1.21 + Return a tuple containing a list of methods and a list of message parts,
1.22 + with the parts collectively describing the given object 'obj' and its
1.23 + recurrences, using 'method' as the means of publishing details (with
1.24 + CANCEL being used to retract or remove details).
1.25 +
1.26 + If 'attendee' is indicated, the attendee's participation will be taken
1.27 + into account when generating the description.
1.28 + """
1.29 +
1.30 + # Assume that the outcome will be composed of requests and
1.31 + # cancellations. It would not seem completely bizarre to produce
1.32 + # publishing messages if a refresh message was unprovoked.
1.33 +
1.34 + responses = []
1.35 + methods = set()
1.36 +
1.37 + # Get the parent event, add SENT-BY details to the organiser.
1.38 +
1.39 + if not attendee or self.is_participating(attendee, obj=obj):
1.40 + organiser, organiser_attr = uri_item(obj.get_item("ORGANIZER"))
1.41 + self.update_sender(organiser_attr)
1.42 + responses.append(obj.to_part(method))
1.43 + methods.add(method)
1.44 +
1.45 + # Get recurrences for parent events.
1.46 +
1.47 + if not self.recurrenceid:
1.48 +
1.49 + # Collect active and cancelled recurrences.
1.50 +
1.51 + for rl, section, rmethod in [
1.52 + (self.store.get_active_recurrences(self.user, self.uid), None, method),
1.53 + (self.store.get_cancelled_recurrences(self.user, self.uid), "cancellations", "CANCEL"),
1.54 + ]:
1.55 +
1.56 + for recurrenceid in rl:
1.57 +
1.58 + # Get the recurrence, add SENT-BY details to the organiser.
1.59 +
1.60 + obj = self.get_stored_object(self.uid, recurrenceid, section)
1.61 +
1.62 + if not attendee or self.is_participating(attendee, obj=obj):
1.63 + organiser, organiser_attr = uri_item(obj.get_item("ORGANIZER"))
1.64 + self.update_sender(organiser_attr)
1.65 + responses.append(obj.to_part(rmethod))
1.66 + methods.add(rmethod)
1.67 +
1.68 + return methods, responses
1.69 +
1.70 + def get_unscheduled_parts(self, periods):
1.71 +
1.72 + "Return message parts describing unscheduled 'periods'."
1.73 +
1.74 + unscheduled_parts = []
1.75 +
1.76 + if periods:
1.77 + obj = self.obj.copy()
1.78 + obj.remove_all(["RRULE", "RDATE", "DTSTART", "DTEND", "DURATION"])
1.79 +
1.80 + for p in periods:
1.81 + if not p.origin:
1.82 + continue
1.83 + obj["RECURRENCE-ID"] = obj["DTSTART"] = [(format_datetime(p.get_start()), p.get_start_attr())]
1.84 + obj["DTEND"] = [(format_datetime(p.get_end()), p.get_end_attr())]
1.85 + unscheduled_parts.append(obj.to_part("CANCEL"))
1.86 +
1.87 + return unscheduled_parts
1.88 +
1.89 class ClientForObject(Client):
1.90
1.91 "A client maintaining a specific object."
1.92 @@ -668,6 +741,18 @@
1.93
1.94 return self.recurrenceid and self.get_stored_object(self.uid, None) or None
1.95
1.96 + def revert_cancellations(self, periods):
1.97 +
1.98 + """
1.99 + Restore cancelled recurrences corresponding to any of the given
1.100 + 'periods'.
1.101 + """
1.102 +
1.103 + for recurrenceid in self.store.get_cancelled_recurrences(self.user, self.uid):
1.104 + obj = self.get_stored_object(self.uid, recurrenceid, "cancellations")
1.105 + if set(self.get_periods(obj)).intersection(periods):
1.106 + self.store.remove_cancellation(self.user, self.uid, recurrenceid)
1.107 +
1.108 # Convenience methods for modifying free/busy collections.
1.109
1.110 def get_recurrence_start_point(self, recurrenceid):