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