Commit 6ea99540 by Torkel Ödegaard

Merge branch 'master' of github.com:grafana/grafana

parents b22b3e5b 61609780
......@@ -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,69 @@ 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];
if (_.isString(data.value)) {
data.valueFormatted = _.escape(data.value);
data.value = 0;
data.valueRounded = 0;
} else {
const decimalInfo = this.getDecimalsForValue(data.value);
const 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);
}
this.setValueMapping(data);
}
setColoring(options) {
if (options.background) {
this.panel.colorValue = false;
......@@ -192,10 +263,10 @@ class SingleStatCtrl extends MetricsPanelCtrl {
if (this.panel.valueName === 'name') {
data.value = 0;
data.valueRounded = 0;
data.valueFormated = this.series[0].alias;
data.valueFormatted = this.series[0].alias;
} else if (_.isString(lastValue)) {
data.value = 0;
data.valueFormated = _.escape(lastValue);
data.valueFormatted = _.escape(lastValue);
data.valueRounded = 0;
} else {
data.value = this.series[0].stats[this.panel.valueName];
......@@ -203,7 +274,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
var decimalInfo = this.getDecimalsForValue(data.value);
var formatFunc = kbn.valueFormats[this.panel.format];
data.valueFormated = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals);
data.valueFormatted = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals);
data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
}
......@@ -211,7 +282,10 @@ class SingleStatCtrl extends MetricsPanelCtrl {
data.scopedVars = _.extend({}, this.panel.scopedVars);
data.scopedVars["__name"] = {value: this.series[0].label};
}
this.setValueMapping(data);
}
setValueMapping(data) {
// check value to text mappings if its enabled
if (this.panel.mappingType === 1) {
for (let i = 0; i < this.panel.valueMaps.length; i++) {
......@@ -219,7 +293,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
// special null case
if (map.value === 'null') {
if (data.value === null || data.value === void 0) {
data.valueFormated = map.text;
data.valueFormatted = map.text;
return;
}
continue;
......@@ -228,7 +302,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
// value/number to text mapping
var value = parseFloat(map.value);
if (value === data.valueRounded) {
data.valueFormated = map.text;
data.valueFormatted = map.text;
return;
}
}
......@@ -238,7 +312,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
// special null case
if (map.from === 'null' && map.to === 'null') {
if (data.value === null || data.value === void 0) {
data.valueFormated = map.text;
data.valueFormatted = map.text;
return;
}
continue;
......@@ -248,14 +322,14 @@ class SingleStatCtrl extends MetricsPanelCtrl {
var from = parseFloat(map.from);
var to = parseFloat(map.to);
if (to >= data.valueRounded && from <= data.valueRounded) {
data.valueFormated = map.text;
data.valueFormatted = map.text;
return;
}
}
}
if (data.value === null || data.value === void 0) {
data.valueFormated = "no value";
data.valueFormatted = "no value";
}
}
......@@ -317,7 +391,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
if (panel.prefix) { body += getSpan('singlestat-panel-prefix', panel.prefixFontSize, panel.prefix); }
var value = applyColoringThresholds(data.value, data.valueFormated);
var value = applyColoringThresholds(data.value, data.valueFormatted);
body += getSpan('singlestat-panel-value', panel.valueFontSize, value);
if (panel.postfix) { body += getSpan('singlestat-panel-postfix', panel.postfixFontSize, panel.postfix); }
......@@ -329,7 +403,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
function getValueText() {
var result = panel.prefix ? panel.prefix : '';
result += data.valueFormated;
result += data.valueFormatted;
result += panel.postfix ? panel.postfix : '';
return result;
......
......@@ -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() {
......@@ -49,14 +47,16 @@ describe('SingleStatCtrl', function() {
expect(ctx.data.valueRounded).to.be(15);
});
it('should set formated falue', function() {
expect(ctx.data.valueFormated).to.be('15');
it('should set formatted falue', function() {
expect(ctx.data.valueFormatted).to.be('15');
});
});
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';
});
......@@ -65,14 +65,16 @@ describe('SingleStatCtrl', function() {
expect(ctx.data.valueRounded).to.be(0);
});
it('should set formated falue', function() {
expect(ctx.data.valueFormated).to.be('test.cpu1');
it('should set formatted falue', function() {
expect(ctx.data.valueFormatted).to.be('test.cpu1');
});
});
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() {
......@@ -80,14 +82,16 @@ describe('SingleStatCtrl', function() {
expect(ctx.data.valueRounded).to.be(100);
});
it('should set formated falue', function() {
expect(ctx.data.valueFormated).to.be('100');
it('should set formatted falue', function() {
expect(ctx.data.valueFormatted).to.be('100');
});
});
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'}];
});
......@@ -100,32 +104,157 @@ describe('SingleStatCtrl', function() {
});
it('Should replace value with text', function() {
expect(ctx.data.valueFormated).to.be('OK');
expect(ctx.data.valueFormatted).to.be('OK');
});
});
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'}];
});
it('Should replace value with text OK', function() {
expect(ctx.data.valueFormated).to.be('OK');
expect(ctx.data.valueFormatted).to.be('OK');
});
});
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'}];
});
it('Should replace value with text NOT OK', function() {
expect(ctx.data.valueFormated).to.be('NOT OK');
expect(ctx.data.valueFormatted).to.be('NOT OK');
});
});
describe('When table data', function() {
const tableData = [{
"columns": [
{ "text": "Time", "type": "time" },
{ "text": "test1" },
{ "text": "mean" },
{ "text": "test2" }
],
"rows": [
[1492759673649, 'ignore1', 15, 'ignore2']
],
"type": "table"
}];
singleStatScenario('with default values', function(ctx) {
ctx.setup(function() {
ctx.data = tableData;
ctx.ctrl.panel.tableColumn = 'mean';
});
it('Should use first rows value 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');
});
});
singleStatScenario('MainValue should use same number for decimals as displayed when checking thresholds', function(ctx) {
ctx.setup(function() {
ctx.data = tableData;
ctx.data[0].rows[0] = [1492759673649,'ignore1', 99.99999, 'ignore2'];
ctx.ctrl.panel.tableColumn = 'mean';
});
it('Should be rounded', function() {
expect(ctx.data.value).to.be(99.99999);
expect(ctx.data.valueRounded).to.be(100);
});
it('should set formatted falue', function() {
expect(ctx.data.valueFormatted).to.be('100');
});
});
singleStatScenario('When value to text mapping is specified', function(ctx) {
ctx.setup(function() {
ctx.data = tableData;
ctx.data[0].rows[0] = [1492759673649,'ignore1', 9.9, 'ignore2'];
ctx.ctrl.panel.tableColumn = 'mean';
ctx.ctrl.panel.valueMaps = [{value: '10', text: 'OK'}];
});
it('value should remain', function() {
expect(ctx.data.value).to.be(9.9);
});
it('round should be rounded up', function() {
expect(ctx.data.valueRounded).to.be(10);
});
it('Should replace value with text', function() {
expect(ctx.data.valueFormatted).to.be('OK');
});
});
singleStatScenario('When range to text mapping is specified for first range', function(ctx) {
ctx.setup(function() {
ctx.data = tableData;
ctx.data[0].rows[0] = [1492759673649,'ignore1', 41, 'ignore2'];
ctx.ctrl.panel.tableColumn = 'mean';
ctx.ctrl.panel.mappingType = 2;
ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
});
it('Should replace value with text OK', function() {
expect(ctx.data.valueFormatted).to.be('OK');
});
});
singleStatScenario('When range to text mapping is specified for other ranges', function(ctx) {
ctx.setup(function() {
ctx.data = tableData;
ctx.data[0].rows[0] = [1492759673649,'ignore1', 65, 'ignore2'];
ctx.ctrl.panel.tableColumn = 'mean';
ctx.ctrl.panel.mappingType = 2;
ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
});
it('Should replace value with text NOT OK', function() {
expect(ctx.data.valueFormatted).to.be('NOT OK');
});
});
singleStatScenario('When value is string', function(ctx) {
ctx.setup(function() {
ctx.data = tableData;
ctx.data[0].rows[0] = [1492759673649,'ignore1', 65, 'ignore2'];
ctx.ctrl.panel.tableColumn = 'test1';
});
it('Should replace value with text NOT OK', function() {
expect(ctx.data.valueFormatted).to.be('ignore1');
});
});
});
});
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