# HG changeset patch # User Paul Boddie # Date 1456949831 -3600 # Node ID 2264ab469f6dc753bbe477dda3c743d532fbf838 # Parent 5f9f96eb4f8f82aaa0de102ecf973d581732da08 Introduced a free/busy collection abstraction for potential access and representation efficiency improvements. diff -r 5f9f96eb4f8f -r 2264ab469f6d imip_store.py --- a/imip_store.py Tue Feb 09 15:57:23 2016 +0100 +++ b/imip_store.py Wed Mar 02 21:17:11 2016 +0100 @@ -24,7 +24,7 @@ from imiptools.data import make_calendar, parse_object, to_stream from imiptools.dates import format_datetime, get_datetime, to_timezone from imiptools.filesys import fix_permissions, FileBase -from imiptools.period import FreeBusyPeriod +from imiptools.period import FreeBusyPeriod, FreeBusyCollection from imiptools.text import parse_line from os.path import isdir, isfile, join from os import listdir, remove, rmdir @@ -577,23 +577,29 @@ "Get free/busy details for the given 'user'." filename = self.get_object_in_store(user, name or "freebusy") + if not filename or not isfile(filename): - return [] + periods = [] else: - return map(lambda t: FreeBusyPeriod(*t), + periods = map(lambda t: FreeBusyPeriod(*t), (get_table or self._get_table_atomic)(user, filename, [(4, None)])) + return FreeBusyCollection(periods) + def get_freebusy_for_other(self, user, other, get_table=None): "For the given 'user', get free/busy details for the 'other' user." filename = self.get_object_in_store(user, "freebusy-other", other) + if not filename or not isfile(filename): - return [] + periods = [] else: - return map(lambda t: FreeBusyPeriod(*t), + periods = map(lambda t: FreeBusyPeriod(*t), (get_table or self._get_table_atomic)(user, filename, [(4, None)])) + return FreeBusyCollection(periods) + def set_freebusy(self, user, freebusy, name=None, set_table=None): "For the given 'user', set 'freebusy' details." @@ -603,7 +609,7 @@ return False (set_table or self._set_table_atomic)(user, filename, - map(lambda fb: fb.as_tuple(strings_only=True), freebusy)) + map(lambda fb: fb.as_tuple(strings_only=True), freebusy.periods)) return True def set_freebusy_for_other(self, user, freebusy, other, set_table=None): @@ -615,7 +621,7 @@ return False (set_table or self._set_table_atomic)(user, filename, - map(lambda fb: fb.as_tuple(strings_only=True), freebusy)) + map(lambda fb: fb.as_tuple(strings_only=True), freebusy.periods)) return True # Tentative free/busy periods related to countering. @@ -644,7 +650,7 @@ finally: self.release_lock(user) - return offers + return FreeBusyCollection(offers) def set_freebusy_offers(self, user, freebusy): @@ -1011,11 +1017,14 @@ "Get free/busy details for the given 'quota' and 'user'." filename = self.get_object_in_store(quota, "freebusy", user) - if not filename or not isfile(filename): - return [] - return map(lambda t: FreeBusyPeriod(*t), - (get_table or self._get_table_atomic)(quota, filename, [(4, None)])) + if not filename or not isfile(filename): + periods = [] + else: + periods = map(lambda t: FreeBusyPeriod(*t), + (get_table or self._get_table_atomic)(quota, filename, [(4, None)])) + + return FreeBusyCollection(periods) def set_freebusy(self, quota, user, freebusy, set_table=None): @@ -1026,7 +1035,7 @@ return False (set_table or self._set_table_atomic)(quota, filename, - map(lambda fb: fb.as_tuple(strings_only=True), freebusy)) + map(lambda fb: fb.as_tuple(strings_only=True), freebusy.periods)) return True # Journal entry methods. @@ -1039,11 +1048,14 @@ """ filename = self.get_object_in_store(quota, "journal", group) - if not filename or not isfile(filename): - return [] - return map(lambda t: FreeBusyPeriod(*t), - self._get_table_atomic(quota, filename, [(4, None)])) + if not filename or not isfile(filename): + periods = [] + else: + periods = map(lambda t: FreeBusyPeriod(*t), + self._get_table_atomic(quota, filename, [(4, None)])) + + return FreeBusyCollection(periods) def set_entries(self, quota, group, entries): @@ -1057,7 +1069,7 @@ return False self._set_table_atomic(quota, filename, - map(lambda fb: fb.as_tuple(strings_only=True), entries)) + map(lambda fb: fb.as_tuple(strings_only=True), entries.periods)) return True # vim: tabstop=4 expandtab shiftwidth=4 diff -r 5f9f96eb4f8f -r 2264ab469f6d imiptools/client.py --- a/imiptools/client.py Tue Feb 09 15:57:23 2016 +0100 +++ b/imiptools/client.py Wed Mar 02 21:17:11 2016 +0100 @@ -27,9 +27,6 @@ from imiptools.dates import check_permitted_values, format_datetime, get_default_timezone, \ get_duration, get_timestamp from imiptools.i18n import get_translator -from imiptools.period import can_schedule, remove_event_periods, \ - remove_additional_periods, remove_affected_period, \ - update_freebusy from imiptools.profile import Preferences import imip_store @@ -288,7 +285,7 @@ offer. """ - update_freebusy(freebusy, periods, transp, uid, recurrenceid, summary, organiser, expires) + freebusy.update_freebusy(periods, transp, uid, recurrenceid, summary, organiser, expires) # Preparation of messages communicating the state of events. @@ -862,7 +859,7 @@ Indicate whether within 'freebusy' the given 'periods' can be scheduled. """ - return can_schedule(freebusy, periods, self.uid, self.recurrenceid) + return freebusy.can_schedule(periods, self.uid, self.recurrenceid) def have_new_object(self, strict=True): @@ -993,9 +990,9 @@ "Remove this event from the given 'freebusy' collection." - removed = remove_event_periods(freebusy, self.uid, self.recurrenceid) + removed = freebusy.remove_event_periods(self.uid, self.recurrenceid) if not removed and self.recurrenceid: - return remove_affected_period(freebusy, self.uid, self.get_recurrence_start_point(self.recurrenceid)) + return freebusy.remove_affected_period(self.uid, self.get_recurrence_start_point(self.recurrenceid)) else: return removed @@ -1011,18 +1008,18 @@ if self.recurrenceid: recurrenceid = self.get_recurrence_start_point(self.recurrenceid) - remove_affected_period(freebusy, self.uid, recurrenceid) + freebusy.remove_affected_period(self.uid, recurrenceid) else: # Remove obsolete recurrence periods. - remove_additional_periods(freebusy, self.uid, recurrenceids) + freebusy.remove_additional_periods(self.uid, recurrenceids) # Remove original periods affected by additional recurrences. if recurrenceids: for recurrenceid in recurrenceids: recurrenceid = self.get_recurrence_start_point(recurrenceid) - remove_affected_period(freebusy, self.uid, recurrenceid) + freebusy.remove_affected_period(self.uid, recurrenceid) def update_freebusy(self, freebusy, user, as_organiser, offer=False): diff -r 5f9f96eb4f8f -r 2264ab469f6d imiptools/data.py --- a/imiptools/data.py Tue Feb 09 15:57:23 2016 +0100 +++ b/imiptools/data.py Wed Mar 02 21:17:11 2016 +0100 @@ -3,7 +3,7 @@ """ Interpretation of vCalendar content. -Copyright (C) 2014, 2015 Paul Boddie +Copyright (C) 2014, 2015, 2016 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -29,7 +29,7 @@ get_recurrence_start_point, \ get_time, get_tzid, to_datetime, to_timezone, \ to_utc_datetime -from imiptools.period import FreeBusyPeriod, Period, RecurringPeriod, period_overlaps +from imiptools.period import FreeBusyPeriod, Period, RecurringPeriod from vCalendar import iterwrite, parse, ParseError, to_dict, to_node from vRecurrence import get_parameters, get_rule import email.utils @@ -568,7 +568,7 @@ # Get a constrained view if start and end limits are specified. if period: - periods = period_overlaps(freebusy, period, True) + periods = freebusy.period_overlaps(period, True) else: periods = freebusy diff -r 5f9f96eb4f8f -r 2264ab469f6d imiptools/handlers/common.py --- a/imiptools/handlers/common.py Tue Feb 09 15:57:23 2016 +0100 +++ b/imiptools/handlers/common.py Wed Mar 02 21:17:11 2016 +0100 @@ -3,7 +3,7 @@ """ Common handler functionality for different entities. -Copyright (C) 2014, 2015 Paul Boddie +Copyright (C) 2014, 2015, 2016 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -22,7 +22,7 @@ from imiptools.data import get_address, get_uri, make_freebusy, to_part, \ uri_dict from imiptools.dates import format_datetime -from imiptools.period import FreeBusyPeriod, Period, replace_overlapping +from imiptools.period import FreeBusyPeriod, Period class CommonFreebusy: @@ -59,7 +59,7 @@ for sender, sender_attr in senders: stored_freebusy = self.store.get_freebusy_for_other(self.user, sender) - replace_overlapping(stored_freebusy, period, freebusy) + stored_freebusy.replace_overlapping(period, freebusy) self.store.set_freebusy_for_other(self.user, stored_freebusy, sender) def request(self): diff -r 5f9f96eb4f8f -r 2264ab469f6d imiptools/handlers/scheduling/freebusy.py --- a/imiptools/handlers/scheduling/freebusy.py Tue Feb 09 15:57:23 2016 +0100 +++ b/imiptools/handlers/scheduling/freebusy.py Wed Mar 02 21:17:11 2016 +0100 @@ -21,8 +21,6 @@ from imiptools.data import uri_values from imiptools.dates import ValidityError, to_timezone -from imiptools.period import coalesce_freebusy, invert_freebusy, \ - periods_from, remove_periods def schedule_in_freebusy(handler, args, freebusy=None): @@ -117,17 +115,16 @@ if attendee != handler.user: freebusy = handler.get_store().get_freebusy_for_other(handler.user, attendee) if freebusy: - remove_periods(freebusy, event_periods) + freebusy.remove_periods(event_periods) busy += freebusy # Obtain the combined busy periods. - busy.sort() - busy = coalesce_freebusy(busy) + busy = busy.coalesce_freebusy() # Obtain free periods. - free = invert_freebusy(busy) + free = busy.invert_freebusy() permitted_values = handler.get_permitted_values() periods = [] @@ -153,7 +150,7 @@ # Get free periods from the time of each period. - for found in periods_from(free, period): + for found in free.periods_from(period): # Skip any periods before the last period. diff -r 5f9f96eb4f8f -r 2264ab469f6d imiptools/period.py --- a/imiptools/period.py Tue Feb 09 15:57:23 2016 +0100 +++ b/imiptools/period.py Wed Mar 02 21:17:11 2016 +0100 @@ -454,280 +454,328 @@ def make_corrected(self, start, end): return self.__class__(start, end, self.tzid, self.origin, self.get_start_attr(), self.get_end_attr()) -# Time and period management. +class FreeBusyCollection: + + "An abstraction for a collection of free/busy periods." + + def __init__(self, periods=None): + + """ + Initialise the collection with the given list of 'periods', or start an + empty collection if no list is given. + """ + + self.periods = periods or [] + + # List emulation methods. -def can_schedule(freebusy, periods, uid, recurrenceid): + def __list__(self): + return self.periods + + def __iter__(self): + return iter(self.periods) + + def __len__(self): + return len(self.periods) + + def __getitem__(self, i): + return self.periods[i] + + def __iadd__(self, other): + for period in other: + self.insert_period(period) + return self + + # Operations. - """ - Return whether the 'freebusy' list can accommodate the given 'periods' - employing the specified 'uid' and 'recurrenceid'. - """ + def can_schedule(self, periods, uid, recurrenceid): + + """ + Return whether the collection can accommodate the given 'periods' + employing the specified 'uid' and 'recurrenceid'. + """ + + for conflict in self.have_conflict(periods, True): + if conflict.uid != uid or conflict.recurrenceid != recurrenceid: + return False + + return True + + def have_conflict(self, periods, get_conflicts=False): - for conflict in have_conflict(freebusy, periods, True): - if conflict.uid != uid or conflict.recurrenceid != recurrenceid: + """ + Return whether any period in the collection overlaps with the given + 'periods', returning a collection of such overlapping periods if + 'get_conflicts' is set to a true value. + """ + + conflicts = set() + for p in periods: + overlapping = self.period_overlaps(p, get_conflicts) + if overlapping: + if get_conflicts: + conflicts.update(overlapping) + else: + return True + + if get_conflicts: + return conflicts + else: return False - return True + def insert_period(self, period): -def have_conflict(freebusy, periods, get_conflicts=False): + "Insert the given 'period' into the collection." - """ - Return whether any period in 'freebusy' overlaps with the given 'periods', - returning a collection of such overlapping periods if 'get_conflicts' is - set to a true value. - """ + i = bisect_left(self.periods, period) + if i == len(self.periods): + self.periods.append(period) + elif self.periods[i] != period: + self.periods.insert(i, period) + + def remove_periods(self, periods): - conflicts = set() - for p in periods: - overlapping = period_overlaps(freebusy, p, get_conflicts) - if overlapping: - if get_conflicts: - conflicts.update(overlapping) - else: - return True + "Remove the given 'periods' from the collection." + + for period in periods: + i = bisect_left(self.periods, period) + if i < len(self.periods) and self.periods[i] == period: + del self.periods[i] - if get_conflicts: - return conflicts - else: - return False + def remove_event_periods(self, uid, recurrenceid=None): -def insert_period(freebusy, period): - - "Insert into 'freebusy' the given 'period'." + """ + Remove from the collection all periods associated with 'uid' and + 'recurrenceid' (which if omitted causes the "parent" object's periods to + be referenced). - i = bisect_left(freebusy, period) - if i == len(freebusy): - freebusy.append(period) - elif freebusy[i] != period: - freebusy.insert(i, period) - -def remove_periods(freebusy, periods): + Return the removed periods. + """ - "Remove from 'freebusy' the given 'periods'." - - for period in periods: - i = bisect_left(freebusy, period) - if i < len(freebusy) and freebusy[i] == period: - del freebusy[i] - -def remove_event_periods(freebusy, uid, recurrenceid=None): + removed = [] + i = 0 + while i < len(self.periods): + fb = self.periods[i] + if fb.uid == uid and fb.recurrenceid == recurrenceid: + removed.append(self.periods[i]) + del self.periods[i] + else: + i += 1 - """ - Remove from 'freebusy' all periods associated with 'uid' and 'recurrenceid' - (which if omitted causes the "parent" object's periods to be referenced). + return removed - Return the removed periods. - """ + def remove_additional_periods(self, uid, recurrenceids=None): - removed = [] - i = 0 - while i < len(freebusy): - fb = freebusy[i] - if fb.uid == uid and fb.recurrenceid == recurrenceid: - removed.append(freebusy[i]) - del freebusy[i] - else: - i += 1 + """ + Remove from the collection all periods associated with 'uid' having a + recurrence identifier indicating an additional or modified period. - return removed + If 'recurrenceids' is specified, remove all periods associated with + 'uid' that do not have a recurrence identifier in the given list. + + Return the removed periods. + """ -def remove_additional_periods(freebusy, uid, recurrenceids=None): - - """ - Remove from 'freebusy' all periods associated with 'uid' having a - recurrence identifier indicating an additional or modified period. + removed = [] + i = 0 + while i < len(self.periods): + fb = self.periods[i] + if fb.uid == uid and fb.recurrenceid and ( + recurrenceids is None or + recurrenceids is not None and fb.recurrenceid not in recurrenceids + ): + removed.append(self.periods[i]) + del self.periods[i] + else: + i += 1 - If 'recurrenceids' is specified, remove all periods associated with 'uid' - that do not have a recurrence identifier in the given list. + return removed - Return the removed periods. - """ + def remove_affected_period(self, uid, start): - removed = [] - i = 0 - while i < len(freebusy): - fb = freebusy[i] - if fb.uid == uid and fb.recurrenceid and ( - recurrenceids is None or - recurrenceids is not None and fb.recurrenceid not in recurrenceids - ): - removed.append(freebusy[i]) - del freebusy[i] - else: - i += 1 + """ + Remove from the collection the period associated with 'uid' that + provides an occurrence starting at the given 'start' (provided by a + recurrence identifier, converted to a datetime). A recurrence identifier + is used to provide an alternative time period whilst also acting as a + reference to the originally-defined occurrence. + + Return any removed period in a list. + """ - return removed + removed = [] + + search = Period(start, start) + found = bisect_left(self.periods, search) -def remove_affected_period(freebusy, uid, start): + while found < len(self.periods): + fb = self.periods[found] + + # Stop looking if the start no longer matches the recurrence identifier. - """ - Remove from 'freebusy' a period associated with 'uid' that provides an - occurrence starting at the given 'start' (provided by a recurrence - identifier, converted to a datetime). A recurrence identifier is used to - provide an alternative time period whilst also acting as a reference to the - originally-defined occurrence. + if fb.get_start_point() != search.get_start_point(): + break + + # If the period belongs to the parent object, remove it and return. - Return any removed period in a list. - """ - - removed = [] + if not fb.recurrenceid and uid == fb.uid: + removed.append(self.periods[found]) + del self.periods[found] + break - search = Period(start, start) - found = bisect_left(freebusy, search) + # Otherwise, keep looking for a matching period. + + found += 1 - while found < len(freebusy): - fb = freebusy[found] + return removed + + def periods_from(self, period): - # Stop looking if the start no longer matches the recurrence identifier. + "Return the entries in the collection at or after 'period'." - if fb.get_start_point() != search.get_start_point(): - break + first = bisect_left(self.periods, period) + return self.periods[first:] - # If the period belongs to the parent object, remove it and return. + def periods_until(self, period): + + "Return the entries in the collection before 'period'." - if not fb.recurrenceid and uid == fb.uid: - removed.append(freebusy[found]) - del freebusy[found] - break + last = bisect_right(self.periods, Period(period.get_end(), period.get_end(), period.get_tzid())) + return self.periods[:last] + + def get_overlapping(self, period): - # Otherwise, keep looking for a matching period. - - found += 1 - - return removed - -def periods_from(freebusy, period): + """ + Return the entries in the collection providing periods overlapping with + 'period'. + """ - "Return the entries in 'freebusy' at or after 'period'." + # Find the range of periods potentially overlapping the period in the + # free/busy collection. - first = bisect_left(freebusy, period) - return freebusy[first:] + startpoints = self.periods_until(period) -def periods_until(freebusy, period): + # Find the range of periods potentially overlapping the period in a version + # of the free/busy collection sorted according to end datetimes. - "Return the entries in 'freebusy' before 'period'." + endpoints = [(Period(fb.get_end_point(), fb.get_end_point()), fb) for fb in startpoints] + endpoints.sort() + first = bisect_left(endpoints, (Period(period.get_start_point(), period.get_start_point()),)) + endpoints = endpoints[first:] - last = bisect_right(freebusy, Period(period.get_end(), period.get_end(), period.get_tzid())) - return freebusy[:last] + overlapping = set() -def get_overlapping(freebusy, period): + for p, fb in endpoints: + if fb.overlaps(period): + overlapping.add(fb) - """ - Return the entries in 'freebusy' providing periods overlapping with - 'period'. - """ + overlapping = list(overlapping) + overlapping.sort() + return overlapping - # Find the range of periods potentially overlapping the period in the - # free/busy collection. + def period_overlaps(self, period, get_periods=False): - startpoints = periods_until(freebusy, period) - - # Find the range of periods potentially overlapping the period in a version - # of the free/busy collection sorted according to end datetimes. + """ + Return whether any period in the collection overlaps with the given + 'period', returning a collection of overlapping periods if 'get_periods' + is set to a true value. + """ - endpoints = [(Period(fb.get_end_point(), fb.get_end_point()), fb) for fb in startpoints] - endpoints.sort() - first = bisect_left(endpoints, (Period(period.get_start_point(), period.get_start_point()),)) - endpoints = endpoints[first:] - - overlapping = set() + overlapping = self.get_overlapping(period) - for p, fb in endpoints: - if fb.overlaps(period): - overlapping.add(fb) + if get_periods: + return overlapping + else: + return len(overlapping) != 0 - overlapping = list(overlapping) - overlapping.sort() - return overlapping + def remove_overlapping(self, period): -def period_overlaps(freebusy, period, get_periods=False): + "Remove all periods overlapping with 'period' from the collection." + + overlapping = self.get_overlapping(period) - """ - Return whether any period in 'freebusy' overlaps with the given 'period', - returning a collection of overlapping periods if 'get_periods' is set to a - true value. - """ + if overlapping: + for fb in overlapping: + self.periods.remove(fb) - overlapping = get_overlapping(freebusy, period) + def replace_overlapping(self, period, replacements): - if get_periods: - return overlapping - else: - return len(overlapping) != 0 + """ + Replace existing periods in the collection within the given 'period', + using the given 'replacements'. + """ -def remove_overlapping(freebusy, period): + self.remove_overlapping(period) + for replacement in replacements: + self.insert_period(replacement) - "Remove from 'freebusy' all periods overlapping with 'period'." + def coalesce_freebusy(self): - overlapping = get_overlapping(freebusy, period) + "Coalesce the periods in the collection, returning a new collection." - if overlapping: - for fb in overlapping: - freebusy.remove(fb) + if not self.periods: + return FreeBusyCollection(self.periods) -def replace_overlapping(freebusy, period, replacements): + fb = [] + start = self.periods[0].get_start_point() + end = self.periods[0].get_end_point() - """ - Replace existing periods in 'freebusy' within the given 'period', using the - given 'replacements'. - """ - - remove_overlapping(freebusy, period) - for replacement in replacements: - insert_period(freebusy, replacement) - -def coalesce_freebusy(freebusy): + for period in self.periods[1:]: + if period.get_start_point() > end: + fb.append(FreeBusyPeriod(start, end)) + start = period.get_start_point() + end = period.get_end_point() + else: + end = max(end, period.get_end_point()) - "Coalesce the periods in 'freebusy'." + fb.append(FreeBusyPeriod(start, end)) + return FreeBusyCollection(fb) - if not freebusy: - return freebusy + def invert_freebusy(self): + + "Return the free periods from the collection as a new collection." - fb = [] - start = freebusy[0].get_start_point() - end = freebusy[0].get_end_point() + if not self.periods: + return FreeBusyCollection([FreeBusyPeriod(None, None)]) + + # Coalesce periods that overlap or are adjacent. - for period in freebusy[1:]: - if period.get_start_point() > end: - fb.append(FreeBusyPeriod(start, end)) - start = period.get_start_point() - end = period.get_end_point() - else: - end = max(end, period.get_end_point()) + fb = self.coalesce_freebusy() + free = [] + + # Add a start-of-time period if appropriate. - fb.append(FreeBusyPeriod(start, end)) - return fb + first = fb[0].get_start_point() + if first: + free.append(FreeBusyPeriod(None, first)) -def invert_freebusy(freebusy): - - "Return the free periods from 'freebusy'." + start = fb[0].get_end_point() - if not freebusy: - return [FreeBusyPeriod(None, None)] - - # Coalesce periods that overlap or are adjacent. + for period in fb[1:]: + free.append(FreeBusyPeriod(start, period.get_start_point())) + start = period.get_end_point() - fb = coalesce_freebusy(freebusy) - free = [] + # Add an end-of-time period if appropriate. - # Add a start-of-time period if appropriate. + if start: + free.append(FreeBusyPeriod(start, None)) - first = fb[0].get_start_point() - if first: - free.append(FreeBusyPeriod(None, first)) + return FreeBusyCollection(free) + + def update_freebusy(self, periods, transp, uid, recurrenceid, summary, organiser, expires=None): - start = fb[0].get_end_point() + """ + Update the free/busy details with the given 'periods', 'transp' setting, + 'uid' plus 'recurrenceid' and 'summary' and 'organiser' details. - for period in fb[1:]: - free.append(FreeBusyPeriod(start, period.get_start_point())) - start = period.get_end_point() + An optional 'expires' datetime string indicates the expiry time of any + free/busy offer. + """ - # Add an end-of-time period if appropriate. + self.remove_event_periods(uid, recurrenceid) - if start: - free.append(FreeBusyPeriod(start, None)) - - return free + for p in periods: + self.insert_period(FreeBusyPeriod(p.get_start_point(), p.get_end_point(), uid, transp, recurrenceid, summary, organiser, expires)) # Period layout. @@ -1014,19 +1062,4 @@ return spans -def update_freebusy(freebusy, periods, transp, uid, recurrenceid, summary, organiser, expires=None): - - """ - Update the free/busy details with the given 'periods', 'transp' setting, - 'uid' plus 'recurrenceid' and 'summary' and 'organiser' details. - - An optional 'expires' datetime string indicates the expiry time of any - free/busy offer. - """ - - remove_event_periods(freebusy, uid, recurrenceid) - - for p in periods: - insert_period(freebusy, FreeBusyPeriod(p.get_start_point(), p.get_end_point(), uid, transp, recurrenceid, summary, organiser, expires)) - # vim: tabstop=4 expandtab shiftwidth=4 diff -r 5f9f96eb4f8f -r 2264ab469f6d imipweb/calendar.py --- a/imipweb/calendar.py Tue Feb 09 15:57:23 2016 +0100 +++ b/imipweb/calendar.py Wed Mar 02 21:17:11 2016 +0100 @@ -3,7 +3,7 @@ """ A Web interface to an event calendar. -Copyright (C) 2014, 2015 Paul Boddie +Copyright (C) 2014, 2015, 2016 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -26,7 +26,6 @@ get_start_of_next_day, get_timestamp, ends_on_same_day, \ to_date, to_timezone from imiptools.period import add_day_start_points, add_empty_days, add_slots, \ - get_overlapping, \ get_scale, get_slots, get_spans, partition_by_day, \ remove_end_slot, Period, Point from imipweb.resource import FormUtilities, ResourceClient @@ -323,8 +322,8 @@ view_end = view_period.get_end() duration = view_period.get_duration() - preceding_events = view_start and get_overlapping(freebusy, Period(None, view_start, self.get_tzid())) or [] - following_events = view_end and get_overlapping(freebusy, Period(view_end, None, self.get_tzid())) or [] + preceding_events = view_start and freebusy.get_overlapping(Period(None, view_start, self.get_tzid())) or [] + following_events = view_end and freebusy.get_overlapping(Period(view_end, None, self.get_tzid())) or [] last_preceding = preceding_events and to_date(preceding_events[-1].get_end()) + timedelta(1) or None first_following = following_events and to_date(following_events[0].get_start()) or None diff -r 5f9f96eb4f8f -r 2264ab469f6d imipweb/event.py --- a/imipweb/event.py Tue Feb 09 15:57:23 2016 +0100 +++ b/imipweb/event.py Wed Mar 02 21:17:11 2016 +0100 @@ -3,7 +3,7 @@ """ A Web interface to a calendar event. -Copyright (C) 2014, 2015 Paul Boddie +Copyright (C) 2014, 2015, 2016 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -23,7 +23,6 @@ uri_parts, uri_values from imiptools.dates import format_datetime, to_timezone from imiptools.mail import Messenger -from imiptools.period import have_conflict from imipweb.data import EventPeriod, event_period_from_period, FormPeriod, PeriodError from imipweb.resource import DateTimeFormUtilities, FormUtilities, ResourceClientForObject @@ -713,7 +712,7 @@ partstat = participant_attr and participant_attr.get("PARTSTAT") recurrences = self.obj.get_recurrence_start_points(recurrenceids, tzid) - for p in have_conflict(freebusy, periods, True): + for p in freebusy.have_conflict(periods, True): if not self.recurrenceid and p.is_replaced(recurrences): continue diff -r 5f9f96eb4f8f -r 2264ab469f6d tools/make_freebusy.py --- a/tools/make_freebusy.py Tue Feb 09 15:57:23 2016 +0100 +++ b/tools/make_freebusy.py Wed Mar 02 21:17:11 2016 +0100 @@ -37,7 +37,7 @@ from imiptools.client import Client from imiptools.data import get_window_end, Object from imiptools.dates import get_default_timezone, to_utc_datetime -from imiptools.period import insert_period +from imiptools.period import FreeBusyCollection from imip_store import FileStore, FilePublisher, FileJournal def make_freebusy(client, participant, store_and_publish, include_needs_action, @@ -86,7 +86,7 @@ if not all_events: all_events = store.get_all_events(user) - fb = [] + fb = FreeBusyCollection() # With providers of additional periods, append to the existing collection. @@ -115,7 +115,7 @@ if obj.get_participation(partstat, include_needs_action): for p in obj.get_active_periods(recurrenceids, tzid, window_end): fbp = obj.get_freebusy_period(p, partstat == "ORG") - insert_period(fb, fbp) + fb.insert_period(fbp) # Store and publish the free/busy collection.