imip-agent

Annotated tools/make_freebusy.py

523:b9c05d30449f
2015-05-15 Paul Boddie Support the cancellation of previously unseparated recurrences.
paul@120 1
#!/usr/bin/env python
paul@120 2
paul@367 3
from imiptools.data import get_window_end, Object
paul@507 4
from imiptools.dates import format_datetime, get_default_timezone, to_recurrence_start
paul@481 5
from imiptools.period import FreeBusyPeriod
paul@291 6
from imiptools.profile import Preferences
paul@120 7
from imip_store import FileStore, FilePublisher
paul@120 8
import sys
paul@120 9
paul@395 10
def get_periods(fb, obj, tzid, window_end, only_organiser, recurrenceids):
paul@377 11
paul@377 12
    # Update free/busy details with the actual periods associated with the event.
paul@377 13
paul@395 14
    recurrenceid = format_datetime(obj.get_utc_datetime("RECURRENCE-ID")) or ""
paul@507 15
    recurrenceids = [to_recurrence_start(r, tzid) for r in recurrenceids]
paul@395 16
paul@481 17
    for p in obj.get_periods_for_freebusy(tzid, window_end):
paul@481 18
        if recurrenceid or p.start not in recurrenceids:
paul@481 19
            fb.append(FreeBusyPeriod(
paul@481 20
                p.start, p.end,
paul@395 21
                obj.get_value("UID"),
paul@395 22
                only_organiser and "ORG" or obj.get_value("TRANSP") or "OPAQUE",
paul@395 23
                recurrenceid,
paul@395 24
                obj.get_value("SUMMARY"),
paul@395 25
                obj.get_value("ORGANIZER")
paul@395 26
                ))
paul@377 27
paul@377 28
# Main program.
paul@377 29
paul@120 30
try:
paul@120 31
    user = sys.argv[1]
paul@395 32
    args = sys.argv[2:]
paul@481 33
    participant = args and args[0] not in ("-n", "-s", "-v") and args[0] or user
paul@395 34
    store_and_publish = "-s" in args
paul@395 35
    include_needs_action = "-n" in args
paul@481 36
    verbose = "-v" in args
paul@120 37
except IndexError:
paul@395 38
    print >>sys.stderr, """\
paul@395 39
Need a user and an optional participant (if different from the user),
paul@395 40
along with the -s option if updating the store and the published details.
paul@395 41
"""
paul@120 42
    sys.exit(1)
paul@120 43
paul@291 44
preferences = Preferences(user)
paul@291 45
tzid = preferences.get("TZID") or get_default_timezone()
paul@291 46
paul@367 47
# Get the size of the free/busy window.
paul@120 48
paul@367 49
try:
paul@367 50
    window_size = int(preferences.get("window_size"))
paul@367 51
except (TypeError, ValueError):
paul@367 52
    window_size = 100
paul@367 53
window_end = get_window_end(tzid, window_size)
paul@367 54
paul@367 55
store = FileStore()
paul@367 56
publisher = FilePublisher()
paul@367 57
paul@367 58
# Get all identifiers for events.
paul@367 59
paul@367 60
uids = store.get_events(user)
paul@349 61
paul@349 62
all_events = set()
paul@367 63
for uid in uids:
paul@349 64
    all_events.add((uid, None))
paul@367 65
    all_events.update([(uid, recurrenceid) for recurrenceid in store.get_recurrences(user, uid)])
paul@367 66
paul@367 67
# Filter out cancelled events.
paul@349 68
paul@367 69
cancelled = store.get_cancellations(user) or []
paul@349 70
all_events.difference_update(cancelled)
paul@120 71
paul@367 72
# Obtain event objects.
paul@367 73
paul@120 74
objs = []
paul@349 75
for uid, recurrenceid in all_events:
paul@481 76
    if verbose:
paul@481 77
        print >>sys.stderr, uid, recurrenceid
paul@367 78
    event = store.get_event(user, uid, recurrenceid)
paul@367 79
    if event:
paul@367 80
        objs.append(Object(event))
paul@367 81
paul@367 82
# Build a free/busy collection for the given user.
paul@120 83
paul@120 84
fb = []
paul@120 85
for obj in objs:
paul@377 86
    attendees = obj.get_value_map("ATTENDEE")
paul@377 87
    organiser = obj.get_value("ORGANIZER")
paul@395 88
    recurrenceids = store.get_recurrences(user, obj.get_value("UID"))
paul@291 89
paul@377 90
    for attendee, attendee_attr in attendees.items():
paul@367 91
paul@395 92
        # Only consider events where the stated participant actually attends.
paul@395 93
paul@395 94
        if attendee == participant:
paul@395 95
            partstat = attendee_attr.get("PARTSTAT", "NEEDS-ACTION")
paul@367 96
paul@395 97
            if partstat not in ("DECLINED", "DELEGATED", "NEEDS-ACTION") or \
paul@395 98
                include_needs_action and partstat == "NEEDS-ACTION":
paul@395 99
paul@395 100
                get_periods(fb, obj, tzid, window_end, False, recurrenceids)
paul@395 101
paul@377 102
            break
paul@367 103
paul@377 104
    # Where not attending, retain the affected periods and mark them as
paul@377 105
    # organising periods.
paul@377 106
paul@377 107
    else:
paul@395 108
        if organiser == participant:
paul@395 109
            get_periods(fb, obj, tzid, window_end, True, recurrenceids)
paul@120 110
paul@120 111
fb.sort()
paul@120 112
paul@367 113
# Store and publish the free/busy collection.
paul@367 114
paul@367 115
if store_and_publish:
paul@395 116
    if user == participant:
paul@395 117
        store.set_freebusy(user, fb)
paul@395 118
        publisher.set_freebusy(user, fb)
paul@395 119
    else:
paul@395 120
        store.set_freebusy_for_other(user, fb, participant)
paul@367 121
else:
paul@367 122
    for item in fb:
paul@481 123
        print "\t".join(item.as_tuple())
paul@120 124
paul@120 125
# vim: tabstop=4 expandtab shiftwidth=4