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'); + }); + }); + }); + +