# HG changeset patch # User Paul Boddie # Date 1348933775 -7200 # Node ID 502a1532a3c2a0fc5b02fa7f3b919c1fd911c3b4 # Parent 20a2886f8b23a5e092d487aa4b608ed9725d39a2 Added support for anchors in the event parser so that event fragments can be referenced directly by links to events. Permitted Wiki markup usage in the description and location metadata. Added metadata term categories in order to more easily distinguish between different kinds of terms. diff -r 20a2886f8b23 -r 502a1532a3c2 EventAggregatorSupport.py --- a/EventAggregatorSupport.py Sat Sep 29 17:13:22 2012 +0200 +++ b/EventAggregatorSupport.py Sat Sep 29 17:49:35 2012 +0200 @@ -8,6 +8,7 @@ @license: GNU GPL (v2 or later), see COPYING.txt for details. """ +from GeneralSupport import * from LocationSupport import * from MoinDateSupport import * from MoinRemoteSupport import * @@ -16,7 +17,6 @@ from MoinMoin.Page import Page from MoinMoin.action import AttachFile -from MoinMoin import search from MoinMoin import wikiutil import codecs @@ -42,10 +42,6 @@ __version__ = "0.9" -# Regular expressions where MoinMoin does not provide the required support. - -category_regexp = None - # Page parsing. definition_list_regexp = re.compile(ur'(?P^(?P#*)\s+(?P.*?):: )(?P.*?)$', re.UNICODE | re.MULTILINE) @@ -55,44 +51,8 @@ country_code_regexp = re.compile(ur'(?:^|\W)(?P[A-Z]{2})(?:$|\W+$)', re.UNICODE) -# Simple content parsing. - -verbatim_regexp = re.compile(ur'(?:' - ur'<.*?)\)>>' - ur'|' - ur'\[\[Verbatim\((?P.*?)\)\]\]' - ur'|' - ur'!(?P.*?)(\s|$)?' - ur'|' - ur'`(?P.*?)`' - ur'|' - ur'{{{(?P.*?)}}}' - ur')', re.UNICODE) - # Utility functions. -def getCategoryPattern(request): - global category_regexp - - try: - return request.cfg.cache.page_category_regexact - except AttributeError: - - # Use regular expression from MoinMoin 1.7.1 otherwise. - - if category_regexp is None: - category_regexp = re.compile(u'^%s$' % ur'(?PCategory(?P(?!Template)\S+))', re.UNICODE) - return category_regexp - -def getWikiDict(pagename, request): - if pagename and Page(request, pagename).exists() and request.user.may.read(pagename): - if hasattr(request.dicts, "dict"): - return request.dicts.dict(pagename) - else: - return request.dicts[pagename] - else: - return None - def getLocationPosition(location, locations): """ @@ -111,31 +71,6 @@ return latitude, longitude -def to_list(s, sep): - return [x.strip() for x in s.split(sep) if x.strip()] - -def sort_none_first(x, y): - if x is None: - return -1 - elif y is None: - return 1 - else: - return cmp(x, y) - -def sort_start_first(x, y): - x_ts = x.as_limits() - if x_ts is not None: - x_start, x_end = x_ts - y_ts = y.as_limits() - if y_ts is not None: - y_start, y_end = y_ts - start_order = cmp(x_start, y_start) - if start_order == 0: - return cmp(x_end, y_end) - else: - return start_order - return 0 - # Utility classes and associated functions. class ActionSupport(ActionSupport): @@ -212,127 +147,22 @@ return start_day_default, end_day_default -# Textual representations. - -def getSimpleWikiText(text): - - """ - Return the plain text representation of the given 'text' which may employ - certain Wiki syntax features, such as those providing verbatim or monospaced - text. - """ - - # NOTE: Re-implementing support for verbatim text and linking avoidance. - - return "".join([s for s in verbatim_regexp.split(text) if s is not None]) - -def getEncodedWikiText(text): - - "Encode the given 'text' in a verbatim representation." - - return "<>" % text - -def getPrettyTitle(title): - - "Return a nicely formatted version of the given 'title'." - - return title.replace("_", " ").replace("/", u" » ") - -# Category discovery and searching. - -def getCategories(request): - - """ - From the AdvancedSearch macro, return a list of category page names using - the given 'request'. - """ - - # This will return all pages with "Category" in the title. - - cat_filter = getCategoryPattern(request).search - return request.rootpage.getPageList(filter=cat_filter) - -def getCategoryMapping(category_pagenames, request): - - """ - For the given 'category_pagenames' return a list of tuples of the form - (category name, category page name) using the given 'request'. - """ - - cat_pattern = getCategoryPattern(request) - mapping = [] - for pagename in category_pagenames: - name = cat_pattern.match(pagename).group("key") - if name != "Category": - mapping.append((name, pagename)) - mapping.sort() - return mapping - -def getCategoryPages(pagename, request): - - """ - Return the pages associated with the given category 'pagename' using the - 'request'. - """ - - query = search.QueryParser().parse_query('category:%s' % pagename) - results = search.searchPages(request, query, "page_name") - - cat_pattern = getCategoryPattern(request) - pages = [] - for page in results.hits: - if not cat_pattern.match(page.page_name): - pages.append(page) - return pages - -def getAllCategoryPages(category_names, request): - - """ - Return all pages belonging to the categories having the given - 'category_names', using the given 'request'. - """ - - pages = [] - pagenames = set() - - for category_name in category_names: - - # Get the pages and page names in the category. - - pages_in_category = getCategoryPages(category_name, request) - - # Visit each page in the category. - - for page_in_category in pages_in_category: - pagename = page_in_category.page_name - - # Only process each page once. - - if pagename in pagenames: - continue - else: - pagenames.add(pagename) - - pages.append(page_in_category) - - return pages - -def getPagesFromResults(result_pages, request): - - "Return genuine pages for the given 'result_pages' using the 'request'." - - return [Page(request, page.page_name) for page in result_pages] - # Event parsing from page texts. -def parseEvents(text, event_page): +def parseEvents(text, event_page, fragment=None): """ Parse events in the given 'text', returning a list of event objects for the - given 'event_page'. + given 'event_page'. An optional 'fragment' can be specified to indicate a + specific region of the event page. """ + template_details = {} + if fragment: + template_details["fragment"] = fragment + details = {} + details.update(template_details) events = [Event(event_page, details)] for match in definition_list_regexp.finditer(text): @@ -351,12 +181,12 @@ # Dates. - if term in ("start", "end"): + if term in Event.date_terms: desc = getDateTime(desc) # Lists (whose elements may be quoted). - elif term in ("topics", "categories"): + elif term in Event.list_terms: desc = map(getSimpleWikiText, to_list(desc, ",")) # Position details. @@ -371,9 +201,14 @@ # Labels which may well be quoted. - elif term in ("title", "summary", "description", "location"): + elif term in Event.title_terms: desc = getSimpleWikiText(desc.strip()) + # Plain Wiki text terms. + + elif term in Event.other_terms: + desc = desc.strip() + if desc is not None: # Handle apparent duplicates by creating a new set of @@ -384,6 +219,7 @@ # Make a new event. details = {} + details.update(template_details) events.append(Event(event_page, details)) details[term] = desc @@ -427,14 +263,14 @@ return [] - def linkToPage(self, request, text, query_string=None): + def linkToPage(self, request, text, query_string=None, anchor=None): """ Using 'request', return a link to this page with the given link 'text' - and optional 'query_string'. + and optional 'query_string' and 'anchor'. """ - return linkToResource(self.url, request, text, query_string) + return linkToResource(self.url, request, text, query_string, anchor) # Formatting-related functions. @@ -602,10 +438,10 @@ "Return a list of events from this page." if self.events is None: + self.events = [] if self.getFormat() == "wiki": - self.events = parseEvents(self.getBody(), self) - else: - self.events = [] + for format, attributes, region in getFragments(self.getBody(), True): + self.events += parseEvents(region, self, attributes.get("fragment")) return self.events @@ -685,17 +521,17 @@ # Dates. - if term in ("start", "end"): + if term in event.date_terms: desc = desc.replace("YYYY-MM-DD", str(event_details[term])) # Lists (whose elements may be quoted). - elif term in ("topics", "categories"): + elif term in event.list_terms: desc = ", ".join([getEncodedWikiText(item) for item in event_details[term]]) # Labels which must be quoted. - elif term in ("title", "summary"): + elif term in event.title_terms: desc = getEncodedWikiText(event_details[term]) # Position details. @@ -705,7 +541,7 @@ # Text which need not be quoted, but it will be Wiki text. - elif term in ("description", "link", "location"): + elif term in event.other_terms: desc = event_details[term] replaced_terms.add(term) @@ -743,14 +579,14 @@ self.flushCategoryMembership() self.page.saveText(self.getBody(), 0) - def linkToPage(self, request, text, query_string=None): + def linkToPage(self, request, text, query_string=None, anchor=None): """ Using 'request', return a link to this page with the given link 'text' - and optional 'query_string'. + and optional 'query_string' and 'anchor'. """ - return linkToPage(request, self.page, text, query_string) + return linkToPage(request, self.page, text, query_string, anchor) # Formatting-related functions. @@ -781,6 +617,13 @@ "A description of an event." + title_terms = "title", "summary" + date_terms = "start", "end" + list_terms = "topics", "categories" + other_terms = "description", "location", "link" + geo_terms = "geo", + all_terms = title_terms + date_terms + list_terms + other_terms + geo_terms + def __init__(self, page, details): self.page = page self.details = details @@ -826,7 +669,8 @@ "Return the URL of this event." - return self.page.getPageURL() + fragment = self.details.get("fragment") + return self.page.getPageURL() + (fragment and "#" + fragment or "") def linkToEvent(self, request, text, query_string=None): @@ -835,7 +679,7 @@ and optional 'query_string'. """ - return self.page.linkToPage(request, text, query_string) + return self.page.linkToPage(request, text, query_string, self.details.get("fragment")) def getMetadata(self): @@ -945,14 +789,14 @@ return self.details.get("url") or self.page.getPageURL() - def linkToEvent(self, request, text, query_string=None): + def linkToEvent(self, request, text, query_string=None, anchor=None): """ Using 'request', return a link to this event with the given link 'text' - and optional 'query_string'. + and optional 'query_string' and 'anchor'. """ - return linkToResource(self.getEventURL(), request, text, query_string) + return linkToResource(self.getEventURL(), request, text, query_string, anchor) def getMetadata(self): @@ -1320,6 +1164,24 @@ return scale +# Event sorting. + +def sort_start_first(x, y): + x_ts = x.as_limits() + if x_ts is not None: + x_start, x_end = x_ts + y_ts = y.as_limits() + if y_ts is not None: + y_start, y_end = y_ts + start_order = cmp(x_start, y_start) + if start_order == 0: + return cmp(x_end, y_end) + else: + return start_order + return 0 + +# Country code parsing. + def getCountry(s): "Find a country code in the given string 's'." @@ -3358,11 +3220,12 @@ event_details = event.getDetails() write = write or request.write + if event_details.has_key("fragment"): + write(fmt.anchordef(event_details["fragment"])) + write(fmt.definition_list(on=1)) - for term in ("title", "summary", "start", "end", "description", "link", - "location", "geo", "topics", "categories"): - + for term in event.all_terms: if event_details.has_key(term): value = event_details[term] if value: @@ -3370,7 +3233,10 @@ write(fmt.text(term)) write(fmt.definition_term(on=0)) write(fmt.definition_desc(on=1)) - write(formatText(str(value), request, fmt)) + if term in event.list_terms: + write(", ".join([formatText(str(v), request, fmt) for v in value])) + else: + write(formatText(str(value), request, fmt)) write(fmt.definition_desc(on=0)) write(fmt.definition_list(on=0)) @@ -3530,7 +3396,7 @@ link = event.getEventURL() write('\r\n') - write('%s\r\n' % wikiutil.escape(event_summary)) + write('%s\r\n' % escape(event_summary)) write('%s\r\n' % link) # Write a description according to the preferred source of diff -r 20a2886f8b23 -r 502a1532a3c2 README.txt --- a/README.txt Sat Sep 29 17:13:22 2012 +0200 +++ b/README.txt Sat Sep 29 17:49:35 2012 +0200 @@ -327,7 +327,8 @@ * The EventLocationsDict or equivalent can now retain time zone/regime information about each location. * Added an event parser that can format special page regions in different - ways. + ways and support links directly to such regions. + * Permitted Wiki markup in the description and location metadata. New in EventAggregator 0.8.5 (Changes since EventAggregator 0.8.4) ------------------------------------------------------------------ diff -r 20a2886f8b23 -r 502a1532a3c2 parsers/event.py --- a/parsers/event.py Sat Sep 29 17:13:22 2012 +0200 +++ b/parsers/event.py Sat Sep 29 17:49:35 2012 +0200 @@ -32,7 +32,9 @@ self.raw = raw self.request = request - #self.attrs = parseAttributes(kw.get("format_args", ""), False) + attrs = parseAttributes(kw.get("format_args", ""), False) + + self.fragment = attrs.get("fragment") def format(self, fmt, write=None): @@ -42,7 +44,7 @@ using the request. """ - events = parseEvents(self.raw, EventPage(self.request.page)) + events = parseEvents(self.raw, EventPage(self.request.page), self.fragment) for event in events: formatEvent(event, self.request, fmt, write=write) @@ -57,7 +59,7 @@ request. """ - events = parseEvents(self.raw, EventPage(self.request.page)) + events = parseEvents(self.raw, EventPage(self.request.page), self.fragment) formatEventsForOutputType(events, self.request, mimetype, write=write) # Class methods.