preferences.py
author Tero Marttila <terom@fixme.fi>
Mon, 09 Feb 2009 11:05:53 +0200
changeset 64 cdb6403c2498
parent 62 e7ca94b94a4e
child 73 5a7188bf2894
permissions -rw-r--r--
beginnings of a LogSearchIndex class
"""
    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, or preference name
        """
        
        # Preference -> name
        if isinstance(pref, Preference) :
            pref = pref.name
        
        # look up
        return self.values[pref]

    # support dict-access
    __getitem__ = get

    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)

                # bind to request.prefs
                # XXX: better way to do this? :/
                request.prefs = prefs

                # 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 :
                    if not cookies :
                        cookies = Cookie.SimpleCookie('')

                    # 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_cls.name
    
    def process (self, prefs, fmt_cls) :
        """
            class LogFormatter -> LogFormatter(tz, time_fmt)
        """

        return fmt_cls(prefs[timezone], prefs[time_format])

# and then build the Preferences object
import log_formatter

time_format = TimeFormat()
date_format = DateFormat()
timezone    = Timezone()
formatter   = Formatter(log_formatter.FORMATTERS, log_formatter.IrssiFormatter)

preferences = Preferences([
    time_format,
    date_format,
    timezone,
    formatter,
])