imip-agent

imiptools/handlers/person.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.     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_uri    24 from imiptools.dates import format_datetime    25 from imiptools.handlers.common import CommonFreebusy    26 from imiptools.period import replace_overlapping    27 from imiptools.profile import Preferences    28     29 class PersonHandler(Handler):    30     31     "Handling mechanisms specific to people."    32     33     def _record(self, from_organiser=True, queue=False, cancel=False):    34     35         oa = self.require_organiser_and_attendees(from_organiser)    36         if not oa:    37             return False    38     39         (organiser, organiser_attr), attendees = organiser_item, attendees = oa    40     41         # Handle notifications and invitations.    42     43         if from_organiser:    44     45             # Process each attendee separately.    46     47             for attendee, attendee_attr in attendees.items():    48     49                 if not self.have_new_object(attendee):    50                     continue    51     52                 # Set the complete event or an additional occurrence.    53     54                 self.store.set_event(attendee, self.uid, self.recurrenceid, self.obj.to_node())    55     56                 # Remove additional recurrences if handling a complete event.    57     58                 if not self.recurrenceid:    59                     self.store.remove_recurrences(attendee, self.uid)    60     61                 # Queue any request.    62     63                 if queue:    64                     self.store.queue_request(attendee, self.uid, self.recurrenceid)    65                 elif cancel:    66                     self.store.cancel_event(attendee, self.uid, self.recurrenceid)    67     68                     # No return message will occur to update the free/busy    69                     # information, so this is done here.    70     71                     freebusy = self.store.get_freebusy(attendee)    72                     self.remove_from_freebusy(freebusy)    73     74                     self.store.set_freebusy(attendee, freebusy)    75     76                     if self.publisher:    77                         self.publisher.set_freebusy(attendee, freebusy)    78     79                 self.update_freebusy_from_organiser(attendee, organiser_item)    80     81         # As organiser, update attendance.    82     83         else:    84             if self.merge_attendance(attendees, organiser):    85                 self.update_freebusy_from_attendees(organiser, attendees)    86     87         return True    88     89     def _record_freebusy(self, from_organiser=True):    90     91         "Record free/busy information for the received information."    92     93         if from_organiser:    94             organiser_item = self.require_organiser(from_organiser)    95             if not organiser_item:    96                 return    97     98             senders = [organiser_item]    99         else:   100             oa = self.require_organiser_and_attendees(from_organiser)   101             if not oa:   102                 return   103    104             organiser_item, attendees = oa   105             senders = attendees.items()   106    107             if not senders:   108                 return   109    110         freebusy = []   111    112         for value in self.obj.get_values("FREEBUSY") or []:   113             if not isinstance(value, list):   114                 value = [value]   115             for v in value:   116                 try:   117                     start, end = v.split("/", 1)   118                     freebusy.append((start, end))   119                 except ValueError:   120                     pass   121    122         dtstart = format_datetime(self.obj.get_utc_datetime("DTSTART"))   123         dtend = format_datetime(self.obj.get_utc_datetime("DTEND"))   124         user = get_uri(self.recipient)   125    126         for sender, sender_attr in senders:   127             stored_freebusy = self.store.get_freebusy_for_other(user, sender)   128             replace_overlapping(stored_freebusy, (dtstart, dtend), freebusy)   129             self.store.set_freebusy_for_other(user, stored_freebusy, sender)   130    131 class Event(PersonHandler):   132    133     "An event handler."   134    135     def add(self):   136    137         # NOTE: Queue a suggested modification to any active event.   138    139         return self.wrap("An addition to an event has been received.", link=False)   140    141     def cancel(self):   142    143         "Queue a cancellation of any active event."   144    145         self._record(from_organiser=True, queue=False, cancel=True)   146         return self.wrap("A cancellation has been received.", link=False)   147    148     def counter(self):   149    150         # NOTE: Queue a suggested modification to any active event.   151    152         return self.wrap("A counter proposal has been received.", link=False)   153    154     def declinecounter(self):   155    156         # NOTE: Queue a suggested modification to any active event.   157    158         return self.wrap("A declining counter proposal has been received.", link=False)   159    160     def publish(self):   161    162         "Register details of any relevant event."   163    164         self._record(from_organiser=True, queue=False)   165         return self.wrap("Details of an event have been received.")   166    167     def refresh(self):   168    169         "Generate details of any active event."   170    171         # NOTE: Return event details if configured to do so.   172    173         return self.wrap("A request for updated event details has been received.")   174    175     def reply(self):   176    177         "Record replies and notify the recipient."   178    179         self._record(from_organiser=False, queue=False)   180         return self.wrap("A reply has been received.")   181    182     def request(self):   183    184         "Hold requests and notify the recipient."   185    186         self._record(from_organiser=True, queue=True)   187         return self.wrap("A request has been received.")   188    189 class Freebusy(PersonHandler, CommonFreebusy):   190    191     "A free/busy handler."   192    193     def publish(self):   194    195         "Register free/busy information."   196    197         self._record_freebusy(from_organiser=True)   198    199         # Produce a message if configured to do so.   200    201         preferences = Preferences(get_uri(self.recipient))   202         if preferences.get("freebusy_messages") == "notify":   203             return self.wrap("A free/busy update has been received.", link=False)   204    205     def reply(self):   206    207         "Record replies and notify the recipient."   208    209         self._record_freebusy(from_organiser=False)   210    211         # Produce a message if configured to do so.   212    213         preferences = Preferences(get_uri(self.recipient))   214         if preferences.get("freebusy_messages") == "notify":   215             return self.wrap("A reply to a free/busy request has been received.", link=False)   216    217     def request(self):   218    219         """   220         Respond to a request by preparing a reply containing free/busy   221         information for each indicated attendee.   222         """   223    224         # Produce a reply if configured to do so.   225    226         preferences = Preferences(get_uri(self.recipient))   227         if preferences.get("freebusy_sharing") == "share":   228             return CommonFreebusy.request(self)   229    230 # Handler registry.   231    232 handlers = [   233     ("VFREEBUSY",   Freebusy),   234     ("VEVENT",      Event),   235     ]   236    237 # vim: tabstop=4 expandtab shiftwidth=4