--- /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 <th> 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),
+ )
+