--- a/candolint/handlers.py Wed Sep 20 11:25:26 2017 +0800
+++ b/candolint/handlers.py Wed Sep 20 13:44:36 2017 +0800
+ def get_chart_data(self, check):
+ .select(*Check.get_light_fields())
+ .where(Check.project == check.project)
+ .where(Change.branch == check.change.branch))
+ more = checks.where(Check.ordinal >= check.ordinal).count()
+ history = list(reversed(checks
+ .order_by(Check.ordinal.desc())
+ .offset(max(0, more - limit))))
+ 'errors': c.errors if c.success else None,
+ 'warnings': c.warnings if c.success else None,
+ 'duration': int((c.finished - c.started).total_seconds())
+ 'current': check.ordinal
def get_html(self, project, check, lines):
adapter = project.get_adapter()
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
--- a/static/candolint.js Wed Sep 20 11:25:26 2017 +0800
+++ b/static/candolint.js Wed Sep 20 13:44:36 2017 +0800
+ 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 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 + ')');
+ .attr('shape-rendering', 'crispEdges');
+ x.rangeRound([0, width]);
+ y1.rangeRound([height, 0]);
+ y2.rangeRound([height, 0]);
+ .attr('stroke', '#000')
+ .attr('stroke-dasharray', '5,2')
+ .attr('x1', x(data.current) + 0.5)
+ .attr('x2', x(data.current) + 0.5)
+ var hover = cg.append('line')
+ .attr('stroke', '#000')
+ .attr('transform', 'translate(0,' + (height + padding) + ')')
+ .call(d3.axisBottom(x));
+ .attr('transform', 'translate(' + (-padding) + ',0)')
+ .call(d3.axisLeft(y1).ticks(5));
+ .attr('transform', 'translate(' + (width + padding) + ',0)')
+ .call(d3.axisRight(y2).ticks(5).tickFormat(formatDuration));
+ {name: 'duration', color: '#00a8e6', axis: y2},
+ {name: 'warnings', color: '#faa732', axis: y1},
+ {name: 'errors', color: '#da314b', axis: y1}
+ ], function(i, series) {
+ .attr('fill', series.color)
+ .attr('stroke', 'white')
+ .attr('stroke-width', 2);
+ .defined(function(d) { return d[series.name] !== null; })
+ .x(function(d) { return x(d.ordinal); })
+ .y(function(d) { return series.axis(d[series.name]); });
+ .attr('stroke', series.color)
+ .filter(function(d) { return d[series.name] !== null; })
+ .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();
--- a/templates/base.html Wed Sep 20 11:25:26 2017 +0800
+++ b/templates/base.html Wed Sep 20 13:44:36 2017 +0800
<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>
<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
(<a href="{{ project.get_url() }}/{{ check.ordinal }}/compare/{{ reference.ordinal }}">compare</a>).
+ <div class="chart-container"><svg id="check-chart"></svg></div>
+ <script type="application/json" id="check-chart-data">{% raw json_encode(chart) %}</script>
{% include ui/check-log.html %}