web/views/old_calendar.py
brancholdstable
changeset 7254 c0278ad81823
parent 7178 a62f24e1497e
parent 7239 078cfa8a5187
child 7258 2e7f0d6fa2d6
child 7265 8eedf71f280e
equal deleted inserted replaced
7178:a62f24e1497e 7254:c0278ad81823
     1 # copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     3 #
       
     4 # This file is part of CubicWeb.
       
     5 #
       
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
       
     7 # terms of the GNU Lesser General Public License as published by the Free
       
     8 # Software Foundation, either version 2.1 of the License, or (at your option)
       
     9 # any later version.
       
    10 #
       
    11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT
       
    12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
       
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
       
    14 # details.
       
    15 #
       
    16 # You should have received a copy of the GNU Lesser General Public License along
       
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
       
    18 """html calendar views"""
       
    19 
       
    20 __docformat__ = "restructuredtext en"
       
    21 _ = unicode
       
    22 
       
    23 from datetime import date, time, timedelta
       
    24 
       
    25 from logilab.mtconverter import xml_escape
       
    26 from logilab.common.date import (ONEDAY, ONEWEEK, days_in_month, previous_month,
       
    27                                  next_month, first_day, last_day, date_range)
       
    28 
       
    29 from cubicweb.interfaces import ICalendarViews
       
    30 from cubicweb.selectors import implements, adaptable
       
    31 from cubicweb.view import EntityView, EntityAdapter, implements_adapter_compat
       
    32 
       
    33 class ICalendarViewsAdapter(EntityAdapter):
       
    34     """calendar views interface"""
       
    35     __needs_bw_compat__ = True
       
    36     __regid__ = 'ICalendarViews'
       
    37     __select__ = implements(ICalendarViews, warn=False) # XXX for bw compat, should be abstract
       
    38 
       
    39     @implements_adapter_compat('ICalendarViews')
       
    40     def matching_dates(self, begin, end):
       
    41         """
       
    42         :param begin: day considered as begin of the range (`DateTime`)
       
    43         :param end: day considered as end of the range (`DateTime`)
       
    44 
       
    45         :return:
       
    46           a list of dates (`DateTime`) in the range [`begin`, `end`] on which
       
    47           this entity apply
       
    48         """
       
    49         raise NotImplementedError
       
    50 
       
    51 
       
    52 # used by i18n tools
       
    53 WEEKDAYS = [_("monday"), _("tuesday"), _("wednesday"), _("thursday"),
       
    54             _("friday"), _("saturday"), _("sunday")]
       
    55 MONTHNAMES = [ _('january'), _('february'), _('march'), _('april'), _('may'),
       
    56                _('june'), _('july'), _('august'), _('september'), _('october'),
       
    57                _('november'), _('december')
       
    58                ]
       
    59 
       
    60 class _CalendarView(EntityView):
       
    61     """base calendar view containing helpful methods to build calendar views"""
       
    62     __select__ = adaptable('ICalendarViews')
       
    63     paginable = False
       
    64 
       
    65     # Navigation building methods / views ####################################
       
    66 
       
    67     PREV = u'<a href="%s">&lt;&lt;</a>&#160;&#160;<a href="%s">&lt;</a>'
       
    68     NEXT = u'<a href="%s">&gt;</a>&#160;&#160;<a href="%s">&gt;&gt;</a>'
       
    69     NAV_HEADER = u"""<table class="calendarPageHeader">
       
    70 <tr><td class="prev">%s</td><td class="next">%s</td></tr>
       
    71 </table>
       
    72 """ % (PREV, NEXT)
       
    73 
       
    74     def nav_header(self, date, smallshift=3, bigshift=9):
       
    75         """prints shortcut links to go to previous/next steps (month|week)"""
       
    76         prev1 = previous_month(date, smallshift)
       
    77         next1 = next_month(date, smallshift)
       
    78         prev2 = previous_month(date, bigshift)
       
    79         next2 = next_month(date, bigshift)
       
    80         rql = self.cw_rset.printable_rql()
       
    81         return self.NAV_HEADER % (
       
    82             xml_escape(self._cw.build_url(rql=rql, vid=self.__regid__, year=prev2.year,
       
    83                                           month=prev2.month)),
       
    84             xml_escape(self._cw.build_url(rql=rql, vid=self.__regid__, year=prev1.year,
       
    85                                           month=prev1.month)),
       
    86             xml_escape(self._cw.build_url(rql=rql, vid=self.__regid__, year=next1.year,
       
    87                                           month=next1.month)),
       
    88             xml_escape(self._cw.build_url(rql=rql, vid=self.__regid__, year=next2.year,
       
    89                                           month=next2.month)))
       
    90 
       
    91 
       
    92     # Calendar building methods ##############################################
       
    93 
       
    94     def build_calendars(self, schedule, begin, end):
       
    95         """build several HTML calendars at once, one for each month
       
    96         between begin and end
       
    97         """
       
    98         return [self.build_calendar(schedule, date)
       
    99                 for date in date_range(begin, end, incmonth=1)]
       
   100 
       
   101     def build_calendar(self, schedule, first_day):
       
   102         """method responsible for building *one* HTML calendar"""
       
   103         # FIXME  iterates between [first_day-first_day.day_of_week ;
       
   104         #                          last_day+6-last_day.day_of_week]
       
   105         umonth = self._cw.format_date(first_day, '%B %Y') # localized month name
       
   106         rows = []
       
   107         current_row = [NO_CELL] * first_day.weekday()
       
   108         for daynum in xrange(0, days_in_month(first_day)):
       
   109             # build cell day
       
   110             day = first_day + timedelta(daynum)
       
   111             events = schedule.get(day)
       
   112             if events:
       
   113                 events = [u'\n'.join(event) for event in events.values()]
       
   114                 current_row.append(CELL % (daynum+1, '\n'.join(events)))
       
   115             else:
       
   116                 current_row.append(EMPTY_CELL % (daynum+1))
       
   117             # store & reset current row on Sundays
       
   118             if day.weekday() == 6:
       
   119                 rows.append(u'<tr>%s%s</tr>' % (WEEKNUM_CELL % day.isocalendar()[1], ''.join(current_row)))
       
   120                 current_row = []
       
   121         current_row.extend([NO_CELL] * (6-day.weekday()))
       
   122         rql = self.cw_rset.printable_rql()
       
   123         if day.weekday() != 6:
       
   124             rows.append(u'<tr>%s%s</tr>' % (WEEKNUM_CELL % day.isocalendar()[1], ''.join(current_row)))
       
   125         url = self._cw.build_url(rql=rql, vid='calendarmonth',
       
   126                                  year=first_day.year, month=first_day.month)
       
   127         monthlink = u'<a href="%s">%s</a>' % (xml_escape(url), umonth)
       
   128         return CALENDAR(self._cw) % (monthlink, '\n'.join(rows))
       
   129 
       
   130     def _mk_schedule(self, begin, end, itemvid='calendaritem'):
       
   131         """private method that gathers information from resultset
       
   132         and builds calendars according to it
       
   133 
       
   134         :param begin: begin of date range
       
   135         :param end: end of date rangs
       
   136         :param itemvid: which view to call to render elements in cells
       
   137 
       
   138         returns { day1 : { hour : [views] },
       
   139                   day2 : { hour : [views] } ... }
       
   140         """
       
   141         # put this here since all sub views are calling this method
       
   142         self._cw.add_css('cubicweb.calendar.css')
       
   143         schedule = {}
       
   144         for row in xrange(len(self.cw_rset.rows)):
       
   145             entity = self.cw_rset.get_entity(row, 0)
       
   146             infos = u'<div class="event">'
       
   147             infos += self._cw.view(itemvid, self.cw_rset, row=row)
       
   148             infos += u'</div>'
       
   149             for date_ in entity.cw_adapt_to('ICalendarViews').matching_dates(begin, end):
       
   150                 day = date(date_.year, date_.month, date_.day)
       
   151                 try:
       
   152                     dt = time(date_.hour, date_.minute, date_.second)
       
   153                 except AttributeError:
       
   154                     # date instance
       
   155                     dt = time(0, 0, 0)
       
   156                 schedule.setdefault(day, {})
       
   157                 schedule[day].setdefault(dt, []).append(infos)
       
   158         return schedule
       
   159 
       
   160 
       
   161     @staticmethod
       
   162     def get_date_range(day, shift=4):
       
   163         """returns a couple (begin, end)
       
   164 
       
   165         <begin> is the first day of current_month - shift
       
   166         <end> is the last day of current_month + (shift+1)
       
   167         """
       
   168         begin = first_day(previous_month(day, shift))
       
   169         end = last_day(next_month(day, shift))
       
   170         return begin, end
       
   171 
       
   172     def _build_ampm_cells(self, events):
       
   173         """create a view without any hourly details.
       
   174 
       
   175         :param events: dictionnary with all events classified by hours
       
   176         """
       
   177         # split events according am/pm
       
   178         am_events = [event for e_time, e_list in events.iteritems()
       
   179                      if 0 <= e_time.hour < 12
       
   180                      for event in e_list]
       
   181         pm_events = [event for e_time, e_list in events.iteritems()
       
   182                      if 12 <= e_time.hour < 24
       
   183                      for event in e_list]
       
   184         # format each am/pm cell
       
   185         if am_events:
       
   186             am_content = AMPM_CONTENT % ("amCell", "am", '\n'.join(am_events))
       
   187         else:
       
   188             am_content = AMPM_EMPTY % ("amCell", "am")
       
   189         if pm_events:
       
   190             pm_content = AMPM_CONTENT % ("pmCell", "pm", '\n'.join(pm_events))
       
   191         else:
       
   192             pm_content = AMPM_EMPTY % ("pmCell", "pm")
       
   193         return am_content, pm_content
       
   194 
       
   195 
       
   196 
       
   197 class YearCalendarView(_CalendarView):
       
   198     __regid__ = 'calendaryear'
       
   199     title = _('calendar (year)')
       
   200 
       
   201     def call(self, year=None, month=None):
       
   202         """this view renders a 3x3 calendars' table"""
       
   203         year = year or int(self._cw.form.get('year', date.today().year))
       
   204         month = month or int(self._cw.form.get('month', date.today().month))
       
   205         center_date = date(year, month, 1)
       
   206         begin, end = self.get_date_range(day=center_date)
       
   207         schedule = self._mk_schedule(begin, end)
       
   208         self.w(self.nav_header(center_date))
       
   209         calendars = tuple(self.build_calendars(schedule, begin, end))
       
   210         self.w(SMALL_CALENDARS_PAGE % calendars)
       
   211 
       
   212 
       
   213 class SemesterCalendarView(_CalendarView):
       
   214     """this view renders three semesters as three rows of six columns,
       
   215     one column per month
       
   216     """
       
   217     __regid__ = 'calendarsemester'
       
   218     title = _('calendar (semester)')
       
   219 
       
   220     def call(self, year=None, month=None):
       
   221         year = year or int(self._cw.form.get('year', date.today().year))
       
   222         month = month or int(self._cw.form.get('month', date.today().month))
       
   223         begin = previous_month(date(year, month, 1), 2)
       
   224         end = next_month(date(year, month, 1), 3)
       
   225         schedule = self._mk_schedule(begin, end)
       
   226         self.w(self.nav_header(date(year, month, 1), 1, 6))
       
   227         self.w(u'<table class="semesterCalendar">')
       
   228         self.build_calendars(schedule, begin, end)
       
   229         self.w(u'</table>')
       
   230         self.w(self.nav_header(date(year, month, 1), 1, 6))
       
   231 
       
   232     def build_calendars(self, schedule, begin, end):
       
   233         self.w(u'<tr>')
       
   234         rql = self.cw_rset.printable_rql()
       
   235         for cur_month in date_range(begin, end, incmonth=1):
       
   236             umonth = u'%s&#160;%s' % (self._cw.format_date(cur_month, '%B'), cur_month.year)
       
   237             url = self._cw.build_url(rql=rql, vid=self.__regid__,
       
   238                                      year=cur_month.year, month=cur_month.month)
       
   239             self.w(u'<th colspan="2"><a href="%s">%s</a></th>' % (xml_escape(url),
       
   240                                                                   umonth))
       
   241         self.w(u'</tr>')
       
   242         _ = self._cw._
       
   243         for day_num in xrange(31):
       
   244             self.w(u'<tr>')
       
   245             for cur_month in date_range(begin, end, incmonth=1):
       
   246                 if day_num >= days_in_month(cur_month):
       
   247                     self.w(u'%s%s' % (NO_CELL, NO_CELL))
       
   248                 else:
       
   249                     day = date(cur_month.year, cur_month.month, day_num+1)
       
   250                     events = schedule.get(day)
       
   251                     self.w(u'<td>%s&#160;%s</td>\n' % (_(WEEKDAYS[day.weekday()])[0].upper(), day_num+1))
       
   252                     self.format_day_events(day, events)
       
   253             self.w(u'</tr>')
       
   254 
       
   255     def format_day_events(self, day, events):
       
   256         if events:
       
   257             events = ['\n'.join(event) for event in events.values()]
       
   258             self.w(WEEK_CELL % '\n'.join(events))
       
   259         else:
       
   260             self.w(WEEK_EMPTY_CELL)
       
   261 
       
   262 
       
   263 class MonthCalendarView(_CalendarView):
       
   264     """this view renders a 3x1 calendars' table"""
       
   265     __regid__ = 'calendarmonth'
       
   266     title = _('calendar (month)')
       
   267 
       
   268     def call(self, year=None, month=None):
       
   269         year = year or int(self._cw.form.get('year', date.today().year))
       
   270         month = month or int(self._cw.form.get('month', date.today().month))
       
   271         center_date = date(year, month, 1)
       
   272         begin, end = self.get_date_range(day=center_date, shift=1)
       
   273         schedule = self._mk_schedule(begin, end)
       
   274         calendars = self.build_calendars(schedule, begin, end)
       
   275         self.w(self.nav_header(center_date, 1, 3))
       
   276         self.w(BIG_CALENDARS_PAGE % tuple(calendars))
       
   277         self.w(self.nav_header(center_date, 1, 3))
       
   278 
       
   279 
       
   280 class WeekCalendarView(_CalendarView):
       
   281     """this view renders a calendar for week events"""
       
   282     __regid__ = 'calendarweek'
       
   283     title = _('calendar (week)')
       
   284 
       
   285     def call(self, year=None, week=None):
       
   286         year = year or int(self._cw.form.get('year', date.today().year))
       
   287         week = week or int(self._cw.form.get('week', date.today().isocalendar()[1]))
       
   288         day0 = date(year, 1, 1)
       
   289         first_day_of_week = day0 - day0.weekday()*ONEDAY + ONEWEEK
       
   290         begin, end = first_day_of_week- ONEWEEK, first_day_of_week + 2*ONEWEEK
       
   291         schedule = self._mk_schedule(begin, end, itemvid='calendarlargeitem')
       
   292         self.w(self.nav_header(first_day_of_week))
       
   293         self.w(u'<table class="weekCalendar">')
       
   294         _weeks = [(first_day_of_week-ONEWEEK, first_day_of_week-ONEDAY),
       
   295                   (first_day_of_week, first_day_of_week+6*ONEDAY),
       
   296                   (first_day_of_week+ONEWEEK, first_day_of_week+13*ONEDAY)]
       
   297         self.build_calendar(schedule, _weeks)
       
   298         self.w(u'</table>')
       
   299         self.w(self.nav_header(first_day_of_week))
       
   300 
       
   301     def build_calendar(self, schedule, weeks):
       
   302         rql = self.cw_rset.printable_rql()
       
   303         _ = self._cw._
       
   304         for monday, sunday in weeks:
       
   305             umonth = self._cw.format_date(monday, '%B %Y')
       
   306             url = self._cw.build_url(rql=rql, vid='calendarmonth',
       
   307                                      year=monday.year, month=monday.month)
       
   308             monthlink = '<a href="%s">%s</a>' % (xml_escape(url), umonth)
       
   309             self.w(u'<tr><th colspan="3">%s %s (%s)</th></tr>' \
       
   310                   % (_('week'), monday.isocalendar()[1], monthlink))
       
   311             for day in date_range(monday, sunday+ONEDAY):
       
   312                 self.w(u'<tr>')
       
   313                 self.w(u'<td>%s</td>' % _(WEEKDAYS[day.weekday()]))
       
   314                 self.w(u'<td>%s</td>' % (day.strftime('%Y-%m-%d')))
       
   315                 events = schedule.get(day)
       
   316                 if events:
       
   317                     events = ['\n'.join(event) for event in events.values()]
       
   318                     self.w(WEEK_CELL % '\n'.join(events))
       
   319                 else:
       
   320                     self.w(WEEK_EMPTY_CELL)
       
   321                 self.w(u'</tr>')
       
   322 
       
   323     def nav_header(self, date, smallshift=1, bigshift=3):
       
   324         """prints shortcut links to go to previous/next steps (month|week)"""
       
   325         prev1 = date - ONEWEEK * smallshift
       
   326         prev2 = date - ONEWEEK * bigshift
       
   327         next1 = date + ONEWEEK * smallshift
       
   328         next2 = date + ONEWEEK * bigshift
       
   329         rql = self.cw_rset.printable_rql()
       
   330         return self.NAV_HEADER % (
       
   331             xml_escape(self._cw.build_url(rql=rql, vid=self.__regid__, year=prev2.year, week=prev2.isocalendar()[1])),
       
   332             xml_escape(self._cw.build_url(rql=rql, vid=self.__regid__, year=prev1.year, week=prev1.isocalendar()[1])),
       
   333             xml_escape(self._cw.build_url(rql=rql, vid=self.__regid__, year=next1.year, week=next1.isocalendar()[1])),
       
   334             xml_escape(self._cw.build_url(rql=rql, vid=self.__regid__, year=next2.year, week=next2.isocalendar()[1])))
       
   335 
       
   336 
       
   337 
       
   338 class AMPMYearCalendarView(YearCalendarView):
       
   339     __regid__ = 'ampmcalendaryear'
       
   340     title = _('am/pm calendar (year)')
       
   341 
       
   342     def build_calendar(self, schedule, first_day):
       
   343         """method responsible for building *one* HTML calendar"""
       
   344         umonth = self._cw.format_date(first_day, '%B %Y') # localized month name
       
   345         rows = [] # each row is: (am,pm), (am,pm) ... week_title
       
   346         current_row = [(NO_CELL, NO_CELL, NO_CELL)] * first_day.weekday()
       
   347         rql = self.cw_rset.printable_rql()
       
   348         for daynum in xrange(0, days_in_month(first_day)):
       
   349             # build cells day
       
   350             day = first_day + timedelta(daynum)
       
   351             events = schedule.get(day)
       
   352             if events:
       
   353                 current_row.append((AMPM_DAY % (daynum+1),) + self._build_ampm_cells(events))
       
   354             else:
       
   355                 current_row.append((AMPM_DAY % (daynum+1),
       
   356                                     AMPM_EMPTY % ("amCell", "am"),
       
   357                                     AMPM_EMPTY % ("pmCell", "pm")))
       
   358             # store & reset current row on Sundays
       
   359             if day.weekday() == 6:
       
   360                 url = self._cw.build_url(rql=rql, vid='ampmcalendarweek',
       
   361                                      year=day.year, week=day.isocalendar()[1])
       
   362                 weeklink = '<a href="%s">%s</a>' % (xml_escape(url),
       
   363                                                     day.isocalendar()[1])
       
   364                 current_row.append(WEEKNUM_CELL % weeklink)
       
   365                 rows.append(current_row)
       
   366                 current_row = []
       
   367         current_row.extend([(NO_CELL, NO_CELL, NO_CELL)] * (6-day.weekday()))
       
   368         url = self._cw.build_url(rql=rql, vid='ampmcalendarweek',
       
   369                              year=day.year, week=day.isocalendar()[1])
       
   370         weeklink = '<a href="%s">%s</a>' % (xml_escape(url), day.isocalendar()[1])
       
   371         current_row.append(WEEKNUM_CELL % weeklink)
       
   372         rows.append(current_row)
       
   373         # build two rows for each week: am & pm
       
   374         formatted_rows = []
       
   375         for row in rows:
       
   376             week_title = row.pop()
       
   377             day_row = [day for day, am, pm in row]
       
   378             am_row = [am for day, am, pm in row]
       
   379             pm_row = [pm for day, am, pm in row]
       
   380             formatted_rows.append('<tr>%s%s</tr>'% (week_title, '\n'.join(day_row)))
       
   381             formatted_rows.append('<tr class="amRow"><td>&#160;</td>%s</tr>'% '\n'.join(am_row))
       
   382             formatted_rows.append('<tr class="pmRow"><td>&#160;</td>%s</tr>'% '\n'.join(pm_row))
       
   383         # tigh everything together
       
   384         url = self._cw.build_url(rql=rql, vid='ampmcalendarmonth',
       
   385                              year=first_day.year, month=first_day.month)
       
   386         monthlink = '<a href="%s">%s</a>' % (xml_escape(url), umonth)
       
   387         return CALENDAR(self._cw) % (monthlink, '\n'.join(formatted_rows))
       
   388 
       
   389 
       
   390 
       
   391 class AMPMSemesterCalendarView(SemesterCalendarView):
       
   392     """this view renders a 3x1 calendars' table"""
       
   393     __regid__ = 'ampmcalendarsemester'
       
   394     title = _('am/pm calendar (semester)')
       
   395 
       
   396     def build_calendars(self, schedule, begin, end):
       
   397         self.w(u'<tr>')
       
   398         rql = self.cw_rset.printable_rql()
       
   399         for cur_month in date_range(begin, end, incmonth=1):
       
   400             umonth = u'%s&#160;%s' % (self._cw.format_date(cur_month, '%B'), cur_month.year)
       
   401             url = self._cw.build_url(rql=rql, vid=self.__regid__,
       
   402                                  year=cur_month.year, month=cur_month.month)
       
   403             self.w(u'<th colspan="3"><a href="%s">%s</a></th>' % (xml_escape(url),
       
   404                                                                   umonth))
       
   405         self.w(u'</tr>')
       
   406         _ = self._cw._
       
   407         for day_num in xrange(31):
       
   408             self.w(u'<tr>')
       
   409             for cur_month in date_range(begin, end, incmonth=1):
       
   410                 if day_num >= days_in_month(cur_month):
       
   411                     self.w(u'%s%s%s' % (NO_CELL, NO_CELL, NO_CELL))
       
   412                 else:
       
   413                     day = date(cur_month.year, cur_month.month, day_num+1)
       
   414                     events = schedule.get(day)
       
   415                     self.w(u'<td>%s&#160;%s</td>\n' % (_(WEEKDAYS[day.weekday()])[0].upper(),
       
   416                                                        day_num+1))
       
   417                     self.format_day_events(day, events)
       
   418             self.w(u'</tr>')
       
   419 
       
   420     def format_day_events(self, day, events):
       
   421         if events:
       
   422             self.w(u'\n'.join(self._build_ampm_cells(events)))
       
   423         else:
       
   424             self.w(u'%s %s'% (AMPM_EMPTY % ("amCell", "am"),
       
   425                               AMPM_EMPTY % ("pmCell", "pm")))
       
   426 
       
   427 
       
   428 class AMPMMonthCalendarView(MonthCalendarView):
       
   429     """this view renders a 3x1 calendars' table"""
       
   430     __regid__ = 'ampmcalendarmonth'
       
   431     title = _('am/pm calendar (month)')
       
   432 
       
   433     def build_calendar(self, schedule, first_day):
       
   434         """method responsible for building *one* HTML calendar"""
       
   435         umonth = self._cw.format_date(first_day, '%B %Y') # localized month name
       
   436         rows = [] # each row is: (am,pm), (am,pm) ... week_title
       
   437         current_row = [(NO_CELL, NO_CELL, NO_CELL)] * first_day.weekday()
       
   438         rql = self.cw_rset.printable_rql()
       
   439         for daynum in xrange(0, days_in_month(first_day)):
       
   440             # build cells day
       
   441             day = first_day + timedelta(daynum)
       
   442             events = schedule.get(day)
       
   443             if events:
       
   444                 current_row.append((AMPM_DAY % (daynum+1),) + self._build_ampm_cells(events))
       
   445             else:
       
   446                 current_row.append((AMPM_DAY % (daynum+1),
       
   447                                     AMPM_EMPTY % ("amCell", "am"),
       
   448                                     AMPM_EMPTY % ("pmCell", "pm")))
       
   449             # store & reset current row on Sundays
       
   450             if day.weekday() == 6:
       
   451                 url = self._cw.build_url(rql=rql, vid='ampmcalendarweek',
       
   452                                          year=day.year, week=day.isocalendar()[1])
       
   453                 weeklink = '<a href="%s">%s</a>' % (xml_escape(url),
       
   454                                                     day.isocalendar()[1])
       
   455                 current_row.append(WEEKNUM_CELL % weeklink)
       
   456                 rows.append(current_row)
       
   457                 current_row = []
       
   458         current_row.extend([(NO_CELL, NO_CELL, NO_CELL)] * (6-day.weekday()))
       
   459         url = self._cw.build_url(rql=rql, vid='ampmcalendarweek',
       
   460                                  year=day.year, week=day.isocalendar()[1])
       
   461         weeklink = '<a href="%s">%s</a>' % (xml_escape(url),
       
   462                                             day.isocalendar()[1])
       
   463         current_row.append(WEEKNUM_CELL % weeklink)
       
   464         rows.append(current_row)
       
   465         # build two rows for each week: am & pm
       
   466         formatted_rows = []
       
   467         for row in rows:
       
   468             week_title = row.pop()
       
   469             day_row = [day for day, am, pm in row]
       
   470             am_row = [am for day, am, pm in row]
       
   471             pm_row = [pm for day, am, pm in row]
       
   472             formatted_rows.append('<tr>%s%s</tr>'% (week_title, '\n'.join(day_row)))
       
   473             formatted_rows.append('<tr class="amRow"><td>&#160;</td>%s</tr>'% '\n'.join(am_row))
       
   474             formatted_rows.append('<tr class="pmRow"><td>&#160;</td>%s</tr>'% '\n'.join(pm_row))
       
   475         # tigh everything together
       
   476         url = self._cw.build_url(rql=rql, vid='ampmcalendarmonth',
       
   477                                  year=first_day.year, month=first_day.month)
       
   478         monthlink = '<a href="%s">%s</a>' % (xml_escape(url),
       
   479                                              umonth)
       
   480         return CALENDAR(self._cw) % (monthlink, '\n'.join(formatted_rows))
       
   481 
       
   482 
       
   483 
       
   484 class AMPMWeekCalendarView(WeekCalendarView):
       
   485     """this view renders a 3x1 calendars' table"""
       
   486     __regid__ = 'ampmcalendarweek'
       
   487     title = _('am/pm calendar (week)')
       
   488 
       
   489     def build_calendar(self, schedule, weeks):
       
   490         rql = self.cw_rset.printable_rql()
       
   491         w = self.w
       
   492         _ = self._cw._
       
   493         for monday, sunday in weeks:
       
   494             umonth = self._cw.format_date(monday, '%B %Y')
       
   495             url = self._cw.build_url(rql=rql, vid='ampmcalendarmonth',
       
   496                                      year=monday.year, month=monday.month)
       
   497             monthlink = '<a href="%s">%s</a>' % (xml_escape(url), umonth)
       
   498             w(u'<tr>%s</tr>' % (
       
   499                 WEEK_TITLE % (_('week'), monday.isocalendar()[1], monthlink)))
       
   500             w(u'<tr><th>%s</th><th>&#160;</th></tr>'% _(u'Date'))
       
   501             for day in date_range(monday, sunday+ONEDAY):
       
   502                 events = schedule.get(day)
       
   503                 style = day.weekday() % 2 and "even" or "odd"
       
   504                 w(u'<tr class="%s">' % style)
       
   505                 if events:
       
   506                     hours = events.keys()
       
   507                     hours.sort()
       
   508                     w(AMPM_DAYWEEK % (
       
   509                         len(hours), _(WEEKDAYS[day.weekday()]),
       
   510                         self._cw.format_date(day)))
       
   511                     w(AMPM_WEEK_CELL % (
       
   512                         hours[0].hour, hours[0].minute,
       
   513                         '\n'.join(events[hours[0]])))
       
   514                     w(u'</tr>')
       
   515                     for hour in hours[1:]:
       
   516                         w(u'<tr class="%s">%s</tr>'% (
       
   517                             style, AMPM_WEEK_CELL % (hour.hour, hour.minute,
       
   518                                                      '\n'.join(events[hour]))))
       
   519                 else:
       
   520                     w(AMPM_DAYWEEK_EMPTY % (
       
   521                         _(WEEKDAYS[day.weekday()]),
       
   522                         self._cw.format_date(day)))
       
   523                     w(WEEK_EMPTY_CELL)
       
   524                     w(u'</tr>')
       
   525 
       
   526 
       
   527 SMALL_CALENDARS_PAGE = u"""<table class="smallCalendars">
       
   528 <tr><td class="calendar">%s</td><td class="calendar">%s</td><td class="calendar">%s</td></tr>
       
   529 <tr><td class="calendar">%s</td><td class="calendar">%s</td><td class="calendar">%s</td></tr>
       
   530 <tr><td class="calendar">%s</td><td class="calendar">%s</td><td class="calendar">%s</td></tr>
       
   531 </table>
       
   532 """
       
   533 
       
   534 BIG_CALENDARS_PAGE = u"""<table class="bigCalendars">
       
   535 <tr><td class="calendar">%s</td></tr>
       
   536 <tr><td class="calendar">%s</td></tr>
       
   537 <tr><td class="calendar">%s</td></tr>
       
   538 </table>
       
   539 """
       
   540 
       
   541 WEEKNUM_CELL = u'<td class="weeknum">%s</td>'
       
   542 
       
   543 def CALENDAR(req):
       
   544     _ = req._
       
   545     WEEKNUM_HEADER = u'<th class="weeknum">%s</th>' % _('week')
       
   546     CAL_HEADER = WEEKNUM_HEADER + u' \n'.join([u'<th class="weekday">%s</th>' % _(day)[0].upper()
       
   547                                                for day in WEEKDAYS])
       
   548     return u"""<table>
       
   549 <tr><th class="month" colspan="8">%%s</th></tr>
       
   550 <tr>
       
   551   %s
       
   552 </tr>
       
   553 %%s
       
   554 </table>
       
   555 """ % (CAL_HEADER,)
       
   556 
       
   557 
       
   558 DAY_TEMPLATE = """<tr><td class="weekday">%(daylabel)s</td><td>%(dmydate)s</td><td>%(dayschedule)s</td>
       
   559 """
       
   560 
       
   561 NO_CELL = u'<td class="noday"></td>'
       
   562 EMPTY_CELL = u'<td class="cellEmpty"><span class="cellTitle">%s</span></td>'
       
   563 CELL = u'<td class="cell"><span class="cellTitle">%s</span><div class="cellContent">%s</div></td>'
       
   564 
       
   565 AMPM_DAY = u'<td class="cellDay">%d</td>'
       
   566 AMPM_EMPTY = u'<td class="%sEmpty"><span class="cellTitle">%s</span></td>'
       
   567 AMPM_CONTENT = u'<td class="%s"><span class="cellTitle">%s</span><div class="cellContent">%s</div></td>'
       
   568 
       
   569 WEEK_TITLE = u'<th class="weekTitle" colspan="2">%s %s (%s)</th>'
       
   570 WEEK_EMPTY_CELL = u'<td class="weekEmptyCell">&#160;</td>'
       
   571 WEEK_CELL = u'<td class="weekCell"><div class="cellContent">%s</div></td>'
       
   572 
       
   573 AMPM_DAYWEEK_EMPTY = u'<td>%s&#160;%s</td>'
       
   574 AMPM_DAYWEEK = u'<td rowspan="%d">%s&#160;%s</td>'
       
   575 AMPM_WEEK_CELL = u'<td class="ampmWeekCell"><div class="cellContent">%02d:%02d - %s</div></td>'