imip-agent

imiptools/handlers/person_outgoing.py

729:f3d6831fcc21
2015-09-12 Paul Boddie Ensure user initialisation before invoking each handler method.
     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.client import Client    24 from imiptools.data import get_uri, uri_dict, uri_values    25 from imiptools.handlers import Handler    26 from imiptools.handlers.common import CommonEvent    27     28 class PersonHandler(CommonEvent, Handler):    29     30     "Handling mechanisms specific to people."    31     32     def set_identity(self, method):    33     34         """    35         Set the current user for the current object in the context of the given    36         'method'. It is usually set when initialising the handler, using the    37         recipient details, but outgoing messages do not reference the recipient    38         in this way.    39         """    40     41         if self.obj:    42             from_organiser = method in self.organiser_methods    43             self.user = get_uri(self.obj.get_value(from_organiser and "ORGANIZER" or "ATTENDEE"))    44     45     def _add(self):    46     47         "Add a recurrence for the current object."    48     49         if not Client.is_participating(self):    50             return False    51     52         # Obtain valid organiser and attendee details.    53     54         oa = self.require_organiser_and_attendees()    55         if not oa:    56             return False    57     58         (organiser, organiser_attr), attendees = oa    59     60         # Ignore unknown objects.    61     62         if not self.get_stored_object_version():    63             return    64     65         # Record the event as a recurrence of the parent object.    66     67         self.update_recurrenceid()    68     69         # Update free/busy information.    70     71         self.update_event_in_freebusy()    72     73         # Set the additional occurrence.    74     75         self.store.set_event(self.user, self.uid, self.recurrenceid, self.obj.to_node())    76     77         return True    78     79     def _record(self, from_organiser=True):    80     81         """    82         Record details from the current object given a message originating    83         from an organiser if 'from_organiser' is set to a true value.    84         """    85     86         if not Client.is_participating(self):    87             return False    88     89         # Check for a new event, tolerating not-strictly-new events if the    90         # attendee is responding.    91     92         if not self.have_new_object(strict=from_organiser):    93             return False    94     95         # Update the object.    96     97         if from_organiser:    98     99             # Set the complete event or an additional occurrence.   100    101             self.store.set_event(self.user, self.uid, self.recurrenceid, self.obj.to_node())   102    103             # Remove additional recurrences if handling a complete event.   104    105             if not self.recurrenceid:   106                 self.store.remove_recurrences(self.user, self.uid)   107    108         else:   109             # Obtain valid attendees, merging their attendance with the stored   110             # object.   111    112             attendees = self.require_attendees(from_organiser)   113             self.merge_attendance(attendees)   114    115         # Remove any associated request.   116    117         self.store.dequeue_request(self.user, self.uid, self.recurrenceid)   118    119         # Update free/busy information.   120    121         self.update_event_in_freebusy(from_organiser)   122    123         return True   124    125     def _remove(self):   126    127         """   128         Remove details from the current object given a message originating   129         from an organiser if 'from_organiser' is set to a true value.   130         """   131    132         if not Client.is_participating(self):   133             return False   134    135         # Check for event using UID.   136    137         if not self.have_new_object():   138             return False   139    140         # Obtain any stored object, using parent object details if a newly-   141         # indicated occurrence is referenced.   142    143         obj = self.get_stored_object_version()   144         old = not obj and self.get_parent_object() or obj   145    146         if not old:   147             return False   148    149         # Only cancel the event completely if all attendees are given.   150    151         attendees = uri_dict(old.get_value_map("ATTENDEE"))   152         all_attendees = set(attendees.keys())   153         given_attendees = set(uri_values(self.obj.get_values("ATTENDEE")))   154         cancel_entire_event = not all_attendees.difference(given_attendees)   155    156         # Update the recipient's record of the organiser's schedule.   157    158         self.remove_freebusy_from_organiser(self.obj.get_value("ORGANIZER"))   159    160         # Otherwise, remove the given attendees and update the event.   161    162         if not cancel_entire_event and obj:   163             for attendee in given_attendees:   164                 if attendees.has_key(attendee):   165                     del attendees[attendee]   166             obj["ATTENDEE"] = attendees.items()   167    168         # Update the stored object with sequence information.   169    170         if obj:   171             obj["SEQUENCE"] = self.obj.get_items("SEQUENCE") or []   172             obj["DTSTAMP"] = self.obj.get_items("DTSTAMP") or []   173    174         # Update free/busy information.   175    176         if cancel_entire_event or self.user in given_attendees:   177             self.remove_event_from_freebusy()   178    179         # Set the complete event if not an additional occurrence. For any newly-   180         # indicated occurrence, use the received event details.   181    182         self.store.set_event(self.user, self.uid, self.recurrenceid, (obj or self.obj).to_node())   183    184         # Perform any cancellation after recording the latest state of the   185         # event.   186    187         if cancel_entire_event:   188             self.store.cancel_event(self.user, self.uid, self.recurrenceid)   189             self.store.dequeue_request(self.user, self.uid, self.recurrenceid)   190    191         # Remove any associated request.   192    193         self.store.dequeue_request(self.user, self.uid, self.recurrenceid)   194    195         return True   196    197 class Event(PersonHandler):   198    199     "An event handler."   200    201     def add(self):   202    203         "Record the addition of a recurrence to an event."   204    205         self._add()   206    207     def cancel(self):   208    209         "Remove an event or a recurrence."   210    211         self._remove()   212    213     def counter(self):   214    215         "Counter-proposals are tentative and do not change events."   216    217         pass   218    219     def declinecounter(self):   220    221         "Declined counter-proposals are advisory and do not change events."   222    223         pass   224    225     def publish(self):   226    227         "Published events are recorded."   228    229         self._record(True)   230    231     def refresh(self):   232    233         "Requests to refresh events do not provide event information."   234    235         pass   236    237     def reply(self):   238    239         "Replies to requests are inspected for attendee information."   240    241         self._record(False)   242    243     def request(self):   244    245         "Record events sent for potential scheduling."   246    247         self._record(True)   248    249 # Handler registry.   250    251 handlers = [   252     ("VEVENT",      Event),   253     ]   254    255 # vim: tabstop=4 expandtab shiftwidth=4