|
1 from werkzeug import Request, Response, exceptions |
|
2 from werkzeug.utils import html |
|
3 |
|
4 import pypngtile |
|
5 |
|
6 import os.path |
|
7 |
|
8 class BaseApplication (object): |
|
9 def __init__ (self, image_root): |
|
10 if not os.path.isdir(image_root) : |
|
11 raise Exception("Given image_root does not exist: {image_root}".format(image_root=image_root)) |
|
12 |
|
13 self.image_root = os.path.abspath(image_root) |
|
14 |
|
15 self.image_cache = { } |
|
16 |
|
17 def lookup_image (self, url): |
|
18 """ |
|
19 Lookup image by request path. |
|
20 |
|
21 Returns image_name, image_path. |
|
22 """ |
|
23 |
|
24 if not os.path.isdir(self.image_root): |
|
25 raise exceptions.InternalServerError("Server image_root has gone missing") |
|
26 |
|
27 # path to image |
|
28 name = url.lstrip('/') |
|
29 |
|
30 # build absolute path |
|
31 path = os.path.abspath(os.path.join(self.image_root, name)) |
|
32 |
|
33 # ensure the path points inside the data root |
|
34 if not path.startswith(self.image_root): |
|
35 raise exceptions.NotFound(name) |
|
36 |
|
37 if not os.path.exists(path): |
|
38 raise exceptions.NotFound(name) |
|
39 |
|
40 if os.path.isdir(path): |
|
41 raise exceptions.BadRequest("Is a directory: {name}".format(name=name)) |
|
42 |
|
43 if not path.endswith('.png'): |
|
44 raise exceptions.BadRequest("Not a PNG image: {name}".format(name=name)) |
|
45 |
|
46 return name, path |
|
47 |
|
48 def get_image (self, url): |
|
49 """ |
|
50 Return Image object. |
|
51 """ |
|
52 |
|
53 name, path = self.lookup_image(url) |
|
54 |
|
55 # get Image object |
|
56 image = self.image_cache.get(path) |
|
57 |
|
58 if not image: |
|
59 # open |
|
60 image = pypngtile.Image(path) |
|
61 |
|
62 # check |
|
63 if image.status() not in (pypngtile.CACHE_FRESH, pypngtile.CACHE_STALE): |
|
64 raise exceptions.InternalServerError("Image cache not available: {name}".format(name=name)) |
|
65 |
|
66 # load |
|
67 image.open() |
|
68 |
|
69 # cache |
|
70 self.image_cache[path] = image |
|
71 |
|
72 return image, name |
|
73 |
|
74 def handle (self, request): |
|
75 """ |
|
76 Handle request for an image |
|
77 """ |
|
78 |
|
79 raise NotImplementedError() |
|
80 |
|
81 @Request.application |
|
82 def __call__ (self, request): |
|
83 """ |
|
84 WSGI entry point. |
|
85 """ |
|
86 |
|
87 try: |
|
88 return self.handle(request) |
|
89 |
|
90 except exceptions.HTTPException as error: |
|
91 return error |
|
92 |
|
93 STYLESHEETS = ( ) |
|
94 SCRIPTS = ( ) |
|
95 |
|
96 def render_html (self, title, body, stylesheets=None, scripts=None): |
|
97 if stylesheets is None: |
|
98 stylesheets = self.STYLESHEETS |
|
99 |
|
100 if scripts is None: |
|
101 scripts = self.SCRIPTS |
|
102 |
|
103 return html.html(lang='en', *[ |
|
104 html.head( |
|
105 html.title(title), |
|
106 *[ |
|
107 html.link(rel='stylesheet', href=href) for href in stylesheets |
|
108 ] |
|
109 ), |
|
110 html.body( |
|
111 *(body + tuple( |
|
112 html.script(src=src) for src in scripts |
|
113 )) |
|
114 ), |
|
115 ]) |
|
116 |
|
117 |