web/views/calendar.py
branchtls-sprint
changeset 1604 fd943737d630
parent 1398 5fe84a5f7035
child 1635 866563e2d0fc
equal deleted inserted replaced
1603:56217e741939 1604:fd943737d630
    27             _("friday"), _("saturday"), _("sunday"))
    27             _("friday"), _("saturday"), _("sunday"))
    28 MONTHNAMES = ( _('january'), _('february'), _('march'), _('april'), _('may'),
    28 MONTHNAMES = ( _('january'), _('february'), _('march'), _('april'), _('may'),
    29                _('june'), _('july'), _('august'), _('september'), _('october'),
    29                _('june'), _('july'), _('august'), _('september'), _('october'),
    30                _('november'), _('december')
    30                _('november'), _('december')
    31                )
    31                )
    32         
    32 
    33 # Calendar views ##############################################################
    33 # Calendar views ##############################################################
    34 
    34 
    35 
    35 
    36 class iCalView(EntityView):
    36 class iCalView(EntityView):
    37     """A calendar view that generates a iCalendar file (RFC 2445)
    37     """A calendar view that generates a iCalendar file (RFC 2445)
    97         if dates:
    97         if dates:
    98             if task.start and task.stop:
    98             if task.start and task.stop:
    99                 self.w('<br/>' % self.req._('from %(date)s' % {'date': self.format_date(task.start)}))
    99                 self.w('<br/>' % self.req._('from %(date)s' % {'date': self.format_date(task.start)}))
   100                 self.w('<br/>' % self.req._('to %(date)s' % {'date': self.format_date(task.stop)}))
   100                 self.w('<br/>' % self.req._('to %(date)s' % {'date': self.format_date(task.stop)}))
   101                 self.w('<br/>to %s'%self.format_date(task.stop))
   101                 self.w('<br/>to %s'%self.format_date(task.stop))
   102                 
   102 
   103 class CalendarLargeItemView(CalendarItemView):
   103 class CalendarLargeItemView(CalendarItemView):
   104     id = 'calendarlargeitem'
   104     id = 'calendarlargeitem'
   105 
   105 
   106     
   106 
   107 class _TaskEntry(object):
   107 class _TaskEntry(object):
   108     def __init__(self, task, color, index=0):
   108     def __init__(self, task, color, index=0):
   109         self.task = task
   109         self.task = task
   110         self.color = color
   110         self.color = color
   111         self.index = index
   111         self.index = index
   114     def in_working_hours(self):
   114     def in_working_hours(self):
   115         """predicate returning True is the task is in working hours"""
   115         """predicate returning True is the task is in working hours"""
   116         if self.task.start.hour > 7 and self.task.stop.hour < 20:
   116         if self.task.start.hour > 7 and self.task.stop.hour < 20:
   117             return True
   117             return True
   118         return False
   118         return False
   119     
   119 
   120     def is_one_day_task(self):
   120     def is_one_day_task(self):
   121         task = self.task
   121         task = self.task
   122         return task.start and task.stop and task.start.isocalendar() ==  task.stop.isocalendar()
   122         return task.start and task.stop and task.start.isocalendar() ==  task.stop.isocalendar()
   123         
   123 
       
   124 
   124 class OneMonthCal(EntityView):
   125 class OneMonthCal(EntityView):
   125     """At some point, this view will probably replace ampm calendars"""
   126     """At some point, this view will probably replace ampm calendars"""
   126     id = 'onemonthcal'
   127     id = 'onemonthcal'
   127     __select__ = implements(ICalendarable)
   128     __select__ = implements(ICalendarable)
   128     need_navigation = False
   129     need_navigation = False
   177                 else:
   178                 else:
   178                     the_dates = date_range(max(tstart, firstday),
   179                     the_dates = date_range(max(tstart, firstday),
   179                                            min(tstop, lastday))
   180                                            min(tstop, lastday))
   180             if not the_dates:
   181             if not the_dates:
   181                 continue
   182                 continue
   182             
   183 
   183             for d in the_dates:
   184             for d in the_dates:
   184                 d_tasks = dates.setdefault((d.year, d.month, d.day), {})
   185                 d_tasks = dates.setdefault((d.year, d.month, d.day), {})
   185                 t_users = d_tasks.setdefault(task, set())
   186                 t_users = d_tasks.setdefault(task, set())
   186                 t_users.add( user )
   187                 t_users.add( user )
   187                 if len(d_tasks) > task_max:
   188                 if len(d_tasks) > task_max:
   240                 curdate.year, html_escape(nextlink)))
   241                 curdate.year, html_escape(nextlink)))
   241 
   242 
   242         # output header
   243         # output header
   243         self.w(u'<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>' %
   244         self.w(u'<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>' %
   244                tuple(self.req._(day) for day in WEEKDAYS))
   245                tuple(self.req._(day) for day in WEEKDAYS))
   245         
   246 
   246         # build calendar
   247         # build calendar
   247         for mdate, task_rows in zip(month_dates, days):
   248         for mdate, task_rows in zip(month_dates, days):
   248             if mdate.weekday() == 0:
   249             if mdate.weekday() == 0:
   249                 self.w(u'<tr>')
   250                 self.w(u'<tr>')
   250             self._build_calendar_cell(mdate, task_rows, curdate)
   251             self._build_calendar_cell(mdate, task_rows, curdate)
   270         if celldate == date.today():
   271         if celldate == date.today():
   271             classes += " today"
   272             classes += " today"
   272         self.w(u'<td class="cell%s">' % classes)
   273         self.w(u'<td class="cell%s">' % classes)
   273         self.w(u'<div class="calCellTitle%s">' % classes)
   274         self.w(u'<div class="calCellTitle%s">' % classes)
   274         self.w(u'<div class="day">%s</div>' % celldate.day)
   275         self.w(u'<div class="day">%s</div>' % celldate.day)
   275         
   276 
   276         if len(self.rset.column_types(0)) == 1:
   277         if len(self.rset.column_types(0)) == 1:
   277             etype = list(self.rset.column_types(0))[0]
   278             etype = list(self.rset.column_types(0))[0]
   278             url = self.build_url(vid='creation', etype=etype,
   279             url = self.build_url(vid='creation', etype=etype,
   279                                  schedule=True,
   280                                  schedule=True,
   280                                  start=self.format_date(celldate), stop=self.format_date(celldate),
   281                                  start=self.format_date(celldate), stop=self.format_date(celldate),
   312     """At some point, this view will probably replace ampm calendars"""
   313     """At some point, this view will probably replace ampm calendars"""
   313     id = 'oneweekcal'
   314     id = 'oneweekcal'
   314     __select__ = implements(ICalendarable)
   315     __select__ = implements(ICalendarable)
   315     need_navigation = False
   316     need_navigation = False
   316     title = _('one week')
   317     title = _('one week')
   317     
   318 
   318     def call(self):
   319     def call(self):
   319         self.req.add_js( ('cubicweb.ajax.js', 'cubicweb.calendar.js') )
   320         self.req.add_js( ('cubicweb.ajax.js', 'cubicweb.calendar.js') )
   320         self.req.add_css('cubicweb.calendar.css')
   321         self.req.add_css('cubicweb.calendar.css')
   321         # XXX: restrict directly with RQL
   322         # XXX: restrict directly with RQL
   322         _today =  datetime.today()
   323         _today =  datetime.today()
   325         else:
   326         else:
   326             year = _today.year
   327             year = _today.year
   327         if 'week' in self.req.form:
   328         if 'week' in self.req.form:
   328             week = int(self.req.form['week'])
   329             week = int(self.req.form['week'])
   329         else:
   330         else:
   330             week = _today.isocalendar()[1]        
   331             week = _today.isocalendar()[1]
   331         # week - 1 since we get week number > 0 while we want it to start from 0
   332         # week - 1 since we get week number > 0 while we want it to start from 0
   332         first_day_of_week = todate(strptime('%s-%s-1' % (year, week - 1), '%Y-%U-%w'))
   333         first_day_of_week = todate(strptime('%s-%s-1' % (year, week - 1), '%Y-%U-%w'))
   333         lastday = first_day_of_week + timedelta(6)
   334         lastday = first_day_of_week + timedelta(6)
   334         firstday = first_day_of_week
   335         firstday = first_day_of_week
   335         dates = [[] for i in range(7)]
   336         dates = [[] for i in range(7)]
   357             if tstart and tstop:
   358             if tstart and tstop:
   358                 the_dates = date_range(max(tstart, firstday),
   359                 the_dates = date_range(max(tstart, firstday),
   359                                        min(tstop, lastday))
   360                                        min(tstop, lastday))
   360             if not the_dates:
   361             if not the_dates:
   361                 continue
   362                 continue
   362                 
   363 
   363             if task not in task_colors:
   364             if task not in task_colors:
   364                 task_colors[task] = colors[next_color_index]
   365                 task_colors[task] = colors[next_color_index]
   365                 next_color_index = (next_color_index+1) % len(colors)
   366                 next_color_index = (next_color_index+1) % len(colors)
   366             
   367 
   367             for d in the_dates:
   368             for d in the_dates:
   368                 day = d.weekday()
   369                 day = d.weekday()
   369                 task_descr = _TaskEntry(task, task_colors[task])  
   370                 task_descr = _TaskEntry(task, task_colors[task])
   370                 dates[day].append(task_descr)
   371                 dates[day].append(task_descr)
   371             
   372 
   372         self.w(u'<div id="oneweekcalid">')
   373         self.w(u'<div id="oneweekcalid">')
   373         # build schedule
   374         # build schedule
   374         self.w(u'<table class="omcalendar" id="week">')
   375         self.w(u'<table class="omcalendar" id="week">')
   375         prevlink, nextlink = self._prevnext_links(first_day_of_week)  # XXX
   376         prevlink, nextlink = self._prevnext_links(first_day_of_week)  # XXX
   376         self.w(u'<tr><th class="transparent"></th>')
   377         self.w(u'<tr><th class="transparent"></th>')
   389             if wdate.isocalendar() == _today.isocalendar():
   390             if wdate.isocalendar() == _today.isocalendar():
   390                 self.w(u'<th class="today">%s<br/>%s</th>' % (self.req._(day), self.format_date(wdate)))
   391                 self.w(u'<th class="today">%s<br/>%s</th>' % (self.req._(day), self.format_date(wdate)))
   391             else:
   392             else:
   392                 self.w(u'<th>%s<br/>%s</th>' % (self.req._(day), self.format_date(wdate)))
   393                 self.w(u'<th>%s<br/>%s</th>' % (self.req._(day), self.format_date(wdate)))
   393         self.w(u'</tr>')
   394         self.w(u'</tr>')
   394         
   395 
   395         # build week calendar
   396         # build week calendar
   396         self.w(u'<tr>')
   397         self.w(u'<tr>')
   397         self.w(u'<td style="width:5em;">') # column for hours
   398         self.w(u'<td style="width:5em;">') # column for hours
   398         extra = ""
   399         extra = ""
   399         for h in range(8, 20):
   400         for h in range(8, 20):
   400             self.w(u'<div class="hour" %s>'%extra)
   401             self.w(u'<div class="hour" %s>'%extra)
   401             self.w(u'%02d:00'%h)
   402             self.w(u'%02d:00'%h)
   402             self.w(u'</div>')            
   403             self.w(u'</div>')
   403         self.w(u'</td>')
   404         self.w(u'</td>')
   404         
   405 
   405         for i, day in enumerate(WEEKDAYS):
   406         for i, day in enumerate(WEEKDAYS):
   406             wdate = first_day_of_week + timedelta(i)
   407             wdate = first_day_of_week + timedelta(i)
   407             classes = ""
   408             classes = ""
   408             if wdate.isocalendar() == _today.isocalendar():
   409             if wdate.isocalendar() == _today.isocalendar():
   409                 classes = " today"
   410                 classes = " today"
   421             else:
   422             else:
   422                 extra = ""
   423                 extra = ""
   423             self.w(u'<div class="columndiv"%s>'% extra)
   424             self.w(u'<div class="columndiv"%s>'% extra)
   424             for h in range(8, 20):
   425             for h in range(8, 20):
   425                 self.w(u'<div class="hourline" style="top:%sex;">'%((h-7)*8))
   426                 self.w(u'<div class="hourline" style="top:%sex;">'%((h-7)*8))
   426                 self.w(u'</div>')            
   427                 self.w(u'</div>')
   427             if dates[i]:
   428             if dates[i]:
   428                 self._build_calendar_cell(wdate, dates[i])
   429                 self._build_calendar_cell(wdate, dates[i])
   429             self.w(u'</div>')
   430             self.w(u'</div>')
   430             self.w(u'</td>')
   431             self.w(u'</td>')
   431         self.w(u'</tr>')
   432         self.w(u'</tr>')
   432         self.w(u'</table></div>')
   433         self.w(u'</table></div>')
   433         self.w(u'<div id="coord"></div>')
   434         self.w(u'<div id="coord"></div>')
   434         self.w(u'<div id="debug">&nbsp;</div>')
   435         self.w(u'<div id="debug">&nbsp;</div>')
   435  
   436 
   436     def _build_calendar_cell(self, date, task_descrs):
   437     def _build_calendar_cell(self, date, task_descrs):
   437         inday_tasks = [t for t in task_descrs if t.is_one_day_task() and  t.in_working_hours()]
   438         inday_tasks = [t for t in task_descrs if t.is_one_day_task() and  t.in_working_hours()]
   438         wholeday_tasks = [t for t in task_descrs if not t.is_one_day_task()]
   439         wholeday_tasks = [t for t in task_descrs if not t.is_one_day_task()]
   439         inday_tasks.sort(key=lambda t:t.task.start)
   440         inday_tasks.sort(key=lambda t:t.task.start)
   440         sorted_tasks = []
   441         sorted_tasks = []
   472             if task.stop:
   473             if task.stop:
   473                 if date < todate(task.stop) < date + ONEDAY:
   474                 if date < todate(task.stop) < date + ONEDAY:
   474                     stop_hour = min(20, task.stop.hour)
   475                     stop_hour = min(20, task.stop.hour)
   475                     if stop_hour < 20:
   476                     if stop_hour < 20:
   476                         stop_min = task.stop.minute
   477                         stop_min = task.stop.minute
   477                     
   478 
   478             height = 100.0*(stop_hour+stop_min/60.0-start_hour-start_min/60.0)/(20-8)
   479             height = 100.0*(stop_hour+stop_min/60.0-start_hour-start_min/60.0)/(20-8)
   479             top = 100.0*(start_hour+start_min/60.0-8)/(20-8)
   480             top = 100.0*(start_hour+start_min/60.0-8)/(20-8)
   480             left = width*task_desc.index
   481             left = width*task_desc.index
   481             style = "height: %s%%; width: %s%%; top: %s%%; left: %s%%; " % \
   482             style = "height: %s%%; width: %s%%; top: %s%%; left: %s%%; " % \
   482                 (height, width, top, left)
   483                 (height, width, top, left)
   502                 self.w(u'end')
   503                 self.w(u'end')
   503                 self.w(u'</div>')
   504                 self.w(u'</div>')
   504                 self.w(u'</div>')
   505                 self.w(u'</div>')
   505             self.w(u'</div>')
   506             self.w(u'</div>')
   506 
   507 
   507             
   508 
   508     def _prevnext_links(self, curdate):
   509     def _prevnext_links(self, curdate):
   509         prevdate = curdate - timedelta(7)
   510         prevdate = curdate - timedelta(7)
   510         nextdate = curdate + timedelta(7)
   511         nextdate = curdate + timedelta(7)
   511         rql = self.rset.printable_rql()
   512         rql = self.rset.printable_rql()
   512         prevlink = ajax_replace_url('oneweekcalid', rql, 'oneweekcal',
   513         prevlink = ajax_replace_url('oneweekcalid', rql, 'oneweekcal',