[Fuego] [PATCH] flot: rewrite of the flot plugin web application
Daniel Sangorrin
daniel.sangorrin at toshiba.co.jp
Mon Apr 17 02:22:27 UTC 2017
The new version is able to display more data which is the
first step for the creation of a common output format
and HTML displaying.
Things that have changed:
- Reference values are displayed in the tooltips only, not
in the graph, to avoid clutter.
- There is one checkbox for each data series, instead of
dividing them into boards and firmware versions. This is
much easier to use.
- There is more information in the new tooltips.
- Renames most variables to names that make sense for
maintainability.
- There is only a single json file, not three like before.
Signed-off-by: Daniel Sangorrin <daniel.sangorrin at toshiba.co.jp>
---
.../src/main/webapp/flot/mod.js | 445 +++++++++------------
.../src/main/webapp/flot/my.css | 21 +-
2 files changed, 205 insertions(+), 261 deletions(-)
diff --git a/frontend-install/plugins/flot-plotter-plugin/src/main/webapp/flot/mod.js b/frontend-install/plugins/flot-plotter-plugin/src/main/webapp/flot/mod.js
index 2ff5343..1c4cc3a 100644
--- a/frontend-install/plugins/flot-plotter-plugin/src/main/webapp/flot/mod.js
+++ b/frontend-install/plugins/flot-plotter-plugin/src/main/webapp/flot/mod.js
@@ -23,288 +23,213 @@
jQuery.noConflict();
jQuery(document).ready(function () {
- var localurl = jQuery(location).attr('href').split("/");
- var pathname = jQuery(location).attr('pathname').split("/");
+// Jenkins logs path
+var jenkins_logs_path = 'http://'+location['host'] + '/fuego/userContent/fuego.logs/';
- var prefix = pathname[1];
- var i = 2;
- while (pathname[i] != "view" && pathname[i] != "job") {
- prefix = prefix + '/' + pathname[i];
- i++;
- }
-
- var jenurl = 'http://'+'/'+location['host'] + '/' + prefix +'/userContent/fuego.logs/';
-
- // testname is currently: (<board>.<testplan>.Benchmark.<testsuite>)
- var testname = localurl[localurl.length - 2],
- testsuite = testname.split(".")[3],
- metrics = [],
- tests = [],
- plots = [],
- fws = [],
- devices = [],
- glob_suffix = '**',
- fw_ver_len = 12,
- fw_ver_prefix_len = fw_ver_len - glob_suffix.length;
+// get the test name from the URL
+var localurl = jQuery(location).attr('href').split("/");
+var testsuite = localurl[localurl.length - 2].split(".")[3] // E.g.: Dhrystone
- var options = {
- lines: { show: true , lineWidth:1.2 },
- points: { show: true },
- xaxis: {},
- yaxis: {},
- grid: { hoverable: true, clickable: true, backgroundColor: "#f5f5f5", borderWidth: 0.5 },
- pan: { interactive: true },
- zoom: { interactive: true },
- legend: { position: 'nw', noColumns:2, container: null },
- colors: ["#008f00", "#73fa79", "#009193", "#73fcd6", "#ff9300", "#ffd479", "#942193", "#d783ff", "#424242", "#a9a9a9", "#011993", "#76d6ff", "#929000", "#fffc79", "#941100", "#ff7e79"]
- };
- var options_f = {
- lines: { show: true, lineWidth:1.0 },
- points: { show: false },
- grid: { backgroundColor: "#f0f0f0", borderWidth: 0.5 },
- legend: { show: false },
- selection: { mode: "x", color: "blue" },
- // colors: ["#0000ff", "#008000", "#00bfbf", "#148f8f", "#bf00bf", "#bfbf00", "#000", "#3cff00"]
- colors: ["#008f00", "#73fa79", "#009193", "#73fcd6", "#ff9300", "#ffd479", "#942193", "#d783ff", "#424242", "#a9a9a9", "#011993", "#76d6ff", "#929000", "#fffc79", "#941100", "#ff7e79"]
- };
+// results.json file
+var results_json = null;
+var plots = [];
-function getSuitesInfo(series) {
- if (testsuite in series) {
- metrics = series[testsuite];
- for (var i=0;i<metrics.length;i++) {
- jQuery('.plots').append('<div class="container"><div class="area_header">'+testsuite+' / '+metrics[i]+'</div>'+
- '<div class="cont2"><div style="width:800px;height:200px;float:right" id="ph'+i+'"></div><p></p><div style="width:800px;height:70px;float:right" id="phf'+i+'"></div></div>'+
- '<div class="cont3">Legend:<div id="phl'+i+'"></div><br/>'+
- '<div class="devices"><input type="checkbox" name="all_dev" checked="checked" id="all_dev_'+i+'"><label for="all_dev">All devices:</label><br/><div id="pht'+i+'"></div></div>'+
- '<div class="firmware"><input type="checkbox" name="all_fw" checked="checked" id="all_fw_'+i+'"><label for="all_fw">All firmware:</label><br/><div id="phfw'+i+'"></div>'+
- '</div></div>');
- }
- }
- else {
- jQuery('.plots').append('<div class="container"><div class="area_header">'+testsuite+':'+ 'No data (check metrics.json file)' +'</div>'
- +'</div></div>');
+// hidden tooltip element
+jQuery('<div id="tooltip"></div>').css( {
+ position: 'absolute',
+ display: 'none',
+ border: '1px solid #fdd',
+ padding: '2px',
+ 'background-color': '#ffe',
+ opacity:0.90
+}).appendTo("body").fadeIn(200);
+
+function show_upperplot_tooltip(event, pos, item) {
+ if (item) {
+ var x = item.datapoint[0].toFixed(2);
+ var y = item.datapoint[1].toFixed(2);
+ var info = results_json[item.series.label];
+ jQuery("#tooltip").html("value: " + info['data'][item.dataIndex] + "<br \>" +
+ "ref: " + info['ref'][item.dataIndex] + "<br \>" +
+ "timestamp: " + info['timestamp'][item.dataIndex] + "<br \>" +
+ "board: " + info['board'] + "<br \>" +
+ "spec: " + info['spec'] + "<br \>" +
+ "fwver: " + info['fwver'] + "<br \>" +
+ "platform: " + info['platform'] + "<br \>" +
+ "groupname: " + info['groupname'] + "<br \>" +
+ "test: " + info['test'] + "<br \>" +
+ "x: " + x + "<br \>" +
+ "y: " + y)
+ .css({top: item.pageY+5, left: item.pageX-200})
+ .fadeIn(200);
+ } else {
+ jQuery("#tooltip").hide();
}
}
-function getBuildsInfo(series) {
- devices = series;
- devices.forEach(function(dev){
- dev['info'][1].forEach(function(fw){
- if (fws.indexOf(fw) == -1) {
- fws.push(fw);}});});
- fws.sort();
- fws.reverse();
+function handle_lowerplot_zoom(event, ranges) {
+ var id = jQuery(this).attr("id"); // e.g.: upper-2048_Kb_Record_Write-0
+ var groupname = id.split('-')[1];
+ var index = id.split('-')[2];
+ var plot = plots[index];
+
+ plot.getOptions().xaxes[0].min = ranges.xaxis.from;
+ plot.getOptions().xaxes[0].max = ranges.xaxis.to;
+ plot.getOptions().yaxes[0].min = ranges.yaxis.from;
+ plot.getOptions().yaxes[0].max = ranges.yaxis.to;
+ plot.setupGrid();
+ plot.draw();
}
-jQuery.ajax({ url: jenurl+'/Benchmark.'+testsuite+'/metrics.json', method: 'GET', dataType: 'json', async: false, success: getSuitesInfo});
-jQuery.ajax({ url: jenurl+'/Benchmark.'+testsuite+'/Benchmark.'+testsuite+'.info.json', method: 'GET', dataType: 'json', async: false, success: getBuildsInfo});
+function replot_groupname() {
+ var id = jQuery(this).attr("id"); // e.g.: label-2048_Kb_Record_Write-0
+ var groupname = id.split('-')[1];
+ var index = id.split('-')[2];
-for (var i=0;i<metrics.length;i++) {
- var tmp_ph = "#ph"+i,
- placeholder = jQuery("#ph"+i),
- placeholder_f = jQuery("#phf"+i),
- legend = jQuery("#phl"+i),
- target_list = jQuery("#pht"+i),
- plot, plot_f,
- previousPoint = null;
-
- jQuery.ajax({url:jenurl+'/Benchmark.'+testsuite+'/Benchmark.'+testsuite+'.'+metrics[i]+'.json',method:'GET',dataType:'json',async:false,success:onDataReceived});
- jQuery(placeholder_f).bind("plotselected", hand_o);
- jQuery("#pht"+i+" input").click(drawChoices);
- jQuery("#all_dev_"+i).click(drawChoices);
- jQuery("#phfw"+i+" input").click(drawChoices);
- jQuery("#all_fw_"+i).click(drawChoices);
- jQuery(placeholder).bind("plothover", MoreInfo_PopUp);
+ plot_groupname(groupname, index);
}
-function MoreInfo_PopUp (event, pos, item) {
- var fw, sdk, name;
- if (item) {
- jQuery("#tooltip").remove();
- if (previousPoint != item.dataIndex) {
- previousPoint = item.dataIndex;
- var x = item.datapoint[0].toFixed(2),
- y = item.datapoint[1].toFixed(2),
- id = -1,
- o = 0;
-
- while (id < 0) {
- id = devices[o]['info'][0].indexOf(parseFloat(x).toString());
- if (id >= 0) {
- fw = devices[o]['info'][1][id];
- sdk = devices[o]['info'][2][id];
- name = devices[o].device;
- }
- o++;
- }
-
- if (fw){
- previousPoint = null;
- showTooltip(item.pageX,item.pageY,"<b>"+item.series.label+"</b>"+
- "<br/>Build: "+parseFloat(x).toFixed()+
- "<br/>Device: "+name+
- "<br/>Value: "+y+
- "<br/>SDK: "+sdk+
- "<br/>FW: "+fw);
- }
- }
- }
- else {
- jQuery("#tooltip").remove();
- previousPoint = null;
- }
-}
-
-function hand_o (event,ranges) {
- var n = parseInt(this.id.substring(this.id.length-1));
- plots[n][0]=jQuery.plot(jQuery("#ph"+n),plots[n][2],jQuery.extend(true,{},options,{
- xaxis:{min:ranges.xaxis.from, max:ranges.xaxis.to},
- yaxis: {min:ranges.yaxis.from,max:ranges.yaxis.to}
- }));
-}
-
-function drawChoices() {
- var res = [], checked_fw = [], checked_dev = [],
- all_devs_checked = false, all_fw_checked = false,
- k = parseInt(this.id.slice(-1)); // XXX this assumes the maximum of 10 graphs!
-
- // Handle group check-boxes, and set all_*_checked falgs
- if (jQuery(this).attr("name") == "all_dev") {
- all_devs_checked = jQuery(this).attr("checked");
- jQuery("#pht"+k+" input").attr("checked", all_devs_checked);
- } else if (jQuery(this).attr("name") == "all_fw") {
- all_fw_checked = jQuery(this).attr("checked");
- jQuery("#phfw"+k+" input").attr("checked", all_fw_checked);
- }
-
- if (!all_devs_checked) {
- all_devs_checked = (jQuery("#pht"+k+" input:checked").length == jQuery("#pht"+k+" input").length);
- jQuery("#all_dev_"+k).attr("checked", all_devs_checked);
- }
-
- if (!all_fw_checked) {
- all_fw_checked = (jQuery("#phfw"+k+" input:checked").length == jQuery("#phfw"+k+" input").length);
- jQuery("#all_fw_"+k).attr("checked", all_fw_checked);
- }
-
- jQuery("#pht"+k+" input:checked").each(function(){
- var devname = jQuery(this).attr("name");
- devices.forEach(function(dev){
- if (dev.device == devname) {
- checked_dev.push(jQuery.extend(true, {}, dev))}});
- });
-
- checked_dev.forEach(function(dev){
- var new_bids = [];
- jQuery("#phfw"+k+" input:checked").each(function(){
- var fwname = jQuery(this).attr("name");
- for (var f=0; f<dev.info[1].length; f++) {
- var fw = dev.info[1][f];
- if (fwname == fw ||
- // Special handling of firmware version groups
- (fw.length == fw_ver_len && fwname.slice(-1 * glob_suffix.length) == glob_suffix &&
- fwname.slice(0, fw_ver_prefix_len) == fw.slice(0, fw_ver_prefix_len))) {
- for (var ind=0; ind<devices.length; ind++) {
- if (devices[ind].device == dev.device) {
- new_bids.push(devices[ind].info[0][f]);
- }
- }
+function plot_groupname(groupname, index) {
+ // prepare data to display
+ var label_id = 'label-' + groupname + '-' + index;
+ var flot_results_json = []
+ jQuery.each(results_json, function(label, results_json_item) {
+ var is_checked = jQuery('#' + label_id).children('input[name="' + label + '"]').attr("checked");
+ if (is_checked && (results_json_item['groupname'] == groupname)) {
+ var data = [];
+ results_json_item['data'].forEach(function(data_item, j) {
+ data.push([j, data_item]);
+ });
+ flot_results_json.push({ 'label' : label, 'data' : data});
}
- }
});
- dev.info[0] = new_bids;
- });
- checked_dev.forEach(function(dev){
- plots[k][2].forEach(function(graph){
- graph.data.forEach(function(dot){
- if (jQuery.inArray(dot[0],dev.info[0]) >= 0) {
- var exist = false;
- res.forEach(function(res_point){
- if (res_point.label == graph.label) {
- res_point.data.push([dot[0],dot[1]]);
- exist = true; }
- });
- if (exist == false){
- res.push({label:graph.label,data:[[dot[0],dot[1]]],points:graph.points});
- }
- }
- });
+ // calculate y_max, y_min and x_max fromt the result data
+ var y_min = 1000000000;
+ var y_max = -1;
+ var x_max = 0;
+ var num_samples = 75; // we only plot the last 75 points on the upper plot
+
+ flot_results_json.forEach (function (flot_result_item) {
+ slice = flot_result_item['data'].slice(-1 * num_samples);
+ slice.forEach(function (data_value) {
+ x = parseInt(data_value[0]);
+ y = parseFloat(data_value[1]);
+ y_max = Math.max(y_max, y);
+ y_min = Math.min(y_min, y);
+ x_max = Math.max(x_max, x)
+ });
});
- });
- res.forEach(function(re){re.data.sort(function(a,b){return(a[0]-b[0])});});
- plotGraphs(jQuery("#ph"+k),jQuery("#phf"+k),res);
-}
-function showTooltip(x, y, contents) {
- jQuery('<div id="tooltip">' + contents + '</div>').css( {
- position: 'absolute',
- display: 'none',
- top: y + 1,
- left: x + 5,
- border: '1px solid #fdd',
- padding: '2px', 'background-color':'#ffe', opacity:0.90
- }).appendTo("body").fadeIn(200);
+ // prepare the plot options for the upper and lower plots
+ var upper_options = {
+ lines : { show: true , lineWidth:1.2 },
+ points : { show: true },
+ xaxis : { max: x_max,
+ min: Math.max(x_max - num_samples, 0) },
+ yaxis : { max: 1.02 * y_max,
+ min: 0.98 * y_min},
+ grid : { hoverable: true, clickable: true,
+ backgroundColor: "#f5f5f5", borderWidth: 0.5 },
+ pan : { interactive: true },
+ zoom : { interactive: true },
+ legend : { position: 'nw',
+ noColumns:2,
+ container: jQuery("#legend_item_"+index) },
+ colors : [ "#008f00", "#73fa79", "#009193", "#73fcd6", "#ff9300",
+ "#ffd479", "#942193", "#d783ff", "#424242", "#a9a9a9",
+ "#011993", "#76d6ff", "#929000", "#fffc79", "#941100",
+ "#ff7e79" ],
+ };
+
+ var lower_options = {
+ lines : { show: true, lineWidth:1.0 },
+ points : { show: false },
+ grid : { backgroundColor: "#f0f0f0", borderWidth: 0.5 },
+ legend : { show: false },
+ selection: { mode: "x", color: "blue" },
+ colors : [ "#008f00", "#73fa79", "#009193", "#73fcd6", "#ff9300",
+ "#ffd479", "#942193", "#d783ff", "#424242", "#a9a9a9",
+ "#011993", "#76d6ff", "#929000", "#fffc79", "#941100",
+ "#ff7e79" ]
+ };
+
+ // finally plot the data
+ var upper_placeholder = jQuery('#upper-' + groupname + '-' + index);
+ var lower_placeholder = jQuery('#lower-' + groupname + '-' + index);
+
+ jQuery.plot(lower_placeholder, flot_results_json, lower_options);
+ return jQuery.plot(upper_placeholder, flot_results_json, upper_options);
}
-function plotGraphs(placeholder, overview, series) {
- var width = 75, // Last N points to show on main graph
- y_max = -1, // Max Y-value for group of tests
- y_min = 1000000000,
- x_max = 0; // The last build number
-
- series.forEach (function (a) {
- if ('data' in a) {
- var data = a.data;
- slice = data.slice(-1 * width);
- slice.forEach(function (e) {
- y_val = parseFloat(e[1]);
- if (y_max < y_val) y_max = y_val;
- if (y_min > y_val) y_min = y_val;
- x_val = parseInt(e[0]);
- if (x_max < x_val) x_max = x_val;
- });
- }});
-
- options.xaxis.max = x_max;
- // XXX includes outside points if some of inside ones are disabled
- options.xaxis.min = x_max - width;
+function plot_all_groupnames(series) {
+ // results_json is the results.json file (global)
+ results_json = series
+
+ // extract set of boards, specs, fwvers
+ var labels = [];
+ var boards = [];
+ var specs = [];
+ var fwvers = [];
+ var groupnames = [];
+ var tests = [];
+
+ jQuery.each(results_json, function(key, results_json_item) {
+ if (!(labels.includes(key)))
+ labels.push(key);
+ if (!(boards.includes(results_json_item['board'])))
+ boards.push(results_json_item['board']);
+ if (!(specs.includes(results_json_item['spec'])))
+ specs.push(results_json_item['spec']);
+ if (!(fwvers.includes(results_json_item['fwver']))) // FIXTHIS: remove tail?
+ fwvers.push(results_json_item['fwver']);
+ if (!(groupnames.includes(results_json_item['groupname'])))
+ groupnames.push(results_json_item['groupname']);
+ if (!(tests.includes(results_json_item['test'])))
+ tests.push(results_json_item['test']);
+ });
- options.yaxis.max = 1.02 * y_max;
- options.yaxis.min = 0.98 * y_min;
+ // there is one plot per groupname
+ groupnames.forEach(function(groupname, i) {
+ // FIXTHIS: make sure that groupname has no - in it
+ var label_id = 'label-' + groupname + '-' + i;
+ // create all html elements
+ jQuery('.plots').append(
+ '<div class="container">' +
+ ' <div class="area_header">' + testsuite + ' / ' + groupname + '</div>' +
+ ' <div class="two_figures_container">' +
+ ' <div style="width:100%;height:200px;" id="upper-' + groupname + '-' + i + '"></div>' +
+ ' <p></p>' +
+ ' <div style="width:100%;height:70px;" id="lower-' + groupname + '-' + i + '"></div>' +
+ ' </div>' +
+ ' <br/>' +
+ ' <div class="legend_container">Legend:' +
+ ' <div id="legend_item_' + i + '"></div>' +
+ ' <br/>' +
+ ' <div id="' + label_id + '"></div>' +
+ ' </div>' +
+ '</div>');
+
+ labels.forEach(function(label) {
+ if (results_json[label].groupname == groupname) {
+ var id = 'id_' + label + '_'+ i;
+ jQuery('#' + label_id).append(
+ '<input type="checkbox" name="' + label + '" checked="checked" id="' + id + '">' +
+ '<label for="' + id + '">' + label + '</label><br/>');
+ }
+ });
- options.legend.container = legend;
+ // hook callbacks to interactive elements
+ jQuery('#lower-' + groupname + '-' + i).bind("plotselected", handle_lowerplot_zoom);
+ jQuery('#upper-' + groupname + '-' + i).bind("plothover", show_upperplot_tooltip);
+ jQuery('#' + label_id).click(replot_groupname);
- plot = jQuery.plot(placeholder, series, options);
- plot_f = jQuery.plot(overview, series, options_f);
+ // plot this group
+ upper_plot = plot_groupname(groupname, i);
+ plots.push(upper_plot);
+ });
}
-function onDataReceived(series) {
- var deviceContainer = jQuery("#pht"+i),
- fwContainer = jQuery("#phfw"+i);
-
- devices.forEach(function (dev) {
- deviceContainer.append('<input class="shift" type="checkbox" name="'+dev['device']+'" checked="checked" id="id_' + dev['device'] + '_'+ i +'">' +
- '<label for="id_'+dev['device']+'">'+dev['device']+'</label><br/>');
- });
-
- var prev_fw = '';
- var new_fw = '';
- fws.forEach(function(fw){
- // Combine unofficial (e.g. nightly) builds into groups.
- // Otherwise, the list becomes too long.
- if (fw.length == fw_ver_len && fw.substr(8,2) != '00') {
- new_fw = fw.substr(0, fw_ver_len - glob_suffix.length) + glob_suffix;
- } else {
- new_fw = fw;
- }
- if (prev_fw != new_fw) {
- fwContainer.append('<input class="shift" type="checkbox" name="'+ new_fw +'" checked="checked" id="fwid_'+ new_fw +'_'+ i +'">'+'<label for="fwid_'+ new_fw +'">'+ new_fw +'</label><br/>');
- prev_fw = new_fw;
- }
- });
+jQuery.ajax({ url: jenkins_logs_path+'/Benchmark.'+testsuite+'/results.json', method: 'GET', dataType: 'json', async: false, success: plot_all_groupnames});
- plotGraphs(placeholder, placeholder_f, series);
- plots.push([plot,plot_f,series]);
- }
})
diff --git a/frontend-install/plugins/flot-plotter-plugin/src/main/webapp/flot/my.css b/frontend-install/plugins/flot-plotter-plugin/src/main/webapp/flot/my.css
index b2b233b..f39ea44 100644
--- a/frontend-install/plugins/flot-plotter-plugin/src/main/webapp/flot/my.css
+++ b/frontend-install/plugins/flot-plotter-plugin/src/main/webapp/flot/my.css
@@ -1 +1,20 @@
-.cont2,.devices{float:left}.cont2,.place,.overview{width:800px}.overview{height:70px}.cont3,.firmware{float:left}.cont3,.shift{margin-left:10px}.cont3{width:300px}.devices,.firmware{width:145px}.container{width:1130px;clear:right}.area_header{font-size:24px;padding-top:30px;clear:left}
+.container {
+ width: 800px;
+ margin-top: 30px;
+ margin-bottom: 30px;
+}
+
+.area_header {
+ font-size: 24px;
+ padding-top: 30px;
+ padding-bottom: 20px;
+}
+
+.two_figures_container {
+ float: left;
+ width: 100%;
+}
+
+.legend_container {
+ margin-left: 10px;
+}
--
2.7.4
More information about the Fuego
mailing list