From 36c018b76eac59cf26b8286ee3ed9eebada815f9 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Fri, 11 Sep 2020 14:34:14 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20reset=20zoom=20button?= =?UTF-8?q?=20&=20yAxisFormat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/benchmark.css | 12 ++++++++ src/assets/funcs.js | 59 ++++++++++++++++++++++++++-------------- src/assets/main.js | 44 ++++++++++++++++++++++++++---- 3 files changed, 89 insertions(+), 26 deletions(-) diff --git a/src/assets/benchmark.css b/src/assets/benchmark.css index 98df2b4f3..541b1f975 100644 --- a/src/assets/benchmark.css +++ b/src/assets/benchmark.css @@ -111,3 +111,15 @@ footer { .benchmark-chart { max-width: 1000px; } + +.benchmark-button-reset { + margin-top: 1rem; + border-radius: 5px; + background-color: #5c8aac; + outline-color: #1f77b4; + outline-width: 10px; +} + +.benchmark-button-reset:hover { + background-color: #1f77b4; +} diff --git a/src/assets/funcs.js b/src/assets/funcs.js index 6428e61c0..f0a1c4b77 100644 --- a/src/assets/funcs.js +++ b/src/assets/funcs.js @@ -34,16 +34,16 @@ function* cycle(iterable) { } -var longestCommonPrefix = function(strs) { +var longestCommonPrefix = function (strs) { let prefix = "" - if(strs === null || strs.length === 0) return prefix + if (strs === null || strs.length === 0) return prefix - for (let i=0; i < strs[0].length; i++){ + for (let i = 0; i < strs[0].length; i++) { const char = strs[0][i] // loop through all characters of the very first string. - for (let j = 1; j < strs.length; j++){ - // loop through all other strings in the array - if(strs[j][i] !== char) return prefix + for (let j = 1; j < strs.length; j++) { + // loop through all other strings in the array + if (strs[j][i] !== char) return prefix } prefix = prefix + char } @@ -52,12 +52,27 @@ var longestCommonPrefix = function(strs) { } -export function renderGraph(canvas, dataset, labels, xAxis, alpha = 60, fill = true, legendAlign = 'center', labelString = 'iter/sec') { +export function renderGraph(canvas, dataset, labels, userConfig) { - const colorCycle = cycle(['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf']); + const defaultConfig = { + fill: true, + alpha: 60, + legendAlign: 'center', + yLabelString: 'iter/sec', + xAxis: 'id', + yAxisFormat: 'linear', + colors: ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf'] + } + //override defaults, unless undefined + const definedProps = obj => Object.fromEntries( + Object.entries(obj).filter(([k, v]) => v !== undefined) + ); + const config = { ...defaultConfig, ...definedProps(userConfig) }; + + const colorCycle = cycle(config.colors); // if all test names start with a common prefix, then show this as a title - let tests_prefix = dataset.length > 1 ? longestCommonPrefix(dataset.map(s => s.name)): '' + let tests_prefix = dataset.length > 1 ? longestCommonPrefix(dataset.map(s => s.name)) : '' // when using parametrized pytests, the names are enclosed by [...] if (tests_prefix.endsWith('[')) { tests_prefix = tests_prefix.slice(0, -1) @@ -71,8 +86,8 @@ export function renderGraph(canvas, dataset, labels, xAxis, alpha = 60, fill = t label: s.name.slice(tests_prefix.length), data: s.data.map(d => d ? d.bench.value : null), borderColor: color, - fill: fill, - backgroundColor: color + `${alpha}`, // Add alpha for #rrggbbaa + fill: config.fill, + backgroundColor: color + `${config.alpha}`, // Add alpha for #rrggbbaa spanGaps: true, } }).sort((a, b) => { return a.label > b.label; }) @@ -83,7 +98,7 @@ export function renderGraph(canvas, dataset, labels, xAxis, alpha = 60, fill = t labelString: 'Commit ID', }, } - if (xAxis === 'date') { + if (config.xAxis === 'date') { xAxes['type'] = 'time' xAxes['time'] = { minUnit: 'second' @@ -93,11 +108,12 @@ export function renderGraph(canvas, dataset, labels, xAxis, alpha = 60, fill = t const yAxes = { scaleLabel: { display: true, - labelString, + labelString: config.yLabelString, }, ticks: { beginAtZero: true, - } + }, + type: config.yAxisFormat, } const options = { scales: { @@ -111,7 +127,7 @@ export function renderGraph(canvas, dataset, labels, xAxis, alpha = 60, fill = t }, legend: { position: 'top', - align: legendAlign, + align: config.legendAlign, // TODO legend titles only available in chartjs v3 // rather then chart title // title: {text: 'hallo', display: true}, @@ -121,7 +137,7 @@ export function renderGraph(canvas, dataset, labels, xAxis, alpha = 60, fill = t afterTitle: items => { const { datasetIndex, index } = items[0]; const data = dataset[datasetIndex].data[index]; - const lines = [data.commit.message + '\n', (xAxis === 'date') ? data.commit.id.slice(0, 7) : moment(data.commit.timestamp).toString()] + const lines = [data.commit.message + '\n', (config.xAxis === 'date') ? data.commit.id.slice(0, 7) : moment(data.commit.timestamp).toString()] if (data.cpu) { lines.push('\nCPU:') for (const [key, value] of Object.entries(data.cpu)) { @@ -165,17 +181,18 @@ export function renderGraph(canvas, dataset, labels, xAxis, alpha = 60, fill = t }, plugins: { zoom: { - // pan: { - // enabled: true - // }, + pan: { + enabled: false + }, zoom: { enabled: true, - drag: true + drag: true, + mode: 'xy', } } } }; - new Chart(canvas, { + return new Chart(canvas, { type: 'line', data, options, diff --git a/src/assets/main.js b/src/assets/main.js index d92c8e585..f440b9e4f 100644 --- a/src/assets/main.js +++ b/src/assets/main.js @@ -37,6 +37,24 @@ function setupData(data) { })); } +function addResetZoomButton(chart, graphsElem) { + // assign the chart to the DOM and add a zoom reset + const chartId = Object.keys(window.charts).length + window.charts[chartId] = chart; + const resetButton = document.createElement('button'); + resetButton.className = 'benchmark-button-reset'; + resetButton.id = chartId + resetButton.innerText = 'Reset Zoom' + resetButton.onclick = function (event) { + console.log("resetting zoom"); + console.log(window.charts) + console.log(event.target.id); + window.charts[event.target.id].resetZoom(); + }; + graphsElem.appendChild(resetButton); +} + + /** * Returns the sum of all numbers passed to the function. * @param name name of the entry set @@ -85,6 +103,8 @@ function renderBenchSetGroups(domElement, name, benchSets, configEntry, configGr graphsElem.className = 'benchmark-graphs'; setElem.appendChild(graphsElem); + var chart, chartConfig; + if (configGroup.single_chart) { const canvas = document.createElement('canvas'); canvas.className = 'benchmark-chart'; @@ -98,9 +118,17 @@ function renderBenchSetGroups(domElement, name, benchSets, configEntry, configGr }; }); const labels = uniqueCommits.map((d) => (configGroup.xAxis === 'date') ? moment(d[0]) : d[1].slice(0, 7)); - const fill = configGroup.backgroundFill === undefined ? true : configGroup.backgroundFill - const legendAlign = configGroup.legendAlign === undefined ? 'center' : configGroup.legendAlign - renderGraph(canvas, datasets, labels, configGroup.xAxis, 10, fill, legendAlign) + chartConfig = { + fill: configGroup.backgroundFill, + legendAlign: configGroup.legendAlign, + yAxisFormat: configGroup.yAxisFormat, + alpha: 10, + xAxis: configGroup.xAxis, + colors: configGroup.colors, + }; + chart = renderGraph(canvas, datasets, labels, chartConfig); + addResetZoomButton(chart, graphsElem); + } else { for (const [benchName, benches] of benchSet.entries()) { const canvas = document.createElement('canvas'); @@ -111,8 +139,13 @@ function renderBenchSetGroups(domElement, name, benchSets, configEntry, configGr data: benches }] const labels = benches.map((d) => (configGroup.xAxis === 'date') ? moment(d.commit.timestamp) : d.commit.id.slice(0, 7)); - const labelString = benches.length > 0 ? benches[0].bench.unit : ''; - renderGraph(canvas, datasets, labels, configGroup.xAxis, 60, labelString); + chartConfig = { + alpha: 60, + xAxis: configGroup.xAxis, + yLabelString: benches.length > 0 ? benches[0].bench.unit : '' + }; + chart = renderGraph(canvas, datasets, labels, chartConfig); + addResetZoomButton(chart, graphsElem); } } @@ -125,6 +158,7 @@ function renderBenchSetGroups(domElement, name, benchSets, configEntry, configGr * @param Object config map of {suites: {key: data}, groups: {key: data}} */ function renderAllCharts(dataSets, config) { + window.charts = {}; const main = document.getElementById('main'); for (const { name, dataSet } of dataSets) { const configEntry = config.suites[name] || {};