1.1 --- a/imiptools/period.py Fri Mar 27 18:23:41 2015 +0100
1.2 +++ b/imiptools/period.py Fri Mar 27 20:02:10 2015 +0100
1.3 @@ -259,7 +259,47 @@
1.4
1.5 return scale
1.6
1.7 -POINT, REPEATED = 0, 1
1.8 +class Point:
1.9 +
1.10 + "A qualified point in time."
1.11 +
1.12 + PRINCIPAL, REPEATED = 0, 1
1.13 +
1.14 + def __init__(self, point, indicator=None):
1.15 + self.point = point
1.16 + self.indicator = indicator or self.PRINCIPAL
1.17 +
1.18 + def __hash__(self):
1.19 + return hash((self.point, self.indicator))
1.20 +
1.21 + def __cmp__(self, other):
1.22 + if isinstance(other, Point):
1.23 + return cmp((self.point, self.indicator), (other.point, other.indicator))
1.24 + elif isinstance(other, datetime):
1.25 + return cmp(self.point, other)
1.26 + else:
1.27 + return 1
1.28 +
1.29 + def __eq__(self, other):
1.30 + return self.__cmp__(other) == 0
1.31 +
1.32 + def __ne__(self, other):
1.33 + return not self == other
1.34 +
1.35 + def __lt__(self, other):
1.36 + return self.__cmp__(other) < 0
1.37 +
1.38 + def __le__(self, other):
1.39 + return self.__cmp__(other) <= 0
1.40 +
1.41 + def __gt__(self, other):
1.42 + return not self <= other
1.43 +
1.44 + def __ge__(self, other):
1.45 + return not self < other
1.46 +
1.47 + def __repr__(self):
1.48 + return "Point(%r, Point.%s)" % (self.point, self.indicator and "REPEATED" or "PRINCIPAL")
1.49
1.50 def get_slots(scale):
1.51
1.52 @@ -270,9 +310,10 @@
1.53 the slot, together with a list of parallel event tuples, each tuple
1.54 containing the original details of an event.
1.55
1.56 - Each point in time is described as a tuple containing the actual time point
1.57 - plus an indicator (0 as POINT or 1 as REPEATED), with REPEATED being used to
1.58 - indicate repeated points used for the end of "instant" events.
1.59 + Each point in time is described as a Point representing the actual point in
1.60 + time together with an indicator of the nature of the point in time (as a
1.61 + principal point in a time scale or as a repeated point used to terminate
1.62 + events occurring for an instant in time).
1.63 """
1.64
1.65 slots = []
1.66 @@ -309,7 +350,7 @@
1.67
1.68 # Add an entry for the time point before "instants".
1.69
1.70 - slots.append(((point, POINT), active[:]))
1.71 + slots.append((Point(point), active[:]))
1.72
1.73 # Discard events ending at the same time as they began.
1.74
1.75 @@ -325,7 +366,7 @@
1.76
1.77 # Add another entry for the time point after "instants".
1.78
1.79 - slots.append(((point, REPEATED), active[:]))
1.80 + slots.append((Point(point, Point.REPEATED), active[:]))
1.81
1.82 return slots
1.83
1.84 @@ -341,9 +382,9 @@
1.85 current_date = None
1.86 previously_active = []
1.87
1.88 - for (point, indicator), active in slots:
1.89 - start_of_day = get_start_of_day(point, tzid)
1.90 - this_date = point.date()
1.91 + for point, active in slots:
1.92 + start_of_day = get_start_of_day(point.point, tzid)
1.93 + this_date = point.point.date()
1.94
1.95 # For each new day, add a slot for the start of the day where periods
1.96 # are active and where no such slot already exists.
1.97 @@ -355,15 +396,15 @@
1.98 if current_date:
1.99 current_date += timedelta(1)
1.100 while current_date < this_date:
1.101 - new_slots.append(((get_start_of_day(current_date, tzid), POINT), previously_active))
1.102 + new_slots.append((Point(get_start_of_day(current_date, tzid)), previously_active))
1.103 current_date += timedelta(1)
1.104 else:
1.105 current_date = this_date
1.106
1.107 # Add any continuing periods.
1.108
1.109 - if point != start_of_day:
1.110 - new_slots.append(((start_of_day, POINT), previously_active))
1.111 + if point.point != start_of_day:
1.112 + new_slots.append((Point(start_of_day), previously_active))
1.113
1.114 # Add the currently active periods at this point in time.
1.115
1.116 @@ -400,11 +441,11 @@
1.117
1.118 d = {}
1.119
1.120 - for (point, indicator), value in slots:
1.121 - day = point.date()
1.122 + for point, value in slots:
1.123 + day = point.point.date()
1.124 if not d.has_key(day):
1.125 d[day] = []
1.126 - d[day].append(((point, indicator), value))
1.127 + d[day].append((point, value))
1.128
1.129 return d
1.130
1.131 @@ -420,7 +461,7 @@
1.132 if last_day:
1.133 empty_day = last_day + timedelta(1)
1.134 while empty_day < day:
1.135 - days[empty_day] = [((get_start_of_day(empty_day, tzid), POINT), None)]
1.136 + days[empty_day] = [(Point(get_start_of_day(empty_day, tzid)), None)]
1.137 empty_day += timedelta(1)
1.138 last_day = day
1.139
1.140 @@ -428,7 +469,7 @@
1.141
1.142 "Inspect the given 'slots', returning a mapping of event uids to spans."
1.143
1.144 - all_point_details = [point_details for point_details, active in slots]
1.145 + points = [point for point, active in slots]
1.146 spans = {}
1.147
1.148 for _point, active in slots:
1.149 @@ -436,8 +477,8 @@
1.150 if t and len(t) >= 2:
1.151 start, end, uid, recurrenceid, summary, organiser, key = get_freebusy_details(t)
1.152
1.153 - start_slot = bisect_left(all_point_details, (start,))
1.154 - end_slot = bisect_left(all_point_details, (end,))
1.155 + start_slot = bisect_left(points, start)
1.156 + end_slot = bisect_left(points, end)
1.157 spans[key] = end_slot - start_slot
1.158
1.159 return spans
2.1 --- a/imipweb/calendar.py Fri Mar 27 18:23:41 2015 +0100
2.2 +++ b/imipweb/calendar.py Fri Mar 27 20:02:10 2015 +0100
2.3 @@ -28,7 +28,7 @@
2.4 from imiptools.period import add_day_start_points, add_empty_days, add_slots, \
2.5 convert_periods, get_freebusy_details, \
2.6 get_scale, get_slots, get_spans, partition_by_day, \
2.7 - POINT, REPEATED
2.8 + Point
2.9 from imipweb.resource import Resource
2.10
2.11 class CalendarPage(Resource):
2.12 @@ -371,15 +371,14 @@
2.13
2.14 last = None
2.15
2.16 - for point_details, active in day_slots:
2.17 - point, indicator = point_details
2.18 + for point, active in day_slots:
2.19 columns = max(columns, len(active))
2.20 - day_points[point_details] = active
2.21 + day_points[point] = active
2.22
2.23 if last:
2.24 intervals.append((last, point))
2.25
2.26 - last = point_details
2.27 + last = point
2.28
2.29 if last:
2.30 intervals.append((last, None))
2.31 @@ -544,8 +543,8 @@
2.32 intervals = list(intervals)
2.33 intervals.sort()
2.34
2.35 - for (point, indicator), endpoint in intervals:
2.36 - continuation = point == get_start_of_day(point, tzid)
2.37 + for point, endpoint in intervals:
2.38 + continuation = point.point == get_start_of_day(point.point, tzid)
2.39
2.40 # Some rows contain no period details and are marked as such.
2.41
2.42 @@ -553,7 +552,7 @@
2.43 have_active_request = False
2.44
2.45 for slots, group_type in zip(groups, group_types):
2.46 - if slots and slots.get((point, indicator)):
2.47 + if slots and slots.get(point):
2.48 if group_type == "request":
2.49 have_active_request = True
2.50 else:
2.51 @@ -564,12 +563,13 @@
2.52
2.53 css = " ".join([
2.54 "slot",
2.55 - (have_active or indicator == REPEATED) and "busy" or have_active_request and "suggested" or "empty",
2.56 + (have_active or point.indicator == Point.REPEATED) and "busy" or \
2.57 + have_active_request and "suggested" or "empty",
2.58 continuation and "daystart" or ""
2.59 ])
2.60
2.61 page.tr(class_=css)
2.62 - if indicator == POINT:
2.63 + if point.indicator == Point.PRINCIPAL:
2.64 page.th(class_="timeslot")
2.65 self._time_point(point, endpoint)
2.66 else:
2.67 @@ -579,14 +579,14 @@
2.68 # Obtain slots for the time point from each group.
2.69
2.70 for columns, slots, group_type in zip(group_columns, groups, group_types):
2.71 - active = slots and slots.get((point, indicator))
2.72 + active = slots and slots.get(point)
2.73
2.74 # Where no periods exist for the given time interval, generate
2.75 # an empty cell. Where a participant provides no periods at all,
2.76 # the colspan is adjusted to be 1, not 0.
2.77
2.78 if not active:
2.79 - self._empty_slot(point, endpoint, max(columns, 1), indicator)
2.80 + self._empty_slot(point, endpoint, max(columns, 1))
2.81 continue
2.82
2.83 slots = slots.items()
2.84 @@ -603,7 +603,7 @@
2.85 # Flush empty slots preceding this one.
2.86
2.87 if empty:
2.88 - self._empty_slot(point, endpoint, empty, indicator)
2.89 + self._empty_slot(point, endpoint, empty)
2.90 empty = 0
2.91
2.92 start, end, uid, recurrenceid, summary, organiser, key = get_freebusy_details(t)
2.93 @@ -614,10 +614,10 @@
2.94 # Points defining the ends of instant events should
2.95 # never define the start of new events.
2.96
2.97 - if indicator == POINT and (point == start or continuation):
2.98 + if point.indicator == Point.PRINCIPAL and (point.point == start or continuation):
2.99
2.100 - has_continued = continuation and point != start
2.101 - will_continue = not ends_on_same_day(point, end, tzid)
2.102 + has_continued = continuation and point.point != start
2.103 + will_continue = not ends_on_same_day(point.point, end, tzid)
2.104 is_organiser = organiser == self.user
2.105
2.106 css = " ".join([
2.107 @@ -633,7 +633,7 @@
2.108
2.109 html_id = "%s-%s-%s" % (group_type, uid, recurrenceid or "")
2.110
2.111 - if point == start and html_id not in self.html_ids:
2.112 + if point.point == start and html_id not in self.html_ids:
2.113 page.td(class_=css, rowspan=span, id=html_id)
2.114 self.html_ids.add(html_id)
2.115 else:
2.116 @@ -656,7 +656,7 @@
2.117 empty = columns - len(active)
2.118
2.119 if empty:
2.120 - self._empty_slot(point, endpoint, empty, indicator)
2.121 + self._empty_slot(point, endpoint, empty)
2.122
2.123 page.tr.close()
2.124
2.125 @@ -685,12 +685,12 @@
2.126
2.127 page = self.page
2.128 tzid = self.get_tzid()
2.129 - daystr = format_datetime(point.date())
2.130 + daystr = format_datetime(point.point.date())
2.131 value, identifier = self._slot_value_and_identifier(point, endpoint)
2.132 slots = self.env.get_args().get("slot", [])
2.133 self._slot_selector(value, identifier, slots)
2.134 - page.label(self.format_time(point, "long"), class_="timepoint day-%s" % daystr, for_=identifier)
2.135 - page.span(self.format_time(endpoint or get_end_of_day(point, tzid), "long"), class_="endpoint")
2.136 + page.label(self.format_time(point.point, "long"), class_="timepoint day-%s" % daystr, for_=identifier)
2.137 + page.span(self.format_time(endpoint and endpoint.point or get_end_of_day(point.point, tzid), "long"), class_="endpoint")
2.138
2.139 def _slot_selector(self, value, identifier, slots):
2.140
2.141 @@ -708,17 +708,16 @@
2.142 else:
2.143 page.input(name="slot", type="checkbox", value=value, id=identifier, class_="newevent selector")
2.144
2.145 - def _empty_slot(self, point, endpoint, colspan, indicator):
2.146 + def _empty_slot(self, point, endpoint, colspan):
2.147
2.148 """
2.149 Show an empty slot cell for the given 'point' and 'endpoint', with the
2.150 - given 'colspan' and 'indicator' (0 as POINT or 1 as REPEATED)
2.151 - configuring the cell's appearance.
2.152 + given 'colspan' configuring the cell's appearance.
2.153 """
2.154
2.155 page = self.page
2.156 - page.td(class_="empty%s" % (indicator == POINT and " container" or ""), colspan=colspan)
2.157 - if indicator == POINT:
2.158 + page.td(class_="empty%s" % (point.indicator == Point.PRINCIPAL and " container" or ""), colspan=colspan)
2.159 + if point.indicator == Point.PRINCIPAL:
2.160 value, identifier = self._slot_value_and_identifier(point, endpoint)
2.161 page.label("Select/deselect period", class_="newevent popup", for_=identifier)
2.162 page.td.close()
2.163 @@ -738,7 +737,7 @@
2.164 'endpoint'.
2.165 """
2.166
2.167 - value = "%s-%s" % (format_datetime(point), endpoint and format_datetime(endpoint) or "")
2.168 + value = "%s-%s" % (format_datetime(point.point), endpoint and format_datetime(endpoint.point) or "")
2.169 identifier = "slot-%s" % value
2.170 return value, identifier
2.171