Commit 8874be4c by Daniel Lee

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.
parent 2c51f114
...@@ -3,11 +3,19 @@ ...@@ -3,11 +3,19 @@
<h5 class="section-heading">Value</h5> <h5 class="section-heading">Value</h5>
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form" ng-show="ctrl.dataType === 'timeseries'">
<label class="gf-form-label width-6">Stat</label> <label class="gf-form-label width-6">Stat</label>
<div class="gf-form-select-wrapper width-7"> <div class="gf-form-select-wrapper width-7">
<select class="gf-form-input" ng-model="ctrl.panel.valueName" ng-options="f for f in ctrl.valueNameOptions" ng-change="ctrl.render()"></select> <select class="gf-form-input" ng-model="ctrl.panel.valueName" ng-options="f for f in ctrl.valueNameOptions" ng-change="ctrl.render()"></select>
</div> </div>
</div>
<div class="gf-form" ng-show="ctrl.dataType === 'table'">
<label class="gf-form-label width-6">Column</label>
<div class="gf-form-select-wrapper width-7">
<select class="gf-form-input" ng-model="ctrl.panel.tableColumn" ng-options="f for f in ctrl.tableColumnOptions" ng-change="ctrl.refresh()"></select>
</div>
</div>
<div class="gf-form">
<label class="gf-form-label width-6">Font size</label> <label class="gf-form-label width-6">Font size</label>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input" ng-model="ctrl.panel.valueFontSize" ng-options="f for f in ctrl.fontSizes" ng-change="ctrl.render()"></select> <select class="gf-form-input" ng-model="ctrl.panel.valueFontSize" ng-options="f for f in ctrl.fontSizes" ng-change="ctrl.render()"></select>
......
...@@ -14,6 +14,7 @@ import {MetricsPanelCtrl} from 'app/plugins/sdk'; ...@@ -14,6 +14,7 @@ import {MetricsPanelCtrl} from 'app/plugins/sdk';
class SingleStatCtrl extends MetricsPanelCtrl { class SingleStatCtrl extends MetricsPanelCtrl {
static templateUrl = 'module.html'; static templateUrl = 'module.html';
dataType = 'timeseries';
series: any[]; series: any[];
data: any; data: any;
fontSizes: any[]; fontSizes: any[];
...@@ -22,6 +23,7 @@ class SingleStatCtrl extends MetricsPanelCtrl { ...@@ -22,6 +23,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
panel: any; panel: any;
events: any; events: any;
valueNameOptions: any[] = ['min','max','avg', 'current', 'total', 'name', 'first', 'delta', 'diff', 'range']; valueNameOptions: any[] = ['min','max','avg', 'current', 'total', 'name', 'first', 'delta', 'diff', 'range'];
tableColumnOptions: any;
// Set and populate defaults // Set and populate defaults
panelDefaults = { panelDefaults = {
...@@ -67,7 +69,8 @@ class SingleStatCtrl extends MetricsPanelCtrl { ...@@ -67,7 +69,8 @@ class SingleStatCtrl extends MetricsPanelCtrl {
maxValue: 100, maxValue: 100,
thresholdMarkers: true, thresholdMarkers: true,
thresholdLabels: false thresholdLabels: false
} },
tableColumn: ''
}; };
/** @ngInject */ /** @ngInject */
...@@ -98,11 +101,16 @@ class SingleStatCtrl extends MetricsPanelCtrl { ...@@ -98,11 +101,16 @@ class SingleStatCtrl extends MetricsPanelCtrl {
} }
onDataReceived(dataList) { onDataReceived(dataList) {
this.series = dataList.map(this.seriesHandler.bind(this)); const data: any = {};
if (dataList.length > 0 && dataList[0].type === 'table'){
var data: any = {}; this.dataType = 'table';
this.setValues(data); 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.data = data;
this.render(); this.render();
} }
...@@ -117,6 +125,61 @@ class SingleStatCtrl extends MetricsPanelCtrl { ...@@ -117,6 +125,61 @@ class SingleStatCtrl extends MetricsPanelCtrl {
return series; 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) { setColoring(options) {
if (options.background) { if (options.background) {
this.panel.colorValue = false; this.panel.colorValue = false;
......
...@@ -26,11 +26,7 @@ describe('SingleStatCtrl', function() { ...@@ -26,11 +26,7 @@ describe('SingleStatCtrl', function() {
beforeEach(function() { beforeEach(function() {
setupFunc(); setupFunc();
var data = [ ctx.ctrl.onDataReceived(ctx.data);
{target: 'test.cpu1', datapoints: ctx.datapoints}
];
ctx.ctrl.onDataReceived(data);
ctx.data = ctx.ctrl.data; ctx.data = ctx.ctrl.data;
}); });
}; };
...@@ -41,7 +37,9 @@ describe('SingleStatCtrl', function() { ...@@ -41,7 +37,9 @@ describe('SingleStatCtrl', function() {
singleStatScenario('with defaults', function(ctx) { singleStatScenario('with defaults', function(ctx) {
ctx.setup(function() { 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() { it('Should use series avg as default main value', function() {
...@@ -56,7 +54,9 @@ describe('SingleStatCtrl', function() { ...@@ -56,7 +54,9 @@ describe('SingleStatCtrl', function() {
singleStatScenario('showing serie name instead of value', function(ctx) { singleStatScenario('showing serie name instead of value', function(ctx) {
ctx.setup(function() { ctx.setup(function() {
ctx.datapoints = [[10,1], [20,2]]; ctx.data = [
{target: 'test.cpu1', datapoints: [[10,1], [20,2]]}
];
ctx.ctrl.panel.valueName = 'name'; ctx.ctrl.panel.valueName = 'name';
}); });
...@@ -72,7 +72,9 @@ describe('SingleStatCtrl', function() { ...@@ -72,7 +72,9 @@ describe('SingleStatCtrl', function() {
singleStatScenario('MainValue should use same number for decimals as displayed when checking thresholds', function(ctx) { singleStatScenario('MainValue should use same number for decimals as displayed when checking thresholds', function(ctx) {
ctx.setup(function() { 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() { it('Should be rounded', function() {
...@@ -87,7 +89,9 @@ describe('SingleStatCtrl', function() { ...@@ -87,7 +89,9 @@ describe('SingleStatCtrl', function() {
singleStatScenario('When value to text mapping is specified', function(ctx) { singleStatScenario('When value to text mapping is specified', function(ctx) {
ctx.setup(function() { 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'}]; ctx.ctrl.panel.valueMaps = [{value: '10', text: 'OK'}];
}); });
...@@ -106,7 +110,9 @@ describe('SingleStatCtrl', function() { ...@@ -106,7 +110,9 @@ describe('SingleStatCtrl', function() {
singleStatScenario('When range to text mapping is specifiedfor first range', function(ctx) { singleStatScenario('When range to text mapping is specifiedfor first range', function(ctx) {
ctx.setup(function() { ctx.setup(function() {
ctx.datapoints = [[41,50]]; ctx.data = [
{target: 'test.cpu1', datapoints: [[41,50]]}
];
ctx.ctrl.panel.mappingType = 2; ctx.ctrl.panel.mappingType = 2;
ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}]; ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
}); });
...@@ -118,7 +124,9 @@ describe('SingleStatCtrl', function() { ...@@ -118,7 +124,9 @@ describe('SingleStatCtrl', function() {
singleStatScenario('When range to text mapping is specified for other ranges', function(ctx) { singleStatScenario('When range to text mapping is specified for other ranges', function(ctx) {
ctx.setup(function() { ctx.setup(function() {
ctx.datapoints = [[65,75]]; ctx.data = [
{target: 'test.cpu1', datapoints: [[65,75]]}
];
ctx.ctrl.panel.mappingType = 2; ctx.ctrl.panel.mappingType = 2;
ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}]; ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
}); });
...@@ -128,4 +136,60 @@ describe('SingleStatCtrl', function() { ...@@ -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');
});
});
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment