# HG changeset patch # User Paul Boddie # Date 1425774943 -3600 # Node ID df76475bf82c394bb9e575ba3d9ad16f1b64ecee # Parent 63473567026a7b276eb305c006ad509ad44c5848 Added summary and organiser details to free/busy tables in order to avoid accessing individual object files. Updated the free/busy tool, also introducing missing subtraction of replaced occurrences from recurring events necessitated when EXDATE usage on original events was discontinued, together with generation of other parties' free/busy details and flexible handling of the NEEDS-ACTION participation status. diff -r 63473567026a -r df76475bf82c imip_manager.py --- a/imip_manager.py Sat Mar 07 00:13:41 2015 +0100 +++ b/imip_manager.py Sun Mar 08 01:35:43 2015 +0100 @@ -228,7 +228,9 @@ update_freebusy(freebusy, self.obj.get_periods_for_freebusy(self.get_tzid(), self.get_window_end()), self.obj.get_value("TRANSP") or "OPAQUE", - self.uid, self.recurrenceid) + self.uid, self.recurrenceid, + self.obj.get_value("SUMMARY"), + organiser) user_attr = self.messenger and self.messenger.sender != get_address(self.user) and \ {"SENT-BY" : get_uri(self.messenger.sender)} or {} @@ -374,15 +376,23 @@ obj = self._get_object(uid, recurrenceid) if obj: periods = obj.get_periods_for_freebusy(self.get_tzid(), self.get_window_end()) - - # Subtract any recurrences from the free/busy details of a parent - # object. - recurrenceids = self._get_recurrences(uid) + # Convert the periods to more substantial free/busy items. + for start, end in periods: + + # Subtract any recurrences from the free/busy details of a + # parent object. + if recurrenceid or start not in recurrenceids: - summary.append((start, end, uid, obj.get_value("TRANSP"), recurrenceid)) + summary.append(( + start, end, uid, + obj.get_value("TRANSP"), + recurrenceid, + obj.get_value("SUMMARY"), + obj.get_value("ORGANIZER") + )) return summary # Preference methods. @@ -430,7 +440,9 @@ update_freebusy(freebusy, obj.get_periods_for_freebusy(self.get_tzid(), self.get_window_end()), is_only_organiser and "ORG" or obj.get_value("TRANSP"), - uid, recurrenceid) + uid, recurrenceid, + obj.get_value("SUMMARY"), + obj.get_value("ORGANIZER")) # Subtract any recurrences from the free/busy details of a parent # object. @@ -709,7 +721,7 @@ elif save: to_cancel = self.update_attendees(obj, added, removed) self.store.set_event(self.user, uid, recurrenceid, node=obj.to_node()) - self.update_freebusy(uid, recurrenceid, obj=obj) + self.update_freebusy(uid, recurrenceid, obj) self.remove_request(uid, recurrenceid) # Remove the request and the object. @@ -1702,7 +1714,7 @@ page.td.close() empty = 0 - start, end, uid, recurrenceid, key = get_freebusy_details(t) + start, end, uid, recurrenceid, summary, organiser, key = get_freebusy_details(t) span = spans[key] # Produce a table cell only at the start of the period @@ -1710,11 +1722,9 @@ if point == start or continuation: - obj = self._get_object(uid, recurrenceid) - has_continued = continuation and point != start will_continue = not ends_on_same_day(point, end, tzid) - is_organiser = obj and get_uri(obj.get_value("ORGANIZER")) == self.user + is_organiser = organiser == self.user css = " ".join( ["event"] + @@ -1732,18 +1742,13 @@ else: page.td(class_=css, rowspan=span) - if not obj: - page.span("(Participant is busy)") + # Only link to events if they are not being + # updated by requests. + + if not summary or (uid, recurrenceid) in self._get_requests() and group_type != "request": + page.span(summary or "(Participant is busy)") else: - summary = obj.get_value("SUMMARY") - - # Only link to events if they are not being - # updated by requests. - - if (uid, recurrenceid) in self._get_requests() and group_type != "request": - page.span(summary) - else: - page.a(summary, href=self.link_to(uid, recurrenceid)) + page.a(summary, href=self.link_to(uid, recurrenceid)) page.td.close() else: diff -r 63473567026a -r df76475bf82c imip_store.py --- a/imip_store.py Sat Mar 07 00:13:41 2015 +0100 +++ b/imip_store.py Sun Mar 08 01:35:43 2015 +0100 @@ -26,6 +26,7 @@ from os.path import exists, isfile, join from os import listdir, remove, rmdir from time import sleep +import codecs class FileStore(FileBase): @@ -61,7 +62,7 @@ self.acquire_lock(user) try: - f = open(filename, "rb") + f = codecs.open(filename, "rb", encoding="utf-8") try: l = [] for line in f.readlines(): @@ -88,7 +89,7 @@ self.acquire_lock(user) try: - f = open(filename, "wb") + f = codecs.open(filename, "wb", encoding="utf-8") try: for item in items: if empty_defaults: @@ -514,11 +515,11 @@ rwrite(("UID", {}, user)) rwrite(("DTSTAMP", {}, datetime.utcnow().strftime("%Y%m%dT%H%M%SZ"))) - for start, end, uid, transp, recurrenceid in freebusy: + for start, end, uid, transp, recurrenceid, summary, organiser in freebusy: if not transp or transp == "OPAQUE": rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end]))) - f = open(filename, "w") + f = open(filename, "wb") try: to_stream(f, make_calendar([("VFREEBUSY", {}, record)], "PUBLISH")) finally: diff -r 63473567026a -r df76475bf82c imiptools/content.py --- a/imiptools/content.py Sat Mar 07 00:13:41 2015 +0100 +++ b/imiptools/content.py Sun Mar 08 01:35:43 2015 +0100 @@ -207,8 +207,11 @@ event. """ - update_freebusy(freebusy, periods, transp or self.obj.get_value("TRANSP"), - self.uid, recurrenceid) + update_freebusy(freebusy, periods, + transp or self.obj.get_value("TRANSP"), + self.uid, recurrenceid, + self.obj.get_value("SUMMARY"), + self.obj.get_value("ORGANIZER")) def update_freebusy(self, freebusy, periods, transp=None): diff -r 63473567026a -r df76475bf82c imiptools/data.py --- a/imiptools/data.py Sat Mar 07 00:13:41 2015 +0100 +++ b/imiptools/data.py Sun Mar 08 01:35:43 2015 +0100 @@ -159,7 +159,7 @@ rwrite(("DTSTART", {"VALUE" : "DATE-TIME"}, periods[0][0])) rwrite(("DTEND", {"VALUE" : "DATE-TIME"}, periods[-1][1])) - for start, end, uid, transp, recurrenceid in periods: + for start, end, uid, transp, recurrenceid, summary, organiser in periods: if transp == "OPAQUE": rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end]))) diff -r 63473567026a -r df76475bf82c imiptools/period.py --- a/imiptools/period.py Sat Mar 07 00:13:41 2015 +0100 +++ b/imiptools/period.py Sun Mar 08 01:35:43 2015 +0100 @@ -33,7 +33,7 @@ """ for conflict in have_conflict(freebusy, periods, True): - start, end, found_uid, found_transp, found_recurrenceid = conflict + start, end, found_uid, found_transp, found_recurrenceid = conflict[:5] if found_uid != uid and found_recurrenceid != recurrenceid: return False @@ -390,7 +390,7 @@ for point, active in slots: for t in active: if t and len(t) >= 2: - start, end, uid, recurrenceid, key = get_freebusy_details(t) + start, end, uid, recurrenceid, summary, organiser, key = get_freebusy_details(t) try: start_slot = points.index(start) @@ -406,12 +406,15 @@ def get_freebusy_details(t): - "Return a tuple of the form (start, end, uid, recurrenceid, key) from 't'." + """ + Return a tuple of the form (start, end, uid, recurrenceid, summary, + organiser, key) from 't'. + """ # Handle both complete free/busy details... - if len(t) > 4: - start, end, uid, transp, recurrenceid = t[:5] + if len(t) >= 7: + start, end, uid, transp, recurrenceid, summary, organiser = t[:7] key = uid, recurrenceid # ...and published details without specific event details. @@ -420,20 +423,22 @@ start, end = t[:2] uid = None recurrenceid = None + summary = None + organiser = None key = (start, end) - return start, end, uid, recurrenceid, key + return start, end, uid, recurrenceid, summary, organiser, key -def update_freebusy(freebusy, periods, transp, uid, recurrenceid): +def update_freebusy(freebusy, periods, transp, uid, recurrenceid, summary, organiser): """ - Update the free/busy details with the given 'periods', 'transp' setting and - 'uid' plus 'recurrenceid'. + Update the free/busy details with the given 'periods', 'transp' setting, + 'uid' plus 'recurrenceid' and 'summary' and 'organiser' details. """ remove_period(freebusy, uid, recurrenceid) for start, end in periods: - insert_period(freebusy, (start, end, uid, transp, recurrenceid)) + insert_period(freebusy, (start, end, uid, transp, recurrenceid, summary, organiser)) # vim: tabstop=4 expandtab shiftwidth=4 diff -r 63473567026a -r df76475bf82c tools/make_freebusy.py --- a/tools/make_freebusy.py Sat Mar 07 00:13:41 2015 +0100 +++ b/tools/make_freebusy.py Sun Mar 08 01:35:43 2015 +0100 @@ -6,24 +6,36 @@ from imip_store import FileStore, FilePublisher import sys -def get_periods(fb, obj, tzid, window_end, only_organiser): +def get_periods(fb, obj, tzid, window_end, only_organiser, recurrenceids): # Update free/busy details with the actual periods associated with the event. + recurrenceid = format_datetime(obj.get_utc_datetime("RECURRENCE-ID")) or "" + for start, end in obj.get_periods_for_freebusy(tzid, window_end): - fb.append((start, end, - obj.get_value("UID"), - only_organiser and "ORG" or obj.get_value("TRANSP") or "OPAQUE", - format_datetime(obj.get_utc_datetime("RECURRENCE-ID")) or "", - )) + if recurrenceid or start not in recurrenceids: + fb.append(( + start, end, + obj.get_value("UID"), + only_organiser and "ORG" or obj.get_value("TRANSP") or "OPAQUE", + recurrenceid, + obj.get_value("SUMMARY"), + obj.get_value("ORGANIZER") + )) # Main program. try: user = sys.argv[1] - store_and_publish = "-s" in sys.argv[2:] + args = sys.argv[2:] + participant = args and args[0] not in ("-n", "-s") and args[0] or user + store_and_publish = "-s" in args + include_needs_action = "-n" in args except IndexError: - print >>sys.stderr, "Need a user, along with the -s option if updating the store." + print >>sys.stderr, """\ +Need a user and an optional participant (if different from the user), +along with the -s option if updating the store and the published details. +""" sys.exit(1) preferences = Preferences(user) @@ -69,30 +81,39 @@ for obj in objs: attendees = obj.get_value_map("ATTENDEE") organiser = obj.get_value("ORGANIZER") + recurrenceids = store.get_recurrences(user, obj.get_value("UID")) for attendee, attendee_attr in attendees.items(): - # Only consider events where this user actually attends. + # Only consider events where the stated participant actually attends. + + if attendee == participant: + partstat = attendee_attr.get("PARTSTAT", "NEEDS-ACTION") - if attendee == user: - if attendee_attr.get("PARTSTAT", "NEEDS-ACTION") not in ("DECLINED", "DELEGATED", "NEEDS-ACTION"): - get_periods(fb, obj, tzid, window_end, False) + if partstat not in ("DECLINED", "DELEGATED", "NEEDS-ACTION") or \ + include_needs_action and partstat == "NEEDS-ACTION": + + get_periods(fb, obj, tzid, window_end, False, recurrenceids) + break # Where not attending, retain the affected periods and mark them as # organising periods. else: - if organiser == user: - get_periods(fb, obj, tzid, window_end, True) + if organiser == participant: + get_periods(fb, obj, tzid, window_end, True, recurrenceids) fb.sort() # Store and publish the free/busy collection. if store_and_publish: - store.set_freebusy(user, fb) - publisher.set_freebusy(user, fb) + if user == participant: + store.set_freebusy(user, fb) + publisher.set_freebusy(user, fb) + else: + store.set_freebusy_for_other(user, fb, participant) else: for item in fb: print "\t".join(item)