1.1 --- a/EventAggregatorSupport.py Mon Dec 20 01:18:48 2010 +0100
1.2 +++ b/EventAggregatorSupport.py Sun Jan 16 03:10:14 2011 +0100
1.3 @@ -2,7 +2,7 @@
1.4 """
1.5 MoinMoin - EventAggregator library
1.6
1.7 - @copyright: 2008, 2009, 2010 by Paul Boddie <paul@boddie.org.uk>
1.8 + @copyright: 2008, 2009, 2010, 2011 by Paul Boddie <paul@boddie.org.uk>
1.9 @copyright: 2000-2004 Juergen Hermann <jh@web.de>,
1.10 2005-2008 MoinMoin:ThomasWaldmann.
1.11 @license: GNU GPL (v2 or later), see COPYING.txt for details.
1.12 @@ -943,6 +943,13 @@
1.13 return full_coverage, all_events
1.14
1.15 def getCoverageScale(coverage):
1.16 +
1.17 + """
1.18 + Return a scale for the given coverage so that the times involved are
1.19 + exposed. The scale consists of a list of non-overlapping timespans forming
1.20 + a contiguous period of time.
1.21 + """
1.22 +
1.23 times = set()
1.24 for timespan in coverage:
1.25 start, end = timespan.as_times()
1.26 @@ -960,6 +967,7 @@
1.27 else:
1.28 first = 0
1.29 start = time
1.30 +
1.31 return scale
1.32
1.33 # Date-related functions.
1.34 @@ -1193,6 +1201,14 @@
1.35 def as_date(self):
1.36 return Date(self.data[:3])
1.37
1.38 + def __cmp__(self, other):
1.39 + if isinstance(other, DateTime):
1.40 + self_utc = self.to_utc()
1.41 + other_utc = other.to_utc()
1.42 + if self_utc is not None and other_utc is not None:
1.43 + return cmp(self_utc.as_tuple(), other_utc.as_tuple())
1.44 + return Month.__cmp__(self, other)
1.45 +
1.46 def has_time(self):
1.47 return self.data[3] is not None and self.data[4] is not None
1.48
3.1 --- a/macros/EventAggregator.py Mon Dec 20 01:18:48 2010 +0100
3.2 +++ b/macros/EventAggregator.py Sun Jan 16 03:10:14 2011 +0100
3.3 @@ -2,7 +2,7 @@
3.4 """
3.5 MoinMoin - EventAggregator Macro
3.6
3.7 - @copyright: 2008, 2009, 2010 by Paul Boddie <paul@boddie.org.uk>
3.8 + @copyright: 2008, 2009, 2010, 2011 by Paul Boddie <paul@boddie.org.uk>
3.9 @copyright: 2000-2004 Juergen Hermann <jh@web.de>,
3.10 2005-2008 MoinMoin:ThomasWaldmann.
3.11 @license: GNU GPL (v2 or later), see COPYING.txt for details.
3.12 @@ -37,7 +37,7 @@
3.13 'mode' parameters are used to configure the links employed by the view.
3.14
3.15 The 'name_usage' parameter controls how names are shown on calendar mode
3.16 - events.
3.17 + events, such as how often labels are repeated.
3.18 """
3.19
3.20 self.page = page
3.21 @@ -191,9 +191,11 @@
3.22
3.23 # Pop-up descriptions of the downloadable calendars.
3.24
3.25 + get_label = self.mode == "day" and self.getFullDateLabel or self.getFullMonthLabel
3.26 +
3.27 calendar_period = "%s - %s" % (
3.28 - self.getFullMonthLabel(self.calendar_start),
3.29 - self.getFullMonthLabel(self.calendar_end)
3.30 + get_label(self.calendar_start),
3.31 + get_label(self.calendar_end)
3.32 )
3.33 raw_calendar_period = "%s - %s" % (self.raw_calendar_start, self.raw_calendar_end)
3.34
3.35 @@ -386,6 +388,59 @@
3.36
3.37 return "".join(output)
3.38
3.39 + # Common layout methods.
3.40 +
3.41 + def getEventStyle(self, colour_seed):
3.42 +
3.43 + "Generate colour style information using the given 'colour_seed'."
3.44 +
3.45 + bg = getColour(colour_seed)
3.46 + fg = getBlackOrWhite(bg)
3.47 + return "background-color: rgb(%d, %d, %d); color: rgb(%d, %d, %d);" % (bg + fg)
3.48 +
3.49 + def writeEventSummaryBox(self, event):
3.50 +
3.51 + "Return an event summary box linking to the given 'event'."
3.52 +
3.53 + page = self.page
3.54 + request = page.request
3.55 + fmt = page.formatter
3.56 +
3.57 + output = []
3.58 +
3.59 + event_page = event.getPage()
3.60 + event_details = event.getDetails()
3.61 + event_summary = event.getSummary(self.parent_name)
3.62 +
3.63 + is_ambiguous = event_details["start"].ambiguous() or event_details["end"].ambiguous()
3.64 + style = self.getEventStyle(event_summary)
3.65 +
3.66 + # The event box contains the summary, alongside
3.67 + # other elements.
3.68 +
3.69 + output.append(fmt.div(on=1, css_class="event-summary-box"))
3.70 + output.append(fmt.div(on=1, css_class="event-summary", style=style))
3.71 +
3.72 + if is_ambiguous:
3.73 + output.append(fmt.icon("/!\\"))
3.74 +
3.75 + output.append(event_page.linkToPage(request, event_summary))
3.76 + output.append(fmt.div(on=0))
3.77 +
3.78 + # Add a pop-up element for long summaries.
3.79 +
3.80 + output.append(fmt.div(on=1, css_class="event-summary-popup", style=style))
3.81 +
3.82 + if is_ambiguous:
3.83 + output.append(fmt.icon("/!\\"))
3.84 +
3.85 + output.append(event_page.linkToPage(request, event_summary))
3.86 + output.append(fmt.div(on=0))
3.87 +
3.88 + output.append(fmt.div(on=0))
3.89 +
3.90 + return "".join(output)
3.91 +
3.92 # Calendar layout methods.
3.93
3.94 def writeMonthTableHeading(self, year_month):
3.95 @@ -554,13 +609,8 @@
3.96 starts_today = event_details["start"] == date
3.97 ends_today = event_details["end"] == date
3.98 event_summary = event.getSummary(self.parent_name)
3.99 - is_ambiguous = event_details["start"].ambiguous() or event_details["end"].ambiguous()
3.100
3.101 - # Generate a colour for the event.
3.102 -
3.103 - bg = getColour(event_summary)
3.104 - fg = getBlackOrWhite(bg)
3.105 - style = ("background-color: rgb(%d, %d, %d); color: rgb(%d, %d, %d);" % (bg + fg))
3.106 + style = self.getEventStyle(event_summary)
3.107
3.108 # Determine if the event name should be shown.
3.109
3.110 @@ -676,34 +726,9 @@
3.111 # Output the event.
3.112
3.113 if starts_today and ends_today or not hide_text:
3.114 -
3.115 - # The event box contains the summary, alongside
3.116 - # other elements.
3.117 -
3.118 - output.append(fmt.div(on=1, css_class="event-summary-box"))
3.119 - output.append(fmt.div(on=1, css_class="event-summary", style=style))
3.120 -
3.121 - if is_ambiguous:
3.122 - output.append(fmt.icon("/!\\"))
3.123 -
3.124 - output.append(event_page.linkToPage(request, event_summary))
3.125 - output.append(fmt.div(on=0))
3.126 + output.append(self.writeEventSummaryBox(event))
3.127
3.128 - # Add a pop-up element for long summaries.
3.129 -
3.130 - output.append(fmt.div(on=1, css_class="event-summary-popup", style=style))
3.131 -
3.132 - if is_ambiguous:
3.133 - output.append(fmt.icon("/!\\"))
3.134 -
3.135 - output.append(event_page.linkToPage(request, event_summary))
3.136 - output.append(fmt.div(on=0))
3.137 -
3.138 - output.append(fmt.div(on=0))
3.139 -
3.140 - # Output end of day content.
3.141 -
3.142 - output.append(fmt.div(on=0))
3.143 + output.append(fmt.table_cell(on=0))
3.144
3.145 # Output end of day gap.
3.146
3.147 @@ -711,10 +736,6 @@
3.148 output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-end-gap"}))
3.149 output.append(fmt.table_cell(on=0))
3.150
3.151 - # End of day.
3.152 -
3.153 - output.append(fmt.table_cell(on=0))
3.154 -
3.155 # End of set.
3.156
3.157 output.append(fmt.table_row(on=0))
3.158 @@ -744,7 +765,7 @@
3.159
3.160 # Day layout methods.
3.161
3.162 - def writeDayHeading(self, date):
3.163 + def writeDayHeading(self, date, colspan=1):
3.164 page = self.page
3.165 request = page.request
3.166 fmt = page.formatter
3.167 @@ -754,7 +775,7 @@
3.168 output = []
3.169 output.append(fmt.table_row(on=1))
3.170
3.171 - output.append(fmt.table_cell(on=1, attrs={"class" : "event-weekday-heading"}))
3.172 + output.append(fmt.table_cell(on=1, attrs={"class" : "event-weekday-heading", "colspan" : str(colspan)}))
3.173 output.append(fmt.text(full_date_label))
3.174 output.append(fmt.table_cell(on=0))
3.175
3.176 @@ -787,33 +808,118 @@
3.177 # determine whether it provides content for each period.
3.178
3.179 scale = EventAggregatorSupport.getCoverageScale(full_coverage)
3.180 + period = None
3.181
3.182 for period in scale:
3.183 - start = period.start
3.184 +
3.185 + # Ignore timespans before this day.
3.186 +
3.187 + if not date in period:
3.188 + continue
3.189 +
3.190 + # Write an empty heading for the start of the day where the first
3.191 + # applicable timespan starts before this day.
3.192
3.193 - output.append(fmt.table_row(on=1))
3.194 - output.append(fmt.table_cell(on=1, attrs={"class" : "event-weekday-heading"}))
3.195 - output.append(fmt.text(str(start)))
3.196 - output.append(fmt.table_cell(on=0))
3.197 + if period.start < date:
3.198 + output.append(fmt.table_row(on=1))
3.199 + output.append(self.writeDayScaleHeading(""))
3.200 +
3.201 + # Otherwise, write a heading describing the time.
3.202 +
3.203 + else:
3.204 + output.append(fmt.table_row(on=1))
3.205 + output.append(self.writeDayScaleHeading(str(period.start)))
3.206
3.207 # Visit each slot corresponding to a location (or no location).
3.208
3.209 - #for location in locations:
3.210 + for location in locations:
3.211
3.212 - # # Visit each coverage span, presenting the events in the span.
3.213 + # Visit each coverage span, presenting the events in the span.
3.214 +
3.215 + for events in day_slots[location]:
3.216
3.217 - # for events in day_slots[location]:
3.218 + # Add a spacer.
3.219 +
3.220 + output.append(self.writeDaySpacer())
3.221
3.222 - # # Output each set.
3.223 + # Output each set's contribution to this period.
3.224
3.225 - # output.append(self.writeDaySlot(day, events))
3.226 + output.append(self.writeDaySlot(period, events))
3.227
3.228 output.append(fmt.table_row(on=0))
3.229
3.230 + # Write a final time heading if the last period ends in the current day.
3.231 +
3.232 + if period is not None:
3.233 + if period.end == date:
3.234 + output.append(fmt.table_row(on=1))
3.235 + output.append(self.writeDayScaleHeading(str(period.end)))
3.236 +
3.237 + for location in locations:
3.238 + for events in day_slots[location]:
3.239 + output.append(self.writeDaySpacer())
3.240 + output.append(self.writeEmptyDaySlot())
3.241 +
3.242 + output.append(fmt.table_row(on=0))
3.243 +
3.244 + return "".join(output)
3.245 +
3.246 + def writeDayScaleHeading(self, heading):
3.247 + page = self.page
3.248 + fmt = page.formatter
3.249 +
3.250 + output = []
3.251 + output.append(fmt.table_cell(on=1, attrs={"class" : "event-scale-heading"}))
3.252 + output.append(fmt.text(heading))
3.253 + output.append(fmt.table_cell(on=0))
3.254 +
3.255 return "".join(output)
3.256
3.257 - def writeDaySlot(self, date, events):
3.258 - pass
3.259 + def writeDaySlot(self, period, events):
3.260 + page = self.page
3.261 + fmt = page.formatter
3.262 +
3.263 + output = []
3.264 +
3.265 + for event in events:
3.266 + if period not in event:
3.267 + continue
3.268 +
3.269 + event_summary = event.getSummary(self.parent_name)
3.270 + style = self.getEventStyle(event_summary)
3.271 +
3.272 + output.append(fmt.table_cell(on=1, attrs={
3.273 + "class" : "event-timespan-content event-timespan-busy",
3.274 + "style" : style}))
3.275 + output.append(self.writeEventSummaryBox(event))
3.276 + output.append(fmt.table_cell(on=0))
3.277 + break
3.278 +
3.279 + else:
3.280 + output.append(self.writeEmptyDaySlot())
3.281 +
3.282 + return "".join(output)
3.283 +
3.284 + def writeEmptyDaySlot(self):
3.285 + page = self.page
3.286 + fmt = page.formatter
3.287 +
3.288 + output = []
3.289 +
3.290 + output.append(fmt.table_cell(on=1,
3.291 + attrs={"class" : "event-timespan-content event-timespan-empty"}))
3.292 + output.append(fmt.table_cell(on=0))
3.293 +
3.294 + return "".join(output)
3.295 +
3.296 + def writeDaySpacer(self):
3.297 + page = self.page
3.298 + fmt = page.formatter
3.299 +
3.300 + output = []
3.301 + output.append(fmt.table_cell(on=1, attrs={"class" : "event-timespan-spacer"}))
3.302 + output.append(fmt.table_cell(on=0))
3.303 + return "".join(output)
3.304
3.305 # HTML-related functions.
3.306
3.307 @@ -925,6 +1031,8 @@
3.308
3.309 mode = EventAggregatorSupport.getQualifiedParameter(request, calendar_name, "mode", mode or "calendar")
3.310
3.311 + # Different modes require different levels of precision.
3.312 +
3.313 if mode == "day":
3.314 get_date = EventAggregatorSupport.getParameterDate
3.315 get_form_date = EventAggregatorSupport.getFormDate
3.316 @@ -1215,7 +1323,12 @@
3.317 full_coverage, day_slots = EventAggregatorSupport.getCoverage(
3.318 date, date, shown_events.get(date, []))
3.319
3.320 - output.append(view.writeDayHeading(date))
3.321 + # Work out how many columns the day title will need.
3.322 + # Includer spacers before each event column.
3.323 +
3.324 + colspan = sum(map(len, day_slots.values())) * 2 + 1
3.325 +
3.326 + output.append(view.writeDayHeading(date, colspan))
3.327
3.328 # Either generate empty days...
3.329