1.1 --- a/macros/EventAggregator.py Sun Jan 22 00:45:28 2012 +0100
1.2 +++ b/macros/EventAggregator.py Sun Feb 05 01:29:45 2012 +0100
1.3 @@ -9,1301 +9,10 @@
1.4 """
1.5
1.6 from MoinMoin import wikiutil
1.7 -from MoinMoin.action import AttachFile
1.8 -from MoinMoin.Page import Page
1.9 from EventAggregatorSupport import *
1.10 -import calendar
1.11
1.12 Dependencies = ['pages']
1.13
1.14 -# Abstractions.
1.15 -
1.16 -class View:
1.17 -
1.18 - "A view of the event calendar."
1.19 -
1.20 - def __init__(self, page, calendar_name, raw_calendar_start, raw_calendar_end,
1.21 - original_calendar_start, original_calendar_end, calendar_start, calendar_end,
1.22 - first, last, category_names, remote_sources, template_name, parent_name, mode,
1.23 - resolution, name_usage, map_name):
1.24 -
1.25 - """
1.26 - Initialise the view with the current 'page', a 'calendar_name' (which
1.27 - may be None), the 'raw_calendar_start' and 'raw_calendar_end' (which
1.28 - are the actual start and end values provided by the request), the
1.29 - calculated 'original_calendar_start' and 'original_calendar_end' (which
1.30 - are the result of calculating the calendar's limits from the raw start
1.31 - and end values), and the requested, calculated 'calendar_start' and
1.32 - 'calendar_end' (which may involve different start and end values due to
1.33 - navigation in the user interface), along with the 'first' and 'last'
1.34 - months of event coverage.
1.35 -
1.36 - The additional 'category_names', 'remote_sources', 'template_name',
1.37 - 'parent_name' and 'mode' parameters are used to configure the links
1.38 - employed by the view.
1.39 -
1.40 - The 'resolution' affects the view for certain modes and is also used to
1.41 - parameterise links.
1.42 -
1.43 - The 'name_usage' parameter controls how names are shown on calendar mode
1.44 - events, such as how often labels are repeated.
1.45 -
1.46 - The 'map_name' parameter provides the name of a map to be used in the
1.47 - map mode.
1.48 - """
1.49 -
1.50 - self.page = page
1.51 - self.calendar_name = calendar_name
1.52 - self.raw_calendar_start = raw_calendar_start
1.53 - self.raw_calendar_end = raw_calendar_end
1.54 - self.original_calendar_start = original_calendar_start
1.55 - self.original_calendar_end = original_calendar_end
1.56 - self.calendar_start = calendar_start
1.57 - self.calendar_end = calendar_end
1.58 - self.template_name = template_name
1.59 - self.parent_name = parent_name
1.60 - self.mode = mode
1.61 - self.resolution = resolution
1.62 - self.name_usage = name_usage
1.63 - self.map_name = map_name
1.64 -
1.65 - self.category_name_parameters = "&".join([("category=%s" % name) for name in category_names])
1.66 - self.remote_source_parameters = "&".join([("source=%s" % source) for source in remote_sources])
1.67 -
1.68 - # Calculate the duration in terms of the highest common unit of time.
1.69 -
1.70 - self.duration = last - first
1.71 -
1.72 - if self.calendar_name is not None:
1.73 -
1.74 - # Store the view parameters.
1.75 -
1.76 - self.previous_start = first.previous()
1.77 - self.next_start = first.next()
1.78 - self.previous_end = last.previous()
1.79 - self.next_end = last.next()
1.80 -
1.81 - self.previous_set_start = first.update(-self.duration)
1.82 - self.next_set_start = first.update(self.duration)
1.83 - self.previous_set_end = last.update(-self.duration)
1.84 - self.next_set_end = last.update(self.duration)
1.85 -
1.86 - def getIdentifier(self):
1.87 -
1.88 - "Return a unique identifier to be used to refer to this view."
1.89 -
1.90 - # NOTE: Nasty hack to get a unique identifier if no name is given.
1.91 -
1.92 - return self.calendar_name or str(id(self))
1.93 -
1.94 - def getQualifiedParameterName(self, argname):
1.95 -
1.96 - "Return the 'argname' qualified using the calendar name."
1.97 -
1.98 - return getQualifiedParameterName(self.calendar_name, argname)
1.99 -
1.100 - def getDateQueryString(self, argname, date, prefix=1):
1.101 -
1.102 - """
1.103 - Return a query string fragment for the given 'argname', referring to the
1.104 - month given by the specified 'year_month' object, appropriate for this
1.105 - calendar.
1.106 -
1.107 - If 'prefix' is specified and set to a false value, the parameters in the
1.108 - query string will not be calendar-specific, but could be used with the
1.109 - summary action.
1.110 - """
1.111 -
1.112 - suffixes = ["year", "month", "day"]
1.113 -
1.114 - if date is not None:
1.115 - args = []
1.116 - for suffix, value in zip(suffixes, date.as_tuple()):
1.117 - suffixed_argname = "%s-%s" % (argname, suffix)
1.118 - if prefix:
1.119 - suffixed_argname = self.getQualifiedParameterName(suffixed_argname)
1.120 - args.append("%s=%s" % (suffixed_argname, value))
1.121 - return "&".join(args)
1.122 - else:
1.123 - return ""
1.124 -
1.125 - def getRawDateQueryString(self, argname, date, prefix=1):
1.126 -
1.127 - """
1.128 - Return a query string fragment for the given 'argname', referring to the
1.129 - date given by the specified 'date' value, appropriate for this
1.130 - calendar.
1.131 -
1.132 - If 'prefix' is specified and set to a false value, the parameters in the
1.133 - query string will not be calendar-specific, but could be used with the
1.134 - summary action.
1.135 - """
1.136 -
1.137 - if date is not None:
1.138 - if prefix:
1.139 - argname = self.getQualifiedParameterName(argname)
1.140 - return "%s=%s" % (argname, wikiutil.url_quote_plus(date))
1.141 - else:
1.142 - return ""
1.143 -
1.144 - def getNavigationLink(self, start, end, mode=None, resolution=None):
1.145 -
1.146 - """
1.147 - Return a query string fragment for navigation to a view showing months
1.148 - from 'start' to 'end' inclusive, with the optional 'mode' indicating the
1.149 - view style and the optional 'resolution' indicating the resolution of a
1.150 - view, if configurable.
1.151 - """
1.152 -
1.153 - return "%s&%s&%s=%s&%s=%s" % (
1.154 - self.getRawDateQueryString("start", start),
1.155 - self.getRawDateQueryString("end", end),
1.156 - self.getQualifiedParameterName("mode"), mode or self.mode,
1.157 - self.getQualifiedParameterName("resolution"), resolution or self.resolution
1.158 - )
1.159 -
1.160 - def getNewEventLink(self, start):
1.161 -
1.162 - """
1.163 - Return a query string activating the new event form, incorporating the
1.164 - calendar parameters, specialising the form for the given 'start' date or
1.165 - month.
1.166 - """
1.167 -
1.168 - if start is not None:
1.169 - details = start.as_tuple()
1.170 - pairs = zip(["start-year=%d", "start-month=%d", "start-day=%d"], details)
1.171 - args = [(param % value) for (param, value) in pairs]
1.172 - args = "&".join(args)
1.173 - else:
1.174 - args = ""
1.175 -
1.176 - # Prepare navigation details for the calendar shown with the new event
1.177 - # form.
1.178 -
1.179 - navigation_link = self.getNavigationLink(
1.180 - self.calendar_start, self.calendar_end
1.181 - )
1.182 -
1.183 - return "action=EventAggregatorNewEvent&%s&%s&template=%s&parent=%s&%s" % (
1.184 - args, self.category_name_parameters, self.template_name, self.parent_name or "",
1.185 - navigation_link)
1.186 -
1.187 - def getFullDateLabel(self, date):
1.188 - page = self.page
1.189 - request = page.request
1.190 - return getFullDateLabel(request, date)
1.191 -
1.192 - def getFullMonthLabel(self, year_month):
1.193 - page = self.page
1.194 - request = page.request
1.195 - return getFullMonthLabel(request, year_month)
1.196 -
1.197 - def getFullLabel(self, arg):
1.198 - return self.resolution == "date" and self.getFullDateLabel(arg) or self.getFullMonthLabel(arg)
1.199 -
1.200 - def _getCalendarPeriod(self, start_label, end_label, default_label):
1.201 - output = []
1.202 - if start_label:
1.203 - output.append(start_label)
1.204 - if end_label and start_label != end_label:
1.205 - if output:
1.206 - output.append(" - ")
1.207 - output.append(end_label)
1.208 - return "".join(output) or default_label
1.209 -
1.210 - def getCalendarPeriod(self):
1.211 - _ = self.page.request.getText
1.212 - return self._getCalendarPeriod(
1.213 - self.calendar_start and self.getFullLabel(self.calendar_start),
1.214 - self.calendar_end and self.getFullLabel(self.calendar_end),
1.215 - _("All events")
1.216 - )
1.217 -
1.218 - def getOriginalCalendarPeriod(self):
1.219 - _ = self.page.request.getText
1.220 - return self._getCalendarPeriod(
1.221 - self.original_calendar_start and self.getFullLabel(self.original_calendar_start),
1.222 - self.original_calendar_end and self.getFullLabel(self.original_calendar_end),
1.223 - _("All events")
1.224 - )
1.225 -
1.226 - def getRawCalendarPeriod(self):
1.227 - _ = self.page.request.getText
1.228 - return self._getCalendarPeriod(
1.229 - self.raw_calendar_start,
1.230 - self.raw_calendar_end,
1.231 - _("No period specified")
1.232 - )
1.233 -
1.234 - def writeDownloadControls(self):
1.235 -
1.236 - """
1.237 - Return a representation of the download controls, featuring links for
1.238 - view, calendar and customised downloads and subscriptions.
1.239 - """
1.240 -
1.241 - page = self.page
1.242 - request = page.request
1.243 - fmt = page.formatter
1.244 - _ = request.getText
1.245 -
1.246 - output = []
1.247 -
1.248 - # The full URL is needed for webcal links.
1.249 -
1.250 - full_url = "%s%s" % (request.getBaseURL(), getPathInfo(request))
1.251 -
1.252 - # Generate the links.
1.253 -
1.254 - download_dialogue_link = "action=EventAggregatorSummary&parent=%s&resolution=%s&%s&%s" % (
1.255 - self.parent_name or "",
1.256 - self.resolution,
1.257 - self.category_name_parameters,
1.258 - self.remote_source_parameters
1.259 - )
1.260 - download_all_link = download_dialogue_link + "&doit=1"
1.261 - download_link = download_all_link + ("&%s&%s" % (
1.262 - self.getDateQueryString("start", self.calendar_start, prefix=0),
1.263 - self.getDateQueryString("end", self.calendar_end, prefix=0)
1.264 - ))
1.265 -
1.266 - # Subscription links just explicitly select the RSS format.
1.267 -
1.268 - subscribe_dialogue_link = download_dialogue_link + "&format=RSS"
1.269 - subscribe_all_link = download_all_link + "&format=RSS"
1.270 - subscribe_link = download_link + "&format=RSS"
1.271 -
1.272 - # Adjust the "download all" and "subscribe all" links if the calendar
1.273 - # has an inherent period associated with it.
1.274 -
1.275 - period_limits = []
1.276 -
1.277 - if self.raw_calendar_start:
1.278 - period_limits.append("&%s" %
1.279 - self.getRawDateQueryString("start", self.raw_calendar_start, prefix=0)
1.280 - )
1.281 - if self.raw_calendar_end:
1.282 - period_limits.append("&%s" %
1.283 - self.getRawDateQueryString("end", self.raw_calendar_end, prefix=0)
1.284 - )
1.285 -
1.286 - period_limits = "".join(period_limits)
1.287 -
1.288 - download_dialogue_link += period_limits
1.289 - download_all_link += period_limits
1.290 - subscribe_dialogue_link += period_limits
1.291 - subscribe_all_link += period_limits
1.292 -
1.293 - # Pop-up descriptions of the downloadable calendars.
1.294 -
1.295 - calendar_period = self.getCalendarPeriod()
1.296 - original_calendar_period = self.getOriginalCalendarPeriod()
1.297 - raw_calendar_period = self.getRawCalendarPeriod()
1.298 -
1.299 - # Write the controls.
1.300 -
1.301 - # Download controls.
1.302 -
1.303 - output.append(fmt.div(on=1, css_class="event-download-controls"))
1.304 -
1.305 - output.append(fmt.span(on=1, css_class="event-download"))
1.306 - output.append(fmt.text(_("Download...")))
1.307 - output.append(fmt.div(on=1, css_class="event-download-popup"))
1.308 -
1.309 - output.append(fmt.div(on=1, css_class="event-download-item"))
1.310 - output.append(fmt.span(on=1, css_class="event-download-types"))
1.311 - output.append(fmt.span(on=1, css_class="event-download-webcal"))
1.312 - output.append(linkToResource(full_url.replace("http", "webcal", 1), request, _("webcal"), download_link))
1.313 - output.append(fmt.span(on=0))
1.314 - output.append(fmt.span(on=1, css_class="event-download-http"))
1.315 - output.append(linkToPage(request, page, _("http"), download_link))
1.316 - output.append(fmt.span(on=0))
1.317 - output.append(fmt.span(on=0)) # end types
1.318 - output.append(fmt.span(on=1, css_class="event-download-label"))
1.319 - output.append(fmt.text(_("Download this view")))
1.320 - output.append(fmt.span(on=0)) # end label
1.321 - output.append(fmt.span(on=1, css_class="event-download-period"))
1.322 - output.append(fmt.text(calendar_period))
1.323 - output.append(fmt.span(on=0))
1.324 - output.append(fmt.div(on=0))
1.325 -
1.326 - output.append(fmt.div(on=1, css_class="event-download-item"))
1.327 - output.append(fmt.span(on=1, css_class="event-download-types"))
1.328 - output.append(fmt.span(on=1, css_class="event-download-webcal"))
1.329 - output.append(linkToResource(full_url.replace("http", "webcal", 1), request, _("webcal"), download_all_link))
1.330 - output.append(fmt.span(on=0))
1.331 - output.append(fmt.span(on=1, css_class="event-download-http"))
1.332 - output.append(linkToPage(request, page, _("http"), download_all_link))
1.333 - output.append(fmt.span(on=0))
1.334 - output.append(fmt.span(on=0)) # end types
1.335 - output.append(fmt.span(on=1, css_class="event-download-label"))
1.336 - output.append(fmt.text(_("Download this calendar")))
1.337 - output.append(fmt.span(on=0)) # end label
1.338 - output.append(fmt.span(on=1, css_class="event-download-period"))
1.339 - output.append(fmt.text(original_calendar_period))
1.340 - output.append(fmt.span(on=0))
1.341 - output.append(fmt.span(on=1, css_class="event-download-period-raw"))
1.342 - output.append(fmt.text(raw_calendar_period))
1.343 - output.append(fmt.span(on=0))
1.344 - output.append(fmt.div(on=0))
1.345 -
1.346 - output.append(fmt.div(on=1, css_class="event-download-item"))
1.347 - output.append(fmt.span(on=1, css_class="event-download-link"))
1.348 - output.append(linkToPage(request, page, _("Edit download options..."), download_dialogue_link))
1.349 - output.append(fmt.span(on=0)) # end label
1.350 - output.append(fmt.div(on=0))
1.351 -
1.352 - output.append(fmt.div(on=0)) # end of pop-up
1.353 - output.append(fmt.span(on=0)) # end of download
1.354 -
1.355 - # Subscription controls.
1.356 -
1.357 - output.append(fmt.span(on=1, css_class="event-download"))
1.358 - output.append(fmt.text(_("Subscribe...")))
1.359 - output.append(fmt.div(on=1, css_class="event-download-popup"))
1.360 -
1.361 - output.append(fmt.div(on=1, css_class="event-download-item"))
1.362 - output.append(fmt.span(on=1, css_class="event-download-label"))
1.363 - output.append(linkToPage(request, page, _("Subscribe to this view"), subscribe_link))
1.364 - output.append(fmt.span(on=0)) # end label
1.365 - output.append(fmt.span(on=1, css_class="event-download-period"))
1.366 - output.append(fmt.text(calendar_period))
1.367 - output.append(fmt.span(on=0))
1.368 - output.append(fmt.div(on=0))
1.369 -
1.370 - output.append(fmt.div(on=1, css_class="event-download-item"))
1.371 - output.append(fmt.span(on=1, css_class="event-download-label"))
1.372 - output.append(linkToPage(request, page, _("Subscribe to this calendar"), subscribe_all_link))
1.373 - output.append(fmt.span(on=0)) # end label
1.374 - output.append(fmt.span(on=1, css_class="event-download-period"))
1.375 - output.append(fmt.text(original_calendar_period))
1.376 - output.append(fmt.span(on=0))
1.377 - output.append(fmt.span(on=1, css_class="event-download-period-raw"))
1.378 - output.append(fmt.text(raw_calendar_period))
1.379 - output.append(fmt.span(on=0))
1.380 - output.append(fmt.div(on=0))
1.381 -
1.382 - output.append(fmt.div(on=1, css_class="event-download-item"))
1.383 - output.append(fmt.span(on=1, css_class="event-download-link"))
1.384 - output.append(linkToPage(request, page, _("Edit subscription options..."), subscribe_dialogue_link))
1.385 - output.append(fmt.span(on=0)) # end label
1.386 - output.append(fmt.div(on=0))
1.387 -
1.388 - output.append(fmt.div(on=0)) # end of pop-up
1.389 - output.append(fmt.span(on=0)) # end of download
1.390 -
1.391 - output.append(fmt.div(on=0)) # end of controls
1.392 -
1.393 - return "".join(output)
1.394 -
1.395 - def writeViewControls(self):
1.396 -
1.397 - """
1.398 - Return a representation of the view mode controls, permitting viewing of
1.399 - aggregated events in calendar, list or table form.
1.400 - """
1.401 -
1.402 - page = self.page
1.403 - request = page.request
1.404 - fmt = page.formatter
1.405 - _ = request.getText
1.406 -
1.407 - output = []
1.408 -
1.409 - start = self.calendar_start
1.410 - end = self.calendar_end
1.411 -
1.412 - help_page = Page(request, "HelpOnEventAggregator")
1.413 - calendar_link = self.getNavigationLink(start and start.as_month(), end and end.as_month(), "calendar", "month")
1.414 - list_link = self.getNavigationLink(start, end, "list")
1.415 - table_link = self.getNavigationLink(start, end, "table")
1.416 - map_link = self.getNavigationLink(start, end, "map")
1.417 - new_event_link = self.getNewEventLink(start)
1.418 -
1.419 - # Write the controls.
1.420 -
1.421 - output.append(fmt.div(on=1, css_class="event-view-controls"))
1.422 -
1.423 - output.append(fmt.span(on=1, css_class="event-view"))
1.424 - output.append(linkToPage(request, help_page, _("Help")))
1.425 - output.append(fmt.span(on=0))
1.426 -
1.427 - output.append(fmt.span(on=1, css_class="event-view"))
1.428 - output.append(linkToPage(request, page, _("New event"), new_event_link))
1.429 - output.append(fmt.span(on=0))
1.430 -
1.431 - if self.mode != "calendar":
1.432 - output.append(fmt.span(on=1, css_class="event-view"))
1.433 - output.append(linkToPage(request, page, _("View as calendar"), calendar_link))
1.434 - output.append(fmt.span(on=0))
1.435 -
1.436 - if self.mode != "list":
1.437 - output.append(fmt.span(on=1, css_class="event-view"))
1.438 - output.append(linkToPage(request, page, _("View as list"), list_link))
1.439 - output.append(fmt.span(on=0))
1.440 -
1.441 - if self.mode != "table":
1.442 - output.append(fmt.span(on=1, css_class="event-view"))
1.443 - output.append(linkToPage(request, page, _("View as table"), table_link))
1.444 - output.append(fmt.span(on=0))
1.445 -
1.446 - if self.mode != "map" and self.map_name is not None:
1.447 - output.append(fmt.span(on=1, css_class="event-view"))
1.448 - output.append(linkToPage(request, page, _("View as map"), map_link))
1.449 - output.append(fmt.span(on=0))
1.450 -
1.451 - output.append(fmt.div(on=0))
1.452 -
1.453 - return "".join(output)
1.454 -
1.455 - def writeMapHeading(self):
1.456 -
1.457 - """
1.458 - Return the calendar heading for the current calendar, providing links
1.459 - permitting navigation to other periods.
1.460 - """
1.461 -
1.462 - label = self.getCalendarPeriod()
1.463 -
1.464 - if self.raw_calendar_start is None or self.raw_calendar_end is None:
1.465 - fmt = self.page.formatter
1.466 - output = []
1.467 - output.append(fmt.span(on=1))
1.468 - output.append(fmt.text(label))
1.469 - output.append(fmt.span(on=0))
1.470 - return "".join(output)
1.471 - else:
1.472 - return self._writeCalendarHeading(label, self.calendar_start, self.calendar_end)
1.473 -
1.474 - def writeDateHeading(self, date):
1.475 - if isinstance(date, Date):
1.476 - return self.writeDayHeading(date)
1.477 - else:
1.478 - return self.writeMonthHeading(date)
1.479 -
1.480 - def writeMonthHeading(self, year_month):
1.481 -
1.482 - """
1.483 - Return the calendar heading for the given 'year_month' (a Month object)
1.484 - providing links permitting navigation to other months.
1.485 - """
1.486 -
1.487 - full_month_label = self.getFullMonthLabel(year_month)
1.488 - end_month = year_month.update(self.duration - 1)
1.489 - return self._writeCalendarHeading(full_month_label, year_month, end_month)
1.490 -
1.491 - def writeDayHeading(self, date):
1.492 -
1.493 - """
1.494 - Return the calendar heading for the given 'date' (a Date object)
1.495 - providing links permitting navigation to other dates.
1.496 - """
1.497 -
1.498 - full_date_label = self.getFullDateLabel(date)
1.499 - end_date = date.update(self.duration - 1)
1.500 - return self._writeCalendarHeading(full_date_label, date, end_date)
1.501 -
1.502 - def _writeCalendarHeading(self, label, start, end):
1.503 -
1.504 - """
1.505 - Write a calendar heading providing links permitting navigation to other
1.506 - periods, using the given 'label' along with the 'start' and 'end' dates
1.507 - to provide a link to a particular period.
1.508 - """
1.509 -
1.510 - page = self.page
1.511 - request = page.request
1.512 - fmt = page.formatter
1.513 - _ = request.getText
1.514 -
1.515 - output = []
1.516 -
1.517 - # Prepare navigation links.
1.518 -
1.519 - if self.calendar_name is not None:
1.520 - calendar_name = self.calendar_name
1.521 -
1.522 - # Links to the previous set of months and to a calendar shifted
1.523 - # back one month.
1.524 -
1.525 - previous_set_link = self.getNavigationLink(
1.526 - self.previous_set_start, self.previous_set_end
1.527 - )
1.528 - previous_link = self.getNavigationLink(
1.529 - self.previous_start, self.previous_end
1.530 - )
1.531 -
1.532 - # Links to the next set of months and to a calendar shifted
1.533 - # forward one month.
1.534 -
1.535 - next_set_link = self.getNavigationLink(
1.536 - self.next_set_start, self.next_set_end
1.537 - )
1.538 - next_link = self.getNavigationLink(
1.539 - self.next_start, self.next_end
1.540 - )
1.541 -
1.542 - # A link leading to this date being at the top of the calendar.
1.543 -
1.544 - date_link = self.getNavigationLink(start, end)
1.545 -
1.546 - output.append(fmt.span(on=1, css_class="previous"))
1.547 - output.append(linkToPage(request, page, "<<", previous_set_link))
1.548 - output.append(fmt.text(" "))
1.549 - output.append(linkToPage(request, page, "<", previous_link))
1.550 - output.append(fmt.span(on=0))
1.551 -
1.552 - output.append(fmt.span(on=1, css_class="next"))
1.553 - output.append(linkToPage(request, page, ">", next_link))
1.554 - output.append(fmt.text(" "))
1.555 - output.append(linkToPage(request, page, ">>", next_set_link))
1.556 - output.append(fmt.span(on=0))
1.557 -
1.558 - output.append(linkToPage(request, page, label, date_link))
1.559 -
1.560 - else:
1.561 - output.append(fmt.span(on=1))
1.562 - output.append(fmt.text(label))
1.563 - output.append(fmt.span(on=0))
1.564 -
1.565 - return "".join(output)
1.566 -
1.567 - def writeDayNumberHeading(self, date, busy):
1.568 -
1.569 - """
1.570 - Return a link for the given 'date' which will activate the new event
1.571 - action for the given day. If 'busy' is given as a true value, the
1.572 - heading will be marked as busy.
1.573 - """
1.574 -
1.575 - page = self.page
1.576 - request = page.request
1.577 - fmt = page.formatter
1.578 - _ = request.getText
1.579 -
1.580 - output = []
1.581 -
1.582 - year, month, day = date.as_tuple()
1.583 - new_event_link = self.getNewEventLink(date)
1.584 -
1.585 - # Prepare a link to the day view for this day.
1.586 -
1.587 - day_view_link = self.getNavigationLink(date, date, "day", "date")
1.588 -
1.589 - # Output the heading class.
1.590 -
1.591 - output.append(
1.592 - fmt.table_cell(on=1, attrs={
1.593 - "class" : "event-day-heading event-day-%s" % (busy and "busy" or "empty"),
1.594 - "colspan" : "3"
1.595 - }))
1.596 -
1.597 - # Output the number and pop-up menu.
1.598 -
1.599 - output.append(fmt.div(on=1, css_class="event-day-box"))
1.600 -
1.601 - output.append(fmt.span(on=1, css_class="event-day-number-popup"))
1.602 - output.append(fmt.span(on=1, css_class="event-day-number-link"))
1.603 - output.append(linkToPage(request, page, _("View day"), day_view_link))
1.604 - output.append(fmt.span(on=0))
1.605 - output.append(fmt.span(on=1, css_class="event-day-number-link"))
1.606 - output.append(linkToPage(request, page, _("New event"), new_event_link))
1.607 - output.append(fmt.span(on=0))
1.608 - output.append(fmt.span(on=0))
1.609 -
1.610 - output.append(fmt.span(on=1, css_class="event-day-number"))
1.611 - output.append(fmt.text(unicode(day)))
1.612 - output.append(fmt.span(on=0))
1.613 -
1.614 - output.append(fmt.div(on=0))
1.615 -
1.616 - # End of heading.
1.617 -
1.618 - output.append(fmt.table_cell(on=0))
1.619 -
1.620 - return "".join(output)
1.621 -
1.622 - # Common layout methods.
1.623 -
1.624 - def getEventStyle(self, colour_seed):
1.625 -
1.626 - "Generate colour style information using the given 'colour_seed'."
1.627 -
1.628 - bg = getColour(colour_seed)
1.629 - fg = getBlackOrWhite(bg)
1.630 - return "background-color: rgb(%d, %d, %d); color: rgb(%d, %d, %d);" % (bg + fg)
1.631 -
1.632 - def writeEventSummaryBox(self, event):
1.633 -
1.634 - "Return an event summary box linking to the given 'event'."
1.635 -
1.636 - page = self.page
1.637 - request = page.request
1.638 - fmt = page.formatter
1.639 -
1.640 - output = []
1.641 -
1.642 - event_details = event.getDetails()
1.643 - event_summary = event.getSummary(self.parent_name)
1.644 -
1.645 - is_ambiguous = event.as_timespan().ambiguous()
1.646 - style = self.getEventStyle(event_summary)
1.647 -
1.648 - # The event box contains the summary, alongside
1.649 - # other elements.
1.650 -
1.651 - output.append(fmt.div(on=1, css_class="event-summary-box"))
1.652 - output.append(fmt.div(on=1, css_class="event-summary", style=style))
1.653 -
1.654 - if is_ambiguous:
1.655 - output.append(fmt.icon("/!\\"))
1.656 -
1.657 - output.append(event.linkToEvent(request, event_summary))
1.658 - output.append(fmt.div(on=0))
1.659 -
1.660 - # Add a pop-up element for long summaries.
1.661 -
1.662 - output.append(fmt.div(on=1, css_class="event-summary-popup", style=style))
1.663 -
1.664 - if is_ambiguous:
1.665 - output.append(fmt.icon("/!\\"))
1.666 -
1.667 - output.append(event.linkToEvent(request, event_summary))
1.668 - output.append(fmt.div(on=0))
1.669 -
1.670 - output.append(fmt.div(on=0))
1.671 -
1.672 - return "".join(output)
1.673 -
1.674 - # Calendar layout methods.
1.675 -
1.676 - def writeMonthTableHeading(self, year_month):
1.677 - page = self.page
1.678 - fmt = page.formatter
1.679 -
1.680 - output = []
1.681 - output.append(fmt.table_row(on=1))
1.682 - output.append(fmt.table_cell(on=1, attrs={"class" : "event-month-heading", "colspan" : "21"}))
1.683 -
1.684 - output.append(self.writeMonthHeading(year_month))
1.685 -
1.686 - output.append(fmt.table_cell(on=0))
1.687 - output.append(fmt.table_row(on=0))
1.688 -
1.689 - return "".join(output)
1.690 -
1.691 - def writeWeekdayHeadings(self):
1.692 - page = self.page
1.693 - request = page.request
1.694 - fmt = page.formatter
1.695 - _ = request.getText
1.696 -
1.697 - output = []
1.698 - output.append(fmt.table_row(on=1))
1.699 -
1.700 - for weekday in range(0, 7):
1.701 - output.append(fmt.table_cell(on=1, attrs={"class" : "event-weekday-heading", "colspan" : "3"}))
1.702 - output.append(fmt.text(_(getDayLabel(weekday))))
1.703 - output.append(fmt.table_cell(on=0))
1.704 -
1.705 - output.append(fmt.table_row(on=0))
1.706 - return "".join(output)
1.707 -
1.708 - def writeDayNumbers(self, first_day, number_of_days, month, coverage):
1.709 - page = self.page
1.710 - fmt = page.formatter
1.711 -
1.712 - output = []
1.713 - output.append(fmt.table_row(on=1))
1.714 -
1.715 - for weekday in range(0, 7):
1.716 - day = first_day + weekday
1.717 - date = month.as_date(day)
1.718 -
1.719 - # Output out-of-month days.
1.720 -
1.721 - if day < 1 or day > number_of_days:
1.722 - output.append(fmt.table_cell(on=1,
1.723 - attrs={"class" : "event-day-heading event-day-excluded", "colspan" : "3"}))
1.724 - output.append(fmt.table_cell(on=0))
1.725 -
1.726 - # Output normal days.
1.727 -
1.728 - else:
1.729 - # Output the day heading, making a link to a new event
1.730 - # action.
1.731 -
1.732 - output.append(self.writeDayNumberHeading(date, date in coverage))
1.733 -
1.734 - # End of day numbers.
1.735 -
1.736 - output.append(fmt.table_row(on=0))
1.737 - return "".join(output)
1.738 -
1.739 - def writeEmptyWeek(self, first_day, number_of_days):
1.740 - page = self.page
1.741 - fmt = page.formatter
1.742 -
1.743 - output = []
1.744 - output.append(fmt.table_row(on=1))
1.745 -
1.746 - for weekday in range(0, 7):
1.747 - day = first_day + weekday
1.748 -
1.749 - # Output out-of-month days.
1.750 -
1.751 - if day < 1 or day > number_of_days:
1.752 - output.append(fmt.table_cell(on=1,
1.753 - attrs={"class" : "event-day-content event-day-excluded", "colspan" : "3"}))
1.754 - output.append(fmt.table_cell(on=0))
1.755 -
1.756 - # Output empty days.
1.757 -
1.758 - else:
1.759 - output.append(fmt.table_cell(on=1,
1.760 - attrs={"class" : "event-day-content event-day-empty", "colspan" : "3"}))
1.761 -
1.762 - output.append(fmt.table_row(on=0))
1.763 - return "".join(output)
1.764 -
1.765 - def writeWeekSlots(self, first_day, number_of_days, month, week_end, week_slots):
1.766 - output = []
1.767 -
1.768 - locations = week_slots.keys()
1.769 - locations.sort(sort_none_first)
1.770 -
1.771 - # Visit each slot corresponding to a location (or no location).
1.772 -
1.773 - for location in locations:
1.774 -
1.775 - # Visit each coverage span, presenting the events in the span.
1.776 -
1.777 - for events in week_slots[location]:
1.778 -
1.779 - # Output each set.
1.780 -
1.781 - output.append(self.writeWeekSlot(first_day, number_of_days, month, week_end, events))
1.782 -
1.783 - # Add a spacer.
1.784 -
1.785 - output.append(self.writeWeekSpacer(first_day, number_of_days))
1.786 -
1.787 - return "".join(output)
1.788 -
1.789 - def writeWeekSlot(self, first_day, number_of_days, month, week_end, events):
1.790 - page = self.page
1.791 - request = page.request
1.792 - fmt = page.formatter
1.793 -
1.794 - output = []
1.795 - output.append(fmt.table_row(on=1))
1.796 -
1.797 - # Then, output day details.
1.798 -
1.799 - for weekday in range(0, 7):
1.800 - day = first_day + weekday
1.801 - date = month.as_date(day)
1.802 -
1.803 - # Skip out-of-month days.
1.804 -
1.805 - if day < 1 or day > number_of_days:
1.806 - output.append(fmt.table_cell(on=1,
1.807 - attrs={"class" : "event-day-content event-day-excluded", "colspan" : "3"}))
1.808 - output.append(fmt.table_cell(on=0))
1.809 - continue
1.810 -
1.811 - # Output the day.
1.812 -
1.813 - if date not in events:
1.814 - output.append(fmt.table_cell(on=1,
1.815 - attrs={"class" : "event-day-content event-day-empty", "colspan" : "3"}))
1.816 -
1.817 - # Get event details for the current day.
1.818 -
1.819 - for event in events:
1.820 - event_details = event.getDetails()
1.821 -
1.822 - if date not in event:
1.823 - continue
1.824 -
1.825 - # Get basic properties of the event.
1.826 -
1.827 - starts_today = event_details["start"] == date
1.828 - ends_today = event_details["end"] == date
1.829 - event_summary = event.getSummary(self.parent_name)
1.830 -
1.831 - style = self.getEventStyle(event_summary)
1.832 -
1.833 - # Determine if the event name should be shown.
1.834 -
1.835 - start_of_period = starts_today or weekday == 0 or day == 1
1.836 -
1.837 - if self.name_usage == "daily" or start_of_period:
1.838 - hide_text = 0
1.839 - else:
1.840 - hide_text = 1
1.841 -
1.842 - # Output start of day gap and determine whether
1.843 - # any event content should be explicitly output
1.844 - # for this day.
1.845 -
1.846 - if starts_today:
1.847 -
1.848 - # Single day events...
1.849 -
1.850 - if ends_today:
1.851 - colspan = 3
1.852 - event_day_type = "event-day-single"
1.853 -
1.854 - # Events starting today...
1.855 -
1.856 - else:
1.857 - output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-start-gap"}))
1.858 - output.append(fmt.table_cell(on=0))
1.859 -
1.860 - # Calculate the span of this cell.
1.861 - # Events whose names appear on every day...
1.862 -
1.863 - if self.name_usage == "daily":
1.864 - colspan = 2
1.865 - event_day_type = "event-day-starting"
1.866 -
1.867 - # Events whose names appear once per week...
1.868 -
1.869 - else:
1.870 - if event_details["end"] <= week_end:
1.871 - event_length = event_details["end"].day() - day + 1
1.872 - colspan = (event_length - 2) * 3 + 4
1.873 - else:
1.874 - event_length = week_end.day() - day + 1
1.875 - colspan = (event_length - 1) * 3 + 2
1.876 -
1.877 - event_day_type = "event-day-multiple"
1.878 -
1.879 - # Events continuing from a previous week...
1.880 -
1.881 - elif start_of_period:
1.882 -
1.883 - # End of continuing event...
1.884 -
1.885 - if ends_today:
1.886 - colspan = 2
1.887 - event_day_type = "event-day-ending"
1.888 -
1.889 - # Events continuing for at least one more day...
1.890 -
1.891 - else:
1.892 -
1.893 - # Calculate the span of this cell.
1.894 - # Events whose names appear on every day...
1.895 -
1.896 - if self.name_usage == "daily":
1.897 - colspan = 3
1.898 - event_day_type = "event-day-full"
1.899 -
1.900 - # Events whose names appear once per week...
1.901 -
1.902 - else:
1.903 - if event_details["end"] <= week_end:
1.904 - event_length = event_details["end"].day() - day + 1
1.905 - colspan = (event_length - 1) * 3 + 2
1.906 - else:
1.907 - event_length = week_end.day() - day + 1
1.908 - colspan = event_length * 3
1.909 -
1.910 - event_day_type = "event-day-multiple"
1.911 -
1.912 - # Continuing events whose names appear on every day...
1.913 -
1.914 - elif self.name_usage == "daily":
1.915 - if ends_today:
1.916 - colspan = 2
1.917 - event_day_type = "event-day-ending"
1.918 - else:
1.919 - colspan = 3
1.920 - event_day_type = "event-day-full"
1.921 -
1.922 - # Continuing events whose names appear once per week...
1.923 -
1.924 - else:
1.925 - colspan = None
1.926 -
1.927 - # Output the main content only if it is not
1.928 - # continuing from a previous day.
1.929 -
1.930 - if colspan is not None:
1.931 -
1.932 - # Colour the cell for continuing events.
1.933 -
1.934 - attrs={
1.935 - "class" : "event-day-content event-day-busy %s" % event_day_type,
1.936 - "colspan" : str(colspan)
1.937 - }
1.938 -
1.939 - if not (starts_today and ends_today):
1.940 - attrs["style"] = style
1.941 -
1.942 - output.append(fmt.table_cell(on=1, attrs=attrs))
1.943 -
1.944 - # Output the event.
1.945 -
1.946 - if starts_today and ends_today or not hide_text:
1.947 - output.append(self.writeEventSummaryBox(event))
1.948 -
1.949 - output.append(fmt.table_cell(on=0))
1.950 -
1.951 - # Output end of day gap.
1.952 -
1.953 - if ends_today and not starts_today:
1.954 - output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-end-gap"}))
1.955 - output.append(fmt.table_cell(on=0))
1.956 -
1.957 - # End of set.
1.958 -
1.959 - output.append(fmt.table_row(on=0))
1.960 - return "".join(output)
1.961 -
1.962 - def writeWeekSpacer(self, first_day, number_of_days):
1.963 - page = self.page
1.964 - fmt = page.formatter
1.965 -
1.966 - output = []
1.967 - output.append(fmt.table_row(on=1))
1.968 -
1.969 - for weekday in range(0, 7):
1.970 - day = first_day + weekday
1.971 - css_classes = "event-day-spacer"
1.972 -
1.973 - # Skip out-of-month days.
1.974 -
1.975 - if day < 1 or day > number_of_days:
1.976 - css_classes += " event-day-excluded"
1.977 -
1.978 - output.append(fmt.table_cell(on=1, attrs={"class" : css_classes, "colspan" : "3"}))
1.979 - output.append(fmt.table_cell(on=0))
1.980 -
1.981 - output.append(fmt.table_row(on=0))
1.982 - return "".join(output)
1.983 -
1.984 - # Day layout methods.
1.985 -
1.986 - def writeDayTableHeading(self, date, colspan=1):
1.987 - page = self.page
1.988 - fmt = page.formatter
1.989 -
1.990 - output = []
1.991 - output.append(fmt.table_row(on=1))
1.992 -
1.993 - output.append(fmt.table_cell(on=1, attrs={"class" : "event-full-day-heading", "colspan" : str(colspan)}))
1.994 - output.append(self.writeDayHeading(date))
1.995 - output.append(fmt.table_cell(on=0))
1.996 -
1.997 - output.append(fmt.table_row(on=0))
1.998 - return "".join(output)
1.999 -
1.1000 - def writeEmptyDay(self, date):
1.1001 - page = self.page
1.1002 - fmt = page.formatter
1.1003 -
1.1004 - output = []
1.1005 - output.append(fmt.table_row(on=1))
1.1006 -
1.1007 - output.append(fmt.table_cell(on=1,
1.1008 - attrs={"class" : "event-day-content event-day-empty"}))
1.1009 -
1.1010 - output.append(fmt.table_row(on=0))
1.1011 - return "".join(output)
1.1012 -
1.1013 - def writeDaySlots(self, date, full_coverage, day_slots):
1.1014 -
1.1015 - """
1.1016 - Given a 'date', non-empty 'full_coverage' for the day concerned, and a
1.1017 - non-empty mapping of 'day_slots' (from locations to event collections),
1.1018 - output the day slots for the day.
1.1019 - """
1.1020 -
1.1021 - page = self.page
1.1022 - fmt = page.formatter
1.1023 -
1.1024 - output = []
1.1025 -
1.1026 - locations = day_slots.keys()
1.1027 - locations.sort(sort_none_first)
1.1028 -
1.1029 - # Traverse the time scale of the full coverage, visiting each slot to
1.1030 - # determine whether it provides content for each period.
1.1031 -
1.1032 - scale = getCoverageScale(full_coverage)
1.1033 -
1.1034 - # Define a mapping of events to rowspans.
1.1035 -
1.1036 - rowspans = {}
1.1037 -
1.1038 - # Populate each period with event details, recording how many periods
1.1039 - # each event populates.
1.1040 -
1.1041 - day_rows = []
1.1042 -
1.1043 - for period in scale:
1.1044 -
1.1045 - # Ignore timespans before this day.
1.1046 -
1.1047 - if period != date:
1.1048 - continue
1.1049 -
1.1050 - # Visit each slot corresponding to a location (or no location).
1.1051 -
1.1052 - day_row = []
1.1053 -
1.1054 - for location in locations:
1.1055 -
1.1056 - # Visit each coverage span, presenting the events in the span.
1.1057 -
1.1058 - for events in day_slots[location]:
1.1059 - event = self.getActiveEvent(period, events)
1.1060 - if event is not None:
1.1061 - if not rowspans.has_key(event):
1.1062 - rowspans[event] = 1
1.1063 - else:
1.1064 - rowspans[event] += 1
1.1065 - day_row.append((location, event))
1.1066 -
1.1067 - day_rows.append((period, day_row))
1.1068 -
1.1069 - # Output the locations.
1.1070 -
1.1071 - output.append(fmt.table_row(on=1))
1.1072 -
1.1073 - # Add a spacer.
1.1074 -
1.1075 - output.append(self.writeDaySpacer(colspan=2, cls="location"))
1.1076 -
1.1077 - for location in locations:
1.1078 -
1.1079 - # Add spacers to the column spans.
1.1080 -
1.1081 - columns = len(day_slots[location]) * 2 - 1
1.1082 - output.append(fmt.table_cell(on=1, attrs={"class" : "event-location-heading", "colspan" : str(columns)}))
1.1083 - output.append(fmt.text(location or ""))
1.1084 - output.append(fmt.table_cell(on=0))
1.1085 -
1.1086 - # Add a trailing spacer.
1.1087 -
1.1088 - output.append(self.writeDaySpacer(cls="location"))
1.1089 -
1.1090 - output.append(fmt.table_row(on=0))
1.1091 -
1.1092 - # Output the periods with event details.
1.1093 -
1.1094 - period = None
1.1095 - events_written = set()
1.1096 -
1.1097 - for period, day_row in day_rows:
1.1098 -
1.1099 - # Write an empty heading for the start of the day where the first
1.1100 - # applicable timespan starts before this day.
1.1101 -
1.1102 - if period.start < date:
1.1103 - output.append(fmt.table_row(on=1))
1.1104 - output.append(self.writeDayScaleHeading(""))
1.1105 -
1.1106 - # Otherwise, write a heading describing the time.
1.1107 -
1.1108 - else:
1.1109 - output.append(fmt.table_row(on=1))
1.1110 - output.append(self.writeDayScaleHeading(period.start.time_string()))
1.1111 -
1.1112 - output.append(self.writeDaySpacer())
1.1113 -
1.1114 - # Visit each slot corresponding to a location (or no location).
1.1115 -
1.1116 - for location, event in day_row:
1.1117 -
1.1118 - # Output each location slot's contribution.
1.1119 -
1.1120 - if event is None or event not in events_written:
1.1121 - output.append(self.writeDaySlot(period, event, event is None and 1 or rowspans[event]))
1.1122 - if event is not None:
1.1123 - events_written.add(event)
1.1124 -
1.1125 - # Add a trailing spacer.
1.1126 -
1.1127 - output.append(self.writeDaySpacer())
1.1128 -
1.1129 - output.append(fmt.table_row(on=0))
1.1130 -
1.1131 - # Write a final time heading if the last period ends in the current day.
1.1132 -
1.1133 - if period is not None:
1.1134 - if period.end == date:
1.1135 - output.append(fmt.table_row(on=1))
1.1136 - output.append(self.writeDayScaleHeading(period.end.time_string()))
1.1137 -
1.1138 - for slot in day_row:
1.1139 - output.append(self.writeDaySpacer())
1.1140 - output.append(self.writeEmptyDaySlot())
1.1141 -
1.1142 - output.append(fmt.table_row(on=0))
1.1143 -
1.1144 - return "".join(output)
1.1145 -
1.1146 - def writeDayScaleHeading(self, heading):
1.1147 - page = self.page
1.1148 - fmt = page.formatter
1.1149 -
1.1150 - output = []
1.1151 - output.append(fmt.table_cell(on=1, attrs={"class" : "event-scale-heading"}))
1.1152 - output.append(fmt.text(heading))
1.1153 - output.append(fmt.table_cell(on=0))
1.1154 -
1.1155 - return "".join(output)
1.1156 -
1.1157 - def getActiveEvent(self, period, events):
1.1158 - for event in events:
1.1159 - if period not in event:
1.1160 - continue
1.1161 - return event
1.1162 - else:
1.1163 - return None
1.1164 -
1.1165 - def writeDaySlot(self, period, event, rowspan):
1.1166 - page = self.page
1.1167 - fmt = page.formatter
1.1168 -
1.1169 - output = []
1.1170 -
1.1171 - if event is not None:
1.1172 - event_summary = event.getSummary(self.parent_name)
1.1173 - style = self.getEventStyle(event_summary)
1.1174 -
1.1175 - output.append(fmt.table_cell(on=1, attrs={
1.1176 - "class" : "event-timespan-content event-timespan-busy",
1.1177 - "style" : style,
1.1178 - "rowspan" : str(rowspan)
1.1179 - }))
1.1180 - output.append(self.writeEventSummaryBox(event))
1.1181 - output.append(fmt.table_cell(on=0))
1.1182 - else:
1.1183 - output.append(self.writeEmptyDaySlot())
1.1184 -
1.1185 - return "".join(output)
1.1186 -
1.1187 - def writeEmptyDaySlot(self):
1.1188 - page = self.page
1.1189 - fmt = page.formatter
1.1190 -
1.1191 - output = []
1.1192 -
1.1193 - output.append(fmt.table_cell(on=1,
1.1194 - attrs={"class" : "event-timespan-content event-timespan-empty"}))
1.1195 - output.append(fmt.table_cell(on=0))
1.1196 -
1.1197 - return "".join(output)
1.1198 -
1.1199 - def writeDaySpacer(self, colspan=1, cls="timespan"):
1.1200 - page = self.page
1.1201 - fmt = page.formatter
1.1202 -
1.1203 - output = []
1.1204 - output.append(fmt.table_cell(on=1, attrs={
1.1205 - "class" : "event-%s-spacer" % cls,
1.1206 - "colspan" : str(colspan)}))
1.1207 - output.append(fmt.table_cell(on=0))
1.1208 - return "".join(output)
1.1209 -
1.1210 - # Map layout methods.
1.1211 -
1.1212 - def writeMapTableHeading(self):
1.1213 - page = self.page
1.1214 - fmt = page.formatter
1.1215 -
1.1216 - output = []
1.1217 - output.append(fmt.table_cell(on=1, attrs={"class" : "event-map-heading"}))
1.1218 - output.append(self.writeMapHeading())
1.1219 - output.append(fmt.table_cell(on=0))
1.1220 -
1.1221 - return "".join(output)
1.1222 -
1.1223 - def showDictError(self, text, pagename):
1.1224 - page = self.page
1.1225 - fmt = page.formatter
1.1226 - request = page.request
1.1227 -
1.1228 - output = []
1.1229 -
1.1230 - output.append(fmt.div(on=1, attrs={"class" : "event-aggregator-error"}))
1.1231 - output.append(fmt.paragraph(on=1))
1.1232 - output.append(fmt.text(text))
1.1233 - output.append(fmt.paragraph(on=0))
1.1234 - output.append(fmt.paragraph(on=1))
1.1235 - output.append(linkToPage(request, Page(request, pagename), pagename))
1.1236 - output.append(fmt.paragraph(on=0))
1.1237 -
1.1238 - return "".join(output)
1.1239 -
1.1240 - def writeMapEventSummaries(self, events):
1.1241 - page = self.page
1.1242 - fmt = page.formatter
1.1243 - request = page.request
1.1244 -
1.1245 - # Sort the events by date.
1.1246 -
1.1247 - events.sort(sort_start_first)
1.1248 -
1.1249 - # Write out a self-contained list of events.
1.1250 -
1.1251 - output = []
1.1252 - output.append(fmt.bullet_list(on=1, attr={"class" : "event-map-location-events"}))
1.1253 -
1.1254 - for event in events:
1.1255 -
1.1256 - # Get the event details.
1.1257 -
1.1258 - event_summary = event.getSummary(self.parent_name)
1.1259 - start, end = event.as_limits()
1.1260 - event_period = self._getCalendarPeriod(
1.1261 - start and self.getFullDateLabel(start),
1.1262 - end and self.getFullDateLabel(end),
1.1263 - "")
1.1264 -
1.1265 - output.append(fmt.listitem(on=1))
1.1266 -
1.1267 - # Link to the page using the summary.
1.1268 -
1.1269 - output.append(event.linkToEvent(request, event_summary))
1.1270 -
1.1271 - # Add the event period.
1.1272 -
1.1273 - output.append(fmt.text(" "))
1.1274 - output.append(fmt.span(on=1, css_class="event-map-period"))
1.1275 - output.append(fmt.text(event_period))
1.1276 - output.append(fmt.span(on=0))
1.1277 -
1.1278 - output.append(fmt.listitem(on=0))
1.1279 -
1.1280 - output.append(fmt.bullet_list(on=0))
1.1281 -
1.1282 - return "".join(output)
1.1283 -
1.1284 -# HTML-related functions.
1.1285 -
1.1286 -def getColour(s):
1.1287 - colour = [0, 0, 0]
1.1288 - digit = 0
1.1289 - for c in s:
1.1290 - colour[digit] += ord(c)
1.1291 - colour[digit] = colour[digit] % 256
1.1292 - digit += 1
1.1293 - digit = digit % 3
1.1294 - return tuple(colour)
1.1295 -
1.1296 -def getBlackOrWhite(colour):
1.1297 - if sum(colour) / 3.0 > 127:
1.1298 - return (0, 0, 0)
1.1299 - else:
1.1300 - return (255, 255, 255)
1.1301 -
1.1302 # Macro functions.
1.1303
1.1304 def execute(macro, args):
1.1305 @@ -1354,7 +63,6 @@
1.1306 request = macro.request
1.1307 fmt = macro.formatter
1.1308 page = fmt.page
1.1309 - _ = request.getText
1.1310
1.1311 # Interpret the arguments.
1.1312
1.1313 @@ -1454,529 +162,6 @@
1.1314 first, last, category_names, remote_sources, template_name, parent_name,
1.1315 mode, resolution, name_usage, map_name)
1.1316
1.1317 - # Make a calendar.
1.1318 -
1.1319 - output = []
1.1320 -
1.1321 - output.append(fmt.div(on=1, css_class="event-calendar"))
1.1322 -
1.1323 - # Output download controls.
1.1324 -
1.1325 - output.append(fmt.div(on=1, css_class="event-controls"))
1.1326 - output.append(view.writeDownloadControls())
1.1327 - output.append(fmt.div(on=0))
1.1328 -
1.1329 - # Output a table.
1.1330 -
1.1331 - if mode == "table":
1.1332 -
1.1333 - # Start of table view output.
1.1334 -
1.1335 - output.append(fmt.table(on=1, attrs={"tableclass" : "event-table"}))
1.1336 -
1.1337 - output.append(fmt.table_row(on=1))
1.1338 - output.append(fmt.table_cell(on=1, attrs={"class" : "event-table-heading"}))
1.1339 - output.append(fmt.text(_("Event dates")))
1.1340 - output.append(fmt.table_cell(on=0))
1.1341 - output.append(fmt.table_cell(on=1, attrs={"class" : "event-table-heading"}))
1.1342 - output.append(fmt.text(_("Event location")))
1.1343 - output.append(fmt.table_cell(on=0))
1.1344 - output.append(fmt.table_cell(on=1, attrs={"class" : "event-table-heading"}))
1.1345 - output.append(fmt.text(_("Event details")))
1.1346 - output.append(fmt.table_cell(on=0))
1.1347 - output.append(fmt.table_row(on=0))
1.1348 -
1.1349 - # Show the events in order.
1.1350 -
1.1351 - all_shown_events.sort(sort_start_first)
1.1352 -
1.1353 - for event in all_shown_events:
1.1354 - event_page = event.getPage()
1.1355 - event_summary = event.getSummary(parent_name)
1.1356 - event_details = event.getDetails()
1.1357 -
1.1358 - # Prepare CSS classes with category-related styling.
1.1359 -
1.1360 - css_classes = ["event-table-details"]
1.1361 -
1.1362 - for topic in event_details.get("topics") or event_details.get("categories") or []:
1.1363 -
1.1364 - # Filter the category text to avoid illegal characters.
1.1365 -
1.1366 - css_classes.append("event-table-category-%s" % "".join(filter(lambda c: c.isalnum(), topic)))
1.1367 -
1.1368 - attrs = {"class" : " ".join(css_classes)}
1.1369 -
1.1370 - output.append(fmt.table_row(on=1))
1.1371 -
1.1372 - # Start and end dates.
1.1373 -
1.1374 - output.append(fmt.table_cell(on=1, attrs=attrs))
1.1375 - output.append(fmt.span(on=1))
1.1376 - output.append(fmt.text(str(event_details["start"])))
1.1377 - output.append(fmt.span(on=0))
1.1378 -
1.1379 - if event_details["start"] != event_details["end"]:
1.1380 - output.append(fmt.text(" - "))
1.1381 - output.append(fmt.span(on=1))
1.1382 - output.append(fmt.text(str(event_details["end"])))
1.1383 - output.append(fmt.span(on=0))
1.1384 -
1.1385 - output.append(fmt.table_cell(on=0))
1.1386 -
1.1387 - # Location.
1.1388 -
1.1389 - output.append(fmt.table_cell(on=1, attrs=attrs))
1.1390 -
1.1391 - if event_details.has_key("location"):
1.1392 - output.append(event_page.formatText(event_details["location"], request, fmt))
1.1393 -
1.1394 - output.append(fmt.table_cell(on=0))
1.1395 -
1.1396 - # Link to the page using the summary.
1.1397 -
1.1398 - output.append(fmt.table_cell(on=1, attrs=attrs))
1.1399 - output.append(event.linkToEvent(request, event_summary))
1.1400 - output.append(fmt.table_cell(on=0))
1.1401 -
1.1402 - output.append(fmt.table_row(on=0))
1.1403 -
1.1404 - # End of table view output.
1.1405 -
1.1406 - output.append(fmt.table(on=0))
1.1407 -
1.1408 - # Output a map view.
1.1409 -
1.1410 - elif mode == "map":
1.1411 -
1.1412 - # Special dictionary pages.
1.1413 -
1.1414 - maps_page = getattr(request.cfg, "event_aggregator_maps_page", "EventMapsDict")
1.1415 - locations_page = getattr(request.cfg, "event_aggregator_locations_page", "EventLocationsDict")
1.1416 -
1.1417 - map_image = None
1.1418 -
1.1419 - # Get the maps and locations.
1.1420 -
1.1421 - maps = getWikiDict(maps_page, request)
1.1422 - locations = getWikiDict(locations_page, request)
1.1423 -
1.1424 - # Get the map image definition.
1.1425 -
1.1426 - if maps is not None and map_name is not None:
1.1427 - try:
1.1428 - map_details = maps[map_name].split()
1.1429 -
1.1430 - map_bottom_left_latitude, map_bottom_left_longitude, map_top_right_latitude, map_top_right_longitude = \
1.1431 - map(getMapReference, map_details[:4])
1.1432 - map_width, map_height = map(int, map_details[4:6])
1.1433 - map_image = map_details[6]
1.1434 -
1.1435 - map_x_scale = map_width / (map_top_right_longitude - map_bottom_left_longitude).to_degrees()
1.1436 - map_y_scale = map_height / (map_top_right_latitude - map_bottom_left_latitude).to_degrees()
1.1437 -
1.1438 - except (KeyError, ValueError):
1.1439 - pass
1.1440 -
1.1441 - # Report errors.
1.1442 -
1.1443 - if maps is None:
1.1444 - output.append(view.showDictError(
1.1445 - _("You do not have read access to the maps page:"),
1.1446 - maps_page))
1.1447 -
1.1448 - elif map_name is None:
1.1449 - output.append(view.showDictError(
1.1450 - _("Please specify a valid map name corresponding to an entry on the following page:"),
1.1451 - maps_page))
1.1452 -
1.1453 - elif map_image is None:
1.1454 - output.append(view.showDictError(
1.1455 - _("Please specify a valid entry for %s on the following page:") % map_name,
1.1456 - maps_page))
1.1457 -
1.1458 - elif locations is None:
1.1459 - output.append(view.showDictError(
1.1460 - _("You do not have read access to the locations page:"),
1.1461 - locations_page))
1.1462 -
1.1463 - # Attempt to show the map.
1.1464 -
1.1465 - else:
1.1466 -
1.1467 - # Get events by position.
1.1468 -
1.1469 - events_by_location = {}
1.1470 - event_locations = {}
1.1471 -
1.1472 - for event in all_shown_events:
1.1473 - event_details = event.getDetails()
1.1474 -
1.1475 - location = event_details.get("location")
1.1476 -
1.1477 - if location is not None and not event_locations.has_key(location):
1.1478 -
1.1479 - # Get any explicit position of an event.
1.1480 -
1.1481 - if event_details.has_key("geo"):
1.1482 - latitude, longitude = event_details["geo"]
1.1483 -
1.1484 - # Or look up the position of a location using the locations
1.1485 - # page.
1.1486 -
1.1487 - else:
1.1488 - latitude, longitude = getLocationPosition(location, locations)
1.1489 -
1.1490 - # Use a normalised location if necessary.
1.1491 -
1.1492 - if latitude is None and longitude is None:
1.1493 - normalised_location = getNormalisedLocation(location)
1.1494 - if normalised_location is not None:
1.1495 - latitude, longitude = getLocationPosition(normalised_location, locations)
1.1496 - if latitude is not None and longitude is not None:
1.1497 - location = normalised_location
1.1498 -
1.1499 - # Only remember positioned locations.
1.1500 -
1.1501 - if latitude is not None and longitude is not None:
1.1502 - event_locations[location] = latitude, longitude
1.1503 -
1.1504 - # Record events according to location.
1.1505 -
1.1506 - if not events_by_location.has_key(location):
1.1507 - events_by_location[location] = []
1.1508 -
1.1509 - events_by_location[location].append(event)
1.1510 -
1.1511 - # Get the map image URL.
1.1512 -
1.1513 - map_image_url = AttachFile.getAttachUrl(maps_page, map_image, request)
1.1514 -
1.1515 - # Start of map view output.
1.1516 -
1.1517 - map_identifier = "map-%s" % view.getIdentifier()
1.1518 - output.append(fmt.div(on=1, css_class="event-map", id=map_identifier))
1.1519 -
1.1520 - output.append(fmt.table(on=1))
1.1521 -
1.1522 - output.append(fmt.table_row(on=1))
1.1523 - output.append(view.writeMapTableHeading())
1.1524 - output.append(fmt.table_row(on=0))
1.1525 -
1.1526 - output.append(fmt.table_row(on=1))
1.1527 - output.append(fmt.table_cell(on=1))
1.1528 -
1.1529 - output.append(fmt.div(on=1, css_class="event-map-container"))
1.1530 - output.append(fmt.image(map_image_url))
1.1531 - output.append(fmt.number_list(on=1))
1.1532 -
1.1533 - # Events with no location are unpositioned.
1.1534 -
1.1535 - if events_by_location.has_key(None):
1.1536 - unpositioned_events = events_by_location[None]
1.1537 - del events_by_location[None]
1.1538 - else:
1.1539 - unpositioned_events = []
1.1540 -
1.1541 - # Events whose location is unpositioned are themselves considered
1.1542 - # unpositioned.
1.1543 -
1.1544 - for location in set(events_by_location.keys()).difference(event_locations.keys()):
1.1545 - unpositioned_events += events_by_location[location]
1.1546 -
1.1547 - # Sort the locations before traversing them.
1.1548 -
1.1549 - event_locations = event_locations.items()
1.1550 - event_locations.sort()
1.1551 -
1.1552 - # Show the events in the map.
1.1553 -
1.1554 - for location, (latitude, longitude) in event_locations:
1.1555 - events = events_by_location[location]
1.1556 -
1.1557 - # Skip unpositioned locations and locations outside the map.
1.1558 -
1.1559 - if latitude is None or longitude is None or \
1.1560 - latitude < map_bottom_left_latitude or \
1.1561 - longitude < map_bottom_left_longitude or \
1.1562 - latitude > map_top_right_latitude or \
1.1563 - longitude > map_top_right_longitude:
1.1564 -
1.1565 - unpositioned_events += events
1.1566 - continue
1.1567 -
1.1568 - # Get the position and dimensions of the map marker.
1.1569 - # NOTE: Use one degree as the marker size.
1.1570 -
1.1571 - marker_x, marker_y = getPositionForCentrePoint(
1.1572 - getPositionForReference(map_top_right_latitude, longitude, latitude, map_bottom_left_longitude,
1.1573 - map_x_scale, map_y_scale),
1.1574 - map_x_scale, map_y_scale)
1.1575 -
1.1576 - # Put a marker on the map.
1.1577 -
1.1578 - output.append(fmt.listitem(on=1, css_class="event-map-label"))
1.1579 -
1.1580 - # Have a positioned marker for the print mode.
1.1581 -
1.1582 - output.append(fmt.div(on=1, css_class="event-map-label-only",
1.1583 - style="left:%dpx; top:%dpx; min-width:%dpx; min-height:%dpx") % (
1.1584 - marker_x, marker_y, map_x_scale, map_y_scale))
1.1585 - output.append(fmt.div(on=0))
1.1586 -
1.1587 - # Have a marker containing a pop-up when using the screen mode,
1.1588 - # providing a normal block when using the print mode.
1.1589 -
1.1590 - output.append(fmt.div(on=1, css_class="event-map-label",
1.1591 - style="left:%dpx; top:%dpx; min-width:%dpx; min-height:%dpx") % (
1.1592 - marker_x, marker_y, map_x_scale, map_y_scale))
1.1593 - output.append(fmt.div(on=1, css_class="event-map-details"))
1.1594 - output.append(fmt.div(on=1, css_class="event-map-shadow"))
1.1595 - output.append(fmt.div(on=1, css_class="event-map-location"))
1.1596 -
1.1597 - output.append(fmt.heading(on=1, depth=2))
1.1598 - output.append(fmt.text(location))
1.1599 - output.append(fmt.heading(on=0, depth=2))
1.1600 -
1.1601 - output.append(view.writeMapEventSummaries(events))
1.1602 -
1.1603 - output.append(fmt.div(on=0))
1.1604 - output.append(fmt.div(on=0))
1.1605 - output.append(fmt.div(on=0))
1.1606 - output.append(fmt.div(on=0))
1.1607 - output.append(fmt.listitem(on=0))
1.1608 -
1.1609 - output.append(fmt.number_list(on=0))
1.1610 - output.append(fmt.div(on=0))
1.1611 - output.append(fmt.table_cell(on=0))
1.1612 - output.append(fmt.table_row(on=0))
1.1613 -
1.1614 - # Write unpositioned events.
1.1615 -
1.1616 - if unpositioned_events:
1.1617 - unpositioned_identifier = "unpositioned-%s" % view.getIdentifier()
1.1618 -
1.1619 - output.append(fmt.table_row(on=1, css_class="event-map-unpositioned",
1.1620 - id=unpositioned_identifier))
1.1621 - output.append(fmt.table_cell(on=1))
1.1622 -
1.1623 - output.append(fmt.heading(on=1, depth=2))
1.1624 - output.append(fmt.text(_("Events not shown on the map")))
1.1625 - output.append(fmt.heading(on=0, depth=2))
1.1626 -
1.1627 - # Show and hide controls.
1.1628 -
1.1629 - output.append(fmt.div(on=1, css_class="event-map-show-control"))
1.1630 - output.append(fmt.anchorlink(on=1, name=unpositioned_identifier))
1.1631 - output.append(fmt.text(_("Show unpositioned events")))
1.1632 - output.append(fmt.anchorlink(on=0))
1.1633 - output.append(fmt.div(on=0))
1.1634 -
1.1635 - output.append(fmt.div(on=1, css_class="event-map-hide-control"))
1.1636 - output.append(fmt.anchorlink(on=1, name=map_identifier))
1.1637 - output.append(fmt.text(_("Hide unpositioned events")))
1.1638 - output.append(fmt.anchorlink(on=0))
1.1639 - output.append(fmt.div(on=0))
1.1640 -
1.1641 - output.append(view.writeMapEventSummaries(unpositioned_events))
1.1642 -
1.1643 - # End of map view output.
1.1644 -
1.1645 - output.append(fmt.table_cell(on=0))
1.1646 - output.append(fmt.table_row(on=0))
1.1647 - output.append(fmt.table(on=0))
1.1648 - output.append(fmt.div(on=0))
1.1649 -
1.1650 - # Output a list.
1.1651 -
1.1652 - elif mode == "list":
1.1653 -
1.1654 - # Start of list view output.
1.1655 -
1.1656 - output.append(fmt.bullet_list(on=1, attr={"class" : "event-listings"}))
1.1657 -
1.1658 - # Output a list.
1.1659 -
1.1660 - for period in first.until(last):
1.1661 -
1.1662 - output.append(fmt.listitem(on=1, attr={"class" : "event-listings-period"}))
1.1663 - output.append(fmt.div(on=1, attr={"class" : "event-listings-heading"}))
1.1664 -
1.1665 - # Either write a date heading or produce links for navigable
1.1666 - # calendars.
1.1667 -
1.1668 - output.append(view.writeDateHeading(period))
1.1669 -
1.1670 - output.append(fmt.div(on=0))
1.1671 -
1.1672 - output.append(fmt.bullet_list(on=1, attr={"class" : "event-period-listings"}))
1.1673 -
1.1674 - # Show the events in order.
1.1675 -
1.1676 - events_in_period = getEventsInPeriod(all_shown_events, getCalendarPeriod(period, period))
1.1677 - events_in_period.sort(sort_start_first)
1.1678 -
1.1679 - for event in events_in_period:
1.1680 - event_page = event.getPage()
1.1681 - event_details = event.getDetails()
1.1682 - event_summary = event.getSummary(parent_name)
1.1683 -
1.1684 - output.append(fmt.listitem(on=1, attr={"class" : "event-listing"}))
1.1685 -
1.1686 - # Link to the page using the summary.
1.1687 -
1.1688 - output.append(fmt.paragraph(on=1))
1.1689 - output.append(event.linkToEvent(request, event_summary))
1.1690 - output.append(fmt.paragraph(on=0))
1.1691 -
1.1692 - # Start and end dates.
1.1693 -
1.1694 - output.append(fmt.paragraph(on=1))
1.1695 - output.append(fmt.span(on=1))
1.1696 - output.append(fmt.text(str(event_details["start"])))
1.1697 - output.append(fmt.span(on=0))
1.1698 - output.append(fmt.text(" - "))
1.1699 - output.append(fmt.span(on=1))
1.1700 - output.append(fmt.text(str(event_details["end"])))
1.1701 - output.append(fmt.span(on=0))
1.1702 - output.append(fmt.paragraph(on=0))
1.1703 -
1.1704 - # Location.
1.1705 -
1.1706 - if event_details.has_key("location"):
1.1707 - output.append(fmt.paragraph(on=1))
1.1708 - output.append(event_page.formatText(event_details["location"], request, fmt))
1.1709 - output.append(fmt.paragraph(on=1))
1.1710 -
1.1711 - # Topics.
1.1712 -
1.1713 - if event_details.has_key("topics") or event_details.has_key("categories"):
1.1714 - output.append(fmt.bullet_list(on=1, attr={"class" : "event-topics"}))
1.1715 -
1.1716 - for topic in event_details.get("topics") or event_details.get("categories") or []:
1.1717 - output.append(fmt.listitem(on=1))
1.1718 - output.append(event_page.formatText(topic, request, fmt))
1.1719 - output.append(fmt.listitem(on=0))
1.1720 -
1.1721 - output.append(fmt.bullet_list(on=0))
1.1722 -
1.1723 - output.append(fmt.listitem(on=0))
1.1724 -
1.1725 - output.append(fmt.bullet_list(on=0))
1.1726 -
1.1727 - # End of list view output.
1.1728 -
1.1729 - output.append(fmt.bullet_list(on=0))
1.1730 -
1.1731 - # Output a month calendar. This shows month-by-month data.
1.1732 -
1.1733 - elif mode == "calendar":
1.1734 -
1.1735 - # Visit all months in the requested range, or across known events.
1.1736 -
1.1737 - for month in first.months_until(last):
1.1738 -
1.1739 - # Output a month.
1.1740 -
1.1741 - output.append(fmt.table(on=1, attrs={"tableclass" : "event-month"}))
1.1742 -
1.1743 - # Either write a month heading or produce links for navigable
1.1744 - # calendars.
1.1745 -
1.1746 - output.append(view.writeMonthTableHeading(month))
1.1747 -
1.1748 - # Weekday headings.
1.1749 -
1.1750 - output.append(view.writeWeekdayHeadings())
1.1751 -
1.1752 - # Process the days of the month.
1.1753 -
1.1754 - start_weekday, number_of_days = month.month_properties()
1.1755 -
1.1756 - # The start weekday is the weekday of day number 1.
1.1757 - # Find the first day of the week, counting from below zero, if
1.1758 - # necessary, in order to land on the first day of the month as
1.1759 - # day number 1.
1.1760 -
1.1761 - first_day = 1 - start_weekday
1.1762 -
1.1763 - while first_day <= number_of_days:
1.1764 -
1.1765 - # Find events in this week and determine how to mark them on the
1.1766 - # calendar.
1.1767 -
1.1768 - week_start = month.as_date(max(first_day, 1))
1.1769 - week_end = month.as_date(min(first_day + 6, number_of_days))
1.1770 -
1.1771 - full_coverage, week_slots = getCoverage(
1.1772 - getEventsInPeriod(all_shown_events, getCalendarPeriod(week_start, week_end)))
1.1773 -
1.1774 - # Output a week, starting with the day numbers.
1.1775 -
1.1776 - output.append(view.writeDayNumbers(first_day, number_of_days, month, full_coverage))
1.1777 -
1.1778 - # Either generate empty days...
1.1779 -
1.1780 - if not week_slots:
1.1781 - output.append(view.writeEmptyWeek(first_day, number_of_days))
1.1782 -
1.1783 - # Or generate each set of scheduled events...
1.1784 -
1.1785 - else:
1.1786 - output.append(view.writeWeekSlots(first_day, number_of_days, month, week_end, week_slots))
1.1787 -
1.1788 - # Process the next week...
1.1789 -
1.1790 - first_day += 7
1.1791 -
1.1792 - # End of month.
1.1793 -
1.1794 - output.append(fmt.table(on=0))
1.1795 -
1.1796 - # Output a day view.
1.1797 -
1.1798 - elif mode == "day":
1.1799 -
1.1800 - # Visit all days in the requested range, or across known events.
1.1801 -
1.1802 - for date in first.days_until(last):
1.1803 -
1.1804 - output.append(fmt.table(on=1, attrs={"tableclass" : "event-calendar-day"}))
1.1805 -
1.1806 - full_coverage, day_slots = getCoverage(
1.1807 - getEventsInPeriod(all_shown_events, getCalendarPeriod(date, date)), "datetime")
1.1808 -
1.1809 - # Work out how many columns the day title will need.
1.1810 - # Include spacers after the scale and each event column.
1.1811 -
1.1812 - colspan = sum(map(len, day_slots.values())) * 2 + 2
1.1813 -
1.1814 - output.append(view.writeDayTableHeading(date, colspan))
1.1815 -
1.1816 - # Either generate empty days...
1.1817 -
1.1818 - if not day_slots:
1.1819 - output.append(view.writeEmptyDay(date))
1.1820 -
1.1821 - # Or generate each set of scheduled events...
1.1822 -
1.1823 - else:
1.1824 - output.append(view.writeDaySlots(date, full_coverage, day_slots))
1.1825 -
1.1826 - # End of day.
1.1827 -
1.1828 - output.append(fmt.table(on=0))
1.1829 -
1.1830 - # Output view controls.
1.1831 -
1.1832 - output.append(fmt.div(on=1, css_class="event-controls"))
1.1833 - output.append(view.writeViewControls())
1.1834 - output.append(fmt.div(on=0))
1.1835 -
1.1836 - # Close the calendar region.
1.1837 -
1.1838 - output.append(fmt.div(on=0))
1.1839 -
1.1840 - return ''.join(output)
1.1841 + return view.render(all_shown_events)
1.1842
1.1843 # vim: tabstop=4 expandtab shiftwidth=4