imip-agent

imiptools/handlers/person_outgoing.py

394:63473567026a
2015-03-07 Paul Boddie Removed VTODO and VJOURNAL handlers, since they are mostly beyond the scope of this project for the time being.
     1 #!/usr/bin/env python     2      3 """     4 Handlers for a person for whom scheduling is performed, inspecting outgoing     5 messages to obtain scheduling done externally.     6      7 Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>     8      9 This program is free software; you can redistribute it and/or modify it under    10 the terms of the GNU General Public License as published by the Free Software    11 Foundation; either version 3 of the License, or (at your option) any later    12 version.    13     14 This program is distributed in the hope that it will be useful, but WITHOUT    15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    16 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    17 details.    18     19 You should have received a copy of the GNU General Public License along with    20 this program.  If not, see <http://www.gnu.org/licenses/>.    21 """    22     23 from imiptools.content import Handler    24 from imiptools.data import get_window_end, uri_dict, uri_item, uri_values    25 from imiptools.period import remove_affected_period    26     27 class PersonHandler(Handler):    28     29     "Handling mechanisms specific to people."    30     31     def _get_identity(self, from_organiser=True):    32     33         """    34         Get the identity of interest in a usable form for any unprocessed    35         object.    36         """    37     38         identity, attr = item = uri_item(self.obj.get_item(from_organiser and "ORGANIZER" or "ATTENDEE"))    39     40         # Check for event using UID.    41     42         if not self.have_new_object(identity):    43             return None    44     45         return item    46     47     def _record(self, from_organiser=True, update_freebusy=False):    48     49         "Record free/busy and object information."    50     51         item = self._get_identity(from_organiser)    52         if not item:    53             return False    54     55         identity, attr = item    56     57         # Update the object.    58     59         if from_organiser:    60     61             # Set the complete event or an additional occurrence.    62     63             self.store.set_event(identity, self.uid, self.recurrenceid, self.obj.to_node())    64     65             # Remove additional recurrences if handling a complete event.    66     67             if not self.recurrenceid:    68                 self.store.remove_recurrences(identity, self.uid)    69     70         else:    71             organiser_item, attendees = self.require_organiser_and_attendees(from_organiser)    72             self.merge_attendance(attendees, identity)    73     74         # Remove any associated request.    75     76         self.store.dequeue_request(identity, self.uid, self.recurrenceid)    77     78         # Update free/busy information.    79     80         if update_freebusy:    81     82             freebusy = self.store.get_freebusy(identity)    83     84             # Interpretation of periods can depend on the time zone.    85     86             tzid = self.get_tzid(identity)    87     88             # Use the stored event in case the reply is incomplete, as is seen    89             # when Claws sends a REPLY for an object originally employing    90             # recurrence information.    91     92             obj = self.get_object(identity)    93     94             # If newer than any old version, discard old details from the    95             # free/busy record and check for suitability.    96     97             periods = obj.get_periods_for_freebusy(tzid, get_window_end(tzid))    98     99             self.update_freebusy_for_participant(freebusy, periods, attr,   100                 from_organiser and self.is_not_attendee(identity, obj))   101    102             # Remove either original recurrence or additional recurrence   103             # details depending on whether an additional recurrence or a   104             # complete event are being handled, respectively.   105    106             self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(identity, self.uid))   107             self.store.set_freebusy(identity, freebusy)   108    109             if self.publisher:   110                 self.publisher.set_freebusy(identity, freebusy)   111    112         return True   113    114     def _remove(self, from_organiser=True, update_freebusy=False):   115    116         "Remove free/busy information for any unprocessed object."   117    118         item = self._get_identity(from_organiser)   119         if not item:   120             return False   121    122         identity, attr = item   123    124         # Only cancel the event completely if all attendees are given.   125         # NOTE: Need to also check for recurrence identifiers and selective   126         # NOTE: cancellations.   127    128         obj = self.get_object(identity)   129    130         attendees = uri_dict(obj.get_value_map("ATTENDEE"))   131         all_attendees = set(attendees.keys())   132         given_attendees = set(uri_values(self.obj.get_values("ATTENDEE")))   133    134         if given_attendees == all_attendees:   135             self.store.cancel_event(identity, self.uid, self.recurrenceid)   136    137         # Otherwise, remove the given attendees and update the event.   138    139         else:   140             for attendee in given_attendees:   141                 if attendees.has_key(attendee):   142                     del attendees[attendee]   143             obj["ATTENDEE"] = attendees.items()   144    145         # Update the stored object with sequence information.   146    147         obj["SEQUENCE"] = self.obj.get_items("SEQUENCE")   148         obj["DTSTAMP"] = self.obj.get_items("DTSTAMP")   149    150         # Set the complete event if not an additional occurrence.   151    152         self.store.set_event(identity, self.uid, self.recurrenceid, obj.to_node())   153    154         # Remove any associated request.   155    156         self.store.dequeue_request(identity, self.uid, self.recurrenceid)   157    158         # Update free/busy information.   159    160         if update_freebusy:   161             freebusy = self.store.get_freebusy(identity)   162             self.remove_from_freebusy(freebusy)   163             self.store.set_freebusy(identity, freebusy)   164    165             if self.publisher:   166                 self.publisher.set_freebusy(identity, freebusy)   167    168         return True   169    170 class Event(PersonHandler):   171    172     "An event handler."   173    174     def add(self):   175         pass   176    177     def cancel(self):   178         self._remove(True, True)   179    180     def counter(self):   181         pass   182    183     def declinecounter(self):   184         pass   185    186     def publish(self):   187         self._record(True, True)   188    189     def refresh(self):   190         pass   191    192     def reply(self):   193         self._record(False, True)   194    195     def request(self):   196         self._record(True, True)   197    198 class Freebusy(PersonHandler):   199    200     "A free/busy handler."   201    202     def publish(self):   203         pass   204    205     def reply(self):   206         pass   207    208     def request(self):   209         pass   210    211 # Handler registry.   212    213 handlers = [   214     ("VFREEBUSY",   Freebusy),   215     ("VEVENT",      Event),   216     ]   217    218 # vim: tabstop=4 expandtab shiftwidth=4