EventAggregator

macros/EventAggregator.py

18:83350447c606
2009-03-26 Paul Boddie Removed build dependencies.
     1 # -*- coding: iso-8859-1 -*-     2 """     3     MoinMoin - EventAggregator Macro     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 import wikiutil    12 import EventAggregatorSupport    13 import calendar    14     15 try:    16     set    17 except NameError:    18     from sets import Set as set    19     20 Dependencies = ['pages']    21     22 # Date labels.    23     24 month_labels = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]    25 weekday_labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]    26     27 # HTML-related functions.    28     29 def getColour(s):    30     colour = [0, 0, 0]    31     digit = 0    32     for c in s:    33         colour[digit] += ord(c)    34         colour[digit] = colour[digit] % 256    35         digit += 1    36         digit = digit % 3    37     return tuple(colour)    38     39 def getBlackOrWhite(colour):    40     if sum(colour) / 3.0 > 127:    41         return (0, 0, 0)    42     else:    43         return (255, 255, 255)    44     45 # Macro functions.    46     47 def getMonth(arg):    48     n = None    49     50     if arg.startswith("current"):    51         date = EventAggregatorSupport.getCurrentMonth()    52         if len(arg) > 8:    53             n = int(arg[7:])    54     55     elif arg.startswith("yearstart"):    56         date = (EventAggregatorSupport.getCurrentYear(), 1)    57         if len(arg) > 10:    58             n = int(arg[9:])    59     60     elif arg.startswith("yearend"):    61         date = (EventAggregatorSupport.getCurrentYear(), 12)    62         if len(arg) > 8:    63             n = int(arg[7:])    64     65     else:    66         date = EventAggregatorSupport.getMonth(arg)    67     68     if n is not None:    69         date = EventAggregatorSupport.monthupdate(date, n)    70     71     return date    72     73 def getFormMonth(request, calendar_name, argname):    74     arg = request.form.get("%s-%s" % (calendar_name, argname), [None])[0]    75     if arg is not None:    76         return getMonth(arg)    77     else:    78         return None    79     80 def execute(macro, args):    81     82     """    83     Execute the 'macro' with the given 'args': an optional list of selected    84     category names (categories whose pages are to be shown), together with    85     optional named arguments of the following forms:    86     87       start=YYYY-MM     shows event details starting from the specified month    88       start=current-N   shows event details relative to the current month    89       end=YYYY-MM       shows event details ending at the specified month    90       end=current+N     shows event details relative to the current month    91     92       mode=calendar     shows a calendar view of events    93       mode=list         shows a list of events by month    94       mode=ics          provides iCalendar data for the events    95     96       names=daily       shows the name of an event on every day of that event    97       names=weekly      shows the name of an event once per week    98     99       calendar=NAME     uses the given NAME to provide request parameters which   100                         can be used to control the calendar view   101     """   102    103     request = macro.request   104     fmt = macro.formatter   105     page = fmt.page   106     _ = request.getText   107    108     # Interpret the arguments.   109    110     try:   111         parsed_args = args and wikiutil.parse_quoted_separated(args, name_value=False) or []   112     except AttributeError:   113         parsed_args = args.split(",")   114    115     parsed_args = [arg for arg in parsed_args if arg]   116    117     # Get special arguments.   118    119     category_names = []   120     calendar_start = None   121     calendar_end = None   122     mode = "calendar"   123     name_usage = "daily"   124     calendar_name = None   125    126     for arg in parsed_args:   127         if arg.startswith("start="):   128             calendar_start = getMonth(arg[6:])   129    130         elif arg.startswith("end="):   131             calendar_end = getMonth(arg[4:])   132    133         elif arg.startswith("mode="):   134             mode = arg[5:]   135    136         elif arg.startswith("names="):   137             name_usage = arg[6:]   138    139         elif arg.startswith("calendar="):   140             calendar_name = arg[9:]   141    142         else:   143             category_names.append(arg)   144    145     # Find request parameters to override settings.   146    147     if calendar_name is not None:   148         calendar_start = getFormMonth(request, calendar_name, "start") or calendar_start   149         calendar_end = getFormMonth(request, calendar_name, "end") or calendar_end   150    151     # Get the events.   152    153     events, shown_events, all_shown_events, earliest, latest = \   154         EventAggregatorSupport.getEvents(request, category_names, calendar_start, calendar_end)   155    156     # Get a concrete period of time.   157    158     first, last = EventAggregatorSupport.getConcretePeriod(calendar_start, calendar_end, earliest, latest)   159    160     # Define some useful navigation months.   161    162     if calendar_name is not None:   163         span = EventAggregatorSupport.span(first, last)   164         number_of_months = span[0] * 12 + span[1] + 1   165    166         previous_month_start = EventAggregatorSupport.prevmonth(first)   167         next_month_start = EventAggregatorSupport.nextmonth(first)   168         previous_month_end = EventAggregatorSupport.prevmonth(last)   169         next_month_end = EventAggregatorSupport.nextmonth(last)   170    171         previous_set_start = EventAggregatorSupport.monthupdate(first, -number_of_months)   172         next_set_start = EventAggregatorSupport.monthupdate(first, number_of_months)   173         previous_set_end = EventAggregatorSupport.monthupdate(last, -number_of_months)   174         next_set_end = EventAggregatorSupport.monthupdate(last, number_of_months)   175    176     # Make a calendar.   177    178     output = []   179    180     # Output top-level information.   181    182     if mode == "list":   183         output.append(fmt.bullet_list(on=1, attr={"class" : "event-listings"}))   184    185     # Visit all months in the requested range, or across known events.   186    187     for year, month in EventAggregatorSupport.daterange(first, last):   188    189         # Either output a calendar view...   190    191         if mode == "calendar":   192    193             # Output a month.   194    195             output.append(fmt.table(on=1, attrs={"tableclass" : "event-month"}))   196    197             output.append(fmt.table_row(on=1))   198             output.append(fmt.table_cell(on=1, attrs={"class" : "event-month-heading", "colspan" : "7"}))   199    200             # Either write a month heading or produce a link for navigable   201             # calendars.   202    203             month_label = _(month_labels[month - 1]) # zero-based labels   204    205             if calendar_name is not None:   206    207                 # Links to the previous set of months and to a calendar shifted   208                 # back one month.   209    210                 previous_set_link = "%s-start=%04d-%02d&%s-end=%04d-%02d" % (   211                     (calendar_name,) + previous_set_start + (calendar_name,) + previous_set_end   212                     )   213                 previous_month_link = "%s-start=%04d-%02d&%s-end=%04d-%02d" % (   214                     (calendar_name,) + previous_month_start + (calendar_name,) + previous_month_end   215                     )   216    217                 output.append(fmt.span(on=1, css_class="previous-month"))   218                 output.append(page.link_to_raw(request, wikiutil.escape("<<"), previous_set_link))   219                 output.append(fmt.text(" "))   220                 output.append(page.link_to_raw(request, wikiutil.escape("<"), previous_month_link))   221                 output.append(fmt.span(on=0))   222    223                 # Links to the next set of months and to a calendar shifted   224                 # forward one month.   225    226                 next_set_link = "%s-start=%04d-%02d&%s-end=%04d-%02d" % (   227                     (calendar_name,) + next_set_start + (calendar_name,) + next_set_end   228                     )   229                 next_month_link = "%s-start=%04d-%02d&%s-end=%04d-%02d" % (   230                     (calendar_name,) + next_month_start + (calendar_name,) + next_month_end   231                     )   232    233                 output.append(fmt.span(on=1, css_class="next-month"))   234                 output.append(page.link_to_raw(request, wikiutil.escape(">"), next_month_link))   235                 output.append(fmt.text(" "))   236                 output.append(page.link_to_raw(request, wikiutil.escape(">>"), next_set_link))   237                 output.append(fmt.span(on=0))   238    239                 # A link leading to this month being at the top of the calendar.   240    241                 full_month_label = "%s %s" % (month_label, year)   242                 month_link = "%s-start=%04d-%02d&%s-end=%04d-%02d" % (   243                     (calendar_name, year, month, calendar_name) +   244                     EventAggregatorSupport.monthupdate((year, month), number_of_months - 1)   245                     )   246                 output.append(page.link_to_raw(request, wikiutil.escape(full_month_label), month_link))   247    248             else:   249                 output.append(fmt.span(on=1))   250                 output.append(fmt.text(month_label))   251                 output.append(fmt.span(on=0))   252                 output.append(fmt.text(" "))   253                 output.append(fmt.span(on=1))   254                 output.append(fmt.text(year))   255                 output.append(fmt.span(on=0))   256    257             output.append(fmt.table_cell(on=0))   258             output.append(fmt.table_row(on=0))   259    260             # Weekday headings.   261    262             output.append(fmt.table_row(on=1))   263    264             for weekday in range(0, 7):   265                 output.append(fmt.table_cell(on=1, attrs={"class" : "event-weekday-heading"}))   266                 output.append(fmt.text(_(weekday_labels[weekday])))   267                 output.append(fmt.table_cell(on=0))   268    269             output.append(fmt.table_row(on=0))   270    271             # Process the days of the month.   272    273             start_weekday, number_of_days = calendar.monthrange(year, month)   274    275             # The start weekday is the weekday of day number 1.   276             # Find the first day of the week, counting from below zero, if   277             # necessary, in order to land on the first day of the month as   278             # day number 1.   279    280             first_day = 1 - start_weekday   281    282             while first_day <= number_of_days:   283    284                 # Find events in this week and determine how to mark them on the   285                 # calendar.   286    287                 week_start = (year, month, max(first_day, 1))   288                 week_end = (year, month, min(first_day + 6, number_of_days))   289    290                 week_coverage, week_events = EventAggregatorSupport.getCoverage(   291                     week_start, week_end, shown_events.get((year, month), []))   292    293                 # Output a week, starting with the day numbers.   294    295                 output.append(fmt.table_row(on=1))   296    297                 for weekday in range(0, 7):   298                     day = first_day + weekday   299                     date = (year, month, day)   300    301                     # Output out-of-month days.   302    303                     if day < 1 or day > number_of_days:   304                         output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-heading event-day-excluded"}))   305                         output.append(fmt.table_cell(on=0))   306    307                     # Output normal days.   308    309                     else:   310                         if date in week_coverage:   311                             output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-heading event-day-busy"}))   312                         else:   313                             output.append(fmt.table_cell(on=1, attrs={"class" : "event-day-heading event-day-empty"}))   314    315                         output.append(fmt.div(on=1))   316                         output.append(fmt.span(on=1, css_class="event-day-number"))   317                         output.append(fmt.text(day))   318                         output.append(fmt.span(on=0))   319                         output.append(fmt.div(on=0))   320    321                         # End of day.   322    323                         output.append(fmt.table_cell(on=0))   324    325                 # End of day numbers.   326    327                 output.append(fmt.table_row(on=0))   328    329                 # Either generate empty days...   330    331                 if not week_events:   332                     output.append(fmt.table_row(on=1))   333    334                     for weekday in range(0, 7):   335                         day = first_day + weekday   336                         date = (year, month, day)   337    338                         # Output out-of-month days.   339    340                         if day < 1 or day > number_of_days:   341                             output.append(fmt.table_cell(on=1, attrs={"class" : "event-day event-day-excluded"}))   342                             output.append(fmt.table_cell(on=0))   343    344                         # Output empty days.   345    346                         else:   347                             output.append(fmt.table_cell(on=1, attrs={"class" : "event-day event-day-empty"}))   348    349                     output.append(fmt.table_row(on=0))   350    351                 # Or visit each set of scheduled events...   352    353                 else:   354                     for coverage, events in week_events:   355    356                         # Output each set.   357    358                         output.append(fmt.table_row(on=1))   359    360                         # Then, output day details.   361    362                         for weekday in range(0, 7):   363                             day = first_day + weekday   364                             date = (year, month, day)   365    366                             # Skip out-of-month days.   367    368                             if day < 1 or day > number_of_days:   369                                 output.append(fmt.table_cell(on=1, attrs={"class" : "event-day event-day-excluded"}))   370                                 output.append(fmt.table_cell(on=0))   371                                 continue   372    373                             # Output the day.   374    375                             if date in coverage:   376                                 output.append(fmt.table_cell(on=1, attrs={"class" : "event-day event-day-busy"}))   377                             else:   378                                 output.append(fmt.table_cell(on=1, attrs={"class" : "event-day event-day-empty"}))   379    380                             # Get event details for the current day.   381    382                             for event_page, event_details in events:   383                                 if not (event_details["start"] <= date <= event_details["end"]):   384                                     continue   385    386                                 # Get a pretty version of the page name.   387    388                                 pretty_pagename = EventAggregatorSupport.getPrettyPageName(event_page)   389    390                                 # Generate a colour for the event.   391    392                                 bg = getColour(event_page.page_name)   393                                 fg = getBlackOrWhite(bg)   394                                 style = ("background-color: rgb(%d, %d, %d); color: rgb(%d, %d, %d);" % (bg + fg))   395                                 hidden_style = ("background-color: rgb(%d, %d, %d); color: rgb(%d, %d, %d);" % (bg + bg))   396    397                                 css_classes = ["event-summary"]   398    399                                 if event_details["start"] == date:   400                                     css_classes.append("event-starts")   401                                     start_of_event = 1   402                                 else:   403                                     start_of_event = 0   404    405                                 if event_details["end"] == date:   406                                     css_classes.append("event-ends")   407    408                                 # Output the event.   409    410                                 if name_usage == "daily" or start_of_event or weekday == 0 or day == 1:   411                                     hide_text = 0   412                                 else:   413                                     hide_text = 1   414    415                                 if not hide_text:   416                                     output.append(fmt.div(on=1, css_class=(" ".join(css_classes)), style=style))   417                                     output.append(event_page.link_to_raw(request, wikiutil.escape(pretty_pagename)))   418                                 else:   419                                     output.append(fmt.div(on=1, css_class=(" ".join(css_classes)), style=hidden_style))   420                                     output.append(fmt.text(pretty_pagename))   421    422                                 output.append(fmt.div(on=0))   423    424                             # End of day.   425    426                             output.append(fmt.table_cell(on=0))   427    428                         # End of set.   429    430                         output.append(fmt.table_row(on=0))   431    432                 # Process the next week...   433    434                 first_day += 7   435    436             # End of month.   437    438             output.append(fmt.table(on=0))   439    440         # Or output a summary view...   441    442         elif mode == "list":   443    444             output.append(fmt.listitem(on=1, attr={"class" : "event-listings-month"}))   445             output.append(fmt.div(on=1, attr={"class" : "event-listings-month-heading"}))   446             output.append(fmt.span(on=1))   447             output.append(fmt.text(_(month_labels[month - 1]))) # zero-based labels   448             output.append(fmt.span(on=0))   449             output.append(fmt.text(" "))   450             output.append(fmt.span(on=1))   451             output.append(fmt.text(year))   452             output.append(fmt.span(on=0))   453             output.append(fmt.div(on=0))   454    455             output.append(fmt.bullet_list(on=1, attr={"class" : "event-month-listings"}))   456    457             for event_page, event_details in shown_events.get((year, month), []):   458    459                 # Get a pretty version of the page name.   460    461                 pretty_pagename = EventAggregatorSupport.getPrettyPageName(event_page)   462    463                 output.append(fmt.listitem(on=1, attr={"class" : "event-listing"}))   464    465                 # Link to the page using the pretty name.   466    467                 output.append(event_page.link_to_raw(request, wikiutil.escape(pretty_pagename)))   468    469                 # Add the event details.   470    471                 output.append(fmt.definition_list(on=1, attr={"class" : "event-details"}))   472    473                 for key, value in event_details.items():   474                     output.append(fmt.definition_term(on=1))   475                     output.append(fmt.text(key))   476                     output.append(fmt.definition_term(on=0))   477                     output.append(fmt.definition_desc(on=1))   478                     output.append(fmt.text(value))   479                     output.append(fmt.definition_desc(on=0))   480    481                 output.append(fmt.definition_list(on=0))   482                 output.append(fmt.listitem(on=0))   483    484             output.append(fmt.bullet_list(on=0))   485    486     # Output top-level information.   487    488     # End of list view output.   489    490     if mode == "list":   491         output.append(fmt.bullet_list(on=0))   492    493     return ''.join(output)   494    495 # vim: tabstop=4 expandtab shiftwidth=4