add some user-preferences support (e.g. timezone, time formats)
--- a/handlers.py Mon Feb 09 01:17:32 2009 +0200
+++ b/handlers.py Mon Feb 09 03:05:43 2009 +0200
@@ -7,6 +7,8 @@
from qmsk.web import http, template
import urls, channels, helpers
+import preferences as prefs
+from preferences import preferences
# load templates from here
templates = template.TemplateLoader("templates",
@@ -31,6 +33,7 @@
return http.Redirect(urls.channel_view.build(request, channel=channel.id))
+@preferences.handler(prefs.Formatter)
def channel_view (request, channel, count, formatter) :
"""
The main channel view page, display the most important info, and all requisite links
@@ -39,9 +42,6 @@
# get latest events
lines = channel.source.get_latest(count)
- # formatter
- formatter = formatter(pytz.utc, '%H:%M:%S')
-
# lines
lines = formatter.format_html(lines)
@@ -73,20 +73,18 @@
pass
-def channel_date (request, channel, date, formatter) :
+@preferences.handler(prefs.Formatter, prefs.Timezone)
+def channel_date (request, channel, date, formatter, timezone) :
"""
Display all log data for the given date
"""
- # XXX: fix date timezone
- date = date.replace(tzinfo=pytz.utc)
+ # fix date timezone
+ date = date.replace(tzinfo=timezone)
# get latest events
lines = channel.source.get_date(date)
- # formatter
- formatter = formatter(pytz.utc, '%H:%M:%S')
-
# lines
lines = formatter.format_html(lines)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/preferences.py Mon Feb 09 03:05:43 2009 +0200
@@ -0,0 +1,277 @@
+"""
+ Handling user preferences
+"""
+
+import functools
+import Cookie
+
+from qmsk.web import urltree
+
+class Preference (urltree.URLType) :
+ """
+ A specific preference
+ """
+
+ # the name to use
+ name = None
+
+ # the default value
+ default = None
+
+ def process (self, preferences, value) :
+ """
+ Post-process this preference value. This can access the values of other preferences or the request via
+ the given RequestPreferences. Note that the other items will not be post-processed here.
+
+ Defaults to just return value.
+ """
+
+ return value
+
+class RequestPreferences (object) :
+ """
+ Represents the specific preferences for some request
+ """
+
+ def __init__ (self, preferences, request, value_map=None) :
+ """
+ Initialize with the given Preferences object, http Request, and list of mapping of raw preference values
+ """
+
+ # store
+ self.preferences = preferences
+ self.request = request
+
+ # initialize
+ self.values = {}
+ self.set_cookies = {}
+
+ # load preferences
+ for pref in preferences.pref_list :
+ # got a value for it?
+ if value_map and pref.name in value_map :
+ # get value
+ value = value_map[pref.name]
+
+ # parse it
+ value = pref.parse(value)
+
+ else :
+ # use default value
+ value = pref.default
+
+ # add
+ self.values[pref.name] = value
+
+ # then post-process using the Preferences
+ self.values = dict(
+ (name, self.preferences.pref_map[name].process(self, value)) for name, value in self.values.iteritems()
+ )
+
+ def get (self, pref) :
+ """
+ Return the value for the given Preference
+ """
+
+ return self.values[pref.name]
+
+ def set (self, name, value_obj) :
+ """
+ Set a new value for the given preference
+ """
+
+ # sanity-check to make sure we're not setting it twice...
+ assert name not in self.set_cookies
+
+ # encode using the Preference object
+ value_str = self.preferences.pref_map[name].build(value_obj)
+
+ # update in our dict
+ self.values[name] = value_obj
+
+ # add to set_cookies
+ self.set_cookies[name] = value_str
+
+class Preferences (object) :
+ """
+ Handle user preferences using cookies
+ """
+
+ def __init__ (self, pref_list) :
+ """
+ Use the given list of Preference objects
+ """
+
+ # store
+ self.pref_list = pref_list
+
+ # translate to mapping as well
+ self.pref_map = dict((pref.name, pref) for pref in pref_list)
+
+ def load (self, request, ) :
+ """
+ Load the set of preferences for the given request, and return as a { name -> value } dict
+ """
+
+ # the dict of values
+ values = {}
+
+ # load the cookies
+ cookie_data = request.env.get('HTTP_COOKIE')
+
+ # got any?
+ if cookie_data :
+ # parse into a SimpleCookie
+ cookies = Cookie.SimpleCookie(cookie_data)
+
+ # update the the values
+ values.update((morsel.key, morsel.value) for morsel in cookies.itervalues())
+
+ else :
+ cookies = None
+
+ # apply any query parameters
+ for pref in self.pref_list :
+ # look for a query param
+ value = request.get_arg(pref.name)
+
+ if value :
+ # override
+ values[pref.name] = value
+
+ # build the RequestPreferences object
+ return cookies, RequestPreferences(self, request, values)
+
+ def handler (self, *pref_list) :
+ """
+ Intended to be used as a decorator for a request handler, this will load the give Preferences and pass
+ them to the wrapped handler as keyword arguments, in addition to any others given.
+ """
+
+ def _decorator (func) :
+ @functools.wraps(func)
+ def _handler (request, **args) :
+ # load preferences
+ cookies, prefs = self.load(request)
+
+ # update args with new ones
+ args.update(((pref.name, prefs.get(pref)) for pref in pref_list))
+
+ # handle to get response
+ response = func(request, **args)
+
+ # set cookies?
+ if prefs.set_cookies :
+ # update cookies
+ for key, value in prefs.set_cookies.iteritems() :
+ cookies[key] = value
+
+ # add headers
+ for morsel in cookies.itervalues() :
+ response.add_header('Set-cookie', morsel.OutputString())
+
+ return response
+
+ # return wrapped handler
+ return _handler
+
+ # return decorator...
+ return _decorator
+
+# now for our defined preferences....
+import pytz
+
+class TimeFormat (urltree.URLStringType, Preference) :
+ """
+ Time format
+ """
+
+ # set name
+ name = 'time_format'
+
+ # default value
+ default = "%H:%M:%S"
+
+class DateFormat (urltree.URLStringType, Preference) :
+ """
+ Date format
+ """
+
+ # set name
+ name = 'date_format'
+
+ # default value
+ default = "%Y-%m%d"
+
+class Timezone (Preference) :
+ """
+ Timezone
+ """
+
+ # set name
+ name = 'timezone'
+
+ # default value is UTC...
+ default = pytz.utc
+
+ def parse (self, name) :
+ """
+ tz_name -> pytz.timezone
+ """
+
+ return pytz.timezone(name)
+
+ def build (self, tz) :
+ """
+ pytz.timezone -> tz_name
+ """
+
+ return tz.zone
+
+class Formatter (Preference) :
+ """
+ LogFormatter to use
+ """
+
+ # set name
+ name = 'formatter'
+
+ def __init__ (self, formatters, default) :
+ """
+ Use the given { name -> class LogFormatter } dict and default (a LogFormatter class)
+ """
+
+ self.formatters = formatters
+ self.default = default
+
+ def parse (self, fmt_name) :
+ """
+ fmt_name -> class LogFormatter
+ """
+
+ return self.formatters[fmt_name]
+
+ def build (self, fmt_cls) :
+ """
+ class LogFormatter -> fmt_name
+ """
+
+ return fmt.name
+
+ def process (self, prefs, fmt_cls) :
+ """
+ class LogFormatter -> LogFormatter(tz, time_fmt)
+ """
+
+ return fmt_cls(prefs.get(Timezone), prefs.get(TimeFormat))
+
+# and then build the Preferences object
+import log_formatter
+
+preferences = Preferences([
+ TimeFormat(),
+ DateFormat(),
+ Timezone(),
+ Formatter(log_formatter.FORMATTERS, log_formatter.IrssiFormatter),
+])
+
--- a/urls.py Mon Feb 09 01:17:32 2009 +0200
+++ b/urls.py Mon Feb 09 03:05:43 2009 +0200
@@ -13,7 +13,7 @@
import utils
# for configuration
-import channels, log_formatter
+import channels
# our URLConfig
urls = url = urltree.URLConfig(
@@ -21,9 +21,6 @@
# LogChannel
cid = utils.URLChannelName(channels.channel_list.dict()),
- # LogFormatter
- fmt = utils.URLFormatterName(log_formatter.FORMATTERS),
-
# datetime
date = utils.URLDateType('%Y-%m-%d'),
)
@@ -32,10 +29,10 @@
# urls
index = url('/', handlers.index )
channel_select = url('/channel_select/?channel:cid', handlers.channel_select )
-channel_view = url('/channels/{channel:cid}/?count:int=10&formatter:fmt=irssi', handlers.channel_view )
+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}/?formatter:fmt=irssi', handlers.channel_date )
+channel_date = url('/channels/{channel:cid}/date/{date:date}', handlers.channel_date )
channel_search = url('/channels/{channel:cid}/search/?q', handlers.channel_search )
# mapper
--- a/utils.py Mon Feb 09 01:17:32 2009 +0200
+++ b/utils.py Mon Feb 09 03:05:43 2009 +0200
@@ -32,33 +32,6 @@
return chan.id
-class URLFormatterName (URLType) :
- """
- Handle LogFormatter names in URLs. Note that they evaluate into the LogFormatter class itself, not an
- instance, although build requiers an instance
- """
-
- def __init__ (self, formatters) :
- """
- Use the given { name -> class LogFormatter } dict
- """
-
- self.formatters = formatters
-
- def parse (self, fmt_name) :
- """
- fmt_name -> class LogFormatter
- """
-
- return self.formatters[fmt_name]
-
- def build (self, fmt) :
- """
- LogFormatter -> fmt_name
- """
-
- return fmt.name
-
class URLDateType (URLType) :
"""
Handle dates in URLs as naive datetime objects (with indeterminate time info)