summaryrefslogtreecommitdiff
path: root/obbenchlib/htmlgen.py
diff options
context:
space:
mode:
Diffstat (limited to 'obbenchlib/htmlgen.py')
-rw-r--r--obbenchlib/htmlgen.py232
1 files changed, 232 insertions, 0 deletions
diff --git a/obbenchlib/htmlgen.py b/obbenchlib/htmlgen.py
new file mode 100644
index 0000000..195f2c1
--- /dev/null
+++ b/obbenchlib/htmlgen.py
@@ -0,0 +1,232 @@
+# Copyright 2015 Lars Wirzenius
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# =*= License: GPL-3+ =*=
+
+
+import glob
+import os
+
+import jinja2
+import markdown
+import yaml
+
+import obbenchlib
+
+
+class HtmlGenerator(object):
+
+ def __init__(self):
+ self.statedir = None
+ self.resultdir = None
+ self.spec = None
+
+ def generate_html(self):
+ results = self.load_results()
+
+ env = jinja2.Environment(
+ loader=jinja2.PackageLoader('obbenchlib'),
+ autoescape=lambda foo: True,
+ extensions=['jinja2.ext.autoescape'])
+
+ self.create_html_dir()
+ page_classes = [FrontPage, BenchmarkPage, ProfileData, CssFile]
+ for page_class in page_classes:
+ page = page_class()
+ page.env = env
+ page.results = results
+ page.spec = self.spec
+
+ for filename, data in page.generate():
+ self.write_file(filename, data)
+
+ @property
+ def htmldir(self):
+ return os.path.join(self.statedir, 'html')
+
+ def create_html_dir(self):
+ if not os.path.exists(self.htmldir):
+ os.mkdir(self.htmldir)
+
+ def load_results(self):
+ results = []
+ for filename in glob.glob(os.path.join(self.resultdir, '*.yaml')):
+ with open(filename) as f:
+ results.append(yaml.safe_load(f))
+ return results
+
+ def write_file(self, relative_path, text):
+ filename = os.path.join(self.htmldir, relative_path)
+ with open(filename, 'w') as f:
+ f.write(text)
+
+
+class HtmlPage(object):
+
+ def __init__(self):
+ self.env = None
+ self.results = None
+ self.spec = None
+
+ def format_markdown(self, text):
+ return markdown.markdown(text)
+
+ def get_step_names(self, benchmark):
+ return [step['obnam'] for step in benchmark['steps']]
+
+ def generate(self):
+ raise NotImplementedError()
+
+ def render(self, template_name, variables):
+ template = self.env.get_template(template_name)
+ return template.render(**variables)
+
+
+class FrontPage(HtmlPage):
+
+ def generate(self):
+ variables = {
+ 'description': self.format_markdown(self.spec['description']),
+ 'benchmark_names': [
+ benchmark['name']
+ for benchmark in sorted(self.spec['benchmarks'])
+ ],
+ 'results_table': self.results_table(),
+ }
+ yield 'index.html', self.render('index.j2', variables)
+
+ def results_table(self):
+ table = {}
+ for result in self.results:
+ key = '{commit_timestamp} {commit_id} {run_timestamp}'.format(
+ **result)
+ if key not in table:
+ table[key] = {
+ 'commit_id': result['commit_id'],
+ 'commit_date': result['commit_date'],
+ }
+ table[key][result['benchmark_name']] = self.duration(result)
+
+ return [table[key] for key in sorted(table.keys())]
+
+ def duration(self, result):
+ total = 0
+ for step in result['steps']:
+ for key in step:
+ if key != 'live':
+ total += step[key].get('duration', 0)
+ return total
+
+
+class BenchmarkPage(HtmlPage):
+
+ def generate(self):
+ benchmark_names = [
+ benchmark['name']
+ for benchmark in self.spec['benchmarks']
+ ]
+
+ for benchmark_name in benchmark_names:
+ yield self.generate_benchmark_page(benchmark_name)
+
+ def generate_benchmark_page(self, benchmark_name):
+ benchmark = self.find_benchmark(benchmark_name)
+ table_rows = self.table_rows(benchmark)
+
+ variables = {
+ 'benchmark_name': benchmark_name,
+ 'description': self.format_markdown(
+ benchmark.get('description', '')),
+ 'table_rows': table_rows,
+ 'step_names': self.get_step_names(benchmark),
+ }
+
+ return (
+ '{}.html'.format(benchmark_name),
+ self.render('benchmark.j2', variables)
+ )
+
+ def find_benchmark(self, benchmark_name):
+ for benchmark in self.spec['benchmarks']:
+ if benchmark['name'] == benchmark_name:
+ return benchmark
+ return {}
+
+ def table_rows(self, benchmark):
+ results = self.get_results_for_benchmark(benchmark)
+ step_names = self.get_step_names(benchmark)
+ rows = []
+ for result in results:
+ rows.append(self.table_row(result, step_names))
+ return sorted(rows, key=lambda row: row['commit_timestamp'])
+
+ def get_results_for_benchmark(self, benchmark):
+ return [
+ result
+ for result in self.results
+ if result['benchmark_name'] == benchmark['name']
+ ]
+
+ def table_row(self, result, step_names):
+ row = {
+ 'result_id': result['result_id'],
+ 'commit_timestamp': result['commit_timestamp'],
+ 'commit_date': result['commit_date'],
+ 'commit_id': result['commit_id'],
+ 'total': 0,
+ 'steps': [],
+ }
+ for i, step in enumerate(result['steps']):
+ for step_name in step_names:
+ if step_name in step:
+ row['steps'].append({
+ 'filename_txt': '{}_{}.txt'.format(
+ result['result_id'], i),
+ 'duration': step[step_name]['duration'],
+ })
+ row['total'] += row['steps'][-1]['duration']
+ break
+ return row
+
+
+class ProfileData(HtmlPage):
+
+ def generate(self):
+ for result in self.results:
+ for i, step in enumerate(result['steps']):
+ for operation in step:
+ if 'profile' in step[operation]:
+ yield self.generate_profile_data(
+ result, step, i, operation)
+ yield self.generate_profile_text(
+ result, step, i, operation)
+
+ def generate_profile_data(self, result, step, i, operation):
+ filename = '{}_{}.prof'.format(result['result_id'], i)
+ return filename, step[operation]['profile']
+
+ def generate_profile_text(self, result, step, i, operation):
+ filename = '{}_{}.txt'.format(result['result_id'], i)
+ return filename, step[operation]['profile-text']
+
+
+class CssFile(object):
+
+ def generate(self):
+ filename = os.path.join(
+ os.path.dirname(obbenchlib.__file__), 'obbench.css')
+ with open(filename) as f:
+ data = f.read()
+ yield 'obbench.css', data