# HG changeset patch # User Paul Boddie # Date 1427064198 -3600 # Node ID 321f8e56f3d3f2f88597722a9a6bf827956f13d7 # Parent 188404c542da8efef59f35bea5b93c1fc31e2d11 Expose period origins upon request for events. diff -r 188404c542da -r 321f8e56f3d3 imiptools/data.py --- a/imiptools/data.py Sun Mar 22 22:08:20 2015 +0100 +++ b/imiptools/data.py Sun Mar 22 23:43:18 2015 +0100 @@ -19,6 +19,7 @@ this program. If not, see . """ +from bisect import bisect_left from datetime import datetime, timedelta from email.mime.text import MIMEText from imiptools.dates import format_datetime, get_datetime, get_duration, \ @@ -100,14 +101,14 @@ # Computed results. def has_recurrence(self, tzid, recurrence): - recurrences = [start for start, end in get_periods(self, tzid, recurrence, True)] + recurrences = [start for start, end in get_periods(self, tzid, recurrence, inclusive=True)] return recurrence in recurrences - def get_periods(self, tzid, end): - return get_periods(self, tzid, end) + def get_periods(self, tzid, end, origin=False): + return get_periods(self, tzid, end, origin=origin) - def get_periods_for_freebusy(self, tzid, end): - periods = self.get_periods(tzid, end) + def get_periods_for_freebusy(self, tzid, end, origin=False): + periods = self.get_periods(tzid, end, origin) return get_periods_for_freebusy(self, periods, tzid) def get_tzid(self): @@ -367,12 +368,16 @@ def get_tzid(dtstart_attr, dtend_attr): return dtstart_attr.get("TZID") or dtend_attr.get("TZID") -def get_periods(obj, tzid, window_end, inclusive=False): +def get_periods(obj, tzid, window_end, inclusive=False, origin=False): """ Return periods for the given object 'obj', confining materialised periods to before the given 'window_end' datetime. If 'inclusive' is set to a true value, any period occurring at the 'window_end' will be included. + + If 'origin' is set to a true value, the property type providing each period + will be included in the results, with each result taking the form + (start, end, property type). """ rrule = obj.get_value("RRULE") @@ -394,7 +399,8 @@ tzid = get_tzid(dtstart_attr, dtend_attr) or tzid if not rrule: - periods = [(dtstart, dtend)] + origin_value = origin and ("DTSTART",) or () + periods = [(dtstart, dtend) + origin_value] else: # Recurrence rules create multiple instances to be checked. # Conflicts may only be assessed within a period defined by policy @@ -403,12 +409,13 @@ selector = get_rule(dtstart, rrule) parameters = get_parameters(rrule) + origin_value = origin and ("RRULE",) or () periods = [] for start in selector.materialise(dtstart, window_end, parameters.get("COUNT"), parameters.get("BYSETPOS"), inclusive): start = to_timezone(datetime(*start), tzid) end = start + duration - periods.append((start, end)) + periods.append((start, end) + origin_value) # Add recurrence dates. @@ -416,11 +423,17 @@ rdates = obj.get_date_values("RDATE", tzid) if rdates: + origin_value = origin and ("RDATE",) or () for rdate in rdates: if isinstance(rdate, tuple): - periods.add(rdate) + periods.add(rdate + origin_value) else: - periods.add((rdate, rdate + duration)) + periods.add((rdate, rdate + duration) + origin_value) + + # Return a sorted list of the periods. + + periods = list(periods) + periods.sort() # Exclude exception dates. @@ -432,13 +445,10 @@ period = exdate else: period = (exdate, exdate + duration) - if period in periods: - periods.remove(period) + i = bisect_left(periods, period) + while i < len(periods) and periods[i][:2] == period: + del periods[i] - # Return a sorted list of the periods. - - periods = list(periods) - periods.sort() return periods def get_periods_for_freebusy(obj, periods, tzid): @@ -461,10 +471,11 @@ l = [] - for start, end in periods: + for t in periods: + start, end = t[:2] start, end = get_freebusy_period(start, end, tzid) start, end = [to_timezone(x, "UTC") for x in start, end] - l.append((format_datetime(start), format_datetime(end))) + l.append((format_datetime(start), format_datetime(end)) + t[2:]) return l