# HG changeset patch # User Paul Boddie # Date 1327186762 -3600 # Node ID 22c22e6ad66f8c4155e5557fdd5c9ec5ddce9e26 # Parent 7f9841140b6b3e1eebb7fcddcbe0fad66633f26b Moved much of EventAggregatorSupport to the new MoinSupport distribution. Updated the copyright and release details. diff -r 7f9841140b6b -r 22c22e6ad66f EventAggregatorSupport.py --- a/EventAggregatorSupport.py Mon Dec 05 23:44:32 2011 +0100 +++ b/EventAggregatorSupport.py Sat Jan 21 23:59:22 2012 +0100 @@ -2,24 +2,25 @@ """ MoinMoin - EventAggregator library - @copyright: 2008, 2009, 2010, 2011 by Paul Boddie + @copyright: 2008, 2009, 2010, 2011, 2012 by Paul Boddie @copyright: 2000-2004 Juergen Hermann , 2005-2008 MoinMoin:ThomasWaldmann. @license: GNU GPL (v2 or later), see COPYING.txt for details. """ +from LocationSupport import * +from MoinDateSupport import * +from MoinSupport import * + from MoinMoin.Page import Page from MoinMoin.action import cache from MoinMoin import caching -from MoinMoin import search, version +from MoinMoin import search from MoinMoin import wikiutil -import calendar + import codecs -import datetime import time import re -import bisect -import operator import urllib, urllib2 try: @@ -33,24 +34,13 @@ from sets import Set as set try: - import pytz -except ImportError: - pytz = None - -try: import vCalendar except ImportError: vCalendar = None escape = wikiutil.escape -__version__ = "0.8.1" - -# Date labels. - -month_labels = ["January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December"] -weekday_labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] +__version__ = "0.9" # Regular expressions where MoinMoin does not provide the required support. @@ -64,47 +54,6 @@ # Value parsing. country_code_regexp = re.compile(ur'(?:^|\W)(?P[A-Z]{2})(?:$|\W+$)', re.UNICODE) -location_normalised_regexp = re.compile( - ur"(?:\d+\w*\s+)?" # preceding postcode (optional) - ur"(?P" # start of group of interest - ur"\w[\w\s-]+?" # area or town - ur"(?:,(?:\s*[\w-]+)+)?" # country (optional) - ur")$", re.UNICODE) - -# Month, date, time and datetime parsing. - -month_regexp_str = ur'(?P[0-9]{4})-(?P[0-9]{2})' -date_regexp_str = ur'(?P[0-9]{4})-(?P[0-9]{2})-(?P[0-9]{2})' -time_regexp_str = ur'(?P[0-2][0-9]):(?P[0-5][0-9])(?::(?P[0-6][0-9]))?' -timezone_offset_str = ur'(?P(UTC)?(?:(?P[-+])(?P[0-9]{2})(?::?(?P[0-9]{2}))?))' -timezone_olson_str = ur'(?P[a-zA-Z]+(?:/[-_a-zA-Z]+){1,2})' -timezone_utc_str = ur'UTC' -timezone_regexp_str = ur'(?P' + timezone_offset_str + '|' + timezone_olson_str + '|' + timezone_utc_str + ')' -datetime_regexp_str = date_regexp_str + ur'(?:\s+' + time_regexp_str + ur'(?:\s+' + timezone_regexp_str + ur')?)?' - -month_regexp = re.compile(month_regexp_str, re.UNICODE) -date_regexp = re.compile(date_regexp_str, re.UNICODE) -time_regexp = re.compile(time_regexp_str, re.UNICODE) -timezone_olson_regexp = re.compile(timezone_olson_str, re.UNICODE) -timezone_offset_regexp = re.compile(timezone_offset_str, re.UNICODE) -datetime_regexp = re.compile(datetime_regexp_str, re.UNICODE) - -# iCalendar date and datetime parsing. - -date_icalendar_regexp_str = ur'(?P[0-9]{4})(?P[0-9]{2})(?P[0-9]{2})' -datetime_icalendar_regexp_str = date_icalendar_regexp_str + \ - ur'(?:' \ - ur'T(?P[0-2][0-9])(?P[0-5][0-9])(?P[0-6][0-9])' \ - ur'(?PZ)?' \ - ur')?' - -date_icalendar_regexp = re.compile(date_icalendar_regexp_str, re.UNICODE) -datetime_icalendar_regexp = re.compile(datetime_icalendar_regexp_str, re.UNICODE) - -# Content type parsing. - -encoding_regexp_str = ur'(?P[^\s;]*)(?:;\s*charset=(?P[-A-Za-z0-9]+))?' -encoding_regexp = re.compile(encoding_regexp_str) # Simple content parsing. @@ -144,19 +93,6 @@ else: return None -def getContentTypeAndEncoding(content_type): - m = encoding_regexp.search(content_type) - if m: - return m.group("content_type"), m.group("encoding") - else: - return None, None - -def int_or_none(x): - if x is None: - return x - else: - return int(x) - def to_list(s, sep): return [x.strip() for x in s.split(sep) if x.strip()] @@ -182,78 +118,11 @@ return start_order return 0 -def sign(x): - if x < 0: - return -1 - else: - return 1 - # Utility classes and associated functions. -class Form: - - """ - A wrapper preserving MoinMoin 1.8.x (and earlier) behaviour in a 1.9.x - environment. - """ - - def __init__(self, form): - self.form = form - - def has_key(self, name): - return not not self.form.getlist(name) - - def get(self, name, default=None): - values = self.form.getlist(name) - if not values: - return default - else: - return values - - def __getitem__(self, name): - return self.form.getlist(name) - -class ActionSupport: - - """ - Work around disruptive MoinMoin changes in 1.9, and also provide useful - convenience methods. - """ +class ActionSupport(ActionSupport): - def get_form(self): - return get_form(self.request) - - def _get_selected(self, value, input_value): - - """ - Return the HTML attribute text indicating selection of an option (or - otherwise) if 'value' matches 'input_value'. - """ - - return input_value is not None and value == input_value and 'selected="selected"' or '' - - def _get_selected_for_list(self, value, input_values): - - """ - Return the HTML attribute text indicating selection of an option (or - otherwise) if 'value' matches one of the 'input_values'. - """ - - return value in input_values and 'selected="selected"' or '' - - def _get_input(self, form, name, default=None): - - """ - Return the input from 'form' having the given 'name', returning either - the input converted to an integer or the given 'default' (optional, None - if not specified). - """ - - value = form.get(name, [None])[0] - if not value: # true if 0 obtained - return default - else: - return int(value) + "Extend the generic action support." def get_month_lists(self, default_as_current=0): @@ -311,50 +180,6 @@ return start_day_default, end_day_default -def get_form(request): - - "Work around disruptive MoinMoin changes in 1.9." - - if hasattr(request, "values"): - return Form(request.values) - else: - return request.form - -class send_headers_cls: - - """ - A wrapper to preserve MoinMoin 1.8.x (and earlier) request behaviour in a - 1.9.x environment. - """ - - def __init__(self, request): - self.request = request - - def __call__(self, headers): - for header in headers: - parts = header.split(":") - self.request.headers.add(parts[0], ":".join(parts[1:])) - -def get_send_headers(request): - - "Return a function that can send response headers." - - if hasattr(request, "http_headers"): - return request.http_headers - elif hasattr(request, "emit_http_headers"): - return request.emit_http_headers - else: - return send_headers_cls(request) - -def escattr(s): - return escape(s, 1) - -def getPathInfo(request): - if hasattr(request, "getPathinfo"): - return request.getPathinfo() - else: - return request.path - # Textual representations. def getSimpleWikiText(text): @@ -381,76 +206,6 @@ return title.replace("_", " ").replace("/", u" » ") -def getMonthLabel(month): - - "Return an unlocalised label for the given 'month'." - - return month_labels[month - 1] # zero-based labels - -def getDayLabel(weekday): - - "Return an unlocalised label for the given 'weekday'." - - return weekday_labels[weekday] - -def getNormalisedLocation(location): - - """ - Attempt to return a normalised 'location' of the form ", " or - "". - """ - - match = location_normalised_regexp.search(location) - if match: - return match.group("location") - else: - return None - -def getLocationPosition(location, locations): - - """ - Attempt to return the position of the given 'location' using the 'locations' - dictionary provided. If no position can be found, return a latitude of None - and a longitude of None. - """ - - latitude, longitude = None, None - - if location is not None: - try: - latitude, longitude = map(getMapReference, locations[location].split()) - except (KeyError, ValueError): - pass - - return latitude, longitude - -# Action support functions. - -def getPageRevision(page): - - "Return the revision details dictionary for the given 'page'." - - # From Page.edit_info... - - if hasattr(page, "editlog_entry"): - line = page.editlog_entry() - else: - line = page._last_edited(page.request) # MoinMoin 1.5.x and 1.6.x - - # Similar to Page.mtime_usecs behaviour... - - if line: - timestamp = line.ed_time_usecs - mtime = wikiutil.version2timestamp(long(timestamp)) # must be long for py 2.2.x - comment = line.comment - else: - mtime = 0 - comment = "" - - # Leave the time zone empty. - - return {"timestamp" : DateTime(time.gmtime(mtime)[:6] + (None,)), "comment" : comment} - # Category discovery and searching. def getCategories(request): @@ -536,11 +291,6 @@ return [Page(request, page.page_name) for page in result_pages] -# Interfaces. - -class ActsAsTimespan: - pass - # Event resources providing collections of events. class EventResource: @@ -1596,719 +1346,6 @@ return scale -# Date-related functions. - -def cmp_dates_as_day_start(a, b): - - """ - Compare dates/datetimes 'a' and 'b' treating dates without time information - as the earliest time in a particular day. - """ - - are_equal = a == b - - if are_equal: - a2 = a.as_datetime_or_date() - b2 = b.as_datetime_or_date() - - if isinstance(a2, Date) and isinstance(b2, DateTime): - return -1 - elif isinstance(a2, DateTime) and isinstance(b2, Date): - return 1 - - return cmp(a, b) - -class Convertible: - - "Support for converting temporal objects." - - def _get_converter(self, resolution): - if resolution == "month": - return lambda x: x and x.as_month() - elif resolution == "date": - return lambda x: x and x.as_date() - elif resolution == "datetime": - return lambda x: x and x.as_datetime_or_date() - else: - return lambda x: x - -class Temporal(Convertible): - - "A simple temporal representation, common to dates and times." - - def __init__(self, data): - self.data = list(data) - - def __repr__(self): - return "%s(%r)" % (self.__class__.__name__, self.data) - - def __hash__(self): - return hash(self.as_tuple()) - - def as_tuple(self): - return tuple(self.data) - - def convert(self, resolution): - return self._get_converter(resolution)(self) - - def __cmp__(self, other): - - """ - The result of comparing this instance with 'other' is derived from a - comparison of the instances' date(time) data at the highest common - resolution, meaning that if a date is compared to a datetime, the - datetime will be considered as a date. Thus, a date and a datetime - referring to the same date will be considered equal. - """ - - if not isinstance(other, Temporal): - return NotImplemented - else: - data = self.as_tuple() - other_data = other.as_tuple() - length = min(len(data), len(other_data)) - return cmp(data[:length], other_data[:length]) - - def __sub__(self, other): - - """ - Return the difference between this object and the 'other' object at the - highest common accuracy of both objects. - """ - - if not isinstance(other, Temporal): - return NotImplemented - else: - data = self.as_tuple() - other_data = other.as_tuple() - if len(data) < len(other_data): - return len(self.until(other)) - else: - return len(other.until(self)) - - def _until(self, start, end, nextfn, prevfn): - - """ - Return a collection of units of time by starting from the given 'start' - and stepping across intervening units until 'end' is reached, using the - given 'nextfn' and 'prevfn' to step from one unit to the next. - """ - - current = start - units = [current] - if current < end: - while current < end: - current = nextfn(current) - units.append(current) - elif current > end: - while current > end: - current = prevfn(current) - units.append(current) - return units - - def ambiguous(self): - - "Only times can be ambiguous." - - return 0 - -class Month(Temporal): - - "A simple year-month representation." - - def __str__(self): - return "%04d-%02d" % self.as_tuple()[:2] - - def as_datetime(self, day, hour, minute, second, zone): - return DateTime(self.as_tuple() + (day, hour, minute, second, zone)) - - def as_date(self, day): - if day < 0: - weekday, ndays = self.month_properties() - day = ndays + 1 + day - return Date(self.as_tuple() + (day,)) - - def as_month(self): - return self - - def year(self): - return self.data[0] - - def month(self): - return self.data[1] - - def month_properties(self): - - """ - Return the weekday of the 1st of the month, along with the number of - days, as a tuple. - """ - - year, month = self.as_tuple()[:2] - return calendar.monthrange(year, month) - - def month_update(self, n=1): - - "Return the month updated by 'n' months." - - year, month = self.as_tuple()[:2] - return Month((year + (month - 1 + n) / 12, (month - 1 + n) % 12 + 1)) - - update = month_update - - def next_month(self): - - "Return the month following this one." - - return self.month_update(1) - - next = next_month - - def previous_month(self): - - "Return the month preceding this one." - - return self.month_update(-1) - - previous = previous_month - - def months_until(self, end): - - "Return the collection of months from this month until 'end'." - - return self._until(self.as_month(), end.as_month(), Month.next_month, Month.previous_month) - - until = months_until - -class Date(Month): - - "A simple year-month-day representation." - - def constrain(self): - year, month, day = self.as_tuple()[:3] - - month = max(min(month, 12), 1) - wd, last_day = calendar.monthrange(year, month) - day = max(min(day, last_day), 1) - - self.data[1:3] = month, day - - def __str__(self): - return "%04d-%02d-%02d" % self.as_tuple()[:3] - - def as_datetime(self, hour, minute, second, zone): - return DateTime(self.as_tuple() + (hour, minute, second, zone)) - - def as_start_of_day(self): - return self.as_datetime(None, None, None, None) - - def as_date(self): - return self - - def as_datetime_or_date(self): - return self - - def as_month(self): - return Month(self.data[:2]) - - def day(self): - return self.data[2] - - def day_update(self, n=1): - - "Return the month updated by 'n' days." - - delta = datetime.timedelta(n) - dt = datetime.date(*self.as_tuple()[:3]) - dt_new = dt + delta - return Date((dt_new.year, dt_new.month, dt_new.day)) - - update = day_update - - def next_day(self): - - "Return the date following this one." - - year, month, day = self.as_tuple()[:3] - _wd, end_day = calendar.monthrange(year, month) - if day == end_day: - if month == 12: - return Date((year + 1, 1, 1)) - else: - return Date((year, month + 1, 1)) - else: - return Date((year, month, day + 1)) - - next = next_day - - def previous_day(self): - - "Return the date preceding this one." - - year, month, day = self.as_tuple()[:3] - if day == 1: - if month == 1: - return Date((year - 1, 12, 31)) - else: - _wd, end_day = calendar.monthrange(year, month - 1) - return Date((year, month - 1, end_day)) - else: - return Date((year, month, day - 1)) - - previous = previous_day - - def days_until(self, end): - - "Return the collection of days from this date until 'end'." - - return self._until(self.as_date(), end.as_date(), Date.next_day, Date.previous_day) - - until = days_until - -class DateTime(Date): - - "A simple date plus time representation." - - def constrain(self): - Date.constrain(self) - - hour, minute, second = self.as_tuple()[3:6] - - if self.has_time(): - hour = max(min(hour, 23), 0) - minute = max(min(minute, 59), 0) - - if second is not None: - second = max(min(second, 60), 0) # support leap seconds - - self.data[3:6] = hour, minute, second - - def __str__(self): - return Date.__str__(self) + self.time_string() - - def time_string(self): - if self.has_time(): - data = self.as_tuple() - time_str = " %02d:%02d" % data[3:5] - if data[5] is not None: - time_str += ":%02d" % data[5] - if data[6] is not None: - time_str += " %s" % data[6] - return time_str - else: - return "" - - def as_HTTP_datetime_string(self): - weekday = calendar.weekday(*self.data[:3]) - return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (( - getDayLabel(weekday), - self.data[2], - getMonthLabel(self.data[1]), - self.data[0] - ) + tuple(self.data[3:6])) - - def as_datetime(self): - return self - - def as_date(self): - return Date(self.data[:3]) - - def as_datetime_or_date(self): - - """ - Return a date for this datetime if fields are missing. Otherwise, return - this datetime itself. - """ - - if not self.has_time(): - return self.as_date() - else: - return self - - def __cmp__(self, other): - - """ - The result of comparing this instance with 'other' is, if both instances - are datetime instances, derived from a comparison of the datetimes - converted to UTC. If one or both datetimes cannot be converted to UTC, - the datetimes are compared using the basic temporal comparison which - compares their raw time data. - """ - - this = self - - if this.has_time(): - if isinstance(other, DateTime): - if other.has_time(): - this_utc = this.to_utc() - other_utc = other.to_utc() - if this_utc is not None and other_utc is not None: - return cmp(this_utc.as_tuple(), other_utc.as_tuple()) - else: - other = other.padded() - else: - this = this.padded() - - return Date.__cmp__(this, other) - - def has_time(self): - - """ - Return whether this object has any time information. Objects without - time information can refer to the very start of a day. - """ - - return self.data[3] is not None and self.data[4] is not None - - def time(self): - return self.data[3:] - - def seconds(self): - return self.data[5] - - def time_zone(self): - return self.data[6] - - def set_time_zone(self, value): - self.data[6] = value - - def padded(self, empty_value=0): - - """ - Return a datetime with missing fields defined as being the given - 'empty_value' or 0 if not specified. - """ - - data = [] - for x in self.data[:6]: - if x is None: - data.append(empty_value) - else: - data.append(x) - - data += self.data[6:] - return DateTime(data) - - def to_utc(self): - - """ - Return this object converted to UTC, or None if such a conversion is not - defined. - """ - - if not self.has_time(): - return None - - offset = self.utc_offset() - if offset: - hours, minutes = offset - - # Invert the offset to get the correction. - - hours, minutes = -hours, -minutes - - # Get the components. - - hour, minute, second, zone = self.time() - date = self.as_date() - - # Add the minutes and hours. - - minute += minutes - if minute < 0 or minute > 59: - hour += minute / 60 - minute = minute % 60 - - # NOTE: This makes various assumptions and probably would not work - # NOTE: for general arithmetic. - - hour += hours - if hour < 0: - date = date.previous_day() - hour += 24 - elif hour > 23: - date = date.next_day() - hour -= 24 - - return date.as_datetime(hour, minute, second, "UTC") - - # Cannot convert. - - else: - return None - - def utc_offset(self): - - "Return the UTC offset in hours and minutes." - - zone = self.time_zone() - if not zone: - return None - - # Support explicit UTC zones. - - if zone == "UTC": - return 0, 0 - - # Attempt to return a UTC offset where an explicit offset has been set. - - match = timezone_offset_regexp.match(zone) - if match: - if match.group("sign") == "-": - sign = -1 - else: - sign = 1 - - hours = int(match.group("hours")) * sign - minutes = int(match.group("minutes") or 0) * sign - return hours, minutes - - # Attempt to handle Olson time zone identifiers. - - dt = self.as_olson_datetime() - if dt: - seconds = dt.utcoffset().seconds - hours = seconds / 3600 - minutes = (seconds % 3600) / 60 - return hours, minutes - - # Otherwise return None. - - return None - - def olson_identifier(self): - - "Return the Olson identifier from any zone information." - - zone = self.time_zone() - if not zone: - return None - - # Attempt to match an identifier. - - match = timezone_olson_regexp.match(zone) - if match: - return match.group("olson") - else: - return None - - def _as_olson_datetime(self, hours=None): - - """ - Return a Python datetime object for this datetime interpreted using any - Olson time zone identifier and the given 'hours' offset, raising one of - the pytz exceptions in case of ambiguity. - """ - - olson = self.olson_identifier() - if olson and pytz: - tz = pytz.timezone(olson) - data = self.padded().as_tuple()[:6] - dt = datetime.datetime(*data) - - # With an hours offset, find a time probably in a previously - # applicable time zone. - - if hours is not None: - td = datetime.timedelta(0, hours * 3600) - dt += td - - ldt = tz.localize(dt, None) - - # With an hours offset, adjust the time to define it within the - # previously applicable time zone but at the presumably intended - # position. - - if hours is not None: - ldt -= td - - return ldt - else: - return None - - def as_olson_datetime(self): - - """ - Return a Python datetime object for this datetime interpreted using any - Olson time zone identifier, choosing the time from the zone before the - period of ambiguity. - """ - - try: - return self._as_olson_datetime() - except (pytz.UnknownTimeZoneError, pytz.AmbiguousTimeError): - - # Try again, using an earlier local time and then stepping forward - # in the chosen zone. - # NOTE: Four hours earlier seems reasonable. - - return self._as_olson_datetime(-4) - - def ambiguous(self): - - "Return whether the time is local and ambiguous." - - try: - self._as_olson_datetime() - except (pytz.UnknownTimeZoneError, pytz.AmbiguousTimeError): - return 1 - - return 0 - -class Timespan(ActsAsTimespan, Convertible): - - """ - A period of time which can be compared against others to check for overlaps. - """ - - def __init__(self, start, end): - self.start = start - self.end = end - - # NOTE: Should perhaps catch ambiguous time problems elsewhere. - - if self.ambiguous() and self.start is not None and self.end is not None and start > end: - self.start, self.end = end, start - - def __repr__(self): - return "%s(%r, %r)" % (self.__class__.__name__, self.start, self.end) - - def __hash__(self): - return hash((self.start, self.end)) - - def as_timespan(self): - return self - - def as_limits(self): - return self.start, self.end - - def ambiguous(self): - return self.start is not None and self.start.ambiguous() or self.end is not None and self.end.ambiguous() - - def convert(self, resolution): - return Timespan(*map(self._get_converter(resolution), self.as_limits())) - - def is_before(self, a, b): - - """ - Return whether 'a' is before 'b'. Since the end datetime of one period - may be the same as the start datetime of another period, and yet the - first period is intended to be concluded by the end datetime and not - overlap with the other period, a different test is employed for datetime - comparisons. - """ - - # Datetimes without times can be equal to dates and be considered as - # occurring before those dates. Generally, datetimes should not be - # produced without time information as getDateTime converts such - # datetimes to dates. - - if isinstance(a, DateTime) and (isinstance(b, DateTime) or not a.has_time()): - return a <= b - else: - return a < b - - def __contains__(self, other): - - """ - This instance is considered to contain 'other' if one is not before or - after the other. If this instance overlaps or coincides with 'other', - then 'other' is regarded as belonging to this instance's time period. - """ - - return self == other - - def __cmp__(self, other): - - """ - Return whether this timespan occupies the same period of time as the - 'other'. Timespans are considered less than others if their end points - precede the other's start point, and are considered greater than others - if their start points follow the other's end point. - """ - - if isinstance(other, ActsAsTimespan): - other = other.as_timespan() - - if self.end is not None and other.start is not None and self.is_before(self.end, other.start): - return -1 - elif self.start is not None and other.end is not None and self.is_before(other.end, self.start): - return 1 - else: - return 0 - - else: - if self.end is not None and self.is_before(self.end, other): - return -1 - elif self.start is not None and self.is_before(other, self.start): - return 1 - else: - return 0 - -class TimespanCollection: - - """ - A class providing a list-like interface supporting membership tests at a - particular resolution in order to maintain a collection of non-overlapping - timespans. - """ - - def __init__(self, resolution, values=None): - self.resolution = resolution - self.values = values or [] - - def as_timespan(self): - return Timespan(*self.as_limits()) - - def as_limits(self): - - "Return the earliest and latest points in time for this collection." - - if not self.values: - return None, None - else: - first, last = self.values[0], self.values[-1] - if isinstance(first, ActsAsTimespan): - first = first.as_timespan().start - if isinstance(last, ActsAsTimespan): - last = last.as_timespan().end - return first, last - - def convert(self, value): - if isinstance(value, ActsAsTimespan): - ts = value.as_timespan() - return ts and ts.convert(self.resolution) - else: - return value.convert(self.resolution) - - def __iter__(self): - return iter(self.values) - - def __len__(self): - return len(self.values) - - def __getitem__(self, i): - return self.values[i] - - def __setitem__(self, i, value): - self.values[i] = value - - def __contains__(self, value): - test_value = self.convert(value) - return test_value in self.values - - def append(self, value): - self.values.append(value) - - def insert(self, i, value): - self.values.insert(i, value) - - def pop(self): - return self.values.pop() - - def insert_in_order(self, value): - bisect.insort_left(self, value) - def getCountry(s): "Find a country code in the given string 's'." @@ -2320,444 +1357,8 @@ else: return None -def getDate(s): - - "Parse the string 's', extracting and returning a date object." - - dt = getDateTime(s) - if dt is not None: - return dt.as_date() - else: - return None - -def getDateTime(s): - - """ - Parse the string 's', extracting and returning a datetime object where time - information has been given or a date object where time information is - absent. - """ - - m = datetime_regexp.search(s) - if m: - groups = list(m.groups()) - - # Convert date and time data to integer or None. - - return DateTime(map(int_or_none, groups[:6]) + [m.group("zone")]).as_datetime_or_date() - else: - return None - -def getDateFromCalendar(s): - - """ - Parse the iCalendar format string 's', extracting and returning a date - object. - """ - - dt = getDateTimeFromCalendar(s) - if dt is not None: - return dt.as_date() - else: - return None - -def getDateTimeFromCalendar(s): - - """ - Parse the iCalendar format datetime string 's', extracting and returning a - datetime object where time information has been given or a date object where - time information is absent. - """ - - m = datetime_icalendar_regexp.search(s) - if m: - groups = list(m.groups()) - - # Convert date and time data to integer or None. - - return DateTime(map(int_or_none, groups[:6]) + [m.group("utc") and "UTC" or None]).as_datetime_or_date() - else: - return None - -def getDateStrings(s): - - "Parse the string 's', extracting and returning all date strings." - - start = 0 - m = date_regexp.search(s, start) - l = [] - while m: - l.append("-".join(m.groups())) - m = date_regexp.search(s, m.end()) - return l - -def getMonth(s): - - "Parse the string 's', extracting and returning a month object." - - m = month_regexp.search(s) - if m: - return Month(map(int, m.groups())) - else: - return None - -def getCurrentDate(): - - "Return the current date as a (year, month, day) tuple." - - today = datetime.date.today() - return Date((today.year, today.month, today.day)) - -def getCurrentMonth(): - - "Return the current month as a (year, month) tuple." - - today = datetime.date.today() - return Month((today.year, today.month)) - -def getCurrentYear(): - - "Return the current year." - - today = datetime.date.today() - return today.year - -# Location-related functions. - -class Reference: - - "A map reference." - - def __init__(self, degrees, minutes=0, seconds=0): - self.degrees = degrees - self.minutes = minutes - self.seconds = seconds - - def __repr__(self): - return "Reference(%d, %d, %f)" % (self.degrees, self.minutes, self.seconds) - - def __str__(self): - return "%d:%d:%f" % (self.degrees, self.minutes, self.seconds) - - def __add__(self, other): - if not isinstance(other, Reference): - return NotImplemented - else: - s = sign(self.degrees) - o = sign(other.degrees) - carry, seconds = adc(s * self.seconds, o * other.seconds) - carry, minutes = adc(s * self.minutes, o * other.minutes + carry) - return Reference(self.degrees + other.degrees + carry, minutes, seconds) - - def __sub__(self, other): - if not isinstance(other, Reference): - return NotImplemented - else: - return self.__add__(Reference(-other.degrees, other.minutes, other.seconds)) - - def _compare(self, op, other): - if not isinstance(other, Reference): - return NotImplemented - else: - return op(self.to_degrees(), other.to_degrees()) - - def __eq__(self, other): - return self._compare(operator.eq, other) - - def __ne__(self, other): - return self._compare(operator.ne, other) - - def __lt__(self, other): - return self._compare(operator.lt, other) - - def __le__(self, other): - return self._compare(operator.le, other) - - def __gt__(self, other): - return self._compare(operator.gt, other) - - def __ge__(self, other): - return self._compare(operator.ge, other) - - def to_degrees(self): - return sign(self.degrees) * (abs(self.degrees) + self.minutes / 60.0 + self.seconds / 3600.0) - - def to_pixels(self, scale): - return self.to_degrees() * scale - -def adc(x, y): - result = x + y - return divmod(result, 60) - -def getPositionForReference(latitude, longitude, map_y, map_x, map_x_scale, map_y_scale): - return (longitude - map_x).to_pixels(map_x_scale), (latitude - map_y).to_pixels(map_y_scale) - -def getPositionForCentrePoint(position, map_x_scale, map_y_scale): - x, y = position - return x - map_x_scale / 2.0, y - map_y_scale / 2.0 - -def getMapReference(value): - - "Return a map reference by parsing the given 'value'." - - if value.find(":") != -1: - return getMapReferenceFromDMS(value) - else: - return getMapReferenceFromDecimal(value) - -def getMapReferenceFromDMS(value): - - """ - Return a map reference by parsing the given 'value' expressed as degrees, - minutes, seconds. - """ - - values = value.split(":") - values = map(int, values[:2]) + map(float, values[2:3]) - return Reference(*values) - -def getMapReferenceFromDecimal(value): - - "Return a map reference by parsing the given 'value' in decimal degrees." - - value = float(value) - degrees, remainder = divmod(abs(value * 3600), 3600) - minutes, seconds = divmod(remainder, 60) - return Reference(sign(value) * degrees, minutes, seconds) - -# User interface functions. - -def getParameter(request, name, default=None): - - """ - Using the given 'request', return the value of the parameter with the given - 'name', returning the optional 'default' (or None) if no value was supplied - in the 'request'. - """ - - return get_form(request).get(name, [default])[0] - -def getQualifiedParameter(request, calendar_name, argname, default=None): - - """ - Using the given 'request', 'calendar_name' and 'argname', retrieve the - value of the qualified parameter, returning the optional 'default' (or None) - if no value was supplied in the 'request'. - """ - - argname = getQualifiedParameterName(calendar_name, argname) - return getParameter(request, argname, default) - -def getQualifiedParameterName(calendar_name, argname): - - """ - Return the qualified parameter name using the given 'calendar_name' and - 'argname'. - """ - - if calendar_name is None: - return argname - else: - return "%s-%s" % (calendar_name, argname) - -def getParameterDate(arg): - - "Interpret 'arg', recognising keywords and simple arithmetic operations." - - n = None - - if arg is None: - return None - - elif arg.startswith("current"): - date = getCurrentDate() - if len(arg) > 8: - n = int(arg[7:]) - - elif arg.startswith("yearstart"): - date = Date((getCurrentYear(), 1, 1)) - if len(arg) > 10: - n = int(arg[9:]) - - elif arg.startswith("yearend"): - date = Date((getCurrentYear(), 12, 31)) - if len(arg) > 8: - n = int(arg[7:]) - - else: - date = getDate(arg) - - if n is not None: - date = date.day_update(n) - - return date - -def getParameterMonth(arg): - - "Interpret 'arg', recognising keywords and simple arithmetic operations." - - n = None - - if arg is None: - return None - - elif arg.startswith("current"): - date = getCurrentMonth() - if len(arg) > 8: - n = int(arg[7:]) - - elif arg.startswith("yearstart"): - date = Month((getCurrentYear(), 1)) - if len(arg) > 10: - n = int(arg[9:]) - - elif arg.startswith("yearend"): - date = Month((getCurrentYear(), 12)) - if len(arg) > 8: - n = int(arg[7:]) - - else: - date = getMonth(arg) - - if n is not None: - date = date.month_update(n) - - return date - -def getFormDate(request, calendar_name, argname): - - """ - Return the date from the 'request' for the calendar with the given - 'calendar_name' using the parameter having the given 'argname'. - """ - - arg = getQualifiedParameter(request, calendar_name, argname) - return getParameterDate(arg) - -def getFormMonth(request, calendar_name, argname): - - """ - Return the month from the 'request' for the calendar with the given - 'calendar_name' using the parameter having the given 'argname'. - """ - - arg = getQualifiedParameter(request, calendar_name, argname) - return getParameterMonth(arg) - -def getFormDateTriple(request, yeararg, montharg, dayarg): - - """ - Return the date from the 'request' for the calendar with the given - 'calendar_name' using the parameters having the given 'yeararg', 'montharg' - and 'dayarg' names. - """ - - year = getParameter(request, yeararg) - month = getParameter(request, montharg) - day = getParameter(request, dayarg) - if year and month and day: - return Date((int(year), int(month), int(day))) - else: - return None - -def getFormMonthPair(request, yeararg, montharg): - - """ - Return the month from the 'request' for the calendar with the given - 'calendar_name' using the parameters having the given 'yeararg' and - 'montharg' names. - """ - - year = getParameter(request, yeararg) - month = getParameter(request, montharg) - if year and month: - return Month((int(year), int(month))) - else: - return None - -def getFullDateLabel(request, date): - - """ - Return the full month plus year label using the given 'request' and - 'year_month'. - """ - - if not date: - return "" - - _ = request.getText - year, month, day = date.as_tuple()[:3] - start_weekday, number_of_days = date.month_properties() - weekday = (start_weekday + day - 1) % 7 - day_label = _(getDayLabel(weekday)) - month_label = _(getMonthLabel(month)) - return "%s %s %s %s" % (day_label, day, month_label, year) - -def getFullMonthLabel(request, year_month): - - """ - Return the full month plus year label using the given 'request' and - 'year_month'. - """ - - if not year_month: - return "" - - _ = request.getText - year, month = year_month.as_tuple()[:2] - month_label = _(getMonthLabel(month)) - return "%s %s" % (month_label, year) - # Page-related functions. -def getPrettyPageName(page): - - "Return a nicely formatted title/name for the given 'page'." - - title = page.split_title(force=1) - return getPrettyTitle(title) - -def linkToPage(request, page, text, query_string=None): - - """ - Using 'request', return a link to 'page' with the given link 'text' and - optional 'query_string'. - """ - - text = wikiutil.escape(text) - return page.link_to_raw(request, text, query_string) - -def linkToResource(url, request, text, query_string=None): - - """ - Using 'request', return a link to 'url' with the given link 'text' and - optional 'query_string'. - """ - - if query_string: - query_string = wikiutil.makeQueryString(query_string) - url = "%s?%s" % (url, query_string) - - formatter = request.page and getattr(request.page, "formatter", None) or request.html_formatter - - output = [] - output.append(formatter.url(1, url)) - output.append(formatter.text(text)) - output.append(formatter.url(0)) - return "".join(output) - -def getFullPageName(parent, title): - - """ - Return a full page name from the given 'parent' page (can be empty or None) - and 'title' (a simple page name). - """ - - if parent: - return "%s/%s" % (parent.rstrip("/"), title) - else: - return title - def fillEventPageFromTemplate(template_page, new_page, event_details, category_pagenames): """ diff -r 7f9841140b6b -r 22c22e6ad66f PKG-INFO --- a/PKG-INFO Mon Dec 05 23:44:32 2011 +0100 +++ b/PKG-INFO Sat Jan 21 23:59:22 2012 +0100 @@ -1,12 +1,12 @@ Metadata-Version: 1.1 Name: EventAggregator -Version: 0.8.1 +Version: 0.9 Author: Paul Boddie Author-email: paul at boddie org uk Maintainer: Paul Boddie Maintainer-email: paul at boddie org uk Home-page: http://moinmo.in/MacroMarket/EventAggregator -Download-url: http://moinmo.in/MacroMarket/EventAggregator?action=AttachFile&do=view&target=EventAggregator-0.8.1.tar.bz2 +Download-url: http://moinmo.in/MacroMarket/EventAggregator?action=AttachFile&do=view&target=EventAggregator-0.9.tar.bz2 Summary: Aggregate event data and display it in an event calendar (or summarise it in iCalendar and RSS resources) License: GPL (version 2 or later) Description: The EventAggregator macro for MoinMoin can be used to display event @@ -15,7 +15,7 @@ summaries of events stored in a Wiki, and the EventAggregatorNewEvent action provides a convenient way of adding new events. Keywords: MoinMoin Wiki calendar event RSS iCalendar -Requires: MoinMoin +Requires: MoinMoin MoinSupport Classifier: Development Status :: 3 - Alpha Classifier: License :: OSI Approved :: GNU General Public License (GPL) Classifier: Programming Language :: Python diff -r 7f9841140b6b -r 22c22e6ad66f README.txt --- a/README.txt Mon Dec 05 23:44:32 2011 +0100 +++ b/README.txt Sat Jan 21 23:59:22 2012 +0100 @@ -28,6 +28,11 @@ Important Notices ----------------- +In release 0.9, much of the common support code has been moved to the +MoinSupport distribution, thus introducing that distribution as a dependency +which must be installed for EventAggregator to work. See the documentation +regarding dependencies for further details. + Release 0.7.1 restores MoinMoin 1.9.x compatibility which was accidentally lost in the 0.7 release. @@ -290,6 +295,9 @@ Packages Release Information -------- ------------------- +MoinSupport Tested with 0.1 + Source: http://hgweb.boddie.org.uk/MoinSupport + pytz Tested with 2007k (specifically 2007k-0ubuntu2) Source: http://pytz.sourceforge.net/ @@ -298,6 +306,12 @@ time zone information for the correct interpretation of time information in those summaries. Thus, it is highly recommended that pytz be installed. +New in EventAggregator 0.9 (Changes since EventAggregator 0.8.2) +---------------------------------------------------------------- + + * Moved much of the support library to the MoinSupport distribution, thus + introducing a dependency on that software. + New in EventAggregator 0.8.2 (Changes since EventAggregator 0.8.1) ------------------------------------------------------------------ diff -r 7f9841140b6b -r 22c22e6ad66f docs/COPYING.txt --- a/docs/COPYING.txt Mon Dec 05 23:44:32 2011 +0100 +++ b/docs/COPYING.txt Sat Jan 21 23:59:22 2012 +0100 @@ -1,7 +1,7 @@ Licence Agreement ----------------- -Copyright (C) 2008, 2009, 2010, 2011 Paul Boddie +Copyright (C) 2008, 2009, 2010, 2011, 2012 Paul Boddie Some patches provided by the following contributors: diff -r 7f9841140b6b -r 22c22e6ad66f setup.py --- a/setup.py Mon Dec 05 23:44:32 2011 +0100 +++ b/setup.py Sat Jan 21 23:59:22 2012 +0100 @@ -8,6 +8,6 @@ author = "Paul Boddie", author_email = "paul@boddie.org.uk", url = "http://moinmo.in/MacroMarket/EventAggregator", - version = "0.8.1", + version = "0.9", py_modules = ["EventAggregatorSupport", "MoinMoin.script.import.eventfeed"] )