From 258a0d276cc61c5a391a1fe3e037ad5c7db66458 Mon Sep 17 00:00:00 2001 From: ilgizar Date: Sun, 18 Feb 2018 00:51:32 +0500 Subject: [PATCH 01/30] Add hook processRange to flot plugin. --- public/vendor/flot/jquery.flot.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/vendor/flot/jquery.flot.js b/public/vendor/flot/jquery.flot.js index ec35fb87bd865..040eb808f4867 100644 --- a/public/vendor/flot/jquery.flot.js +++ b/public/vendor/flot/jquery.flot.js @@ -632,6 +632,7 @@ Licensed under the MIT license. processRawData: [], processDatapoints: [], processOffset: [], + processRange: [], drawBackground: [], drawSeries: [], draw: [], @@ -1613,6 +1614,8 @@ Licensed under the MIT license. setRange(axis); }); + executeHooks(hooks.processRange, []); + if (showGrid) { var allocatedAxes = $.grep(axes, function (axis) { From 57013d2228cd2421ca819fbe22d307046158077b Mon Sep 17 00:00:00 2001 From: ilgizar Date: Sun, 18 Feb 2018 00:54:35 +0500 Subject: [PATCH 02/30] Share zero between Y axis. --- .../app/plugins/panel/graph/axes_editor.html | 1 + public/app/plugins/panel/graph/graph.ts | 111 ++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/public/app/plugins/panel/graph/axes_editor.html b/public/app/plugins/panel/graph/axes_editor.html index 6160ef01fec33..ee3654a9bbd06 100644 --- a/public/app/plugins/panel/graph/axes_editor.html +++ b/public/app/plugins/panel/graph/axes_editor.html @@ -29,6 +29,7 @@
Right Y
+
diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 3ed8cbc18364e..ade2fb8096098 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -155,6 +155,116 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) { } } + function processRangeHook(plot) { + var yaxis = plot.getYAxes(); + if (yaxis.length > 1 && panel.yaxes[1].shareZero) { + shareYLevel(yaxis[0].min, yaxis[0].max, yaxis[1].min, yaxis[1].max, 0); + } + } + + function shareYLevel(minLeft, maxLeft, minRight, maxRight, shareLevel) { + if (shareLevel !== 0) { + minLeft -= shareLevel; + maxLeft -= shareLevel; + minRight -= shareLevel; + maxRight -= shareLevel; + } + + // wide Y min and max using increased wideFactor + var deltaLeft = maxLeft - minLeft; + var deltaRight = maxRight - minRight; + var wideFactor = 0.25; + if (deltaLeft === 0) { + minLeft -= wideFactor; + maxLeft += wideFactor; + } + if (deltaRight === 0) { + minRight -= wideFactor; + maxRight += wideFactor; + } + + // on the opposite sides with respect to zero + if ((minLeft >= 0 && maxRight <= 0) || (maxLeft <= 0 && minRight >= 0)) { + if (minLeft >= 0) { + minLeft = -maxLeft; + maxRight = -minRight; + } else { + maxLeft = -minLeft; + minRight = -maxRight; + } + } else { + var limitTop = Infinity; + var limitBottom = -Infinity; + var absLeftMin = Math.abs(minLeft); + var absLeftMax = Math.abs(maxLeft); + var absRightMin = Math.abs(minRight); + var absRightMax = Math.abs(maxRight); + var upLeft = _.max([absLeftMin, absLeftMax]); + var downLeft = _.min([absLeftMin, absLeftMax]); + var upRight = _.max([absRightMin, absRightMax]); + var downRight = _.min([absRightMin, absRightMax]); + var oneSide = (minLeft >= 0 && minRight >= 0) || (maxLeft <= 0 && maxRight <= 0); + var rateLeft, rateRight, rate; + + // on the one hand with respect to zero + if (oneSide) { + rateLeft = downLeft ? upLeft / downLeft : downLeft >= 0 ? limitTop : limitBottom; + rateRight = downRight ? upRight / downRight : downRight >= 0 ? limitTop : limitBottom; + rate = _.max([rateLeft, rateRight]); + + if (rate === limitTop) { + if (maxLeft > 0) { + minLeft = 0; + minRight = 0; + } else { + maxLeft = 0; + maxRight = 0; + } + } else { + var coef = deltaLeft / deltaRight; + if ((rate === rateLeft && minLeft > 0) || (rate === rateRight && maxRight < 0)) { + maxLeft = maxRight * coef; + minRight = minLeft / coef; + } else { + minLeft = minRight * coef; + maxRight = maxLeft / coef; + } + } + } else { + rateLeft = + minLeft && maxLeft + ? minLeft < 0 ? maxLeft / minLeft : limitBottom + : minLeft < 0 || maxRight >= 0 ? limitBottom : limitTop; + rateRight = + minRight && maxRight + ? minRight < 0 ? maxRight / minRight : limitBottom + : minRight < 0 || maxLeft >= 0 ? limitBottom : limitTop; + rate = _.max([rateLeft, rateRight]); + + if (rate === rateLeft) { + minRight = + upRight === absRightMin && (absRightMin !== absRightMax || upLeft !== absLeftMin) + ? -upRight + : upRight / rate; + maxRight = upRight === absRightMax ? upRight : -upRight * rate; + } else { + minLeft = + upLeft === absLeftMin && (absLeftMin !== absLeftMax || upRight !== absRightMin) + ? -upLeft + : upLeft / rate; + maxLeft = upLeft === absLeftMax ? upLeft : -upLeft * rate; + } + } + } + + if (shareLevel !== 0) { + minLeft += shareLevel; + maxLeft += shareLevel; + minRight += shareLevel; + maxRight += shareLevel; + } + } + // Series could have different timeSteps, // let's find the smallest one so that bars are correctly rendered. // In addition, only take series which are rendered as bars for this. @@ -296,6 +406,7 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) { hooks: { draw: [drawHook], processOffset: [processOffsetHook], + processRange: [processRangeHook], }, legend: { show: false }, series: { From 7eeb68b59088686ad3b350d0a8e839d3f41d3116 Mon Sep 17 00:00:00 2001 From: ilgizar Date: Tue, 20 Feb 2018 16:58:49 +0500 Subject: [PATCH 03/30] Refactoring code. Change Y-Zero to Y-Level. --- .../app/plugins/panel/graph/axes_editor.html | 8 +- public/app/plugins/panel/graph/graph.ts | 148 ++++++++++-------- public/app/plugins/panel/graph/module.ts | 2 + public/vendor/flot/jquery.flot.js | 37 +++-- 4 files changed, 117 insertions(+), 78 deletions(-) diff --git a/public/app/plugins/panel/graph/axes_editor.html b/public/app/plugins/panel/graph/axes_editor.html index ee3654a9bbd06..a80ebd3036c93 100644 --- a/public/app/plugins/panel/graph/axes_editor.html +++ b/public/app/plugins/panel/graph/axes_editor.html @@ -29,7 +29,6 @@
Right Y
-
@@ -40,6 +39,13 @@
Right Y
+
+ +
+ + +
+
diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index ade2fb8096098..95790222cac8e 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -157,12 +157,17 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) { function processRangeHook(plot) { var yaxis = plot.getYAxes(); - if (yaxis.length > 1 && panel.yaxes[1].shareZero) { - shareYLevel(yaxis[0].min, yaxis[0].max, yaxis[1].min, yaxis[1].max, 0); + if (yaxis.length > 1 && panel.yaxes[1].shareLevel) { + shareYLevel(yaxis, parseFloat(panel.yaxes[1].shareY || 0)); } } - function shareYLevel(minLeft, maxLeft, minRight, maxRight, shareLevel) { + function shareYLevel(yaxis, shareLevel) { + var minLeft = yaxis[0].min; + var maxLeft = yaxis[0].max; + var minRight = yaxis[1].min; + var maxRight = yaxis[1].max; + if (shareLevel !== 0) { minLeft -= shareLevel; maxLeft -= shareLevel; @@ -183,76 +188,80 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) { maxRight += wideFactor; } - // on the opposite sides with respect to zero - if ((minLeft >= 0 && maxRight <= 0) || (maxLeft <= 0 && minRight >= 0)) { - if (minLeft >= 0) { - minLeft = -maxLeft; - maxRight = -minRight; - } else { - maxLeft = -minLeft; - minRight = -maxRight; - } + // one of graphs on zero + var zero = minLeft === 0 || minRight === 0 || maxLeft === 0 || maxRight === 0; + + // on the one hand with respect to zero + var oneSide = (minLeft >= 0 && minRight >= 0) || (maxLeft <= 0 && maxRight <= 0); + + if (zero && oneSide) { + minLeft = maxLeft > 0 ? 0 : minLeft; + maxLeft = maxLeft > 0 ? maxLeft : 0; + minRight = maxRight > 0 ? 0 : minRight; + maxRight = maxRight > 0 ? maxRight : 0; } else { - var limitTop = Infinity; - var limitBottom = -Infinity; - var absLeftMin = Math.abs(minLeft); - var absLeftMax = Math.abs(maxLeft); - var absRightMin = Math.abs(minRight); - var absRightMax = Math.abs(maxRight); - var upLeft = _.max([absLeftMin, absLeftMax]); - var downLeft = _.min([absLeftMin, absLeftMax]); - var upRight = _.max([absRightMin, absRightMax]); - var downRight = _.min([absRightMin, absRightMax]); - var oneSide = (minLeft >= 0 && minRight >= 0) || (maxLeft <= 0 && maxRight <= 0); - var rateLeft, rateRight, rate; - - // on the one hand with respect to zero - if (oneSide) { - rateLeft = downLeft ? upLeft / downLeft : downLeft >= 0 ? limitTop : limitBottom; - rateRight = downRight ? upRight / downRight : downRight >= 0 ? limitTop : limitBottom; - rate = _.max([rateLeft, rateRight]); - - if (rate === limitTop) { - if (maxLeft > 0) { - minLeft = 0; - minRight = 0; - } else { - maxLeft = 0; - maxRight = 0; - } + // on the opposite sides with respect to zero + if ((minLeft >= 0 && maxRight <= 0) || (maxLeft <= 0 && minRight >= 0)) { + if (minLeft >= 0) { + minLeft = -maxLeft; + maxRight = -minRight; + } else { + maxLeft = -minLeft; + minRight = -maxRight; + } + } else { + // both across zero + var twoCross = minLeft <= 0 && maxLeft >= 0 && minRight <= 0 && maxRight >= 0; + + var rateLeft, rateRight, rate; + if (twoCross) { + rateLeft = minRight ? minLeft / minRight : 0; + rateRight = maxRight ? maxLeft / maxRight : 0; } else { - var coef = deltaLeft / deltaRight; - if ((rate === rateLeft && minLeft > 0) || (rate === rateRight && maxRight < 0)) { - maxLeft = maxRight * coef; - minRight = minLeft / coef; + if (oneSide) { + var absLeftMin = Math.abs(minLeft); + var absLeftMax = Math.abs(maxLeft); + var absRightMin = Math.abs(minRight); + var absRightMax = Math.abs(maxRight); + var upLeft = _.max([absLeftMin, absLeftMax]); + var downLeft = _.min([absLeftMin, absLeftMax]); + var upRight = _.max([absRightMin, absRightMax]); + var downRight = _.min([absRightMin, absRightMax]); + + rateLeft = downLeft ? upLeft / downLeft : upLeft; + rateRight = downRight ? upRight / downRight : upRight; } else { - minLeft = minRight * coef; - maxRight = maxLeft / coef; + if (minLeft > 0 || minRight > 0) { + rateLeft = maxLeft / maxRight; + rateRight = 0; + } else { + rateLeft = 0; + rateRight = minLeft / minRight; + } } } - } else { - rateLeft = - minLeft && maxLeft - ? minLeft < 0 ? maxLeft / minLeft : limitBottom - : minLeft < 0 || maxRight >= 0 ? limitBottom : limitTop; - rateRight = - minRight && maxRight - ? minRight < 0 ? maxRight / minRight : limitBottom - : minRight < 0 || maxLeft >= 0 ? limitBottom : limitTop; - rate = _.max([rateLeft, rateRight]); - - if (rate === rateLeft) { - minRight = - upRight === absRightMin && (absRightMin !== absRightMax || upLeft !== absLeftMin) - ? -upRight - : upRight / rate; - maxRight = upRight === absRightMax ? upRight : -upRight * rate; + rate = rateLeft > rateRight ? rateLeft : rateRight; + + if (oneSide) { + if (minLeft > 0) { + minLeft = maxLeft / rate; + minRight = maxRight / rate; + } else { + maxLeft = minLeft / rate; + maxRight = minRight / rate; + } } else { - minLeft = - upLeft === absLeftMin && (absLeftMin !== absLeftMax || upRight !== absRightMin) - ? -upLeft - : upLeft / rate; - maxLeft = upLeft === absLeftMax ? upLeft : -upLeft * rate; + if (twoCross) { + minLeft = minRight ? minRight * rate : minLeft; + minRight = minLeft ? minLeft / rate : minRight; + maxLeft = maxRight ? maxRight * rate : maxLeft; + maxRight = maxLeft ? maxLeft / rate : maxRight; + } else { + minLeft = minLeft > 0 ? minRight * rate : minLeft; + minRight = minRight > 0 ? minLeft / rate : minRight; + maxLeft = maxLeft < 0 ? maxRight * rate : maxLeft; + maxRight = maxRight < 0 ? maxLeft / rate : maxRight; + } } } } @@ -263,6 +272,11 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) { minRight += shareLevel; maxRight += shareLevel; } + + yaxis[0].min = minLeft; + yaxis[0].max = maxLeft; + yaxis[1].min = minRight; + yaxis[1].max = maxRight; } // Series could have different timeSteps, diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index 59e72124c7438..67b59997278c6 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -46,6 +46,8 @@ class GraphCtrl extends MetricsPanelCtrl { min: null, max: null, format: 'short', + shareLevel: false, + shareY: 0, }, ], xaxis: { diff --git a/public/vendor/flot/jquery.flot.js b/public/vendor/flot/jquery.flot.js index 040eb808f4867..401198b712de7 100644 --- a/public/vendor/flot/jquery.flot.js +++ b/public/vendor/flot/jquery.flot.js @@ -1622,14 +1622,24 @@ Licensed under the MIT license. return axis.show || axis.reserveSpace; }); - $.each(allocatedAxes, function (_, axis) { - // make the ticks - setupTickGeneration(axis); - setTicks(axis); - snapRangeToTicks(axis, axis.ticks); - // find labelWidth/Height for axis - measureTickLabels(axis); - }); + var snaped = false; + for (var i = 0; i < 2; i++) { + $.each(allocatedAxes, function (_, axis) { + // make the ticks + setupTickGeneration(axis); + setTicks(axis); + snaped = snapRangeToTicks(axis, axis.ticks) || snaped; + // find labelWidth/Height for axis + measureTickLabels(axis); + }); + + if (snaped) { + executeHooks(hooks.processRange, []); + snaped = false; + } else { + break; + } + } // with all dimensions calculated, we can compute the // axis bounding boxes, start from the outside @@ -1646,6 +1656,7 @@ Licensed under the MIT license. }); } + plotWidth = surface.width - plotOffset.left - plotOffset.right; plotHeight = surface.height - plotOffset.bottom - plotOffset.top; @@ -1879,13 +1890,19 @@ Licensed under the MIT license. } function snapRangeToTicks(axis, ticks) { + var changed = false; if (axis.options.autoscaleMargin && ticks.length > 0) { // snap to ticks - if (axis.options.min == null) + if (axis.options.min == null) { axis.min = Math.min(axis.min, ticks[0].v); - if (axis.options.max == null && ticks.length > 1) + changed = true; + } + if (axis.options.max == null && ticks.length > 1) { axis.max = Math.max(axis.max, ticks[ticks.length - 1].v); + changed = true; + } } + return changed; } function draw() { From 9e68cbea514380998d4ab5d4b9efe7926935cb05 Mon Sep 17 00:00:00 2001 From: ilgizar Date: Thu, 22 Feb 2018 15:38:32 +0500 Subject: [PATCH 04/30] Refactoring code --- public/app/plugins/panel/graph/align_yaxes.ts | 123 ++++++++++++++++++ .../app/plugins/panel/graph/axes_editor.html | 19 +-- public/app/plugins/panel/graph/graph.ts | 122 +---------------- public/app/plugins/panel/graph/module.ts | 3 +- 4 files changed, 137 insertions(+), 130 deletions(-) create mode 100644 public/app/plugins/panel/graph/align_yaxes.ts diff --git a/public/app/plugins/panel/graph/align_yaxes.ts b/public/app/plugins/panel/graph/align_yaxes.ts new file mode 100644 index 0000000000000..74fc9a063c710 --- /dev/null +++ b/public/app/plugins/panel/graph/align_yaxes.ts @@ -0,0 +1,123 @@ +import _ from 'lodash'; + +/** + * To align two Y axes by Y level + * @param yaxis data [{min: min_y1, min: max_y1}, {min: min_y2, max: max_y2}] + * @param align Y level + */ +export function alignYLevel(yaxis, alignLevel) { + var minLeft = yaxis[0].min; + var maxLeft = yaxis[0].max; + var minRight = yaxis[1].min; + var maxRight = yaxis[1].max; + + if (alignLevel !== 0) { + minLeft -= alignLevel; + maxLeft -= alignLevel; + minRight -= alignLevel; + maxRight -= alignLevel; + } + + // wide Y min and max using increased wideFactor + var deltaLeft = maxLeft - minLeft; + var deltaRight = maxRight - minRight; + var wideFactor = 0.25; + if (deltaLeft === 0) { + minLeft -= wideFactor; + maxLeft += wideFactor; + } + if (deltaRight === 0) { + minRight -= wideFactor; + maxRight += wideFactor; + } + + // one of graphs on zero + var zero = minLeft === 0 || minRight === 0 || maxLeft === 0 || maxRight === 0; + + // on the one hand with respect to zero + var oneSide = (minLeft >= 0 && minRight >= 0) || (maxLeft <= 0 && maxRight <= 0); + + if (zero && oneSide) { + minLeft = maxLeft > 0 ? 0 : minLeft; + maxLeft = maxLeft > 0 ? maxLeft : 0; + minRight = maxRight > 0 ? 0 : minRight; + maxRight = maxRight > 0 ? maxRight : 0; + } else { + // on the opposite sides with respect to zero + if ((minLeft >= 0 && maxRight <= 0) || (maxLeft <= 0 && minRight >= 0)) { + if (minLeft >= 0) { + minLeft = -maxLeft; + maxRight = -minRight; + } else { + maxLeft = -minLeft; + minRight = -maxRight; + } + } else { + // both across zero + var twoCross = minLeft <= 0 && maxLeft >= 0 && minRight <= 0 && maxRight >= 0; + + var rateLeft, rateRight, rate; + if (twoCross) { + rateLeft = minRight ? minLeft / minRight : 0; + rateRight = maxRight ? maxLeft / maxRight : 0; + } else { + if (oneSide) { + var absLeftMin = Math.abs(minLeft); + var absLeftMax = Math.abs(maxLeft); + var absRightMin = Math.abs(minRight); + var absRightMax = Math.abs(maxRight); + var upLeft = _.max([absLeftMin, absLeftMax]); + var downLeft = _.min([absLeftMin, absLeftMax]); + var upRight = _.max([absRightMin, absRightMax]); + var downRight = _.min([absRightMin, absRightMax]); + + rateLeft = downLeft ? upLeft / downLeft : upLeft; + rateRight = downRight ? upRight / downRight : upRight; + } else { + if (minLeft > 0 || minRight > 0) { + rateLeft = maxLeft / maxRight; + rateRight = 0; + } else { + rateLeft = 0; + rateRight = minLeft / minRight; + } + } + } + rate = rateLeft > rateRight ? rateLeft : rateRight; + + if (oneSide) { + if (minLeft > 0) { + minLeft = maxLeft / rate; + minRight = maxRight / rate; + } else { + maxLeft = minLeft / rate; + maxRight = minRight / rate; + } + } else { + if (twoCross) { + minLeft = minRight ? minRight * rate : minLeft; + minRight = minLeft ? minLeft / rate : minRight; + maxLeft = maxRight ? maxRight * rate : maxLeft; + maxRight = maxLeft ? maxLeft / rate : maxRight; + } else { + minLeft = minLeft > 0 ? minRight * rate : minLeft; + minRight = minRight > 0 ? minLeft / rate : minRight; + maxLeft = maxLeft < 0 ? maxRight * rate : maxLeft; + maxRight = maxRight < 0 ? maxLeft / rate : maxRight; + } + } + } + } + + if (alignLevel !== 0) { + minLeft += alignLevel; + maxLeft += alignLevel; + minRight += alignLevel; + maxRight += alignLevel; + } + + yaxis[0].min = minLeft; + yaxis[0].max = maxLeft; + yaxis[1].min = minRight; + yaxis[1].max = maxRight; +} diff --git a/public/app/plugins/panel/graph/axes_editor.html b/public/app/plugins/panel/graph/axes_editor.html index a80ebd3036c93..7bf6756a7df9f 100644 --- a/public/app/plugins/panel/graph/axes_editor.html +++ b/public/app/plugins/panel/graph/axes_editor.html @@ -11,6 +11,7 @@
Right Y
+
@@ -28,8 +29,15 @@
Right Y
- -
+
+ +
+ + +
+ +
+
@@ -39,13 +47,6 @@
Right Y
-
- -
- - -
-
diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 95790222cac8e..3f7d1bee33caf 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -18,6 +18,7 @@ import GraphTooltip from './graph_tooltip'; import { ThresholdManager } from './threshold_manager'; import { EventManager } from 'app/features/annotations/all'; import { convertValuesToHistogram, getSeriesValues } from './histogram'; +import { alignYLevel } from './align_yaxes'; import config from 'app/core/config'; /** @ngInject **/ @@ -157,128 +158,11 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) { function processRangeHook(plot) { var yaxis = plot.getYAxes(); - if (yaxis.length > 1 && panel.yaxes[1].shareLevel) { - shareYLevel(yaxis, parseFloat(panel.yaxes[1].shareY || 0)); + if (yaxis.length > 1 && panel.yaxes[1].align !== null) { + alignYLevel(yaxis, parseFloat(panel.yaxes[1].align)); } } - function shareYLevel(yaxis, shareLevel) { - var minLeft = yaxis[0].min; - var maxLeft = yaxis[0].max; - var minRight = yaxis[1].min; - var maxRight = yaxis[1].max; - - if (shareLevel !== 0) { - minLeft -= shareLevel; - maxLeft -= shareLevel; - minRight -= shareLevel; - maxRight -= shareLevel; - } - - // wide Y min and max using increased wideFactor - var deltaLeft = maxLeft - minLeft; - var deltaRight = maxRight - minRight; - var wideFactor = 0.25; - if (deltaLeft === 0) { - minLeft -= wideFactor; - maxLeft += wideFactor; - } - if (deltaRight === 0) { - minRight -= wideFactor; - maxRight += wideFactor; - } - - // one of graphs on zero - var zero = minLeft === 0 || minRight === 0 || maxLeft === 0 || maxRight === 0; - - // on the one hand with respect to zero - var oneSide = (minLeft >= 0 && minRight >= 0) || (maxLeft <= 0 && maxRight <= 0); - - if (zero && oneSide) { - minLeft = maxLeft > 0 ? 0 : minLeft; - maxLeft = maxLeft > 0 ? maxLeft : 0; - minRight = maxRight > 0 ? 0 : minRight; - maxRight = maxRight > 0 ? maxRight : 0; - } else { - // on the opposite sides with respect to zero - if ((minLeft >= 0 && maxRight <= 0) || (maxLeft <= 0 && minRight >= 0)) { - if (minLeft >= 0) { - minLeft = -maxLeft; - maxRight = -minRight; - } else { - maxLeft = -minLeft; - minRight = -maxRight; - } - } else { - // both across zero - var twoCross = minLeft <= 0 && maxLeft >= 0 && minRight <= 0 && maxRight >= 0; - - var rateLeft, rateRight, rate; - if (twoCross) { - rateLeft = minRight ? minLeft / minRight : 0; - rateRight = maxRight ? maxLeft / maxRight : 0; - } else { - if (oneSide) { - var absLeftMin = Math.abs(minLeft); - var absLeftMax = Math.abs(maxLeft); - var absRightMin = Math.abs(minRight); - var absRightMax = Math.abs(maxRight); - var upLeft = _.max([absLeftMin, absLeftMax]); - var downLeft = _.min([absLeftMin, absLeftMax]); - var upRight = _.max([absRightMin, absRightMax]); - var downRight = _.min([absRightMin, absRightMax]); - - rateLeft = downLeft ? upLeft / downLeft : upLeft; - rateRight = downRight ? upRight / downRight : upRight; - } else { - if (minLeft > 0 || minRight > 0) { - rateLeft = maxLeft / maxRight; - rateRight = 0; - } else { - rateLeft = 0; - rateRight = minLeft / minRight; - } - } - } - rate = rateLeft > rateRight ? rateLeft : rateRight; - - if (oneSide) { - if (minLeft > 0) { - minLeft = maxLeft / rate; - minRight = maxRight / rate; - } else { - maxLeft = minLeft / rate; - maxRight = minRight / rate; - } - } else { - if (twoCross) { - minLeft = minRight ? minRight * rate : minLeft; - minRight = minLeft ? minLeft / rate : minRight; - maxLeft = maxRight ? maxRight * rate : maxLeft; - maxRight = maxLeft ? maxLeft / rate : maxRight; - } else { - minLeft = minLeft > 0 ? minRight * rate : minLeft; - minRight = minRight > 0 ? minLeft / rate : minRight; - maxLeft = maxLeft < 0 ? maxRight * rate : maxLeft; - maxRight = maxRight < 0 ? maxLeft / rate : maxRight; - } - } - } - } - - if (shareLevel !== 0) { - minLeft += shareLevel; - maxLeft += shareLevel; - minRight += shareLevel; - maxRight += shareLevel; - } - - yaxis[0].min = minLeft; - yaxis[0].max = maxLeft; - yaxis[1].min = minRight; - yaxis[1].max = maxRight; - } - // Series could have different timeSteps, // let's find the smallest one so that bars are correctly rendered. // In addition, only take series which are rendered as bars for this. diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index 67b59997278c6..c198d11811598 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -46,8 +46,7 @@ class GraphCtrl extends MetricsPanelCtrl { min: null, max: null, format: 'short', - shareLevel: false, - shareY: 0, + align: null, }, ], xaxis: { From 66590042a6b91dc3ce4e197724d0c1ef96b1bfea Mon Sep 17 00:00:00 2001 From: ilgizar Date: Thu, 22 Feb 2018 15:41:11 +0500 Subject: [PATCH 05/30] Add unit tests. --- .../plugins/panel/graph/specs/align_y.jest.ts | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 public/app/plugins/panel/graph/specs/align_y.jest.ts diff --git a/public/app/plugins/panel/graph/specs/align_y.jest.ts b/public/app/plugins/panel/graph/specs/align_y.jest.ts new file mode 100644 index 0000000000000..046d02ee78767 --- /dev/null +++ b/public/app/plugins/panel/graph/specs/align_y.jest.ts @@ -0,0 +1,167 @@ +import { alignYLevel } from '../align_yaxes'; + +describe('Graph Y axes aligner', function() { + let yaxes, expected; + let alignY = 0; + + describe('on the one hand with respect to zero', () => { + it('Should shrink Y axis', () => { + yaxes = [{ min: 5, max: 10 }, { min: 2, max: 3 }]; + expected = [{ min: 5, max: 10 }, { min: 1.5, max: 3 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + + it('Should shrink Y axis', () => { + yaxes = [{ min: 2, max: 3 }, { min: 5, max: 10 }]; + expected = [{ min: 1.5, max: 3 }, { min: 5, max: 10 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + + it('Should shrink Y axis', () => { + yaxes = [{ min: -10, max: -5 }, { min: -3, max: -2 }]; + expected = [{ min: -10, max: -5 }, { min: -3, max: -1.5 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + + it('Should shrink Y axis', () => { + yaxes = [{ min: -3, max: -2 }, { min: -10, max: -5 }]; + expected = [{ min: -3, max: -1.5 }, { min: -10, max: -5 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + }); + + describe('on the opposite sides with respect to zero', () => { + it('Should shrink Y axes', () => { + yaxes = [{ min: -3, max: -1 }, { min: 5, max: 10 }]; + expected = [{ min: -3, max: 3 }, { min: -10, max: 10 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + + it('Should shrink Y axes', () => { + yaxes = [{ min: 1, max: 3 }, { min: -10, max: -5 }]; + expected = [{ min: -3, max: 3 }, { min: -10, max: 10 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + }); + + describe('both across zero', () => { + it('Should shrink Y axes', () => { + yaxes = [{ min: -10, max: 5 }, { min: -2, max: 3 }]; + expected = [{ min: -10, max: 15 }, { min: -2, max: 3 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + + it('Should shrink Y axes', () => { + yaxes = [{ min: -5, max: 10 }, { min: -3, max: 2 }]; + expected = [{ min: -15, max: 10 }, { min: -3, max: 2 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + }); + + describe('one of graphs on zero', () => { + it('Should shrink Y axes', () => { + yaxes = [{ min: 0, max: 3 }, { min: 5, max: 10 }]; + expected = [{ min: 0, max: 3 }, { min: 0, max: 10 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + + it('Should shrink Y axes', () => { + yaxes = [{ min: 5, max: 10 }, { min: 0, max: 3 }]; + expected = [{ min: 0, max: 10 }, { min: 0, max: 3 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + + it('Should shrink Y axes', () => { + yaxes = [{ min: -3, max: 0 }, { min: -10, max: -5 }]; + expected = [{ min: -3, max: 0 }, { min: -10, max: 0 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + + it('Should shrink Y axes', () => { + yaxes = [{ min: -10, max: -5 }, { min: -3, max: 0 }]; + expected = [{ min: -10, max: 0 }, { min: -3, max: 0 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + }); + + describe('both graphs on zero', () => { + it('Should shrink Y axes', () => { + yaxes = [{ min: 0, max: 3 }, { min: -10, max: 0 }]; + expected = [{ min: -3, max: 3 }, { min: -10, max: 10 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + + it('Should shrink Y axes', () => { + yaxes = [{ min: -3, max: 0 }, { min: 0, max: 10 }]; + expected = [{ min: -3, max: 3 }, { min: -10, max: 10 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + }); + + describe('mixed placement of graphs relative to zero', () => { + it('Should shrink Y axes', () => { + yaxes = [{ min: -10, max: 5 }, { min: 1, max: 3 }]; + expected = [{ min: -10, max: 5 }, { min: -6, max: 3 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + + it('Should shrink Y axes', () => { + yaxes = [{ min: 1, max: 3 }, { min: -10, max: 5 }]; + expected = [{ min: -6, max: 3 }, { min: -10, max: 5 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + + it('Should shrink Y axes', () => { + yaxes = [{ min: -10, max: 5 }, { min: -3, max: -1 }]; + expected = [{ min: -10, max: 5 }, { min: -3, max: 1.5 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + + it('Should shrink Y axes', () => { + yaxes = [{ min: -3, max: -1 }, { min: -10, max: 5 }]; + expected = [{ min: -3, max: 1.5 }, { min: -10, max: 5 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + + it('Should shrink Y axes', () => { + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + }); +}); From 7cddc543068c954124201f1906d0dc576a891499 Mon Sep 17 00:00:00 2001 From: ilgizar Date: Wed, 7 Mar 2018 12:12:39 +0500 Subject: [PATCH 06/30] Add bs-tooltip to Y-Align element. --- public/app/plugins/panel/graph/axes_editor.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/graph/axes_editor.html b/public/app/plugins/panel/graph/axes_editor.html index 7bf6756a7df9f..2c08755c17ac7 100644 --- a/public/app/plugins/panel/graph/axes_editor.html +++ b/public/app/plugins/panel/graph/axes_editor.html @@ -33,7 +33,7 @@
Right Y
- +
From 916539fad9ebd9cb5a278796a2c54e2310efd755 Mon Sep 17 00:00:00 2001 From: ilgizar Date: Wed, 7 Mar 2018 14:21:10 +0500 Subject: [PATCH 07/30] Append test to check not zero level. --- .../plugins/panel/graph/specs/align_y.jest.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/public/app/plugins/panel/graph/specs/align_y.jest.ts b/public/app/plugins/panel/graph/specs/align_y.jest.ts index 046d02ee78767..ff540fd223fc3 100644 --- a/public/app/plugins/panel/graph/specs/align_y.jest.ts +++ b/public/app/plugins/panel/graph/specs/align_y.jest.ts @@ -158,8 +158,41 @@ describe('Graph Y axes aligner', function() { alignYLevel(yaxes, alignY); expect(yaxes).toMatchObject(expected); }); + }); + + describe('on level not zero', () => { + it('Should shrink Y axis', () => { + alignY = 1; + yaxes = [{ min: 5, max: 10 }, { min: 2, max: 4 }]; + expected = [{ min: 4, max: 10 }, { min: 2, max: 4 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + + it('Should shrink Y axes', () => { + alignY = 2; + yaxes = [{ min: -3, max: 1 }, { min: 5, max: 10 }]; + expected = [{ min: -3, max: 7 }, { min: -6, max: 10 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + + it('Should shrink Y axes', () => { + alignY = -1; + yaxes = [{ min: -5, max: 5 }, { min: -2, max: 3 }]; + expected = [{ min: -5, max: 15 }, { min: -2, max: 3 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); it('Should shrink Y axes', () => { + alignY = -2; + yaxes = [{ min: -2, max: 3 }, { min: 5, max: 10 }]; + expected = [{ min: -2, max: 3 }, { min: -2, max: 10 }]; + alignYLevel(yaxes, alignY); expect(yaxes).toMatchObject(expected); }); From 8152b9d6fe750a3f662130973141a6a71a752694 Mon Sep 17 00:00:00 2001 From: ilgizar Date: Mon, 12 Mar 2018 10:54:03 +0500 Subject: [PATCH 08/30] Refactoring --- public/app/plugins/panel/graph/align_yaxes.ts | 191 ++++++++++-------- 1 file changed, 102 insertions(+), 89 deletions(-) diff --git a/public/app/plugins/panel/graph/align_yaxes.ts b/public/app/plugins/panel/graph/align_yaxes.ts index 74fc9a063c710..4884cd124417e 100644 --- a/public/app/plugins/panel/graph/align_yaxes.ts +++ b/public/app/plugins/panel/graph/align_yaxes.ts @@ -6,118 +6,131 @@ import _ from 'lodash'; * @param align Y level */ export function alignYLevel(yaxis, alignLevel) { - var minLeft = yaxis[0].min; - var maxLeft = yaxis[0].max; - var minRight = yaxis[1].min; - var maxRight = yaxis[1].max; + moveLevelToZero(yaxis, alignLevel); - if (alignLevel !== 0) { - minLeft -= alignLevel; - maxLeft -= alignLevel; - minRight -= alignLevel; - maxRight -= alignLevel; - } - - // wide Y min and max using increased wideFactor - var deltaLeft = maxLeft - minLeft; - var deltaRight = maxRight - minRight; - var wideFactor = 0.25; - if (deltaLeft === 0) { - minLeft -= wideFactor; - maxLeft += wideFactor; - } - if (deltaRight === 0) { - minRight -= wideFactor; - maxRight += wideFactor; - } + expandStuckValues(yaxis); // one of graphs on zero - var zero = minLeft === 0 || minRight === 0 || maxLeft === 0 || maxRight === 0; + var zero = yaxis[0].min === 0 || yaxis[1].min === 0 || yaxis[0].max === 0 || yaxis[1].max === 0; - // on the one hand with respect to zero - var oneSide = (minLeft >= 0 && minRight >= 0) || (maxLeft <= 0 && maxRight <= 0); + var oneSide = checkOneSide(yaxis); if (zero && oneSide) { - minLeft = maxLeft > 0 ? 0 : minLeft; - maxLeft = maxLeft > 0 ? maxLeft : 0; - minRight = maxRight > 0 ? 0 : minRight; - maxRight = maxRight > 0 ? maxRight : 0; + yaxis[0].min = yaxis[0].max > 0 ? 0 : yaxis[0].min; + yaxis[0].max = yaxis[0].max > 0 ? yaxis[0].max : 0; + yaxis[1].min = yaxis[1].max > 0 ? 0 : yaxis[1].min; + yaxis[1].max = yaxis[1].max > 0 ? yaxis[1].max : 0; } else { // on the opposite sides with respect to zero - if ((minLeft >= 0 && maxRight <= 0) || (maxLeft <= 0 && minRight >= 0)) { - if (minLeft >= 0) { - minLeft = -maxLeft; - maxRight = -minRight; + if ((yaxis[0].min >= 0 && yaxis[1].max <= 0) || (yaxis[0].max <= 0 && yaxis[1].min >= 0)) { + if (yaxis[0].min >= 0) { + yaxis[0].min = -yaxis[0].max; + yaxis[1].max = -yaxis[1].min; } else { - maxLeft = -minLeft; - minRight = -maxRight; + yaxis[0].max = -yaxis[0].min; + yaxis[1].min = -yaxis[1].max; } } else { - // both across zero - var twoCross = minLeft <= 0 && maxLeft >= 0 && minRight <= 0 && maxRight >= 0; - - var rateLeft, rateRight, rate; - if (twoCross) { - rateLeft = minRight ? minLeft / minRight : 0; - rateRight = maxRight ? maxLeft / maxRight : 0; - } else { - if (oneSide) { - var absLeftMin = Math.abs(minLeft); - var absLeftMax = Math.abs(maxLeft); - var absRightMin = Math.abs(minRight); - var absRightMax = Math.abs(maxRight); - var upLeft = _.max([absLeftMin, absLeftMax]); - var downLeft = _.min([absLeftMin, absLeftMax]); - var upRight = _.max([absRightMin, absRightMax]); - var downRight = _.min([absRightMin, absRightMax]); - - rateLeft = downLeft ? upLeft / downLeft : upLeft; - rateRight = downRight ? upRight / downRight : upRight; - } else { - if (minLeft > 0 || minRight > 0) { - rateLeft = maxLeft / maxRight; - rateRight = 0; - } else { - rateLeft = 0; - rateRight = minLeft / minRight; - } - } - } - rate = rateLeft > rateRight ? rateLeft : rateRight; + var rate = getRate(yaxis); if (oneSide) { - if (minLeft > 0) { - minLeft = maxLeft / rate; - minRight = maxRight / rate; + if (yaxis[0].min > 0) { + yaxis[0].min = yaxis[0].max / rate; + yaxis[1].min = yaxis[1].max / rate; } else { - maxLeft = minLeft / rate; - maxRight = minRight / rate; + yaxis[0].max = yaxis[0].min / rate; + yaxis[1].max = yaxis[1].min / rate; } } else { - if (twoCross) { - minLeft = minRight ? minRight * rate : minLeft; - minRight = minLeft ? minLeft / rate : minRight; - maxLeft = maxRight ? maxRight * rate : maxLeft; - maxRight = maxLeft ? maxLeft / rate : maxRight; + if (checkTwoCross(yaxis)) { + yaxis[0].min = yaxis[1].min ? yaxis[1].min * rate : yaxis[0].min; + yaxis[1].min = yaxis[0].min ? yaxis[0].min / rate : yaxis[1].min; + yaxis[0].max = yaxis[1].max ? yaxis[1].max * rate : yaxis[0].max; + yaxis[1].max = yaxis[0].max ? yaxis[0].max / rate : yaxis[1].max; } else { - minLeft = minLeft > 0 ? minRight * rate : minLeft; - minRight = minRight > 0 ? minLeft / rate : minRight; - maxLeft = maxLeft < 0 ? maxRight * rate : maxLeft; - maxRight = maxRight < 0 ? maxLeft / rate : maxRight; + yaxis[0].min = yaxis[0].min > 0 ? yaxis[1].min * rate : yaxis[0].min; + yaxis[1].min = yaxis[1].min > 0 ? yaxis[0].min / rate : yaxis[1].min; + yaxis[0].max = yaxis[0].max < 0 ? yaxis[1].max * rate : yaxis[0].max; + yaxis[1].max = yaxis[1].max < 0 ? yaxis[0].max / rate : yaxis[1].max; } } } } + restoreLevelFromZero(yaxis, alignLevel); +} + +function expandStuckValues(yaxis) { + // wide Y min and max using increased wideFactor + var wideFactor = 0.25; + if (yaxis[0].max === yaxis[0].min) { + yaxis[0].min -= wideFactor; + yaxis[0].max += wideFactor; + } + if (yaxis[1].max === yaxis[1].min) { + yaxis[1].min -= wideFactor; + yaxis[1].max += wideFactor; + } +} + +function moveLevelToZero(yaxis, alignLevel) { + if (alignLevel !== 0) { + yaxis[0].min -= alignLevel; + yaxis[0].max -= alignLevel; + yaxis[1].min -= alignLevel; + yaxis[1].max -= alignLevel; + } +} + +function restoreLevelFromZero(yaxis, alignLevel) { if (alignLevel !== 0) { - minLeft += alignLevel; - maxLeft += alignLevel; - minRight += alignLevel; - maxRight += alignLevel; + yaxis[0].min += alignLevel; + yaxis[0].max += alignLevel; + yaxis[1].min += alignLevel; + yaxis[1].max += alignLevel; + } +} + +function checkOneSide(yaxis) { + // on the one hand with respect to zero + return (yaxis[0].min >= 0 && yaxis[1].min >= 0) || (yaxis[0].max <= 0 && yaxis[1].max <= 0); +} + +function checkTwoCross(yaxis) { + // both across zero + return yaxis[0].min <= 0 && yaxis[0].max >= 0 && yaxis[1].min <= 0 && yaxis[1].max >= 0; +} + +function getRate(yaxis) { + var rateLeft, rateRight, rate; + if (checkTwoCross(yaxis)) { + rateLeft = yaxis[1].min ? yaxis[0].min / yaxis[1].min : 0; + rateRight = yaxis[1].max ? yaxis[0].max / yaxis[1].max : 0; + } else { + if (checkOneSide(yaxis)) { + var absLeftMin = Math.abs(yaxis[0].min); + var absLeftMax = Math.abs(yaxis[0].max); + var absRightMin = Math.abs(yaxis[1].min); + var absRightMax = Math.abs(yaxis[1].max); + var upLeft = _.max([absLeftMin, absLeftMax]); + var downLeft = _.min([absLeftMin, absLeftMax]); + var upRight = _.max([absRightMin, absRightMax]); + var downRight = _.min([absRightMin, absRightMax]); + + rateLeft = downLeft ? upLeft / downLeft : upLeft; + rateRight = downRight ? upRight / downRight : upRight; + } else { + if (yaxis[0].min > 0 || yaxis[1].min > 0) { + rateLeft = yaxis[0].max / yaxis[1].max; + rateRight = 0; + } else { + rateLeft = 0; + rateRight = yaxis[0].min / yaxis[1].min; + } + } } - yaxis[0].min = minLeft; - yaxis[0].max = maxLeft; - yaxis[1].min = minRight; - yaxis[1].max = maxRight; + rate = rateLeft > rateRight ? rateLeft : rateRight; + + return rate; } From 11ae926388ff80e3caefdc6812a7e651ad3b7ed5 Mon Sep 17 00:00:00 2001 From: ilgizar Date: Mon, 12 Mar 2018 23:11:11 +0500 Subject: [PATCH 09/30] Rename test file according module name. --- .../panel/graph/specs/{align_y.jest.ts => align_yaxes.jest.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename public/app/plugins/panel/graph/specs/{align_y.jest.ts => align_yaxes.jest.ts} (100%) diff --git a/public/app/plugins/panel/graph/specs/align_y.jest.ts b/public/app/plugins/panel/graph/specs/align_yaxes.jest.ts similarity index 100% rename from public/app/plugins/panel/graph/specs/align_y.jest.ts rename to public/app/plugins/panel/graph/specs/align_yaxes.jest.ts From 8c82e5701c41f828f7d7770fb8177b258afd231f Mon Sep 17 00:00:00 2001 From: ilgizar Date: Mon, 12 Mar 2018 23:12:45 +0500 Subject: [PATCH 10/30] Replaced array values to variables yLeft and yRight for easy reading code. --- public/app/plugins/panel/graph/align_yaxes.ts | 134 +++++++++--------- 1 file changed, 70 insertions(+), 64 deletions(-) diff --git a/public/app/plugins/panel/graph/align_yaxes.ts b/public/app/plugins/panel/graph/align_yaxes.ts index 4884cd124417e..b60d75e7b668d 100644 --- a/public/app/plugins/panel/graph/align_yaxes.ts +++ b/public/app/plugins/panel/graph/align_yaxes.ts @@ -6,112 +6,118 @@ import _ from 'lodash'; * @param align Y level */ export function alignYLevel(yaxis, alignLevel) { - moveLevelToZero(yaxis, alignLevel); + var [yLeft, yRight] = yaxis; + moveLevelToZero(yLeft, yRight, alignLevel); - expandStuckValues(yaxis); + expandStuckValues(yLeft, yRight); // one of graphs on zero - var zero = yaxis[0].min === 0 || yaxis[1].min === 0 || yaxis[0].max === 0 || yaxis[1].max === 0; + var zero = yLeft.min === 0 || yRight.min === 0 || yLeft.max === 0 || yRight.max === 0; - var oneSide = checkOneSide(yaxis); + var oneSide = checkOneSide(yLeft, yRight); if (zero && oneSide) { - yaxis[0].min = yaxis[0].max > 0 ? 0 : yaxis[0].min; - yaxis[0].max = yaxis[0].max > 0 ? yaxis[0].max : 0; - yaxis[1].min = yaxis[1].max > 0 ? 0 : yaxis[1].min; - yaxis[1].max = yaxis[1].max > 0 ? yaxis[1].max : 0; + yLeft.min = yLeft.max > 0 ? 0 : yLeft.min; + yLeft.max = yLeft.max > 0 ? yLeft.max : 0; + yRight.min = yRight.max > 0 ? 0 : yRight.min; + yRight.max = yRight.max > 0 ? yRight.max : 0; } else { - // on the opposite sides with respect to zero - if ((yaxis[0].min >= 0 && yaxis[1].max <= 0) || (yaxis[0].max <= 0 && yaxis[1].min >= 0)) { - if (yaxis[0].min >= 0) { - yaxis[0].min = -yaxis[0].max; - yaxis[1].max = -yaxis[1].min; + if (checkOppositeSides(yLeft, yRight)) { + if (yLeft.min >= 0) { + yLeft.min = -yLeft.max; + yRight.max = -yRight.min; } else { - yaxis[0].max = -yaxis[0].min; - yaxis[1].min = -yaxis[1].max; + yLeft.max = -yLeft.min; + yRight.min = -yRight.max; } } else { - var rate = getRate(yaxis); + var rate = getRate(yLeft, yRight); if (oneSide) { - if (yaxis[0].min > 0) { - yaxis[0].min = yaxis[0].max / rate; - yaxis[1].min = yaxis[1].max / rate; + // all graphs above the Y level + if (yLeft.min > 0) { + yLeft.min = yLeft.max / rate; + yRight.min = yRight.max / rate; } else { - yaxis[0].max = yaxis[0].min / rate; - yaxis[1].max = yaxis[1].min / rate; + yLeft.max = yLeft.min / rate; + yRight.max = yRight.min / rate; } } else { - if (checkTwoCross(yaxis)) { - yaxis[0].min = yaxis[1].min ? yaxis[1].min * rate : yaxis[0].min; - yaxis[1].min = yaxis[0].min ? yaxis[0].min / rate : yaxis[1].min; - yaxis[0].max = yaxis[1].max ? yaxis[1].max * rate : yaxis[0].max; - yaxis[1].max = yaxis[0].max ? yaxis[0].max / rate : yaxis[1].max; + if (checkTwoCross(yLeft, yRight)) { + yLeft.min = yRight.min ? yRight.min * rate : yLeft.min; + yRight.min = yLeft.min ? yLeft.min / rate : yRight.min; + yLeft.max = yRight.max ? yRight.max * rate : yLeft.max; + yRight.max = yLeft.max ? yLeft.max / rate : yRight.max; } else { - yaxis[0].min = yaxis[0].min > 0 ? yaxis[1].min * rate : yaxis[0].min; - yaxis[1].min = yaxis[1].min > 0 ? yaxis[0].min / rate : yaxis[1].min; - yaxis[0].max = yaxis[0].max < 0 ? yaxis[1].max * rate : yaxis[0].max; - yaxis[1].max = yaxis[1].max < 0 ? yaxis[0].max / rate : yaxis[1].max; + yLeft.min = yLeft.min > 0 ? yRight.min * rate : yLeft.min; + yRight.min = yRight.min > 0 ? yLeft.min / rate : yRight.min; + yLeft.max = yLeft.max < 0 ? yRight.max * rate : yLeft.max; + yRight.max = yRight.max < 0 ? yLeft.max / rate : yRight.max; } } } } - restoreLevelFromZero(yaxis, alignLevel); + restoreLevelFromZero(yLeft, yRight, alignLevel); } -function expandStuckValues(yaxis) { +function expandStuckValues(yLeft, yRight) { // wide Y min and max using increased wideFactor var wideFactor = 0.25; - if (yaxis[0].max === yaxis[0].min) { - yaxis[0].min -= wideFactor; - yaxis[0].max += wideFactor; + if (yLeft.max === yLeft.min) { + yLeft.min -= wideFactor; + yLeft.max += wideFactor; } - if (yaxis[1].max === yaxis[1].min) { - yaxis[1].min -= wideFactor; - yaxis[1].max += wideFactor; + if (yRight.max === yRight.min) { + yRight.min -= wideFactor; + yRight.max += wideFactor; } } -function moveLevelToZero(yaxis, alignLevel) { +function moveLevelToZero(yLeft, yRight, alignLevel) { if (alignLevel !== 0) { - yaxis[0].min -= alignLevel; - yaxis[0].max -= alignLevel; - yaxis[1].min -= alignLevel; - yaxis[1].max -= alignLevel; + yLeft.min -= alignLevel; + yLeft.max -= alignLevel; + yRight.min -= alignLevel; + yRight.max -= alignLevel; } } -function restoreLevelFromZero(yaxis, alignLevel) { +function restoreLevelFromZero(yLeft, yRight, alignLevel) { if (alignLevel !== 0) { - yaxis[0].min += alignLevel; - yaxis[0].max += alignLevel; - yaxis[1].min += alignLevel; - yaxis[1].max += alignLevel; + yLeft.min += alignLevel; + yLeft.max += alignLevel; + yRight.min += alignLevel; + yRight.max += alignLevel; } } -function checkOneSide(yaxis) { +function checkOneSide(yLeft, yRight) { // on the one hand with respect to zero - return (yaxis[0].min >= 0 && yaxis[1].min >= 0) || (yaxis[0].max <= 0 && yaxis[1].max <= 0); + return (yLeft.min >= 0 && yRight.min >= 0) || (yLeft.max <= 0 && yRight.max <= 0); } -function checkTwoCross(yaxis) { +function checkTwoCross(yLeft, yRight) { // both across zero - return yaxis[0].min <= 0 && yaxis[0].max >= 0 && yaxis[1].min <= 0 && yaxis[1].max >= 0; + return yLeft.min <= 0 && yLeft.max >= 0 && yRight.min <= 0 && yRight.max >= 0; } -function getRate(yaxis) { +function checkOppositeSides(yLeft, yRight) { + // on the opposite sides with respect to zero + return (yLeft.min >= 0 && yRight.max <= 0) || (yLeft.max <= 0 && yRight.min >= 0); +} + +function getRate(yLeft, yRight) { var rateLeft, rateRight, rate; - if (checkTwoCross(yaxis)) { - rateLeft = yaxis[1].min ? yaxis[0].min / yaxis[1].min : 0; - rateRight = yaxis[1].max ? yaxis[0].max / yaxis[1].max : 0; + if (checkTwoCross(yLeft, yRight)) { + rateLeft = yRight.min ? yLeft.min / yRight.min : 0; + rateRight = yRight.max ? yLeft.max / yRight.max : 0; } else { - if (checkOneSide(yaxis)) { - var absLeftMin = Math.abs(yaxis[0].min); - var absLeftMax = Math.abs(yaxis[0].max); - var absRightMin = Math.abs(yaxis[1].min); - var absRightMax = Math.abs(yaxis[1].max); + if (checkOneSide(yLeft, yRight)) { + var absLeftMin = Math.abs(yLeft.min); + var absLeftMax = Math.abs(yLeft.max); + var absRightMin = Math.abs(yRight.min); + var absRightMax = Math.abs(yRight.max); var upLeft = _.max([absLeftMin, absLeftMax]); var downLeft = _.min([absLeftMin, absLeftMax]); var upRight = _.max([absRightMin, absRightMax]); @@ -120,12 +126,12 @@ function getRate(yaxis) { rateLeft = downLeft ? upLeft / downLeft : upLeft; rateRight = downRight ? upRight / downRight : upRight; } else { - if (yaxis[0].min > 0 || yaxis[1].min > 0) { - rateLeft = yaxis[0].max / yaxis[1].max; + if (yLeft.min > 0 || yRight.min > 0) { + rateLeft = yLeft.max / yRight.max; rateRight = 0; } else { rateLeft = 0; - rateRight = yaxis[0].min / yaxis[1].min; + rateRight = yLeft.min / yRight.min; } } } From 7dd66450adc6dea4ac499c1027937160997f64b4 Mon Sep 17 00:00:00 2001 From: ilgizar Date: Mon, 12 Mar 2018 23:43:13 +0500 Subject: [PATCH 11/30] Corrected work for graphs created before this feature. --- public/app/plugins/panel/graph/graph.ts | 2 +- public/vendor/flot/jquery.flot.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 3f7d1bee33caf..231b1ecaf422f 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -158,7 +158,7 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) { function processRangeHook(plot) { var yaxis = plot.getYAxes(); - if (yaxis.length > 1 && panel.yaxes[1].align !== null) { + if (yaxis.length > 1 && 'align' in panel.yaxes[1] && panel.yaxes[1].align !== null) { alignYLevel(yaxis, parseFloat(panel.yaxes[1].align)); } } diff --git a/public/vendor/flot/jquery.flot.js b/public/vendor/flot/jquery.flot.js index 401198b712de7..8ee09e25c4187 100644 --- a/public/vendor/flot/jquery.flot.js +++ b/public/vendor/flot/jquery.flot.js @@ -1633,7 +1633,7 @@ Licensed under the MIT license. measureTickLabels(axis); }); - if (snaped) { + if (snaped && hooks.processRange.length > 0) { executeHooks(hooks.processRange, []); snaped = false; } else { From 9d7ab78d9f0c38c143f8cdf1fda0644897493e12 Mon Sep 17 00:00:00 2001 From: ilgizar Date: Wed, 14 Mar 2018 23:39:05 +0500 Subject: [PATCH 12/30] Resolved conflict --- public/app/plugins/panel/graph/graph.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 231b1ecaf422f..bbfe63a87b2e1 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -17,7 +17,7 @@ import { appEvents, coreModule, updateLegendValues } from 'app/core/core'; import GraphTooltip from './graph_tooltip'; import { ThresholdManager } from './threshold_manager'; import { EventManager } from 'app/features/annotations/all'; -import { convertValuesToHistogram, getSeriesValues } from './histogram'; +import { convertToHistogramData } from './histogram'; import { alignYLevel } from './align_yaxes'; import config from 'app/core/config'; From 230f018c7820542162e7ae4f9e52bf56fcd7011a Mon Sep 17 00:00:00 2001 From: ilgizar Date: Tue, 20 Mar 2018 15:37:18 +0500 Subject: [PATCH 13/30] Added validation of input parameters. --- public/app/plugins/panel/graph/align_yaxes.ts | 12 ++++++++++++ .../plugins/panel/graph/specs/align_yaxes.jest.ts | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/public/app/plugins/panel/graph/align_yaxes.ts b/public/app/plugins/panel/graph/align_yaxes.ts index b60d75e7b668d..71bfcd8423d4d 100644 --- a/public/app/plugins/panel/graph/align_yaxes.ts +++ b/public/app/plugins/panel/graph/align_yaxes.ts @@ -6,6 +6,10 @@ import _ from 'lodash'; * @param align Y level */ export function alignYLevel(yaxis, alignLevel) { + if (isNaN(alignLevel) || !checkCorrectAxis(yaxis)) { + return; + } + var [yLeft, yRight] = yaxis; moveLevelToZero(yLeft, yRight, alignLevel); @@ -92,6 +96,14 @@ function restoreLevelFromZero(yLeft, yRight, alignLevel) { } } +function checkCorrectAxis(axis) { + return axis.length === 2 && checkCorrectAxes(axis[0]) && checkCorrectAxes(axis[1]); +} + +function checkCorrectAxes(axes) { + return 'min' in axes && 'max' in axes; +} + function checkOneSide(yLeft, yRight) { // on the one hand with respect to zero return (yLeft.min >= 0 && yRight.min >= 0) || (yLeft.max <= 0 && yRight.max <= 0); diff --git a/public/app/plugins/panel/graph/specs/align_yaxes.jest.ts b/public/app/plugins/panel/graph/specs/align_yaxes.jest.ts index ff540fd223fc3..963ecfbfa1f80 100644 --- a/public/app/plugins/panel/graph/specs/align_yaxes.jest.ts +++ b/public/app/plugins/panel/graph/specs/align_yaxes.jest.ts @@ -197,4 +197,15 @@ describe('Graph Y axes aligner', function() { expect(yaxes).toMatchObject(expected); }); }); + + describe('on level not number value', () => { + it('Should ignore without errors', () => { + alignY = 'q'; + yaxes = [{ min: 5, max: 10 }, { min: 2, max: 4 }]; + expected = [{ min: 5, max: 10 }, { min: 2, max: 4 }]; + + alignYLevel(yaxes, alignY); + expect(yaxes).toMatchObject(expected); + }); + }); }); From 1588295375574bfda62d34271707535dfed6c16d Mon Sep 17 00:00:00 2001 From: ilgizar Date: Tue, 20 Mar 2018 15:38:48 +0500 Subject: [PATCH 14/30] Changed the way this feature was activated. And changed tolltip. --- public/app/plugins/panel/graph/axes_editor.html | 11 ++++++++--- public/app/plugins/panel/graph/graph.ts | 5 +++-- public/app/plugins/panel/graph/module.ts | 3 ++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/public/app/plugins/panel/graph/axes_editor.html b/public/app/plugins/panel/graph/axes_editor.html index 2c08755c17ac7..f17c9ce105fe7 100644 --- a/public/app/plugins/panel/graph/axes_editor.html +++ b/public/app/plugins/panel/graph/axes_editor.html @@ -31,9 +31,14 @@
Right Y
-
- - +
+
+ +
+
+ + +
diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 73b261fce3a63..713d707915288 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -158,8 +158,9 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) { function processRangeHook(plot) { var yaxis = plot.getYAxes(); - if (yaxis.length > 1 && 'align' in panel.yaxes[1] && panel.yaxes[1].align !== null) { - alignYLevel(yaxis, parseFloat(panel.yaxes[1].align)); + if (yaxis.length > 1 && panel.yaxes[1].alignment) { + var align = panel.yaxes[1].align || 0; + alignYLevel(yaxis, parseFloat(align)); } } diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index c198d11811598..7e7b270bd6162 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -46,7 +46,8 @@ class GraphCtrl extends MetricsPanelCtrl { min: null, max: null, format: 'short', - align: null, + alignment: false, + align: 0, }, ], xaxis: { From e015047ed1c234eed8d14c91b3b6eb5f7f5b7612 Mon Sep 17 00:00:00 2001 From: ilgizar Date: Tue, 20 Mar 2018 16:09:52 +0500 Subject: [PATCH 15/30] Fixed unit test. --- public/app/plugins/panel/graph/specs/align_yaxes.jest.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/app/plugins/panel/graph/specs/align_yaxes.jest.ts b/public/app/plugins/panel/graph/specs/align_yaxes.jest.ts index 963ecfbfa1f80..da3aff912753d 100644 --- a/public/app/plugins/panel/graph/specs/align_yaxes.jest.ts +++ b/public/app/plugins/panel/graph/specs/align_yaxes.jest.ts @@ -200,11 +200,10 @@ describe('Graph Y axes aligner', function() { describe('on level not number value', () => { it('Should ignore without errors', () => { - alignY = 'q'; yaxes = [{ min: 5, max: 10 }, { min: 2, max: 4 }]; expected = [{ min: 5, max: 10 }, { min: 2, max: 4 }]; - alignYLevel(yaxes, alignY); + alignYLevel(yaxes, 'q'); expect(yaxes).toMatchObject(expected); }); }); From 0e159dada1be72b121c93cb2d682b890f44968e9 Mon Sep 17 00:00:00 2001 From: ilgizar Date: Tue, 20 Mar 2018 22:44:48 +0500 Subject: [PATCH 16/30] Allocated to a separate alignment block. Replaced the attribute of the second axis by the attribute of the axes. --- .../app/plugins/panel/graph/axes_editor.html | 20 +++++++++---------- public/app/plugins/panel/graph/graph.ts | 4 ++-- public/app/plugins/panel/graph/module.ts | 6 ++++-- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/public/app/plugins/panel/graph/axes_editor.html b/public/app/plugins/panel/graph/axes_editor.html index f17c9ce105fe7..9020bbe444652 100644 --- a/public/app/plugins/panel/graph/axes_editor.html +++ b/public/app/plugins/panel/graph/axes_editor.html @@ -31,16 +31,6 @@
Right Y
-
-
- -
-
- - -
-
-
@@ -77,6 +67,16 @@
X-Axis
+
+
+
Y-Axes
+ +
+ + +
+
+
diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 713d707915288..dc801a1b33f8c 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -158,8 +158,8 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) { function processRangeHook(plot) { var yaxis = plot.getYAxes(); - if (yaxis.length > 1 && panel.yaxes[1].alignment) { - var align = panel.yaxes[1].align || 0; + if (yaxis.length > 1 && panel.yaxis.alignment) { + var align = panel.yaxis.align || 0; alignYLevel(yaxis, parseFloat(align)); } } diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index 7e7b270bd6162..2fe1ecc868432 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -46,8 +46,6 @@ class GraphCtrl extends MetricsPanelCtrl { min: null, max: null, format: 'short', - alignment: false, - align: 0, }, ], xaxis: { @@ -57,6 +55,10 @@ class GraphCtrl extends MetricsPanelCtrl { values: [], buckets: null, }, + yaxis: { + alignment: false, + align: 0, + }, // show/hide lines lines: true, // fill factor From fc2d1d6ca913df0c0a0a9d2f62f27aace755eace Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 21 Mar 2018 17:08:25 +0300 Subject: [PATCH 17/30] fix dashboard version cleanup on large datasets --- pkg/services/sqlstore/dashboard_version.go | 41 ++++++++----------- .../sqlstore/dashboard_version_test.go | 2 +- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/pkg/services/sqlstore/dashboard_version.go b/pkg/services/sqlstore/dashboard_version.go index 547f62628f31b..3644af77355ef 100644 --- a/pkg/services/sqlstore/dashboard_version.go +++ b/pkg/services/sqlstore/dashboard_version.go @@ -1,7 +1,7 @@ package sqlstore import ( - "strings" + "fmt" "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" @@ -69,36 +69,31 @@ func GetDashboardVersions(query *m.GetDashboardVersionsQuery) error { func DeleteExpiredVersions(cmd *m.DeleteExpiredVersionsCommand) error { return inTransaction(func(sess *DBSession) error { - versions := []DashboardVersionExp{} versionsToKeep := setting.DashboardVersionsToKeep - if versionsToKeep < 1 { versionsToKeep = 1 } - err := sess.Table("dashboard_version"). - Select("dashboard_version.id, dashboard_version.version, dashboard_version.dashboard_id"). - Where(`dashboard_id IN ( - SELECT dashboard_id FROM dashboard_version - GROUP BY dashboard_id HAVING COUNT(dashboard_version.id) > ? - )`, versionsToKeep). - Desc("dashboard_version.dashboard_id", "dashboard_version.version"). - Find(&versions) - + // Idea of this query is finding version IDs to delete based on formula: + // min_version_to_keep = min_version + (versions_count - versions_to_keep) + // where version stats is processed for each dashboard. This guarantees that we keep at least versions_to_keep + // versions, but in some cases (when versions are sparse) this number may be more. + versionIdsToDeleteSybqueryTemplate := `SELECT id + FROM dashboard_version, ( + SELECT dashboard_id, count(version) as count, min(version) as min + FROM dashboard_version + GROUP BY dashboard_id + ) AS vtd + WHERE dashboard_version.dashboard_id=vtd.dashboard_id + AND version < vtd.min + vtd.count - %v` + + versionIdsToDeleteSubquery := fmt.Sprintf(versionIdsToDeleteSybqueryTemplate, versionsToKeep) + deleteExpiredSql := fmt.Sprintf(`DELETE FROM dashboard_version WHERE id IN (%s)`, versionIdsToDeleteSubquery) + expiredResponse, err := sess.Exec(deleteExpiredSql) if err != nil { return err } - - // Keep last versionsToKeep versions and delete other - versionIdsToDelete := getVersionIDsToDelete(versions, versionsToKeep) - if len(versionIdsToDelete) > 0 { - deleteExpiredSql := `DELETE FROM dashboard_version WHERE id IN (?` + strings.Repeat(",?", len(versionIdsToDelete)-1) + `)` - expiredResponse, err := sess.Exec(deleteExpiredSql, versionIdsToDelete...) - if err != nil { - return err - } - cmd.DeletedRows, _ = expiredResponse.RowsAffected() - } + cmd.DeletedRows, _ = expiredResponse.RowsAffected() return nil }) diff --git a/pkg/services/sqlstore/dashboard_version_test.go b/pkg/services/sqlstore/dashboard_version_test.go index 1b74e7847c464..151dc7c4be2a6 100644 --- a/pkg/services/sqlstore/dashboard_version_test.go +++ b/pkg/services/sqlstore/dashboard_version_test.go @@ -136,7 +136,7 @@ func TestDeleteExpiredVersions(t *testing.T) { err := DeleteExpiredVersions(&m.DeleteExpiredVersionsCommand{}) So(err, ShouldBeNil) - query := m.GetDashboardVersionsQuery{DashboardId: savedDash.Id, OrgId: 1} + query := m.GetDashboardVersionsQuery{DashboardId: savedDash.Id, OrgId: 1, Limit: versionsToWrite} GetDashboardVersions(&query) So(len(query.Result), ShouldEqual, versionsToWrite) From f976b690ca4a208c827077eabf4d2d7e856ba076 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 21 Mar 2018 20:26:27 +0300 Subject: [PATCH 18/30] limit number of rows deleted by dashboard version cleanup --- pkg/services/sqlstore/dashboard_version.go | 53 ++++++++-------------- 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/pkg/services/sqlstore/dashboard_version.go b/pkg/services/sqlstore/dashboard_version.go index 3644af77355ef..754bcd504a7f2 100644 --- a/pkg/services/sqlstore/dashboard_version.go +++ b/pkg/services/sqlstore/dashboard_version.go @@ -2,6 +2,7 @@ package sqlstore import ( "fmt" + "strings" "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" @@ -69,6 +70,8 @@ func GetDashboardVersions(query *m.GetDashboardVersionsQuery) error { func DeleteExpiredVersions(cmd *m.DeleteExpiredVersionsCommand) error { return inTransaction(func(sess *DBSession) error { + const MAX_VERSIONS_TO_DELETE = 100 + versionsToKeep := setting.DashboardVersionsToKeep if versionsToKeep < 1 { versionsToKeep = 1 @@ -83,12 +86,25 @@ func DeleteExpiredVersions(cmd *m.DeleteExpiredVersionsCommand) error { SELECT dashboard_id, count(version) as count, min(version) as min FROM dashboard_version GROUP BY dashboard_id - ) AS vtd - WHERE dashboard_version.dashboard_id=vtd.dashboard_id + ) AS vtd + WHERE dashboard_version.dashboard_id=vtd.dashboard_id AND version < vtd.min + vtd.count - %v` versionIdsToDeleteSubquery := fmt.Sprintf(versionIdsToDeleteSybqueryTemplate, versionsToKeep) - deleteExpiredSql := fmt.Sprintf(`DELETE FROM dashboard_version WHERE id IN (%s)`, versionIdsToDeleteSubquery) + versions := []string{} + err := sess.SQL(versionIdsToDeleteSubquery).Find(&versions) + if err != nil { + return err + } + + // Don't delete more than MAX_VERSIONS_TO_DELETE version per time + limit := MAX_VERSIONS_TO_DELETE + if len(versions) < MAX_VERSIONS_TO_DELETE { + limit = len(versions) + } + versions = versions[:limit] + + deleteExpiredSql := fmt.Sprintf(`DELETE FROM dashboard_version WHERE id IN (%s)`, strings.Join(versions, `,`)) expiredResponse, err := sess.Exec(deleteExpiredSql) if err != nil { return err @@ -98,34 +114,3 @@ func DeleteExpiredVersions(cmd *m.DeleteExpiredVersionsCommand) error { return nil }) } - -// Short version of DashboardVersion for getting expired versions -type DashboardVersionExp struct { - Id int64 `json:"id"` - DashboardId int64 `json:"dashboardId"` - Version int `json:"version"` -} - -func getVersionIDsToDelete(versions []DashboardVersionExp, versionsToKeep int) []interface{} { - versionIds := make([]interface{}, 0) - - if len(versions) == 0 { - return versionIds - } - - currentDashboard := versions[0].DashboardId - count := 0 - for _, v := range versions { - if v.DashboardId == currentDashboard { - count++ - } else { - count = 1 - currentDashboard = v.DashboardId - } - if count > versionsToKeep { - versionIds = append(versionIds, v.Id) - } - } - - return versionIds -} From 3f85fcce2ddc0ff81f83565373d218fdef2dfb3f Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 21 Mar 2018 21:01:15 +0300 Subject: [PATCH 19/30] refactor: dashboard version cleanup --- pkg/services/sqlstore/dashboard_version.go | 28 ++++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/pkg/services/sqlstore/dashboard_version.go b/pkg/services/sqlstore/dashboard_version.go index 754bcd504a7f2..54c858df7de5e 100644 --- a/pkg/services/sqlstore/dashboard_version.go +++ b/pkg/services/sqlstore/dashboard_version.go @@ -81,7 +81,7 @@ func DeleteExpiredVersions(cmd *m.DeleteExpiredVersionsCommand) error { // min_version_to_keep = min_version + (versions_count - versions_to_keep) // where version stats is processed for each dashboard. This guarantees that we keep at least versions_to_keep // versions, but in some cases (when versions are sparse) this number may be more. - versionIdsToDeleteSybqueryTemplate := `SELECT id + versionIdsToDeleteSubqueryTemplate := `SELECT id FROM dashboard_version, ( SELECT dashboard_id, count(version) as count, min(version) as min FROM dashboard_version @@ -90,26 +90,28 @@ func DeleteExpiredVersions(cmd *m.DeleteExpiredVersionsCommand) error { WHERE dashboard_version.dashboard_id=vtd.dashboard_id AND version < vtd.min + vtd.count - %v` - versionIdsToDeleteSubquery := fmt.Sprintf(versionIdsToDeleteSybqueryTemplate, versionsToKeep) - versions := []string{} - err := sess.SQL(versionIdsToDeleteSubquery).Find(&versions) + versionIdsToDeleteSubquery := fmt.Sprintf(versionIdsToDeleteSubqueryTemplate, versionsToKeep) + var versionIdsToDelete []interface{} + err := sess.SQL(versionIdsToDeleteSubquery).Find(&versionIdsToDelete) if err != nil { return err } // Don't delete more than MAX_VERSIONS_TO_DELETE version per time limit := MAX_VERSIONS_TO_DELETE - if len(versions) < MAX_VERSIONS_TO_DELETE { - limit = len(versions) + if len(versionIdsToDelete) < MAX_VERSIONS_TO_DELETE { + limit = len(versionIdsToDelete) } - versions = versions[:limit] - - deleteExpiredSql := fmt.Sprintf(`DELETE FROM dashboard_version WHERE id IN (%s)`, strings.Join(versions, `,`)) - expiredResponse, err := sess.Exec(deleteExpiredSql) - if err != nil { - return err + versionIdsToDelete = versionIdsToDelete[:limit] + + if len(versionIdsToDelete) > 0 { + deleteExpiredSql := `DELETE FROM dashboard_version WHERE id IN (?` + strings.Repeat(",?", len(versionIdsToDelete)-1) + `)` + expiredResponse, err := sess.Exec(deleteExpiredSql, versionIdsToDelete...) + if err != nil { + return err + } + cmd.DeletedRows, _ = expiredResponse.RowsAffected() } - cmd.DeletedRows, _ = expiredResponse.RowsAffected() return nil }) From 2ade0881b164437fb68870fdd1ae43fa66612923 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 21 Mar 2018 22:48:17 +0300 Subject: [PATCH 20/30] minor refactor of dashboard version cleanup --- pkg/services/sqlstore/dashboard_version.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pkg/services/sqlstore/dashboard_version.go b/pkg/services/sqlstore/dashboard_version.go index 54c858df7de5e..d91b572754529 100644 --- a/pkg/services/sqlstore/dashboard_version.go +++ b/pkg/services/sqlstore/dashboard_version.go @@ -1,7 +1,6 @@ package sqlstore import ( - "fmt" "strings" "github.com/grafana/grafana/pkg/bus" @@ -81,18 +80,17 @@ func DeleteExpiredVersions(cmd *m.DeleteExpiredVersionsCommand) error { // min_version_to_keep = min_version + (versions_count - versions_to_keep) // where version stats is processed for each dashboard. This guarantees that we keep at least versions_to_keep // versions, but in some cases (when versions are sparse) this number may be more. - versionIdsToDeleteSubqueryTemplate := `SELECT id + versionIdsToDeleteSubquery := `SELECT id FROM dashboard_version, ( SELECT dashboard_id, count(version) as count, min(version) as min FROM dashboard_version GROUP BY dashboard_id ) AS vtd WHERE dashboard_version.dashboard_id=vtd.dashboard_id - AND version < vtd.min + vtd.count - %v` + AND version < vtd.min + vtd.count - ?` - versionIdsToDeleteSubquery := fmt.Sprintf(versionIdsToDeleteSubqueryTemplate, versionsToKeep) var versionIdsToDelete []interface{} - err := sess.SQL(versionIdsToDeleteSubquery).Find(&versionIdsToDelete) + err := sess.SQL(versionIdsToDeleteSubquery, versionsToKeep).Find(&versionIdsToDelete) if err != nil { return err } From 0ffcea08c7188c6760322160b0bcfea1374a086f Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 22 Mar 2018 15:18:05 +0300 Subject: [PATCH 21/30] dashboard version cleanup: more tests and refactor --- pkg/services/sqlstore/dashboard_version.go | 20 +++++++++---------- .../sqlstore/dashboard_version_test.go | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/pkg/services/sqlstore/dashboard_version.go b/pkg/services/sqlstore/dashboard_version.go index d91b572754529..1f2850b2021cf 100644 --- a/pkg/services/sqlstore/dashboard_version.go +++ b/pkg/services/sqlstore/dashboard_version.go @@ -67,10 +67,10 @@ func GetDashboardVersions(query *m.GetDashboardVersionsQuery) error { return nil } +const MAX_VERSIONS_TO_DELETE = 100 + func DeleteExpiredVersions(cmd *m.DeleteExpiredVersionsCommand) error { return inTransaction(func(sess *DBSession) error { - const MAX_VERSIONS_TO_DELETE = 100 - versionsToKeep := setting.DashboardVersionsToKeep if versionsToKeep < 1 { versionsToKeep = 1 @@ -80,27 +80,25 @@ func DeleteExpiredVersions(cmd *m.DeleteExpiredVersionsCommand) error { // min_version_to_keep = min_version + (versions_count - versions_to_keep) // where version stats is processed for each dashboard. This guarantees that we keep at least versions_to_keep // versions, but in some cases (when versions are sparse) this number may be more. - versionIdsToDeleteSubquery := `SELECT id + versionIdsToDeleteQuery := `SELECT id FROM dashboard_version, ( SELECT dashboard_id, count(version) as count, min(version) as min FROM dashboard_version GROUP BY dashboard_id - ) AS vtd - WHERE dashboard_version.dashboard_id=vtd.dashboard_id - AND version < vtd.min + vtd.count - ?` + ) AS vtd + WHERE dashboard_version.dashboard_id=vtd.dashboard_id + AND version < vtd.min + vtd.count - ?` var versionIdsToDelete []interface{} - err := sess.SQL(versionIdsToDeleteSubquery, versionsToKeep).Find(&versionIdsToDelete) + err := sess.SQL(versionIdsToDeleteQuery, versionsToKeep).Find(&versionIdsToDelete) if err != nil { return err } // Don't delete more than MAX_VERSIONS_TO_DELETE version per time - limit := MAX_VERSIONS_TO_DELETE - if len(versionIdsToDelete) < MAX_VERSIONS_TO_DELETE { - limit = len(versionIdsToDelete) + if len(versionIdsToDelete) > MAX_VERSIONS_TO_DELETE { + versionIdsToDelete = versionIdsToDelete[:MAX_VERSIONS_TO_DELETE] } - versionIdsToDelete = versionIdsToDelete[:limit] if len(versionIdsToDelete) > 0 { deleteExpiredSql := `DELETE FROM dashboard_version WHERE id IN (?` + strings.Repeat(",?", len(versionIdsToDelete)-1) + `)` diff --git a/pkg/services/sqlstore/dashboard_version_test.go b/pkg/services/sqlstore/dashboard_version_test.go index 151dc7c4be2a6..a6403755d05b2 100644 --- a/pkg/services/sqlstore/dashboard_version_test.go +++ b/pkg/services/sqlstore/dashboard_version_test.go @@ -141,5 +141,25 @@ func TestDeleteExpiredVersions(t *testing.T) { So(len(query.Result), ShouldEqual, versionsToWrite) }) + + Convey("Don't delete more than MAX_VERSIONS_TO_DELETE per iteration", func() { + versionsToWriteBigNumber := MAX_VERSIONS_TO_DELETE + versionsToWrite + for i := 0; i < versionsToWriteBigNumber-versionsToWrite; i++ { + updateTestDashboard(savedDash, map[string]interface{}{ + "tags": "different-tag", + }) + } + + err := DeleteExpiredVersions(&m.DeleteExpiredVersionsCommand{}) + So(err, ShouldBeNil) + + query := m.GetDashboardVersionsQuery{DashboardId: savedDash.Id, OrgId: 1, Limit: versionsToWriteBigNumber} + GetDashboardVersions(&query) + + // Ensure we have at least versionsToKeep versions + So(len(query.Result), ShouldBeGreaterThanOrEqualTo, versionsToKeep) + // Ensure we haven't deleted more than MAX_VERSIONS_TO_DELETE rows + So(versionsToWriteBigNumber-len(query.Result), ShouldBeLessThanOrEqualTo, MAX_VERSIONS_TO_DELETE) + }) }) } From e722732021bcd243411df2e6a982b7acae9c319d Mon Sep 17 00:00:00 2001 From: Gerben Meijer Date: Thu, 22 Mar 2018 16:27:24 +0100 Subject: [PATCH 22/30] Return actual user ID in UserProfileDTO This fixes calls to /api/user where ID is currently always 0 --- pkg/services/sqlstore/user.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/services/sqlstore/user.go b/pkg/services/sqlstore/user.go index 73ea07f031f77..f42ff5fb2ed6f 100644 --- a/pkg/services/sqlstore/user.go +++ b/pkg/services/sqlstore/user.go @@ -315,6 +315,7 @@ func GetUserProfile(query *m.GetUserProfileQuery) error { } query.Result = m.UserProfileDTO{ + Id: user.Id, Name: user.Name, Email: user.Email, Login: user.Login, From 823f9030488166f2036873d9567387f9a14777c3 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 22 Mar 2018 16:59:06 +0100 Subject: [PATCH 23/30] removed trash can icon from save buttons --- public/app/containers/ManageDashboards/FolderSettings.tsx | 2 +- public/app/features/dashboard/partials/folder_settings.html | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/public/app/containers/ManageDashboards/FolderSettings.tsx b/public/app/containers/ManageDashboards/FolderSettings.tsx index 586a8f05b4c71..ff4e9cb417f67 100644 --- a/public/app/containers/ManageDashboards/FolderSettings.tsx +++ b/public/app/containers/ManageDashboards/FolderSettings.tsx @@ -143,7 +143,7 @@ export class FolderSettings extends React.Component { className="btn btn-success" disabled={!folder.folder.canSave || !folder.folder.hasChanged} > - Save + Save