Merge branch '8784-dir-listings'
[arvados.git] / tools / crunchstat-summary / crunchstat_summary / chartjs.py
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 from __future__ import print_function
6
7 import cgi
8 import json
9 import math
10 import pkg_resources
11
12 from crunchstat_summary import logger
13
14
15 class ChartJS(object):
16     JSLIB = 'https://cdnjs.cloudflare.com/ajax/libs/canvasjs/1.7.0/canvasjs.min.js'
17
18     def __init__(self, label, summarizers):
19         self.label = label
20         self.summarizers = summarizers
21
22     def html(self):
23         return '''<!doctype html><html><head>
24         <title>{} stats</title>
25         <script type="text/javascript" src="{}"></script>
26         <script type="text/javascript">{}</script>
27         </head><body></body></html>
28         '''.format(cgi.escape(self.label), self.JSLIB, self.js())
29
30     def js(self):
31         return 'var sections = {};\n{}'.format(
32             json.dumps(self.sections()),
33             pkg_resources.resource_string('crunchstat_summary', 'chartjs.js'))
34
35     def sections(self):
36         return [
37             {
38                 'label': s.long_label(),
39                 'charts': self.charts(s.label, s.tasks),
40             }
41             for s in self.summarizers]
42
43     def _axisY(self, tasks, stat):
44         ymax = 1
45         for task in tasks.itervalues():
46             for pt in task.series[stat]:
47                 ymax = max(ymax, pt[1])
48         ytick = math.exp((1+math.floor(math.log(ymax, 2)))*math.log(2))/4
49         return {
50             'gridColor': '#cccccc',
51             'gridThickness': 1,
52             'interval': ytick,
53             'minimum': 0,
54             'maximum': ymax,
55             'valueFormatString': "''",
56         }
57
58     def charts(self, label, tasks):
59         return [
60             {
61                 'axisY': self._axisY(tasks=tasks, stat=stat),
62                 'data': [
63                     {
64                         'type': 'line',
65                         'markerType': 'none',
66                         'dataPoints': self._datapoints(
67                             label=uuid, task=task, series=task.series[stat]),
68                     }
69                     for uuid, task in tasks.iteritems()
70                 ],
71                 'title': {
72                     'text': '{}: {} {}'.format(label, stat[0], stat[1]),
73                 },
74                 'zoomEnabled': True,
75             }
76             for stat in (('cpu', 'user+sys__rate'),
77                          ('mem', 'rss'),
78                          ('net:eth0', 'tx+rx__rate'),
79                          ('net:keep0', 'tx+rx__rate'))]
80
81     def _datapoints(self, label, task, series):
82         points = [
83             {'x': pt[0].total_seconds(), 'y': pt[1]}
84             for pt in series]
85         if len(points) > 0:
86             points[-1]['markerType'] = 'cross'
87             points[-1]['markerSize'] = 12
88         return points