1.1 --- a/imiptools/period.py Sat May 16 01:03:51 2015 +0200
1.2 +++ b/imiptools/period.py Sat May 16 18:09:37 2015 +0200
1.3 @@ -20,8 +20,9 @@
1.4 """
1.5
1.6 from bisect import bisect_left, bisect_right, insort_left
1.7 -from datetime import datetime, timedelta
1.8 -from imiptools.dates import get_datetime, get_datetime_attributes, \
1.9 +from datetime import date, datetime, timedelta
1.10 +from imiptools.dates import format_datetime, get_datetime, \
1.11 + get_datetime_attributes, \
1.12 get_start_of_day, to_timezone
1.13
1.14 class Period:
1.15 @@ -29,35 +30,41 @@
1.16 "A basic period abstraction."
1.17
1.18 def __init__(self, start, end=None, origin=None):
1.19 - self.start = start
1.20 - self.end = end
1.21 + self.start = isinstance(start, date) and start or get_datetime(start)
1.22 + self.end = isinstance(end, date) and end or get_datetime(end)
1.23 self.origin = origin
1.24
1.25 def as_tuple(self):
1.26 return self.start, self.end
1.27
1.28 def __hash__(self):
1.29 - return hash((self.start, self.end))
1.30 + return hash((self.get_start(), self.get_end()))
1.31
1.32 def __cmp__(self, other):
1.33 if isinstance(other, Period):
1.34 - return cmp((self.start, self.end), (other.start, other.end))
1.35 + return cmp((self.get_start(), self.get_end()), (other.get_start(), other.get_end()))
1.36 else:
1.37 return 1
1.38
1.39 def get_key(self):
1.40 - return self.start, self.end
1.41 + return self.get_start(), self.get_end()
1.42
1.43 def __repr__(self):
1.44 return "Period(%r, %r)" % (self.start, self.end)
1.45
1.46 # Datetime metadata methods.
1.47
1.48 - def get_start(self):
1.49 - return self.start
1.50 + def get_start(self, tzid=None):
1.51 + if tzid:
1.52 + return to_timezone(self.start, tzid)
1.53 + else:
1.54 + return self.start
1.55
1.56 - def get_end(self):
1.57 - return self.end
1.58 + def get_end(self, tzid=None):
1.59 + if tzid:
1.60 + return to_timezone(self.end, tzid)
1.61 + else:
1.62 + return self.end
1.63
1.64 def get_start_item(self):
1.65 return self.start, get_datetime_attributes(self.start)
1.66 @@ -69,7 +76,14 @@
1.67
1.68 "A free/busy record abstraction."
1.69
1.70 - def __init__(self, start, end=None, uid=None, transp=None, recurrenceid=None, summary=None, organiser=None):
1.71 + def __init__(self, start, end, uid=None, transp=None, recurrenceid=None, summary=None, organiser=None):
1.72 +
1.73 + """
1.74 + Initialise a free/busy period with 'start' and 'end' datetime string
1.75 + representations, employing UTC, plus any 'uid', 'transp',
1.76 + 'recurrenceid', 'summary' and 'organiser' details.
1.77 + """
1.78 +
1.79 Period.__init__(self, start, end)
1.80 self.uid = uid
1.81 self.transp = transp
1.82 @@ -78,7 +92,7 @@
1.83 self.organiser = organiser
1.84
1.85 def as_tuple(self):
1.86 - return self.start, self.end, self.uid, self.transp, self.recurrenceid, self.summary, self.organiser
1.87 + return format_datetime(self.start), format_datetime(self.end), self.uid, self.transp, self.recurrenceid, self.summary, self.organiser
1.88
1.89 def __cmp__(self, other):
1.90 if isinstance(other, FreeBusyPeriod):
1.91 @@ -94,7 +108,7 @@
1.92 self.start, self.end, self.uid, self.transp, self.recurrenceid,
1.93 self.summary, self.organiser)
1.94
1.95 -# Time management with datetime strings in the UTC time zone.
1.96 +# Time and period management.
1.97
1.98 def can_schedule(freebusy, periods, uid, recurrenceid):
1.99
1.100 @@ -193,7 +207,7 @@
1.101
1.102 # Stop looking if the start no longer matches the recurrence identifier.
1.103
1.104 - if fb.start != start:
1.105 + if fb.get_start() != start:
1.106 return
1.107
1.108 # If the period belongs to the parent object, remove it and return.
1.109 @@ -216,21 +230,21 @@
1.110 # Find the range of periods potentially overlapping the period in the
1.111 # free/busy collection.
1.112
1.113 - last = bisect_right(freebusy, Period(period.end))
1.114 + last = bisect_right(freebusy, Period(period.get_end(), period.get_end()))
1.115 startpoints = freebusy[:last]
1.116
1.117 # Find the range of periods potentially overlapping the period in a version
1.118 # of the free/busy collection sorted according to end datetimes.
1.119
1.120 - endpoints = [(fb.end, fb.start, fb) for fb in startpoints]
1.121 + endpoints = [(fb.get_end(), fb.get_start(), fb) for fb in startpoints]
1.122 endpoints.sort()
1.123 - first = bisect_left(endpoints, (period.start,))
1.124 + first = bisect_left(endpoints, (period.get_start(),))
1.125 endpoints = endpoints[first:]
1.126
1.127 overlapping = set()
1.128
1.129 for end, start, fb in endpoints:
1.130 - if end > period.start and start < period.end:
1.131 + if end > period.get_start() and start < period.get_end():
1.132 overlapping.add(fb)
1.133
1.134 overlapping = list(overlapping)
1.135 @@ -273,29 +287,9 @@
1.136 for replacement in replacements:
1.137 insert_period(freebusy, replacement)
1.138
1.139 -# Period layout mostly with datetime objects.
1.140 -
1.141 -def convert_periods(periods, tzid):
1.142 -
1.143 - "Convert 'periods' to use datetime objects employing the given 'tzid'."
1.144 -
1.145 - for p in periods:
1.146 +# Period layout.
1.147
1.148 - # NOTE: This only really works if the datetimes are UTC already.
1.149 - # NOTE: Since the periods should originate from the free/busy data,
1.150 - # NOTE: and since that data should employ UTC times, this should not be
1.151 - # NOTE: an immediate problem.
1.152 -
1.153 - start = get_datetime(p.start)
1.154 - end = get_datetime(p.end)
1.155 -
1.156 - start = isinstance(start, datetime) and to_timezone(start, tzid) or get_start_of_day(start, tzid)
1.157 - end = isinstance(end, datetime) and to_timezone(end, tzid) or get_start_of_day(end, tzid)
1.158 -
1.159 - p.start = start
1.160 - p.end = end
1.161 -
1.162 -def get_scale(periods):
1.163 +def get_scale(periods, tzid):
1.164
1.165 """
1.166 Return an ordered time scale from the given list of 'periods'.
1.167 @@ -313,15 +307,15 @@
1.168
1.169 # Add a point and this event to the starting list.
1.170
1.171 - if not scale.has_key(p.start):
1.172 - scale[p.start] = [], []
1.173 - scale[p.start][0].append(p)
1.174 + if not scale.has_key(p.get_start(tzid)):
1.175 + scale[p.get_start(tzid)] = [], []
1.176 + scale[p.get_start(tzid)][0].append(p)
1.177
1.178 # Add a point and this event to the ending list.
1.179
1.180 - if not scale.has_key(p.end):
1.181 - scale[p.end] = [], []
1.182 - scale[p.end][1].append(p)
1.183 + if not scale.has_key(p.get_end(tzid)):
1.184 + scale[p.get_end(tzid)] = [], []
1.185 + scale[p.get_end(tzid)][1].append(p)
1.186
1.187 return scale
1.188
1.189 @@ -541,8 +535,8 @@
1.190 for p in active:
1.191 if p:
1.192 key = p.get_key()
1.193 - start_slot = bisect_left(points, p.start)
1.194 - end_slot = bisect_left(points, p.end)
1.195 + start_slot = bisect_left(points, p.get_start())
1.196 + end_slot = bisect_left(points, p.get_end())
1.197 spans[key] = end_slot - start_slot
1.198
1.199 return spans
1.200 @@ -557,6 +551,6 @@
1.201 remove_period(freebusy, uid, recurrenceid)
1.202
1.203 for p in periods:
1.204 - insert_period(freebusy, FreeBusyPeriod(p.start, p.end, uid, transp, recurrenceid, summary, organiser))
1.205 + insert_period(freebusy, FreeBusyPeriod(p.get_start(), p.get_end(), uid, transp, recurrenceid, summary, organiser))
1.206
1.207 # vim: tabstop=4 expandtab shiftwidth=4