1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/macros/EventAggregator.py Sat Mar 21 21:39:17 2009 +0100
1.3 @@ -0,0 +1,361 @@
1.4 +# -*- coding: iso-8859-1 -*-
1.5 +"""
1.6 + MoinMoin - EventAggregator Macro
1.7 +
1.8 + @copyright: 2008, 2009 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 MoinMoin.Page import Page
1.15 +from MoinMoin import wikiutil, search, version
1.16 +import calendar
1.17 +import re
1.18 +
1.19 +__version__ = "0.1"
1.20 +
1.21 +Dependencies = ['pages']
1.22 +
1.23 +# Regular expressions where MoinMoin does not provide the required support.
1.24 +
1.25 +category_regexp = None
1.26 +definition_list_regexp = re.compile(ur'^\s+(?P<term>.*?)::\s(?P<desc>.*?)$', re.UNICODE | re.MULTILINE)
1.27 +date_regexp = re.compile(ur'(?P<year>[0-9]{4})-(?P<month>[0-9]{2})-(?P<day>[0-9]{2})', re.UNICODE)
1.28 +month_regexp = re.compile(ur'(?P<year>[0-9]{4})-(?P<month>[0-9]{2})', re.UNICODE)
1.29 +
1.30 +# Date labels.
1.31 +
1.32 +month_labels = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
1.33 +weekday_labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
1.34 +
1.35 +# Utility functions.
1.36 +
1.37 +def isMoin15():
1.38 + return version.release.startswith("1.5.")
1.39 +
1.40 +def getCategoryPattern(request):
1.41 + global category_regexp
1.42 +
1.43 + try:
1.44 + return request.cfg.cache.page_category_regexact
1.45 + except AttributeError:
1.46 +
1.47 + # Use regular expression from MoinMoin 1.7.1 otherwise.
1.48 +
1.49 + if category_regexp is None:
1.50 + category_regexp = re.compile(u'^%s$' % ur'(?P<all>Category(?P<key>(?!Template)\S+))', re.UNICODE)
1.51 + return category_regexp
1.52 +
1.53 +# The main activity functions.
1.54 +
1.55 +def getPages(pagename, request):
1.56 +
1.57 + "Return the links minus category links for 'pagename' using the 'request'."
1.58 +
1.59 + query = search.QueryParser().parse_query('category:%s' % pagename)
1.60 + if isMoin15():
1.61 + results = search.searchPages(request, query)
1.62 + results.sortByPagename()
1.63 + else:
1.64 + results = search.searchPages(request, query, "page_name")
1.65 +
1.66 + cat_pattern = getCategoryPattern(request)
1.67 + pages = []
1.68 + for page in results.hits:
1.69 + if not cat_pattern.match(page.page_name):
1.70 + pages.append(page)
1.71 + return pages
1.72 +
1.73 +def getPrettyPageName(page):
1.74 +
1.75 + "Return a nicely formatted title/name for the given 'page'."
1.76 +
1.77 + return page.split_title(force=1).replace("_", " ").replace("/", u" » ")
1.78 +
1.79 +def getEventDetails(page):
1.80 +
1.81 + "Return a dictionary of event details from the given 'page'."
1.82 +
1.83 + event_details = {}
1.84 +
1.85 + if page.pi["format"] == "wiki":
1.86 + for match in definition_list_regexp.finditer(page.body):
1.87 + # Permit case-insensitive list terms.
1.88 + term = match.group("term").lower()
1.89 + desc = match.group("desc")
1.90 + if term in ("start", "end"):
1.91 + desc = getDate(desc)
1.92 + if desc is not None:
1.93 + event_details[term] = desc
1.94 +
1.95 + return event_details
1.96 +
1.97 +def getDate(s):
1.98 +
1.99 + "Parse the string 's', extracting and returning a date string."
1.100 +
1.101 + m = date_regexp.search(s)
1.102 + if m:
1.103 + return tuple(map(int, m.groups()))
1.104 + else:
1.105 + return None
1.106 +
1.107 +def getMonth(s):
1.108 +
1.109 + "Parse the string 's', extracting and returning a month string."
1.110 +
1.111 + m = month_regexp.search(s)
1.112 + if m:
1.113 + return tuple(map(int, m.groups()))
1.114 + else:
1.115 + return None
1.116 +
1.117 +def execute(macro, args):
1.118 +
1.119 + """
1.120 + Execute the 'macro' with the given 'args': an optional list of selected
1.121 + category names (categories whose pages are to be shown), together with
1.122 + optional named arguments of the form "start=YYYY-MM" and "end=YYYY-MM"
1.123 + (indicating a restricted view of all events), and "mode=list" or
1.124 + "mode=calendar" (indicating the style of view).
1.125 + """
1.126 +
1.127 + request = macro.request
1.128 + fmt = macro.formatter
1.129 + page = fmt.page
1.130 + _ = request.getText
1.131 +
1.132 + # Interpret the arguments.
1.133 +
1.134 + try:
1.135 + parsed_args = args and wikiutil.parse_quoted_separated(args, name_value=False) or []
1.136 + except AttributeError:
1.137 + parsed_args = args.split(",")
1.138 +
1.139 + parsed_args = [arg for arg in parsed_args if arg]
1.140 +
1.141 + # Get special arguments.
1.142 +
1.143 + category_names = []
1.144 + calendar_start = None
1.145 + calendar_end = None
1.146 + mode = "calendar"
1.147 +
1.148 + for arg in parsed_args:
1.149 + if arg.startswith("start="):
1.150 + calendar_start = getMonth(arg[6:])
1.151 + elif arg.startswith("end="):
1.152 + calendar_end = getMonth(arg[4:])
1.153 + elif arg.startswith("mode="):
1.154 + mode = arg[5:]
1.155 + else:
1.156 + category_names.append(arg)
1.157 +
1.158 + # Generate a list of events found on pages belonging to the specified
1.159 + # categories, as found in the macro arguments.
1.160 +
1.161 + events = []
1.162 + shown_events = []
1.163 + earliest = calendar_start
1.164 + latest = calendar_end
1.165 +
1.166 + for category_name in category_names:
1.167 +
1.168 + # Get the pages and page names in the category.
1.169 +
1.170 + pages_in_category = getPages(category_name, request)
1.171 +
1.172 + # Visit each page in the category.
1.173 +
1.174 + for page_in_category in pages_in_category:
1.175 + pagename = page_in_category.page_name
1.176 +
1.177 + # Get a real page, not a result page.
1.178 +
1.179 + real_page_in_category = Page(request, pagename)
1.180 + event_details = getEventDetails(real_page_in_category)
1.181 + event = (real_page_in_category, event_details)
1.182 + events.append(event)
1.183 +
1.184 + # Test for the suitability of the event.
1.185 +
1.186 + if event_details.has_key("start") and event_details.has_key("end"):
1.187 +
1.188 + start_month = event_details["start"][:2]
1.189 + end_month = event_details["end"][:2]
1.190 +
1.191 + # Compare the months of the dates to the requested calendar
1.192 + # window, if any.
1.193 +
1.194 + if (calendar_start is None or end_month >= calendar_start) and \
1.195 + (calendar_end is None or start_month <= calendar_end):
1.196 +
1.197 + shown_events.append(event)
1.198 +
1.199 + if earliest is None or start_month < earliest:
1.200 + earliest = start_month
1.201 + if latest is None or end_month > latest:
1.202 + latest = end_month
1.203 +
1.204 + # Make a calendar.
1.205 +
1.206 + output = []
1.207 +
1.208 + if mode == "calendar":
1.209 +
1.210 + end_year = latest[0]
1.211 +
1.212 + for year in range(earliest[0], end_year + 1):
1.213 + if year < latest[0]:
1.214 + end_month = 12
1.215 + else:
1.216 + end_month = latest[1]
1.217 +
1.218 + if year > earliest[0]:
1.219 + start_month = 1
1.220 + else:
1.221 + start_month = earliest[1]
1.222 +
1.223 + for month in range(start_month, end_month + 1):
1.224 +
1.225 + # Output a month.
1.226 +
1.227 + output.append(fmt.table(on=1, attrs={"tableclass" : "event-month"}))
1.228 +
1.229 + output.append(fmt.table_row(on=1))
1.230 + output.append(fmt.table_cell(on=1, attrs={"class" : "event-month-heading", "colspan" : "7"}))
1.231 + output.append(fmt.span(on=1))
1.232 + output.append(fmt.text(_(month_labels[month - 1]))) # zero-based labels
1.233 + output.append(fmt.span(on=0))
1.234 + output.append(fmt.text(" "))
1.235 + output.append(fmt.span(on=1))
1.236 + output.append(fmt.text(year))
1.237 + output.append(fmt.span(on=0))
1.238 + output.append(fmt.table_cell(on=0))
1.239 + output.append(fmt.table_row(on=0))
1.240 +
1.241 + # Weekday headings.
1.242 +
1.243 + output.append(fmt.table_row(on=1))
1.244 +
1.245 + for weekday in range(0, 7):
1.246 + output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-heading"}))
1.247 + output.append(fmt.text(_(weekday_labels[weekday])))
1.248 + output.append(fmt.table_cell(on=0))
1.249 +
1.250 + output.append(fmt.table_row(on=0))
1.251 +
1.252 + # Process the days of the month.
1.253 +
1.254 + start_weekday, number_of_days = calendar.monthrange(year, month)
1.255 +
1.256 + # The start weekday is the weekday of day number 1.
1.257 + # Find the first day of the week, counting from below zero, if
1.258 + # necessary, in order to land on the first day of the month as
1.259 + # day number 1.
1.260 +
1.261 + first_day = 1 - start_weekday
1.262 +
1.263 + while first_day <= number_of_days:
1.264 +
1.265 + # Output a week.
1.266 +
1.267 + output.append(fmt.table_row(on=1))
1.268 +
1.269 + for weekday in range(0, 7):
1.270 + day = first_day + weekday
1.271 +
1.272 + # Output out-of-month days.
1.273 +
1.274 + if day < 1 or day > number_of_days:
1.275 + output.append(fmt.table_cell(on=1, attrs={"class" : "event-day event-day-excluded"}))
1.276 + output.append(fmt.table_cell(on=0))
1.277 +
1.278 + # Output normal days.
1.279 +
1.280 + else:
1.281 + # Get event details.
1.282 + # NOTE: Can be made more efficient.
1.283 +
1.284 + date = (year, month, day)
1.285 + day_events = []
1.286 +
1.287 + for event_page, event_details in shown_events:
1.288 +
1.289 + # Test for the event on the current day.
1.290 +
1.291 + if event_details["start"] <= date <= event_details["end"]:
1.292 + day_events.append((event_page, event_details))
1.293 +
1.294 + # Output the day.
1.295 +
1.296 + if day_events:
1.297 + output.append(fmt.table_cell(on=1, attrs={"class" : "event-day event-day-busy"}))
1.298 + else:
1.299 + output.append(fmt.table_cell(on=1, attrs={"class" : "event-day event-day-empty"}))
1.300 +
1.301 + output.append(fmt.div(on=1, css_class="event-day-number"))
1.302 + output.append(fmt.text(day))
1.303 + output.append(fmt.div(on=0))
1.304 +
1.305 + # Show event details.
1.306 +
1.307 + for event_page, event_details in day_events:
1.308 +
1.309 + # Get a pretty version of the page name.
1.310 +
1.311 + pretty_pagename = getPrettyPageName(event_page)
1.312 +
1.313 + # Output the event.
1.314 +
1.315 + output.append(event_page.link_to_raw(request, wikiutil.escape(pretty_pagename)))
1.316 + output.append(fmt.linebreak())
1.317 +
1.318 + # End of day.
1.319 +
1.320 + output.append(fmt.table_cell(on=0))
1.321 +
1.322 + output.append(fmt.table_row(on=0))
1.323 +
1.324 + first_day += 7
1.325 +
1.326 + # End of month.
1.327 +
1.328 + output.append(fmt.table(on=0))
1.329 +
1.330 + elif mode == "list":
1.331 + output.append(fmt.bullet_list(on=1, attr={"class" : "event-listings"}))
1.332 +
1.333 + for event_page, event_details in shown_events:
1.334 +
1.335 + # Get a pretty version of the page name.
1.336 +
1.337 + pretty_pagename = getPrettyPageName(event_page)
1.338 +
1.339 + output.append(fmt.listitem(on=1, attr={"class" : "event-listing"}))
1.340 +
1.341 + # Link to the page using the pretty name.
1.342 +
1.343 + output.append(event_page.link_to_raw(request, wikiutil.escape(pretty_pagename)))
1.344 +
1.345 + # Add the event details.
1.346 +
1.347 + output.append(fmt.definition_list(on=1, attr={"class" : "event-details"}))
1.348 +
1.349 + for key, value in event_details.items():
1.350 + output.append(fmt.definition_term(on=1))
1.351 + output.append(fmt.text(key))
1.352 + output.append(fmt.definition_term(on=0))
1.353 + output.append(fmt.definition_desc(on=1))
1.354 + output.append(fmt.text(value))
1.355 + output.append(fmt.definition_desc(on=0))
1.356 +
1.357 + output.append(fmt.definition_list(on=0))
1.358 + output.append(fmt.listitem(on=0))
1.359 +
1.360 + output.append(fmt.bullet_list(on=0))
1.361 +
1.362 + return ''.join(output)
1.363 +
1.364 +# vim: tabstop=4 expandtab shiftwidth=4