1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - EventAggregator library 4 5 @copyright: 2008, 2009 by Paul Boddie <paul@boddie.org.uk> 6 @copyright: 2000-2004 Juergen Hermann <jh@web.de>, 7 2005-2008 MoinMoin:ThomasWaldmann. 8 @license: GNU GPL (v2 or later), see COPYING.txt for details. 9 """ 10 11 from MoinMoin.Page import Page 12 from MoinMoin import search, version 13 import calendar 14 import datetime 15 import re 16 17 __version__ = "0.1" 18 19 # Regular expressions where MoinMoin does not provide the required support. 20 21 category_regexp = None 22 definition_list_regexp = re.compile(ur'^\s+(?P<term>.*?)::\s(?P<desc>.*?)$', re.UNICODE | re.MULTILINE) 23 date_regexp = re.compile(ur'(?P<year>[0-9]{4})-(?P<month>[0-9]{2})-(?P<day>[0-9]{2})', re.UNICODE) 24 month_regexp = re.compile(ur'(?P<year>[0-9]{4})-(?P<month>[0-9]{2})', re.UNICODE) 25 26 # Utility functions. 27 28 def isMoin15(): 29 return version.release.startswith("1.5.") 30 31 def getCategoryPattern(request): 32 global category_regexp 33 34 try: 35 return request.cfg.cache.page_category_regexact 36 except AttributeError: 37 38 # Use regular expression from MoinMoin 1.7.1 otherwise. 39 40 if category_regexp is None: 41 category_regexp = re.compile(u'^%s$' % ur'(?P<all>Category(?P<key>(?!Template)\S+))', re.UNICODE) 42 return category_regexp 43 44 # The main activity functions. 45 46 def getPages(pagename, request): 47 48 "Return the links minus category links for 'pagename' using the 'request'." 49 50 query = search.QueryParser().parse_query('category:%s' % pagename) 51 if isMoin15(): 52 results = search.searchPages(request, query) 53 results.sortByPagename() 54 else: 55 results = search.searchPages(request, query, "page_name") 56 57 cat_pattern = getCategoryPattern(request) 58 pages = [] 59 for page in results.hits: 60 if not cat_pattern.match(page.page_name): 61 pages.append(page) 62 return pages 63 64 def getPrettyPageName(page): 65 66 "Return a nicely formatted title/name for the given 'page'." 67 68 return page.split_title(force=1).replace("_", " ").replace("/", u" ? ") 69 70 def getEventDetails(page): 71 72 "Return a dictionary of event details from the given 'page'." 73 74 event_details = {} 75 76 if page.pi["format"] == "wiki": 77 for match in definition_list_regexp.finditer(page.body): 78 79 # Permit case-insensitive list terms. 80 81 term = match.group("term").lower() 82 desc = match.group("desc") 83 84 # Special value type handling. 85 86 if term in ("start", "end"): 87 desc = getDate(desc) 88 elif term in ("topics",): 89 desc = [value.strip() for value in desc.split(",")] 90 91 if desc is not None: 92 event_details[term] = desc 93 94 return event_details 95 96 def getDate(s): 97 98 "Parse the string 's', extracting and returning a date string." 99 100 m = date_regexp.search(s) 101 if m: 102 return tuple(map(int, m.groups())) 103 else: 104 return None 105 106 def getMonth(s): 107 108 "Parse the string 's', extracting and returning a month string." 109 110 m = month_regexp.search(s) 111 if m: 112 return tuple(map(int, m.groups())) 113 else: 114 return None 115 116 def getCurrentMonth(): 117 118 "Return the current month as a (year, month) tuple." 119 120 today = datetime.date.today() 121 return (today.year, today.month) 122 123 def monthupdate(date, n): 124 125 "Return 'date' updated by 'n' months." 126 127 if n < 0: 128 fn = prevmonth 129 else: 130 fn = nextmonth 131 132 i = 0 133 while i < abs(n): 134 date = fn(date) 135 i += 1 136 137 return date 138 139 def daterange(first, last): 140 141 "Get the range of dates starting at 'first' and ending on 'last'." 142 143 results = [] 144 step = last > first and 1 or -1 145 146 months_only = len(first) == 2 147 start_year = first[0] 148 end_year = last[0] 149 150 for year in range(start_year, end_year + step, step): 151 if step == 1 and year < end_year: 152 end_month = 12 153 elif step == -1 and year > end_year: 154 end_month = 1 155 else: 156 end_month = last[1] 157 158 if step == 1 and year > start_year: 159 start_month = 1 160 elif step == -1 and year < start_year: 161 start_month = 12 162 else: 163 start_month = first[1] 164 165 for month in range(start_month, end_month + step, step): 166 if months_only: 167 results.append((year, month)) 168 else: 169 if step == 1 and month < end_month: 170 _wd, end_day = calendar.monthrange(year, month) 171 elif step == -1 and month > end_month: 172 end_day = 1 173 else: 174 end_day = last[2] 175 176 if step == 1 and month > start_month: 177 start_day = 1 178 elif step == -1 and month < start_month: 179 _wd, start_day = calendar.monthrange(year, month) 180 else: 181 start_day = first[2] 182 183 for day in range(start_day, end_day + step, step): 184 results.append((year, month, day)) 185 186 return results 187 188 def nextdate(date): 189 190 "Return the date following the given 'date'." 191 192 year, month, day = date 193 _wd, end_day = calendar.monthrange(year, month) 194 if day == end_day: 195 if month == 12: 196 return (year + 1, 1, 1) 197 else: 198 return (year, month + 1, 1) 199 else: 200 return (year, month, day + 1) 201 202 def prevdate(date): 203 204 "Return the date preceding the given 'date'." 205 206 year, month, day = date 207 if day == 1: 208 if month == 1: 209 return (year - 1, 12, 31) 210 else: 211 _wd, end_day = calendar.monthrange(year, month - 1) 212 return (year, month - 1, end_day) 213 else: 214 return (year, month, day - 1) 215 216 def nextmonth(date): 217 218 "Return the (year, month) tuple following 'date'." 219 220 year, month = date 221 if month == 12: 222 return (year + 1, 1) 223 else: 224 return year, month + 1 225 226 def prevmonth(date): 227 228 "Return the (year, month) tuple preceding 'date'." 229 230 year, month = date 231 if month == 1: 232 return (year - 1, 12) 233 else: 234 return year, month - 1 235 236 def getEvents(request, category_names, calendar_start=None, calendar_end=None): 237 238 """ 239 Using the 'request', generate a list of events found on pages belonging to 240 the specified 'category_names', using the optional 'calendar_start' and 241 'calendar_end' month tuples of the form (year, month) to indicate a window 242 of interest. 243 244 Return a list of events, a dictionary mapping months to event lists (within 245 the window of interest), a list of all events within the window of interest, 246 the earliest month of an event within the window of interest, and the latest 247 month of an event within the window of interest. 248 """ 249 250 # Re-order the window, if appropriate. 251 252 if calendar_start is not None and calendar_end is not None and calendar_start > calendar_end: 253 calendar_start, calendar_end = calendar_end, calendar_start 254 255 events = [] 256 shown_events = {} 257 all_shown_events = [] 258 259 earliest = None 260 latest = None 261 262 for category_name in category_names: 263 264 # Get the pages and page names in the category. 265 266 pages_in_category = getPages(category_name, request) 267 268 # Visit each page in the category. 269 270 for page_in_category in pages_in_category: 271 pagename = page_in_category.page_name 272 273 # Get a real page, not a result page. 274 275 real_page_in_category = Page(request, pagename) 276 event_details = getEventDetails(real_page_in_category) 277 278 # Define the event as the page together with its details. 279 280 event = (real_page_in_category, event_details) 281 events.append(event) 282 283 # Test for the suitability of the event. 284 285 if event_details.has_key("start") and event_details.has_key("end"): 286 287 start_month = event_details["start"][:2] 288 end_month = event_details["end"][:2] 289 290 # Compare the months of the dates to the requested calendar 291 # window, if any. 292 293 if (calendar_start is None or end_month >= calendar_start) and \ 294 (calendar_end is None or start_month <= calendar_end): 295 296 all_shown_events.append(event) 297 298 if earliest is None or start_month < earliest: 299 earliest = start_month 300 if latest is None or end_month > latest: 301 latest = end_month 302 303 # Store the event in the month-specific dictionary. 304 305 first = max(start_month, calendar_start or start_month) 306 last = min(end_month, calendar_end or end_month) 307 308 for event_month in daterange(first, last): 309 if not shown_events.has_key(event_month): 310 shown_events[event_month] = [] 311 shown_events[event_month].append(event) 312 313 return events, shown_events, all_shown_events, earliest, latest 314 315 # vim: tabstop=4 expandtab shiftwidth=4