1.1 --- a/imiptools/data.py Sat Aug 01 22:24:43 2015 +0200
1.2 +++ b/imiptools/data.py Sat Aug 01 23:06:48 2015 +0200
1.3 @@ -124,6 +124,9 @@
1.4 def get_date_value_items(self, name, tzid=None):
1.5 return get_date_value_items(self.details, name, tzid)
1.6
1.7 + def get_period_values(self, name, tzid=None, period_type=None):
1.8 + return get_period_values(self.details, name, tzid, period_type)
1.9 +
1.10 def get_datetime(self, name):
1.11 t = get_datetime_item(self.details, name)
1.12 if not t: return None
1.13 @@ -171,16 +174,50 @@
1.14
1.15 # Computed results.
1.16
1.17 - def get_periods(self, tzid, end):
1.18 + def get_periods(self, tzid, end=None):
1.19
1.20 """
1.21 Return periods defined by this object, employing the given 'tzid' where
1.22 no time zone information is defined, and limiting the collection to a
1.23 window of time with the given 'end'.
1.24 +
1.25 + If 'end' is omitted, only explicit recurrences and recurrences from
1.26 + explicitly-terminated rules will be returned.
1.27 """
1.28
1.29 return get_periods(self, tzid, end)
1.30
1.31 + def get_active_periods(self, recurrenceids, tzid, end=None):
1.32 +
1.33 + """
1.34 + Return all periods specified by this object that are not replaced by
1.35 + those defined by 'recurrenceids', using 'tzid' as a fallback time zone
1.36 + to convert floating dates and datetimes, and using 'end' to indicate the
1.37 + end of the time window within which periods are considered.
1.38 + """
1.39 +
1.40 + # Specific recurrences yield all specified periods.
1.41 +
1.42 + periods = self.get_periods(tzid, end)
1.43 +
1.44 + if self.get_recurrenceid():
1.45 + return periods
1.46 +
1.47 + # Parent objects need to have their periods tested against redefined
1.48 + # recurrences.
1.49 +
1.50 + active = []
1.51 +
1.52 + for p in periods:
1.53 +
1.54 + # Subtract any recurrences from the free/busy details of a
1.55 + # parent object.
1.56 +
1.57 + if not is_replaced(p, recurrenceids, tzid):
1.58 + active.append(p)
1.59 +
1.60 + return active
1.61 +
1.62 def get_tzid(self):
1.63
1.64 """
1.65 @@ -191,7 +228,10 @@
1.66 if not self.has_key("DTSTART"):
1.67 return None
1.68 dtstart, dtstart_attr = self.get_datetime_item("DTSTART")
1.69 - dtend, dtend_attr = self.get_datetime_item("DTEND")
1.70 + if self.has_key("DTEND"):
1.71 + dtend, dtend_attr = self.get_datetime_item("DTEND")
1.72 + else:
1.73 + dtend_attr = None
1.74 return get_tzid(dtstart_attr, dtend_attr)
1.75
1.76 def is_shared(self):
1.77 @@ -441,6 +481,22 @@
1.78 else:
1.79 return None
1.80
1.81 +def get_period_values(d, name, tzid=None, period_type=None):
1.82 +
1.83 + """
1.84 + Return period values from 'd' for the given property 'name', using 'tzid'
1.85 + where specified to indicate the time zone, and 'period_type' to indicate a
1.86 + particular period type to be used when creating period instances.
1.87 + """
1.88 +
1.89 + values = []
1.90 + for value, attr in get_items(d, name) or []:
1.91 + if not attr.has_key("TZID") and tzid:
1.92 + attr["TZID"] = tzid
1.93 + start, end = get_period(value, attr)
1.94 + values.append((period_type or Period)(start, end, tzid=tzid))
1.95 + return values
1.96 +
1.97 def get_utc_datetime(d, name, date_tzid=None):
1.98
1.99 """
1.100 @@ -524,15 +580,18 @@
1.101
1.102 return is_same_sequence and partstat_set or not is_old_sequence
1.103
1.104 -def get_periods(obj, tzid, window_end, inclusive=False):
1.105 +def get_periods(obj, tzid, end=None, inclusive=False):
1.106
1.107 """
1.108 Return periods for the given object 'obj', employing the given 'tzid' where
1.109 no time zone information is available (for whole day events, for example),
1.110 - confining materialised periods to before the given 'window_end' datetime.
1.111 + confining materialised periods to before the given 'end' datetime.
1.112
1.113 - If 'inclusive' is set to a true value, any period occurring at the
1.114 - 'window_end' will be included.
1.115 + If 'end' is omitted, only explicit recurrences and recurrences from
1.116 + explicitly-terminated rules will be returned.
1.117 +
1.118 + If 'inclusive' is set to a true value, any period occurring at the 'end'
1.119 + will be included.
1.120 """
1.121
1.122 rrule = obj.get_value("RRULE")
1.123 @@ -558,7 +617,9 @@
1.124
1.125 if not rrule:
1.126 periods = [RecurringPeriod(dtstart, dtend, tzid, "DTSTART", dtstart_attr, dtend_attr)]
1.127 - else:
1.128 +
1.129 + elif end or parameters.has_key("UNTIL") or parameters.has_key("COUNT"):
1.130 +
1.131 # Recurrence rules create multiple instances to be checked.
1.132 # Conflicts may only be assessed within a period defined by policy
1.133 # for the agent, with instances outside that period being considered
1.134 @@ -570,14 +631,14 @@
1.135
1.136 until = parameters.get("UNTIL")
1.137 if until:
1.138 - window_end = min(to_timezone(get_datetime(until, dtstart_attr), tzid), window_end)
1.139 + end = min(to_timezone(get_datetime(until, dtstart_attr), tzid), end)
1.140 inclusive = True
1.141
1.142 - for start in selector.materialise(dtstart, window_end, parameters.get("COUNT"), parameters.get("BYSETPOS"), inclusive):
1.143 - create = len(start) == 3 and date or datetime
1.144 - start = to_timezone(create(*start), tzid)
1.145 - end = start + duration
1.146 - periods.append(RecurringPeriod(start, end, tzid, "RRULE"))
1.147 + for recurrence_start in selector.materialise(dtstart, end, parameters.get("COUNT"), parameters.get("BYSETPOS"), inclusive):
1.148 + create = len(recurrence_start) == 3 and date or datetime
1.149 + recurrence_start = to_timezone(create(*recurrence_start), tzid)
1.150 + recurrence_end = recurrence_start + duration
1.151 + periods.append(RecurringPeriod(recurrence_start, recurrence_end, tzid, "RRULE"))
1.152
1.153 # Add recurrence dates.
1.154
4.1 --- a/imiptools/period.py Sat Aug 01 22:24:43 2015 +0200
4.2 +++ b/imiptools/period.py Sat Aug 01 23:06:48 2015 +0200
4.3 @@ -72,7 +72,7 @@
4.4 return self.get_start(), self.get_end()
4.5
4.6 def __repr__(self):
4.7 - return "Period(%r)" % (self.as_tuple(),)
4.8 + return "Period%r" % (self.as_tuple(),)
4.9
4.10 # Datetime metadata methods.
4.11
4.12 @@ -131,8 +131,8 @@
4.13
4.14 null = lambda x: (strings_only and [""] or [x])[0]
4.15 return (
4.16 - format_datetime(self.get_start_point()),
4.17 - format_datetime(self.get_end_point()),
4.18 + strings_only and format_datetime(self.get_start_point()) or self.start,
4.19 + strings_only and format_datetime(self.get_end_point()) or self.end,
4.20 self.uid or null(self.uid),
4.21 self.transp or strings_only and "OPAQUE" or None,
4.22 self.recurrenceid or null(self.recurrenceid),
4.23 @@ -157,7 +157,7 @@
4.24 return self.uid, self.recurrenceid, self.get_start()
4.25
4.26 def __repr__(self):
4.27 - return "FreeBusyPeriod(%r)" % (self.as_tuple(),)
4.28 + return "FreeBusyPeriod%r" % (self.as_tuple(),)
4.29
4.30 class RecurringPeriod(Period):
4.31
4.32 @@ -181,7 +181,7 @@
4.33 return self.start, self.end, self.tzid, self.origin, self.start_attr, self.end_attr
4.34
4.35 def __repr__(self):
4.36 - return "RecurringPeriod(%r)" % (self.as_tuple(),)
4.37 + return "RecurringPeriod%r" % (self.as_tuple(),)
4.38
4.39 # Period and event recurrence logic.
4.40
5.1 --- a/imipweb/resource.py Sat Aug 01 22:24:43 2015 +0200
5.2 +++ b/imipweb/resource.py Sat Aug 01 23:06:48 2015 +0200
5.3 @@ -128,32 +128,34 @@
5.4 return self.requests
5.5
5.6 def _get_request_summary(self):
5.7 +
5.8 + "Return a list of periods comprising the request summary."
5.9 +
5.10 summary = []
5.11 +
5.12 for uid, recurrenceid in self._get_requests():
5.13 obj = self.get_stored_object(uid, recurrenceid)
5.14 if obj:
5.15 - periods = obj.get_periods(self.get_tzid(), self.get_window_end())
5.16 recurrenceids = self._get_recurrences(uid)
5.17
5.18 - # Convert the periods to more substantial free/busy items.
5.19 + # Obtain only active periods, not those replaced by redefined
5.20 + # recurrences.
5.21
5.22 - for p in periods:
5.23 + for p in obj.get_active_periods(recurrenceids, self.get_tzid(), self.get_window_end()):
5.24
5.25 - # Subtract any recurrences from the free/busy details of a
5.26 - # parent object.
5.27 + # Convert the periods to more substantial free/busy items.
5.28
5.29 - if recurrenceid or not self.is_replaced(p, recurrenceids):
5.30 - summary.append(
5.31 - FreeBusyPeriod(
5.32 - p.get_start(),
5.33 - p.get_end(),
5.34 - uid,
5.35 - obj.get_value("TRANSP"),
5.36 - recurrenceid,
5.37 - obj.get_value("SUMMARY"),
5.38 - obj.get_value("ORGANIZER"),
5.39 - p.get_tzid()
5.40 - ))
5.41 + summary.append(
5.42 + FreeBusyPeriod(
5.43 + p.get_start(),
5.44 + p.get_end(),
5.45 + uid,
5.46 + obj.get_value("TRANSP"),
5.47 + recurrenceid,
5.48 + obj.get_value("SUMMARY"),
5.49 + obj.get_value("ORGANIZER"),
5.50 + p.get_tzid()
5.51 + ))
5.52 return summary
5.53
5.54 # Period and recurrence testing.