imip-agent

tools/make_freebusy.py

748:e332784ba2bb
2015-09-17 Paul Boddie Added a setting to control Web publication of free/busy details. Introduced separate files for Web server configuration and added a description of the Web server configuration activity. Added a test for publishing to the free/busy script.
     1 #!/usr/bin/env python     2      3 """     4 Construct free/busy records for a user, either recording that user's own     5 availability schedule or the schedule of another user (using details provided     6 when scheduling events with that user).     7      8 Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>     9     10 This program is free software; you can redistribute it and/or modify it under    11 the terms of the GNU General Public License as published by the Free Software    12 Foundation; either version 3 of the License, or (at your option) any later    13 version.    14     15 This program is distributed in the hope that it will be useful, but WITHOUT    16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    17 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    18 details.    19     20 You should have received a copy of the GNU General Public License along with    21 this program.  If not, see <http://www.gnu.org/licenses/>.    22 """    23     24 from codecs import getwriter    25 from imiptools.client import Client    26 from imiptools.data import get_window_end, Object    27 from imiptools.dates import get_default_timezone, to_utc_datetime    28 from imiptools.period import insert_period    29 from imiptools.profile import Preferences    30 from imip_store import FileStore, FilePublisher    31 import sys    32     33 def make_freebusy(store, publisher, preferences, user, participant,    34     store_and_publish, include_needs_action, reset_updated_list, verbose):    35     36     """    37     Using the given 'store', 'publisher' and 'preferences', make free/busy    38     details for the records of the given 'user', generating details for    39     'participant' if not indicated as None; otherwise, generating free/busy    40     details concerning the given user.    41     42     If 'store_and_publish' is set, the stored details will be updated;    43     otherwise, the details will be written to standard output.    44     45     If 'include_needs_action' is set, details of objects whose participation    46     status is set to "NEEDS-ACTION" for the participant will be included in the    47     details.    48     49     If 'reset_updated_list' is set, all objects will be inspected for periods;    50     otherwise, only those in the stored free/busy providers file will be    51     inspected.    52     53     If 'verbose' is set, messages will be written to standard error.    54     """    55     56     participant = participant or user    57     tzid = preferences.get("TZID") or get_default_timezone()    58     59     # Get the size of the free/busy window.    60     61     try:    62         window_size = int(preferences.get("window_size"))    63     except (TypeError, ValueError):    64         window_size = 100    65     window_end = get_window_end(tzid, window_size)    66     67     # Get identifiers for uncancelled events either from a list of events    68     # providing free/busy periods at the end of the given time window, or from    69     # a list of all events.    70     71     all_events = not reset_updated_list and store.get_freebusy_providers(user, window_end)    72     73     if not all_events:    74         all_events = store.get_all_events(user)    75         fb = []    76     77     # With providers of additional periods, append to the existing collection.    78     79     else:    80         if user == participant:    81             fb = store.get_freebusy(user)    82         else:    83             fb = store.get_freebusy_for_other(user, participant)    84     85     # Obtain event objects.    86     87     objs = []    88     for uid, recurrenceid in all_events:    89         if verbose:    90             print >>sys.stderr, uid, recurrenceid    91         event = store.get_event(user, uid, recurrenceid)    92         if event:    93             objs.append(Object(event))    94     95     # Build a free/busy collection for the given user.    96     97     for obj in objs:    98         partstat = obj.get_participation_status(participant)    99         recurrenceids = not obj.get_recurrenceid() and store.get_recurrences(user, obj.get_uid())   100    101         if obj.get_participation(partstat, include_needs_action):   102             for p in obj.get_active_periods(recurrenceids, tzid, window_end):   103                 fbp = obj.get_freebusy_period(p, partstat == "ORG")   104                 insert_period(fb, fbp)   105    106     # Store and publish the free/busy collection.   107    108     if store_and_publish:   109         if user == participant:   110             store.set_freebusy(user, fb)   111    112             if Client(user).is_sharing() and Client(user).is_publishing():   113                 publisher.set_freebusy(user, fb)   114    115             # Update the list of objects providing periods on future occasions.   116    117             store.set_freebusy_providers(user, to_utc_datetime(window_end, tzid),   118                 [obj for obj in objs if obj.possibly_active_from(window_end, tzid)])   119         else:   120             store.set_freebusy_for_other(user, fb, participant)   121    122     # Alternatively, just write the collection to standard output.   123    124     else:   125         f = getwriter("utf-8")(sys.stdout)   126         for item in fb:   127             print >>f, "\t".join(item.as_tuple(strings_only=True))   128    129 # Main program.   130    131 if __name__ == "__main__":   132    133     # Interpret the command line arguments.   134    135     participants = []   136     args = []   137     store_dir = []   138     publishing_dir = []   139     preferences_dir = []   140     ignored = []   141    142     # Collect user details first, switching to other arguments when encountering   143     # switches.   144    145     l = participants   146    147     for arg in sys.argv[1:]:   148         if arg in ("-n", "-s", "-v", "-r"):   149             args.append(arg)   150             l = ignored   151         elif arg == "-S":   152             l = store_dir   153         elif arg == "-P":   154             l = publishing_dir   155         elif arg == "-p":   156             l = preferences_dir   157         else:   158             l.append(arg)   159    160     try:   161         user = participants[0]   162     except IndexError:   163         print >>sys.stderr, """\   164 Need a user and an optional participant (if different from the user),   165 along with the -s option if updating the store and the published details.   166 Specify -n to include objects with PARTSTAT of NEEDS-ACTION.   167 Specify -r to inspect all objects, not just those expected to provide details.   168 Specify -v for additional messages on standard error.   169     """   170         sys.exit(1)   171    172     # Define any other participant of interest plus options.   173    174     participant = participants[1:] and participants[1] or None   175     store_and_publish = "-s" in args   176     include_needs_action = "-n" in args   177     reset_updated_list = "-r" in args   178     verbose = "-v" in args   179    180     # Override defaults if indicated.   181    182     store_dir = store_dir and store_dir[0] or None   183     publishing_dir = publishing_dir and publishing_dir[0] or None   184     preferences_dir = preferences_dir and preferences_dir[0] or None   185    186     # Obtain store-related objects.   187    188     store = FileStore(store_dir)   189     publisher = FilePublisher(publishing_dir)   190     preferences = Preferences(user, preferences_dir)   191    192     # Obtain a list of users for processing.   193    194     if user in ("*", "all"):   195         users = store.get_users()   196     else:   197         users = [user]   198    199     # Process the given users.   200    201     for user in users:   202         if verbose:   203             print >>sys.stderr, user   204         make_freebusy(store, publisher, preferences, user, participant,   205             store_and_publish, include_needs_action, reset_updated_list, verbose)   206    207 # vim: tabstop=4 expandtab shiftwidth=4