Commit 19d5d322 by Torkel Ödegaard

simplified a lot of the time series data manipulation prior to handing it to…

simplified a lot of the time series data manipulation prior to handing it to flot, kibana used a lot of code to sort and make sure all time points needed had values, this is not needed for graphite time series data. Fixes som strange issues with "null point mode" and inconsistent rendering between graphite and grafana flot,
parent d90f2537
......@@ -82,7 +82,7 @@ function (angular, $, kbn, moment, _) {
});
// Set barwidth based on specified interval
var barwidth = kbn.interval_to_ms(scope.panel.interval);
var barwidth = kbn.interval_to_ms(scope.interval);
var stack = scope.panel.stack ? true : null;
......@@ -121,7 +121,7 @@ function (angular, $, kbn, moment, _) {
mode: "time",
min: _.isUndefined(scope.range.from) ? null : scope.range.from.getTime(),
max: _.isUndefined(scope.range.to) ? null : scope.range.to.getTime(),
timeformat: time_format(scope.panel.interval),
timeformat: time_format(scope.interval),
label: "Datetime",
ticks: elem.width()/100
},
......@@ -139,21 +139,8 @@ function (angular, $, kbn, moment, _) {
addAnnotations(options);
// when rendering stacked bars, we need to ensure each point that has data is zero-filled
// so that the stacking happens in the proper order
var required_times = [];
if (data.length > 1) {
required_times = Array.prototype.concat.apply([], _.map(data, function (query) {
return query.time_series.getOrderedTimes();
}));
required_times = _.uniq(required_times.sort(function (a, b) {
// decending numeric sort
return a-b;
}), true);
}
for (var i = 0; i < data.length; i++) {
var _d = data[i].time_series.getFlotPairs(required_times, scope.panel.nullPointMode);
var _d = data[i].time_series.getFlotPairs(scope.panel.nullPointMode);
data[i].yaxis = data[i].info.yaxis;
data[i].data = _d;
data[i].info.y_format = data[i].yaxis === 1 ? scope.panel.y_format : scope.panel.y2_format;
......
......@@ -8,10 +8,6 @@
<div class="editor-option">
<label class="small">Y-Axis</label><input type="checkbox" ng-model="panel['y-axis']" ng-checked="panel['y-axis']" ng-change="render()">
</div>
<div class="editor-option" ng-show="panel.points">
<label class="small">Point Radius</label>
<select class="input-mini" ng-model="panel.pointradius" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10]" ng-change="render()"></select>
</div>
<div class="editor-option">
<label class="small">Left Y Format <tip>Y-axis formatting</tip></label>
<select class="input-small" ng-model="panel.y_format" ng-options="f for f in ['none','short','bytes', 'ms']" ng-change="render()"></select>
......
......@@ -101,8 +101,8 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
/** @scratch /panels/histogram/3
* y_format:: 'none','bytes','short '
*/
y_format : 'none',
y2_format : 'none',
y_format : 'short',
y2_format : 'short',
/** @scratch /panels/histogram/5
* grid object:: Min and max y-axis values
* grid.min::: Minimum y-axis value
......@@ -130,22 +130,10 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
sort : ['_score','desc']
},
/** @scratch /panels/histogram/3
* ==== Interval options
* auto_int:: Automatically scale intervals?
*/
auto_int : true,
/** @scratch /panels/histogram/3
* resolution:: If auto_int is true, shoot for this many bars.
*/
resolution : 100,
/** @scratch /panels/histogram/3
* interval:: If auto_int is set to false, use this as the interval.
*/
interval : '5m',
/** @scratch /panels/histogram/3
* interval:: Array of possible intervals in the *View* selector. Example [`auto',`1s',`5m',`3h']
*/
intervals : ['auto','1s','1m','5m','10m','30m','1h','3h','12h','1d','1w','1y'],
/** @scratch /panels/histogram/3
* ==== Drawing options
* lines:: Show line chart
......@@ -227,15 +215,6 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
};
$scope.set_interval = function(interval) {
if(interval !== 'auto') {
$scope.panel.auto_int = false;
$scope.panel.interval = interval;
} else {
$scope.panel.auto_int = true;
}
};
$scope.remove_panel_from_row = function(row, panel) {
if ($scope.fullscreen) {
$rootScope.$emit('panel-fullscreen-exit');
......@@ -250,24 +229,15 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
$scope.get_data();
};
$scope.interval_label = function(interval) {
return $scope.panel.auto_int && interval === $scope.panel.interval ? interval+" (auto)" : interval;
};
$scope.updateTimeRange = function () {
var range = filterSrv.timeRange();
var interval = filterSrv.timeRange();
if ($scope.panel.auto_int) {
if (range) {
interval = kbn.secondsToHms(
kbn.calculate_interval(range.from, range.to, $scope.panel.resolution, 0) / 1000
);
}
}
$scope.range = filterSrv.timeRange();
$scope.interval = '10m';
$scope.interval = $scope.panel.interval = interval || '10m';
$scope.range = range;
if ($scope.range) {
$scope.interval = kbn.secondsToHms(
kbn.calculate_interval($scope.range.from, $scope.range.to, $scope.panel.resolution, 0) / 1000
);
}
};
$scope.colors = [
......@@ -327,18 +297,10 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
var color = $scope.panel.aliasColors[alias] || $scope.colors[data.length];
var yaxis = $scope.panel.aliasYAxis[alias] || 1;
var tsOpts = {
interval: $scope.interval,
var time_series = new timeSeries.ZeroFilled({
start_date: $scope.range && $scope.range.from,
end_date: $scope.range && $scope.range.to,
};
var time_series = new timeSeries.ZeroFilled(tsOpts);
_.each(targetData.datapoints, function(valueArray) {
if (valueArray[0] !== null) {
time_series.addValue(valueArray[1] * 1000, valueArray[0]);
}
datapoints: targetData.datapoints
});
var seriesInfo = {
......
......@@ -24,6 +24,10 @@
<label class="small">Line Width</label>
<select class="input-mini" ng-model="panel.linewidth" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]" ng-change="render()"></select>
</div>
<div class="editor-option" ng-show="panel.points">
<label class="small">Point Radius</label>
<select class="input-mini" ng-model="panel.pointradius" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10]" ng-change="render()"></select>
</div>
<div class="editor-option">
<label class="small">Null point mode <tip>Define how null values should be drawn</tip></label>
<select class="input-medium" ng-model="panel.nullPointMode" ng-options="f for f in ['connected', 'null', 'null as zero']" ng-change="render()"></select>
......
......@@ -33,197 +33,41 @@ function (_, Interval) {
*/
ts.ZeroFilled = function (opts) {
opts = _.defaults(opts, {
interval: '10m',
start_date: null,
end_date: null,
datapoints: []
});
// the expected differenece between readings.
this.interval = new Interval(opts.interval);
// will keep all values here, keyed by their time
this._data = {};
this.start_time = opts.start_date && getDatesTime(opts.start_date);
this.end_time = opts.end_date && getDatesTime(opts.end_date);
this.opts = opts;
this.datapoints = opts.datapoints;
};
/**
* Add a row
* @param {int} time The time for the value, in
* @param {any} value The value at this time
*/
ts.ZeroFilled.prototype.addValue = function (time, value) {
if (time instanceof Date) {
time = getDatesTime(time);
} else {
time = base10Int(time);
}
if (!isNaN(time)) {
this._data[time] = (_.isUndefined(value) ? 0 : value);
}
this._cached_times = null;
};
/**
* Get an array of the times that have been explicitly set in the series
* @param {array} include (optional) list of timestamps to include in the response
* @return {array} An array of integer times.
*/
ts.ZeroFilled.prototype.getOrderedTimes = function (include) {
var times = _.map(_.keys(this._data), base10Int);
if (_.isArray(include)) {
times = times.concat(include);
}
return _.uniq(times.sort(function (a, b) {
// decending numeric sort
return a - b;
}), true);
};
/**
* return the rows in the format:
* [ [time, value], [time, value], ... ]
*
* Heavy lifting is done by _get(Min|Default|All)FlotPairs()
* @param {array} required_times An array of timestamps that must be in the resulting pairs
* @return {array}
*/
ts.ZeroFilled.prototype.getFlotPairs = function (required_times, fillStyle) {
var times = this.getOrderedTimes(required_times),
strategy,
pairs;
if(fillStyle === 'null as zero') {
strategy = this._getAllFlotPairs;
} else if(fillStyle === 'null') {
strategy = this._getNullFlotPairs;
} else if(fillStyle === 'connected') {
strategy = this._getNoZeroFlotPairs;
} else {
strategy = this._getMinFlotPairs;
}
pairs = _.reduce(
times, // what
strategy, // how
[], // where
this // context
);
// if the first or last pair is inside either the start or end time,
// add those times to the series with null values so the graph will stretch to contain them.
// Removing, flot 0.8.1's max/min params satisfy this
/*
if (this.start_time && (pairs.length === 0 || pairs[0][0] > this.start_time)) {
pairs.unshift([this.start_time, null]);
}
if (this.end_time && (pairs.length === 0 || pairs[pairs.length - 1][0] < this.end_time)) {
pairs.push([this.end_time, null]);
}
*/
return pairs;
};
/**
* ** called as a reduce stragegy in getFlotPairs() **
* Fill zero's on either side of the current time, unless there is already a measurement there or
* we are looking at an edge.
* @return {array} An array of points to plot with flot
*/
ts.ZeroFilled.prototype._getMinFlotPairs = function (result, time, i, times) {
var next, expected_next, prev, expected_prev;
// check for previous measurement
if (i > 0) {
prev = times[i - 1];
expected_prev = this.interval.before(time);
if (prev < expected_prev) {
result.push([expected_prev, 0]);
}
}
// add the current time
result.push([ time, this._data[time] || 0]);
// check for next measurement
if (times.length > i) {
next = times[i + 1];
expected_next = this.interval.after(time);
if (next > expected_next) {
result.push([expected_next, 0]);
}
}
return result;
};
/**
* ** called as a reduce stragegy in getFlotPairs() **
* Fill zero's to the right of each time, until the next measurement is reached or we are at the
* last measurement
* @return {array} An array of points to plot with flot
*/
ts.ZeroFilled.prototype._getAllFlotPairs = function (result, time, i, times) {
var next, expected_next;
result.push([ times[i], this._data[times[i]] || 0 ]);
next = times[i + 1];
expected_next = this.interval.after(time);
for(; times.length > i && next > expected_next; expected_next = this.interval.after(expected_next)) {
result.push([expected_next, 0]);
}
return result;
};
/**
* ** called as a reduce stragegy in getFlotPairs() **
* Same as min, but fills with nulls
* @return {array} An array of points to plot with flot
*/
ts.ZeroFilled.prototype._getNullFlotPairs = function (result, time, i, times) {
var next, expected_next, prev, expected_prev;
// check for previous measurement
if (i > 0) {
prev = times[i - 1];
expected_prev = this.interval.before(time);
if (prev < expected_prev) {
result.push([expected_prev, null]);
ts.ZeroFilled.prototype.getFlotPairs = function (fillStyle) {
var result = [];
_.each(this.datapoints, function(valueArray) {
var currentTime = valueArray[1];
var currentValue = valueArray[0];
if (currentValue === null) {
if (fillStyle === 'connected') {
return;
}
if (fillStyle === 'null as zero') {
currentValue = 0;
}
if (fillStyle === 'null') {
// do nothing
}
}
}
// add the current time
result.push([ time, this._data[time] || null]);
// check for next measurement
if (times.length > i) {
next = times[i + 1];
expected_next = this.interval.after(time);
if (next > expected_next) {
result.push([expected_next, null]);
}
}
result.push([currentTime * 1000, currentValue]);
});
return result;
};
/**
* ** called as a reduce stragegy in getFlotPairs() **
* Not fill zero's on either side of the current time, only the current time
* @return {array} An array of points to plot with flot
*/
ts.ZeroFilled.prototype._getNoZeroFlotPairs = function (result, time) {
// add the current time
if(this._data[time] !== null){
result.push([ time, this._data[time]]);
}
return result;
};
return ts;
});
\ No newline at end of file
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