1.1 --- a/imip_manager.py Thu Jan 22 20:13:06 2015 +0100
1.2 +++ b/imip_manager.py Fri Jan 23 23:58:26 2015 +0100
1.3 @@ -35,7 +35,9 @@
1.4 from imiptools.dates import format_datetime, get_datetime, get_start_of_day, \
1.5 to_timezone
1.6 from imiptools.mail import Messenger
1.7 -from imiptools.period import have_conflict, get_slots, get_spans, partition_slots
1.8 +from imiptools.period import add_day_start_points, add_slots, convert_periods, \
1.9 + get_scale, have_conflict, get_slots, get_spans, \
1.10 + partition_by_day
1.11 from imiptools.profile import Preferences
1.12 from vCalendar import to_node
1.13 import markup
1.14 @@ -179,6 +181,7 @@
1.15 self.encoding = "utf-8"
1.16
1.17 self.store = imip_store.FileStore()
1.18 + self.objects = {}
1.19
1.20 try:
1.21 self.publisher = imip_store.FilePublisher()
1.22 @@ -189,12 +192,15 @@
1.23 return path_info.lstrip("/").split("/", 1)[0]
1.24
1.25 def _get_object(self, uid):
1.26 + if self.objects.has_key(uid):
1.27 + return self.objects[uid]
1.28 +
1.29 f = uid and self.store.get_event(self.user, uid) or None
1.30
1.31 if not f:
1.32 return None
1.33
1.34 - obj = parse_object(f, "utf-8")
1.35 + self.objects[uid] = obj = parse_object(f, "utf-8")
1.36
1.37 if not obj:
1.38 return None
1.39 @@ -210,6 +216,19 @@
1.40 self.requests = self.store.get_requests(self.user)
1.41 return self.requests
1.42
1.43 + def _get_request_summary(self):
1.44 + summary = []
1.45 + for uid in self._get_requests():
1.46 + obj = self._get_object(uid)
1.47 + if obj:
1.48 + details = self._get_details(obj)
1.49 + summary.append((
1.50 + get_value(details, "DTSTART"),
1.51 + get_value(details, "DTEND"),
1.52 + uid
1.53 + ))
1.54 + return summary
1.55 +
1.56 # Preference methods.
1.57
1.58 def get_user_locale(self):
1.59 @@ -222,6 +241,8 @@
1.60 self.preferences = Preferences(self.user)
1.61 return self.preferences
1.62
1.63 + # Prettyprinting of dates and times.
1.64 +
1.65 def format_date(self, dt, format):
1.66 return self._format_datetime(babel.dates.format_date, dt, format)
1.67
1.68 @@ -466,10 +487,12 @@
1.69 "Show the calendar for the current user."
1.70
1.71 self.new_page(title="Calendar")
1.72 + page = self.page
1.73 +
1.74 self.show_requests_on_page()
1.75
1.76 + request_summary = self._get_request_summary()
1.77 freebusy = self.store.get_freebusy(self.user)
1.78 - page = self.page
1.79
1.80 if not freebusy:
1.81 page.p("No events scheduled.")
1.82 @@ -493,29 +516,126 @@
1.83 # Requests could be listed and linked to their tentative positions in
1.84 # the calendar.
1.85
1.86 - slots = get_slots(freebusy)
1.87 - partitioned = partition_slots(slots, tzid)
1.88 - columns = max(map(lambda i: len(i[1]), slots)) + 1
1.89 + groups = []
1.90 + group_columns = []
1.91 + all_points = set()
1.92 +
1.93 + # Obtain time point information for each group of periods.
1.94 +
1.95 + for periods in [request_summary, freebusy]:
1.96 + periods = convert_periods(periods, tzid)
1.97 +
1.98 + # Get the time scale with start and end points.
1.99 +
1.100 + scale = get_scale(periods)
1.101 +
1.102 + # Get the time slots for the periods.
1.103 +
1.104 + slots = get_slots(scale)
1.105 +
1.106 + # Add start of day time points for multi-day periods.
1.107 +
1.108 + add_day_start_points(slots)
1.109 +
1.110 + # Record the slots and all time points employed.
1.111 +
1.112 + groups.append(slots)
1.113 + all_points.update([point for point, slot in slots])
1.114 +
1.115 + # Partition the groups into days.
1.116 +
1.117 + days = {}
1.118 + partitioned_groups = []
1.119 +
1.120 + for slots in groups:
1.121 +
1.122 + # Propagate time points to all groups of time slots.
1.123 +
1.124 + add_slots(slots, all_points)
1.125 +
1.126 + # Count the number of columns employed by the group.
1.127 +
1.128 + columns = 0
1.129 +
1.130 + # Partition the time slots by day.
1.131 +
1.132 + partitioned = {}
1.133 +
1.134 + for day, day_slots in partition_by_day(slots).items():
1.135 + columns = max(columns, max(map(lambda i: len(i[1]), day_slots)))
1.136 +
1.137 + if not days.has_key(day):
1.138 + days[day] = set()
1.139 +
1.140 + # Convert each partition to a mapping from points to active
1.141 + # periods.
1.142 +
1.143 + day_slots = dict(day_slots)
1.144 + partitioned[day] = day_slots
1.145 + days[day].update(day_slots.keys())
1.146 +
1.147 + if partitioned:
1.148 + group_columns.append(columns + 1)
1.149 + partitioned_groups.append(partitioned)
1.150
1.151 page.table(border=1, cellspacing=0, cellpadding=5)
1.152 + self.show_calendar_days(days, partitioned_groups, group_columns)
1.153 + page.table.close()
1.154
1.155 - for day, slots in partitioned:
1.156 - spans = get_spans(slots)
1.157 + def show_calendar_days(self, days, partitioned_groups, group_columns):
1.158 + page = self.page
1.159 +
1.160 + # Determine the number of columns required, the days providing time
1.161 + # slots.
1.162
1.163 + all_columns = sum(group_columns)
1.164 + all_days = days.items()
1.165 + all_days.sort()
1.166 +
1.167 + # Produce a heading and time points for each day.
1.168 +
1.169 + for day, points in all_days:
1.170 page.tr()
1.171 - page.th(class_="dayheading", colspan=columns)
1.172 + page.th(class_="dayheading", colspan=all_columns)
1.173 page.add(self.format_date(day, "full"))
1.174 page.th.close()
1.175 page.tr.close()
1.176
1.177 - for point, active in slots:
1.178 - dt = to_timezone(get_datetime(point), tzid)
1.179 - continuation = dt == get_start_of_day(dt)
1.180 + groups_for_day = [partitioned.get(day) for partitioned in partitioned_groups]
1.181 +
1.182 + self.show_calendar_points(points, groups_for_day, group_columns)
1.183 +
1.184 + def show_calendar_points(self, points, groups, group_columns):
1.185 + page = self.page
1.186 +
1.187 + # Produce a row for each time point.
1.188 +
1.189 + points = list(points)
1.190 + points.sort()
1.191 +
1.192 + for point in points:
1.193 + continuation = point == get_start_of_day(point)
1.194
1.195 - page.tr()
1.196 - page.th(class_="timeslot")
1.197 - page.add(self.format_time(dt, "long"))
1.198 - page.th.close()
1.199 + page.tr()
1.200 + page.th(class_="timeslot")
1.201 + page.add(self.format_time(point, "long"))
1.202 + page.th.close()
1.203 +
1.204 + # Obtain slots for the time point from each group.
1.205 +
1.206 + for columns, slots in zip(group_columns, groups):
1.207 + active = slots and slots.get(point)
1.208 +
1.209 + if not active:
1.210 + page.td(class_="empty", colspan=columns)
1.211 + page.td.close()
1.212 + continue
1.213 +
1.214 + slots = slots.items()
1.215 + slots.sort()
1.216 + spans = get_spans(slots)
1.217 +
1.218 + # Show a column for each active period.
1.219
1.220 for t in active:
1.221 if t:
1.222 @@ -533,9 +653,7 @@
1.223 page.td(class_="empty")
1.224 page.td.close()
1.225
1.226 - page.tr.close()
1.227 -
1.228 - page.table.close()
1.229 + page.tr.close()
1.230
1.231 def select_action(self):
1.232