preferences.py
changeset 53 8103d18907a0
child 59 8ec729c5d998
--- /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),
+])
+