webapp: allow git-like hash truncation
authorHelmut Grohne <helmut@subdivi.de>
Sun, 11 May 2014 13:25:46 +0000 (15:25 +0200)
committerHelmut Grohne <helmut@subdivi.de>
Sun, 11 May 2014 13:27:56 +0000 (15:27 +0200)
webapp.py

index 2fd69bb..9612c38 100755 (executable)
--- a/webapp.py
+++ b/webapp.py
@@ -8,7 +8,8 @@ from wsgiref.simple_server import make_server
 
 import jinja2
 from werkzeug.exceptions import HTTPException, NotFound
-from werkzeug.routing import Map, Rule, RequestRedirect
+from werkzeug.routing import Map, Rule
+from werkzeug.utils import redirect
 from werkzeug.wrappers import Request, Response
 from werkzeug.wsgi import SharedDataMiddleware
 
@@ -61,6 +62,12 @@ def html_response(unicode_iterator, max_age=24 * 60 * 60):
     resp.expires = datetime.datetime.now() + datetime.timedelta(seconds=max_age)
     return resp
 
+class InternalRedirect(Exception):
+    def __init__(self, target, code=301):
+        Exception.__init__(self)
+        self.target = target
+        self.code = code
+
 class Application(object):
     def __init__(self, db):
         self.db = db
@@ -84,17 +91,18 @@ class Application(object):
             elif endpoint == "hash":
                 if args["function"] == "image_sha512":
                     # backwards compatibility
-                    raise RequestRedirect("%s/hash/png_sha512/%s" %
-                                          (request.environ["SCRIPT_NAME"],
-                                           args["hashvalue"]))
+                    raise InternalRedirect("/hash/png_sha512/%s" %
+                                           args["hashvalue"])
                 return self.show_hash(args["function"], args["hashvalue"])
             elif endpoint == "index":
                 if not request.environ["PATH_INFO"]:
-                    raise RequestRedirect(request.environ["SCRIPT_NAME"] + "/")
+                    raise InternalRedirect("/")
                 return html_response(index_template.render(dict(urlroot="")))
             elif endpoint == "source":
                 return self.show_source(args["package"])
             raise NotFound()
+        except InternalRedirect as r:
+            return redirect(request.environ["SCRIPT_NAME"] + r.target, r.code)
         except HTTPException as e:
             return e
 
@@ -214,8 +222,16 @@ class Application(object):
             entries = [dict(package=package, filename=filename, size=size,
                             function=otherfunc)
                        for package, filename, size, otherfunc in fetchiter(cur)]
-        if not entries:
-            raise NotFound()
+            if not entries:
+                # Assumption: '~' serves as an infinite character larger than
+                # any other character in the hash column.
+                cur.execute("SELECT DISTINCT hash.hash FROM hash JOIN function ON hash.fid = function.id WHERE function.name = ? AND hash.hash >= ? AND hash.hash <= ? LIMIT 2;",
+                            (function, hashvalue, hashvalue + '~'))
+                values = cur.fetchall()
+                if len(values) == 1:
+                    raise InternalRedirect("/hash/%s/%s" %
+                                           (function, values[0][0]), 302)
+                raise NotFound()
         params = dict(function=function, hashvalue=hashvalue, entries=entries,
                       urlroot="../..")
         return html_response(hash_template.render(params))