# HG changeset patch # User Paul Boddie # Date 1427482930 -3600 # Node ID fe3f321df873d4cbd379fc8a31d306ce92c8db0f # Parent a53eedb3d6e1e7c99240ee84b0d911ecfeacc4d4 Added an abstraction for points in time encapsulating both a datetime and an indicator describing the nature of the time point. diff -r a53eedb3d6e1 -r fe3f321df873 imiptools/period.py --- a/imiptools/period.py Fri Mar 27 18:23:41 2015 +0100 +++ b/imiptools/period.py Fri Mar 27 20:02:10 2015 +0100 @@ -259,7 +259,47 @@ return scale -POINT, REPEATED = 0, 1 +class Point: + + "A qualified point in time." + + PRINCIPAL, REPEATED = 0, 1 + + def __init__(self, point, indicator=None): + self.point = point + self.indicator = indicator or self.PRINCIPAL + + def __hash__(self): + return hash((self.point, self.indicator)) + + def __cmp__(self, other): + if isinstance(other, Point): + return cmp((self.point, self.indicator), (other.point, other.indicator)) + elif isinstance(other, datetime): + return cmp(self.point, other) + else: + return 1 + + def __eq__(self, other): + return self.__cmp__(other) == 0 + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + return self.__cmp__(other) < 0 + + def __le__(self, other): + return self.__cmp__(other) <= 0 + + def __gt__(self, other): + return not self <= other + + def __ge__(self, other): + return not self < other + + def __repr__(self): + return "Point(%r, Point.%s)" % (self.point, self.indicator and "REPEATED" or "PRINCIPAL") def get_slots(scale): @@ -270,9 +310,10 @@ the slot, together with a list of parallel event tuples, each tuple containing the original details of an event. - Each point in time is described as a tuple containing the actual time point - plus an indicator (0 as POINT or 1 as REPEATED), with REPEATED being used to - indicate repeated points used for the end of "instant" events. + Each point in time is described as a Point representing the actual point in + time together with an indicator of the nature of the point in time (as a + principal point in a time scale or as a repeated point used to terminate + events occurring for an instant in time). """ slots = [] @@ -309,7 +350,7 @@ # Add an entry for the time point before "instants". - slots.append(((point, POINT), active[:])) + slots.append((Point(point), active[:])) # Discard events ending at the same time as they began. @@ -325,7 +366,7 @@ # Add another entry for the time point after "instants". - slots.append(((point, REPEATED), active[:])) + slots.append((Point(point, Point.REPEATED), active[:])) return slots @@ -341,9 +382,9 @@ current_date = None previously_active = [] - for (point, indicator), active in slots: - start_of_day = get_start_of_day(point, tzid) - this_date = point.date() + for point, active in slots: + start_of_day = get_start_of_day(point.point, tzid) + this_date = point.point.date() # For each new day, add a slot for the start of the day where periods # are active and where no such slot already exists. @@ -355,15 +396,15 @@ if current_date: current_date += timedelta(1) while current_date < this_date: - new_slots.append(((get_start_of_day(current_date, tzid), POINT), previously_active)) + new_slots.append((Point(get_start_of_day(current_date, tzid)), previously_active)) current_date += timedelta(1) else: current_date = this_date # Add any continuing periods. - if point != start_of_day: - new_slots.append(((start_of_day, POINT), previously_active)) + if point.point != start_of_day: + new_slots.append((Point(start_of_day), previously_active)) # Add the currently active periods at this point in time. @@ -400,11 +441,11 @@ d = {} - for (point, indicator), value in slots: - day = point.date() + for point, value in slots: + day = point.point.date() if not d.has_key(day): d[day] = [] - d[day].append(((point, indicator), value)) + d[day].append((point, value)) return d @@ -420,7 +461,7 @@ if last_day: empty_day = last_day + timedelta(1) while empty_day < day: - days[empty_day] = [((get_start_of_day(empty_day, tzid), POINT), None)] + days[empty_day] = [(Point(get_start_of_day(empty_day, tzid)), None)] empty_day += timedelta(1) last_day = day @@ -428,7 +469,7 @@ "Inspect the given 'slots', returning a mapping of event uids to spans." - all_point_details = [point_details for point_details, active in slots] + points = [point for point, active in slots] spans = {} for _point, active in slots: @@ -436,8 +477,8 @@ if t and len(t) >= 2: start, end, uid, recurrenceid, summary, organiser, key = get_freebusy_details(t) - start_slot = bisect_left(all_point_details, (start,)) - end_slot = bisect_left(all_point_details, (end,)) + start_slot = bisect_left(points, start) + end_slot = bisect_left(points, end) spans[key] = end_slot - start_slot return spans diff -r a53eedb3d6e1 -r fe3f321df873 imipweb/calendar.py --- a/imipweb/calendar.py Fri Mar 27 18:23:41 2015 +0100 +++ b/imipweb/calendar.py Fri Mar 27 20:02:10 2015 +0100 @@ -28,7 +28,7 @@ from imiptools.period import add_day_start_points, add_empty_days, add_slots, \ convert_periods, get_freebusy_details, \ get_scale, get_slots, get_spans, partition_by_day, \ - POINT, REPEATED + Point from imipweb.resource import Resource class CalendarPage(Resource): @@ -371,15 +371,14 @@ last = None - for point_details, active in day_slots: - point, indicator = point_details + for point, active in day_slots: columns = max(columns, len(active)) - day_points[point_details] = active + day_points[point] = active if last: intervals.append((last, point)) - last = point_details + last = point if last: intervals.append((last, None)) @@ -544,8 +543,8 @@ intervals = list(intervals) intervals.sort() - for (point, indicator), endpoint in intervals: - continuation = point == get_start_of_day(point, tzid) + for point, endpoint in intervals: + continuation = point.point == get_start_of_day(point.point, tzid) # Some rows contain no period details and are marked as such. @@ -553,7 +552,7 @@ have_active_request = False for slots, group_type in zip(groups, group_types): - if slots and slots.get((point, indicator)): + if slots and slots.get(point): if group_type == "request": have_active_request = True else: @@ -564,12 +563,13 @@ css = " ".join([ "slot", - (have_active or indicator == REPEATED) and "busy" or have_active_request and "suggested" or "empty", + (have_active or point.indicator == Point.REPEATED) and "busy" or \ + have_active_request and "suggested" or "empty", continuation and "daystart" or "" ]) page.tr(class_=css) - if indicator == POINT: + if point.indicator == Point.PRINCIPAL: page.th(class_="timeslot") self._time_point(point, endpoint) else: @@ -579,14 +579,14 @@ # Obtain slots for the time point from each group. for columns, slots, group_type in zip(group_columns, groups, group_types): - active = slots and slots.get((point, indicator)) + active = slots and slots.get(point) # Where no periods exist for the given time interval, generate # an empty cell. Where a participant provides no periods at all, # the colspan is adjusted to be 1, not 0. if not active: - self._empty_slot(point, endpoint, max(columns, 1), indicator) + self._empty_slot(point, endpoint, max(columns, 1)) continue slots = slots.items() @@ -603,7 +603,7 @@ # Flush empty slots preceding this one. if empty: - self._empty_slot(point, endpoint, empty, indicator) + self._empty_slot(point, endpoint, empty) empty = 0 start, end, uid, recurrenceid, summary, organiser, key = get_freebusy_details(t) @@ -614,10 +614,10 @@ # Points defining the ends of instant events should # never define the start of new events. - if indicator == POINT and (point == start or continuation): + if point.indicator == Point.PRINCIPAL and (point.point == start or continuation): - has_continued = continuation and point != start - will_continue = not ends_on_same_day(point, end, tzid) + has_continued = continuation and point.point != start + will_continue = not ends_on_same_day(point.point, end, tzid) is_organiser = organiser == self.user css = " ".join([ @@ -633,7 +633,7 @@ html_id = "%s-%s-%s" % (group_type, uid, recurrenceid or "") - if point == start and html_id not in self.html_ids: + if point.point == start and html_id not in self.html_ids: page.td(class_=css, rowspan=span, id=html_id) self.html_ids.add(html_id) else: @@ -656,7 +656,7 @@ empty = columns - len(active) if empty: - self._empty_slot(point, endpoint, empty, indicator) + self._empty_slot(point, endpoint, empty) page.tr.close() @@ -685,12 +685,12 @@ page = self.page tzid = self.get_tzid() - daystr = format_datetime(point.date()) + daystr = format_datetime(point.point.date()) value, identifier = self._slot_value_and_identifier(point, endpoint) slots = self.env.get_args().get("slot", []) self._slot_selector(value, identifier, slots) - page.label(self.format_time(point, "long"), class_="timepoint day-%s" % daystr, for_=identifier) - page.span(self.format_time(endpoint or get_end_of_day(point, tzid), "long"), class_="endpoint") + page.label(self.format_time(point.point, "long"), class_="timepoint day-%s" % daystr, for_=identifier) + page.span(self.format_time(endpoint and endpoint.point or get_end_of_day(point.point, tzid), "long"), class_="endpoint") def _slot_selector(self, value, identifier, slots): @@ -708,17 +708,16 @@ else: page.input(name="slot", type="checkbox", value=value, id=identifier, class_="newevent selector") - def _empty_slot(self, point, endpoint, colspan, indicator): + def _empty_slot(self, point, endpoint, colspan): """ Show an empty slot cell for the given 'point' and 'endpoint', with the - given 'colspan' and 'indicator' (0 as POINT or 1 as REPEATED) - configuring the cell's appearance. + given 'colspan' configuring the cell's appearance. """ page = self.page - page.td(class_="empty%s" % (indicator == POINT and " container" or ""), colspan=colspan) - if indicator == POINT: + page.td(class_="empty%s" % (point.indicator == Point.PRINCIPAL and " container" or ""), colspan=colspan) + if point.indicator == Point.PRINCIPAL: value, identifier = self._slot_value_and_identifier(point, endpoint) page.label("Select/deselect period", class_="newevent popup", for_=identifier) page.td.close() @@ -738,7 +737,7 @@ 'endpoint'. """ - value = "%s-%s" % (format_datetime(point), endpoint and format_datetime(endpoint) or "") + value = "%s-%s" % (format_datetime(point.point), endpoint and format_datetime(endpoint.point) or "") identifier = "slot-%s" % value return value, identifier