Commit 63273f4c by Torkel Ödegaard

Merge branch 'master' of github.com:torkelo/grafana-private into pro

parents 79beefe5 d7ef6dae
# 1.9.0 (unreleased)
**Enhancements**
- [Issue #1130](https://github.com/grafana/grafana/issues/1130). SinglestatPanel: Added null point handling, and value to text mapping
**Fixes**
- [Issue #1087](https://github.com/grafana/grafana/issues/1087). Panel: Fixed IE9 crash due to angular drag drop
- [Issue #1093](https://github.com/grafana/grafana/issues/1093). SingleStatPanel: Fixed position for drilldown link tooltip when dashboard requires scrolling
- [Issue #1095](https://github.com/grafana/grafana/issues/1095). DrilldownLink: template variables in params property was not interpolated
- [Issue #1114](https://github.com/grafana/grafana/issues/1114). Graphite: Lexer fix, allow equal sign (=) in metric paths
- [Issue #1136](https://github.com/grafana/grafana/issues/1136). Graph: Fix to legend value Max and negative values
- [Issue #1150](https://github.com/grafana/grafana/issues/1150). SinglestatPanel: Fixed absolute drilldown link issue
# 1.9.0-rc1 (2014-11-17)
......
......@@ -5,7 +5,6 @@ function () {
function PanelMeta(options) {
this.description = options.description;
this.titlePos = options.titlePos;
this.fullscreen = options.fullscreen;
this.menu = [];
this.editorTabs = [];
......
......@@ -64,7 +64,7 @@ function (_, kbn) {
var result = [];
this.stats.total = 0;
this.stats.max = Number.MIN_VALUE;
this.stats.max = -Number.MAX_VALUE;
this.stats.min = Number.MAX_VALUE;
this.stats.avg = null;
this.stats.current = null;
......@@ -106,7 +106,7 @@ function (_, kbn) {
this.stats.timeStep = this.datapoints[1][1] - this.datapoints[0][1];
}
if (this.stats.max === Number.MIN_VALUE) { this.stats.max = null; }
if (this.stats.max === -Number.MAX_VALUE) { this.stats.max = null; }
if (this.stats.min === Number.MAX_VALUE) { this.stats.min = null; }
if (result.length) {
......
......@@ -17,7 +17,7 @@
var window, document, ARGS, $, jQuery, moment, kbn;
// Setup some variables
var dashboard, timspan;
var dashboard;
// All url parameters are available via the ARGS object
var ARGS;
......@@ -30,11 +30,11 @@ dashboard = {
// Set a title
dashboard.title = 'Scripted dash';
// set default time
// Set default time
// time can be overriden in the url using from/to parameteres, but this is
// handled automatically in grafana core during dashboard initialization
dashboard.time = {
from: 'now-6h',
from: "now-6h",
to: "now"
};
......
......@@ -22,10 +22,7 @@ var window, document, ARGS, $, jQuery, moment, kbn;
return function(callback) {
// Setup some variables
var dashboard, timspan;
// Set a default timespan if one isn't specified
timspan = ARGS.from || 'now-1d';
var dashboard;
// Intialize a skeleton with nothing but a rows array and service object
dashboard = {
......@@ -35,9 +32,13 @@ return function(callback) {
// Set a title
dashboard.title = 'Scripted dash';
// Set default time
// time can be overriden in the url using from/to parameteres, but this is
// handled automatically in grafana core during dashboard initialization
dashboard.time = {
from: timspan,
to: "now"
from: "now-6h",
to: "now"
};
var rows = 1;
......
......@@ -17,25 +17,27 @@
var window, document, ARGS, $, jQuery, moment, kbn;
// Setup some variables
var dashboard, timspan;
var dashboard;
// All url parameters are available via the ARGS object
var ARGS;
// Set a default timespan if one isn't specified
timspan = ARGS.from || 'now-1d';
// Intialize a skeleton with nothing but a rows array and service object
dashboard = {
rows : [],
};
// Set a title
dashboard.title = 'Scripted dash';
dashboard.title = 'Scripted and templated dash';
// Set default time
// time can be overriden in the url using from/to parameteres, but this is
// handled automatically in grafana core during dashboard initialization
dashboard.time = {
from: timspan,
from: "now-6h",
to: "now"
};
dashboard.templating = {
enable: true,
list: [
......
......@@ -147,11 +147,6 @@ function (angular, $, _) {
dismiss(2200);
};
if ($scope.panelMeta.titlePos && $scope.panel.title) {
elem.css('text-align', 'left');
$link.css('padding-left', '10px');
}
elem.click(showMenu);
$compile(elem.contents())($scope);
}
......
......@@ -13,6 +13,10 @@
<label class="small">Postfix</label>
<input type="text" class="input-small" ng-model="panel.postfix" ng-blur="render()" ng-trim="false"></input>
</div>
<div class="editor-option">
<label class="small">Null point mode<tip>Define how null values should handled, connected = ignored</tip></label>
<select class="input-medium" ng-model="panel.nullPointMode" ng-options="f for f in ['connected', 'null', 'null as zero']" ng-change="get_data()"></select>
</div>
</div>
<div class="section">
......@@ -38,6 +42,9 @@
<select class="input-small" ng-model="panel.format" ng-options="f for f in ['none','short','bytes', 'bits', 'bps', 's', 'ms', 'µs', 'ns', 'percent']" ng-change="render()"></select>
</div>
</div>
</div>
<div class="editor-row">
<div class="section">
<h5>Coloring</h5>
<editor-opt-bool text="Background" model="panel.colorBackground" change="setColoring({background: true})"></editor-opt-bool>
......@@ -54,9 +61,7 @@
<a class="pointer" ng-click="invertColorOrder()">invert order</a>
</div>
</div>
</div>
<div class="editor-row">
<div class="section">
<h5>Spark lines</h5>
<editor-opt-bool text="Spark line" model="panel.sparkline.show" change="render()"></editor-opt-bool>
......@@ -72,5 +77,39 @@
</div>
</div>
<div class="editor-row">
<div class="section">
<h5>Value to text mapping</h5>
<div class="editor-option">
<label class="small">Specify mappings</label>
<div class="grafana-target">
<div class="grafana-target-inner">
<ul class="grafana-segment-list">
<li class="grafana-target-segment" ng-repeat-start="map in panel.valueMaps">
<i class="icon-remove pointer" ng-click="removeValueMap(map)"></i>
</li>
<li>
<input type="text" ng-model="map.value" placeholder="value" class="input-mini grafana-target-segment-input" ng-blur="render()">
</li>
<li class="grafana-target-segment">
<i class="icon-arrow-right"></i>
</li>
<li ng-repeat-end>
<input type="text" placeholder="text" ng-model="map.text" class="input-mini grafana-target-segment-input" ng-blur="render()">
</li>
<li>
<a class="pointer grafana-target-segment" ng-click="addValueMap();">
<i class="icon-plus"></i>
</a>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
</div>
......@@ -18,7 +18,6 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
$scope.panelMeta = new PanelMeta({
description: 'Singlestat panel',
titlePos: 'left',
fullscreen: true,
metricsEditor: true
});
......@@ -35,9 +34,14 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
format: 'none',
prefix: '',
postfix: '',
nullText: null,
valueMaps: [
{ value: 'null', op: '=', text: 'N/A' }
],
nullPointMode: 'connected',
valueName: 'avg',
prefixFontSize: '50%',
valueFontSize: '100%',
valueFontSize: '80%',
postfixFontSize: '50%',
thresholds: '',
colorBackground: false,
......@@ -99,7 +103,7 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
alias: seriesData.target,
});
series.flotpairs = series.getFlotPairs('connected');
series.flotpairs = series.getFlotPairs($scope.panel.nullPointMode);
return series;
};
......@@ -170,15 +174,12 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
if (!$scope.series || $scope.series.length === 0) {
data.flotpairs = [];
data.mainValue = Number.NaN;
data.mainValueFormated = 'NaN';
data.mainValueFormated = $scope.getFormatedValue(null);
}
else {
var series = $scope.series[0];
data.mainValue = series.stats[$scope.panel.valueName];
var decimalInfo = $scope.getDecimalsForValue(data.mainValue);
var formatFunc = kbn.valueFormats[$scope.panel.format];
data.mainValueFormated = formatFunc(data.mainValue, decimalInfo.decimals, decimalInfo.scaledDecimals);
data.mainValueFormated = $scope.getFormatedValue(data.mainValue);
data.flotpairs = series.flotpairs;
}
......@@ -192,6 +193,44 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
$scope.$emit('render');
};
$scope.getFormatedValue = function(mainValue) {
// first check value to text mappings
for(var i = 0; i < $scope.panel.valueMaps.length; i++) {
var map = $scope.panel.valueMaps[i];
// special null case
if (map.value === 'null') {
if (mainValue === null || mainValue === void 0) {
return map.text;
}
continue;
}
// value/number to text mapping
var value = parseFloat(map.value);
if (value === mainValue) {
return map.text;
}
}
if (mainValue === null || mainValue === void 0) {
return "no value";
}
var decimalInfo = $scope.getDecimalsForValue(mainValue);
var formatFunc = kbn.valueFormats[$scope.panel.format];
return formatFunc(mainValue, decimalInfo.decimals, decimalInfo.scaledDecimals);
};
$scope.removeValueMap = function(map) {
var index = _.indexOf($scope.panel.valueMaps, map);
$scope.panel.valueMaps.splice(index, 1);
$scope.render();
};
$scope.addValueMap = function() {
$scope.panel.valueMaps.push({value: '', op: '=', text: '' });
};
$scope.init();
});
});
......@@ -148,7 +148,7 @@ function (angular, app, _, $) {
var body = getBigValueHtml();
if (panel.colorBackground && data.mainValue) {
if (panel.colorBackground && !isNaN(data.mainValue)) {
var color = getColorForValue(data.mainValue);
if (color) {
$panelContainer.css('background-color', color);
......@@ -186,7 +186,13 @@ function (angular, app, _, $) {
var linkInfo = linkSrv.getPanelLinkAnchorInfo(panel.links[0]);
if (linkInfo.href[0] === '#') { linkInfo.href = linkInfo.href.substring(1); }
$timeout(function() { $location.url(linkInfo.href); });
if (linkInfo.href.indexOf('http') === 0) {
window.location.href = linkInfo.href;
} else {
$timeout(function() {
$location.url(linkInfo.href);
});
}
drilldownTooltip.detach();
});
......
......@@ -194,7 +194,7 @@ function (_) {
name: "aliasSub",
category: categories.Special,
params: [{ name: "search", type: 'string' }, { name: "replace", type: 'string' }],
defaultParams: ['', '']
defaultParams: ['', '\\1']
});
addFuncDef({
......@@ -241,6 +241,8 @@ function (_) {
params: [
{ name: "node", type: "int", options: [0,1,2,3,4,5,6,7,8,9,10,12] },
{ name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
{ name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
{ name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
],
defaultParams: [3]
});
......@@ -358,6 +360,17 @@ function (_) {
});
addFuncDef({
name: 'timeStack',
category: categories.Transform,
params: [
{ name: "timeShiftUnit", type: "select", options: ['1h', '6h', '12h', '1d', '2d', '7d', '14d', '30d'] },
{ name: "timeShiftStart", type: "int" },
{ name: "timeShiftEnd", type: "int" }
],
defaultParams: ['1d', 0, 7]
});
addFuncDef({
name: 'summarize',
category: categories.Transform,
params: [
......
......@@ -129,6 +129,7 @@ define([
i === 63 || // ?
i === 37 || // %
i === 35 || // #
i === 61 || // =
i >= 97 && i <= 122; // a-z
}
......
......@@ -372,8 +372,9 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
function getTimeFilter(options) {
var from = getInfluxTime(options.range.from);
var until = getInfluxTime(options.range.to);
var fromIsAbsolute = from[from.length-1] === 's';
if (until === 'now()') {
if (until === 'now()' && !fromIsAbsolute) {
return 'time > now() - ' + from;
}
......
......@@ -14,7 +14,7 @@
@grayDark: lighten(#000, 20%); // #333
@gray: lighten(#000, 33.5%); // #555
@grayLight: lighten(#000, 60%); // #999
@grayLighter: lighten(#000, 93.5%); // #eee
@grayLighter: lighten(#000, 97.5%); // #eee
@white: #fff;
......@@ -53,14 +53,14 @@
// Scaffolding
// -------------------------
@bodyBackground: @grayLighter;
@bodyBackground: #EAEAEA;
@textColor: #666;
// Links
// -------------------------
@linkColor: @textColor;
@linkColorDisabled: lighten(@linkColor,35%);
@linkColor: darken(@textColor, 20%);
@linkColorDisabled: lighten(@linkColor,30%);
@linkColorHover: @blue;
......
......@@ -29,6 +29,12 @@ define([
expect(tokens[2].value).to.be('192-168-1-1');
});
it('should tokenize metric expression with equal sign', function() {
var lexer = new Lexer('apps=test');
var tokens = lexer.tokenize();
expect(tokens[0].value).to.be('apps=test');
});
it('simple function2', function() {
var lexer = new Lexer('offset(test.metric, -100)');
var tokens = lexer.tokenize();
......
......@@ -35,6 +35,14 @@ define([
expect(series.stats.current).to.be(10);
});
it('max value should work for negative values', function() {
series = new TimeSeries({
datapoints: [[-10,1], [-4, 2]]
});
series.getFlotPairs('null', yAxisFormats);
expect(series.stats.max).to.be(-4);
});
});
describe('series overrides', function() {
......
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