26:05579dfd3e35
Anton Shestakov <engored@ya.ru>, Wed, 27 May 2015 18:56:20 +0800
viewer: ?local=1 to generate colors for only visible ("local") min/max values

next change 28:f834362f7f83
previous change 25:a98e0e20561c

viewer.py

Permissions: -rwxr-xr-x

Other formats: Feeds:
#!/usr/bin/env python
import colorsys
import json
import logging
import re
import sqlite3
import subprocess
from tornado.ioloop import IOLoop
from tornado.options import define, options
from tornado.web import Application, RequestHandler, URLSpec
from settings import DBPATH, HG, TEMPLATES, TESTHGREPO
from bench import MARKS
define('listen', metavar='IP', default='127.0.0.1')
define('port', metavar='PORT', default=8065, type=int)
define('xheaders', metavar='True|False', default=False, type=bool)
define('debug', metavar='True|False', default=False, type=bool)
def getinfo(revset):
output = subprocess.check_output([HG, 'log', '-R', TESTHGREPO, '-T', 'json', '-r', revset])
return json.loads(output)
class BaseHandler(RequestHandler):
def prepare(self):
self.conn = sqlite3.connect(DBPATH)
super(BaseHandler, self).prepare()
def on_finish(self):
self.conn.close()
class IndexHandler(BaseHandler):
def get(self):
self.redirect('results.html')
class ResultsHandler(BaseHandler):
def get(self, ext):
if ext == 'tsv':
self.results_tsv()
elif ext == 'asc':
self.results_asc()
elif ext == 'html':
self.results_html()
def getchangesets(self):
rev = self.get_argument('rev', 'tip')
revcount = 120
return getinfo('first({}:0, {})'.format(rev, revcount))
def getlimits(self, changesets=None):
query = 'SELECT MIN(time), MAX(time) FROM results WHERE mark = ? AND cache = ?'
extra = []
if changesets:
query += ' AND node IN (' + ','.join('?' * len(changesets)) + ')'
extra = [cset['node'] for cset in changesets]
return {
mark:
self.conn.execute(query, [mark, False] + extra).fetchone()
+
self.conn.execute(query, [mark, True] + extra).fetchone()
for mark in MARKS
}
def getresults(self, changesets, local=False):
results = {}
limits = self.getlimits(changesets if local else None)
resultsq = self.conn.execute(
'SELECT node, mark, time, cache FROM results'
' WHERE node IN (' + ','.join('?' * len(changesets)) + ')',
[cset['node'] for cset in changesets])
for node, mark, time, cache in resultsq:
if mark not in MARKS:
continue
results.setdefault(node, {}).setdefault(mark, [None, None, None, None])
if not cache:
color = green_to_red(limits[mark][0:2], time) if time is not None else None
results[node][mark][0:2] = [time, color]
else:
color = green_to_red(limits[mark][2:4], time) if time is not None else None
results[node][mark][2:4] = [time, color]
return results
def results_tsv(self):
self.set_header('Content-Type', 'text/plain; charset=UTF-8')
changesets = self.getchangesets()
results = self.getresults(changesets)
self.write('rev\tnode')
for mark in MARKS:
self.write('\t{0} (without cache)\t{0} (with cache)'.format(mark))
self.write('\n')
for cset in changesets:
self.write('{rev}\t{node}'.format(**cset))
for mark in MARKS:
marks = results.get(cset['node'], {}).get(mark, ('', '', ''))
self.write('\t{0}\t{2}'.format(*marks))
self.write('\n')
self.finish()
def results_asc(self):
self.set_header('Content-Type', 'text/plain; charset=UTF-8')
rev = self.get_argument('rev', 'tip')
revcount = 120
regex = re.compile(r'^[-\\|/+ ]+([0-9a-f]{40})$')
template = '{rev}:{node|short} {tags} {date|isodate} {author|user} {desc|firstline|strip}\n {node}\n\n'
revset = 'first({}:0, {})'.format(rev, revcount)
cmd = [HG, 'log', '-R', TESTHGREPO, '-G', '-T', template, '-r', revset]
graph = subprocess.Popen(cmd, stdout=subprocess.PIPE)
while graph.poll() is None:
line = graph.stdout.readline()
matches = regex.findall(line)
for match in matches:
msg = []
rows = self.conn.execute(
'SELECT mark, time, cache FROM results WHERE node = ?',
(match,))
matchtimes = {}
for mark, time, cache in rows:
matchtimes.setdefault(mark, [None, None])
matchtimes[mark][1 if cache else 0] = time
for mark in MARKS:
msg.append('{mark}: {0:.2f}s/{1:.2f}s'.format(*matchtimes[mark], mark=mark))
line = line.replace(match, ' '.join(msg))
self.write(line)
self.finish()
def results_html(self):
changesets = self.getchangesets()
results = self.getresults(changesets, self.get_argument('local', False))
self.render('results.html', changesets=changesets, marks=MARKS, results=results)
def green_to_red((low, high), value):
hue = (value - low) / (high - low) if high != low else 0.5
r, g, b = colorsys.hsv_to_rgb((1 - hue) * 0.3, 1, 1)
return (int(r * 255), int(g * 255), int(b * 255))
class Viewer(Application):
def __init__(self):
handlers = [
URLSpec(r'/', IndexHandler),
URLSpec(r'/results\.(html|tsv|asc)', ResultsHandler),
]
settings = dict(
template_path=TEMPLATES,
debug=options.debug
)
super(Viewer, self).__init__(handlers, **settings)
def listen(self, port, address='', **kwargs):
logging.info('{} is serving on {}:{}'.format(self.__class__.__name__, address, port))
super(Viewer, self).listen(port, address, **kwargs)
def main():
options.parse_command_line()
application = Viewer()
application.listen(options.port, address=options.listen, xheaders=options.xheaders)
IOLoop.instance().start()
if __name__ == '__main__':
main()