warning-ui/ui.py
author Benjamin Smedberg <benjamin@smedbergs.us>
Fri, 19 Dec 2008 16:50:47 -0500
changeset 8 c33d0e330649de0393a2f86a529001f83b1010a4
child 17 47ddc2da18a6dc676951cdb2d85552e898d32ee3
permissions -rw-r--r--
Add a web frontend to the warning database: cherrypy+genshi FTW!

import cherrypy, os, sys, re, sqlite3
from genshi.template import TemplateLoader

class Root(object):
    def __init__(self):
        self.loader = TemplateLoader(cherrypy.config['tools.staticdir.root'],
                                     auto_reload=True)

    def dbcursor(self):
        return sqlite3.connect(cherrypy.request.app.config['database']['path']).cursor()

    def render(self, tmpl, **kwargs):
        cherrypy.response.headers['Content-Type'] = 'application/xhtml+xml'
        tmpl = self.loader.load(tmpl)
        return tmpl.generate(**kwargs).render('xhtml', doctype='xhtml')

    @cherrypy.expose
    def index(self):
        try:
            cur = self.dbcursor()
            cur.execute('''SELECT builds.buildnumber, builds.rev, uwarnings.ucount
                           FROM builds LEFT OUTER JOIN
                             (SELECT buildnumber, count(*) AS ucount
                              FROM warnings
                              GROUP BY buildnumber) AS uwarnings
                           ON builds.buildnumber = uwarnings.buildnumber
                           ORDER BY builds.buildnumber DESC''')
            return self.render('index.html', builds=cur.fetchall())
        finally:
            cur.close()

    @cherrypy.expose
    def search(self, id, user="", path="", msg=""):
        id = int(id)
        user = user.strip()
        path = path.strip()
        msg = msg.strip()
        
        bindp = [id]

        subclauses = []
        if user != '':
            subclauses.append('AND wl2.blamewho LIKE ?')
            bindp.append(user)

        if path != '':
            subclauses.append('AND wl2.file LIKE ?')
            bindp.append(path)

        if msg != '':
            subclauses.append('AND wl2.msg LIKE ?')
            bindp.append(msg)

        if len(subclauses):
            clause = '''AND EXISTS (SELECT *
                                    FROM wlines AS wl2
                                    WHERE
                                      wl2.buildnumber = warnings.buildnumber AND
                                      wl2.signature = warnings.signature %s)''' % ' '.join(subclauses)
        else:
            clause = ''

        try:
            cur = self.dbcursor()
            q = '''SELECT warnings.signature, file, lineno, msg
                   FROM warnings, wlines
                   WHERE
                     warnings.buildnumber = wlines.buildnumber AND
                     warnings.signature = wlines.signature AND
                     wlines.wline = 0 AND
                     warnings.buildnumber = ? %s''' % (clause, )
            print "Query: %s\nBind: %s" % (q, bindp)

            cur.execute(q, bindp)

            return self.render('search.html',
                               id=id,
                               user=user,
                               path=path,
                               msg=msg,
                               results=cur.fetchall())
        finally:
            cur.close()

    @cherrypy.expose
    def build(self, id):
        id = int(id)
        try:
            cur = self.dbcursor()
            cur.execute('''SELECT rev
                           FROM builds
                           WHERE buildnumber = ?''', (id,))
            rev, = cur.fetchone()

            cur.execute('''SELECT count(*) as ucount, sum(count) AS tcount
                           FROM warnings
                           WHERE buildnumber = ?''', (id,))
            unique, total = cur.fetchone()

            cur.execute('''SELECT buildnumber, rev
                           FROM builds
                           WHERE
                             buildnumber < ?
                           ORDER BY buildnumber DESC
                           LIMIT 1''', (id,))
            prev = cur.fetchone()
            if prev is None:
                previd = None
                prevrev = None
                newwarnings = None
                fixedwarnings = None
            else:
                previd, prevrev = prev

                cur.execute('''SELECT warnings.signature, file, lineno, msg
                               FROM warnings, wlines
                               WHERE
                                 warnings.buildnumber = wlines.buildnumber AND
                                 warnings.signature = wlines.signature AND
                                 wlines.wline = 0 AND
                                 warnings.buildnumber = ? AND
                                 NOT EXISTS (SELECT *
                                             FROM warnings AS oldwarnings
                                             WHERE oldwarnings.signature = warnings.signature AND
                                             oldwarnings.buildnumber = ?)''', (id, previd))
                newwarnings = cur.fetchall()

                cur.execute('''SELECT warnings.signature, file, lineno, msg
                               FROM warnings, wlines
                               WHERE
                                 warnings.buildnumber = wlines.buildnumber AND
                                 warnings.signature = wlines.signature AND
                                 wlines.wline = 0 AND
                                 warnings.buildnumber = ? AND
                                 NOT EXISTS (SELECT *
                                             FROM warnings AS oldwarnings
                                             WHERE oldwarnings.signature = warnings.signature AND
                                             oldwarnings.buildnumber = ?)''', (previd, id))
                fixedwarnings = cur.fetchall()
                
            return self.render('build.html',
                               rev=rev,
                               id=id,
                               unique=unique,
                               total=total,
                               previd=previd,
                               prevrev=prevrev,
                               newwarnings=newwarnings,
                               fixedwarnings=fixedwarnings)
        finally:
            cur.close()

    @cherrypy.expose
    def warning(self, signature):
        try:
            cur = self.dbcursor()
            cur.execute('''SELECT buildnumber, rev
                           FROM builds
                           WHERE
                             EXISTS (SELECT *
                                     FROM warnings
                                     WHERE
                                       warnings.buildnumber = builds.buildnumber AND
                                       warnings.signature = ?)
                           ORDER BY buildnumber ASC LIMIT 1''', (signature,))

            firstid, firstrev = cur.fetchone()
            
            cur.execute('''SELECT buildnumber, rev
                           FROM builds
                           WHERE
                             NOT EXISTS (SELECT *
                                         FROM warnings
                                         WHERE
                                           warnings.buildnumber = builds.buildnumber AND
                                           warnings.signature = ?) AND
                             buildnumber > ?
                           ORDER BY buildnumber ASC LIMIT 1''', (signature, firstid))

            lastid, lastrev = cur.fetchone() or (None, None)

            cur.execute('''SELECT file, lineno, msg, blametype, blamefile, blamerev, blameline, blamewho
                           FROM wlines
                           WHERE
                             buildnumber = ? AND
                             signature = ?
                           ORDER BY wline ASC''', (firstid, signature))

            wlines = cur.fetchall()

            return self.render('warning.html',
                               firstid=firstid, firstrev=firstrev,
                               lastid=lastid, lastrev=lastrev,
                               wlines=wlines)
        finally:
            cur.close()

def main(configfiles):
    for cf in configfiles:
        cherrypy.config.update(cf)
    if 'tools.staticdir.root' not in cherrypy.config:
        thisdir = os.path.abspath(os.path.dirname(__file__))
        cherrypy.config.update({'tools.staticdir.root': thisdir})

    app = cherrypy.tree.mount(Root(), '/', {
        'global': {
            'tools.encode.on': True,
            'tools.encode.encoding': 'utf-8',
        },
        '/static': {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': 'static',
        },
    })
    for cf in configfiles:
        app.merge(cf)

    cherrypy.server.quickstart()
    cherrypy.engine.start()

if __name__ == '__main__':
    main(sys.argv[1:])