diff --git a/CHANGELOG.md b/CHANGELOG.md index 00daadbdffa..44bc4d0be12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,14 @@ # 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 # 1.9.0-rc1 (2014-11-17) diff --git a/src/app/components/panelmeta.js b/src/app/components/panelmeta.js index 9b2b2d90104..fc26f1c3d0e 100644 --- a/src/app/components/panelmeta.js +++ b/src/app/components/panelmeta.js @@ -5,7 +5,6 @@ function () { function PanelMeta(options) { this.description = options.description; - this.titlePos = options.titlePos; this.fullscreen = options.fullscreen; this.menu = []; this.editorTabs = []; diff --git a/src/app/dashboards/scripted.js b/src/app/dashboards/scripted.js index 8fca0248496..93e67aae5b9 100644 --- a/src/app/dashboards/scripted.js +++ b/src/app/dashboards/scripted.js @@ -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" }; diff --git a/src/app/dashboards/scripted_async.js b/src/app/dashboards/scripted_async.js index 0a50aa44d5e..71d77892c3b 100644 --- a/src/app/dashboards/scripted_async.js +++ b/src/app/dashboards/scripted_async.js @@ -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; diff --git a/src/app/dashboards/scripted_templated.js b/src/app/dashboards/scripted_templated.js index 9ce145f4606..a0d3e081293 100644 --- a/src/app/dashboards/scripted_templated.js +++ b/src/app/dashboards/scripted_templated.js @@ -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: [ diff --git a/src/app/directives/panelMenu.js b/src/app/directives/panelMenu.js index 4b3970fba39..286d6e0bab7 100644 --- a/src/app/directives/panelMenu.js +++ b/src/app/directives/panelMenu.js @@ -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); } diff --git a/src/app/panels/singlestat/editor.html b/src/app/panels/singlestat/editor.html index 3265b3961d7..67df1405682 100644 --- a/src/app/panels/singlestat/editor.html +++ b/src/app/panels/singlestat/editor.html @@ -13,6 +13,10 @@ +
+ + +
@@ -38,6 +42,9 @@
+ + +
Coloring
@@ -54,9 +61,7 @@ invert order
- -
Spark lines
@@ -72,5 +77,39 @@
+
+
+
Value to text mapping
+
+ +
+
+
    +
  • + +
  • + +
  • + +
  • +
  • + +
  • +
  • + +
  • + +
  • + + + +
  • + +
+
+
+
+
+
diff --git a/src/app/panels/singlestat/module.js b/src/app/panels/singlestat/module.js index 91dd019c4b7..3e248c7361a 100644 --- a/src/app/panels/singlestat/module.js +++ b/src/app/panels/singlestat/module.js @@ -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(); }); }); diff --git a/src/app/services/graphite/gfunc.js b/src/app/services/graphite/gfunc.js index b875eec9719..af186633ea2 100644 --- a/src/app/services/graphite/gfunc.js +++ b/src/app/services/graphite/gfunc.js @@ -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] }); @@ -357,6 +359,17 @@ function (_) { defaultParams: ['1d'] }); + 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, diff --git a/src/app/services/graphite/lexer.js b/src/app/services/graphite/lexer.js index ee37e6a22ba..7306737d96e 100644 --- a/src/app/services/graphite/lexer.js +++ b/src/app/services/graphite/lexer.js @@ -129,6 +129,7 @@ define([ i === 63 || // ? i === 37 || // % i === 35 || // # + i === 61 || // = i >= 97 && i <= 122; // a-z } diff --git a/src/app/services/influxdb/influxdbDatasource.js b/src/app/services/influxdb/influxdbDatasource.js index 43e245bfc4c..c30f950e51b 100644 --- a/src/app/services/influxdb/influxdbDatasource.js +++ b/src/app/services/influxdb/influxdbDatasource.js @@ -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; } diff --git a/src/css/less/variables.light.less b/src/css/less/variables.light.less index 063d0148c37..0e78514cea0 100644 --- a/src/css/less/variables.light.less +++ b/src/css/less/variables.light.less @@ -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; diff --git a/src/test/specs/lexer-specs.js b/src/test/specs/lexer-specs.js index 04531423802..65260d2e013 100644 --- a/src/test/specs/lexer-specs.js +++ b/src/test/specs/lexer-specs.js @@ -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();