From 8874be4c663a089a2e01998c7c9753731c459665 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 21 Apr 2017 16:11:49 +0200 Subject: [PATCH] singlestat: add support for table data If data is of type Table, then will return the first row of data. The user can select which column should be shown in the SingleStat. --- .../app/plugins/panel/singlestat/editor.html | 10 ++- public/app/plugins/panel/singlestat/module.ts | 75 ++++++++++++++-- ...inglestat-specs.ts => singlestat_specs.ts} | 86 ++++++++++++++++--- 3 files changed, 153 insertions(+), 18 deletions(-) rename public/app/plugins/panel/singlestat/specs/{singlestat-specs.ts => singlestat_specs.ts} (67%) diff --git a/public/app/plugins/panel/singlestat/editor.html b/public/app/plugins/panel/singlestat/editor.html index 50f18ea6146..937f3c6cd9d 100644 --- a/public/app/plugins/panel/singlestat/editor.html +++ b/public/app/plugins/panel/singlestat/editor.html @@ -3,11 +3,19 @@
Value
-
+
+
+
+ +
+ +
+
+
diff --git a/public/app/plugins/panel/singlestat/module.ts b/public/app/plugins/panel/singlestat/module.ts index c8ab03bd688..7003130b64c 100644 --- a/public/app/plugins/panel/singlestat/module.ts +++ b/public/app/plugins/panel/singlestat/module.ts @@ -14,6 +14,7 @@ import {MetricsPanelCtrl} from 'app/plugins/sdk'; class SingleStatCtrl extends MetricsPanelCtrl { static templateUrl = 'module.html'; + dataType = 'timeseries'; series: any[]; data: any; fontSizes: any[]; @@ -22,6 +23,7 @@ class SingleStatCtrl extends MetricsPanelCtrl { panel: any; events: any; valueNameOptions: any[] = ['min','max','avg', 'current', 'total', 'name', 'first', 'delta', 'diff', 'range']; + tableColumnOptions: any; // Set and populate defaults panelDefaults = { @@ -67,7 +69,8 @@ class SingleStatCtrl extends MetricsPanelCtrl { maxValue: 100, thresholdMarkers: true, thresholdLabels: false - } + }, + tableColumn: '' }; /** @ngInject */ @@ -98,11 +101,16 @@ class SingleStatCtrl extends MetricsPanelCtrl { } onDataReceived(dataList) { - this.series = dataList.map(this.seriesHandler.bind(this)); - - var data: any = {}; - this.setValues(data); - + const data: any = {}; + if (dataList.length > 0 && dataList[0].type === 'table'){ + this.dataType = 'table'; + const tableData = dataList.map(this.tableHandler.bind(this)); + this.setTableValues(tableData, data); + } else { + this.dataType = 'timeseries'; + this.series = dataList.map(this.seriesHandler.bind(this)); + this.setValues(data); + } this.data = data; this.render(); } @@ -117,6 +125,61 @@ class SingleStatCtrl extends MetricsPanelCtrl { return series; } + tableHandler(tableData) { + const datapoints = []; + const columnNames = {}; + + tableData.columns.forEach((column, columnIndex) => { + columnNames[columnIndex] = column.text; + }); + + this.tableColumnOptions = columnNames; + if (!_.find(tableData.columns, ['text', this.panel.tableColumn])) { + this.setTableColumnToSensibleDefault(tableData); + } + + tableData.rows.forEach((row) => { + const datapoint = {}; + + row.forEach((value, columnIndex) => { + const key = columnNames[columnIndex]; + datapoint[key] = value; + }); + + datapoints.push(datapoint); + }); + + return datapoints; + } + + setTableColumnToSensibleDefault(tableData) { + if (this.tableColumnOptions.length === 1) { + this.panel.tableColumn = this.tableColumnOptions[0]; + } else { + this.panel.tableColumn = _.find(tableData.columns, (col) => { return col.type !== 'time'; }).text; + } + } + + setTableValues(tableData, data) { + if (!tableData || tableData.length === 0) { + return; + } + + if (tableData[0].length === 0 || !tableData[0][0][this.panel.tableColumn]) { + return; + } + + let highestValue = 0; + let lowestValue = Number.MAX_VALUE; + const datapoint = tableData[0][0]; + data.value = datapoint[this.panel.tableColumn]; + + var decimalInfo = this.getDecimalsForValue(data.value); + var formatFunc = kbn.valueFormats[this.panel.format]; + data.valueFormatted = formatFunc(datapoint[this.panel.tableColumn], decimalInfo.decimals, decimalInfo.scaledDecimals); + data.valueRounded = kbn.roundValue(data.value, this.panel.decimals || 0); + } + setColoring(options) { if (options.background) { this.panel.colorValue = false; diff --git a/public/app/plugins/panel/singlestat/specs/singlestat-specs.ts b/public/app/plugins/panel/singlestat/specs/singlestat_specs.ts similarity index 67% rename from public/app/plugins/panel/singlestat/specs/singlestat-specs.ts rename to public/app/plugins/panel/singlestat/specs/singlestat_specs.ts index 9c7a979af12..51ade2f9a98 100644 --- a/public/app/plugins/panel/singlestat/specs/singlestat-specs.ts +++ b/public/app/plugins/panel/singlestat/specs/singlestat_specs.ts @@ -26,11 +26,7 @@ describe('SingleStatCtrl', function() { beforeEach(function() { setupFunc(); - var data = [ - {target: 'test.cpu1', datapoints: ctx.datapoints} - ]; - - ctx.ctrl.onDataReceived(data); + ctx.ctrl.onDataReceived(ctx.data); ctx.data = ctx.ctrl.data; }); }; @@ -41,7 +37,9 @@ describe('SingleStatCtrl', function() { singleStatScenario('with defaults', function(ctx) { ctx.setup(function() { - ctx.datapoints = [[10,1], [20,2]]; + ctx.data = [ + {target: 'test.cpu1', datapoints: [[10,1], [20,2]]} + ]; }); it('Should use series avg as default main value', function() { @@ -56,7 +54,9 @@ describe('SingleStatCtrl', function() { singleStatScenario('showing serie name instead of value', function(ctx) { ctx.setup(function() { - ctx.datapoints = [[10,1], [20,2]]; + ctx.data = [ + {target: 'test.cpu1', datapoints: [[10,1], [20,2]]} + ]; ctx.ctrl.panel.valueName = 'name'; }); @@ -72,7 +72,9 @@ describe('SingleStatCtrl', function() { singleStatScenario('MainValue should use same number for decimals as displayed when checking thresholds', function(ctx) { ctx.setup(function() { - ctx.datapoints = [[99.999,1], [99.99999,2]]; + ctx.data = [ + {target: 'test.cpu1', datapoints: [[99.999,1], [99.99999,2]]} + ]; }); it('Should be rounded', function() { @@ -87,7 +89,9 @@ describe('SingleStatCtrl', function() { singleStatScenario('When value to text mapping is specified', function(ctx) { ctx.setup(function() { - ctx.datapoints = [[9.9,1]]; + ctx.data = [ + {target: 'test.cpu1', datapoints: [[9.9,1]]} + ]; ctx.ctrl.panel.valueMaps = [{value: '10', text: 'OK'}]; }); @@ -106,7 +110,9 @@ describe('SingleStatCtrl', function() { singleStatScenario('When range to text mapping is specifiedfor first range', function(ctx) { ctx.setup(function() { - ctx.datapoints = [[41,50]]; + ctx.data = [ + {target: 'test.cpu1', datapoints: [[41,50]]} + ]; ctx.ctrl.panel.mappingType = 2; ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}]; }); @@ -118,7 +124,9 @@ describe('SingleStatCtrl', function() { singleStatScenario('When range to text mapping is specified for other ranges', function(ctx) { ctx.setup(function() { - ctx.datapoints = [[65,75]]; + ctx.data = [ + {target: 'test.cpu1', datapoints: [[65,75]]} + ]; ctx.ctrl.panel.mappingType = 2; ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}]; }); @@ -128,4 +136,60 @@ describe('SingleStatCtrl', function() { }); }); + const tableData = [{ + "columns": [ + { + "text": "Time", + "type": "time" + }, + { + "text": "test1" + }, + { + "text": "mean" + }, + { + "text": "test2" + } + ], + "rows": [ + [ + 1492759673649, + 'ignore1', + 15, + 'ignore2' + ] + ], + "type": "table" + }]; + + singleStatScenario('When table data', function(ctx) { + ctx.setup(function() { + ctx.data = tableData; + ctx.ctrl.panel.tableColumn = 'mean'; + }); + + it('Should use series avg as default main value', function() { + expect(ctx.data.value).to.be(15); + expect(ctx.data.valueRounded).to.be(15); + }); + + it('should set formatted value', function() { + expect(ctx.data.valueFormatted).to.be('15'); + }); + }); + + singleStatScenario('When table data has multiple columns', function(ctx) { + ctx.setup(function() { + ctx.data = tableData; + ctx.ctrl.panel.tableColumn = ''; + }); + + it('Should set column to first column that is not time', function() { + expect(ctx.ctrl.panel.tableColumn).to.be('test1'); + }); + }); + }); + +