; google.setOnLoadCallback(draw_charts); var fields = {}; var distinct_values = []; var ninputs; var inputs_table; var ntools; var ifield; var tfield; var nfields; var nresults; var results; var hashres = {}; var linecmp; var linecmp_options = { hAxis: {title: 'input', viewWindow: {}, format: '#', maxAlternation: 3, minTextSpacing: 5, maxTextLines: 3, slantedText: false }, legend: {position: 'top', alignment: 'end'}, pointSize: 4, chartArea: {width: '90%'} } var input_line; var input_dashboard; // Tool-indexed tables var sumresults_table; var tools_table; var cross_table; var cross_details; function register_tool_indexed_table(table) { google.visualization.events.addListener(table, 'select', function(){ select_tools(table.getSelection()); }); } function select_tools(selection) { tools_table.setSelection(selection); sumresults_table.setSelection(selection); cross_table.setSelection(selection); cross_details.setSelection(selection); } function select_input(t) { $('#single_input_select').val(t).trigger("change"); inputs_table.setSelection([{row: t}]); } function draw_input_line() { input_line.setOptions(linecmp_options) //input_line.draw(linecmp); input_dashboard.draw(linecmp); } function change_input_line() { // line graph data linecmp = new google.visualization.DataTable(); var column = $('#input_select').val(); linecmp.addColumn('string', 'input'); // Discrete values for the LineChart linecmp.addColumn('number', 'input_'); // Continuous values for the control for (var t = 0; t < ntools; ++t) linecmp.addColumn('number', 'tool ' + t); for (var i = 0; i < ninputs; ++i) { var res = [i.toString(), i]; for (var t = 0; t < ntools; ++t) { var d = hashres[[t,i]]; if (d) res.push(d[column]); else res.push(null); } linecmp.addRow(res); } } function setup_input_line() { change_input_line(); var datacols = [] for (var t = 0; t < ntools; ++t) datacols.push(t + 2); var control = new google.visualization.ControlWrapper({ controlType: 'ChartRangeFilter', containerId: 'input_control', options: { filterColumnIndex: 1, ui: { chartType: 'LineChart', chartOptions: { chartArea: {width: '90%'}, hAxis: {baselineColor: 'none'} }, chartView: { columns: [1].concat(datacols) // [1] == continuous values for the control }, minRangeSize: 1 } }, state: {range: {start: 0, end: ninputs}} }); input_line = new google.visualization.ChartWrapper({ chartType: 'LineChart', containerId: 'input_line', options: linecmp_options, dataTable: linecmp, view: {columns: [0].concat(datacols)} // [0] == discrete values for the line chart }); input_dashboard = new google.visualization.Dashboard(document.getElementById('input_dashboard')); input_dashboard.bind(control, input_line); draw_input_line(); } function update_input_line() { change_input_line(); draw_input_line(); } function update_cross_table() { var crossdata = new google.visualization.DataTable(); var crossdatadet = new google.visualization.DataTable(); var crosscol = $('#cross_select').val(); var crosscol2 = $('#cross_select2').val(); crossdata.addColumn('string', '↓ greater than →'); crossdatadet.addColumn('string', '↓ greater than →'); for (var t = 0; t < ntools; ++t) { crossdata.addColumn('number', 'tool ' + t); crossdatadet.addColumn('string', 'tool ' + t); } for (var w = 0; w < ntools; ++w) { var r = []; var d = []; var wrap = function(i) { return '' + i + ''; }; for (var b = 0; b < ntools; ++b) { var bres = 0; var dres = []; if (w != b) for (var i = 0; i < ninputs; ++i) { var wi = hashres[[w,i]]; if (wi == undefined) continue; var bi = hashres[[b,i]]; if (bi == undefined) continue; var wv = wi[crosscol]; var bv = bi[crosscol]; if (wv > bv) { ++bres; dres.push(wrap(i)); } else if ((wv == bv) && crosscol2 >= 0) { var wv = wi[crosscol2]; var bv = bi[crosscol2]; if (wv > bv) { ++bres; dres.push(wrap(i)); } } } else bres = null; r[b] = bres; d[b] = dres.join(' '); } crossdata.addRow(['tool ' + w].concat(r)); crossdatadet.addRow(['tool ' + w].concat(d)); } for (var c = 1; c < ntools + 1; ++c) { var range = crossdata.getColumnRange(c); var rangeformatter = new google.visualization.ColorFormat(); rangeformatter.addRange(range['min'], range['min'] + 1, 'green', null); rangeformatter.addRange(range['max'], null, 'red', null); rangeformatter.format(crossdata, c); } cross_table = new google.visualization.Table(document.getElementById('cross_table')); register_tool_indexed_table(cross_table); cross_table.draw(crossdata, {allowHtml: true}); cross_details = new google.visualization.Table(document.getElementById('cross_details')); register_tool_indexed_table(cross_details); cross_details.draw(crossdatadet, {allowHtml: true}); } function update_scatter_table() { var tool1 = $('#tool1_select').val(); var tool2 = $('#tool2_select').val(); var f = $('#scatter_select').val(); var bycolor = parseInt($('#bycolor_select').val()); var fname = rawdata.fields[f]; var scatterdata = new google.visualization.DataTable(); scatterdata.addColumn({type: 'number', role: 'domain'}); var ndv = 1; var dv = {}; if (bycolor >= 0) { // Give each value a column number. var v = results.getDistinctValues(bycolor); ndv = v.length; for (var i = 0; i < ndv; ++i) { dv[v[i]] = i; scatterdata.addColumn({type: 'number', role: 'data', label: rawdata.fields[bycolor] + '=' + v[i] }); scatterdata.addColumn({type: 'string', role: 'tooltip'}); } scatterdata.addColumn({type: 'number', role: 'data', label: 'disagreement' }); scatterdata.addColumn({type: 'string', role: 'tooltip'}); } else { scatterdata.addColumn({type: 'number', role: 'data'}); scatterdata.addColumn({type: 'string', role: 'tooltip'}); } var max1; for (var i = 0; i < ninputs; ++i) { var i1 = hashres[[tool1, i]]; var i2 = hashres[[tool2, i]]; if (i1 == undefined || i2 == undefined) continue; row = [i1[f]]; var col = 1; if (bycolor >= 0) { for (col = 1; col < 2 * ndv + 3; ++col) row[col] = null; var v = i1[bycolor]; col = dv[v] * 2 + 1; if (v != i2[bycolor]) col = ndv * 2 + 1; } var v2 = i2[f]; row[col] = v2; row[col + 1] = 'input ' + i; if (max1 == undefined || v2 > max1) max1 = v2; scatterdata.addRow(row); } var max = scatterdata.getColumnRange(0)['max']; if (max1 > max) max = max1; var legend = (bycolor >= 0) ? { position: 'right', alignment: 'center' } : 'none'; scatter_table = new google.visualization.ScatterChart(document.getElementById('scatter_table')); scatter_table.draw(scatterdata, { hAxis: {title: fname + ' from tool ' + tool1, maxValue: max}, vAxis: {title: fname + ' from tool ' + tool2, maxValue: max}, legend: legend, chartArea: { left:80, width:420, height:420 }, pointSize: 5 }); google.visualization.events.addListener(scatter_table, 'select', function(){ var sel = scatter_table.getSelection(); var row = sel[0]['row']; var col = sel[0]['column']; if ((row == undefined) || (col == undefined)) return; // The row might not match the input number // if some results were missing. Use the // tooltip instead. var t = scatterdata.getValue(row, col + 1).substr(6); select_input(t); }); } function update_single_input_bars() { var i = parseInt($('#single_input_select').val()); var column_table = new google.visualization.DataTable(); column_table.addColumn({type: 'string', label: 'measurements', role: 'domain'}) for (var t = 0; t < ntools; ++t) { if (hashres[[t,i]] == undefined) continue; column_table.addColumn({type: 'number', label: 'tool ' + t, role: 'data'}) } for (var m = 0; m < nfields; ++m) { if (!is_datacol(m)) continue; var tmp = []; for (var t = 0; t < ntools; ++t) { var rt = hashres[[t,i]]; if (rt == undefined) continue; tmp.push(rt[m]); } var max = Math.max.apply(Math, tmp); var row = [rawdata.fields[m]]; for (var j = 0; j < tmp.length; ++j) row.push({v: tmp[j] / max, f: tmp[j].toString()}); column_table.addRow(row); } single_input_bars = new google.visualization.ColumnChart(document.getElementById('single_input_bars')); single_input_bars.draw(column_table, { legend: {position: 'top', alignment: 'end'}, vAxis: { textPosition: 'none' }, chartArea: { width: '100%' }, bar: { groupWidth: '80%' }, allowHtml: true }); } function draw_charts() { var tools_table_data = new google.visualization.DataTable(); tools_table_data.addColumn('number', 'id', 'id'); tools_table_data.addColumn('string', 'command', 'command'); ntools = rawdata.tool.length; for (var r = 0; r < ntools; ++r) { tools_table_data.addRow([r, rawdata.tool[r]]); } inputs_table_data = new google.visualization.DataTable(); inputs_table_data.addColumn('number', 'id', 'id'); inputs_table_data.addColumn('string', 'input', 'input'); ninputs = rawdata.formula.length; for (var r = 0; r < ninputs; ++r) { inputs_table_data.addRow([r, rawdata.formula[r]]); } results = new google.visualization.DataTable(); nfields = rawdata.fields.length; for (var c = 0; c < nfields; ++c) { var name = rawdata.fields[c]; if (name == 'exit_status') results.addColumn('string', name, name); else results.addColumn('number', name, name); fields[name] = c; } // FIXME: we should uses rawdata.inputs to set ifield and tfield tfield = fields['tool']; ifield = fields['formula']; sfield = fields['states']; esfield = fields['exit_status']; ecfield = fields['exit_code']; is_datacol = function(c) { return (c != ifield && c != tfield && c != esfield && c != ecfield); } nresults = rawdata.results.length; for (var r = 0; r < nresults; ++r) { var row = rawdata.results[r]; if (row[sfield] == null) continue; results.addRow(row); hashres[[row[tfield],row[ifield]]] = row; } var paging_options = { height: '20em' }; tools_table = new google.visualization.Table(document.getElementById('tools_table')); register_tool_indexed_table(tools_table); tools_table.draw(tools_table_data, paging_options); inputs_table = new google.visualization.Table(document.getElementById('inputs_table')); google.visualization.events.addListener(inputs_table, 'select', function(){ var sel = inputs_table.getSelection(); if (sel.length != 1) return; select_input(sel[0]['row']); }); inputs_table.draw(inputs_table_data, paging_options); var results_table = new google.visualization.Table(document.getElementById('results_table')); results_table.draw(results, paging_options); // Do we have all any missing input for some tool? var missing = [['', 'missing inputs']]; for (var t = 0; t < ntools; ++t) { var m = []; for (var i = 0; i < ninputs; ++i) { if (hashres[[t,i]] == undefined) { m.push('' + i + ''); } } if (m.length) missing.push(['tool ' + t, m.join(" ")]); } if (missing.length > 1) { var missing_table_data = google.visualization.arrayToDataTable(missing, false); var missing_table = new google.visualization.Table(document.getElementById('missing_table')); missing_table.draw(missing_table_data, {allowHtml: true}); } var aggreg = [{column:ifield, aggregation: google.visualization.data.count, type: 'number'}] var sumshow = [0, 1]; var col = 2; for (var c = 0; c < nfields; ++c) { if (is_datacol(c)) { aggreg.push({column:c, aggregation: google.visualization.data.sum, type: 'number'}) aggreg.push({column:c, aggregation: google.visualization.data.avg, type: 'number'}) aggreg.push({column:c, aggregation: google.visualization.data.min, type: 'number'}) aggreg.push({column:c, aggregation: google.visualization.data.max, type: 'number'}) sumshow.push(col); col += 4; } } var sumresults = new google.visualization.data.group(results, [tfield], aggreg); var redformatter = new google.visualization.ColorFormat(); redformatter.addRange(0, ninputs, 'black', 'red'); sumresults.setColumnLabel(1, 'inputs'); redformatter.format(sumresults, 1); // Compute highlight the min and max in each column. for (var c = 2; c < sumshow.length; ++c) { var col = sumshow[c]; var range = sumresults.getColumnRange(col); var rangeformatter = new google.visualization.ColorFormat(); rangeformatter.addRange(range['min'], range['min'] + 1, 'green', null); rangeformatter.addRange(range['max'], null, 'red', null); rangeformatter.format(sumresults, col); } var sumresults_view = new google.visualization.DataView(sumresults); sumresults_view.setColumns(sumshow); sumresults_table = new google.visualization.Table(document.getElementById('sumresults_table')); google.visualization.events.addListener(sumresults_table, 'select', function(){ select_tools(sumresults_table.getSelection()); }); sumresults_table.draw(sumresults_view, {allowHtml: true}); // Transpose the avg/min/max values that are in avgresults_view // Instead of // | C1 | avg | min | max | C2 | avg | min | max // T1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 // T2 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 // We want // | T1 | min | max | T2 | min | max // C1 | 11 | 12 | 13 | 21 | 22 | 23 // C2 | 15 | 16 | 17 | 25 | 26 | 27 // Additionally, we should normalize each cell. var column_table = new google.visualization.DataTable(); column_table.addColumn({type: 'string', label: 'measurements', role: 'domain'}) for (var t = 0; t < ntools; ++t) { column_table.addColumn({type: 'number', label: 'tool ' + t, role: 'data'}) column_table.addColumn({type: 'number', role: 'interval'}) column_table.addColumn({type: 'number', role: 'interval'}) } var pos = 3; for (var m = 0; m < nfields; ++m) { if (is_datacol(m)) { var row = [rawdata.fields[m]]; var max = sumresults.getColumnRange(pos + 2)['max']; for (var t = 0; t < ntools; ++t) { row.push({v: sumresults.getValue(t, pos)/max, f: "avg " + sumresults.getValue(t, pos).toFixed(2)}, {v: sumresults.getValue(t, pos + 1)/max, f: "min " + sumresults.getValue(t, pos + 1).toString()}, {v: sumresults.getValue(t, pos + 2)/max, f: "max " + sumresults.getValue(t, pos + 2).toString()}); } pos += 4; column_table.addRow(row); } } avgresults_bars = new google.visualization.ColumnChart(document.getElementById('avgresults_bars')); avgresults_bars.draw(column_table, { legend: {position: 'top', alignment: 'end'}, vAxis: { textPosition: 'none' }, chartArea: { width: '100%' }, bar: { groupWidth: '80%' }, allowHtml: true }); var sel = $('#input_select'); var sel2 = $('#scatter_select'); var sel3 = $('#bycolor_select'); var first = true; sel3.append($("