diff --git a/CHANGELOG.md b/CHANGELOG.md index aa3114b39af..5d5721604c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ **New features** - [Issue #1331](https://github.com/grafana/grafana/issues/1331). Graph & Singlestat: New axis/unit format selector and more units (kbytes, Joule, Watt, eV), and new design for graph axis & grid tab and single stat options tab views +- [Issue #1241](https://github.com/grafana/grafana/issues/1242). Timepicker: New option in timepicker (under dashboard settings), to change ``now`` to be for example ``now-1m``, usefull when you want to ignore last minute because it contains incomplete data +- [Issue #171](https://github.com/grafana/grafana/issues/171). Panel: Different time periods, panels can override dashboard relative time and/or add a time shift **Enhancements** - [Issue #1297](https://github.com/grafana/grafana/issues/1297). Graphite: Added cumulative and minimumBelow graphite functions @@ -13,6 +15,9 @@ - [Issue #1298](https://github.com/grafana/grafana/issues/1298). InfluxDB: Fix handling of empty array in templating variable query - [Issue #1309](https://github.com/grafana/grafana/issues/1309). Graph: Fixed issue when using zero as a grid threshold - [Issue #1345](https://github.com/grafana/grafana/issues/1345). UI: Fixed position of confirm modal when scrolled down +- [Issue #1372](https://github.com/grafana/grafana/issues/1372). Graphite: Fix for nested complex queries, where a query references a query that references another query (ie the #[A-Z] syntax) +- [Issue #1363](https://github.com/grafana/grafana/issues/1363). Templating: Fix to allow custom template variables to contain white space, now only splits on ',' +- [Issue #1359](https://github.com/grafana/grafana/issues/1359). Graph: Fix for all series tooltip showing series with all null values when ``Hide Empty`` option is enabled **Tech** - [Issue #1311](https://github.com/grafana/grafana/issues/1311). Tech: Updated Font-Awesome from 3.2 to 4.2 diff --git a/src/app/features/dashboard/partials/panelTime.html b/src/app/features/dashboard/partials/panelTime.html new file mode 100644 index 00000000000..7b8b507a2ae --- /dev/null +++ b/src/app/features/dashboard/partials/panelTime.html @@ -0,0 +1,43 @@ +
+
+
+
    +
  • + +
  • +
  • + Override relative time +
  • +
  • + Last +
  • +
  • + +
  • +
+
+
+
+
    +
  • + +
  • +
  • + Add time shift +
  • +
  • + Amount +
  • +
  • + +
  • +
+
+
+
+
+ diff --git a/src/app/features/dashboard/timeSrv.js b/src/app/features/dashboard/timeSrv.js index 564f30de7fd..0d8dc31567b 100644 --- a/src/app/features/dashboard/timeSrv.js +++ b/src/app/features/dashboard/timeSrv.js @@ -19,12 +19,23 @@ define([ this.time = dashboard.time; this._initTimeFromUrl(); + this._parseTime(); if(this.dashboard.refresh) { this.set_interval(this.dashboard.refresh); } }; + this._parseTime = function() { + // when absolute time is saved in json it is turned to a string + if (_.isString(this.time.from) && this.time.from.indexOf('Z') >= 0) { + this.time.from = new Date(this.time.from); + } + if (_.isString(this.time.to) && this.time.to.indexOf('Z') >= 0) { + this.time.to = new Date(this.time.to); + } + }; + this._parseUrlParam = function(value) { if (value.indexOf('now') !== -1) { return value; @@ -109,9 +120,7 @@ define([ this.timeRange = function(parse) { var _t = this.time; - if(_.isUndefined(_t) || _.isUndefined(_t.from)) { - return false; - } + if(parse === false) { return { from: _t.from, diff --git a/src/app/features/graphite/datasource.js b/src/app/features/graphite/datasource.js index 639e150a320..a486250ac74 100644 --- a/src/app/features/graphite/datasource.js +++ b/src/app/features/graphite/datasource.js @@ -276,6 +276,7 @@ function (angular, _, $, config, kbn, moment) { targetValue = targets[this._seriesRefLetters[i]]; targetValue = targetValue.replace(regex, nestedSeriesRegexReplacer); + targets[this._seriesRefLetters[i]] = targetValue; clean_options.push("target=" + encodeURIComponent(targetValue)); } diff --git a/src/app/features/graphite/queryCtrl.js b/src/app/features/graphite/queryCtrl.js index 0654c474af5..d878386d461 100644 --- a/src/app/features/graphite/queryCtrl.js +++ b/src/app/features/graphite/queryCtrl.js @@ -293,7 +293,7 @@ function (angular, _, config, gfunc, Parser) { function MetricSegment(options) { if (options === '*' || options.value === '*') { this.value = '*'; - this.html = $sce.trustAsHtml(''); + this.html = $sce.trustAsHtml(''); this.expandable = true; return; } diff --git a/src/app/features/influxdb/datasource.js b/src/app/features/influxdb/datasource.js index 692fdbdbbf1..c867fb2a4a5 100644 --- a/src/app/features/influxdb/datasource.js +++ b/src/app/features/influxdb/datasource.js @@ -374,7 +374,7 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) { var fromIsAbsolute = from[from.length-1] === 's'; if (until === 'now()' && !fromIsAbsolute) { - return 'time > now() - ' + from; + return 'time > ' + from; } return 'time > ' + from + ' and time < ' + until; @@ -382,14 +382,7 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) { function getInfluxTime(date) { if (_.isString(date)) { - if (date === 'now') { - return 'now()'; - } - else if (date.indexOf('now') >= 0) { - return date.substring(4); - } - - date = kbn.parseDate(date); + return date.replace('now', 'now()'); } return to_utc_epoch_seconds(date); diff --git a/src/app/features/templating/templateValuesSrv.js b/src/app/features/templating/templateValuesSrv.js index 51b11dfb3fa..baff0a840b8 100644 --- a/src/app/features/templating/templateValuesSrv.js +++ b/src/app/features/templating/templateValuesSrv.js @@ -81,8 +81,8 @@ function (angular, _, kbn) { this._updateNonQueryVariable = function(variable) { // extract options in comma seperated string - variable.options = _.map(variable.query.split(/[\s,]+/), function(text) { - return { text: text, value: text }; + variable.options = _.map(variable.query.split(/[,]+/), function(text) { + return { text: text.trim(), value: text.trim() }; }); if (variable.type === 'interval') { diff --git a/src/app/panels/graph/graph.tooltip.js b/src/app/panels/graph/graph.tooltip.js index 9e750cb6a9c..9e0ff0b8c7b 100644 --- a/src/app/panels/graph/graph.tooltip.js +++ b/src/app/panels/graph/graph.tooltip.js @@ -71,7 +71,7 @@ function ($) { for (i = 0; i < seriesList.length; i++) { series = seriesList[i]; - if (!series.data.length) { + if (!series.data.length || (scope.panel.legend.hideEmpty && series.allIsNull)) { results.push({ hidden: true }); continue; } @@ -163,7 +163,7 @@ function ($) { value = series.formatValue(hoverInfo.value); seriesHtml += '
'; - seriesHtml += ' ' + series.label + ':
'; + seriesHtml += ' ' + series.label + ':
'; seriesHtml += '
' + value + '
'; plot.highlight(i, hoverInfo.hoverIndex); } @@ -174,7 +174,7 @@ function ($) { else if (item) { series = seriesList[item.seriesIndex]; group = '
'; - group += ' ' + series.label + ':
'; + group += ' ' + series.label + ':
'; if (scope.panel.stack && scope.panel.tooltip.value_type === 'individual') { value = item.datapoint[1] - item.datapoint[2]; diff --git a/src/app/panels/graph/module.html b/src/app/panels/graph/module.html index fa205e488a6..20896fc5c83 100644 --- a/src/app/panels/graph/module.html +++ b/src/app/panels/graph/module.html @@ -3,6 +3,10 @@
+ + {{panelMeta.timeInfo}} + +
No datapoints No datapoints returned from metric query diff --git a/src/app/panels/graph/module.js b/src/app/panels/graph/module.js index 7fde36b475f..56a15a7b72e 100644 --- a/src/app/panels/graph/module.js +++ b/src/app/panels/graph/module.js @@ -26,6 +26,7 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) { $scope.panelMeta.addEditorTab('Axes & Grid', 'app/panels/graph/axisEditor.html'); $scope.panelMeta.addEditorTab('Display Styles', 'app/panels/graph/styleEditor.html'); + $scope.panelMeta.addEditorTab('Time range', 'app/features/dashboard/partials/panelTime.html'); $scope.panelMeta.addExtendedMenuItem('Export CSV', '', 'exportCsv()'); $scope.panelMeta.addExtendedMenuItem('Toggle legend', '', 'toggleLegend()'); @@ -88,6 +89,9 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) { value_type: 'cumulative', shared: false, }, + // time overrides + timeFrom: null, + timeShift: null, // metric queries targets: [{}], // series color overrides @@ -114,6 +118,26 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) { $scope.updateTimeRange = function () { $scope.range = timeSrv.timeRange(); $scope.rangeUnparsed = timeSrv.timeRange(false); + + $scope.panelMeta.timeInfo = ""; + + // check panel time overrrides + if ($scope.panel.timeFrom) { + if (_.isString($scope.rangeUnparsed.from)) { + $scope.panelMeta.timeInfo = "last " + $scope.panel.timeFrom; + $scope.rangeUnparsed.from = 'now-' + $scope.panel.timeFrom; + $scope.range.from = kbn.parseDate($scope.rangeUnparsed.from); + } + } + + if ($scope.panel.timeShift) { + var timeShift = '-' + $scope.panel.timeShift; + $scope.panelMeta.timeInfo += ' timeshift ' + timeShift; + $scope.range.from = kbn.parseDateMath(timeShift, $scope.range.from); + $scope.range.to = kbn.parseDateMath(timeShift, $scope.range.to); + $scope.rangeUnparsed = $scope.range; + } + if ($scope.panel.maxDataPoints) { $scope.resolution = $scope.panel.maxDataPoints; } diff --git a/src/app/panels/timepicker/editor.html b/src/app/panels/timepicker/editor.html index fc51d35ad9f..8771bcadb20 100644 --- a/src/app/panels/timepicker/editor.html +++ b/src/app/panels/timepicker/editor.html @@ -1,18 +1,44 @@
-
-
- - -
-
- - -
+
+
+
    +
  • + Relative time options +
  • +
  • + +
  • +
  • + Until +
  • +
  • + now- +
  • +
  • + +
  • +
+
+
+
+
    +
  • + Auto-refresh options +
  • +
  • + +
  • +
+
+
-

-
- - For these changes to fully take effect save and reload the dashboard. -
-

-
+

+
+ + For these changes to fully take effect save and reload the dashboard. +
+

+
diff --git a/src/app/panels/timepicker/module.js b/src/app/panels/timepicker/module.js index eca4936ad00..2f0d9bd75b8 100644 --- a/src/app/panels/timepicker/module.js +++ b/src/app/panels/timepicker/module.js @@ -58,10 +58,14 @@ function (angular, app, _, moment, kbn) { $scope.init = function() { var time = timeSrv.timeRange(true); - if(time) { - $scope.panel.now = timeSrv.timeRange(false).to === "now" ? true : false; - $scope.time = getScopeTimeObj(time.from,time.to); + $scope.panel.now = false; + + var unparsed = timeSrv.timeRange(false); + if (_.isString(unparsed.to) && unparsed.to.indexOf('now') === 0) { + $scope.panel.now = true; } + + $scope.time = getScopeTimeObj(time.from, time.to); }; $scope.customTime = function() { @@ -142,6 +146,10 @@ function (angular, app, _, moment, kbn) { to: "now" }; + if ($scope.panel.nowDelay) { + _filter.to = 'now-' + $scope.panel.nowDelay; + } + timeSrv.setTime(_filter); $scope.time = getScopeTimeObj(kbn.parseDate(_filter.from),new Date()); diff --git a/src/app/partials/dashboard.html b/src/app/partials/dashboard.html index 5795c6e2c8f..d5118a3ee96 100644 --- a/src/app/partials/dashboard.html +++ b/src/app/partials/dashboard.html @@ -104,7 +104,7 @@
- + ADD ROW
diff --git a/src/app/partials/dasheditor.html b/src/app/partials/dasheditor.html index f19c5701c38..fdeb3a4db24 100644 --- a/src/app/partials/dasheditor.html +++ b/src/app/partials/dasheditor.html @@ -54,7 +54,7 @@ - + diff --git a/src/app/partials/roweditor.html b/src/app/partials/roweditor.html index e15f765d2af..450d8a01a3e 100644 --- a/src/app/partials/roweditor.html +++ b/src/app/partials/roweditor.html @@ -41,7 +41,7 @@ - + diff --git a/src/app/partials/templating_editor.html b/src/app/partials/templating_editor.html index c17d0259b2b..1fd600a5701 100644 --- a/src/app/partials/templating_editor.html +++ b/src/app/partials/templating_editor.html @@ -31,7 +31,7 @@ {{variable.query}} - + Edit @@ -39,7 +39,7 @@ - + diff --git a/src/css/less/graph.less b/src/css/less/graph.less index 5ad38283689..46263a43988 100644 --- a/src/css/less/graph.less +++ b/src/css/less/graph.less @@ -141,7 +141,7 @@ vertical-align: top; position: relative; left: 4px; - top: -20px; + top: -25px; } .graph-legend { @@ -260,7 +260,6 @@ transform-origin: right top; } - .axisLabel { color: @textColor; font-size: @fontSizeSmall; @@ -269,3 +268,13 @@ font-size: 12px; } +.graph-time-info { + font-weight: bold; + float: right; + margin-right: 15px; + color: @blue; + font-size: 85%; + position: relative; + top: -20px; +} + diff --git a/src/css/less/panel.less b/src/css/less/panel.less index 7a9a8aba4ff..451431b9d76 100644 --- a/src/css/less/panel.less +++ b/src/css/less/panel.less @@ -49,8 +49,8 @@ .panel-loading { position:absolute; - top: 0px; - right: 4px; + top: -3px; + right: 0px; z-index: 800; } diff --git a/src/test/specs/graph-tooltip-specs.js b/src/test/specs/graph-tooltip-specs.js index b7def74ac89..155469f18a7 100644 --- a/src/test/specs/graph-tooltip-specs.js +++ b/src/test/specs/graph-tooltip-specs.js @@ -19,6 +19,7 @@ define([ tooltip: { shared: true }, + legend: { }, stack: false }; diff --git a/src/test/specs/graphiteDatasource-specs.js b/src/test/specs/graphiteDatasource-specs.js index e23f0e89d4c..58bc62be7ce 100644 --- a/src/test/specs/graphiteDatasource-specs.js +++ b/src/test/specs/graphiteDatasource-specs.js @@ -74,6 +74,13 @@ define([ expect(results[2]).to.be('target=asPercent(series1%2Cseries2)'); }); + it('should replace target placeholder when nesting query references', function() { + var results = ctx.ds.buildGraphiteParams({ + targets: [{target: 'series1'}, {target: 'sumSeries(#A)'}, {target: 'asPercent(#A,#B)'}] + }); + expect(results[2]).to.be('target=' + encodeURIComponent("asPercent(series1,sumSeries(series1))")); + }); + it('should fix wrong minute interval parameters', function() { var results = ctx.ds.buildGraphiteParams({ targets: [{target: "summarize(prod.25m.count, '25m', 'sum')" }] diff --git a/src/test/specs/influxdb-datasource-specs.js b/src/test/specs/influxdb-datasource-specs.js index f8b545e7db2..028423ac1f4 100644 --- a/src/test/specs/influxdb-datasource-specs.js +++ b/src/test/specs/influxdb-datasource-specs.js @@ -17,7 +17,7 @@ define([ describe('When querying influxdb with one target using query editor target spec', function() { var results; var urlExpected = "/series?p=mupp&q=select+mean(value)+from+%22test%22"+ - "+where+time+%3E+now()+-+1h+group+by+time(1s)+order+asc"; + "+where+time+%3E+now()-1h+group+by+time(1s)+order+asc"; var query = { range: { from: 'now-1h', to: 'now' }, targets: [{ series: 'test', column: 'value', function: 'mean' }], @@ -50,7 +50,7 @@ define([ describe('When querying influxdb with one raw query', function() { var results; var urlExpected = "/series?p=mupp&q=select+value+from+series"+ - "+where+time+%3E+now()+-+1h"; + "+where+time+%3E+now()-1h"; var query = { range: { from: 'now-1h', to: 'now' }, targets: [{ query: "select value from series where $timeFilter", rawQuery: true }] @@ -73,7 +73,7 @@ define([ describe('When issuing annotation query', function() { var results; var urlExpected = "/series?p=mupp&q=select+title+from+events.backend_01"+ - "+where+time+%3E+now()+-+1h"; + "+where+time+%3E+now()-1h"; var range = { from: 'now-1h', to: 'now' }; var annotation = { query: 'select title from events.$server where $timeFilter' }; diff --git a/src/test/specs/kbn-format-specs.js b/src/test/specs/kbn-format-specs.js index 785d9376411..7c75e7eca93 100644 --- a/src/test/specs/kbn-format-specs.js +++ b/src/test/specs/kbn-format-specs.js @@ -69,4 +69,15 @@ define([ }); + describe('relative time to date parsing', function() { + it('should handle negative time', function() { + var date = kbn.parseDateMath('-2d', new Date(2014,1,5)); + expect(date.getTime()).to.equal(new Date(2014, 1, 3).getTime()); + }); + it('should handle multiple math expressions', function() { + var date = kbn.parseDateMath('-2d-6h', new Date(2014, 1, 5)); + expect(date.toString()).to.equal(new Date(2014, 1, 2, 18).toString()); + }); + }); + });