# HG changeset patch # User Paul Boddie # Date 1339974554 -7200 # Node ID 1ae42f5a82a29fb3231889af783ca33982930843 # Parent ed174df75dcfe9b1cbf412e9ee5d549033851fdb Moved page text parsing into a separate function for the extraction of events. Moved iCalendar and RSS output generation into the main library for use by other extensions. Tidied up the parameters in various generated action links. diff -r ed174df75dcf -r 1ae42f5a82a2 EventAggregatorSupport.py --- a/EventAggregatorSupport.py Mon Jun 04 21:07:26 2012 +0200 +++ b/EventAggregatorSupport.py Mon Jun 18 01:09:14 2012 +0200 @@ -323,6 +323,73 @@ return [Page(request, page.page_name) for page in result_pages] +# Event parsing from page texts. + +def parseEvents(text, page): + + """ + Parse events in the given 'text', returning a list of event objects for the + given 'page'. + """ + + details = {} + events = [Event(page, details)] + + for match in definition_list_regexp.finditer(text): + + # Skip commented-out items. + + if match.group("optcomment"): + continue + + # Permit case-insensitive list terms. + + term = match.group("term").lower() + desc = match.group("desc") + + # Special value type handling. + + # Dates. + + if term in ("start", "end"): + desc = getDateTime(desc) + + # Lists (whose elements may be quoted). + + elif term in ("topics", "categories"): + desc = map(getSimpleWikiText, to_list(desc, ",")) + + # Position details. + + elif term == "geo": + try: + desc = map(getMapReference, to_list(desc, None)) + if len(desc) != 2: + continue + except (KeyError, ValueError): + continue + + # Labels which may well be quoted. + + elif term in ("title", "summary", "description", "location"): + desc = getSimpleWikiText(desc.strip()) + + if desc is not None: + + # Handle apparent duplicates by creating a new set of + # details. + + if details.has_key(term): + + # Make a new event. + + details = {} + events.append(Event(page, details)) + + details[term] = desc + + return events + # Event resources providing collections of events. class EventResource: @@ -535,62 +602,10 @@ "Return a list of events from this page." if self.events is None: - details = {} - self.events = [Event(self, details)] - if self.getFormat() == "wiki": - for match in definition_list_regexp.finditer(self.getBody()): - - # Skip commented-out items. - - if match.group("optcomment"): - continue - - # Permit case-insensitive list terms. - - term = match.group("term").lower() - desc = match.group("desc") - - # Special value type handling. - - # Dates. - - if term in ("start", "end"): - desc = getDateTime(desc) - - # Lists (whose elements may be quoted). - - elif term in ("topics", "categories"): - desc = map(getSimpleWikiText, to_list(desc, ",")) - - # Position details. - - elif term == "geo": - try: - desc = map(getMapReference, to_list(desc, None)) - if len(desc) != 2: - continue - except (KeyError, ValueError): - continue - - # Labels which may well be quoted. - - elif term in ("title", "summary", "description", "location"): - desc = getSimpleWikiText(desc.strip()) - - if desc is not None: - - # Handle apparent duplicates by creating a new set of - # details. - - if details.has_key(term): - - # Make a new event. - - details = {} - self.events.append(Event(self, details)) - - details[term] = desc + self.events = parseEvents(self.getBody(), self) + else: + self.events = [] return self.events @@ -1676,8 +1691,10 @@ self.calendar_start, self.calendar_end ) - return "action=EventAggregatorNewEvent&%s&%s&template=%s&parent=%s&%s" % ( - args, self.category_name_parameters, self.template_name, self.parent_name or "", + return "action=EventAggregatorNewEvent%s%s&template=%s&parent=%s&%s" % ( + args and "&%s" % args, + self.category_name_parameters and "&%s" % self.category_name_parameters, + self.template_name, self.parent_name or "", navigation_link) def getFullDateLabel(self, date): @@ -1747,11 +1764,11 @@ # Generate the links. - download_dialogue_link = "action=EventAggregatorSummary&parent=%s&resolution=%s&%s&%s" % ( + download_dialogue_link = "action=EventAggregatorSummary&parent=%s&resolution=%s%s%s" % ( self.parent_name or "", self.resolution, - self.category_name_parameters, - self.remote_source_parameters + self.category_name_parameters and "&%s" % self.category_name_parameters, + self.remote_source_parameters and "&%s" % self.remote_source_parameters ) download_all_link = download_dialogue_link + "&doit=1" download_link = download_all_link + ("&%s&%s" % ( @@ -3362,4 +3379,191 @@ """ +# Event-only formatting. + +def formatEventsForOutputType(events, request, mimetype, parent=None, descriptions=None, latest_timestamp=None): + + """ + Format the given 'events' using the 'request' for the given 'mimetype'. + + The optional 'parent' indicates the "natural" parent page of the events. Any + event pages residing beneath the parent page will have their names + reproduced as relative to the parent page. + + The optional 'descriptions' indicates the nature of any description given + for events in the output resource. + + The optional 'latest_timestamp' indicates the timestamp of the latest edit + of the page or event collection. + """ + + # Start the collection. + + if mimetype == "text/calendar": + request.write("BEGIN:VCALENDAR\r\n") + request.write("PRODID:-//MoinMoin//EventAggregatorSummary\r\n") + request.write("VERSION:2.0\r\n") + + elif mimetype == "application/rss+xml": + + # Using the page name and the page URL in the title, link and + # description. + + path_info = getPathInfo(request) + + request.write('\r\n') + request.write('\r\n') + request.write('%s\r\n' % path_info[1:]) + request.write('%s%s\r\n' % (request.getBaseURL(), path_info)) + request.write('Events published on %s%s\r\n' % (request.getBaseURL(), path_info)) + + if latest_timestamp is not None: + request.write('%s\r\n' % latest_timestamp.as_HTTP_datetime_string()) + + # Sort the events by start date, reversed. + + ordered_events = getOrderedEvents(events) + ordered_events.reverse() + events = ordered_events + + # Output the collection one by one. + + for event in events: + formatEventForOutputType(event, request, mimetype, parent, descriptions) + + # End the collection. + + if mimetype == "text/calendar": + request.write("END:VCALENDAR\r\n") + + elif mimetype == "application/rss+xml": + request.write('\r\n') + request.write('\r\n') + +def formatEventForOutputType(event, request, mimetype, parent=None, descriptions=None): + + """ + Format the given 'event' using the 'request' for the given 'mimetype'. + + The optional 'parent' indicates the "natural" parent page of the events. Any + event pages residing beneath the parent page will have their names + reproduced as relative to the parent page. + + The optional 'descriptions' indicates the nature of any description given + for events in the output resource. + """ + + event_details = event.getDetails() + + if mimetype == "text/calendar": + + # NOTE: A custom formatter making attributes for links and plain + # NOTE: text for values could be employed here. + + # Get the summary details. + + event_summary = event.getSummary(parent) + link = event.getEventURL() + + # Output the event details. + + request.write("BEGIN:VEVENT\r\n") + request.write("UID:%s\r\n" % link) + request.write("URL:%s\r\n" % link) + request.write("DTSTAMP:%04d%02d%02dT%02d%02d%02dZ\r\n" % event_details["created"].as_tuple()[:6]) + request.write("LAST-MODIFIED:%04d%02d%02dT%02d%02d%02dZ\r\n" % event_details["last-modified"].as_tuple()[:6]) + request.write("SEQUENCE:%d\r\n" % event_details["sequence"]) + + start = event_details["start"] + end = event_details["end"] + + if isinstance(start, DateTime): + request.write("DTSTART") + write_calendar_datetime(request, start) + else: + request.write("DTSTART;VALUE=DATE:%04d%02d%02d\r\n" % start.as_date().as_tuple()) + + if isinstance(end, DateTime): + request.write("DTEND") + write_calendar_datetime(request, end) + else: + request.write("DTEND;VALUE=DATE:%04d%02d%02d\r\n" % end.next_day().as_date().as_tuple()) + + request.write("SUMMARY:%s\r\n" % getQuotedText(event_summary)) + + # Optional details. + + if event_details.get("topics") or event_details.get("categories"): + request.write("CATEGORIES:%s\r\n" % ",".join( + [getQuotedText(topic) + for topic in event_details.get("topics") or event_details.get("categories")] + )) + if event_details.has_key("location"): + request.write("LOCATION:%s\r\n" % getQuotedText(event_details["location"])) + if event_details.has_key("geo"): + request.write("GEO:%s\r\n" % getQuotedText(";".join([str(ref.to_degrees()) for ref in event_details["geo"]]))) + + request.write("END:VEVENT\r\n") + + elif mimetype == "application/rss+xml": + + event_page = event.getPage() + event_details = event.getDetails() + + # Get a parser and formatter for the formatting of some attributes. + + fmt = request.html_formatter + + # Get the summary details. + + event_summary = event.getSummary(parent) + link = event.getEventURL() + + request.write('\r\n') + request.write('%s\r\n' % wikiutil.escape(event_summary)) + request.write('%s\r\n' % link) + + # Write a description according to the preferred source of + # descriptions. + + if descriptions == "page": + description = event_details.get("description", "") + else: + description = event_details["last-comment"] + + request.write('%s\r\n' % + fmt.text(event_page.formatText(description, fmt))) + + for topic in event_details.get("topics") or event_details.get("categories") or []: + request.write('%s\r\n' % + fmt.text(event_page.formatText(topic, fmt))) + + request.write('%s\r\n' % event_details["created"].as_HTTP_datetime_string()) + request.write('%s#%s\r\n' % (link, event_details["sequence"])) + request.write('\r\n') + +# iCalendar format helper functions. + +def write_calendar_datetime(request, datetime): + + """ + Write to the given 'request' the 'datetime' using appropriate time zone + information. + """ + + utc_datetime = datetime.to_utc() + if utc_datetime: + request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02dZ\r\n" % utc_datetime.padded().as_tuple()[:-1]) + else: + zone = datetime.time_zone() + if zone: + request.write(";TZID=/%s" % zone) + request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02d\r\n" % datetime.padded().as_tuple()[:-1]) + +def getQuotedText(text): + + "Return the 'text' quoted for iCalendar purposes." + + return text.replace(";", r"\;").replace(",", r"\,") + # vim: tabstop=4 expandtab shiftwidth=4 diff -r ed174df75dcf -r 1ae42f5a82a2 actions/EventAggregatorSummary.py --- a/actions/EventAggregatorSummary.py Mon Jun 04 21:07:26 2012 +0200 +++ b/actions/EventAggregatorSummary.py Mon Jun 18 01:09:14 2012 +0200 @@ -242,12 +242,6 @@ pass -def getQuotedText(text): - - "Return the 'text' quoted for iCalendar purposes." - - return text.replace(";", r"\;").replace(",", r"\,") - def write_resource(request): """ @@ -317,140 +311,19 @@ # iCalendar output... if format == "iCalendar": - request.write("BEGIN:VCALENDAR\r\n") - request.write("PRODID:-//MoinMoin//EventAggregatorSummary\r\n") - request.write("VERSION:2.0\r\n") - - for event in all_shown_events: - event_details = event.getDetails() - - # NOTE: A custom formatter making attributes for links and plain - # NOTE: text for values could be employed here. - - # Get the summary details. - - event_summary = event.getSummary(parent) - link = event.getEventURL() - - # Output the event details. - - request.write("BEGIN:VEVENT\r\n") - request.write("UID:%s\r\n" % link) - request.write("URL:%s\r\n" % link) - request.write("DTSTAMP:%04d%02d%02dT%02d%02d%02dZ\r\n" % event_details["created"].as_tuple()[:6]) - request.write("LAST-MODIFIED:%04d%02d%02dT%02d%02d%02dZ\r\n" % event_details["last-modified"].as_tuple()[:6]) - request.write("SEQUENCE:%d\r\n" % event_details["sequence"]) - - start = event_details["start"] - end = event_details["end"] - - if isinstance(start, DateTime): - request.write("DTSTART") - write_calendar_datetime(request, start) - else: - request.write("DTSTART;VALUE=DATE:%04d%02d%02d\r\n" % start.as_date().as_tuple()) - - if isinstance(end, DateTime): - request.write("DTEND") - write_calendar_datetime(request, end) - else: - request.write("DTEND;VALUE=DATE:%04d%02d%02d\r\n" % end.next_day().as_date().as_tuple()) - - request.write("SUMMARY:%s\r\n" % getQuotedText(event_summary)) - - # Optional details. - - if event_details.get("topics") or event_details.get("categories"): - request.write("CATEGORIES:%s\r\n" % ",".join( - [getQuotedText(topic) - for topic in event_details.get("topics") or event_details.get("categories")] - )) - if event_details.has_key("location"): - request.write("LOCATION:%s\r\n" % getQuotedText(event_details["location"])) - if event_details.has_key("geo"): - request.write("GEO:%s\r\n" % getQuotedText(";".join([str(ref.to_degrees()) for ref in event_details["geo"]]))) - - request.write("END:VEVENT\r\n") - - request.write("END:VCALENDAR\r\n") + mimetype = "text/calendar" # RSS output... elif format == "RSS": - - # Using the page name and the page URL in the title, link and - # description. - - path_info = getPathInfo(request) - - request.write('\r\n') - request.write('\r\n') - request.write('%s\r\n' % path_info[1:]) - request.write('%s%s\r\n' % (request.getBaseURL(), path_info)) - request.write('Events published on %s%s\r\n' % (request.getBaseURL(), path_info)) - - if latest_timestamp is not None: - request.write('%s\r\n' % latest_timestamp.as_HTTP_datetime_string()) - - # Sort all_shown_events by start date, reversed. + mimetype = "application/rss+xml" - ordered_events = getOrderedEvents(all_shown_events) - ordered_events.reverse() - - for event in ordered_events: - event_page = event.getPage() - event_details = event.getDetails() - - # Get a parser and formatter for the formatting of some attributes. - - fmt = request.html_formatter - - # Get the summary details. - - event_summary = event.getSummary(parent) - link = event.getEventURL() - - request.write('\r\n') - request.write('%s\r\n' % wikiutil.escape(event_summary)) - request.write('%s\r\n' % link) + # Catch-all... - # Write a description according to the preferred source of - # descriptions. - - if descriptions == "page": - description = event_details.get("description", "") - else: - description = event_details["last-comment"] - - request.write('%s\r\n' % - fmt.text(event_page.formatText(description, fmt))) - - for topic in event_details.get("topics") or event_details.get("categories") or []: - request.write('%s\r\n' % - fmt.text(event_page.formatText(topic, fmt))) - - request.write('%s\r\n' % event_details["created"].as_HTTP_datetime_string()) - request.write('%s#%s\r\n' % (link, event_details["sequence"])) - request.write('\r\n') + else: + mimetype = None - request.write('\r\n') - request.write('\r\n') - -def write_calendar_datetime(request, datetime): - - """ - Write to the given 'request' the 'datetime' using appropriate time zone - information. - """ - - utc_datetime = datetime.to_utc() - if utc_datetime: - request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02dZ\r\n" % utc_datetime.padded().as_tuple()[:-1]) - else: - zone = datetime.time_zone() - if zone: - request.write(";TZID=/%s" % zone) - request.write(";VALUE=DATE-TIME:%04d%02d%02dT%02d%02d%02d\r\n" % datetime.padded().as_tuple()[:-1]) + formatEventsForOutputType(all_shown_events, request, mimetype, parent, descriptions, latest_timestamp) # Action function.