1.1 --- a/EventAggregatorSupport.py Mon Dec 05 23:44:32 2011 +0100
1.2 +++ b/EventAggregatorSupport.py Sat Jan 21 23:59:22 2012 +0100
1.3 @@ -2,24 +2,25 @@
1.4 """
1.5 MoinMoin - EventAggregator library
1.6
1.7 - @copyright: 2008, 2009, 2010, 2011 by Paul Boddie <paul@boddie.org.uk>
1.8 + @copyright: 2008, 2009, 2010, 2011, 2012 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 """
1.13
1.14 +from LocationSupport import *
1.15 +from MoinDateSupport import *
1.16 +from MoinSupport import *
1.17 +
1.18 from MoinMoin.Page import Page
1.19 from MoinMoin.action import cache
1.20 from MoinMoin import caching
1.21 -from MoinMoin import search, version
1.22 +from MoinMoin import search
1.23 from MoinMoin import wikiutil
1.24 -import calendar
1.25 +
1.26 import codecs
1.27 -import datetime
1.28 import time
1.29 import re
1.30 -import bisect
1.31 -import operator
1.32 import urllib, urllib2
1.33
1.34 try:
1.35 @@ -33,24 +34,13 @@
1.36 from sets import Set as set
1.37
1.38 try:
1.39 - import pytz
1.40 -except ImportError:
1.41 - pytz = None
1.42 -
1.43 -try:
1.44 import vCalendar
1.45 except ImportError:
1.46 vCalendar = None
1.47
1.48 escape = wikiutil.escape
1.49
1.50 -__version__ = "0.8.1"
1.51 -
1.52 -# Date labels.
1.53 -
1.54 -month_labels = ["January", "February", "March", "April", "May", "June",
1.55 - "July", "August", "September", "October", "November", "December"]
1.56 -weekday_labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
1.57 +__version__ = "0.9"
1.58
1.59 # Regular expressions where MoinMoin does not provide the required support.
1.60
1.61 @@ -64,47 +54,6 @@
1.62 # Value parsing.
1.63
1.64 country_code_regexp = re.compile(ur'(?:^|\W)(?P<code>[A-Z]{2})(?:$|\W+$)', re.UNICODE)
1.65 -location_normalised_regexp = re.compile(
1.66 - ur"(?:\d+\w*\s+)?" # preceding postcode (optional)
1.67 - ur"(?P<location>" # start of group of interest
1.68 - ur"\w[\w\s-]+?" # area or town
1.69 - ur"(?:,(?:\s*[\w-]+)+)?" # country (optional)
1.70 - ur")$", re.UNICODE)
1.71 -
1.72 -# Month, date, time and datetime parsing.
1.73 -
1.74 -month_regexp_str = ur'(?P<year>[0-9]{4})-(?P<month>[0-9]{2})'
1.75 -date_regexp_str = ur'(?P<year>[0-9]{4})-(?P<month>[0-9]{2})-(?P<day>[0-9]{2})'
1.76 -time_regexp_str = ur'(?P<hour>[0-2][0-9]):(?P<minute>[0-5][0-9])(?::(?P<second>[0-6][0-9]))?'
1.77 -timezone_offset_str = ur'(?P<offset>(UTC)?(?:(?P<sign>[-+])(?P<hours>[0-9]{2})(?::?(?P<minutes>[0-9]{2}))?))'
1.78 -timezone_olson_str = ur'(?P<olson>[a-zA-Z]+(?:/[-_a-zA-Z]+){1,2})'
1.79 -timezone_utc_str = ur'UTC'
1.80 -timezone_regexp_str = ur'(?P<zone>' + timezone_offset_str + '|' + timezone_olson_str + '|' + timezone_utc_str + ')'
1.81 -datetime_regexp_str = date_regexp_str + ur'(?:\s+' + time_regexp_str + ur'(?:\s+' + timezone_regexp_str + ur')?)?'
1.82 -
1.83 -month_regexp = re.compile(month_regexp_str, re.UNICODE)
1.84 -date_regexp = re.compile(date_regexp_str, re.UNICODE)
1.85 -time_regexp = re.compile(time_regexp_str, re.UNICODE)
1.86 -timezone_olson_regexp = re.compile(timezone_olson_str, re.UNICODE)
1.87 -timezone_offset_regexp = re.compile(timezone_offset_str, re.UNICODE)
1.88 -datetime_regexp = re.compile(datetime_regexp_str, re.UNICODE)
1.89 -
1.90 -# iCalendar date and datetime parsing.
1.91 -
1.92 -date_icalendar_regexp_str = ur'(?P<year>[0-9]{4})(?P<month>[0-9]{2})(?P<day>[0-9]{2})'
1.93 -datetime_icalendar_regexp_str = date_icalendar_regexp_str + \
1.94 - ur'(?:' \
1.95 - ur'T(?P<hour>[0-2][0-9])(?P<minute>[0-5][0-9])(?P<second>[0-6][0-9])' \
1.96 - ur'(?P<utc>Z)?' \
1.97 - ur')?'
1.98 -
1.99 -date_icalendar_regexp = re.compile(date_icalendar_regexp_str, re.UNICODE)
1.100 -datetime_icalendar_regexp = re.compile(datetime_icalendar_regexp_str, re.UNICODE)
1.101 -
1.102 -# Content type parsing.
1.103 -
1.104 -encoding_regexp_str = ur'(?P<content_type>[^\s;]*)(?:;\s*charset=(?P<encoding>[-A-Za-z0-9]+))?'
1.105 -encoding_regexp = re.compile(encoding_regexp_str)
1.106
1.107 # Simple content parsing.
1.108
1.109 @@ -144,19 +93,6 @@
1.110 else:
1.111 return None
1.112
1.113 -def getContentTypeAndEncoding(content_type):
1.114 - m = encoding_regexp.search(content_type)
1.115 - if m:
1.116 - return m.group("content_type"), m.group("encoding")
1.117 - else:
1.118 - return None, None
1.119 -
1.120 -def int_or_none(x):
1.121 - if x is None:
1.122 - return x
1.123 - else:
1.124 - return int(x)
1.125 -
1.126 def to_list(s, sep):
1.127 return [x.strip() for x in s.split(sep) if x.strip()]
1.128
1.129 @@ -182,78 +118,11 @@
1.130 return start_order
1.131 return 0
1.132
1.133 -def sign(x):
1.134 - if x < 0:
1.135 - return -1
1.136 - else:
1.137 - return 1
1.138 -
1.139 # Utility classes and associated functions.
1.140
1.141 -class Form:
1.142 -
1.143 - """
1.144 - A wrapper preserving MoinMoin 1.8.x (and earlier) behaviour in a 1.9.x
1.145 - environment.
1.146 - """
1.147 -
1.148 - def __init__(self, form):
1.149 - self.form = form
1.150 -
1.151 - def has_key(self, name):
1.152 - return not not self.form.getlist(name)
1.153 -
1.154 - def get(self, name, default=None):
1.155 - values = self.form.getlist(name)
1.156 - if not values:
1.157 - return default
1.158 - else:
1.159 - return values
1.160 -
1.161 - def __getitem__(self, name):
1.162 - return self.form.getlist(name)
1.163 -
1.164 -class ActionSupport:
1.165 -
1.166 - """
1.167 - Work around disruptive MoinMoin changes in 1.9, and also provide useful
1.168 - convenience methods.
1.169 - """
1.170 +class ActionSupport(ActionSupport):
1.171
1.172 - def get_form(self):
1.173 - return get_form(self.request)
1.174 -
1.175 - def _get_selected(self, value, input_value):
1.176 -
1.177 - """
1.178 - Return the HTML attribute text indicating selection of an option (or
1.179 - otherwise) if 'value' matches 'input_value'.
1.180 - """
1.181 -
1.182 - return input_value is not None and value == input_value and 'selected="selected"' or ''
1.183 -
1.184 - def _get_selected_for_list(self, value, input_values):
1.185 -
1.186 - """
1.187 - Return the HTML attribute text indicating selection of an option (or
1.188 - otherwise) if 'value' matches one of the 'input_values'.
1.189 - """
1.190 -
1.191 - return value in input_values and 'selected="selected"' or ''
1.192 -
1.193 - def _get_input(self, form, name, default=None):
1.194 -
1.195 - """
1.196 - Return the input from 'form' having the given 'name', returning either
1.197 - the input converted to an integer or the given 'default' (optional, None
1.198 - if not specified).
1.199 - """
1.200 -
1.201 - value = form.get(name, [None])[0]
1.202 - if not value: # true if 0 obtained
1.203 - return default
1.204 - else:
1.205 - return int(value)
1.206 + "Extend the generic action support."
1.207
1.208 def get_month_lists(self, default_as_current=0):
1.209
1.210 @@ -311,50 +180,6 @@
1.211
1.212 return start_day_default, end_day_default
1.213
1.214 -def get_form(request):
1.215 -
1.216 - "Work around disruptive MoinMoin changes in 1.9."
1.217 -
1.218 - if hasattr(request, "values"):
1.219 - return Form(request.values)
1.220 - else:
1.221 - return request.form
1.222 -
1.223 -class send_headers_cls:
1.224 -
1.225 - """
1.226 - A wrapper to preserve MoinMoin 1.8.x (and earlier) request behaviour in a
1.227 - 1.9.x environment.
1.228 - """
1.229 -
1.230 - def __init__(self, request):
1.231 - self.request = request
1.232 -
1.233 - def __call__(self, headers):
1.234 - for header in headers:
1.235 - parts = header.split(":")
1.236 - self.request.headers.add(parts[0], ":".join(parts[1:]))
1.237 -
1.238 -def get_send_headers(request):
1.239 -
1.240 - "Return a function that can send response headers."
1.241 -
1.242 - if hasattr(request, "http_headers"):
1.243 - return request.http_headers
1.244 - elif hasattr(request, "emit_http_headers"):
1.245 - return request.emit_http_headers
1.246 - else:
1.247 - return send_headers_cls(request)
1.248 -
1.249 -def escattr(s):
1.250 - return escape(s, 1)
1.251 -
1.252 -def getPathInfo(request):
1.253 - if hasattr(request, "getPathinfo"):
1.254 - return request.getPathinfo()
1.255 - else:
1.256 - return request.path
1.257 -
1.258 # Textual representations.
1.259
1.260 def getSimpleWikiText(text):
1.261 @@ -381,76 +206,6 @@
1.262
1.263 return title.replace("_", " ").replace("/", u" » ")
1.264
1.265 -def getMonthLabel(month):
1.266 -
1.267 - "Return an unlocalised label for the given 'month'."
1.268 -
1.269 - return month_labels[month - 1] # zero-based labels
1.270 -
1.271 -def getDayLabel(weekday):
1.272 -
1.273 - "Return an unlocalised label for the given 'weekday'."
1.274 -
1.275 - return weekday_labels[weekday]
1.276 -
1.277 -def getNormalisedLocation(location):
1.278 -
1.279 - """
1.280 - Attempt to return a normalised 'location' of the form "<town>, <country>" or
1.281 - "<town>".
1.282 - """
1.283 -
1.284 - match = location_normalised_regexp.search(location)
1.285 - if match:
1.286 - return match.group("location")
1.287 - else:
1.288 - return None
1.289 -
1.290 -def getLocationPosition(location, locations):
1.291 -
1.292 - """
1.293 - Attempt to return the position of the given 'location' using the 'locations'
1.294 - dictionary provided. If no position can be found, return a latitude of None
1.295 - and a longitude of None.
1.296 - """
1.297 -
1.298 - latitude, longitude = None, None
1.299 -
1.300 - if location is not None:
1.301 - try:
1.302 - latitude, longitude = map(getMapReference, locations[location].split())
1.303 - except (KeyError, ValueError):
1.304 - pass
1.305 -
1.306 - return latitude, longitude
1.307 -
1.308 -# Action support functions.
1.309 -
1.310 -def getPageRevision(page):
1.311 -
1.312 - "Return the revision details dictionary for the given 'page'."
1.313 -
1.314 - # From Page.edit_info...
1.315 -
1.316 - if hasattr(page, "editlog_entry"):
1.317 - line = page.editlog_entry()
1.318 - else:
1.319 - line = page._last_edited(page.request) # MoinMoin 1.5.x and 1.6.x
1.320 -
1.321 - # Similar to Page.mtime_usecs behaviour...
1.322 -
1.323 - if line:
1.324 - timestamp = line.ed_time_usecs
1.325 - mtime = wikiutil.version2timestamp(long(timestamp)) # must be long for py 2.2.x
1.326 - comment = line.comment
1.327 - else:
1.328 - mtime = 0
1.329 - comment = ""
1.330 -
1.331 - # Leave the time zone empty.
1.332 -
1.333 - return {"timestamp" : DateTime(time.gmtime(mtime)[:6] + (None,)), "comment" : comment}
1.334 -
1.335 # Category discovery and searching.
1.336
1.337 def getCategories(request):
1.338 @@ -536,11 +291,6 @@
1.339
1.340 return [Page(request, page.page_name) for page in result_pages]
1.341
1.342 -# Interfaces.
1.343 -
1.344 -class ActsAsTimespan:
1.345 - pass
1.346 -
1.347 # Event resources providing collections of events.
1.348
1.349 class EventResource:
1.350 @@ -1596,719 +1346,6 @@
1.351
1.352 return scale
1.353
1.354 -# Date-related functions.
1.355 -
1.356 -def cmp_dates_as_day_start(a, b):
1.357 -
1.358 - """
1.359 - Compare dates/datetimes 'a' and 'b' treating dates without time information
1.360 - as the earliest time in a particular day.
1.361 - """
1.362 -
1.363 - are_equal = a == b
1.364 -
1.365 - if are_equal:
1.366 - a2 = a.as_datetime_or_date()
1.367 - b2 = b.as_datetime_or_date()
1.368 -
1.369 - if isinstance(a2, Date) and isinstance(b2, DateTime):
1.370 - return -1
1.371 - elif isinstance(a2, DateTime) and isinstance(b2, Date):
1.372 - return 1
1.373 -
1.374 - return cmp(a, b)
1.375 -
1.376 -class Convertible:
1.377 -
1.378 - "Support for converting temporal objects."
1.379 -
1.380 - def _get_converter(self, resolution):
1.381 - if resolution == "month":
1.382 - return lambda x: x and x.as_month()
1.383 - elif resolution == "date":
1.384 - return lambda x: x and x.as_date()
1.385 - elif resolution == "datetime":
1.386 - return lambda x: x and x.as_datetime_or_date()
1.387 - else:
1.388 - return lambda x: x
1.389 -
1.390 -class Temporal(Convertible):
1.391 -
1.392 - "A simple temporal representation, common to dates and times."
1.393 -
1.394 - def __init__(self, data):
1.395 - self.data = list(data)
1.396 -
1.397 - def __repr__(self):
1.398 - return "%s(%r)" % (self.__class__.__name__, self.data)
1.399 -
1.400 - def __hash__(self):
1.401 - return hash(self.as_tuple())
1.402 -
1.403 - def as_tuple(self):
1.404 - return tuple(self.data)
1.405 -
1.406 - def convert(self, resolution):
1.407 - return self._get_converter(resolution)(self)
1.408 -
1.409 - def __cmp__(self, other):
1.410 -
1.411 - """
1.412 - The result of comparing this instance with 'other' is derived from a
1.413 - comparison of the instances' date(time) data at the highest common
1.414 - resolution, meaning that if a date is compared to a datetime, the
1.415 - datetime will be considered as a date. Thus, a date and a datetime
1.416 - referring to the same date will be considered equal.
1.417 - """
1.418 -
1.419 - if not isinstance(other, Temporal):
1.420 - return NotImplemented
1.421 - else:
1.422 - data = self.as_tuple()
1.423 - other_data = other.as_tuple()
1.424 - length = min(len(data), len(other_data))
1.425 - return cmp(data[:length], other_data[:length])
1.426 -
1.427 - def __sub__(self, other):
1.428 -
1.429 - """
1.430 - Return the difference between this object and the 'other' object at the
1.431 - highest common accuracy of both objects.
1.432 - """
1.433 -
1.434 - if not isinstance(other, Temporal):
1.435 - return NotImplemented
1.436 - else:
1.437 - data = self.as_tuple()
1.438 - other_data = other.as_tuple()
1.439 - if len(data) < len(other_data):
1.440 - return len(self.until(other))
1.441 - else:
1.442 - return len(other.until(self))
1.443 -
1.444 - def _until(self, start, end, nextfn, prevfn):
1.445 -
1.446 - """
1.447 - Return a collection of units of time by starting from the given 'start'
1.448 - and stepping across intervening units until 'end' is reached, using the
1.449 - given 'nextfn' and 'prevfn' to step from one unit to the next.
1.450 - """
1.451 -
1.452 - current = start
1.453 - units = [current]
1.454 - if current < end:
1.455 - while current < end:
1.456 - current = nextfn(current)
1.457 - units.append(current)
1.458 - elif current > end:
1.459 - while current > end:
1.460 - current = prevfn(current)
1.461 - units.append(current)
1.462 - return units
1.463 -
1.464 - def ambiguous(self):
1.465 -
1.466 - "Only times can be ambiguous."
1.467 -
1.468 - return 0
1.469 -
1.470 -class Month(Temporal):
1.471 -
1.472 - "A simple year-month representation."
1.473 -
1.474 - def __str__(self):
1.475 - return "%04d-%02d" % self.as_tuple()[:2]
1.476 -
1.477 - def as_datetime(self, day, hour, minute, second, zone):
1.478 - return DateTime(self.as_tuple() + (day, hour, minute, second, zone))
1.479 -
1.480 - def as_date(self, day):
1.481 - if day < 0:
1.482 - weekday, ndays = self.month_properties()
1.483 - day = ndays + 1 + day
1.484 - return Date(self.as_tuple() + (day,))
1.485 -
1.486 - def as_month(self):
1.487 - return self
1.488 -
1.489 - def year(self):
1.490 - return self.data[0]
1.491 -
1.492 - def month(self):
1.493 - return self.data[1]
1.494 -
1.495 - def month_properties(self):
1.496 -
1.497 - """
1.498 - Return the weekday of the 1st of the month, along with the number of
1.499 - days, as a tuple.
1.500 - """
1.501 -
1.502 - year, month = self.as_tuple()[:2]
1.503 - return calendar.monthrange(year, month)
1.504 -
1.505 - def month_update(self, n=1):
1.506 -
1.507 - "Return the month updated by 'n' months."
1.508 -
1.509 - year, month = self.as_tuple()[:2]
1.510 - return Month((year + (month - 1 + n) / 12, (month - 1 + n) % 12 + 1))
1.511 -
1.512 - update = month_update
1.513 -
1.514 - def next_month(self):
1.515 -
1.516 - "Return the month following this one."
1.517 -
1.518 - return self.month_update(1)
1.519 -
1.520 - next = next_month
1.521 -
1.522 - def previous_month(self):
1.523 -
1.524 - "Return the month preceding this one."
1.525 -
1.526 - return self.month_update(-1)
1.527 -
1.528 - previous = previous_month
1.529 -
1.530 - def months_until(self, end):
1.531 -
1.532 - "Return the collection of months from this month until 'end'."
1.533 -
1.534 - return self._until(self.as_month(), end.as_month(), Month.next_month, Month.previous_month)
1.535 -
1.536 - until = months_until
1.537 -
1.538 -class Date(Month):
1.539 -
1.540 - "A simple year-month-day representation."
1.541 -
1.542 - def constrain(self):
1.543 - year, month, day = self.as_tuple()[:3]
1.544 -
1.545 - month = max(min(month, 12), 1)
1.546 - wd, last_day = calendar.monthrange(year, month)
1.547 - day = max(min(day, last_day), 1)
1.548 -
1.549 - self.data[1:3] = month, day
1.550 -
1.551 - def __str__(self):
1.552 - return "%04d-%02d-%02d" % self.as_tuple()[:3]
1.553 -
1.554 - def as_datetime(self, hour, minute, second, zone):
1.555 - return DateTime(self.as_tuple() + (hour, minute, second, zone))
1.556 -
1.557 - def as_start_of_day(self):
1.558 - return self.as_datetime(None, None, None, None)
1.559 -
1.560 - def as_date(self):
1.561 - return self
1.562 -
1.563 - def as_datetime_or_date(self):
1.564 - return self
1.565 -
1.566 - def as_month(self):
1.567 - return Month(self.data[:2])
1.568 -
1.569 - def day(self):
1.570 - return self.data[2]
1.571 -
1.572 - def day_update(self, n=1):
1.573 -
1.574 - "Return the month updated by 'n' days."
1.575 -
1.576 - delta = datetime.timedelta(n)
1.577 - dt = datetime.date(*self.as_tuple()[:3])
1.578 - dt_new = dt + delta
1.579 - return Date((dt_new.year, dt_new.month, dt_new.day))
1.580 -
1.581 - update = day_update
1.582 -
1.583 - def next_day(self):
1.584 -
1.585 - "Return the date following this one."
1.586 -
1.587 - year, month, day = self.as_tuple()[:3]
1.588 - _wd, end_day = calendar.monthrange(year, month)
1.589 - if day == end_day:
1.590 - if month == 12:
1.591 - return Date((year + 1, 1, 1))
1.592 - else:
1.593 - return Date((year, month + 1, 1))
1.594 - else:
1.595 - return Date((year, month, day + 1))
1.596 -
1.597 - next = next_day
1.598 -
1.599 - def previous_day(self):
1.600 -
1.601 - "Return the date preceding this one."
1.602 -
1.603 - year, month, day = self.as_tuple()[:3]
1.604 - if day == 1:
1.605 - if month == 1:
1.606 - return Date((year - 1, 12, 31))
1.607 - else:
1.608 - _wd, end_day = calendar.monthrange(year, month - 1)
1.609 - return Date((year, month - 1, end_day))
1.610 - else:
1.611 - return Date((year, month, day - 1))
1.612 -
1.613 - previous = previous_day
1.614 -
1.615 - def days_until(self, end):
1.616 -
1.617 - "Return the collection of days from this date until 'end'."
1.618 -
1.619 - return self._until(self.as_date(), end.as_date(), Date.next_day, Date.previous_day)
1.620 -
1.621 - until = days_until
1.622 -
1.623 -class DateTime(Date):
1.624 -
1.625 - "A simple date plus time representation."
1.626 -
1.627 - def constrain(self):
1.628 - Date.constrain(self)
1.629 -
1.630 - hour, minute, second = self.as_tuple()[3:6]
1.631 -
1.632 - if self.has_time():
1.633 - hour = max(min(hour, 23), 0)
1.634 - minute = max(min(minute, 59), 0)
1.635 -
1.636 - if second is not None:
1.637 - second = max(min(second, 60), 0) # support leap seconds
1.638 -
1.639 - self.data[3:6] = hour, minute, second
1.640 -
1.641 - def __str__(self):
1.642 - return Date.__str__(self) + self.time_string()
1.643 -
1.644 - def time_string(self):
1.645 - if self.has_time():
1.646 - data = self.as_tuple()
1.647 - time_str = " %02d:%02d" % data[3:5]
1.648 - if data[5] is not None:
1.649 - time_str += ":%02d" % data[5]
1.650 - if data[6] is not None:
1.651 - time_str += " %s" % data[6]
1.652 - return time_str
1.653 - else:
1.654 - return ""
1.655 -
1.656 - def as_HTTP_datetime_string(self):
1.657 - weekday = calendar.weekday(*self.data[:3])
1.658 - return "%s, %02d %s %04d %02d:%02d:%02d GMT" % ((
1.659 - getDayLabel(weekday),
1.660 - self.data[2],
1.661 - getMonthLabel(self.data[1]),
1.662 - self.data[0]
1.663 - ) + tuple(self.data[3:6]))
1.664 -
1.665 - def as_datetime(self):
1.666 - return self
1.667 -
1.668 - def as_date(self):
1.669 - return Date(self.data[:3])
1.670 -
1.671 - def as_datetime_or_date(self):
1.672 -
1.673 - """
1.674 - Return a date for this datetime if fields are missing. Otherwise, return
1.675 - this datetime itself.
1.676 - """
1.677 -
1.678 - if not self.has_time():
1.679 - return self.as_date()
1.680 - else:
1.681 - return self
1.682 -
1.683 - def __cmp__(self, other):
1.684 -
1.685 - """
1.686 - The result of comparing this instance with 'other' is, if both instances
1.687 - are datetime instances, derived from a comparison of the datetimes
1.688 - converted to UTC. If one or both datetimes cannot be converted to UTC,
1.689 - the datetimes are compared using the basic temporal comparison which
1.690 - compares their raw time data.
1.691 - """
1.692 -
1.693 - this = self
1.694 -
1.695 - if this.has_time():
1.696 - if isinstance(other, DateTime):
1.697 - if other.has_time():
1.698 - this_utc = this.to_utc()
1.699 - other_utc = other.to_utc()
1.700 - if this_utc is not None and other_utc is not None:
1.701 - return cmp(this_utc.as_tuple(), other_utc.as_tuple())
1.702 - else:
1.703 - other = other.padded()
1.704 - else:
1.705 - this = this.padded()
1.706 -
1.707 - return Date.__cmp__(this, other)
1.708 -
1.709 - def has_time(self):
1.710 -
1.711 - """
1.712 - Return whether this object has any time information. Objects without
1.713 - time information can refer to the very start of a day.
1.714 - """
1.715 -
1.716 - return self.data[3] is not None and self.data[4] is not None
1.717 -
1.718 - def time(self):
1.719 - return self.data[3:]
1.720 -
1.721 - def seconds(self):
1.722 - return self.data[5]
1.723 -
1.724 - def time_zone(self):
1.725 - return self.data[6]
1.726 -
1.727 - def set_time_zone(self, value):
1.728 - self.data[6] = value
1.729 -
1.730 - def padded(self, empty_value=0):
1.731 -
1.732 - """
1.733 - Return a datetime with missing fields defined as being the given
1.734 - 'empty_value' or 0 if not specified.
1.735 - """
1.736 -
1.737 - data = []
1.738 - for x in self.data[:6]:
1.739 - if x is None:
1.740 - data.append(empty_value)
1.741 - else:
1.742 - data.append(x)
1.743 -
1.744 - data += self.data[6:]
1.745 - return DateTime(data)
1.746 -
1.747 - def to_utc(self):
1.748 -
1.749 - """
1.750 - Return this object converted to UTC, or None if such a conversion is not
1.751 - defined.
1.752 - """
1.753 -
1.754 - if not self.has_time():
1.755 - return None
1.756 -
1.757 - offset = self.utc_offset()
1.758 - if offset:
1.759 - hours, minutes = offset
1.760 -
1.761 - # Invert the offset to get the correction.
1.762 -
1.763 - hours, minutes = -hours, -minutes
1.764 -
1.765 - # Get the components.
1.766 -
1.767 - hour, minute, second, zone = self.time()
1.768 - date = self.as_date()
1.769 -
1.770 - # Add the minutes and hours.
1.771 -
1.772 - minute += minutes
1.773 - if minute < 0 or minute > 59:
1.774 - hour += minute / 60
1.775 - minute = minute % 60
1.776 -
1.777 - # NOTE: This makes various assumptions and probably would not work
1.778 - # NOTE: for general arithmetic.
1.779 -
1.780 - hour += hours
1.781 - if hour < 0:
1.782 - date = date.previous_day()
1.783 - hour += 24
1.784 - elif hour > 23:
1.785 - date = date.next_day()
1.786 - hour -= 24
1.787 -
1.788 - return date.as_datetime(hour, minute, second, "UTC")
1.789 -
1.790 - # Cannot convert.
1.791 -
1.792 - else:
1.793 - return None
1.794 -
1.795 - def utc_offset(self):
1.796 -
1.797 - "Return the UTC offset in hours and minutes."
1.798 -
1.799 - zone = self.time_zone()
1.800 - if not zone:
1.801 - return None
1.802 -
1.803 - # Support explicit UTC zones.
1.804 -
1.805 - if zone == "UTC":
1.806 - return 0, 0
1.807 -
1.808 - # Attempt to return a UTC offset where an explicit offset has been set.
1.809 -
1.810 - match = timezone_offset_regexp.match(zone)
1.811 - if match:
1.812 - if match.group("sign") == "-":
1.813 - sign = -1
1.814 - else:
1.815 - sign = 1
1.816 -
1.817 - hours = int(match.group("hours")) * sign
1.818 - minutes = int(match.group("minutes") or 0) * sign
1.819 - return hours, minutes
1.820 -
1.821 - # Attempt to handle Olson time zone identifiers.
1.822 -
1.823 - dt = self.as_olson_datetime()
1.824 - if dt:
1.825 - seconds = dt.utcoffset().seconds
1.826 - hours = seconds / 3600
1.827 - minutes = (seconds % 3600) / 60
1.828 - return hours, minutes
1.829 -
1.830 - # Otherwise return None.
1.831 -
1.832 - return None
1.833 -
1.834 - def olson_identifier(self):
1.835 -
1.836 - "Return the Olson identifier from any zone information."
1.837 -
1.838 - zone = self.time_zone()
1.839 - if not zone:
1.840 - return None
1.841 -
1.842 - # Attempt to match an identifier.
1.843 -
1.844 - match = timezone_olson_regexp.match(zone)
1.845 - if match:
1.846 - return match.group("olson")
1.847 - else:
1.848 - return None
1.849 -
1.850 - def _as_olson_datetime(self, hours=None):
1.851 -
1.852 - """
1.853 - Return a Python datetime object for this datetime interpreted using any
1.854 - Olson time zone identifier and the given 'hours' offset, raising one of
1.855 - the pytz exceptions in case of ambiguity.
1.856 - """
1.857 -
1.858 - olson = self.olson_identifier()
1.859 - if olson and pytz:
1.860 - tz = pytz.timezone(olson)
1.861 - data = self.padded().as_tuple()[:6]
1.862 - dt = datetime.datetime(*data)
1.863 -
1.864 - # With an hours offset, find a time probably in a previously
1.865 - # applicable time zone.
1.866 -
1.867 - if hours is not None:
1.868 - td = datetime.timedelta(0, hours * 3600)
1.869 - dt += td
1.870 -
1.871 - ldt = tz.localize(dt, None)
1.872 -
1.873 - # With an hours offset, adjust the time to define it within the
1.874 - # previously applicable time zone but at the presumably intended
1.875 - # position.
1.876 -
1.877 - if hours is not None:
1.878 - ldt -= td
1.879 -
1.880 - return ldt
1.881 - else:
1.882 - return None
1.883 -
1.884 - def as_olson_datetime(self):
1.885 -
1.886 - """
1.887 - Return a Python datetime object for this datetime interpreted using any
1.888 - Olson time zone identifier, choosing the time from the zone before the
1.889 - period of ambiguity.
1.890 - """
1.891 -
1.892 - try:
1.893 - return self._as_olson_datetime()
1.894 - except (pytz.UnknownTimeZoneError, pytz.AmbiguousTimeError):
1.895 -
1.896 - # Try again, using an earlier local time and then stepping forward
1.897 - # in the chosen zone.
1.898 - # NOTE: Four hours earlier seems reasonable.
1.899 -
1.900 - return self._as_olson_datetime(-4)
1.901 -
1.902 - def ambiguous(self):
1.903 -
1.904 - "Return whether the time is local and ambiguous."
1.905 -
1.906 - try:
1.907 - self._as_olson_datetime()
1.908 - except (pytz.UnknownTimeZoneError, pytz.AmbiguousTimeError):
1.909 - return 1
1.910 -
1.911 - return 0
1.912 -
1.913 -class Timespan(ActsAsTimespan, Convertible):
1.914 -
1.915 - """
1.916 - A period of time which can be compared against others to check for overlaps.
1.917 - """
1.918 -
1.919 - def __init__(self, start, end):
1.920 - self.start = start
1.921 - self.end = end
1.922 -
1.923 - # NOTE: Should perhaps catch ambiguous time problems elsewhere.
1.924 -
1.925 - if self.ambiguous() and self.start is not None and self.end is not None and start > end:
1.926 - self.start, self.end = end, start
1.927 -
1.928 - def __repr__(self):
1.929 - return "%s(%r, %r)" % (self.__class__.__name__, self.start, self.end)
1.930 -
1.931 - def __hash__(self):
1.932 - return hash((self.start, self.end))
1.933 -
1.934 - def as_timespan(self):
1.935 - return self
1.936 -
1.937 - def as_limits(self):
1.938 - return self.start, self.end
1.939 -
1.940 - def ambiguous(self):
1.941 - return self.start is not None and self.start.ambiguous() or self.end is not None and self.end.ambiguous()
1.942 -
1.943 - def convert(self, resolution):
1.944 - return Timespan(*map(self._get_converter(resolution), self.as_limits()))
1.945 -
1.946 - def is_before(self, a, b):
1.947 -
1.948 - """
1.949 - Return whether 'a' is before 'b'. Since the end datetime of one period
1.950 - may be the same as the start datetime of another period, and yet the
1.951 - first period is intended to be concluded by the end datetime and not
1.952 - overlap with the other period, a different test is employed for datetime
1.953 - comparisons.
1.954 - """
1.955 -
1.956 - # Datetimes without times can be equal to dates and be considered as
1.957 - # occurring before those dates. Generally, datetimes should not be
1.958 - # produced without time information as getDateTime converts such
1.959 - # datetimes to dates.
1.960 -
1.961 - if isinstance(a, DateTime) and (isinstance(b, DateTime) or not a.has_time()):
1.962 - return a <= b
1.963 - else:
1.964 - return a < b
1.965 -
1.966 - def __contains__(self, other):
1.967 -
1.968 - """
1.969 - This instance is considered to contain 'other' if one is not before or
1.970 - after the other. If this instance overlaps or coincides with 'other',
1.971 - then 'other' is regarded as belonging to this instance's time period.
1.972 - """
1.973 -
1.974 - return self == other
1.975 -
1.976 - def __cmp__(self, other):
1.977 -
1.978 - """
1.979 - Return whether this timespan occupies the same period of time as the
1.980 - 'other'. Timespans are considered less than others if their end points
1.981 - precede the other's start point, and are considered greater than others
1.982 - if their start points follow the other's end point.
1.983 - """
1.984 -
1.985 - if isinstance(other, ActsAsTimespan):
1.986 - other = other.as_timespan()
1.987 -
1.988 - if self.end is not None and other.start is not None and self.is_before(self.end, other.start):
1.989 - return -1
1.990 - elif self.start is not None and other.end is not None and self.is_before(other.end, self.start):
1.991 - return 1
1.992 - else:
1.993 - return 0
1.994 -
1.995 - else:
1.996 - if self.end is not None and self.is_before(self.end, other):
1.997 - return -1
1.998 - elif self.start is not None and self.is_before(other, self.start):
1.999 - return 1
1.1000 - else:
1.1001 - return 0
1.1002 -
1.1003 -class TimespanCollection:
1.1004 -
1.1005 - """
1.1006 - A class providing a list-like interface supporting membership tests at a
1.1007 - particular resolution in order to maintain a collection of non-overlapping
1.1008 - timespans.
1.1009 - """
1.1010 -
1.1011 - def __init__(self, resolution, values=None):
1.1012 - self.resolution = resolution
1.1013 - self.values = values or []
1.1014 -
1.1015 - def as_timespan(self):
1.1016 - return Timespan(*self.as_limits())
1.1017 -
1.1018 - def as_limits(self):
1.1019 -
1.1020 - "Return the earliest and latest points in time for this collection."
1.1021 -
1.1022 - if not self.values:
1.1023 - return None, None
1.1024 - else:
1.1025 - first, last = self.values[0], self.values[-1]
1.1026 - if isinstance(first, ActsAsTimespan):
1.1027 - first = first.as_timespan().start
1.1028 - if isinstance(last, ActsAsTimespan):
1.1029 - last = last.as_timespan().end
1.1030 - return first, last
1.1031 -
1.1032 - def convert(self, value):
1.1033 - if isinstance(value, ActsAsTimespan):
1.1034 - ts = value.as_timespan()
1.1035 - return ts and ts.convert(self.resolution)
1.1036 - else:
1.1037 - return value.convert(self.resolution)
1.1038 -
1.1039 - def __iter__(self):
1.1040 - return iter(self.values)
1.1041 -
1.1042 - def __len__(self):
1.1043 - return len(self.values)
1.1044 -
1.1045 - def __getitem__(self, i):
1.1046 - return self.values[i]
1.1047 -
1.1048 - def __setitem__(self, i, value):
1.1049 - self.values[i] = value
1.1050 -
1.1051 - def __contains__(self, value):
1.1052 - test_value = self.convert(value)
1.1053 - return test_value in self.values
1.1054 -
1.1055 - def append(self, value):
1.1056 - self.values.append(value)
1.1057 -
1.1058 - def insert(self, i, value):
1.1059 - self.values.insert(i, value)
1.1060 -
1.1061 - def pop(self):
1.1062 - return self.values.pop()
1.1063 -
1.1064 - def insert_in_order(self, value):
1.1065 - bisect.insort_left(self, value)
1.1066 -
1.1067 def getCountry(s):
1.1068
1.1069 "Find a country code in the given string 's'."
1.1070 @@ -2320,444 +1357,8 @@
1.1071 else:
1.1072 return None
1.1073
1.1074 -def getDate(s):
1.1075 -
1.1076 - "Parse the string 's', extracting and returning a date object."
1.1077 -
1.1078 - dt = getDateTime(s)
1.1079 - if dt is not None:
1.1080 - return dt.as_date()
1.1081 - else:
1.1082 - return None
1.1083 -
1.1084 -def getDateTime(s):
1.1085 -
1.1086 - """
1.1087 - Parse the string 's', extracting and returning a datetime object where time
1.1088 - information has been given or a date object where time information is
1.1089 - absent.
1.1090 - """
1.1091 -
1.1092 - m = datetime_regexp.search(s)
1.1093 - if m:
1.1094 - groups = list(m.groups())
1.1095 -
1.1096 - # Convert date and time data to integer or None.
1.1097 -
1.1098 - return DateTime(map(int_or_none, groups[:6]) + [m.group("zone")]).as_datetime_or_date()
1.1099 - else:
1.1100 - return None
1.1101 -
1.1102 -def getDateFromCalendar(s):
1.1103 -
1.1104 - """
1.1105 - Parse the iCalendar format string 's', extracting and returning a date
1.1106 - object.
1.1107 - """
1.1108 -
1.1109 - dt = getDateTimeFromCalendar(s)
1.1110 - if dt is not None:
1.1111 - return dt.as_date()
1.1112 - else:
1.1113 - return None
1.1114 -
1.1115 -def getDateTimeFromCalendar(s):
1.1116 -
1.1117 - """
1.1118 - Parse the iCalendar format datetime string 's', extracting and returning a
1.1119 - datetime object where time information has been given or a date object where
1.1120 - time information is absent.
1.1121 - """
1.1122 -
1.1123 - m = datetime_icalendar_regexp.search(s)
1.1124 - if m:
1.1125 - groups = list(m.groups())
1.1126 -
1.1127 - # Convert date and time data to integer or None.
1.1128 -
1.1129 - return DateTime(map(int_or_none, groups[:6]) + [m.group("utc") and "UTC" or None]).as_datetime_or_date()
1.1130 - else:
1.1131 - return None
1.1132 -
1.1133 -def getDateStrings(s):
1.1134 -
1.1135 - "Parse the string 's', extracting and returning all date strings."
1.1136 -
1.1137 - start = 0
1.1138 - m = date_regexp.search(s, start)
1.1139 - l = []
1.1140 - while m:
1.1141 - l.append("-".join(m.groups()))
1.1142 - m = date_regexp.search(s, m.end())
1.1143 - return l
1.1144 -
1.1145 -def getMonth(s):
1.1146 -
1.1147 - "Parse the string 's', extracting and returning a month object."
1.1148 -
1.1149 - m = month_regexp.search(s)
1.1150 - if m:
1.1151 - return Month(map(int, m.groups()))
1.1152 - else:
1.1153 - return None
1.1154 -
1.1155 -def getCurrentDate():
1.1156 -
1.1157 - "Return the current date as a (year, month, day) tuple."
1.1158 -
1.1159 - today = datetime.date.today()
1.1160 - return Date((today.year, today.month, today.day))
1.1161 -
1.1162 -def getCurrentMonth():
1.1163 -
1.1164 - "Return the current month as a (year, month) tuple."
1.1165 -
1.1166 - today = datetime.date.today()
1.1167 - return Month((today.year, today.month))
1.1168 -
1.1169 -def getCurrentYear():
1.1170 -
1.1171 - "Return the current year."
1.1172 -
1.1173 - today = datetime.date.today()
1.1174 - return today.year
1.1175 -
1.1176 -# Location-related functions.
1.1177 -
1.1178 -class Reference:
1.1179 -
1.1180 - "A map reference."
1.1181 -
1.1182 - def __init__(self, degrees, minutes=0, seconds=0):
1.1183 - self.degrees = degrees
1.1184 - self.minutes = minutes
1.1185 - self.seconds = seconds
1.1186 -
1.1187 - def __repr__(self):
1.1188 - return "Reference(%d, %d, %f)" % (self.degrees, self.minutes, self.seconds)
1.1189 -
1.1190 - def __str__(self):
1.1191 - return "%d:%d:%f" % (self.degrees, self.minutes, self.seconds)
1.1192 -
1.1193 - def __add__(self, other):
1.1194 - if not isinstance(other, Reference):
1.1195 - return NotImplemented
1.1196 - else:
1.1197 - s = sign(self.degrees)
1.1198 - o = sign(other.degrees)
1.1199 - carry, seconds = adc(s * self.seconds, o * other.seconds)
1.1200 - carry, minutes = adc(s * self.minutes, o * other.minutes + carry)
1.1201 - return Reference(self.degrees + other.degrees + carry, minutes, seconds)
1.1202 -
1.1203 - def __sub__(self, other):
1.1204 - if not isinstance(other, Reference):
1.1205 - return NotImplemented
1.1206 - else:
1.1207 - return self.__add__(Reference(-other.degrees, other.minutes, other.seconds))
1.1208 -
1.1209 - def _compare(self, op, other):
1.1210 - if not isinstance(other, Reference):
1.1211 - return NotImplemented
1.1212 - else:
1.1213 - return op(self.to_degrees(), other.to_degrees())
1.1214 -
1.1215 - def __eq__(self, other):
1.1216 - return self._compare(operator.eq, other)
1.1217 -
1.1218 - def __ne__(self, other):
1.1219 - return self._compare(operator.ne, other)
1.1220 -
1.1221 - def __lt__(self, other):
1.1222 - return self._compare(operator.lt, other)
1.1223 -
1.1224 - def __le__(self, other):
1.1225 - return self._compare(operator.le, other)
1.1226 -
1.1227 - def __gt__(self, other):
1.1228 - return self._compare(operator.gt, other)
1.1229 -
1.1230 - def __ge__(self, other):
1.1231 - return self._compare(operator.ge, other)
1.1232 -
1.1233 - def to_degrees(self):
1.1234 - return sign(self.degrees) * (abs(self.degrees) + self.minutes / 60.0 + self.seconds / 3600.0)
1.1235 -
1.1236 - def to_pixels(self, scale):
1.1237 - return self.to_degrees() * scale
1.1238 -
1.1239 -def adc(x, y):
1.1240 - result = x + y
1.1241 - return divmod(result, 60)
1.1242 -
1.1243 -def getPositionForReference(latitude, longitude, map_y, map_x, map_x_scale, map_y_scale):
1.1244 - return (longitude - map_x).to_pixels(map_x_scale), (latitude - map_y).to_pixels(map_y_scale)
1.1245 -
1.1246 -def getPositionForCentrePoint(position, map_x_scale, map_y_scale):
1.1247 - x, y = position
1.1248 - return x - map_x_scale / 2.0, y - map_y_scale / 2.0
1.1249 -
1.1250 -def getMapReference(value):
1.1251 -
1.1252 - "Return a map reference by parsing the given 'value'."
1.1253 -
1.1254 - if value.find(":") != -1:
1.1255 - return getMapReferenceFromDMS(value)
1.1256 - else:
1.1257 - return getMapReferenceFromDecimal(value)
1.1258 -
1.1259 -def getMapReferenceFromDMS(value):
1.1260 -
1.1261 - """
1.1262 - Return a map reference by parsing the given 'value' expressed as degrees,
1.1263 - minutes, seconds.
1.1264 - """
1.1265 -
1.1266 - values = value.split(":")
1.1267 - values = map(int, values[:2]) + map(float, values[2:3])
1.1268 - return Reference(*values)
1.1269 -
1.1270 -def getMapReferenceFromDecimal(value):
1.1271 -
1.1272 - "Return a map reference by parsing the given 'value' in decimal degrees."
1.1273 -
1.1274 - value = float(value)
1.1275 - degrees, remainder = divmod(abs(value * 3600), 3600)
1.1276 - minutes, seconds = divmod(remainder, 60)
1.1277 - return Reference(sign(value) * degrees, minutes, seconds)
1.1278 -
1.1279 -# User interface functions.
1.1280 -
1.1281 -def getParameter(request, name, default=None):
1.1282 -
1.1283 - """
1.1284 - Using the given 'request', return the value of the parameter with the given
1.1285 - 'name', returning the optional 'default' (or None) if no value was supplied
1.1286 - in the 'request'.
1.1287 - """
1.1288 -
1.1289 - return get_form(request).get(name, [default])[0]
1.1290 -
1.1291 -def getQualifiedParameter(request, calendar_name, argname, default=None):
1.1292 -
1.1293 - """
1.1294 - Using the given 'request', 'calendar_name' and 'argname', retrieve the
1.1295 - value of the qualified parameter, returning the optional 'default' (or None)
1.1296 - if no value was supplied in the 'request'.
1.1297 - """
1.1298 -
1.1299 - argname = getQualifiedParameterName(calendar_name, argname)
1.1300 - return getParameter(request, argname, default)
1.1301 -
1.1302 -def getQualifiedParameterName(calendar_name, argname):
1.1303 -
1.1304 - """
1.1305 - Return the qualified parameter name using the given 'calendar_name' and
1.1306 - 'argname'.
1.1307 - """
1.1308 -
1.1309 - if calendar_name is None:
1.1310 - return argname
1.1311 - else:
1.1312 - return "%s-%s" % (calendar_name, argname)
1.1313 -
1.1314 -def getParameterDate(arg):
1.1315 -
1.1316 - "Interpret 'arg', recognising keywords and simple arithmetic operations."
1.1317 -
1.1318 - n = None
1.1319 -
1.1320 - if arg is None:
1.1321 - return None
1.1322 -
1.1323 - elif arg.startswith("current"):
1.1324 - date = getCurrentDate()
1.1325 - if len(arg) > 8:
1.1326 - n = int(arg[7:])
1.1327 -
1.1328 - elif arg.startswith("yearstart"):
1.1329 - date = Date((getCurrentYear(), 1, 1))
1.1330 - if len(arg) > 10:
1.1331 - n = int(arg[9:])
1.1332 -
1.1333 - elif arg.startswith("yearend"):
1.1334 - date = Date((getCurrentYear(), 12, 31))
1.1335 - if len(arg) > 8:
1.1336 - n = int(arg[7:])
1.1337 -
1.1338 - else:
1.1339 - date = getDate(arg)
1.1340 -
1.1341 - if n is not None:
1.1342 - date = date.day_update(n)
1.1343 -
1.1344 - return date
1.1345 -
1.1346 -def getParameterMonth(arg):
1.1347 -
1.1348 - "Interpret 'arg', recognising keywords and simple arithmetic operations."
1.1349 -
1.1350 - n = None
1.1351 -
1.1352 - if arg is None:
1.1353 - return None
1.1354 -
1.1355 - elif arg.startswith("current"):
1.1356 - date = getCurrentMonth()
1.1357 - if len(arg) > 8:
1.1358 - n = int(arg[7:])
1.1359 -
1.1360 - elif arg.startswith("yearstart"):
1.1361 - date = Month((getCurrentYear(), 1))
1.1362 - if len(arg) > 10:
1.1363 - n = int(arg[9:])
1.1364 -
1.1365 - elif arg.startswith("yearend"):
1.1366 - date = Month((getCurrentYear(), 12))
1.1367 - if len(arg) > 8:
1.1368 - n = int(arg[7:])
1.1369 -
1.1370 - else:
1.1371 - date = getMonth(arg)
1.1372 -
1.1373 - if n is not None:
1.1374 - date = date.month_update(n)
1.1375 -
1.1376 - return date
1.1377 -
1.1378 -def getFormDate(request, calendar_name, argname):
1.1379 -
1.1380 - """
1.1381 - Return the date from the 'request' for the calendar with the given
1.1382 - 'calendar_name' using the parameter having the given 'argname'.
1.1383 - """
1.1384 -
1.1385 - arg = getQualifiedParameter(request, calendar_name, argname)
1.1386 - return getParameterDate(arg)
1.1387 -
1.1388 -def getFormMonth(request, calendar_name, argname):
1.1389 -
1.1390 - """
1.1391 - Return the month from the 'request' for the calendar with the given
1.1392 - 'calendar_name' using the parameter having the given 'argname'.
1.1393 - """
1.1394 -
1.1395 - arg = getQualifiedParameter(request, calendar_name, argname)
1.1396 - return getParameterMonth(arg)
1.1397 -
1.1398 -def getFormDateTriple(request, yeararg, montharg, dayarg):
1.1399 -
1.1400 - """
1.1401 - Return the date from the 'request' for the calendar with the given
1.1402 - 'calendar_name' using the parameters having the given 'yeararg', 'montharg'
1.1403 - and 'dayarg' names.
1.1404 - """
1.1405 -
1.1406 - year = getParameter(request, yeararg)
1.1407 - month = getParameter(request, montharg)
1.1408 - day = getParameter(request, dayarg)
1.1409 - if year and month and day:
1.1410 - return Date((int(year), int(month), int(day)))
1.1411 - else:
1.1412 - return None
1.1413 -
1.1414 -def getFormMonthPair(request, yeararg, montharg):
1.1415 -
1.1416 - """
1.1417 - Return the month from the 'request' for the calendar with the given
1.1418 - 'calendar_name' using the parameters having the given 'yeararg' and
1.1419 - 'montharg' names.
1.1420 - """
1.1421 -
1.1422 - year = getParameter(request, yeararg)
1.1423 - month = getParameter(request, montharg)
1.1424 - if year and month:
1.1425 - return Month((int(year), int(month)))
1.1426 - else:
1.1427 - return None
1.1428 -
1.1429 -def getFullDateLabel(request, date):
1.1430 -
1.1431 - """
1.1432 - Return the full month plus year label using the given 'request' and
1.1433 - 'year_month'.
1.1434 - """
1.1435 -
1.1436 - if not date:
1.1437 - return ""
1.1438 -
1.1439 - _ = request.getText
1.1440 - year, month, day = date.as_tuple()[:3]
1.1441 - start_weekday, number_of_days = date.month_properties()
1.1442 - weekday = (start_weekday + day - 1) % 7
1.1443 - day_label = _(getDayLabel(weekday))
1.1444 - month_label = _(getMonthLabel(month))
1.1445 - return "%s %s %s %s" % (day_label, day, month_label, year)
1.1446 -
1.1447 -def getFullMonthLabel(request, year_month):
1.1448 -
1.1449 - """
1.1450 - Return the full month plus year label using the given 'request' and
1.1451 - 'year_month'.
1.1452 - """
1.1453 -
1.1454 - if not year_month:
1.1455 - return ""
1.1456 -
1.1457 - _ = request.getText
1.1458 - year, month = year_month.as_tuple()[:2]
1.1459 - month_label = _(getMonthLabel(month))
1.1460 - return "%s %s" % (month_label, year)
1.1461 -
1.1462 # Page-related functions.
1.1463
1.1464 -def getPrettyPageName(page):
1.1465 -
1.1466 - "Return a nicely formatted title/name for the given 'page'."
1.1467 -
1.1468 - title = page.split_title(force=1)
1.1469 - return getPrettyTitle(title)
1.1470 -
1.1471 -def linkToPage(request, page, text, query_string=None):
1.1472 -
1.1473 - """
1.1474 - Using 'request', return a link to 'page' with the given link 'text' and
1.1475 - optional 'query_string'.
1.1476 - """
1.1477 -
1.1478 - text = wikiutil.escape(text)
1.1479 - return page.link_to_raw(request, text, query_string)
1.1480 -
1.1481 -def linkToResource(url, request, text, query_string=None):
1.1482 -
1.1483 - """
1.1484 - Using 'request', return a link to 'url' with the given link 'text' and
1.1485 - optional 'query_string'.
1.1486 - """
1.1487 -
1.1488 - if query_string:
1.1489 - query_string = wikiutil.makeQueryString(query_string)
1.1490 - url = "%s?%s" % (url, query_string)
1.1491 -
1.1492 - formatter = request.page and getattr(request.page, "formatter", None) or request.html_formatter
1.1493 -
1.1494 - output = []
1.1495 - output.append(formatter.url(1, url))
1.1496 - output.append(formatter.text(text))
1.1497 - output.append(formatter.url(0))
1.1498 - return "".join(output)
1.1499 -
1.1500 -def getFullPageName(parent, title):
1.1501 -
1.1502 - """
1.1503 - Return a full page name from the given 'parent' page (can be empty or None)
1.1504 - and 'title' (a simple page name).
1.1505 - """
1.1506 -
1.1507 - if parent:
1.1508 - return "%s/%s" % (parent.rstrip("/"), title)
1.1509 - else:
1.1510 - return title
1.1511 -
1.1512 def fillEventPageFromTemplate(template_page, new_page, event_details, category_pagenames):
1.1513
1.1514 """