1.1 --- a/imiptools/data.py Thu Feb 26 19:38:14 2015 +0100
1.2 +++ b/imiptools/data.py Thu Feb 26 19:41:00 2015 +0100
1.3 @@ -59,6 +59,13 @@
1.4 def get_utc_datetime(self, name):
1.5 return get_utc_datetime(self.details, name)
1.6
1.7 + def get_item_datetimes(self, name):
1.8 + items = get_item_datetime_items(self.details, name)
1.9 + return items and [dt for dt, attr in items]
1.10 +
1.11 + def get_item_datetime_items(self, name):
1.12 + return get_item_datetime_items(self.details, name)
1.13 +
1.14 def get_datetime(self, name):
1.15 dt, attr = get_datetime_item(self.details, name)
1.16 return dt
1.17 @@ -248,6 +255,23 @@
1.18 def get_value(d, name):
1.19 return get_values(d, name, False)
1.20
1.21 +def get_item_datetime_items(d, name):
1.22 +
1.23 + """
1.24 + Return datetime items from 'd' having the given 'name', where a single item
1.25 + yields potentially many datetime values, each employing the attributes given
1.26 + for the principal item.
1.27 + """
1.28 +
1.29 + item = get_item(d, name)
1.30 + if item:
1.31 + values, attr = item
1.32 + if not isinstance(values, list):
1.33 + values = [values]
1.34 + return [(get_datetime(value, attr), attr) for value in values]
1.35 + else:
1.36 + return None
1.37 +
1.38 def get_utc_datetime(d, name):
1.39 t = get_datetime_item(d, name)
1.40 if not t:
1.41 @@ -322,40 +346,60 @@
1.42 to the given 'window_size' in days starting from the present moment.
1.43 """
1.44
1.45 - # NOTE: Need also RDATE and EXDATE support.
1.46 -
1.47 rrule = obj.get_value("RRULE")
1.48
1.49 - if not rrule:
1.50 - return [(obj.get_datetime("DTSTART"), obj.get_datetime("DTEND"))]
1.51 -
1.52 # Use localised datetimes.
1.53
1.54 dtstart, start_attr = obj.get_datetime_item("DTSTART")
1.55 dtend, end_attr = obj.get_datetime_item("DTEND")
1.56 -
1.57 tzid = start_attr.get("TZID") or end_attr.get("TZID") or tzid
1.58
1.59 # NOTE: Need also DURATION support.
1.60
1.61 duration = dtend - dtstart
1.62
1.63 - # Recurrence rules create multiple instances to be checked.
1.64 - # Conflicts may only be assessed within a period defined by policy
1.65 - # for the agent, with instances outside that period being considered
1.66 - # unchecked.
1.67 + if not rrule:
1.68 + periods = [(dtstart, dtend)]
1.69 + else:
1.70 + # Recurrence rules create multiple instances to be checked.
1.71 + # Conflicts may only be assessed within a period defined by policy
1.72 + # for the agent, with instances outside that period being considered
1.73 + # unchecked.
1.74 +
1.75 + window_end = to_timezone(datetime.now(), tzid) + timedelta(window_size)
1.76
1.77 - window_end = to_timezone(datetime.now(), tzid) + timedelta(window_size)
1.78 + selector = get_rule(dtstart, rrule)
1.79 + parameters = get_parameters(rrule)
1.80 + periods = []
1.81 +
1.82 + for start in selector.materialise(dtstart, window_end, parameters.get("COUNT"), parameters.get("BYSETPOS")):
1.83 + start = to_timezone(datetime(*start), tzid)
1.84 + end = start + duration
1.85 + periods.append((start, end))
1.86 +
1.87 + # Add recurrence dates.
1.88
1.89 - selector = get_rule(dtstart, rrule)
1.90 - parameters = get_parameters(rrule)
1.91 - periods = []
1.92 + periods = set(periods)
1.93 + rdates = obj.get_item_datetimes("RDATE")
1.94 +
1.95 + if rdates:
1.96 + for rdate in rdates:
1.97 + periods.add((rdate, rdate + duration))
1.98 +
1.99 + # Exclude exception dates.
1.100 +
1.101 + exdates = obj.get_item_datetimes("EXDATE")
1.102
1.103 - for start in selector.materialise(dtstart, window_end, parameters.get("COUNT"), parameters.get("BYSETPOS")):
1.104 - start = to_timezone(datetime(*start), tzid)
1.105 - end = start + duration
1.106 - periods.append((start, end))
1.107 + if exdates:
1.108 + for exdate in exdates:
1.109 + period = (exdate, exdate + duration)
1.110 + if period in periods:
1.111 + periods.remove(period)
1.112
1.113 + # Return a sorted list of the periods.
1.114 +
1.115 + periods = list(periods)
1.116 + periods.sort()
1.117 return periods
1.118
1.119 def get_periods_for_freebusy(obj, periods, tzid):