imip-agent

imiptools/handlers/resource.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 resource.     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, get_window_end, to_part    24 from imiptools.dates import get_default_timezone    25 from imiptools.handlers.common import CommonFreebusy    26 from imiptools.period import remove_affected_period    27     28 class ResourceHandler(Handler):    29     30     "Handling mechanisms specific to resources."    31     32     def _record_and_respond(self, handle_for_attendee):    33     34         oa = self.require_organiser_and_attendees()    35         if not oa:    36             return None    37     38         organiser_item, attendees = oa    39     40         # Process each attendee separately.    41     42         calendar = []    43     44         for attendee, attendee_attr in attendees.items():    45     46             # Check for event using UID.    47     48             if not self.have_new_object(attendee):    49                 continue    50     51             # Collect response objects produced when handling the request.    52     53             response = handle_for_attendee(attendee, attendee_attr)    54             if response:    55                 calendar.append(response)    56     57         return calendar    58     59     def _schedule_for_attendee(self, attendee, attendee_attr):    60     61         # Interpretation of periods can depend on the time zone.    62     63         tzid = self.get_tzid(attendee)    64     65         # If newer than any old version, discard old details from the    66         # free/busy record and check for suitability.    67     68         periods = self.obj.get_periods_for_freebusy(tzid, get_window_end(tzid))    69         freebusy = self.store.get_freebusy(attendee)    70         scheduled = self.can_schedule(freebusy, periods)    71     72         attendee_attr["PARTSTAT"] = scheduled and "ACCEPTED" or "DECLINED"    73         if attendee_attr.has_key("RSVP"):    74             del attendee_attr["RSVP"]    75         if self.messenger and self.messenger.sender != get_address(attendee):    76             attendee_attr["SENT-BY"] = get_uri(self.messenger.sender)    77     78         # Make a version of the request with just this attendee.    79     80         self.obj["ATTENDEE"] = [(attendee, attendee_attr)]    81     82         # Update the DTSTAMP.    83     84         self.update_dtstamp()    85     86         # Set the complete event or an additional occurrence.    87     88         event = self.obj.to_node()    89         self.store.set_event(attendee, self.uid, self.recurrenceid, event)    90     91         # Remove additional recurrences if handling a complete event.    92     93         if not self.recurrenceid:    94             self.store.remove_recurrences(attendee, self.uid)    95     96         # Only update free/busy details if the event is scheduled.    97     98         if scheduled:    99             self.update_freebusy(freebusy, periods)   100         else:   101             self.remove_from_freebusy(freebusy)   102    103         # Remove either original recurrence or additional recurrence   104         # details depending on whether an additional recurrence or a   105         # complete event are being handled, respectively.   106    107         self.remove_freebusy_for_recurrences(freebusy)   108         self.store.set_freebusy(attendee, freebusy)   109    110         if self.publisher:   111             self.publisher.set_freebusy(attendee, freebusy)   112    113         return event   114    115     def _cancel_for_attendee(self, attendee, attendee_attr):   116    117         self.store.cancel_event(attendee, self.uid, self.recurrenceid)   118    119         freebusy = self.store.get_freebusy(attendee)   120         self.remove_from_freebusy(freebusy)   121    122         self.store.set_freebusy(attendee, freebusy)   123    124         if self.publisher:   125             self.publisher.set_freebusy(attendee, freebusy)   126    127         return None   128    129 class Event(ResourceHandler):   130    131     "An event handler."   132    133     def add(self):   134         pass   135    136     def cancel(self):   137    138         "Cancel attendance for attendees."   139    140         self._record_and_respond(self._cancel_for_attendee)   141    142     def counter(self):   143    144         "Since this handler does not send requests, it will not handle replies."   145    146         pass   147    148     def declinecounter(self):   149    150         """   151         Since this handler does not send counter proposals, it will not handle   152         replies to such proposals.   153         """   154    155         pass   156    157     def publish(self):   158         pass   159    160     def refresh(self):   161         pass   162    163     def reply(self):   164    165         "Since this handler does not send requests, it will not handle replies."   166    167         pass   168    169     def request(self):   170    171         """   172         Respond to a request by preparing a reply containing accept/decline   173         information for each indicated attendee.   174    175         No support for countering requests is implemented.   176         """   177    178         response = self._record_and_respond(self._schedule_for_attendee)   179         if response:   180             self.add_result("REPLY", map(get_address, self.obj.get_values("ORGANIZER")), to_part("REPLY", response))   181    182 class Freebusy(Handler, CommonFreebusy):   183    184     "A free/busy handler."   185    186     def publish(self):   187         pass   188    189     def reply(self):   190    191         "Since this handler does not send requests, it will not handle replies."   192    193         pass   194    195     # request provided by CommonFreeBusy.request   196    197 # Handler registry.   198    199 handlers = [   200     ("VFREEBUSY",   Freebusy),   201     ("VEVENT",      Event),   202     ]   203    204 # vim: tabstop=4 expandtab shiftwidth=4