1.1 --- a/imip_manager.py Tue Jan 13 22:39:53 2015 +0100
1.2 +++ b/imip_manager.py Tue Jan 13 22:41:18 2015 +0100
1.3 @@ -29,12 +29,13 @@
1.4
1.5 sys.path.append(LIBRARY_PATH)
1.6
1.7 -from imiptools.content import Handler, \
1.8 - format_datetime, get_address, get_datetime, \
1.9 +from imiptools.content import Handler, get_address, \
1.10 get_item, get_uri, get_utc_datetime, get_value, \
1.11 - get_values, parse_object, to_part, to_timezone
1.12 + get_values, parse_object, to_part
1.13 +from imiptools.dates import format_datetime, get_datetime, get_start_of_day, \
1.14 + to_timezone
1.15 from imiptools.mail import Messenger
1.16 -from imiptools.period import have_conflict, get_slots, get_spans
1.17 +from imiptools.period import have_conflict, get_slots, get_spans, partition_slots
1.18 from imiptools.profile import Preferences
1.19 from vCalendar import to_node
1.20 import markup
1.21 @@ -424,7 +425,7 @@
1.22 # Set the locale and obtain the user's timezone.
1.23
1.24 prefs = self.get_preferences()
1.25 - tzid = prefs.get("TZID")
1.26 + tzid = prefs.get("TZID", "UTC")
1.27
1.28 # Day view: start at the earliest known day and produce days until the
1.29 # latest known day, perhaps with expandable sections of empty days.
1.30 @@ -440,37 +441,46 @@
1.31 # the calendar.
1.32
1.33 slots = get_slots(freebusy)
1.34 - spans = get_spans(slots)
1.35 + partitioned = partition_slots(slots, tzid)
1.36 + columns = max(map(lambda i: len(i[1]), slots)) + 1
1.37
1.38 page.table(border=1, cellspacing=0, cellpadding=5)
1.39
1.40 - for point, active in slots:
1.41 - dt = to_timezone(get_datetime(point), tzid or "UTC")
1.42 + for day, slots in partitioned:
1.43 + spans = get_spans(slots)
1.44
1.45 page.tr()
1.46 - page.th(class_="timeslot")
1.47 - page.add(self.format_date(dt, "full"))
1.48 - page.br()
1.49 - page.add(self.format_time(dt, "long"))
1.50 + page.th(class_="dayheading", colspan=columns)
1.51 + page.add(self.format_date(day, "full"))
1.52 page.th.close()
1.53 + page.tr.close()
1.54
1.55 - for t in active:
1.56 - if t:
1.57 - start, end, uid = t[:3]
1.58 - span = spans[uid]
1.59 - if point == start:
1.60 + for point, active in slots:
1.61 + dt = to_timezone(get_datetime(point), tzid)
1.62 + continuation = dt == get_start_of_day(dt)
1.63 +
1.64 + page.tr()
1.65 + page.th(class_="timeslot")
1.66 + page.add(self.format_time(dt, "long"))
1.67 + page.th.close()
1.68
1.69 - page.td(class_="event", rowspan=span)
1.70 - obj = self._get_object(uid)
1.71 - if obj:
1.72 - details = self._get_details(obj)
1.73 - page.a(get_value(details, "SUMMARY"), href="%s/%s" % (self.env.get_url().rstrip("/"), uid))
1.74 + for t in active:
1.75 + if t:
1.76 + start, end, uid = t[:3]
1.77 + span = spans[uid]
1.78 + if point == start or continuation:
1.79 +
1.80 + page.td(class_="event", rowspan=span)
1.81 + obj = self._get_object(uid)
1.82 + if obj:
1.83 + details = self._get_details(obj)
1.84 + page.a(get_value(details, "SUMMARY"), href="%s/%s" % (self.env.get_url().rstrip("/"), uid))
1.85 + page.td.close()
1.86 + else:
1.87 + page.td(class_="empty")
1.88 page.td.close()
1.89 - else:
1.90 - page.td(class_="empty")
1.91 - page.td.close()
1.92
1.93 - page.tr.close()
1.94 + page.tr.close()
1.95
1.96 page.table.close()
1.97
2.1 --- a/imiptools/period.py Tue Jan 13 22:39:53 2015 +0100
2.2 +++ b/imiptools/period.py Tue Jan 13 22:41:18 2015 +0100
2.3 @@ -20,6 +20,7 @@
2.4 """
2.5
2.6 from bisect import bisect_left, insort_left
2.7 +from imiptools.dates import get_datetime, get_start_of_day, to_timezone
2.8
2.9 # Time management.
2.10
2.11 @@ -104,6 +105,9 @@
2.12 """
2.13 Return an ordered time scale from the given list 'l' of tuples, with the
2.14 first two elements of each tuple being start and end times.
2.15 +
2.16 + The returned scale is a collection of (time, (starting, ending)) tuples,
2.17 + where starting and ending are collections of tuples from 'l'.
2.18 """
2.19
2.20 scale = {}
2.21 @@ -163,6 +167,46 @@
2.22
2.23 return slots
2.24
2.25 +def partition_slots(slots, tzid):
2.26 +
2.27 + """
2.28 + Partition the given 'slots' into separate collections having a date-level
2.29 + resolution, using the given 'tzid' to make sure that the day boundaries are
2.30 + defined according to the chosen time zone.
2.31 +
2.32 + Return a collection of (date, slots) tuples.
2.33 + """
2.34 +
2.35 + partitioned = {}
2.36 + current = None
2.37 + current_date = None
2.38 + previously_active = None
2.39 +
2.40 + for point, active in slots:
2.41 + dt = to_timezone(get_datetime(point), tzid)
2.42 + start_of_day = get_start_of_day(dt)
2.43 + this_date = dt.date()
2.44 +
2.45 + # For each new day, create a partition of the original collection.
2.46 +
2.47 + if this_date != current_date:
2.48 + current_date = this_date
2.49 + partitioned[current_date] = current = []
2.50 +
2.51 + # Add any continuing periods.
2.52 +
2.53 + if dt != start_of_day and previously_active:
2.54 + current.append((start_of_day, previously_active))
2.55 +
2.56 + # Add the currently active periods at this point in time.
2.57 +
2.58 + current.append((point, active))
2.59 + previously_active = active
2.60 +
2.61 + partitioned = partitioned.items()
2.62 + partitioned.sort()
2.63 + return partitioned
2.64 +
2.65 def get_spans(slots):
2.66
2.67 "Inspect the given 'slots', returning a mapping of event uids to spans."
2.68 @@ -174,8 +218,14 @@
2.69 for t in active:
2.70 if t:
2.71 start, end, uid = t[:3]
2.72 - start_slot = points.index(start)
2.73 - end_slot = points.index(end)
2.74 + try:
2.75 + start_slot = points.index(start)
2.76 + except ValueError:
2.77 + start_slot = 0
2.78 + try:
2.79 + end_slot = points.index(end)
2.80 + except ValueError:
2.81 + end_slot = len(slots)
2.82 spans[uid] = end_slot - start_slot
2.83
2.84 return spans