web/views/old_calendar.py
changeset 1808 aa09e20dd8c0
parent 431 18b4dd650ef8
parent 1707 71b55db64269
child 1977 606923dff11b
equal deleted inserted replaced
1693:49075f57cf2c 1808:aa09e20dd8c0
     1 """html calendar views
     1 """html calendar views
     2 
     2 
     3 :organization: Logilab
     3 :organization: Logilab
     4 :copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     6 """
     6 """
     7 
     7 
     8 from mx.DateTime import DateTime, RelativeDateTime, Date, Time, today, Sunday
     8 from datetime import date, time, timedelta
     9 
     9 
    10 from logilab.mtconverter import html_escape
    10 from logilab.mtconverter import html_escape
    11 
    11 
    12 from cubicweb.interfaces import ICalendarViews
    12 from cubicweb.interfaces import ICalendarViews
    13 from cubicweb.common.utils import date_range
    13 from cubicweb.utils import ONEDAY, ONEWEEK, date_range, first_day, last_day, previous_month, next_month, days_in_month
    14 from cubicweb.common.selectors import implement_interface
    14 from cubicweb.selectors import implements
    15 from cubicweb.common.registerers import priority_registerer
    15 from cubicweb.view import EntityView
    16 from cubicweb.common.view import EntityView
    16 
    17 
    17 # used by i18n tools
    18 # Define some useful constants
       
    19 ONE_MONTH = RelativeDateTime(months=1)
       
    20 TODAY = today()
       
    21 THIS_MONTH = TODAY.month
       
    22 THIS_YEAR = TODAY.year
       
    23 # mx.DateTime and ustrftime could be used to build WEEKDAYS
       
    24 WEEKDAYS = [_("monday"), _("tuesday"), _("wednesday"), _("thursday"),
    18 WEEKDAYS = [_("monday"), _("tuesday"), _("wednesday"), _("thursday"),
    25             _("friday"), _("saturday"), _("sunday")]
    19             _("friday"), _("saturday"), _("sunday")]
    26 
       
    27 # used by i18n tools
       
    28 MONTHNAMES = [ _('january'), _('february'), _('march'), _('april'), _('may'),
    20 MONTHNAMES = [ _('january'), _('february'), _('march'), _('april'), _('may'),
    29                _('june'), _('july'), _('august'), _('september'), _('october'),
    21                _('june'), _('july'), _('august'), _('september'), _('october'),
    30                _('november'), _('december')
    22                _('november'), _('december')
    31                ]
    23                ]
    32 
    24 
    33 class _CalendarView(EntityView):
    25 class _CalendarView(EntityView):
    34     """base calendar view containing helpful methods to build calendar views"""
    26     """base calendar view containing helpful methods to build calendar views"""
    35     __registerer__ = priority_registerer
    27     __select__ = implements(ICalendarViews,)
    36     __selectors__ = (implement_interface,)
       
    37     accepts_interfaces = (ICalendarViews,)
       
    38     need_navigation = False
    28     need_navigation = False
    39 
    29 
    40     # Navigation building methods / views ####################################
    30     # Navigation building methods / views ####################################
    41 
    31 
    42     PREV = u'<a href="%s">&lt;&lt;</a>&nbsp;&nbsp;<a href="%s">&lt;</a>'
    32     PREV = u'<a href="%s">&lt;&lt;</a>&nbsp;&nbsp;<a href="%s">&lt;</a>'
    43     NEXT = u'<a href="%s">&gt;</a>&nbsp;&nbsp;<a href="%s">&gt;&gt;</a>'
    33     NEXT = u'<a href="%s">&gt;</a>&nbsp;&nbsp;<a href="%s">&gt;&gt;</a>'
    44     NAV_HEADER = u"""<table class="calendarPageHeader">
    34     NAV_HEADER = u"""<table class="calendarPageHeader">
    45 <tr><td class="prev">%s</td><td class="next">%s</td></tr>
    35 <tr><td class="prev">%s</td><td class="next">%s</td></tr>
    46 </table>
    36 </table>
    47 """ % (PREV, NEXT)
    37 """ % (PREV, NEXT)
    48     
    38 
    49     def nav_header(self, date, smallshift=3, bigshift=9):
    39     def nav_header(self, date, smallshift=3, bigshift=9):
    50         """prints shortcut links to go to previous/next steps (month|week)"""
    40         """prints shortcut links to go to previous/next steps (month|week)"""
    51         prev1 = date - RelativeDateTime(months=smallshift)
    41         prev1 = next1 = prev2 = nex2 = date
    52         prev2 = date - RelativeDateTime(months=bigshift)
    42         prev1 = previous_month(date, smallshift)
    53         next1 = date + RelativeDateTime(months=smallshift)
    43         next1 = next_month(date, smallshift)
    54         next2 = date + RelativeDateTime(months=bigshift)
    44         prev2 = previous_month(date, bigshift)
    55         rql, vid = self.rset.printable_rql(), self.id
    45         next2 = next_month(date, bigshift)
       
    46         rql = self.rset.printable_rql()
    56         return self.NAV_HEADER % (
    47         return self.NAV_HEADER % (
    57             html_escape(self.build_url(rql=rql, vid=vid, year=prev2.year, month=prev2.month)),
    48             html_escape(self.build_url(rql=rql, vid=self.id, year=prev2.year,
    58             html_escape(self.build_url(rql=rql, vid=vid, year=prev1.year, month=prev1.month)),
    49                                        month=prev2.month)),
    59             html_escape(self.build_url(rql=rql, vid=vid, year=next1.year, month=next1.month)),
    50             html_escape(self.build_url(rql=rql, vid=self.id, year=prev1.year,
    60             html_escape(self.build_url(rql=rql, vid=vid, year=next2.year, month=next2.month)))
    51                                        month=prev1.month)),
    61         
    52             html_escape(self.build_url(rql=rql, vid=self.id, year=next1.year,
    62     
    53                                        month=next1.month)),
       
    54             html_escape(self.build_url(rql=rql, vid=self.id, year=next2.year,
       
    55                                        month=next2.month)))
       
    56 
       
    57 
    63     # Calendar building methods ##############################################
    58     # Calendar building methods ##############################################
    64     
    59 
    65     def build_calendars(self, schedule, begin, end):
    60     def build_calendars(self, schedule, begin, end):
    66         """build several HTML calendars at once, one for each month
    61         """build several HTML calendars at once, one for each month
    67         between begin and end
    62         between begin and end
    68         """
    63         """
    69         return [self.build_calendar(schedule, date)
    64         return [self.build_calendar(schedule, date)
    70                 for date in date_range(begin, end, incr=ONE_MONTH)]
    65                 for date in date_range(begin, end, incmonth=1)]
    71     
    66 
    72     def build_calendar(self, schedule, first_day):
    67     def build_calendar(self, schedule, first_day):
    73         """method responsible for building *one* HTML calendar"""
    68         """method responsible for building *one* HTML calendar"""
    74         # FIXME  iterates between [first_day-first_day.day_of_week ;
    69         # FIXME  iterates between [first_day-first_day.day_of_week ;
    75         #                          last_day+6-last_day.day_of_week]
    70         #                          last_day+6-last_day.day_of_week]
    76         umonth = self.format_date(first_day, '%B %Y') # localized month name
    71         umonth = self.format_date(first_day, '%B %Y') # localized month name
    77         rows = []
    72         rows = []
    78         current_row = [NO_CELL] * first_day.day_of_week
    73         current_row = [NO_CELL] * first_day.weekday()
    79         for daynum in xrange(0, first_day.days_in_month):
    74         for daynum in xrange(0, days_in_month(first_day)):
    80             # build cell day
    75             # build cell day
    81             day = first_day + daynum
    76             day = first_day + timedelta(daynum)
    82             events = schedule.get(day)
    77             events = schedule.get(day)
    83             if events:
    78             if events:
    84                 events = [u'\n'.join(event) for event in events.values()]
    79                 events = [u'\n'.join(event) for event in events.values()]
    85                 current_row.append(CELL % (daynum+1, '\n'.join(events)))
    80                 current_row.append(CELL % (daynum+1, '\n'.join(events)))
    86             else:
    81             else:
    87                 current_row.append(EMPTY_CELL % (daynum+1))
    82                 current_row.append(EMPTY_CELL % (daynum+1))
    88             # store & reset current row on Sundays
    83             # store & reset current row on Sundays
    89             if day.day_of_week == Sunday:
    84             if day.weekday() == 6:
    90                 rows.append(u'<tr>%s%s</tr>' % (WEEKNUM_CELL % day.iso_week[1], ''.join(current_row)))
    85                 rows.append(u'<tr>%s%s</tr>' % (WEEKNUM_CELL % day.isocalendar()[1], ''.join(current_row)))
    91                 current_row = []
    86                 current_row = []
    92         current_row.extend([NO_CELL] * (Sunday-day.day_of_week))
    87         current_row.extend([NO_CELL] * (6-day.weekday()))
    93         rql = self.rset.printable_rql()
    88         rql = self.rset.printable_rql()
    94         if day.day_of_week != Sunday:
    89         if day.weekday() != 6:
    95             rows.append(u'<tr>%s%s</tr>' % (WEEKNUM_CELL % day.iso_week[1], ''.join(current_row)))
    90             rows.append(u'<tr>%s%s</tr>' % (WEEKNUM_CELL % day.isocalendar()[1], ''.join(current_row)))
    96         url = self.build_url(rql=rql, vid='calendarmonth',
    91         url = self.build_url(rql=rql, vid='calendarmonth',
    97                              year=first_day.year, month=first_day.month)
    92                              year=first_day.year, month=first_day.month)
    98         monthlink = u'<a href="%s">%s</a>' % (html_escape(url), umonth)
    93         monthlink = u'<a href="%s">%s</a>' % (html_escape(url), umonth)
    99         return CALENDAR(self.req) % (monthlink, '\n'.join(rows))
    94         return CALENDAR(self.req) % (monthlink, '\n'.join(rows))
   100 
    95 
   107         :param itemvid: which view to call to render elements in cells
   102         :param itemvid: which view to call to render elements in cells
   108 
   103 
   109         returns { day1 : { hour : [views] },
   104         returns { day1 : { hour : [views] },
   110                   day2 : { hour : [views] } ... }
   105                   day2 : { hour : [views] } ... }
   111         """
   106         """
   112         # put this here since all sub views are calling this method        
   107         # put this here since all sub views are calling this method
   113         self.req.add_css('cubicweb.calendar.css') 
   108         self.req.add_css('cubicweb.calendar.css')
   114         schedule = {}
   109         schedule = {}
   115         for row in xrange(len(self.rset.rows)):
   110         for row in xrange(len(self.rset.rows)):
   116             entity = self.entity(row)
   111             entity = self.entity(row)
   117             infos = u'<div class="event">'
   112             infos = u'<div class="event">'
   118             infos += self.view(itemvid, self.rset, row=row)
   113             infos += self.view(itemvid, self.rset, row=row)
   119             infos += u'</div>'
   114             infos += u'</div>'
   120             for date in entity.matching_dates(begin, end):
   115             for date_ in entity.matching_dates(begin, end):
   121                 day = Date(date.year, date.month, date.day)
   116                 day = date(date_.year, date_.month, date_.day)
   122                 time = Time(date.hour, date.minute, date.second) 
   117                 try:
       
   118                     dt = time(date_.hour, date_.minute, date_.second)
       
   119                 except AttributeError:
       
   120                     # date instance
       
   121                     dt = time(0, 0, 0)
   123                 schedule.setdefault(day, {})
   122                 schedule.setdefault(day, {})
   124                 schedule[day].setdefault(time, []).append(infos)
   123                 schedule[day].setdefault(dt, []).append(infos)
   125         return schedule
   124         return schedule
   126         
   125 
   127 
   126 
   128     @staticmethod
   127     @staticmethod
   129     def get_date_range(day=TODAY, shift=4):
   128     def get_date_range(day, shift=4):
   130         """returns a couple (begin, end)
   129         """returns a couple (begin, end)
   131 
   130 
   132         <begin> is the first day of current_month - shift
   131         <begin> is the first day of current_month - shift
   133         <end> is the last day of current_month + (shift+1)
   132         <end> is the last day of current_month + (shift+1)
   134         """
   133         """
   135         first_day_in_month = DateTime(day.year, day.month, 1)
   134         begin = first_day(previous_month(day, shift))
   136         begin = first_day_in_month - RelativeDateTime(months=shift)
   135         end = last_day(next_month(day, shift))
   137         end = (first_day_in_month + RelativeDateTime(months=shift+1)) - 1
       
   138         return begin, end
   136         return begin, end
   139 
       
   140 
   137 
   141     def _build_ampm_cells(self, daynum, events):
   138     def _build_ampm_cells(self, daynum, events):
   142         """create a view without any hourly details.
   139         """create a view without any hourly details.
   143 
   140 
   144         :param daynum: day of the built cell
   141         :param daynum: day of the built cell
   165 
   162 
   166 class YearCalendarView(_CalendarView):
   163 class YearCalendarView(_CalendarView):
   167     id = 'calendaryear'
   164     id = 'calendaryear'
   168     title = _('calendar (year)')
   165     title = _('calendar (year)')
   169 
   166 
   170     def call(self, year=THIS_YEAR, month=THIS_MONTH):
   167     def call(self, year=None, month=None):
   171         """this view renders a 3x3 calendars' table"""
   168         """this view renders a 3x3 calendars' table"""
   172         year = int(self.req.form.get('year', year))
   169         year = year or int(self.req.form.get('year', date.today().year))
   173         month = int(self.req.form.get('month', month))
   170         month = month or int(self.req.form.get('month', date.today().month))
   174         center_date = DateTime(year, month)
   171         center_date = date(year, month, 1)
   175         begin, end = self.get_date_range(day=center_date)
   172         begin, end = self.get_date_range(day=center_date)
   176         schedule = self._mk_schedule(begin, end)
   173         schedule = self._mk_schedule(begin, end)
   177         self.w(self.nav_header(center_date))
   174         self.w(self.nav_header(center_date))
   178         calendars = tuple(self.build_calendars(schedule, begin, end))
   175         calendars = tuple(self.build_calendars(schedule, begin, end))
   179         self.w(SMALL_CALENDARS_PAGE % calendars)
   176         self.w(SMALL_CALENDARS_PAGE % calendars)
   184     one column per month
   181     one column per month
   185     """
   182     """
   186     id = 'calendarsemester'
   183     id = 'calendarsemester'
   187     title = _('calendar (semester)')
   184     title = _('calendar (semester)')
   188 
   185 
   189     def call(self, year=THIS_YEAR, month=THIS_MONTH):
   186     def call(self, year=None, month=None):
   190         year = int(self.req.form.get('year', year))
   187         year = year or int(self.req.form.get('year', date.today().year))
   191         month = int(self.req.form.get('month', month))
   188         month = month or int(self.req.form.get('month', date.today().month))
   192         begin = DateTime(year, month) - RelativeDateTime(months=2)
   189         begin = previous_month(date(year, month, 1), 2)
   193         end = DateTime(year, month) + RelativeDateTime(months=3)
   190         end = next_month(date(year, month, 1), 3)
   194         schedule = self._mk_schedule(begin, end)
   191         schedule = self._mk_schedule(begin, end)
   195         self.w(self.nav_header(DateTime(year, month), 1, 6))
   192         self.w(self.nav_header(date(year, month, 1), 1, 6))
   196         self.w(u'<table class="semesterCalendar">')
   193         self.w(u'<table class="semesterCalendar">')
   197         self.build_calendars(schedule, begin, end)
   194         self.build_calendars(schedule, begin, end)
   198         self.w(u'</table>')
   195         self.w(u'</table>')
   199         self.w(self.nav_header(DateTime(year, month), 1, 6))
   196         self.w(self.nav_header(date(year, month, 1), 1, 6))
   200 
   197 
   201     def build_calendars(self, schedule, begin, end):
   198     def build_calendars(self, schedule, begin, end):
   202         self.w(u'<tr>')
   199         self.w(u'<tr>')
   203         rql = self.rset.printable_rql()
   200         rql = self.rset.printable_rql()
   204         for cur_month in date_range(begin, end, incr=ONE_MONTH):
   201         for cur_month in date_range(begin, end, incmonth=1):
   205             umonth = u'%s&nbsp;%s' % (self.format_date(cur_month, '%B'), cur_month.year)
   202             umonth = u'%s&nbsp;%s' % (self.format_date(cur_month, '%B'), cur_month.year)
   206             url = self.build_url(rql=rql, vid=self.id,
   203             url = self.build_url(rql=rql, vid=self.id,
   207                                  year=cur_month.year, month=cur_month.month)
   204                                  year=cur_month.year, month=cur_month.month)
   208             self.w(u'<th colspan="2"><a href="%s">%s</a></th>' % (html_escape(url),
   205             self.w(u'<th colspan="2"><a href="%s">%s</a></th>' % (html_escape(url),
   209                                                                   umonth))
   206                                                                   umonth))
   210         self.w(u'</tr>')
   207         self.w(u'</tr>')
   211         _ = self.req._
   208         _ = self.req._
   212         for day_num in xrange(31):
   209         for day_num in xrange(31):
   213             self.w(u'<tr>')
   210             self.w(u'<tr>')
   214             for cur_month in date_range(begin, end, incr=ONE_MONTH):
   211             for cur_month in date_range(begin, end, incmonth=1):
   215                 if day_num >= cur_month.days_in_month:
   212                 if day_num >= days_in_month(cur_month):
   216                     self.w(u'%s%s' % (NO_CELL, NO_CELL))
   213                     self.w(u'%s%s' % (NO_CELL, NO_CELL))
   217                 else:
   214                 else:
   218                     day = DateTime(cur_month.year, cur_month.month, day_num+1)
   215                     day = date(cur_month.year, cur_month.month, day_num+1)
   219                     events = schedule.get(day)
   216                     events = schedule.get(day)
   220                     self.w(u'<td>%s&nbsp;%s</td>\n' % (_(WEEKDAYS[day.day_of_week])[0].upper(), day_num+1))
   217                     self.w(u'<td>%s&nbsp;%s</td>\n' % (_(WEEKDAYS[day.weekday()])[0].upper(), day_num+1))
   221                     self.format_day_events(day, events)
   218                     self.format_day_events(day, events)
   222             self.w(u'</tr>')
   219             self.w(u'</tr>')
   223             
   220 
   224     def format_day_events(self, day, events):
   221     def format_day_events(self, day, events):
   225         if events:
   222         if events:
   226             events = ['\n'.join(event) for event in events.values()]
   223             events = ['\n'.join(event) for event in events.values()]
   227             self.w(WEEK_CELL % '\n'.join(events))
   224             self.w(WEEK_CELL % '\n'.join(events))
   228         else:
   225         else:
   229             self.w(WEEK_EMPTY_CELL)
   226             self.w(WEEK_EMPTY_CELL)
   230         
   227 
   231 
   228 
   232 class MonthCalendarView(_CalendarView):
   229 class MonthCalendarView(_CalendarView):
   233     """this view renders a 3x1 calendars' table"""
   230     """this view renders a 3x1 calendars' table"""
   234     id = 'calendarmonth'
   231     id = 'calendarmonth'
   235     title = _('calendar (month)')
   232     title = _('calendar (month)')
   236     
   233 
   237     def call(self, year=THIS_YEAR, month=THIS_MONTH):
   234     def call(self, year=None, month=None):
   238         year = int(self.req.form.get('year', year))
   235         year = year or int(self.req.form.get('year', date.today().year))
   239         month = int(self.req.form.get('month', month))
   236         month = month or int(self.req.form.get('month', date.today().month))
   240         center_date = DateTime(year, month)
   237         center_date = date(year, month, 1)
   241         begin, end = self.get_date_range(day=center_date, shift=1)
   238         begin, end = self.get_date_range(day=center_date, shift=1)
   242         schedule = self._mk_schedule(begin, end)
   239         schedule = self._mk_schedule(begin, end)
   243         calendars = self.build_calendars(schedule, begin, end)
   240         calendars = self.build_calendars(schedule, begin, end)
   244         self.w(self.nav_header(center_date, 1, 3))
   241         self.w(self.nav_header(center_date, 1, 3))
   245         self.w(BIG_CALENDARS_PAGE % tuple(calendars))
   242         self.w(BIG_CALENDARS_PAGE % tuple(calendars))
   246         self.w(self.nav_header(center_date, 1, 3))
   243         self.w(self.nav_header(center_date, 1, 3))
   247 
   244 
   248         
   245 
   249 class WeekCalendarView(_CalendarView):
   246 class WeekCalendarView(_CalendarView):
   250     """this view renders a calendar for week events"""
   247     """this view renders a calendar for week events"""
   251     id = 'calendarweek'
   248     id = 'calendarweek'
   252     title = _('calendar (week)')
   249     title = _('calendar (week)')
   253     
   250 
   254     def call(self, year=THIS_YEAR, week=TODAY.iso_week[1]):
   251     def call(self, year=None, week=None):
   255         year = int(self.req.form.get('year', year))
   252         year = year or int(self.req.form.get('year', date.today().year))
   256         week = int(self.req.form.get('week', week))
   253         week = week or int(self.req.form.get('week', date.today().isocalendar()[1]))
   257         day0 = DateTime(year)
   254         day0 = date(year, 1, 1)
   258         first_day_of_week = (day0-day0.day_of_week) + 7*week
   255         first_day_of_week = day0 - day0.weekday()*ONEDAY + ONEWEEK
   259         begin, end = first_day_of_week-7, first_day_of_week+14
   256         begin, end = first_day_of_week- ONEWEEK, first_day_of_week + 2*ONEWEEK
   260         schedule = self._mk_schedule(begin, end, itemvid='calendarlargeitem')
   257         schedule = self._mk_schedule(begin, end, itemvid='calendarlargeitem')
   261         self.w(self.nav_header(first_day_of_week))
   258         self.w(self.nav_header(first_day_of_week))
   262         self.w(u'<table class="weekCalendar">')
   259         self.w(u'<table class="weekCalendar">')
   263         _weeks = [(first_day_of_week-7, first_day_of_week-1),
   260         _weeks = [(first_day_of_week-ONEWEEK, first_day_of_week-ONEDAY),
   264                   (first_day_of_week, first_day_of_week+6),
   261                   (first_day_of_week, first_day_of_week+6*ONEDAY),
   265                   (first_day_of_week+7, first_day_of_week+13)]
   262                   (first_day_of_week+ONEWEEK, first_day_of_week+13*ONEDAY)]
   266         self.build_calendar(schedule, _weeks)
   263         self.build_calendar(schedule, _weeks)
   267         self.w(u'</table>')
   264         self.w(u'</table>')
   268         self.w(self.nav_header(first_day_of_week))
   265         self.w(self.nav_header(first_day_of_week))
   269  
   266 
   270     def build_calendar(self, schedule, weeks):
   267     def build_calendar(self, schedule, weeks):
   271         rql = self.rset.printable_rql()
   268         rql = self.rset.printable_rql()
   272         _ = self.req._
   269         _ = self.req._
   273         for monday, sunday in weeks:            
   270         for monday, sunday in weeks:
   274             umonth = self.format_date(monday, '%B %Y')
   271             umonth = self.format_date(monday, '%B %Y')
   275             url = self.build_url(rql=rql, vid='calendarmonth',
   272             url = self.build_url(rql=rql, vid='calendarmonth',
   276                                  year=monday.year, month=monday.month)
   273                                  year=monday.year, month=monday.month)
   277             monthlink = '<a href="%s">%s</a>' % (html_escape(url), umonth)
   274             monthlink = '<a href="%s">%s</a>' % (html_escape(url), umonth)
   278             self.w(u'<tr><th colspan="3">%s %s (%s)</th></tr>' \
   275             self.w(u'<tr><th colspan="3">%s %s (%s)</th></tr>' \
   279                   % (_('week'), monday.iso_week[1], monthlink))
   276                   % (_('week'), monday.isocalendar()[1], monthlink))
   280             for day in date_range(monday, sunday):
   277             for day in date_range(monday, sunday):
   281                 self.w(u'<tr>')
   278                 self.w(u'<tr>')
   282                 self.w(u'<td>%s</td>' % _(WEEKDAYS[day.day_of_week]))
   279                 self.w(u'<td>%s</td>' % _(WEEKDAYS[day.weekday()]))
   283                 self.w(u'<td>%s</td>' % (day.strftime('%Y-%m-%d')))
   280                 self.w(u'<td>%s</td>' % (day.strftime('%Y-%m-%d')))
   284                 events = schedule.get(day)
   281                 events = schedule.get(day)
   285                 if events:
   282                 if events:
   286                     events = ['\n'.join(event) for event in events.values()]
   283                     events = ['\n'.join(event) for event in events.values()]
   287                     self.w(WEEK_CELL % '\n'.join(events))
   284                     self.w(WEEK_CELL % '\n'.join(events))
   288                 else:
   285                 else:
   289                     self.w(WEEK_EMPTY_CELL)
   286                     self.w(WEEK_EMPTY_CELL)
   290                 self.w(u'</tr>')
   287                 self.w(u'</tr>')
   291         
   288 
   292     def nav_header(self, date, smallshift=1, bigshift=3):
   289     def nav_header(self, date, smallshift=1, bigshift=3):
   293         """prints shortcut links to go to previous/next steps (month|week)"""
   290         """prints shortcut links to go to previous/next steps (month|week)"""
   294         prev1 = date - RelativeDateTime(weeks=smallshift)
   291         prev1 = date - ONEWEEK * smallshift
   295         prev2 = date - RelativeDateTime(weeks=bigshift)
   292         prev2 = date - ONEWEEK * bigshift
   296         next1 = date + RelativeDateTime(weeks=smallshift)
   293         next1 = date + ONEWEEK * smallshift
   297         next2 = date + RelativeDateTime(weeks=bigshift)
   294         next2 = date + ONEWEEK * bigshift
   298         rql, vid = self.rset.printable_rql(), self.id
   295         rql = self.rset.printable_rql()
   299         return self.NAV_HEADER % (
   296         return self.NAV_HEADER % (
   300             html_escape(self.build_url(rql=rql, vid=vid, year=prev2.year, week=prev2.iso_week[1])),
   297             html_escape(self.build_url(rql=rql, vid=self.id, year=prev2.year, week=prev2.isocalendar()[1])),
   301             html_escape(self.build_url(rql=rql, vid=vid, year=prev1.year, week=prev1.iso_week[1])),
   298             html_escape(self.build_url(rql=rql, vid=self.id, year=prev1.year, week=prev1.isocalendar()[1])),
   302             html_escape(self.build_url(rql=rql, vid=vid, year=next1.year, week=next1.iso_week[1])),
   299             html_escape(self.build_url(rql=rql, vid=self.id, year=next1.year, week=next1.isocalendar()[1])),
   303             html_escape(self.build_url(rql=rql, vid=vid, year=next2.year, week=next2.iso_week[1])))
   300             html_escape(self.build_url(rql=rql, vid=self.id, year=next2.year, week=next2.isocalendar()[1])))
   304 
   301 
   305 
   302 
   306         
   303 
   307 class AMPMYearCalendarView(YearCalendarView):
   304 class AMPMYearCalendarView(YearCalendarView):
   308     id = 'ampmcalendaryear'
   305     id = 'ampmcalendaryear'
   309     title = _('am/pm calendar (year)')
   306     title = _('am/pm calendar (year)')
   310     
   307 
   311     def build_calendar(self, schedule, first_day):
   308     def build_calendar(self, schedule, first_day):
   312         """method responsible for building *one* HTML calendar"""
   309         """method responsible for building *one* HTML calendar"""
   313         umonth = self.format_date(first_day, '%B %Y') # localized month name
   310         umonth = self.format_date(first_day, '%B %Y') # localized month name
   314         rows = [] # each row is: (am,pm), (am,pm) ... week_title
   311         rows = [] # each row is: (am,pm), (am,pm) ... week_title
   315         current_row = [(NO_CELL, NO_CELL, NO_CELL)] * first_day.day_of_week
   312         current_row = [(NO_CELL, NO_CELL, NO_CELL)] * first_day.weekday()
   316         rql = self.rset.printable_rql()
   313         rql = self.rset.printable_rql()
   317         for daynum in xrange(0, first_day.days_in_month):
   314         for daynum in xrange(0, days_in_month(first_day)):
   318             # build cells day
   315             # build cells day
   319             day = first_day + daynum
   316             day = first_day + timedelta(daynum)
   320             events = schedule.get(day)
   317             events = schedule.get(day)
   321             if events:
   318             if events:
   322                 current_row.append((AMPM_DAY % (daynum+1),) + self._build_ampm_cells(daynum, events))
   319                 current_row.append((AMPM_DAY % (daynum+1),) + self._build_ampm_cells(daynum, events))
   323             else:
   320             else:
   324                 current_row.append((AMPM_DAY % (daynum+1),
   321                 current_row.append((AMPM_DAY % (daynum+1),
   325                                     AMPM_EMPTY % ("amCell", "am"),
   322                                     AMPM_EMPTY % ("amCell", "am"),
   326                                     AMPM_EMPTY % ("pmCell", "pm")))
   323                                     AMPM_EMPTY % ("pmCell", "pm")))
   327             # store & reset current row on Sundays
   324             # store & reset current row on Sundays
   328             if day.day_of_week == Sunday:
   325             if day.weekday() == 6:
   329                 url = self.build_url(rql=rql, vid='ampmcalendarweek',
   326                 url = self.build_url(rql=rql, vid='ampmcalendarweek',
   330                                      year=day.year, week=day.iso_week[1])
   327                                      year=day.year, week=day.isocalendar()[1])
   331                 weeklink = '<a href="%s">%s</a>' % (html_escape(url),
   328                 weeklink = '<a href="%s">%s</a>' % (html_escape(url),
   332                                                     day.iso_week[1])
   329                                                     day.isocalendar()[1])
   333                 current_row.append(WEEKNUM_CELL % weeklink)
   330                 current_row.append(WEEKNUM_CELL % weeklink)
   334                 rows.append(current_row)
   331                 rows.append(current_row)
   335                 current_row = []
   332                 current_row = []
   336         current_row.extend([(NO_CELL, NO_CELL, NO_CELL)] * (Sunday-day.day_of_week))
   333         current_row.extend([(NO_CELL, NO_CELL, NO_CELL)] * (6-day.weekday()))
   337         url = self.build_url(rql=rql, vid='ampmcalendarweek',
   334         url = self.build_url(rql=rql, vid='ampmcalendarweek',
   338                              year=day.year, week=day.iso_week[1])
   335                              year=day.year, week=day.isocalendar()[1])
   339         weeklink = '<a href="%s">%s</a>' % (html_escape(url), day.iso_week[1])
   336         weeklink = '<a href="%s">%s</a>' % (html_escape(url), day.isocalendar()[1])
   340         current_row.append(WEEKNUM_CELL % weeklink)
   337         current_row.append(WEEKNUM_CELL % weeklink)
   341         rows.append(current_row)
   338         rows.append(current_row)
   342         # build two rows for each week: am & pm
   339         # build two rows for each week: am & pm
   343         formatted_rows = []
   340         formatted_rows = []
   344         for row in rows:
   341         for row in rows:
   352         # tigh everything together
   349         # tigh everything together
   353         url = self.build_url(rql=rql, vid='ampmcalendarmonth',
   350         url = self.build_url(rql=rql, vid='ampmcalendarmonth',
   354                              year=first_day.year, month=first_day.month)
   351                              year=first_day.year, month=first_day.month)
   355         monthlink = '<a href="%s">%s</a>' % (html_escape(url), umonth)
   352         monthlink = '<a href="%s">%s</a>' % (html_escape(url), umonth)
   356         return CALENDAR(self.req) % (monthlink, '\n'.join(formatted_rows))
   353         return CALENDAR(self.req) % (monthlink, '\n'.join(formatted_rows))
   357         
   354 
   358 
   355 
   359 
   356 
   360 class AMPMSemesterCalendarView(SemesterCalendarView):
   357 class AMPMSemesterCalendarView(SemesterCalendarView):
   361     """this view renders a 3x1 calendars' table"""
   358     """this view renders a 3x1 calendars' table"""
   362     id = 'ampmcalendarsemester'
   359     id = 'ampmcalendarsemester'
   363     title = _('am/pm calendar (semester)')
   360     title = _('am/pm calendar (semester)')
   364 
   361 
   365     def build_calendars(self, schedule, begin, end):
   362     def build_calendars(self, schedule, begin, end):
   366         self.w(u'<tr>')
   363         self.w(u'<tr>')
   367         rql = self.rset.printable_rql()
   364         rql = self.rset.printable_rql()
   368         for cur_month in date_range(begin, end, incr=ONE_MONTH):
   365         for cur_month in date_range(begin, end, incmonth=1):
   369             umonth = u'%s&nbsp;%s' % (self.format_date(cur_month, '%B'), cur_month.year)
   366             umonth = u'%s&nbsp;%s' % (self.format_date(cur_month, '%B'), cur_month.year)
   370             url = self.build_url(rql=rql, vid=self.id,
   367             url = self.build_url(rql=rql, vid=self.id,
   371                                  year=cur_month.year, month=cur_month.month)
   368                                  year=cur_month.year, month=cur_month.month)
   372             self.w(u'<th colspan="3"><a href="%s">%s</a></th>' % (html_escape(url),
   369             self.w(u'<th colspan="3"><a href="%s">%s</a></th>' % (html_escape(url),
   373                                                                   umonth))
   370                                                                   umonth))
   374         self.w(u'</tr>')
   371         self.w(u'</tr>')
   375         _ = self.req._
   372         _ = self.req._
   376         for day_num in xrange(31):
   373         for day_num in xrange(31):
   377             self.w(u'<tr>')
   374             self.w(u'<tr>')
   378             for cur_month in date_range(begin, end, incr=ONE_MONTH):
   375             for cur_month in date_range(begin, end, incmonth=1):
   379                 if day_num >= cur_month.days_in_month:
   376                 if day_num >= days_in_month(cur_month):
   380                     self.w(u'%s%s%s' % (NO_CELL, NO_CELL, NO_CELL))
   377                     self.w(u'%s%s%s' % (NO_CELL, NO_CELL, NO_CELL))
   381                 else:
   378                 else:
   382                     day = DateTime(cur_month.year, cur_month.month, day_num+1)
   379                     day = date(cur_month.year, cur_month.month, day_num+1)
   383                     events = schedule.get(day)
   380                     events = schedule.get(day)
   384                     self.w(u'<td>%s&nbsp;%s</td>\n' % (_(WEEKDAYS[day.day_of_week])[0].upper(),
   381                     self.w(u'<td>%s&nbsp;%s</td>\n' % (_(WEEKDAYS[day.weekday()])[0].upper(),
   385                                                        day_num+1))
   382                                                        day_num+1))
   386                     self.format_day_events(day, events)
   383                     self.format_day_events(day, events)
   387             self.w(u'</tr>')
   384             self.w(u'</tr>')
   388     
   385 
   389     def format_day_events(self, day, events):
   386     def format_day_events(self, day, events):
   390         if events:
   387         if events:
   391             self.w(u'\n'.join(self._build_ampm_cells(day, events)))
   388             self.w(u'\n'.join(self._build_ampm_cells(day, events)))
   392         else:
   389         else:
   393             self.w(u'%s %s'% (AMPM_EMPTY % ("amCell", "am"), 
   390             self.w(u'%s %s'% (AMPM_EMPTY % ("amCell", "am"),
   394                               AMPM_EMPTY % ("pmCell", "pm")))
   391                               AMPM_EMPTY % ("pmCell", "pm")))
   395 
   392 
   396 
   393 
   397 class AMPMMonthCalendarView(MonthCalendarView):
   394 class AMPMMonthCalendarView(MonthCalendarView):
   398     """this view renders a 3x1 calendars' table"""
   395     """this view renders a 3x1 calendars' table"""
   401 
   398 
   402     def build_calendar(self, schedule, first_day):
   399     def build_calendar(self, schedule, first_day):
   403         """method responsible for building *one* HTML calendar"""
   400         """method responsible for building *one* HTML calendar"""
   404         umonth = self.format_date(first_day, '%B %Y') # localized month name
   401         umonth = self.format_date(first_day, '%B %Y') # localized month name
   405         rows = [] # each row is: (am,pm), (am,pm) ... week_title
   402         rows = [] # each row is: (am,pm), (am,pm) ... week_title
   406         current_row = [(NO_CELL, NO_CELL, NO_CELL)] * first_day.day_of_week
   403         current_row = [(NO_CELL, NO_CELL, NO_CELL)] * first_day.weekday()
   407         rql = self.rset.printable_rql()
   404         rql = self.rset.printable_rql()
   408         for daynum in xrange(0, first_day.days_in_month):
   405         for daynum in xrange(0, days_in_month(first_day)):
   409             # build cells day
   406             # build cells day
   410             day = first_day + daynum
   407             day = first_day + timedelta(daynum)
   411             events = schedule.get(day)
   408             events = schedule.get(day)
   412             if events:
   409             if events:
   413                 current_row.append((AMPM_DAY % (daynum+1),) + self._build_ampm_cells(daynum, events))
   410                 current_row.append((AMPM_DAY % (daynum+1),) + self._build_ampm_cells(daynum, events))
   414             else:
   411             else:
   415                 current_row.append((AMPM_DAY % (daynum+1),
   412                 current_row.append((AMPM_DAY % (daynum+1),
   416                                     AMPM_EMPTY % ("amCell", "am"),
   413                                     AMPM_EMPTY % ("amCell", "am"),
   417                                     AMPM_EMPTY % ("pmCell", "pm")))
   414                                     AMPM_EMPTY % ("pmCell", "pm")))
   418             # store & reset current row on Sundays
   415             # store & reset current row on Sundays
   419             if day.day_of_week == Sunday:
   416             if day.weekday() == 6:
   420                 url = self.build_url(rql=rql, vid='ampmcalendarweek',
   417                 url = self.build_url(rql=rql, vid='ampmcalendarweek',
   421                                      year=day.year, week=day.iso_week[1])
   418                                      year=day.year, week=day.isocalendar()[1])
   422                 weeklink = '<a href="%s">%s</a>' % (html_escape(url),
   419                 weeklink = '<a href="%s">%s</a>' % (html_escape(url),
   423                                                     day.iso_week[1])
   420                                                     day.isocalendar()[1])
   424                 current_row.append(WEEKNUM_CELL % weeklink)
   421                 current_row.append(WEEKNUM_CELL % weeklink)
   425                 rows.append(current_row)
   422                 rows.append(current_row)
   426                 current_row = []
   423                 current_row = []
   427         current_row.extend([(NO_CELL, NO_CELL, NO_CELL)] * (Sunday-day.day_of_week))
   424         current_row.extend([(NO_CELL, NO_CELL, NO_CELL)] * (6-day.weekday()))
   428         url = self.build_url(rql=rql, vid='ampmcalendarweek',
   425         url = self.build_url(rql=rql, vid='ampmcalendarweek',
   429                              year=day.year, week=day.iso_week[1])
   426                              year=day.year, week=day.isocalendar()[1])
   430         weeklink = '<a href="%s">%s</a>' % (html_escape(url),
   427         weeklink = '<a href="%s">%s</a>' % (html_escape(url),
   431                                             day.iso_week[1])
   428                                             day.isocalendar()[1])
   432         current_row.append(WEEKNUM_CELL % weeklink)
   429         current_row.append(WEEKNUM_CELL % weeklink)
   433         rows.append(current_row)
   430         rows.append(current_row)
   434         # build two rows for each week: am & pm
   431         # build two rows for each week: am & pm
   435         formatted_rows = []
   432         formatted_rows = []
   436         for row in rows:
   433         for row in rows:
   444         # tigh everything together
   441         # tigh everything together
   445         url = self.build_url(rql=rql, vid='ampmcalendarmonth',
   442         url = self.build_url(rql=rql, vid='ampmcalendarmonth',
   446                              year=first_day.year, month=first_day.month)
   443                              year=first_day.year, month=first_day.month)
   447         monthlink = '<a href="%s">%s</a>' % (html_escape(url),
   444         monthlink = '<a href="%s">%s</a>' % (html_escape(url),
   448                                              umonth)
   445                                              umonth)
   449         return CALENDAR(self.req) % (monthlink, '\n'.join(formatted_rows))      
   446         return CALENDAR(self.req) % (monthlink, '\n'.join(formatted_rows))
   450     
   447 
   451 
   448 
   452     
   449 
   453 class AMPMWeekCalendarView(WeekCalendarView):
   450 class AMPMWeekCalendarView(WeekCalendarView):
   454     """this view renders a 3x1 calendars' table"""
   451     """this view renders a 3x1 calendars' table"""
   455     id = 'ampmcalendarweek'
   452     id = 'ampmcalendarweek'
   456     title = _('am/pm calendar (week)')
   453     title = _('am/pm calendar (week)')
   457 
   454 
   463             umonth = self.format_date(monday, '%B %Y')
   460             umonth = self.format_date(monday, '%B %Y')
   464             url = self.build_url(rql=rql, vid='ampmcalendarmonth',
   461             url = self.build_url(rql=rql, vid='ampmcalendarmonth',
   465                                  year=monday.year, month=monday.month)
   462                                  year=monday.year, month=monday.month)
   466             monthlink = '<a href="%s">%s</a>' % (html_escape(url), umonth)
   463             monthlink = '<a href="%s">%s</a>' % (html_escape(url), umonth)
   467             w(u'<tr>%s</tr>' % (
   464             w(u'<tr>%s</tr>' % (
   468                 WEEK_TITLE % (_('week'), monday.iso_week[1], monthlink)))
   465                 WEEK_TITLE % (_('week'), monday.isocalendar()[1], monthlink)))
   469             w(u'<tr><th>%s</th><th>&nbsp;</th></tr>'% _(u'Date'))
   466             w(u'<tr><th>%s</th><th>&nbsp;</th></tr>'% _(u'Date'))
   470             for day in date_range(monday, sunday):
   467             for day in date_range(monday, sunday):
   471                 events = schedule.get(day)
   468                 events = schedule.get(day)
   472                 style = day.day_of_week % 2 and "even" or "odd"
   469                 style = day.weekday() % 2 and "even" or "odd"
   473                 w(u'<tr class="%s">' % style)
   470                 w(u'<tr class="%s">' % style)
   474                 if events:
   471                 if events:
   475                     hours = events.keys()
   472                     hours = events.keys()
   476                     hours.sort()
   473                     hours.sort()
   477                     w(AMPM_DAYWEEK % (
   474                     w(AMPM_DAYWEEK % (
   478                         len(hours), _(WEEKDAYS[day.day_of_week]),
   475                         len(hours), _(WEEKDAYS[day.weekday()]),
   479                         self.format_date(day)))
   476                         self.format_date(day)))
   480                     w(AMPM_WEEK_CELL % (
   477                     w(AMPM_WEEK_CELL % (
   481                         hours[0].hour, hours[0].minute,
   478                         hours[0].hour, hours[0].minute,
   482                         '\n'.join(events[hours[0]])))
   479                         '\n'.join(events[hours[0]])))
   483                     w(u'</tr>')
   480                     w(u'</tr>')
   485                         w(u'<tr class="%s">%s</tr>'% (
   482                         w(u'<tr class="%s">%s</tr>'% (
   486                             style, AMPM_WEEK_CELL % (hour.hour, hour.minute,
   483                             style, AMPM_WEEK_CELL % (hour.hour, hour.minute,
   487                                                      '\n'.join(events[hour]))))
   484                                                      '\n'.join(events[hour]))))
   488                 else:
   485                 else:
   489                     w(AMPM_DAYWEEK_EMPTY % (
   486                     w(AMPM_DAYWEEK_EMPTY % (
   490                         _(WEEKDAYS[day.day_of_week]),
   487                         _(WEEKDAYS[day.weekday()]),
   491                         self.format_date(day)))
   488                         self.format_date(day)))
   492                     w(WEEK_EMPTY_CELL)
   489                     w(WEEK_EMPTY_CELL)
   493                     w(u'</tr>')
   490                     w(u'</tr>')
   494 
   491 
   495 
   492