mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Singlestat time (#9298)
* Added a timestamp option to single stat * can now choose last time as value * Finished last_time so it formats correctly, updated value stat * fixed som issues, but still issue with testing * Clean up after fake clock in test * timezone-issue fix, fake time for from now test * fix for timedifference
This commit is contained in:
parent
f1036a9382
commit
de17dcf3b0
@ -1,8 +1,9 @@
|
||||
define([
|
||||
'jquery',
|
||||
'lodash'
|
||||
'lodash',
|
||||
'moment'
|
||||
],
|
||||
function($, _) {
|
||||
function($, _, moment) {
|
||||
'use strict';
|
||||
|
||||
var kbn = {};
|
||||
@ -702,6 +703,28 @@ function($, _) {
|
||||
return kbn.toDuration(size, decimals, 'second');
|
||||
};
|
||||
|
||||
kbn.valueFormats.dateTimeAsIso = function(epoch) {
|
||||
var time = moment(epoch);
|
||||
|
||||
if (moment().isSame(epoch, 'day')) {
|
||||
return time.format('HH:mm:ss');
|
||||
}
|
||||
return time.format('YYYY-MM-DD HH:mm:ss');
|
||||
};
|
||||
|
||||
kbn.valueFormats.dateTimeAsUS = function(epoch) {
|
||||
var time = moment(epoch);
|
||||
|
||||
if (moment().isSame(epoch, 'day')) {
|
||||
return time.format('h:mm:ss a');
|
||||
}
|
||||
return time.format('MM/DD/YYYY h:mm:ss a');
|
||||
};
|
||||
|
||||
kbn.valueFormats.dateTimeFromNow = function(epoch) {
|
||||
return moment(epoch).fromNow();
|
||||
};
|
||||
|
||||
///// FORMAT MENU /////
|
||||
|
||||
kbn.getUnitFormats = function() {
|
||||
@ -745,7 +768,15 @@ function($, _) {
|
||||
{text: 'hours (h)', value: 'h' },
|
||||
{text: 'days (d)', value: 'd' },
|
||||
{text: 'duration (ms)', value: 'dtdurationms' },
|
||||
{text: 'duration (s)', value: 'dtdurations' }
|
||||
{text: 'duration (s)', value: 'dtdurations' },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'date & time',
|
||||
submenu: [
|
||||
{text: 'YYYY-MM-DD HH:mm:ss', value: 'dateTimeAsIso' },
|
||||
{text: 'DD/MM/YYYY h:mm:ss a', value: 'dateTimeAsUS' },
|
||||
{text: 'from now', value: 'dateTimeFromNow' },
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -6,7 +6,7 @@
|
||||
<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>
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.valueName" ng-options="f.value as f.text for f in ctrl.valueNameOptions" ng-change="ctrl.refresh()"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form" ng-show="ctrl.dataType === 'table'">
|
||||
|
@ -5,6 +5,7 @@ import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import 'jquery.flot';
|
||||
import 'jquery.flot.gauge';
|
||||
import moment from 'moment';
|
||||
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import config from 'app/core/config';
|
||||
@ -22,7 +23,19 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
invalidGaugeRange: boolean;
|
||||
panel: any;
|
||||
events: any;
|
||||
valueNameOptions: any[] = ['min','max','avg', 'current', 'total', 'name', 'first', 'delta', 'diff', 'range'];
|
||||
valueNameOptions: any[] = [
|
||||
{value : 'min', text: 'Min'},
|
||||
{value : 'max', text: 'Max'},
|
||||
{value : 'avg', text: 'Average'},
|
||||
{value : 'current', text: 'Current'},
|
||||
{value : 'total', text: 'Total'},
|
||||
{value : 'name', text: 'Name'},
|
||||
{value : 'first', text: 'First'},
|
||||
{value : 'delta', text: 'Delta'},
|
||||
{value : 'diff', text: 'Difference'},
|
||||
{value : 'range', text: 'Range'},
|
||||
{value : 'last_time', text: 'Time of last point'}
|
||||
];
|
||||
tableColumnOptions: any;
|
||||
|
||||
// Set and populate defaults
|
||||
@ -93,7 +106,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
|
||||
setUnitFormat(subItem) {
|
||||
this.panel.format = subItem.value;
|
||||
this.render();
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
onDataError(err) {
|
||||
@ -257,8 +270,8 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
}
|
||||
|
||||
if (this.series && this.series.length > 0) {
|
||||
var lastPoint = _.last(this.series[0].datapoints);
|
||||
var lastValue = _.isArray(lastPoint) ? lastPoint[0] : null;
|
||||
let lastPoint = _.last(this.series[0].datapoints);
|
||||
let lastValue = _.isArray(lastPoint) ? lastPoint[0] : null;
|
||||
|
||||
if (this.panel.valueName === 'name') {
|
||||
data.value = 0;
|
||||
@ -268,12 +281,17 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
data.value = 0;
|
||||
data.valueFormatted = _.escape(lastValue);
|
||||
data.valueRounded = 0;
|
||||
} else if (this.panel.valueName === 'last_time') {
|
||||
let formatFunc = kbn.valueFormats[this.panel.format];
|
||||
data.value = lastPoint[1];
|
||||
data.valueRounded = data.value;
|
||||
data.valueFormatted = formatFunc(data.value, 0, 0);
|
||||
} else {
|
||||
data.value = this.series[0].stats[this.panel.valueName];
|
||||
data.flotpairs = this.series[0].flotpairs;
|
||||
|
||||
var decimalInfo = this.getDecimalsForValue(data.value);
|
||||
var formatFunc = kbn.valueFormats[this.panel.format];
|
||||
let decimalInfo = this.getDecimalsForValue(data.value);
|
||||
let formatFunc = kbn.valueFormats[this.panel.format];
|
||||
data.valueFormatted = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals);
|
||||
data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
|
||||
}
|
||||
|
@ -1,13 +1,16 @@
|
||||
///<reference path="../../../../headers/common.d.ts" />
|
||||
|
||||
import {describe, beforeEach, it, sinon, expect, angularMocks} from '../../../../../test/lib/common';
|
||||
import {describe, beforeEach, afterEach, it, sinon, expect, angularMocks} from '../../../../../test/lib/common';
|
||||
|
||||
import angular from 'angular';
|
||||
import helpers from '../../../../../test/specs/helpers';
|
||||
import {SingleStatCtrl} from '../module';
|
||||
import moment from 'moment';
|
||||
|
||||
describe('SingleStatCtrl', function() {
|
||||
var ctx = new helpers.ControllerTestContext();
|
||||
var epoch = 1505826363746;
|
||||
var clock;
|
||||
|
||||
function singleStatScenario(desc, func) {
|
||||
|
||||
@ -70,6 +73,72 @@ describe('SingleStatCtrl', function() {
|
||||
});
|
||||
});
|
||||
|
||||
singleStatScenario('showing last iso time instead of value', function(ctx) {
|
||||
ctx.setup(function() {
|
||||
ctx.data = [
|
||||
{target: 'test.cpu1', datapoints: [[10, 12], [20,1505634997920]]}
|
||||
];
|
||||
ctx.ctrl.panel.valueName = 'last_time';
|
||||
ctx.ctrl.panel.format = 'dateTimeAsIso';
|
||||
});
|
||||
|
||||
it('Should use time instead of value', function() {
|
||||
expect(ctx.data.value).to.be(1505634997920);
|
||||
expect(ctx.data.valueRounded).to.be(1505634997920);
|
||||
});
|
||||
|
||||
it('should set formatted value', function() {
|
||||
expect(ctx.data.valueFormatted).to.be(moment(1505634997920).format('YYYY-MM-DD HH:mm:ss'));
|
||||
});
|
||||
});
|
||||
|
||||
singleStatScenario('showing last us time instead of value', function(ctx) {
|
||||
ctx.setup(function() {
|
||||
ctx.data = [
|
||||
{target: 'test.cpu1', datapoints: [[10, 12], [20,1505634997920]]}
|
||||
];
|
||||
ctx.ctrl.panel.valueName = 'last_time';
|
||||
ctx.ctrl.panel.format = 'dateTimeAsUS';
|
||||
});
|
||||
|
||||
it('Should use time instead of value', function() {
|
||||
expect(ctx.data.value).to.be(1505634997920);
|
||||
expect(ctx.data.valueRounded).to.be(1505634997920);
|
||||
});
|
||||
|
||||
it('should set formatted value', function() {
|
||||
expect(ctx.data.valueFormatted).to.be(moment(1505634997920).format('MM/DD/YYYY H:mm:ss a'));
|
||||
});
|
||||
});
|
||||
|
||||
singleStatScenario('showing last time from now instead of value', function(ctx) {
|
||||
|
||||
beforeEach(() => {
|
||||
clock = sinon.useFakeTimers(epoch);
|
||||
});
|
||||
|
||||
ctx.setup(function() {
|
||||
ctx.data = [
|
||||
{target: 'test.cpu1', datapoints: [[10, 12], [20,1505634997920]]}
|
||||
];
|
||||
ctx.ctrl.panel.valueName = 'last_time';
|
||||
ctx.ctrl.panel.format = 'dateTimeFromNow';
|
||||
});
|
||||
|
||||
it('Should use time instead of value', function() {
|
||||
expect(ctx.data.value).to.be(1505634997920);
|
||||
expect(ctx.data.valueRounded).to.be(1505634997920);
|
||||
});
|
||||
|
||||
it('should set formatted value', function() {
|
||||
expect(ctx.data.valueFormatted).to.be('2 days ago');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clock.restore();
|
||||
});
|
||||
});
|
||||
|
||||
singleStatScenario('MainValue should use same number for decimals as displayed when checking thresholds', function(ctx) {
|
||||
ctx.setup(function() {
|
||||
ctx.data = [
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {describe, beforeEach, it, sinon, expect} from 'test/lib/common';
|
||||
import {describe, beforeEach, afterEach, it, sinon, expect} from 'test/lib/common';
|
||||
|
||||
import * as dateMath from 'app/core/utils/datemath';
|
||||
import moment from 'moment';
|
||||
@ -68,6 +68,10 @@ describe("DateMath", () => {
|
||||
expect(dateMath.parse(thenEx).format(format)).to.eql(anchored.subtract(5, span).format(format));
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clock.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('rounding', () => {
|
||||
@ -89,6 +93,10 @@ describe("DateMath", () => {
|
||||
expect(dateMath.parse('now/' + span, true).format(format)).to.eql(now.endOf(span).format(format));
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clock.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isValid', () => {
|
||||
|
@ -1,7 +1,8 @@
|
||||
define([
|
||||
'app/core/utils/kbn',
|
||||
'app/core/utils/datemath'
|
||||
], function(kbn, dateMath) {
|
||||
'app/core/utils/datemath',
|
||||
'moment'
|
||||
], function(kbn, dateMath, moment) {
|
||||
'use strict';
|
||||
|
||||
describe('unit format menu', function() {
|
||||
@ -94,6 +95,42 @@ define([
|
||||
describeValueFormat('d', 245, 100, 0, '35 week');
|
||||
describeValueFormat('d', 2456, 10, 0, '6.73 year');
|
||||
|
||||
describe('date time formats', function() {
|
||||
it('should format as iso date', function() {
|
||||
var str = kbn.valueFormats.dateTimeAsIso(1505634997920, 1);
|
||||
expect(str).to.be(moment(1505634997920).format('YYYY-MM-DD HH:mm:ss'));
|
||||
});
|
||||
|
||||
it('should format as iso date and skip date when today', function() {
|
||||
var now = moment();
|
||||
var str = kbn.valueFormats.dateTimeAsIso(now.valueOf(), 1);
|
||||
expect(str).to.be(now.format("HH:mm:ss"));
|
||||
});
|
||||
|
||||
it('should format as US date', function() {
|
||||
var str = kbn.valueFormats.dateTimeAsUS(1505634997920, 1);
|
||||
expect(str).to.be(moment(1505634997920).format('MM/DD/YYYY H:mm:ss a'));
|
||||
});
|
||||
|
||||
it('should format as US date and skip date when today', function() {
|
||||
var now = moment();
|
||||
var str = kbn.valueFormats.dateTimeAsUS(now.valueOf(), 1);
|
||||
expect(str).to.be(now.format("h:mm:ss a"));
|
||||
});
|
||||
|
||||
it('should format as from now with days', function() {
|
||||
var daysAgo = moment().add(-7, 'd');
|
||||
var str = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), 1);
|
||||
expect(str).to.be('7 days ago');
|
||||
});
|
||||
|
||||
it('should format as from now with minutes', function() {
|
||||
var daysAgo = moment().add(-2, 'm');
|
||||
var str = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), 1);
|
||||
expect(str).to.be('2 minutes ago');
|
||||
});
|
||||
});
|
||||
|
||||
describe('kbn.toFixed and negative decimals', function() {
|
||||
it('should treat as zero decimals', function() {
|
||||
var str = kbn.toFixed(186.123, -2);
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
var _global = <any>(window);
|
||||
var beforeEach = _global.beforeEach;
|
||||
var afterEach = _global.afterEach;
|
||||
var before = _global.before;
|
||||
var describe = _global.describe;
|
||||
var it = _global.it;
|
||||
@ -15,6 +16,7 @@ var angularMocks = {
|
||||
|
||||
export {
|
||||
beforeEach,
|
||||
afterEach,
|
||||
before,
|
||||
describe,
|
||||
it,
|
||||
|
@ -23,6 +23,7 @@ module.exports = function(config, grunt) {
|
||||
|
||||
gaze([
|
||||
config.srcDir + '/app/**/*',
|
||||
config.srcDir + '/test/**/*',
|
||||
config.srcDir + '/sass/**/*',
|
||||
], function(err, watcher) {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user