view pics2/pics_app.py @ 56:0249782e231e

pics2: add np_keys and selected thumbnail anchoring
author paulo
date Tue, 10 Sep 2013 01:04:24 -0700
parents 496714f2fd8c
children b7966ae653f2
line source
1 import os
2 import re
3 import glob
4 import traceback
5 import datetime
6 import random
7 import urlparse
9 import html
11 import logging
12 logging.basicConfig(
13 level=logging.DEBUG,
14 filename="_LOG",
15 format="%(asctime)s %(levelname)-8s %(message)s",
16 )
20 def _is_pics_dir(dirpath):
21 return os.path.exists(os.path.join(dirpath, "_picsroot"))
24 def _get_dir_dt(dirpath):
25 if _is_pics_dir(dirpath):
26 dirpath = os.path.join(dirpath, "_picsroot")
28 return datetime.datetime.fromtimestamp(os.stat(dirpath).st_mtime)
31 def _format_dt(dt):
32 return dt.strftime("%Y-%m-%d")
35 def _parse_path_info(path_info):
36 ppi = path_info.split(os.sep)
37 if len(ppi) > 1 and ppi[-1] == '':
38 del ppi[-1]
40 return ppi
43 def _numeric_pad_basename(path, maxdigits=20):
44 return os.path.basename(path).zfill(maxdigits)
47 def _get_images(d):
48 thumb_fns = glob.glob(os.path.join(d, "thumbs", "*.jpg"))
49 thumb_fns = sorted(thumb_fns, key=_numeric_pad_basename)
50 logging.debug("thumb_fns = %s" % thumb_fns)
52 browse_fns = [os.path.join(d, "browse", os.path.basename(i)) for i in thumb_fns]
53 logging.debug("browse_fns = %s" % browse_fns)
55 return zip(thumb_fns, browse_fns)
58 class Main:
59 def _get_pics_url(self, dirpath):
60 script_name = self._environ.get("SCRIPT_NAME", '')
61 return os.path.normpath(os.path.join(os.path.dirname(script_name), dirpath))
64 def _get_app_url(self, dirpath):
65 script_name = self._environ.get("SCRIPT_NAME", '')
66 return os.path.normpath(os.path.join(script_name, dirpath))
69 def _get_standard_html_doc(self, title):
70 root = html.HTML("html")
72 header = root.header
73 header.link(rel="stylesheet", type="text/css", href=self._get_pics_url("index.css"))
74 header.title(title)
76 body = root.body
77 body.h1(title)
79 return (root, header, body)
82 def _go_thumbnail_links_to_browse_imgs_html_body(self, body, t, b, a_args={}, img_args={}):
83 thumb_img_url = self._get_pics_url(t)
84 browse_url = self._get_app_url(b)
86 a = body.a(href=browse_url, **a_args)
87 a.img(src=thumb_img_url, **img_args)
89 body.text(' ')
92 def __init__(self, environ):
93 self._environ = environ
94 self._page_func = None
96 #logging.debug("environ = %s" % (sorted(self._environ.items(), key=lambda x: x[0]),))
97 logging.debug("environ['PATH_INFO'] = %s" % self._environ["PATH_INFO"])
98 logging.debug("environ['SCRIPT_NAME'] = %s" % self._environ["SCRIPT_NAME"])
99 logging.debug("environ['QUERY_STRING'] = %s" % self._environ["QUERY_STRING"])
101 pi = self._environ["PATH_INFO"]
102 ppi = _parse_path_info(pi)
103 logging.debug("ppi = %s" % ppi)
105 if len(ppi) < 1 or ppi[0] != '':
106 raise AssertionError("Parsed path length must start empty: " + pi)
108 if len(ppi) >= 2 and _is_pics_dir(ppi[1]):
109 if len(ppi) == 2:
110 self._page_func = self.page_thumbs
111 elif len(ppi) >= 4 and ppi[2] == "browse" and os.path.exists(os.path.join(*ppi)):
112 self._page_func = self.page_browse
113 elif len(ppi) == 1:
114 self._page_func = self.page_index
116 if self._page_func is None:
117 raise RuntimeError("Cannot find path: " + pi)
120 def page(self):
121 return unicode(self._page_func()).encode("utf-8")
124 def page_index(self):
125 n = 5 # number of thumbnails to display
127 (html_root, html_header, html_body) = self._get_standard_html_doc("Pictures")
129 pics_dirs = []
130 for i in os.listdir('.'):
131 if _is_pics_dir(i):
132 pics_dirs.append((i, _get_dir_dt(i)))
134 pics_dirs.sort(key=lambda x: x[1], reverse=True)
136 for (d, dt) in pics_dirs:
137 html_body.h2.a(d, href=self._get_app_url(d))
138 html_body.h3(_format_dt(dt))
140 imgs = _get_images(d)
141 imgs_idx = [(i, img) for (i, img) in enumerate(imgs)]
143 sampled_imgs_idx = random.sample(imgs_idx, min(len(imgs_idx), n))
144 sampled_imgs_idx.sort(key=lambda x: x[0])
146 html_p = html_body.p
147 for (i, (t, b)) in sampled_imgs_idx:
148 self._go_thumbnail_links_to_browse_imgs_html_body(html_p, t, b)
150 return html_root
153 def page_thumbs(self):
154 ppi = _parse_path_info(self._environ["PATH_INFO"])
155 d = os.path.join(*ppi)
156 (html_root, html_header, html_body) = self._get_standard_html_doc(d)
158 qs = urlparse.parse_qs(self._environ["QUERY_STRING"])
159 from_img = None
160 if "from" in qs:
161 from_img = qs["from"][0]
163 html_p = html_body.p
164 for (t, b) in _get_images(d):
165 if from_img is not None and b == from_img:
166 self._go_thumbnail_links_to_browse_imgs_html_body(html_p, t, b, img_args={"klass":"sel2", "id":"selected"})
167 else:
168 self._go_thumbnail_links_to_browse_imgs_html_body(html_p, t, b)
170 html_body.a("(Other pictures)", href=self._get_app_url(''))
171 return html_root
174 def page_browse(self):
175 ppi = _parse_path_info(self._environ["PATH_INFO"])
176 d = os.path.join(*ppi[:2])
177 imgs = _get_images(d)
178 img = os.path.join(*ppi)
180 # thumbnail preview ribbon
181 w = 7 # must be odd
182 v = w/2
183 imgs_circ = [None] * w
184 x = None
185 n = len(imgs)
186 for (i, (t, b)) in enumerate(imgs):
187 if b == img:
188 x = i + 1
189 imgs_circ[v] = (t, b)
190 for j in range(1, v + 1):
191 if (i + j) < n: imgs_circ[v + j] = imgs[i + j]
192 if (i - j) >= 0: imgs_circ[v - j] = imgs[i - j]
194 break
196 if x is None:
197 raise AssertionError
199 (html_root, html_header, html_body) = self._get_standard_html_doc(u"%s \u2014 %s of %s" % (d, x, len(imgs)))
201 html_header.script('', type="text/javascript", src=self._get_pics_url("np_keys.js"))
203 browse_img_url = self._get_pics_url(img)
204 html_body.p.img(src=browse_img_url)
206 logging.debug("imgs_circ = %s" % imgs_circ)
208 html_p = html_body.p
209 for (i, img_c) in enumerate(imgs_circ):
210 if img_c is not None:
211 (t, b) = img_c
212 a_args = {}
213 img_args = {}
214 if b == img:
215 a_args = {"id": "up"}
216 img_args = {"klass": "sel"}
217 b = "%s?from=%s#selected" % (d, img)
218 elif i == v + 1:
219 a_args = {"id": "next"}
220 elif i == v - 1:
221 a_args = {"id": "prev"}
223 self._go_thumbnail_links_to_browse_imgs_html_body(html_p, t, b, a_args, img_args)
225 return html_root
228 def app(environ, start_response):
229 response_code = "500 Internal Server Error"
230 response_type = "text/plain; charset=UTF-8"
232 try:
233 response_body = Main(environ).page()
234 response_code = "200 OK"
235 response_type = "text/html; charset=UTF-8"
236 except:
237 response_body = traceback.format_exc()
239 response_headers = [
240 ("Content-Type", response_type),
241 ("Content-Length", str(len(response_body))),
242 ]
244 start_response(response_code, response_headers)
246 return [response_body]