first prototype
[~helmut/debian-dedup.git] / webapp.py
1 #!/usr/bin/python
2
3 import sqlite3
4 from wsgiref.simple_server import make_server
5
6 from werkzeug.debug import DebuggedApplication
7 from werkzeug.exceptions import HTTPException, NotFound
8 from werkzeug.routing import Map, Rule
9 from werkzeug.wrappers import Request, Response
10
11 def format_size(size):
12     size = float(size)
13     fmt = "%d B"
14     if size >= 1024:
15         size /= 1024
16         fmt = "%.1f KB"
17     if size >= 1024:
18         size /= 1024
19         fmt = "%.1f MB"
20     if size >= 1024:
21         size /= 1024
22         fmt = "%.1f GB"
23     return fmt % size
24
25 class Application(object):
26     def __init__(self):
27         self.db = sqlite3.connect("test.sqlite3")
28         self.cur = self.db.cursor()
29         self.routingmap = Map([
30             Rule("/<package>", methods=("GET",),
31                  endpoint="package"),
32         ])
33
34     @Request.application
35     def __call__(self, request):
36         mapadapter = self.routingmap.bind_to_environ(request.environ)
37         try:
38             endpoint, args = mapadapter.match()
39             assert endpoint == "package"
40             return self.show_package(args["package"])
41         except HTTPException as e:
42             return e
43
44     def show_package(self, package):
45         self.cur.execute("SELECT version, architecture FROM content WHERE package = ? LIMIT 1;", (package,))
46         row = self.cur.fetchone()
47         if not row:
48             raise NotFound()
49         version, architecture = row
50         self.cur.execute("SELECT count(filename) FROM content WHERE package = ?;", (package,))
51         num_files = self.cur.fetchone()[0]
52         self.cur.execute("SELECT sum(size) FROM content WHERE package = ?;", (package,))
53         total_size = self.cur.fetchone()[0]
54         content = "<p>Version: %s</p><p>Architecture: %s</p>" % (version, architecture)
55         content += "<p>Number of files: %d</p>" % num_files
56         content += "<p>Total size: %s</p>" % format_size(total_size)
57
58         shared = dict()
59         self.cur.execute("SELECT a.filename, a.hash, a.size, b.package FROM content AS a JOIN content AS b ON a.hash = b.hash WHERE a.package = ? AND (a.filename != b.filename OR b.package != ?);", (package, package))
60         for afile, hashval, size, bpkg in self.cur.fetchall():
61             shared.setdefault(bpkg, dict()).setdefault(hashval, (size, set()))[1].add(afile)
62         if shared:
63             sharedstats = []
64             mapping = shared.pop(package, dict())
65             if mapping:
66                 duplicate = sum(len(files) for _, files in mapping.values())
67                 savable = sum(size * (len(files) - 1) for size, files in mapping.values())
68                 sharedstats.append(("self", duplicate, savable))
69             for pkg, mapping in shared.items():
70                 pkglink = '<a href="%s">%s</a>' % (pkg, pkg)
71                 duplicate = sum(len(files) for _, files in mapping.values())
72                 savable = sum(size * len(files) for size, files in mapping.values())
73                 sharedstats.append((pkglink, duplicate, savable))
74             sharedstats.sort(key=lambda row: row[2], reverse=True)
75             content += "<table border='1'><tr><th>package</th><th>files shared</th><th>data shared</th></tr>"
76             for pkg, duplicate, savable in sharedstats:
77                 content += "<tr><td>%s</td><td>%d (%d%%)</td><td>%s (%d%%)</td></tr>" % (pkg, duplicate, 100. * duplicate / num_files, format_size(savable), 100. * savable / total_size)
78             content += "</table>"
79
80         r = Response(content_type="text/html")
81         r.data = "<html><head><title>duplication of %(package)s</title></head><body><h1>%(package)s</h1>%(content)s</body></html>" % dict(package=package, content=content)
82         return r
83
84 def main():
85     app = Application()
86     app = DebuggedApplication(app, evalex=True)
87     make_server("localhost", 8800, app).serve_forever()
88
89 if __name__ == "__main__":
90     main()