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 @@
<h5 class="section-heading">Value</h5>
<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>
<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>
</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>
<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>
......
......@@ -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;
......
......@@ -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');
});
});
});
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