degal/config.py
author Tero Marttila <terom@fixme.fi>
Wed, 01 Jul 2009 20:46:27 +0300
changeset 142 2b8dfacc6d2d
parent 135 6534c77de93f
child 144 97505a789003
permissions -rw-r--r--
externalize footer contents from template into config
"""
    Configuration
"""

import copy, logging
import templates

class InstanceContext (object) :
    """
        An object that behaves like a dict, performing all item lookups as attribute lookups on the given object.

        Additionally, it only lets you re-bind existing attributes via setitem, raising AttributeError if setting
        unknown attributes.

        Useful for binding an exec statement's locals.
    """

    def __init__ (self, obj) : self.obj = obj
    def __getitem__ (self, key) : return getattr(self.obj, key)
    def __setitem__ (self, key, value) : 
        """
            Replace the named attribute, asserting that the target has such an attribute.
        """
        
        # test
        if hasattr(self.obj, key) :
            setattr(self.obj, key, value)
        
        else :
            raise AttributeError(key)

class ConfigurationMachinery (object) :
    """
        The low-level configuration machinery.
    """

    def import_py (self, path) :
        """
            Import python-style configuration from the given File object into this one.

            This runs the file's code using exec, using a context mapped to this object.
        """

        # the code
        file = open(path)
 
        # build suitable locals/globals to proxy this object
        locals = InstanceContext(self)

        # run
        exec file in {}, locals
       
    def import_file (self, path) :
        """
            Import configuration from the given File into this one.
        """
        
        # as python code
        self.import_py(path)

    def load (self, path) :
        """
            Loads the configuration from the given File, creating a new Configuration with defaults from this one.
        """

        # copy ourself
        new = copy.copy(self)

        # import into it
        new.import_file(path)

        # return
        return new

class Configuration (ConfigurationMachinery) :
    """
        Various configuration settings
    """

    # the path to the gallery root
    # only valid in top-level config
    gallery_path        = "."
    
    # force-update items
    force_thumb         = False
    force_html          = False

    def get_force_update (self) :
        return self.force_thumb or self.force_html

    def set_force_update (self, value) :
        self.force_thumb = self.force_html = value
    
    force_update = property(get_force_update, set_force_update)

    # minimum logging level
    log_level           = logging.INFO

    def quiet (self) : self.log_level = logging.WARN
    def debug (self) : self.log_level = logging.DEBUG

    # number of threads to use for concurrency
    # use some heuristic?
    thread_count        = 2

    # the name of this folder, only applies on one level
    # default applies to the root gallery
    title               = "Image Gallery"

    # recognized image extensions, case-insensitive
    image_exts          = ('jpg', 'jpeg', 'png', 'gif', 'bmp')
    
    # subdirectory names used for generated thumbnails/previews
    thumb_dir           = 'thumbs'
    preview_dir         = 'previews'

    # size of generated thumbnails/previews as (width, height) tuples
    thumb_size          = (160, 120)
    preview_size        = (640, 480)

    # number of images displayed per folder page
    images_per_page     = 50
    
    # load Exif data for images, this may be slow
    with_exif           = False

    # name of Exif handler to use
    exif_handler_name   = None

    # explicit Exif handler class
    exif_handler        = None

    # exif tags used in output
    # Copyright (C) 2008, Santtu Pajukanta <santtu@pajukanta.fi>
    # XXX: import from dexif?
    exif_tags           = (
        # TODO Create date is in a useless format, needs some strptime love
        ("CreateDate",              "Create date"               ),
        ("Model",                   "Camera model"              ),
        ("Aperture",                "Aperture"                  ),
        ("ExposureMode",            "Exposure mode"             ),
        ("ExposureCompensation",    "Exposure compensation"     ),
        ("ExposureTime",            "Exposure time"             ),
        ("Flash",                   "Flash mode"                ),
        ("ISO",                     "ISO"                       ),
        ("ShootingMode",            "Shooting mode"             ),
        ("LensType",                "Lens type"                 ),
        ("FocalLength",             "Focal length"              ),
    )

    # footer text HTML element, must use html.raw if this includes formatting
    footer              = templates.footer

    # XXX: move elsewhere?
    def is_image (self, file) :
        """
            Tests if the given File is an image, based on its file extension

        """

        return file.matchext(self.image_exts)