imip-agent

imiptools/handlers/person.py

256:ac813723ed71
2015-02-04 Paul Boddie Made get_periods a method on the Object class, moving the supporting function to the data module.
     1 #!/usr/bin/env python     2      3 """     4 Handlers for a person for whom scheduling is performed.     5      6 Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>     7      8 This program is free software; you can redistribute it and/or modify it under     9 the terms of the GNU General Public License as published by the Free Software    10 Foundation; either version 3 of the License, or (at your option) any later    11 version.    12     13 This program is distributed in the hope that it will be useful, but WITHOUT    14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    15 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    16 details.    17     18 You should have received a copy of the GNU General Public License along with    19 this program.  If not, see <http://www.gnu.org/licenses/>.    20 """    21     22 from imiptools.content import Handler    23 from imiptools.data import get_address, get_uri, uri_dict, uri_items    24 from imiptools.handlers.common import CommonFreebusy    25 from imiptools.profile import Preferences    26     27 class PersonHandler(Handler):    28     29     "Handling mechanisms specific to people."    30     31     def _record_and_deliver(self, from_organiser=True, queue=False, cancel=False):    32     33         oa = self.require_organiser_and_attendees(from_organiser)    34         if not oa:    35             return False    36     37         (organiser, organiser_attr), attendees = organiser_item, attendees = oa    38     39         # Handle notifications and invitations.    40     41         if from_organiser:    42     43             # Process each attendee separately.    44     45             for attendee, attendee_attr in attendees.items():    46     47                 if not self.have_new_object(attendee):    48                     continue    49     50                 # Record other party free/busy information.    51     52                 if organiser != attendee:    53                     freebusy = self.store.get_freebusy_for_other(attendee, organiser)    54     55                     if organiser_attr.get("PARTSTAT") != "DECLINED":    56                         self.update_freebusy_for_other(freebusy, attendee, organiser, self.obj.get_periods())    57                     else:    58                         self.remove_from_freebusy_for_other(freebusy, attendee, organiser)    59     60                 # Store the object and queue any request.    61     62                 self.store.set_event(attendee, self.uid, self.obj.to_node())    63     64                 if queue:    65                     self.store.queue_request(attendee, self.uid)    66                 elif cancel:    67                     self.store.cancel_event(attendee, self.uid)    68     69         # As organiser, update attendance.    70     71         else:    72             obj = self.get_object(organiser)    73     74             if obj and self.have_new_object(organiser, obj=obj):    75     76                 # Get attendee details in a usable form.    77     78                 attendee_map = uri_dict(obj.get_value_map("ATTENDEE"))    79     80                 for attendee, attendee_attr in attendees.items():    81     82                     # Update attendance in the loaded object.    83     84                     attendee_map[attendee] = attendee_attr    85     86                     # Record other party free/busy information.    87     88                     if organiser != attendee:    89                         freebusy = self.store.get_freebusy_for_other(organiser, attendee)    90     91                         if attendee_attr.get("PARTSTAT") != "DECLINED":    92                             self.update_freebusy_for_other(freebusy, organiser, attendee, self.obj.get_periods())    93                         else:    94                             self.remove_from_freebusy_for_other(freebusy, organiser, attendee)    95     96                 # Set the new details and store the object.    97     98                 obj["ATTENDEE"] = attendee_map.items()    99    100                 self.store.set_event(organiser, self.uid, obj.to_node())   101    102         return True   103    104     def _record_freebusy(self, from_organiser=True):   105    106         "Record free/busy information for the received information."   107    108         senders = self.obj.get_items(from_organiser and "ORGANIZER" or "ATTENDEE")   109    110         if not senders:   111             return   112    113         freebusy = []   114    115         for value in self.obj.get_values("FREEBUSY") or []:   116             if not isinstance(value, list):   117                 value = [value]   118             for v in value:   119                 try:   120                     start, end = v.split("/", 1)   121                     freebusy.append((start, end))   122                 except ValueError:   123                     pass   124    125         for sender, sender_attr in uri_items(senders):   126             self.store.set_freebusy_for_other(get_uri(self.recipient), freebusy, sender)   127    128 class Event(PersonHandler):   129    130     "An event handler."   131    132     def add(self):   133    134         # NOTE: Queue a suggested modification to any active event.   135    136         return self.wrap("An addition to an event has been received.", link=False)   137    138     def cancel(self):   139    140         "Queue a cancellation of any active event."   141    142         self._record_and_deliver(from_organiser=True, queue=False, cancel=True)   143         return self.wrap("A cancellation has been received.", link=False)   144    145     def counter(self):   146    147         # NOTE: Queue a suggested modification to any active event.   148    149         return self.wrap("A counter proposal has been received.", link=False)   150    151     def declinecounter(self):   152    153         # NOTE: Queue a suggested modification to any active event.   154    155         return self.wrap("A declining counter proposal has been received.", link=False)   156    157     def publish(self):   158    159         "Register details of any relevant event."   160    161         self._record_and_deliver(from_organiser=True, queue=False)   162         return self.wrap("Details of an event have been received.")   163    164     def refresh(self):   165    166         "Update details of any active event."   167    168         self._record_and_deliver(from_organiser=True, queue=False)   169         return self.wrap("An event update has been received.")   170    171     def reply(self):   172    173         "Record replies and notify the recipient."   174    175         self._record_and_deliver(from_organiser=False, queue=False)   176         return self.wrap("A reply has been received.")   177    178     def request(self):   179    180         "Hold requests and notify the recipient."   181    182         self._record_and_deliver(from_organiser=True, queue=True)   183         return self.wrap("A request has been received.")   184    185 class Freebusy(PersonHandler, CommonFreebusy):   186    187     "A free/busy handler."   188    189     def publish(self):   190    191         "Register free/busy information."   192    193         self._record_freebusy(from_organiser=True)   194    195         # Produce a message if configured to do so.   196    197         preferences = Preferences(get_uri(self.recipient))   198         if preferences.get("freebusy_messages") == "notify":   199             return self.wrap("A free/busy update has been received.", link=False)   200    201     def reply(self):   202    203         "Record replies and notify the recipient."   204    205         self._record_freebusy(from_organiser=False)   206    207         # Produce a message if configured to do so.   208    209         preferences = Preferences(get_uri(self.recipient))   210         if preferences.get("freebusy_messages") == "notify":   211             return self.wrap("A reply to a free/busy request has been received.", link=False)   212    213     def request(self):   214    215         """   216         Respond to a request by preparing a reply containing free/busy   217         information for each indicated attendee.   218         """   219    220         # Produce a reply if configured to do so.   221    222         preferences = Preferences(get_uri(self.recipient))   223         if preferences.get("freebusy_sharing") == "share":   224             return CommonFreebusy.request(self)   225    226 class Journal(PersonHandler):   227    228     "A journal entry handler."   229    230     def add(self):   231    232         # NOTE: Queue a suggested modification to any active entry.   233    234         return self.wrap("An addition to a journal entry has been received.", link=False)   235    236     def cancel(self):   237    238         # NOTE: Queue a suggested modification to any active entry.   239    240         return self.wrap("A cancellation has been received.", link=False)   241    242     def publish(self):   243    244         # NOTE: Register details of any relevant entry.   245    246         self._record_and_deliver(from_organiser=True, queue=False)   247         return self.wrap("Details of a journal entry have been received.")   248    249 class Todo(PersonHandler):   250    251     "A to-do item handler."   252    253     def add(self):   254    255         # NOTE: Queue a suggested modification to any active item.   256    257         return self.wrap("An addition to an item has been received.", link=False)   258    259     def cancel(self):   260    261         # NOTE: Queue a suggested modification to any active item.   262    263         return self.wrap("A cancellation has been received.", link=False)   264    265     def counter(self):   266    267         # NOTE: Queue a suggested modification to any active item.   268    269         return self.wrap("A counter proposal has been received.", link=False)   270    271     def declinecounter(self):   272    273         # NOTE: Queue a suggested modification to any active item.   274    275         return self.wrap("A declining counter proposal has been received.", link=False)   276    277     def publish(self):   278    279         "Register details of any relevant item."   280    281         self._record_and_deliver(from_organiser=True, queue=False)   282         return self.wrap("Details of an item have been received.")   283    284     def refresh(self):   285    286         "Update details of any active item."   287    288         self._record_and_deliver(from_organiser=True, queue=False)   289         return self.wrap("An item update has been received.")   290    291     def reply(self):   292    293         "Record replies and notify the recipient."   294    295         self._record_and_deliver(from_organiser=False, queue=False)   296         return self.wrap("A reply has been received.")   297    298     def request(self):   299    300         "Hold requests and notify the recipient."   301    302         self._record_and_deliver(from_organiser=True, queue=True)   303         return self.wrap("A request has been received.")   304    305 # Handler registry.   306    307 handlers = [   308     ("VFREEBUSY",   Freebusy),   309     ("VEVENT",      Event),   310     ("VTODO",       Todo),   311     ("VJOURNAL",    Journal),   312     ]   313    314 # vim: tabstop=4 expandtab shiftwidth=4