1.1 --- a/imip_store.py Sat May 16 01:03:51 2015 +0200
1.2 +++ b/imip_store.py Sat May 16 18:09:37 2015 +0200
1.3 @@ -22,6 +22,7 @@
1.4 from datetime import datetime
1.5 from imiptools.config import STORE_DIR, PUBLISH_DIR
1.6 from imiptools.data import make_calendar, parse_object, to_stream
1.7 +from imiptools.dates import format_datetime
1.8 from imiptools.filesys import fix_permissions, FileBase
1.9 from imiptools.period import FreeBusyPeriod
1.10 from os.path import exists, isfile, join
1.11 @@ -520,7 +521,8 @@
1.12
1.13 for fb in freebusy:
1.14 if not fb.transp or fb.transp == "OPAQUE":
1.15 - rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([fb.start, fb.end])))
1.16 + rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join(
1.17 + map(format_datetime, [fb.get_start(), fb.get_end()]))))
1.18
1.19 f = open(filename, "wb")
1.20 try:
2.1 --- a/imiptools/data.py Sat May 16 01:03:51 2015 +0200
2.2 +++ b/imiptools/data.py Sat May 16 18:09:37 2015 +0200
2.3 @@ -180,12 +180,14 @@
2.4
2.5 # Write the limits of the resource.
2.6
2.7 - rwrite(("DTSTART", {"VALUE" : "DATE-TIME"}, periods[0].start))
2.8 - rwrite(("DTEND", {"VALUE" : "DATE-TIME"}, periods[-1].end))
2.9 + rwrite(("DTSTART", {"VALUE" : "DATE-TIME"}, format_datetime(periods[0].get_start())))
2.10 + rwrite(("DTEND", {"VALUE" : "DATE-TIME"}, format_datetime(periods[-1].get_end())))
2.11
2.12 for p in periods:
2.13 if p.transp == "OPAQUE":
2.14 - rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([p.start, p.end])))
2.15 + rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join(
2.16 + map(format_datetime, [p.get_start(), p.get_end()])
2.17 + )))
2.18
2.19 return ("VFREEBUSY", {}, record)
2.20
2.21 @@ -540,9 +542,7 @@
2.22 # Create a new period for free/busy purposes with the converted
2.23 # datetime information.
2.24
2.25 - l.append(p.__class__(
2.26 - *((format_datetime(start), format_datetime(end)) + p.as_tuple()[2:])
2.27 - ))
2.28 + l.append(p.__class__(start, end, *p.as_tuple()[2:]))
2.29
2.30 return l
2.31
3.1 --- a/imiptools/handlers/person.py Sat May 16 01:03:51 2015 +0200
3.2 +++ b/imiptools/handlers/person.py Sat May 16 18:09:37 2015 +0200
3.3 @@ -20,10 +20,9 @@
3.4 """
3.5
3.6 from imiptools.data import get_uri, uri_values, values_from_items
3.7 -from imiptools.dates import format_datetime
3.8 from imiptools.handlers import Handler
3.9 from imiptools.handlers.common import CommonFreebusy
3.10 -from imiptools.period import Period, replace_overlapping
3.11 +from imiptools.period import FreeBusyPeriod, Period, replace_overlapping
3.12 from imiptools.profile import Preferences
3.13
3.14 class PersonHandler(Handler):
3.15 @@ -133,12 +132,12 @@
3.16 for v in value:
3.17 try:
3.18 start, end = v.split("/", 1)
3.19 - freebusy.append(Period(start, end))
3.20 + freebusy.append(FreeBusyPeriod(start, end))
3.21 except ValueError:
3.22 pass
3.23
3.24 - dtstart = format_datetime(self.obj.get_utc_datetime("DTSTART"))
3.25 - dtend = format_datetime(self.obj.get_utc_datetime("DTEND"))
3.26 + dtstart = self.obj.get_utc_datetime("DTSTART")
3.27 + dtend = self.obj.get_utc_datetime("DTEND")
3.28
3.29 for sender, sender_attr in senders:
3.30 stored_freebusy = self.store.get_freebusy_for_other(self.user, sender)
4.1 --- a/imiptools/period.py Sat May 16 01:03:51 2015 +0200
4.2 +++ b/imiptools/period.py Sat May 16 18:09:37 2015 +0200
4.3 @@ -20,8 +20,9 @@
4.4 """
4.5
4.6 from bisect import bisect_left, bisect_right, insort_left
4.7 -from datetime import datetime, timedelta
4.8 -from imiptools.dates import get_datetime, get_datetime_attributes, \
4.9 +from datetime import date, datetime, timedelta
4.10 +from imiptools.dates import format_datetime, get_datetime, \
4.11 + get_datetime_attributes, \
4.12 get_start_of_day, to_timezone
4.13
4.14 class Period:
4.15 @@ -29,35 +30,41 @@
4.16 "A basic period abstraction."
4.17
4.18 def __init__(self, start, end=None, origin=None):
4.19 - self.start = start
4.20 - self.end = end
4.21 + self.start = isinstance(start, date) and start or get_datetime(start)
4.22 + self.end = isinstance(end, date) and end or get_datetime(end)
4.23 self.origin = origin
4.24
4.25 def as_tuple(self):
4.26 return self.start, self.end
4.27
4.28 def __hash__(self):
4.29 - return hash((self.start, self.end))
4.30 + return hash((self.get_start(), self.get_end()))
4.31
4.32 def __cmp__(self, other):
4.33 if isinstance(other, Period):
4.34 - return cmp((self.start, self.end), (other.start, other.end))
4.35 + return cmp((self.get_start(), self.get_end()), (other.get_start(), other.get_end()))
4.36 else:
4.37 return 1
4.38
4.39 def get_key(self):
4.40 - return self.start, self.end
4.41 + return self.get_start(), self.get_end()
4.42
4.43 def __repr__(self):
4.44 return "Period(%r, %r)" % (self.start, self.end)
4.45
4.46 # Datetime metadata methods.
4.47
4.48 - def get_start(self):
4.49 - return self.start
4.50 + def get_start(self, tzid=None):
4.51 + if tzid:
4.52 + return to_timezone(self.start, tzid)
4.53 + else:
4.54 + return self.start
4.55
4.56 - def get_end(self):
4.57 - return self.end
4.58 + def get_end(self, tzid=None):
4.59 + if tzid:
4.60 + return to_timezone(self.end, tzid)
4.61 + else:
4.62 + return self.end
4.63
4.64 def get_start_item(self):
4.65 return self.start, get_datetime_attributes(self.start)
4.66 @@ -69,7 +76,14 @@
4.67
4.68 "A free/busy record abstraction."
4.69
4.70 - def __init__(self, start, end=None, uid=None, transp=None, recurrenceid=None, summary=None, organiser=None):
4.71 + def __init__(self, start, end, uid=None, transp=None, recurrenceid=None, summary=None, organiser=None):
4.72 +
4.73 + """
4.74 + Initialise a free/busy period with 'start' and 'end' datetime string
4.75 + representations, employing UTC, plus any 'uid', 'transp',
4.76 + 'recurrenceid', 'summary' and 'organiser' details.
4.77 + """
4.78 +
4.79 Period.__init__(self, start, end)
4.80 self.uid = uid
4.81 self.transp = transp
4.82 @@ -78,7 +92,7 @@
4.83 self.organiser = organiser
4.84
4.85 def as_tuple(self):
4.86 - return self.start, self.end, self.uid, self.transp, self.recurrenceid, self.summary, self.organiser
4.87 + return format_datetime(self.start), format_datetime(self.end), self.uid, self.transp, self.recurrenceid, self.summary, self.organiser
4.88
4.89 def __cmp__(self, other):
4.90 if isinstance(other, FreeBusyPeriod):
4.91 @@ -94,7 +108,7 @@
4.92 self.start, self.end, self.uid, self.transp, self.recurrenceid,
4.93 self.summary, self.organiser)
4.94
4.95 -# Time management with datetime strings in the UTC time zone.
4.96 +# Time and period management.
4.97
4.98 def can_schedule(freebusy, periods, uid, recurrenceid):
4.99
4.100 @@ -193,7 +207,7 @@
4.101
4.102 # Stop looking if the start no longer matches the recurrence identifier.
4.103
4.104 - if fb.start != start:
4.105 + if fb.get_start() != start:
4.106 return
4.107
4.108 # If the period belongs to the parent object, remove it and return.
4.109 @@ -216,21 +230,21 @@
4.110 # Find the range of periods potentially overlapping the period in the
4.111 # free/busy collection.
4.112
4.113 - last = bisect_right(freebusy, Period(period.end))
4.114 + last = bisect_right(freebusy, Period(period.get_end(), period.get_end()))
4.115 startpoints = freebusy[:last]
4.116
4.117 # Find the range of periods potentially overlapping the period in a version
4.118 # of the free/busy collection sorted according to end datetimes.
4.119
4.120 - endpoints = [(fb.end, fb.start, fb) for fb in startpoints]
4.121 + endpoints = [(fb.get_end(), fb.get_start(), fb) for fb in startpoints]
4.122 endpoints.sort()
4.123 - first = bisect_left(endpoints, (period.start,))
4.124 + first = bisect_left(endpoints, (period.get_start(),))
4.125 endpoints = endpoints[first:]
4.126
4.127 overlapping = set()
4.128
4.129 for end, start, fb in endpoints:
4.130 - if end > period.start and start < period.end:
4.131 + if end > period.get_start() and start < period.get_end():
4.132 overlapping.add(fb)
4.133
4.134 overlapping = list(overlapping)
4.135 @@ -273,29 +287,9 @@
4.136 for replacement in replacements:
4.137 insert_period(freebusy, replacement)
4.138
4.139 -# Period layout mostly with datetime objects.
4.140 -
4.141 -def convert_periods(periods, tzid):
4.142 -
4.143 - "Convert 'periods' to use datetime objects employing the given 'tzid'."
4.144 -
4.145 - for p in periods:
4.146 +# Period layout.
4.147
4.148 - # NOTE: This only really works if the datetimes are UTC already.
4.149 - # NOTE: Since the periods should originate from the free/busy data,
4.150 - # NOTE: and since that data should employ UTC times, this should not be
4.151 - # NOTE: an immediate problem.
4.152 -
4.153 - start = get_datetime(p.start)
4.154 - end = get_datetime(p.end)
4.155 -
4.156 - start = isinstance(start, datetime) and to_timezone(start, tzid) or get_start_of_day(start, tzid)
4.157 - end = isinstance(end, datetime) and to_timezone(end, tzid) or get_start_of_day(end, tzid)
4.158 -
4.159 - p.start = start
4.160 - p.end = end
4.161 -
4.162 -def get_scale(periods):
4.163 +def get_scale(periods, tzid):
4.164
4.165 """
4.166 Return an ordered time scale from the given list of 'periods'.
4.167 @@ -313,15 +307,15 @@
4.168
4.169 # Add a point and this event to the starting list.
4.170
4.171 - if not scale.has_key(p.start):
4.172 - scale[p.start] = [], []
4.173 - scale[p.start][0].append(p)
4.174 + if not scale.has_key(p.get_start(tzid)):
4.175 + scale[p.get_start(tzid)] = [], []
4.176 + scale[p.get_start(tzid)][0].append(p)
4.177
4.178 # Add a point and this event to the ending list.
4.179
4.180 - if not scale.has_key(p.end):
4.181 - scale[p.end] = [], []
4.182 - scale[p.end][1].append(p)
4.183 + if not scale.has_key(p.get_end(tzid)):
4.184 + scale[p.get_end(tzid)] = [], []
4.185 + scale[p.get_end(tzid)][1].append(p)
4.186
4.187 return scale
4.188
4.189 @@ -541,8 +535,8 @@
4.190 for p in active:
4.191 if p:
4.192 key = p.get_key()
4.193 - start_slot = bisect_left(points, p.start)
4.194 - end_slot = bisect_left(points, p.end)
4.195 + start_slot = bisect_left(points, p.get_start())
4.196 + end_slot = bisect_left(points, p.get_end())
4.197 spans[key] = end_slot - start_slot
4.198
4.199 return spans
4.200 @@ -557,6 +551,6 @@
4.201 remove_period(freebusy, uid, recurrenceid)
4.202
4.203 for p in periods:
4.204 - insert_period(freebusy, FreeBusyPeriod(p.start, p.end, uid, transp, recurrenceid, summary, organiser))
4.205 + insert_period(freebusy, FreeBusyPeriod(p.get_start(), p.get_end(), uid, transp, recurrenceid, summary, organiser))
4.206
4.207 # vim: tabstop=4 expandtab shiftwidth=4
5.1 --- a/imipweb/calendar.py Sat May 16 01:03:51 2015 +0200
5.2 +++ b/imipweb/calendar.py Sat May 16 18:09:37 2015 +0200
5.3 @@ -26,8 +26,7 @@
5.4 get_start_of_next_day, get_timestamp, ends_on_same_day, \
5.5 to_timezone
5.6 from imiptools.period import add_day_start_points, add_empty_days, add_slots, \
5.7 - convert_periods, get_scale, get_slots, get_spans, \
5.8 - partition_by_day, Point
5.9 + get_scale, get_slots, get_spans, partition_by_day, Point
5.10 from imipweb.resource import Resource
5.11
5.12 class CalendarPage(Resource):
5.13 @@ -295,11 +294,10 @@
5.14 # Obtain time point information for each group of periods.
5.15
5.16 for periods in period_groups:
5.17 - convert_periods(periods, tzid)
5.18
5.19 # Get the time scale with start and end points.
5.20
5.21 - scale = get_scale(periods)
5.22 + scale = get_scale(periods, tzid)
5.23
5.24 # Get the time slots for the periods.
5.25 # Time slots are collections of Point objects with lists of active
6.1 --- a/imipweb/event.py Sat May 16 01:03:51 2015 +0200
6.2 +++ b/imipweb/event.py Sat May 16 18:09:37 2015 +0200
6.3 @@ -26,7 +26,7 @@
6.4 get_datetime_item, get_period_item, \
6.5 to_timezone, to_utc_datetime
6.6 from imiptools.mail import Messenger
6.7 -from imiptools.period import convert_periods, have_conflict
6.8 +from imiptools.period import have_conflict
6.9 from imipweb.data import EventPeriod, \
6.10 event_period_from_period, form_period_from_period, \
6.11 FormDate, FormPeriod, PeriodError
6.12 @@ -212,8 +212,8 @@
6.13 "Set in the given 'obj' the given 'period' as the main start and end."
6.14
6.15 p = event_period_from_period(period)
6.16 - result = self.set_datetime_in_object(p.start, p.start_attr and p.start_attr.get("TZID"), "DTSTART", obj)
6.17 - result = self.set_datetime_in_object(p.end, p.end_attr and p.end_attr.get("TZID"), "DTEND", obj) or result
6.18 + result = self.set_datetime_in_object(p.get_start(), p.start_attr and p.start_attr.get("TZID"), "DTSTART", obj)
6.19 + result = self.set_datetime_in_object(p.get_end(), p.end_attr and p.end_attr.get("TZID"), "DTEND", obj) or result
6.20 return result
6.21
6.22 def set_periods_in_object(self, obj, periods):
6.23 @@ -231,7 +231,7 @@
6.24 for p in periods:
6.25 if p.origin != "RRULE":
6.26 tzid = p.start_attr and p.start_attr.get("TZID") or p.end_attr and p.end_attr.get("TZID")
6.27 - new_rdates.append(get_period_item(p.start, p.end, tzid))
6.28 + new_rdates.append(get_period_item(p.get_start(), p.get_end(), tzid))
6.29
6.30 obj["RDATE"] = new_rdates
6.31
6.32 @@ -873,7 +873,6 @@
6.33
6.34 for p in have_conflict(freebusy, periods, True):
6.35 period = event_period_from_period(p)
6.36 - convert_periods([period], tzid)
6.37 if self.is_replaced(period, recurrenceids):
6.38 continue
6.39 if (p.uid != uid or (recurrenceid and p.recurrenceid) and p.recurrenceid != recurrenceid) and p.transp != "ORG":
6.40 @@ -900,8 +899,8 @@
6.41
6.42 # Provide details of any conflicting event.
6.43
6.44 - start = self.format_datetime(to_timezone(get_datetime(p.start), tzid), "long")
6.45 - end = self.format_datetime(to_timezone(get_datetime(p.end), tzid), "long")
6.46 + start = self.format_datetime(p.get_start(tzid), "long")
6.47 + end = self.format_datetime(p.get_end(tzid), "long")
6.48
6.49 page.tr()
6.50
7.1 --- a/imipweb/resource.py Sat May 16 01:03:51 2015 +0200
7.2 +++ b/imipweb/resource.py Sat May 16 18:09:37 2015 +0200
7.3 @@ -141,7 +141,9 @@
7.4 if recurrenceid or p.start not in recurrenceids:
7.5 summary.append(
7.6 FreeBusyPeriod(
7.7 - p.start, p.end, uid,
7.8 + p.get_start(),
7.9 + p.get_end(),
7.10 + uid,
7.11 obj.get_value("TRANSP"),
7.12 recurrenceid,
7.13 obj.get_value("SUMMARY"),