# HG changeset patch # User Paul Boddie # Date 1423670852 -3600 # Node ID 980a67718ff7ed224a22b0cee0932f2009fa5e4d # Parent b0faee5590f9c4f5bdce058d47cc7cfdd197ffe2 Added missing support for constraining free/busy requests to certain periods. diff -r b0faee5590f9 -r 980a67718ff7 imiptools/data.py --- a/imiptools/data.py Wed Feb 11 17:06:21 2015 +0100 +++ b/imiptools/data.py Wed Feb 11 17:07:32 2015 +0100 @@ -23,6 +23,7 @@ from email.mime.text import MIMEText from imiptools.dates import format_datetime, get_datetime, get_freebusy_period, \ to_timezone, to_utc_datetime +from imiptools.period import period_overlaps from pytz import timezone from vCalendar import iterwrite, parse, ParseError, to_dict, to_node from vRecurrence import get_parameters, get_rule @@ -106,13 +107,17 @@ nodes ) -def make_freebusy(freebusy, uid, organiser, organiser_attr=None, attendee=None, attendee_attr=None): +def make_freebusy(freebusy, uid, organiser, organiser_attr=None, attendee=None, + attendee_attr=None, dtstart=None, dtend=None): """ Return a calendar node defining the free/busy details described in the given 'freebusy' list, employing the given 'uid', for the given 'organiser' and optional 'organiser_attr', with the optional 'attendee' providing recipient details together with the optional 'attendee_attr'. + + The result will be constrained to the 'dtstart' and 'dtend' period if these + parameters are given. """ record = [] @@ -126,7 +131,17 @@ rwrite(("UID", {}, uid)) if freebusy: - for start, end, uid, transp in freebusy: + + # Get a constrained view if start and end limits are specified. + + periods = dtstart and dtend and period_overlaps(freebusy, (dtstart, dtend), True) or freebusy + + # Write the limits of the resource. + + rwrite(("DTSTART", {"VALUE" : "DATE-TIME"}, periods[0][0])) + rwrite(("DTEND", {"VALUE" : "DATE-TIME"}, periods[-1][1])) + + for start, end, uid, transp in periods: if transp == "OPAQUE": rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end]))) diff -r b0faee5590f9 -r 980a67718ff7 imiptools/handlers/common.py --- a/imiptools/handlers/common.py Wed Feb 11 17:06:21 2015 +0100 +++ b/imiptools/handlers/common.py Wed Feb 11 17:07:32 2015 +0100 @@ -20,6 +20,7 @@ """ from imiptools.data import get_address, get_uri, make_freebusy, to_part +from imiptools.dates import format_datetime class CommonFreebusy: @@ -53,7 +54,10 @@ if self.messenger: attendee_attr["SENT-BY"] = get_uri(self.messenger.sender) - rwrite(make_freebusy(freebusy, self.uid, organiser, organiser_attr, attendee, attendee_attr)) + dtstart = format_datetime(self.obj.get_utc_datetime("DTSTART")) + dtend = format_datetime(self.obj.get_utc_datetime("DTEND")) + + rwrite(make_freebusy(freebusy, self.uid, organiser, organiser_attr, attendee, attendee_attr, dtstart, dtend)) # Return the reply. diff -r b0faee5590f9 -r 980a67718ff7 imiptools/handlers/person.py --- a/imiptools/handlers/person.py Wed Feb 11 17:06:21 2015 +0100 +++ b/imiptools/handlers/person.py Wed Feb 11 17:07:32 2015 +0100 @@ -21,7 +21,9 @@ from imiptools.content import Handler from imiptools.data import get_uri +from imiptools.dates import format_datetime from imiptools.handlers.common import CommonFreebusy +from imiptools.period import replace_overlapping from imiptools.profile import Preferences class PersonHandler(Handler): @@ -108,8 +110,14 @@ except ValueError: pass + dtstart = format_datetime(self.obj.get_utc_datetime("DTSTART")) + dtend = format_datetime(self.obj.get_utc_datetime("DTEND")) + user = get_uri(self.recipient) + for sender, sender_attr in senders: - self.store.set_freebusy_for_other(get_uri(self.recipient), freebusy, sender) + stored_freebusy = self.store.get_freebusy_for_other(user, other) + replace_overlapping(stored_freebusy, (dtstart, dtend), freebusy) + self.store.set_freebusy_for_other(user, other, stored_freebusy, sender) class Event(PersonHandler): diff -r b0faee5590f9 -r 980a67718ff7 imiptools/period.py --- a/imiptools/period.py Wed Feb 11 17:06:21 2015 +0100 +++ b/imiptools/period.py Wed Feb 11 17:07:32 2015 +0100 @@ -73,6 +73,32 @@ else: i += 1 +def get_overlapping(freebusy, period): + + """ + Return the indexes in 'freebusy' providing periods overlapping with + 'period'. + """ + + dtstart, dtend = period[:2] + found = bisect_left(freebusy, (dtstart, dtend)) + + # Find earlier overlapping periods. + + start = found + + while start > 0 and freebusy[start - 1][1] > dtstart: + start -= 1 + + # Find later overlapping periods. + + end = found + + while end < len(freebusy) and (dtend is None or freebusy[end][0] < dtend): + end += 1 + + return start, end + def period_overlaps(freebusy, period, get_periods=False): """ @@ -81,37 +107,32 @@ true value. """ - dtstart, dtend = period[:2] - found = bisect_left(freebusy, (dtstart, dtend, None, None)) - - overlapping = [] - - # Find earlier overlapping periods. - - i = found - - while i > 0 and freebusy[i - 1][1] > dtstart: - if get_periods: - overlapping.insert(0, freebusy[i - 1]) - else: - return True - i -= 1 - - # Find later overlapping periods. - - i = found - - while i < len(freebusy) and (dtend is None or freebusy[i][0] < dtend): - if get_periods: - overlapping.append(freebusy[i]) - else: - return True - i += 1 + start, end = get_overlapping(freebusy, period) if get_periods: - return overlapping + return freebusy[start:end] else: - return False + return start != end + +def remove_overlapping(freebusy, period): + + "Remove from 'freebusy' all periods overlapping with 'period'." + + start, end = get_overlapping(freebusy, period) + + if start != end: + del freebusy[start:end] + +def replace_overlapping(freebusy, period, replacements): + + """ + 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) # Period layout mostly with datetime objects.