Skip to content

Commit 325a1fd

Browse files
committed
Display quartiles and extrema as bands on timeline
1 parent 11bbaea commit 325a1fd

File tree

4 files changed

+150
-22
lines changed

4 files changed

+150
-22
lines changed

codespeed/settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,5 @@
6666
# COMP_EXECUTABLES = [
6767
# ('myexe', '21df2423ra'),
6868
# ('myexe', 'L'),]
69+
70+
USE_MEDIAN_BANDS = True # True to enable median bands on Timeline view

codespeed/static/js/timeline.js

Lines changed: 90 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,34 @@ function getColor(exe_id) {
2424
.data('color');
2525
}
2626

27+
function scaleColorAlpha(color, scale) {
28+
var c = $.jqplot.getColorComponents(color);
29+
c[3] = c[3] * scale;
30+
return 'rgba(' + c[0] +', '+ c[1] +', '+ c[2] +', '+ c[3] + ')';
31+
}
32+
2733
function shouldPlotEquidistant() {
2834
return $("#equidistant").is(':checked');
2935
}
3036

37+
function shouldPlotQuartiles() {
38+
return $("#show_quartile_bands").is(':checked');
39+
}
40+
41+
function shouldPlotExtrema() {
42+
return $("#show_extrema_bands").is(':checked');
43+
}
44+
3145
function getConfiguration() {
3246
var config = {
3347
exe: readCheckbox("input[name='executable']:checked"),
3448
base: $("#baseline option:selected").val(),
3549
ben: $("input[name='benchmark']:checked").val(),
3650
env: $("input[name='environments']:checked").val(),
3751
revs: $("#revisions option:selected").val(),
38-
equid: $("#equidistant").is(':checked') ? "on" : "off"
52+
equid: $("#equidistant").is(':checked') ? "on" : "off",
53+
quarts: $("#show_quartile_bands").is(':checked') ? "on" : "off",
54+
extr: $("#show_extrema_bands").is(':checked') ? "on" : "off"
3955
};
4056

4157
var branch = readCheckbox("input[name='branch']:checked");
@@ -53,25 +69,89 @@ function permalinkToChanges(commitid, executableid, environment) {
5369
function OnMarkerClickHandler(ev, gridpos, datapos, neighbor, plot) {
5470
if($("input[name='benchmark']:checked").val() === "grid") { return false; }
5571
if (neighbor) {
56-
var commitid = neighbor.data[3];
72+
var commitid = neighbor.data[neighbor.data.length-2];
5773
// Get executable ID from the seriesindex array
5874
var executableid = seriesindex[neighbor.seriesIndex];
5975
var environment = $("input[name='environments']:checked").val();
6076
permalinkToChanges(commitid, executableid, environment);
6177
}
6278
}
6379

80+
function getHighlighterConfig(median) {
81+
if (median) {
82+
return {
83+
show: true,
84+
tooltipLocation: 'nw',
85+
yvalues: 7,
86+
formatString:'<table class="jqplot-highlighter"> <tr><td>date:</td><td>%s</td></tr> <tr><td>median:</td><td>%s</td></tr> <tr><td>max:</td><td>%s</td></tr> <tr><td>Q3:</td><td>%s</td></tr> <tr><td>Q1:</td><td>%s</td></tr> <tr><td>min:</td><td>%s</td></tr> <tr><td>commit:</td><td>%s</td></tr></table>'
87+
};
88+
} else {
89+
return {
90+
show: true,
91+
tooltipLocation: 'nw',
92+
yvalues: 4,
93+
formatString:'<table class="jqplot-highlighter"> <tr><td>date:</td><td>%s</td></tr> <tr><td>result:</td><td>%s</td></tr> <tr><td>std dev:</td><td>%s</td></tr> <tr><td>commit:</td><td>%s</td></tr></table>'
94+
};
95+
}
96+
}
97+
6498
function renderPlot(data) {
6599
var plotdata = [],
66100
series = [],
67101
lastvalues = [];//hopefully the smallest values for determining significant digits.
68102
seriesindex = [];
103+
var hiddenSeries = 0;
104+
var median = data['data_type'] === 'M';
69105
for (var branch in data.branches) {
70106
// NOTE: Currently, only the "default" branch is shown in the timeline
71107
for (var exe_id in data.branches[branch]) {
72108
// FIXME if (branch !== "default") { label += " - " + branch; }
73109
var label = $("label[for*='executable" + exe_id + "']").html();
74-
series.push({"label": label, "color": getColor(exe_id)});
110+
var seriesConfig = {
111+
label: label,
112+
color: getColor(exe_id)
113+
};
114+
if (median) {
115+
$("span.options.median").css("display", "inline");
116+
var mins = new Array();
117+
var maxes = new Array();
118+
var q1s = new Array();
119+
var q3s = new Array();
120+
for (res in data["branches"][branch][exe_id]) {
121+
var date = data["branches"][branch][exe_id][res][0];
122+
var value = data["branches"][branch][exe_id][res][1];
123+
var max = data["branches"][branch][exe_id][res][2];
124+
var q3 = data["branches"][branch][exe_id][res][3];
125+
var q1 = data["branches"][branch][exe_id][res][4];
126+
var min = data["branches"][branch][exe_id][res][5];
127+
if (min !== "")
128+
mins.push([date, min]);
129+
if (max !== "")
130+
maxes.push([date, max]);
131+
if (q1 !== "")
132+
q1s.push([date, q1]);
133+
if (q3 !== "")
134+
q3s.push([date, q3]);
135+
}
136+
var extrema = new Array(mins, maxes);
137+
var quartiles = new Array(q1s, q3s);
138+
if (shouldPlotQuartiles()) {
139+
seriesConfig['rendererOptions'] = {bandData: quartiles};
140+
} else if (shouldPlotExtrema()) {
141+
seriesConfig['rendererOptions'] = {bandData: extrema};
142+
}
143+
if (shouldPlotQuartiles() && shouldPlotExtrema()) {
144+
series.push({
145+
showLabel: false,
146+
showMarker: false,
147+
color: scaleColorAlpha(getColor(exe_id), 0.6),
148+
rendererOptions: {bandData: extrema}
149+
});
150+
plotdata.push(data.branches[branch][exe_id]);
151+
hiddenSeries++;
152+
}
153+
}
154+
series.push(seriesConfig);
75155
seriesindex.push(exe_id);
76156
plotdata.push(data.branches[branch][exe_id]);
77157
lastvalues.push(data.branches[branch][exe_id][0][1]);
@@ -121,15 +201,10 @@ function renderPlot(data) {
121201
}
122202
},
123203
legend: {show: true, location: 'nw'},
124-
highlighter: {
125-
show: true,
126-
tooltipLocation: 'nw',
127-
yvalues: 4,
128-
formatString:'<table class="jqplot-highlighter"> <tr><td>date:</td><td>%s</td></tr> <tr><td>result:</td><td>%s</td></tr> <tr><td>std dev:</td><td>%s</td></tr> <tr><td>commit:</td><td>%s</td></tr></table>'
129-
},
204+
highlighter: getHighlighterConfig(median),
130205
cursor:{show:true, zoom:true, showTooltip:false, clickReset:true}
131206
};
132-
if (series.length > 4) {
207+
if (series.length > 4 + hiddenSeries) {
133208
// Move legend outside plot area to unclutter
134209
var labels = [];
135210
for (var l in series) {
@@ -193,6 +268,7 @@ function renderMiniplot(plotid, data) {
193268
function render(data) {
194269
$("#revisions").attr("disabled", false);
195270
$("#equidistant").attr("disabled", false);
271+
$("span.options.median").css("display", "none");
196272
$("#plotgrid").html("");
197273
if(data.error !== "None") {
198274
var h = $("#content").height();//get height for error message
@@ -254,6 +330,8 @@ function initializeSite(event) {
254330
$("input[name='benchmark']" ).change(updateUrl);
255331
$("input[name='environments']").change(updateUrl);
256332
$("#equidistant" ).change(updateUrl);
333+
$("#show_quartile_bands" ).change(updateUrl);
334+
$("#show_extrema_bands" ).change(updateUrl);
257335
}
258336

259337
function refreshSite(event) {
@@ -307,6 +385,8 @@ function setValuesOfInputFields(event) {
307385

308386
$("#baselinecolor").css("background-color", baselineColor);
309387
$("#equidistant").prop('checked', valueOrDefault(event.parameters.equid, defaults.equidistant) === "on");
388+
$("#show_quartile_bands").prop('checked', valueOrDefault(event.parameters.quarts, defaults.quartiles) === "on");
389+
$("#show_extrema_bands").prop('checked', valueOrDefault(event.parameters.extr, defaults.extrema) === "on");
310390
}
311391

312392
function init(def) {

codespeed/templates/codespeed/timeline.html

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,16 @@
8383
<input id="equidistant" name="equidistant" type="checkbox" />
8484
<label for="equidistant">Equidistant</label>
8585
</span>
86+
{% if use_median_bands %}
87+
<span class="options median" title="Shows quartile bands in the plots" style="display: none">
88+
<input id="show_quartile_bands" type="checkbox" name="show_quartile_bands" checked="checked"/>
89+
<label for="show_quartile_bands">Show quartile bands</label>
90+
</span>
91+
<span class="options median" title="Shows extrema bands in the plots" style="display: none">
92+
<input id="show_extrema_bands" type="checkbox" name="show_extrema_bands" checked="checked"/>
93+
<label for="show_extrema_bands">Show extrema bands</label>
94+
</span>
95+
{% endif %}
8696
<a id="permalink" href="#">Permalink</a>
8797
</div>
8898
<div id="content" class="clearfix">
@@ -115,7 +125,9 @@
115125
branches: [{% for b in branch_list %}"{{ branch }}", {% endfor %}],
116126
benchmark: "{{ defaultbenchmark }}",
117127
environment: {{ defaultenvironment.id }},
118-
equidistant: "{{ defaultequid }}"
128+
equidistant: "{{ defaultequid }}",
129+
quartiles: "{{ defaultquarts }}",
130+
extrema: "{{ defaultextr }}"
119131
});
120132
});
121133
</script>

codespeed/views.py

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ def gettimelinedata(request):
257257
'benchmark': bench.name,
258258
'benchmark_id': bench.id,
259259
'benchmark_description': bench.description,
260+
'data_type': bench.data_type,
260261
'units': bench.units,
261262
'lessisbetter': lessisbetter,
262263
'branches': {},
@@ -288,16 +289,37 @@ def gettimelinedata(request):
288289

289290
results = []
290291
for res in resultquery:
291-
std_dev = ""
292-
if res.std_dev is not None:
293-
std_dev = res.std_dev
294-
results.append(
295-
[
296-
res.revision.date.strftime('%Y/%m/%d %H:%M:%S %z'),
297-
res.value, std_dev,
298-
res.revision.get_short_commitid(), branch
299-
]
300-
)
292+
if bench.data_type == 'M':
293+
val_min = ""
294+
if res.val_min is not None:
295+
val_min = res.val_min
296+
val_max = ""
297+
if res.val_max is not None:
298+
val_max = res.val_max
299+
q1 = ""
300+
if res.q1 is not None:
301+
q1 = res.q1
302+
q3 = ""
303+
if res.q3 is not None:
304+
q3 = res.q3
305+
results.append(
306+
[
307+
res.revision.date.strftime('%Y/%m/%d %H:%M:%S %z'),
308+
res.value, val_max, q3, q1, val_min,
309+
res.revision.get_short_commitid(), branch
310+
]
311+
)
312+
else:
313+
std_dev = ""
314+
if res.std_dev is not None:
315+
std_dev = res.std_dev
316+
results.append(
317+
[
318+
res.revision.date.strftime('%Y/%m/%d %H:%M:%S %z'),
319+
res.value, std_dev,
320+
res.revision.get_short_commitid(), branch
321+
]
322+
)
301323
timeline['branches'][branch][executable] = results
302324
append = True
303325
if baselinerev is not None and append:
@@ -424,11 +446,20 @@ def timeline(request):
424446
defaultequid = data['equid']
425447
else:
426448
defaultequid = "off"
449+
if 'quarts' in data:
450+
defaultquarts = data['quarts']
451+
else:
452+
defaultquarts = "on"
453+
if 'extr' in data:
454+
defaultextr = data['extr']
455+
else:
456+
defaultextr = "on"
427457

428458
# Information for template
429459
executables = {}
430460
for proj in Project.objects.filter(track=True):
431461
executables[proj] = Executable.objects.filter(project=proj)
462+
use_median_bands = hasattr(settings, 'USE_MEDIAN_BANDS') and settings.USE_MEDIAN_BANDS
432463
return render_to_response('codespeed/timeline.html', {
433464
'checkedexecutables': checkedexecutables,
434465
'defaultbaseline': defaultbaseline,
@@ -442,7 +473,10 @@ def timeline(request):
442473
'environments': enviros,
443474
'branch_list': branch_list,
444475
'defaultbranch': defaultbranch,
445-
'defaultequid': defaultequid
476+
'defaultequid': defaultequid,
477+
'defaultquarts': defaultquarts,
478+
'defaultextr': defaultextr,
479+
'use_median_bands': use_median_bands,
446480
}, context_instance=RequestContext(request))
447481

448482

0 commit comments

Comments
 (0)