--- a/handlers.py Mon Feb 09 03:05:43 2009 +0200
+++ b/handlers.py Mon Feb 09 04:39:24 2009 +0200
@@ -2,7 +2,7 @@
Our URL action handlers
"""
-import pytz
+import datetime, calendar, pytz
from qmsk.web import http, template
@@ -12,7 +12,7 @@
# load templates from here
templates = template.TemplateLoader("templates",
- h = helpers,
+ _helper_class = helpers.Helpers,
urls = urls,
channel_list = channels.channel_list,
)
@@ -33,8 +33,8 @@
return http.Redirect(urls.channel_view.build(request, channel=channel.id))
-@preferences.handler(prefs.Formatter)
-def channel_view (request, channel, count, formatter) :
+@preferences.handler(prefs.Formatter, prefs.Timezone)
+def channel_view (request, channel, count, formatter, timezone) :
"""
The main channel view page, display the most important info, and all requisite links
"""
@@ -47,6 +47,7 @@
return templates.render_to_response("channel_view",
req = request,
+ timezone = timezone,
channel = channel,
count = count,
formatter = formatter,
@@ -66,12 +67,34 @@
else :
raise http.ResponseError("Unknown filetype %r" % format)
-def channel_calendar (request, channel) :
+@preferences.handler(prefs.Timezone)
+def channel_calendar (request, channel, year, month, timezone) :
"""
- Display a list of avilable logs for some days
+ Display a list of avilable logs for some month
"""
- pass
+ # current date as default
+ now = timezone.localize(datetime.datetime.now())
+
+ # target year/month
+ target = timezone.localize(datetime.datetime(
+ year = year if year else now.year,
+ month = month if month else now.month,
+ day = 1
+ ))
+
+ # get set of days available
+ days = channel.source.get_month_days(target)
+
+ # display calendar
+ return templates.render_to_response("channel_calendar",
+ req = request,
+ timezone = timezone,
+ channel = channel,
+ calendar = calendar.Calendar(),
+ month = target.date(),
+ days = days,
+ )
@preferences.handler(prefs.Formatter, prefs.Timezone)
def channel_date (request, channel, date, formatter, timezone) :
@@ -90,6 +113,7 @@
return templates.render_to_response("channel_date",
req = request,
+ timezone = timezone,
channel = channel,
formatter = formatter,
date = date,
--- a/helpers.py Mon Feb 09 03:05:43 2009 +0200
+++ b/helpers.py Mon Feb 09 04:39:24 2009 +0200
@@ -2,21 +2,56 @@
Some additional helpers
"""
-# "inherit" qmsk.web's helpers
-from qmsk.web.helpers import *
+import qmsk.web.helpers
-def tz_name (tz) :
+import datetime, calendar
+
+class Helpers (qmsk.web.helpers.Helpers) :
"""
- Returns a string describing the given timezone
+ Our set of helpers, inheriting from base helpers
"""
- return str(tz)
+ def tz_name (self, tz) :
+ """
+ Returns a string describing the given timezone
+ """
-def fmt_date (date) :
- """
- Formats a date
- """
-
- # XXX: hardcoded
- return date.strftime('%Y-%m-%d')
+ return str(tz)
+ def fmt_date (self, date) :
+ """
+ Formats a date
+ """
+
+ # XXX: hardcoded
+ return date.strftime('%Y-%m-%d')
+
+ def fmt_month (self, date) :
+ """
+ Formats a month
+ """
+
+ return date.strftime('%B %Y')
+
+ def fmt_weekday (self, wday) :
+ """
+ Formats an abbreviated weekday name
+ """
+
+ return calendar.day_abbr[wday]
+
+ def build_date (self, month, mday) :
+ """
+ Returns a datetime.date for the given (month.year, month.month, mday)
+ """
+
+ return datetime.date(month.year, month.month, mday)
+
+ def is_today (self, date) :
+ """
+ checks if the given date is today
+ """
+
+ # construct current date
+ return date == self.ctx['timezone'].localize(datetime.datetime.now()).date()
+
--- a/log_source.py Mon Feb 09 03:05:43 2009 +0200
+++ b/log_source.py Mon Feb 09 04:39:24 2009 +0200
@@ -2,7 +2,7 @@
A source of IRC log files
"""
-import datetime, itertools
+import datetime, calendar, itertools
import os, errno
import pytz
@@ -24,10 +24,19 @@
"""
abstract
+
+ def get_month_days (self, dt) :
+ """
+ Get a set of dates, telling which days in the given month (as a datetime) have logs available
+ """
-class LogFile (LogSource) :
+ abstract
+
+class LogFile (object) :
"""
A file containing LogEvents
+
+ XXX: modify to implement LogSource?
"""
def __init__ (self, path, parser, start_date=None, charset='utf-8', sep='\n') :
@@ -168,7 +177,7 @@
for line in lines[:0:-1] :
yield line.decode(self.charset)
- def get_latest (self, count) :
+ def read_latest (self, count) :
"""
Returns up to count events, from the end of the file, or less, if the file doesn't contain that many lines.
"""
@@ -221,9 +230,12 @@
# convert to date and use that
return self._get_logfile_date(dtz.date())
- def _get_logfile_date (self, d) :
+ def _get_logfile_date (self, d, load=True) :
"""
- Get the logfile corresponding to the given naive date in our timezone
+ Get the logfile corresponding to the given naive date in our timezone. If load is False, only test for the
+ presence of the logfile, do not actually open it.
+
+ Returns None if the logfile does not exist.
"""
# format filename
@@ -231,9 +243,24 @@
# build path
path = os.path.join(self.path, filename)
+
+ try :
+ if load :
+ # open+return the LogFile
+ return LogFile(path, self.parser, d, self.charset)
+
+ else :
+ # test
+ return os.path.exists(path)
- # return the LogFile
- return LogFile(path, self.parser, d, self.charset)
+ # XXX: move to LogFile
+ except IOError, e :
+ # return None for missing files
+ if e.errno == errno.ENOENT :
+ return None
+
+ else :
+ raise
def _iter_date_reverse (self, dt=None) :
"""
@@ -280,18 +307,13 @@
while len(lines) < count :
logfile = None
- try :
- # get next logfile
- files += 1
-
- # open
- logfile = self._get_logfile_date(day_iter.next())
+ # get next logfile
+ files += 1
- except IOError, e :
- # skip nonexistant days if we haven't found any logs yet
- if e.errno != errno.ENOENT :
- raise
-
+ # open
+ logfile = self._get_logfile_date(day_iter.next())
+
+ if not logfile :
if files > MAX_FILES :
raise Exception("No recent logfiles found")
@@ -301,7 +323,7 @@
# read the events
# XXX: use a queue
- lines = list(logfile.get_latest(count)) + lines
+ lines = list(logfile.read_latest(count)) + lines
# return the events
return lines
@@ -333,3 +355,27 @@
# chain together the two sources
return itertools.chain(f_begin.read_from(dtz_begin), f_end.read_until(dtz_end))
+ def get_month_days (self, month) :
+ """
+ Returns a set of dates for which logfiles are available in the given datetime's month
+ """
+
+ # the set of days
+ days = set()
+
+ # iterate over month's days using Calendar
+ for date in calendar.Calendar().itermonthdates(month.year, month.month) :
+ # convert date to target datetime
+ dtz = month.tzinfo.localize(datetime.datetime.combine(date, datetime.time(0))).astimezone(self.tz)
+
+ # date in our target timezone
+ log_date = dtz.date()
+
+ # test for it
+ if self._get_logfile_date(log_date, load=False) :
+ # add to set
+ days.add(date)
+
+ # return set
+ return days
+
--- a/static/irclogs.css Mon Feb 09 03:05:43 2009 +0200
+++ b/static/irclogs.css Mon Feb 09 04:39:24 2009 +0200
@@ -125,4 +125,12 @@
text-align: center;
}
+/*
+ * General
+ */
+/* Calendar */
+table.calendar td#today {
+ font-weight: bold;
+}
+
--- a/templates/channel.tmpl Mon Feb 09 03:05:43 2009 +0200
+++ b/templates/channel.tmpl Mon Feb 09 04:39:24 2009 +0200
@@ -48,5 +48,5 @@
${next.body()}
<%def name="footer_right()">
- All times are in <strong>${h.tz_name(formatter.tz)}</strong>
+ All times are in <strong>${h.tz_name(timezone)}</strong>
</%def>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/channel_calendar.tmpl Mon Feb 09 04:39:24 2009 +0200
@@ -0,0 +1,47 @@
+<%inherit file="channel.tmpl" />
+
+<%def name="month_table(cal, month, dates)">
+<table class="calendar">
+## table header - month name
+ <tr>
+ <th colspan="7">${h.fmt_month(month)}</th>
+ </tr>
+## month header - weekday names
+ <tr>
+ % for weekday in cal.iterweekdays() :
+ <th>${h.fmt_weekday(weekday)}</th>
+ % endfor
+ </tr>
+## iterate over the weeks
+% for week in cal.monthdays2calendar(month.year, month.month) :
+ <tr>
+ ## iterate over the week's days
+ % for day, weekday in week :
+ ## is it an empty cell?
+ % if not day :
+ <td> </td>
+ % else :
+ ## build date
+ <% date = h.build_date(month, day) %>
+ ## is it today?
+ % if day and h.is_today(date) :
+ <td id="today">\
+ % else :
+ <td>\
+ % endif
+ ## link to logs for this day?
+ % if date in dates :
+ <a href="${urls.channel_date.build(req, channel=channel, date=date)}">${day}</a>\
+ % else :
+ ${day}\
+ % endif
+</td>
+ % endif
+ % endfor
+ </tr>
+% endfor
+</table>
+</%def>
+
+${month_table(calendar, month, days)}
+
--- a/urls.py Mon Feb 09 03:05:43 2009 +0200
+++ b/urls.py Mon Feb 09 04:39:24 2009 +0200
@@ -27,13 +27,13 @@
)
# urls
-index = url('/', handlers.index )
-channel_select = url('/channel_select/?channel:cid', handlers.channel_select )
-channel_view = url('/channels/{channel:cid}/?count:int=10', handlers.channel_view )
-channel_last = url('/channels/{channel:cid}/last/{count:int=100}/{format=html}', handlers.channel_last )
-channel_date = url('/channels/{channel:cid}/calendar', handlers.channel_calendar )
-channel_date = url('/channels/{channel:cid}/date/{date:date}', handlers.channel_date )
-channel_search = url('/channels/{channel:cid}/search/?q', handlers.channel_search )
+index = url('/', handlers.index )
+channel_select = url('/channel_select/?channel:cid', handlers.channel_select )
+channel_view = url('/channels/{channel:cid}/?count:int=10', handlers.channel_view )
+channel_last = url('/channels/{channel:cid}/last/{count:int=100}/{format=html}', handlers.channel_last )
+channel_calendar = url('/channels/{channel:cid}/calendar/{year:int=0}/{month:int=0}', handlers.channel_calendar )
+channel_date = url('/channels/{channel:cid}/date/{date:date}', handlers.channel_date )
+channel_search = url('/channels/{channel:cid}/search/?q', handlers.channel_search )
# mapper
mapper = urltree.URLTree(urls)