1.1 --- a/imiptools/data.py Sat Mar 28 00:52:14 2015 +0100
1.2 +++ b/imiptools/data.py Sat Mar 28 00:55:12 2015 +0100
1.3 @@ -25,7 +25,7 @@
1.4 from imiptools.dates import format_datetime, get_datetime, get_duration, \
1.5 get_freebusy_period, get_period, to_datetime, \
1.6 to_timezone, to_utc_datetime
1.7 -from imiptools.period import period_overlaps
1.8 +from imiptools.period import FreeBusyPeriod, Period, period_overlaps
1.9 from pytz import timezone
1.10 from vCalendar import iterwrite, parse, ParseError, to_dict, to_node
1.11 from vRecurrence import get_parameters, get_rule
1.12 @@ -101,14 +101,14 @@
1.13 # Computed results.
1.14
1.15 def has_recurrence(self, tzid, recurrence):
1.16 - recurrences = [start for start, end in get_periods(self, tzid, recurrence, inclusive=True)]
1.17 + recurrences = [p.start for p in get_periods(self, tzid, recurrence, inclusive=True)]
1.18 return recurrence in recurrences
1.19
1.20 - def get_periods(self, tzid, end, origin=False):
1.21 - return get_periods(self, tzid, end, origin=origin)
1.22 + def get_periods(self, tzid, end):
1.23 + return get_periods(self, tzid, end)
1.24
1.25 - def get_periods_for_freebusy(self, tzid, end, origin=False):
1.26 - periods = self.get_periods(tzid, end, origin)
1.27 + def get_periods_for_freebusy(self, tzid, end):
1.28 + periods = self.get_periods(tzid, end)
1.29 return get_periods_for_freebusy(self, periods, tzid)
1.30
1.31 def get_tzid(self):
1.32 @@ -158,16 +158,18 @@
1.33
1.34 # Get a constrained view if start and end limits are specified.
1.35
1.36 - periods = dtstart and dtend and period_overlaps(freebusy, (dtstart, dtend), True) or freebusy
1.37 + periods = dtstart and dtend and \
1.38 + period_overlaps(freebusy, Period(dtstart, dtend), True) or \
1.39 + freebusy
1.40
1.41 # Write the limits of the resource.
1.42
1.43 rwrite(("DTSTART", {"VALUE" : "DATE-TIME"}, periods[0][0]))
1.44 rwrite(("DTEND", {"VALUE" : "DATE-TIME"}, periods[-1][1]))
1.45
1.46 - for start, end, uid, transp, recurrenceid, summary, organiser in periods:
1.47 - if transp == "OPAQUE":
1.48 - rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end])))
1.49 + for p in periods:
1.50 + if p.transp == "OPAQUE":
1.51 + rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([p.start, p.end])))
1.52
1.53 return ("VFREEBUSY", {}, record)
1.54
1.55 @@ -368,16 +370,23 @@
1.56 def get_tzid(dtstart_attr, dtend_attr):
1.57 return dtstart_attr.get("TZID") or dtend_attr.get("TZID")
1.58
1.59 -def get_periods(obj, tzid, window_end, inclusive=False, origin=False):
1.60 +class RecurringPeriod(Period):
1.61 +
1.62 + "A period with origin information from the object."
1.63 +
1.64 + def __init__(self, start, end, origin):
1.65 + Period.__init__(self, start, end)
1.66 + self.origin = origin
1.67 +
1.68 + def __repr__(self):
1.69 + return "RecurringPeriod(%r, %r, %r)" % (self.start, self.end, self.origin)
1.70 +
1.71 +def get_periods(obj, tzid, window_end, inclusive=False):
1.72
1.73 """
1.74 Return periods for the given object 'obj', confining materialised periods
1.75 to before the given 'window_end' datetime. If 'inclusive' is set to a true
1.76 value, any period occurring at the 'window_end' will be included.
1.77 -
1.78 - If 'origin' is set to a true value, the property type providing each period
1.79 - will be included in the results, with each result taking the form
1.80 - (start, end, property type).
1.81 """
1.82
1.83 rrule = obj.get_value("RRULE")
1.84 @@ -399,8 +408,7 @@
1.85 tzid = get_tzid(dtstart_attr, dtend_attr) or tzid
1.86
1.87 if not rrule:
1.88 - origin_value = origin and ("DTSTART",) or ()
1.89 - periods = [(dtstart, dtend) + origin_value]
1.90 + periods = [RecurringPeriod(dtstart, dtend, "DTSTART")]
1.91 else:
1.92 # Recurrence rules create multiple instances to be checked.
1.93 # Conflicts may only be assessed within a period defined by policy
1.94 @@ -409,13 +417,12 @@
1.95
1.96 selector = get_rule(dtstart, rrule)
1.97 parameters = get_parameters(rrule)
1.98 - origin_value = origin and ("RRULE",) or ()
1.99 periods = []
1.100
1.101 for start in selector.materialise(dtstart, window_end, parameters.get("COUNT"), parameters.get("BYSETPOS"), inclusive):
1.102 start = to_timezone(datetime(*start), tzid)
1.103 end = start + duration
1.104 - periods.append((start, end) + origin_value)
1.105 + periods.append(RecurringPeriod(start, end, "RRULE"))
1.106
1.107 # Add recurrence dates.
1.108
1.109 @@ -423,12 +430,11 @@
1.110 rdates = obj.get_date_values("RDATE", tzid)
1.111
1.112 if rdates:
1.113 - origin_value = origin and ("RDATE",) or ()
1.114 for rdate in rdates:
1.115 if isinstance(rdate, tuple):
1.116 - periods.add(rdate + origin_value)
1.117 + periods.add(RecurringPeriod(rdate[0], rdate[1], "RDATE"))
1.118 else:
1.119 - periods.add((rdate, rdate + duration) + origin_value)
1.120 + periods.add(RecurringPeriod(rdate, rdate + duration, "RDATE"))
1.121
1.122 # Return a sorted list of the periods.
1.123
1.124 @@ -442,24 +448,26 @@
1.125 if exdates:
1.126 for exdate in exdates:
1.127 if isinstance(exdate, tuple):
1.128 - period = exdate
1.129 + period = Period(exdate[0], exdate[1])
1.130 else:
1.131 - period = (exdate, exdate + duration)
1.132 + period = Period(exdate, exdate + duration)
1.133 i = bisect_left(periods, period)
1.134 - while i < len(periods) and periods[i][:2] == period:
1.135 + while i < len(periods) and periods[i] == period:
1.136 del periods[i]
1.137
1.138 return periods
1.139
1.140 class compare_periods:
1.141 +
1.142 + "Compare periods for exception date purposes."
1.143 +
1.144 def __init__(self, tzid):
1.145 self.tzid = tzid
1.146 +
1.147 def __call__(self, first, second):
1.148 - first_start, first_end = first[:2]
1.149 - second_start, second_end = second[:2]
1.150 return cmp(
1.151 - (to_datetime(first_start, self.tzid), to_datetime(first_end, self.tzid)),
1.152 - (to_datetime(second_start, self.tzid), to_datetime(second_end, self.tzid))
1.153 + (to_datetime(first.start, self.tzid), to_datetime(first.end, self.tzid)),
1.154 + (to_datetime(second.start, self.tzid), to_datetime(second.end, self.tzid))
1.155 )
1.156
1.157 def get_periods_for_freebusy(obj, periods, tzid):
1.158 @@ -482,11 +490,18 @@
1.159
1.160 l = []
1.161
1.162 - for t in periods:
1.163 - start, end = t[:2]
1.164 - start, end = get_freebusy_period(start, end, tzid)
1.165 + for p in periods:
1.166 + start, end = get_freebusy_period(p.start, p.end, tzid)
1.167 start, end = [to_timezone(x, "UTC") for x in start, end]
1.168 - l.append((format_datetime(start), format_datetime(end)) + t[2:])
1.169 +
1.170 + # Create a new period for free/busy purposes with the converted
1.171 + # datetime information.
1.172 +
1.173 + l.append(
1.174 + FreeBusyPeriod(
1.175 + format_datetime(start), format_datetime(end),
1.176 + *p.as_tuple()[2:]
1.177 + ))
1.178
1.179 return l
1.180