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