diff -r d7a159024912 -r eabea2857143 svv/cal.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/svv/cal.py Sat Jan 08 22:52:25 2011 +0200 @@ -0,0 +1,222 @@ +""" + Calendar view of orders +""" + +from svv.controllers import PageHandler +from svv.html import tags +from svv import database as db +from svv import html + + +import datetime +import calendar +import logging + +log = logging.getLogger('svv.cal') + +class CalendarView (PageHandler) : + """ + Single-month calendar view with events for given month shown + """ + + # first date of week + FIRST_WEEKDAY = 6 + + # year/month format for URLs + URL_FORMAT = "%Y-%m" + + # formatting styles + MONTH_TITLE_FORMAT = "%B %Y" + + @classmethod + def dayofweek_title (cls, dow) : + """ + Return day of week name for given dow number + """ + + return calendar.day_name[dow] + + @classmethod + def _wrap_year (cls, year, month) : + """ + Wraps month to between [1, 12], spilling overflow/underflow by to year. + + Returns (year, month) + """ + + # underflow? + if month == 0 : + # wrap to previous year + return (year - 1, 12) + + # overflow? + elif month == 13 : + # wrap to next year + return (year + 1, 1) + + # sane value + elif 1 <= month <= 12 : + return (year, month) + + # insane value + else : + assert False, "invalid year/month: %d/%d" % (year, month) + + @classmethod + def prev_month (cls, month) : + """ + Compute date for previous month. + """ + + # normalize + y, m = cls._wrap_year(month.year, month.month - 1) + + return datetime.date(y, m, 1) + + @classmethod + def next_month (cls, month) : + """ + Compute date for following month. + """ + + # normalize + y, m = cls._wrap_year(month.year, month.month + 1) + + return datetime.date(y, m, 1) + + + def process (self, **foo) : + """ + Setup + """ + + # db session + self.session = self.app.session() + + def render_day_header (self, month, date) : + """ + Render for day + """ + + today = datetime.date.today() + + classes = [] + + if (date.year, date.month) == (month.year, month.month) : + # current month + classes.append('in-month') + + else : + classes.append('out-month') + + if date == today : + classes.append('today') + + class_ = ' '.join(classes) + + return tags.th(date.day, class_=class_) + + def get_events_for_interval (self, start, end) : + """ + Returns list of Order objects for given interval, ordered by start time. + """ + + # XXX: bad imports + from orders import Order + + return self.session.query(Order).filter( + (Order.event_start.between(start, end)) + | (Order.event_end.between(start, end)) + ).order_by(Order.event_start).all() + + def render_week (self, month, week) : + """ + Render day rows for given week. + """ + + # XXX: nasty + from svv import urls + + # load events for week + week_start = datetime.datetime.combine(min(week), datetime.time(0, 0, 0)) + week_end = datetime.datetime.combine(max(week), datetime.time(23, 59, 59)) + + orders = self.get_events_for_interval(week_start, week_end) + + log.debug("Render week %r -> %r: %d", week_start, week_end, len(orders)) + + # day headers + yield tags.tr(class_='week-header')( + self.render_day_header(month, date) for date in week + ) + + # each even on its own row for now + for order in orders : + yield tags.tr(class_='week-data')( + ( + tags.td( + tags.a(href=self.url_for(urls.OrderView, id=order.id))(order.event_name) + + ) if order.on_date(date) else ( + tags.td("") + + ) + ) for date in week + ) + + def render_calendar (self, month) : + """ + Render calendar for given date's month. + """ + + cal = calendar.Calendar(self.FIRST_WEEKDAY) + + # next/prev month + prev = self.prev_month(month) + next = self.next_month(month) + + return tags.table(class_='calendar')( + tags.caption( + tags.a(href=self.url_for(CalendarView, yearmonth=prev.strftime(self.URL_FORMAT)), class_='prev-month')( + html.raw("«") + ), + month.strftime(self.MONTH_TITLE_FORMAT), + tags.a(href=self.url_for(CalendarView, yearmonth=next.strftime(self.URL_FORMAT)), class_='next-month')( + html.raw("»") + ), + ), + + # week-day headers + tags.thead( + tags.tr( + tags.th( + self.dayofweek_title(dow) + ) for dow in cal.iterweekdays() + ) + ), + + # month weeks + tags.tbody( + ( + self.render_week(month, week) + ) for week in cal.monthdatescalendar(month.year, month.month) + ), + ) + + def render_content (self, yearmonth=None) : + """ + Render calendar HTML for given year/month. + """ + + if yearmonth : + # requested month + month = datetime.datetime.strptime(yearmonth, self.URL_FORMAT).date() + + else : + # this month + month = datetime.date.today() + + return ( + self.render_calendar(month), + ) +