1.1 --- a/tools/make_freebusy.py Sun Jun 05 19:34:36 2016 +0200
1.2 +++ b/tools/make_freebusy.py Sun Jun 05 19:36:32 2016 +0200
1.3 @@ -38,17 +38,20 @@
1.4 from imiptools.client import Client
1.5 from imiptools.data import get_window_end, Object
1.6 from imiptools.dates import get_default_timezone, to_utc_datetime
1.7 -from imiptools.period import FreeBusyCollection
1.8 +from imiptools.period import FreeBusyCollection, FreeBusyGroupCollection, \
1.9 + FreeBusyGroupPeriod
1.10 from imiptools.stores import get_store, get_publisher, get_journal
1.11
1.12 -def make_freebusy(client, participant, store_and_publish, include_needs_action,
1.13 - reset_updated_list, verbose):
1.14 +def make_freebusy(client, participants, storage, store_and_publish,
1.15 + include_needs_action, reset_updated_list, verbose):
1.16
1.17 """
1.18 Using the given 'client' representing a user, make free/busy details for the
1.19 - records of the user, generating details for 'participant' if not indicated
1.20 + records of the user, generating details for 'participants' if not indicated
1.21 as None; otherwise, generating free/busy details concerning the given user.
1.22
1.23 + The 'storage' is the specific store or journal object used to access data.
1.24 +
1.25 If 'store_and_publish' is set, the stored details will be updated;
1.26 otherwise, the details will be written to standard output.
1.27
1.28 @@ -64,11 +67,10 @@
1.29 """
1.30
1.31 user = client.user
1.32 - store = client.get_store()
1.33 + journal = client.get_journal()
1.34 publisher = client.get_publisher()
1.35 preferences = client.get_preferences()
1.36
1.37 - participant = participant or user
1.38 tzid = preferences.get("TZID") or get_default_timezone()
1.39
1.40 # Get the size of the free/busy window.
1.41 @@ -79,67 +81,112 @@
1.42 window_size = 100
1.43 window_end = get_window_end(tzid, window_size)
1.44
1.45 - # Get identifiers for uncancelled events either from a list of events
1.46 - # providing free/busy periods at the end of the given time window, or from
1.47 - # a list of all events.
1.48 + providers = []
1.49 +
1.50 + # Iterate over participants, with None being a special null participant
1.51 + # value.
1.52 +
1.53 + for participant in participants or [None]:
1.54 +
1.55 + # Get identifiers for uncancelled events either from a list of events
1.56 + # providing free/busy periods at the end of the given time window, or from
1.57 + # a list of all events.
1.58
1.59 - all_events = not reset_updated_list and store.get_freebusy_providers(user, window_end)
1.60 + all_events = not reset_updated_list and storage.get_freebusy_providers(user, window_end)
1.61
1.62 - if not all_events:
1.63 - all_events = store.get_all_events(user)
1.64 - fb = FreeBusyCollection()
1.65 + if not all_events:
1.66 + all_events = storage.get_all_events(user)
1.67 + if storage is journal:
1.68 + fb = FreeBusyGroupCollection()
1.69 + else:
1.70 + fb = FreeBusyCollection()
1.71 +
1.72 + # With providers of additional periods, append to the existing collection.
1.73
1.74 - # With providers of additional periods, append to the existing collection.
1.75 + else:
1.76 + if participants is None:
1.77 + fb = storage.get_freebusy_for_update(user)
1.78 + else:
1.79 + fb = storage.get_freebusy_for_other_for_update(user, participant)
1.80 +
1.81 + # Obtain event objects.
1.82
1.83 - else:
1.84 - if user == participant:
1.85 - fb = store.get_freebusy(user)
1.86 - else:
1.87 - fb = store.get_freebusy_for_other(user, participant)
1.88 + objs = []
1.89 + for uid, recurrenceid in all_events:
1.90 + if verbose:
1.91 + print >>sys.stderr, uid, recurrenceid
1.92 + event = storage.get_event(user, uid, recurrenceid)
1.93 + if event:
1.94 + objs.append(Object(event))
1.95
1.96 - # Obtain event objects.
1.97 + # Build a free/busy collection for the given user.
1.98 +
1.99 + for obj in objs:
1.100 + recurrenceids = not obj.get_recurrenceid() and storage.get_recurrences(user, obj.get_uid())
1.101 +
1.102 + # Obtain genuine attendees.
1.103
1.104 - objs = []
1.105 - for uid, recurrenceid in all_events:
1.106 - if verbose:
1.107 - print >>sys.stderr, uid, recurrenceid
1.108 - event = store.get_event(user, uid, recurrenceid)
1.109 - if event:
1.110 - objs.append(Object(event))
1.111 + if storage is journal:
1.112 + attendees = storage.get_delegates(user)
1.113 + else:
1.114 + attendees = [participant]
1.115 +
1.116 + # Generate records for each attendee (applicable to consolidated
1.117 + # journal data).
1.118
1.119 - # Build a free/busy collection for the given user.
1.120 + for attendee in attendees:
1.121 + partstat = obj.get_participation_status(attendee)
1.122 +
1.123 + # Only include objects where the attendee actually participates.
1.124
1.125 - for obj in objs:
1.126 - partstat = obj.get_participation_status(participant)
1.127 - recurrenceids = not obj.get_recurrenceid() and store.get_recurrences(user, obj.get_uid())
1.128 + if obj.get_participation(partstat, include_needs_action):
1.129 +
1.130 + # Add each active period to the collection.
1.131 +
1.132 + for p in obj.get_active_periods(recurrenceids, tzid, window_end):
1.133 +
1.134 + # Obtain a suitable period object.
1.135
1.136 - if obj.get_participation(partstat, include_needs_action):
1.137 - for p in obj.get_active_periods(recurrenceids, tzid, window_end):
1.138 - fbp = obj.get_freebusy_period(p, partstat == "ORG")
1.139 - fb.insert_period(fbp)
1.140 + fbp = obj.get_freebusy_period(p, partstat == "ORG")
1.141 +
1.142 + if storage is journal:
1.143 + fbp = FreeBusyGroupPeriod(*fbp.as_tuple(), attendee=attendee)
1.144
1.145 - # Store and publish the free/busy collection.
1.146 + fb.insert_period(fbp)
1.147 +
1.148 + # Store and publish the free/busy collection.
1.149 +
1.150 + if store_and_publish:
1.151
1.152 - if store_and_publish:
1.153 - if user == participant:
1.154 - store.set_freebusy(user, fb)
1.155 + # Set the user's own free/busy information.
1.156 +
1.157 + if participant is None:
1.158 + storage.set_freebusy(user, fb)
1.159
1.160 - if client.is_sharing() and client.is_publishing():
1.161 - publisher.set_freebusy(user, fb)
1.162 + if client.is_sharing() and client.is_publishing():
1.163 + publisher.set_freebusy(user, fb)
1.164 +
1.165 + # Set free/busy information concerning another user.
1.166 +
1.167 + else:
1.168 + storage.set_freebusy_for_other(user, fb, participant)
1.169
1.170 # Update the list of objects providing periods on future occasions.
1.171
1.172 - store.set_freebusy_providers(user, to_utc_datetime(window_end, tzid),
1.173 - [obj for obj in objs if obj.possibly_active_from(window_end, tzid)])
1.174 - else:
1.175 - store.set_freebusy_for_other(user, fb, participant)
1.176 + if participant is None or storage is journal:
1.177 + providers += [obj for obj in objs if obj.possibly_active_from(window_end, tzid)]
1.178 +
1.179 + # Alternatively, just write the collection to standard output.
1.180
1.181 - # Alternatively, just write the collection to standard output.
1.182 + else:
1.183 + f = getwriter("utf-8")(sys.stdout)
1.184 + for item in fb:
1.185 + print >>f, "\t".join(item.as_tuple(strings_only=True))
1.186
1.187 - else:
1.188 - f = getwriter("utf-8")(sys.stdout)
1.189 - for item in fb:
1.190 - print >>f, "\t".join(item.as_tuple(strings_only=True))
1.191 + # Update free/busy providers if storing.
1.192 +
1.193 + if store_and_publish:
1.194 + storage.set_freebusy_providers(user, to_utc_datetime(window_end, tzid), providers)
1.195
1.196 # Main program.
1.197
1.198 @@ -162,7 +209,7 @@
1.199 l = participants
1.200
1.201 for arg in sys.argv[1:]:
1.202 - if arg in ("-n", "-s", "-v", "-r"):
1.203 + if arg in ("-n", "-s", "-v", "-r", "-q"):
1.204 args.append(arg)
1.205 l = ignored
1.206 elif arg == "-T":
1.207 @@ -182,13 +229,14 @@
1.208 user = participants[0]
1.209 except IndexError:
1.210 print >>sys.stderr, """\
1.211 -Usage: %s <user> [ <other user> ] [ <options> ]
1.212 +Usage: %s <user> [ <other user> ... ] [ <options> ]
1.213
1.214 -Need a user and an optional participant (if different from the user),
1.215 +Need a user and optional participants (if different from the user),
1.216 along with the -s option if updating the store and the published details.
1.217
1.218 Specific options:
1.219
1.220 +-q Access quotas in the journal instead of users in the store
1.221 -s Update the store and published details (write details to standard output
1.222 otherwise)
1.223 -n Include objects with PARTSTAT of NEEDS-ACTION
1.224 @@ -207,7 +255,8 @@
1.225
1.226 # Define any other participant of interest plus options.
1.227
1.228 - participant = participants[1:] and participants[1] or None
1.229 + participants = participants[1:]
1.230 + using_journal = "-q" in args
1.231 store_and_publish = "-s" in args
1.232 include_needs_action = "-n" in args
1.233 reset_updated_list = "-r" in args
1.234 @@ -229,20 +278,44 @@
1.235 publisher = get_publisher(publishing_dir)
1.236 journal = get_journal(store_type, journal_dir)
1.237
1.238 + # Determine which kind of object will be accessed.
1.239 +
1.240 + if using_journal:
1.241 + storage = journal
1.242 + else:
1.243 + storage = store
1.244 +
1.245 # Obtain a list of users for processing.
1.246
1.247 if user in ("*", "all"):
1.248 - users = store.get_users()
1.249 + users = storage.get_users()
1.250 else:
1.251 users = [user]
1.252
1.253 + # Obtain a list of participants for processing.
1.254 +
1.255 + if participants and participants[0] in ("*", "all"):
1.256 + participants = storage.get_freebusy_others(user)
1.257 +
1.258 + # Provide a participants list to iterate over even if no specific
1.259 + # participant is involved. This updates a user's own records, but only for
1.260 + # the general data store.
1.261 +
1.262 + elif not participants:
1.263 + if not using_journal:
1.264 + participants = None
1.265 + else:
1.266 + print >>sys.stderr, "Participants must be indicated when updating quota records."
1.267 + sys.exit(1)
1.268 +
1.269 # Process the given users.
1.270
1.271 for user in users:
1.272 if verbose:
1.273 print >>sys.stderr, user
1.274 make_freebusy(
1.275 - Client(user, None, store, publisher, journal, preferences_dir), participant,
1.276 - store_and_publish, include_needs_action, reset_updated_list, verbose)
1.277 + Client(user, None, store, publisher, journal, preferences_dir),
1.278 + participants, storage, store_and_publish, include_needs_action,
1.279 + reset_updated_list, verbose)
1.280
1.281 # vim: tabstop=4 expandtab shiftwidth=4