"""
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),
])