imip-agent

tests/test_handle.py

1039:a12150034cbd
2016-02-08 Paul Boddie Added a journal storage area, maintaining quota and collective scheduling data for scheduling decisions. Introduced confirmation and retraction functions for resource scheduling so that quotas and collective schedules can be maintained and thus queried by scheduling functions. Updated the documentation, tools and tests.
     1 #!/usr/bin/env python     2      3 """     4 A handler to help with testing.     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.client import ClientForObject    23 from imiptools.data import Object, get_address, parse_object    24 from imiptools.dates import get_datetime, to_timezone    25 from imiptools.mail import Messenger    26 from imiptools.period import RecurringPeriod    27 import imip_store    28 import sys    29     30 class TestClient(ClientForObject):    31     32     """    33     A content handler for use in testing, as opposed to operating within the    34     mail processing pipeline.    35     """    36     37     # Action methods.    38     39     def handle_request(self, action, start=None, end=None):    40     41         """    42         Process the current request for the current user. Return whether the    43         given 'action' was taken.    44     45         If 'start' and 'end' are specified, they will be used in any    46         counter-proposal.    47         """    48     49         # Reply only on behalf of this user.    50     51         if action in ("accept", "decline"):    52             attendee_attr = self.update_participation(action == "accept" and "ACCEPTED" or "DECLINED")    53             method = "REPLY"    54     55         # For counter-proposals, set a new main period for the event.    56     57         elif action == "counter":    58             attendee_attr = self.obj.get_value_map("ATTENDEE").get(self.user)    59             period = self.obj.get_main_period(self.get_tzid())    60     61             # Use the existing or configured time zone for the specified    62             # datetimes.    63     64             start = to_timezone(get_datetime(start), period.tzid)    65             end = to_timezone(get_datetime(end), period.tzid)    66             period = RecurringPeriod(start, end, period.tzid, period.origin, period.get_start_attr(), period.get_end_attr())    67             self.obj.set_period(period)    68             method = "COUNTER"    69         else:    70             return None    71     72         if not attendee_attr:    73             return None    74     75         # NOTE: This is a simpler form of the code in imipweb.client.    76     77         organiser = get_address(self.obj.get_value("ORGANIZER"))    78     79         self.update_sender(attendee_attr)    80         self.obj["ATTENDEE"] = [(self.user, attendee_attr)]    81         self.update_dtstamp()    82         self.update_sequence(False)    83     84         message = self.messenger.make_outgoing_message(    85             [self.obj.to_part(method)],    86             [organiser],    87             outgoing_bcc=get_address(self.user)    88             )    89     90         return message.as_string()    91     92 # A simple main program that attempts to handle a stored request, writing the    93 # response message to standard output.    94     95 if __name__ == "__main__":    96     try:    97         action, store_dir, preferences_dir, user = sys.argv[1:5]    98         if action == "counter":    99             start, end = sys.argv[5:7]   100             i = 7   101         else:   102             start, end = None, None   103             i = 5   104         uid, recurrenceid = (sys.argv[i:i+2] + [None] * 2)[:2]   105     except ValueError:   106         print >>sys.stderr, """\   107 Need 'accept', 'counter' or 'decline', a store directory, a preferences   108 directory, user URI, any counter-proposal datetimes (see below), plus the   109 appropriate event UID and RECURRENCE-ID (if a recurrence is involved).   110    111 The RECURRENCE-ID must be in exactly the form employed by the store, not a   112 different but equivalent representation.   113    114 Alternatively, omit the UID and RECURRENCE-ID and provide event-only details on   115 standard input to force the script to handle an event not already present in the   116 store.   117    118 If 'counter' has been indicated, alternative start and end datetimes are also   119 required.   120 """   121         sys.exit(1)   122    123     store = imip_store.FileStore(store_dir)   124    125     if uid is not None:   126         fragment = store.get_event(user, uid, recurrenceid)   127    128         if not fragment:   129             print >>sys.stderr, "No such event:", uid, recurrenceid   130             sys.exit(1)   131     else:   132         fragment = parse_object(sys.stdin, "utf-8")   133    134     obj = Object(fragment)   135     handler = TestClient(obj, user, Messenger(), store, None, preferences_dir)   136     response = handler.handle_request(action, start, end)   137    138     if response:   139         if uid is not None:   140             store.dequeue_request(user, uid, recurrenceid)   141         print response   142     else:   143         sys.exit(1)   144    145 # vim: tabstop=4 expandtab shiftwidth=4