Download:
child 300:c9294e82e4f6
parent 298:7a103296a019
299:18a3583a520a
Anton Shestakov <av6@dwimlabs.net>, Wed, 20 Sep 2017 13:44:36 +0800
viewer: basic line chart for checks (errors, warnings, duration)

5 файлов изменено, 137 вставок(+), 1 удалений(-) [+]
candolint/handlers.py file | annotate | diff | comparison | revisions
static/candolint.css file | annotate | diff | comparison | revisions
static/candolint.js file | annotate | diff | comparison | revisions
templates/base.html file | annotate | diff | comparison | revisions
templates/check.html file | annotate | diff | comparison | revisions
--- a/candolint/handlers.py Wed Sep 20 11:25:26 2017 +0800
+++ b/candolint/handlers.py Wed Sep 20 13:44:36 2017 +0800
@@ -159,13 +159,43 @@
.first())
return None
+ def get_chart_data(self, check):
+ checks = (Check
+ .select(*Check.get_light_fields())
+ .join(Change)
+ .where(Check.project == check.project)
+ .where(Change.branch == check.change.branch))
+ more = checks.where(Check.ordinal >= check.ordinal).count()
+ limit = 50
+
+ history = list(reversed(checks
+ .order_by(Check.ordinal.desc())
+ .limit(limit)
+ .offset(max(0, more - limit))))
+ if len(history) > 2:
+ points = [
+ {
+ 'ordinal': c.ordinal,
+ 'errors': c.errors if c.success else None,
+ 'warnings': c.warnings if c.success else None,
+ 'duration': int((c.finished - c.started).total_seconds())
+ } for c in history
+ ]
+ return {
+ 'points': points,
+ 'current': check.ordinal
+ }
+ else:
+ return {}
+
def get_html(self, project, check, lines):
adapter = project.get_adapter()
for line in lines:
if 'filename' in line:
line['link'] = adapter.get_line_url(check.change, line)
reference = self.get_reference_check(check)
- self.render('check.html', project=project, check=check, lines=lines, adapter=adapter, reference=reference)
+ chart = self.get_chart_data(check)
+ self.render('check.html', project=project, check=check, lines=lines, adapter=adapter, reference=reference, chart=chart)
class CompareHandler(BaseHandler):
--- a/static/candolint.css Wed Sep 20 11:25:26 2017 +0800
+++ b/static/candolint.css Wed Sep 20 13:44:36 2017 +0800
@@ -86,6 +86,10 @@
background: #898989;
}
+.chart-container {
+ max-height: 130px;
+}
+
.check-log {
background: #3c3c3c;
color: white;
--- a/static/candolint.js Wed Sep 20 11:25:26 2017 +0800
+++ b/static/candolint.js Wed Sep 20 13:44:36 2017 +0800
@@ -32,3 +32,100 @@
}
});
})(jQuery);
+
+(function($, d3) {
+ if ($('#check-chart-data, #check-chart').length !== 2) { return; }
+ var data = JSON.parse($('#check-chart-data').text());
+ var $container = $('.chart-container');
+ var svg = d3.select('#check-chart');
+ var margin = {top: 10, right: 45, bottom: 30, left: 45};
+ var padding = 8;
+ var maxE = d3.max(data.points, function(d) { return d.errors; });
+ var maxW = d3.max(data.points, function(d) { return d.warnings; });
+ var maxD = d3.max(data.points, function(d) { return d.duration; });
+ var x = d3.scalePoint().domain(data.points.map(function(d) { return d.ordinal; }));
+ var y1 = d3.scaleLinear().domain([0, Math.max(maxE, maxW)]);
+ var y2 = d3.scaleLinear().domain([0, maxD]);
+ var formatDuration = function(d) {
+ return moment.utc(d, 'X').format('m:ss');
+ };
+
+ var render = function() {
+ svg.attr('width', $container.width());
+ svg.attr('height', $container.height());
+ var width = +svg.attr('width') - margin.left - margin.right;
+ var height = +svg.attr('height') - margin.top - margin.bottom;
+ var g = svg.append('g')
+ .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ var cg = g.append('g')
+ .attr('shape-rendering', 'crispEdges');
+
+ x.rangeRound([0, width]);
+ y1.rangeRound([height, 0]);
+ y2.rangeRound([height, 0]);
+
+ cg.append('line')
+ .attr('stroke', '#000')
+ .attr('stroke-dasharray', '5,2')
+ .attr('x1', x(data.current) + 0.5)
+ .attr('y1', 0)
+ .attr('x2', x(data.current) + 0.5)
+ .attr('y2', height);
+
+ var hover = cg.append('line')
+ .attr('opacity', 0)
+ .attr('stroke', '#000')
+ .attr('y1', 0)
+ .attr('y2', height);
+
+ cg.append('g')
+ .attr('transform', 'translate(0,' + (height + padding) + ')')
+ .call(d3.axisBottom(x));
+
+ cg.append('g')
+ .attr('transform', 'translate(' + (-padding) + ',0)')
+ .call(d3.axisLeft(y1).ticks(5));
+
+ cg.append('g')
+ .attr('transform', 'translate(' + (width + padding) + ',0)')
+ .call(d3.axisRight(y2).ticks(5).tickFormat(formatDuration));
+
+ $.each([
+ {name: 'duration', color: '#00a8e6', axis: y2},
+ {name: 'warnings', color: '#faa732', axis: y1},
+ {name: 'errors', color: '#da314b', axis: y1}
+ ], function(i, series) {
+ var sg = g.append('g')
+ .attr('fill', series.color)
+ .attr('stroke', 'white')
+ .attr('stroke-width', 2);
+
+ var line = d3.line()
+ .defined(function(d) { return d[series.name] !== null; })
+ .x(function(d) { return x(d.ordinal); })
+ .y(function(d) { return series.axis(d[series.name]); });
+
+ sg.append('path')
+ .datum(data.points)
+ .attr('fill', 'none')
+ .attr('stroke', series.color)
+ .attr('d', line);
+
+ sg.selectAll('circle')
+ .data(data.points)
+ .enter()
+ .filter(function(d) { return d[series.name] !== null; })
+ .append('circle')
+ .attr('r', 5)
+ .attr('cx', function(d) { return x(d.ordinal); })
+ .attr('cy', function(d) { return series.axis(d[series.name]); });
+ });
+ };
+ $(window).on('resize', function() {
+ if (+svg.attr('width') !== $container.width()) {
+ svg.selectAll('*').remove();
+ render();
+ }
+ });
+ render();
+})(jQuery, d3);
--- a/templates/base.html Wed Sep 20 11:25:26 2017 +0800
+++ b/templates/base.html Wed Sep 20 13:44:36 2017 +0800
@@ -12,6 +12,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js" integrity="sha384-rY/jv8mMhqDabXSo+UCggqKtdmBfd3qC2/KvyTDNQ6PcUJXaxK1tMepoQda4g5vB" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/2.27.4/js/uikit.min.js" integrity="sha384-VbNYNtAJFhyFCXc5McpfbfoDYLd7QRxCrCbF2EgoMKb/GGqJN7fcAazTBKUyP/6m" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js" integrity="sha384-DRe+1gYJauFEenXeWS8TmYdBmDUqnR5Rcw7ax4KTqOxXWd4NAMP2VPU5H69U7yP9" crossorigin="anonymous"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.2/d3.min.js" integrity="sha384-CtB3UHEoz5ZhUjR2nHwH9wcUALiwMBvw2SMSQEhkeUrBarVcEa73BiTAH3HO1GsO" crossorigin="anonymous"></script>
</head>
<body>
<nav class="uk-navbar uk-navbar-attached navbar">
--- a/templates/check.html Wed Sep 20 11:25:26 2017 +0800
+++ b/templates/check.html Wed Sep 20 13:44:36 2017 +0800
@@ -68,6 +68,10 @@
(<a href="{{ project.get_url() }}/{{ check.ordinal }}/compare/{{ reference.ordinal }}">compare</a>).
</p>
{% end %}
+ {% if chart %}
+ <div class="chart-container"><svg id="check-chart"></svg></div>
+ <script type="application/json" id="check-chart-data">{% raw json_encode(chart) %}</script>
+ {% end %}
{% include ui/check-log.html %}
</div>
{% end %}