pngtile/application.py
author Tero Marttila <terom@qmsk.net>
Fri, 03 Oct 2014 23:56:25 +0300
changeset 165 1dc09e81a4e2
parent 164 e1e0c8099c8b
child 166 986052d7d0ce
permissions -rw-r--r--
pngtile.tile: handle dir/image requests by redirecting to image_server
from werkzeug import Request, Response, exceptions
from werkzeug.utils import html
import werkzeug.urls

import pypngtile

import os.path

def url (server, *path, **args):
    """
        >>> url('http://foo/', 'bar, 'quux.png', x=5, y=2)
        'http://foo/bar/quux.png?x=5&y=2'
    """

    return werkzeug.urls.Href(server)(*path, **args)

class BaseApplication (object):
    IMAGE_TYPES = (
        'png',
    )

    def __init__ (self, image_root):
        if not os.path.isdir(image_root) :
            raise Exception("Given image_root does not exist: {image_root}".format(image_root=image_root))

        self.image_root = os.path.abspath(image_root)

        self.image_cache = { }

    def lookup_path (self, url):
        """
            Lookup image by request path.

            Returns name, path, type. For dirs, type will be None.
        """

        if not os.path.isdir(self.image_root):
            raise exceptions.InternalServerError("Server image_root has gone missing")
    
        # path to image
        name = url.lstrip('/')
        
        # build absolute path
        path = os.path.abspath(os.path.join(self.image_root, name))

        # ensure the path points inside the data root
        if not path.startswith(self.image_root):
            raise exceptions.NotFound(name)
        
        if not os.path.exists(path):
            raise exceptions.NotFound(name)
        
        # determine time
        if os.path.isdir(path):
            return name, path, None
        else:
            basename, type = path.rsplit('.', 1)
 
            return name, path, type

    def get_image (self, url):
        """
            Return Image object.
        """

        name, path, type = self.lookup_path(url)

        if type not in self.IMAGE_TYPES:
            raise exceptions.BadRequest("Not a supported image: {name}: {type}".format(name=name, type=type))

        # get Image object
        image = self.image_cache.get(path)

        if not image:
            # open
            image = pypngtile.Image(path)

            # check
            if image.status() not in (pypngtile.CACHE_FRESH, pypngtile.CACHE_STALE):
                raise exceptions.InternalServerError("Image cache not available: {name}".format(name=name))

            # load
            image.open()

            # cache
            self.image_cache[path] = image
        
        return image, name

    def handle (self, request):
        """
            Handle request for an image
        """

        raise NotImplementedError()

    @Request.application
    def __call__ (self, request):
        """
            WSGI entry point.
        """

        try:
            return self.handle(request)

        except exceptions.HTTPException as error:
            return error

    STYLESHEETS = ( )
    SCRIPTS = ( )

    def render_html (self, title, body, stylesheets=None, scripts=None, end=()):
        if stylesheets is None:
            stylesheets = self.STYLESHEETS

        if scripts is None:
            scripts = self.SCRIPTS

        return html.html(lang='en', *[
            html.head(
                html.title(title),
                *[
                    html.link(rel='stylesheet', href=href) for href in stylesheets
                ]
            ),
            html.body(
                *(body + tuple(
                    html.script(src=src) for src in scripts
                ) + end)
            ),
        ])