# HG changeset patch # User Paul Boddie # Date 1441056583 -7200 # Node ID 93ad1f9ce395916aef99c61800f7b28166c03137 # Parent d98222e97d5e75b144191bfe5875955071a19e6d Added support/guidance for daily free/busy updates for all users using cron. diff -r d98222e97d5e -r 93ad1f9ce395 README.txt --- a/README.txt Mon Aug 31 18:09:31 2015 +0200 +++ b/README.txt Mon Aug 31 23:29:43 2015 +0200 @@ -58,6 +58,7 @@ The conf directory contains subdirectories for different systems: apache Apache 2 site configuration for publishing resources + cron Cron command scheduling for free/busy updates exim Exim 4 routing and transport configuration postfix Postfix routing and transport configuration @@ -187,6 +188,23 @@ dpkg-statoverride --force --update --add \ cyrus lmtp 750 /var/run/cyrus/socket +Configuring Cron for Free/Busy Updates +-------------------------------------- + +The periods occupied by recurring events are not expanded beyond a certain +window of time by imip-agent. As a consequence, free/busy collections need to +be progressively expanded over time to include periods occupied by such events +that were not previously recorded in those collections. + +The conf/cron/cron.daily/imip-agent file contains commands that update +free/busy collections for all known users, and this should be copied to the +appropriate destination. For example: + +cp conf/cron/cron.daily/imip-agent /etc/cron.daily/ + +Where frequency-specific directories are not supported by cron on a system, a +crontab entry of the appropriate format is required instead. + Prerequisites ------------- diff -r d98222e97d5e -r 93ad1f9ce395 conf/cron/cron.daily/imip-agent --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/cron/cron.daily/imip-agent Mon Aug 31 23:29:43 2015 +0200 @@ -0,0 +1,6 @@ +#!/bin/sh + +INSTALL_DIR=/var/lib/imip-agent +export PYTHONPATH="$INSTALL_DIR" + +"$INSTALL_DIR/tools/make_freebusy.py" all -n -s diff -r d98222e97d5e -r 93ad1f9ce395 imip_store.py --- a/imip_store.py Mon Aug 31 18:09:31 2015 +0200 +++ b/imip_store.py Mon Aug 31 23:29:43 2015 +0200 @@ -166,6 +166,14 @@ return True + # User discovery. + + def get_users(self): + + "Return a list of users." + + return listdir(self.store_dir) + # Event and event metadata access. def get_events(self, user): diff -r d98222e97d5e -r 93ad1f9ce395 tools/install.sh --- a/tools/install.sh Mon Aug 31 18:09:31 2015 +0200 +++ b/tools/install.sh Mon Aug 31 23:29:43 2015 +0200 @@ -1,9 +1,10 @@ #!/bin/sh +# Agents and modules. + AGENTS="imip_person.py imip_person_outgoing.py imip_resource.py" MODULES="markup.py imip_store.py vCalendar.py vContent.py vRecurrence.py" INSTALL_DIR=/var/lib/imip-agent -WEB_INSTALL_DIR=/var/www/imip-agent if [ ! -e "$INSTALL_DIR" ]; then mkdir -p "$INSTALL_DIR" @@ -28,6 +29,18 @@ cp imiptools/*.py "$INSTALL_DIR/imiptools/" cp imiptools/handlers/*.py "$INSTALL_DIR/imiptools/handlers/" +# Tools + +if [ ! -e "$INSTALL_DIR/tools" ]; then + mkdir -p "$INSTALL_DIR/tools" +fi + +cp tools/make_freebusy.py "$INSTALL_DIR/tools/" + +# Web manager interface. + +WEB_INSTALL_DIR=/var/www/imip-agent + if [ ! -e "$WEB_INSTALL_DIR" ]; then mkdir -p "$WEB_INSTALL_DIR" fi diff -r d98222e97d5e -r 93ad1f9ce395 tools/make_freebusy.py --- a/tools/make_freebusy.py Mon Aug 31 18:09:31 2015 +0200 +++ b/tools/make_freebusy.py Mon Aug 31 23:29:43 2015 +0200 @@ -21,7 +21,6 @@ this program. If not, see . """ -from bisect import bisect_left from codecs import getwriter from imiptools.data import get_window_end, Object from imiptools.dates import get_default_timezone, to_utc_datetime @@ -30,26 +29,28 @@ from imip_store import FileStore, FilePublisher import sys -# Main program. +def make_freebusy(user, participant, store_and_publish, include_needs_action, reset_updated_list, verbose): -if __name__ == "__main__": + """ + Make free/busy details for the records of the given 'user', generating + details for 'participant' if not indicated as None; otherwise, generating + free/busy details concerning the given user. - # Interpret the command line arguments. + If 'store_and_publish' is set, the stored details will be updated; + otherwise, the details will be written to standard output. - try: - user = sys.argv[1] - args = sys.argv[2:] - participant = args and args[0] not in ("-n", "-s", "-v", "-r") and args[0] or user - store_and_publish = "-s" in args - include_needs_action = "-n" in args - verbose = "-v" in args - reset_updated_list = "-r" in args - except IndexError: - print >>sys.stderr, """\ - Need a user and an optional participant (if different from the user), - along with the -s option if updating the store and the published details. + If 'include_needs_action' is set, details of objects whose participation + status is set to "NEEDS-ACTION" for the participant will be included in the + details. + + If 'reset_updated_list' is set, all objects will be inspected for periods; + otherwise, only those in the stored free/busy providers file will be + inspected. + + If 'verbose' is set, messages will be written to standard error. """ - sys.exit(1) + + participant = participant or user preferences = Preferences(user) tzid = preferences.get("TZID") or get_default_timezone() @@ -110,12 +111,54 @@ if user == participant: store.set_freebusy(user, fb) publisher.set_freebusy(user, fb) - store.set_freebusy_providers(user, to_utc_datetime(window_end, tzid), [obj for obj in objs if obj.possibly_active_from(window_end, tzid)]) + + # Update the list of objects providing periods on future occasions. + + store.set_freebusy_providers(user, to_utc_datetime(window_end, tzid), + [obj for obj in objs if obj.possibly_active_from(window_end, tzid)]) else: store.set_freebusy_for_other(user, fb, participant) + + # Alternatively, just write the collection to standard output. + else: f = getwriter("utf-8")(sys.stdout) for item in fb: print >>f, "\t".join(item.as_tuple(strings_only=True)) +# Main program. + +if __name__ == "__main__": + + # Interpret the command line arguments. + + try: + user = sys.argv[1] + args = sys.argv[2:] + participant = args and args[0] not in ("-n", "-s", "-v", "-r") and args[0] or None + store_and_publish = "-s" in args + include_needs_action = "-n" in args + reset_updated_list = "-r" in args + verbose = "-v" in args + + except IndexError: + print >>sys.stderr, """\ +Need a user and an optional participant (if different from the user), +along with the -s option if updating the store and the published details. +Specify -n to include objects with PARTSTAT of NEEDS-ACTION. +Specify -r to inspect all objects, not just those expected to provide details. +Specify -v for additional messages on standard error. + """ + sys.exit(1) + + if user in ("*", "all"): + users = FileStore().get_users() + else: + users = [user] + + for user in users: + if verbose: + print >>sys.stderr, user + make_freebusy(user, participant, store_and_publish, include_needs_action, reset_updated_list, verbose) + # vim: tabstop=4 expandtab shiftwidth=4