terom@53: """ terom@53: Handling user preferences terom@53: """ terom@53: terom@53: import functools terom@53: import Cookie terom@53: terom@53: from qmsk.web import urltree terom@53: terom@53: class Preference (urltree.URLType) : terom@53: """ terom@53: A specific preference terom@53: """ terom@53: terom@53: # the name to use terom@53: name = None terom@53: terom@53: # the default value terom@53: default = None terom@53: terom@53: def process (self, preferences, value) : terom@53: """ terom@53: Post-process this preference value. This can access the values of other preferences or the request via terom@53: the given RequestPreferences. Note that the other items will not be post-processed here. terom@53: terom@53: Defaults to just return value. terom@53: """ terom@53: terom@53: return value terom@53: terom@53: class RequestPreferences (object) : terom@53: """ terom@53: Represents the specific preferences for some request terom@53: """ terom@53: terom@53: def __init__ (self, preferences, request, value_map=None) : terom@53: """ terom@53: Initialize with the given Preferences object, http Request, and list of mapping of raw preference values terom@53: """ terom@53: terom@53: # store terom@53: self.preferences = preferences terom@53: self.request = request terom@53: terom@53: # initialize terom@53: self.values = {} terom@53: self.set_cookies = {} terom@53: terom@53: # load preferences terom@53: for pref in preferences.pref_list : terom@53: # got a value for it? terom@53: if value_map and pref.name in value_map : terom@53: # get value terom@53: value = value_map[pref.name] terom@53: terom@53: # parse it terom@53: value = pref.parse(value) terom@53: terom@53: else : terom@53: # use default value terom@53: value = pref.default terom@53: terom@53: # add terom@53: self.values[pref.name] = value terom@53: terom@53: # then post-process using the Preferences terom@53: self.values = dict( terom@53: (name, self.preferences.pref_map[name].process(self, value)) for name, value in self.values.iteritems() terom@53: ) terom@53: terom@53: def get (self, pref) : terom@53: """ terom@53: Return the value for the given Preference terom@53: """ terom@53: terom@53: return self.values[pref.name] terom@53: terom@53: def set (self, name, value_obj) : terom@53: """ terom@53: Set a new value for the given preference terom@53: """ terom@53: terom@53: # sanity-check to make sure we're not setting it twice... terom@53: assert name not in self.set_cookies terom@53: terom@53: # encode using the Preference object terom@53: value_str = self.preferences.pref_map[name].build(value_obj) terom@53: terom@53: # update in our dict terom@53: self.values[name] = value_obj terom@53: terom@53: # add to set_cookies terom@53: self.set_cookies[name] = value_str terom@53: terom@53: class Preferences (object) : terom@53: """ terom@53: Handle user preferences using cookies terom@53: """ terom@53: terom@53: def __init__ (self, pref_list) : terom@53: """ terom@53: Use the given list of Preference objects terom@53: """ terom@53: terom@53: # store terom@53: self.pref_list = pref_list terom@53: terom@53: # translate to mapping as well terom@53: self.pref_map = dict((pref.name, pref) for pref in pref_list) terom@53: terom@53: def load (self, request, ) : terom@53: """ terom@53: Load the set of preferences for the given request, and return as a { name -> value } dict terom@53: """ terom@53: terom@53: # the dict of values terom@53: values = {} terom@53: terom@53: # load the cookies terom@53: cookie_data = request.env.get('HTTP_COOKIE') terom@53: terom@53: # got any? terom@53: if cookie_data : terom@53: # parse into a SimpleCookie terom@53: cookies = Cookie.SimpleCookie(cookie_data) terom@53: terom@53: # update the the values terom@53: values.update((morsel.key, morsel.value) for morsel in cookies.itervalues()) terom@53: terom@53: else : terom@53: cookies = None terom@53: terom@53: # apply any query parameters terom@53: for pref in self.pref_list : terom@53: # look for a query param terom@53: value = request.get_arg(pref.name) terom@53: terom@53: if value : terom@53: # override terom@53: values[pref.name] = value terom@53: terom@53: # build the RequestPreferences object terom@53: return cookies, RequestPreferences(self, request, values) terom@53: terom@53: def handler (self, *pref_list) : terom@53: """ terom@53: Intended to be used as a decorator for a request handler, this will load the give Preferences and pass terom@53: them to the wrapped handler as keyword arguments, in addition to any others given. terom@53: """ terom@53: terom@53: def _decorator (func) : terom@53: @functools.wraps(func) terom@53: def _handler (request, **args) : terom@53: # load preferences terom@53: cookies, prefs = self.load(request) terom@53: terom@53: # update args with new ones terom@53: args.update(((pref.name, prefs.get(pref)) for pref in pref_list)) terom@53: terom@53: # handle to get response terom@53: response = func(request, **args) terom@53: terom@53: # set cookies? terom@53: if prefs.set_cookies : terom@53: # update cookies terom@53: for key, value in prefs.set_cookies.iteritems() : terom@53: cookies[key] = value terom@53: terom@53: # add headers terom@53: for morsel in cookies.itervalues() : terom@53: response.add_header('Set-cookie', morsel.OutputString()) terom@53: terom@53: return response terom@53: terom@53: # return wrapped handler terom@53: return _handler terom@53: terom@53: # return decorator... terom@53: return _decorator terom@53: terom@53: # now for our defined preferences.... terom@53: import pytz terom@53: terom@53: class TimeFormat (urltree.URLStringType, Preference) : terom@53: """ terom@53: Time format terom@53: """ terom@53: terom@53: # set name terom@53: name = 'time_format' terom@53: terom@53: # default value terom@53: default = "%H:%M:%S" terom@53: terom@53: class DateFormat (urltree.URLStringType, Preference) : terom@53: """ terom@53: Date format terom@53: """ terom@53: terom@53: # set name terom@53: name = 'date_format' terom@53: terom@53: # default value terom@53: default = "%Y-%m%d" terom@53: terom@53: class Timezone (Preference) : terom@53: """ terom@53: Timezone terom@53: """ terom@53: terom@53: # set name terom@53: name = 'timezone' terom@53: terom@53: # default value is UTC... terom@53: default = pytz.utc terom@53: terom@53: def parse (self, name) : terom@53: """ terom@53: tz_name -> pytz.timezone terom@53: """ terom@53: terom@53: return pytz.timezone(name) terom@53: terom@53: def build (self, tz) : terom@53: """ terom@53: pytz.timezone -> tz_name terom@53: """ terom@53: terom@53: return tz.zone terom@53: terom@53: class Formatter (Preference) : terom@53: """ terom@53: LogFormatter to use terom@53: """ terom@53: terom@53: # set name terom@53: name = 'formatter' terom@53: terom@53: def __init__ (self, formatters, default) : terom@53: """ terom@53: Use the given { name -> class LogFormatter } dict and default (a LogFormatter class) terom@53: """ terom@53: terom@53: self.formatters = formatters terom@53: self.default = default terom@53: terom@53: def parse (self, fmt_name) : terom@53: """ terom@53: fmt_name -> class LogFormatter terom@53: """ terom@53: terom@53: return self.formatters[fmt_name] terom@53: terom@53: def build (self, fmt_cls) : terom@53: """ terom@53: class LogFormatter -> fmt_name terom@53: """ terom@53: terom@53: return fmt.name terom@53: terom@53: def process (self, prefs, fmt_cls) : terom@53: """ terom@53: class LogFormatter -> LogFormatter(tz, time_fmt) terom@53: """ terom@53: terom@53: return fmt_cls(prefs.get(Timezone), prefs.get(TimeFormat)) terom@53: terom@53: # and then build the Preferences object terom@53: import log_formatter terom@53: terom@53: preferences = Preferences([ terom@53: TimeFormat(), terom@53: DateFormat(), terom@53: Timezone(), terom@53: Formatter(log_formatter.FORMATTERS, log_formatter.IrssiFormatter), terom@53: ]) terom@53: