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