terom@51: """ terom@51: Configuration terom@51: """ terom@51: terom@118: import copy, logging terom@142: import templates terom@67: terom@118: class InstanceContext (object) : terom@118: """ terom@118: An object that behaves like a dict, performing all item lookups as attribute lookups on the given object. terom@118: terom@134: Additionally, it only lets you re-bind existing attributes via setitem, raising AttributeError if setting terom@134: unknown attributes. terom@134: terom@118: Useful for binding an exec statement's locals. terom@118: """ terom@118: terom@118: def __init__ (self, obj) : self.obj = obj terom@118: def __getitem__ (self, key) : return getattr(self.obj, key) terom@134: def __setitem__ (self, key, value) : terom@134: """ terom@134: Replace the named attribute, asserting that the target has such an attribute. terom@134: """ terom@134: terom@134: # test terom@134: if hasattr(self.obj, key) : terom@134: setattr(self.obj, key, value) terom@134: terom@134: else : terom@134: raise AttributeError(key) terom@118: terom@118: class ConfigurationMachinery (object) : terom@118: """ terom@118: The low-level configuration machinery. terom@118: """ terom@118: terom@118: def import_py (self, path) : terom@118: """ terom@118: Import python-style configuration from the given File object into this one. terom@118: terom@118: This runs the file's code using exec, using a context mapped to this object. terom@118: """ terom@118: terom@118: # the code terom@118: file = open(path) terom@118: terom@118: # build suitable locals/globals to proxy this object terom@118: locals = InstanceContext(self) terom@118: terom@118: # run terom@118: exec file in {}, locals terom@118: terom@118: def import_file (self, path) : terom@118: """ terom@118: Import configuration from the given File into this one. terom@118: """ terom@118: terom@118: # as python code terom@118: self.import_py(path) terom@118: terom@118: def load (self, path) : terom@118: """ terom@118: Loads the configuration from the given File, creating a new Configuration with defaults from this one. terom@118: """ terom@118: terom@118: # copy ourself terom@118: new = copy.copy(self) terom@118: terom@118: # import into it terom@118: new.import_file(path) terom@118: terom@118: # return terom@118: return new terom@118: terom@118: class Configuration (ConfigurationMachinery) : terom@51: """ terom@51: Various configuration settings terom@51: """ terom@67: terom@118: # the path to the gallery root terom@118: # only valid in top-level config terom@60: gallery_path = "." terom@87: terom@67: # minimum logging level terom@67: log_level = logging.INFO terom@67: terom@118: def quiet (self) : self.log_level = logging.WARN terom@118: def debug (self) : self.log_level = logging.DEBUG terom@118: terom@117: # number of threads to use for concurrency terom@135: # use some heuristic? terom@117: thread_count = 2 terom@117: terom@118: # the name of this folder, only applies on one level terom@118: # default applies to the root gallery terom@118: title = "Image Gallery" terom@51: terom@118: # recognized image extensions, case-insensitive terom@51: image_exts = ('jpg', 'jpeg', 'png', 'gif', 'bmp') terom@51: terom@118: # subdirectory names used for generated thumbnails/previews terom@51: thumb_dir = 'thumbs' terom@51: preview_dir = 'previews' terom@51: terom@118: # size of generated thumbnails/previews as (width, height) tuples terom@51: thumb_size = (160, 120) terom@51: preview_size = (640, 480) terom@51: terom@57: # number of images displayed per folder page terom@57: images_per_page = 50 terom@57: terom@118: # load Exif data for images, this may be slow terom@118: with_exif = False terom@111: terom@120: # name of Exif handler to use terom@120: exif_handler_name = None terom@120: terom@120: # explicit Exif handler class terom@120: exif_handler = None terom@120: terom@57: # exif tags used in output terom@57: # Copyright (C) 2008, Santtu Pajukanta terom@57: # XXX: import from dexif? terom@118: exif_tags = ( terom@57: # TODO Create date is in a useless format, needs some strptime love terom@57: ("CreateDate", "Create date" ), terom@57: ("Model", "Camera model" ), terom@57: ("Aperture", "Aperture" ), terom@57: ("ExposureMode", "Exposure mode" ), terom@57: ("ExposureCompensation", "Exposure compensation" ), terom@57: ("ExposureTime", "Exposure time" ), terom@57: ("Flash", "Flash mode" ), terom@57: ("ISO", "ISO" ), terom@57: ("ShootingMode", "Shooting mode" ), terom@57: ("LensType", "Lens type" ), terom@118: ("FocalLength", "Focal length" ), terom@118: ) terom@60: terom@142: # footer text HTML element, must use html.raw if this includes formatting terom@142: footer = templates.footer terom@142: terom@118: # XXX: move elsewhere? terom@57: def is_image (self, file) : terom@57: """ terom@57: Tests if the given File is an image, based on its file extension terom@118: terom@57: """ terom@57: terom@57: return file.matchext(self.image_exts) terom@118: