From cc31a12b8c08cf9b10dae1da49564db37189397f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 29 Sep 2014 11:37:56 +0200 Subject: [PATCH] Smart decimal precision when using scaled unit format, Closes #877 --- src/app/components/kbn.js | 253 +++++------------------------ src/app/components/timeSeries.js | 14 +- src/app/directives/grafanaGraph.js | 10 +- src/test/specs/kbn-format-specs.js | 83 +++------- src/vendor/jquery/jquery.flot.js | 2 + 5 files changed, 77 insertions(+), 285 deletions(-) diff --git a/src/app/components/kbn.js b/src/app/components/kbn.js index e41722747fb..cb084b1c166 100644 --- a/src/app/components/kbn.js +++ b/src/app/components/kbn.js @@ -7,7 +7,7 @@ function($, _, moment) { 'use strict'; var kbn = {}; - kbn.formatFunctions = {}; + kbn.valueFormats = {}; kbn.round_interval = function(interval) { switch (true) { @@ -310,192 +310,24 @@ function($, _, moment) { ].join(';') + '">'; }; - kbn.byteFormat = function(size, decimals) { - var ext, steps = 0; - - if(_.isUndefined(decimals)) { - decimals = 2; - } else if (decimals === 0) { - decimals = undefined; - } - - while (Math.abs(size) >= 1024) { - steps++; - size /= 1024; - } - - switch (steps) { - case 0: - ext = " B"; - break; - case 1: - ext = " KiB"; - break; - case 2: - ext = " MiB"; - break; - case 3: - ext = " GiB"; - break; - case 4: - ext = " TiB"; - break; - case 5: - ext = " PiB"; - break; - case 6: - ext = " EiB"; - break; - case 7: - ext = " ZiB"; - break; - case 8: - ext = " YiB"; - break; - } - - return (size.toFixed(decimals) + ext); + kbn.valueFormats.percent = function(size, decimals) { + return kbn.toFixed(size, decimals) + '%'; }; - kbn.bitFormat = function(size, decimals) { - var ext, steps = 0; + kbn.formatFuncCreator = function(factor, extArray) { + return function(size, decimals, scaledDecimals) { + var steps = 0; - if(_.isUndefined(decimals)) { - decimals = 2; - } else if (decimals === 0) { - decimals = undefined; - } + while (Math.abs(size) >= factor) { + steps++; + size /= factor; + } + if (steps > 0) { + decimals = scaledDecimals + (3 * steps); + } - while (Math.abs(size) >= 1024) { - steps++; - size /= 1024; - } - - switch (steps) { - case 0: - ext = " b"; - break; - case 1: - ext = " Kib"; - break; - case 2: - ext = " Mib"; - break; - case 3: - ext = " Gib"; - break; - case 4: - ext = " Tib"; - break; - case 5: - ext = " Pib"; - break; - case 6: - ext = " Eib"; - break; - case 7: - ext = " Zib"; - break; - case 8: - ext = " Yib"; - break; - } - - return (size.toFixed(decimals) + ext); - }; - - kbn.bpsFormat = function(size, decimals) { - var ext, steps = 0; - - if(_.isUndefined(decimals)) { - decimals = 2; - } else if (decimals === 0) { - decimals = undefined; - } - - while (Math.abs(size) >= 1000) { - steps++; - size /= 1000; - } - - switch (steps) { - case 0: - ext = " bps"; - break; - case 1: - ext = " Kbps"; - break; - case 2: - ext = " Mbps"; - break; - case 3: - ext = " Gbps"; - break; - case 4: - ext = " Tbps"; - break; - case 5: - ext = " Pbps"; - break; - case 6: - ext = " Ebps"; - break; - case 7: - ext = " Zbps"; - break; - case 8: - ext = " Ybps"; - break; - } - - return (size.toFixed(decimals) + ext); - }; - - kbn.shortFormat = function(size, decimals) { - var ext, steps = 0; - - if(_.isUndefined(decimals)) { - decimals = 2; - } else if (decimals === 0) { - decimals = undefined; - } - - while (Math.abs(size) >= 1000) { - steps++; - size /= 1000; - } - - switch (steps) { - case 0: - ext = ""; - break; - case 1: - ext = " K"; - break; - case 2: - ext = " Mil"; - break; - case 3: - ext = " Bil"; - break; - case 4: - ext = " Tri"; - break; - case 5: - ext = " Quadr"; - break; - case 6: - ext = " Quint"; - break; - case 7: - ext = " Sext"; - break; - case 8: - ext = " Sept"; - break; - } - - return (size.toFixed(decimals) + ext); + return kbn.toFixed(size, decimals) + extArray[steps]; + }; }; kbn.toFixed = function(value, decimals) { @@ -520,84 +352,87 @@ function($, _, moment) { return formatted; }; - kbn.formatFunctions.ms = function(size, decimals) { + kbn.valueFormats.bits = kbn.formatFuncCreator(1024, [' b', ' Kib', ' Mib', ' Gib', ' Tib', ' Pib', ' Eib', ' Zib', ' Yib']); + kbn.valueFormats.bytes = kbn.formatFuncCreator(1024, [' B', ' KiB', ' MiB', ' GiB', ' TiB', ' PiB', ' EiB', ' ZiB', ' YiB']); + kbn.valueFormats.bps = kbn.formatFuncCreator(1000, [' bps', ' Kbps', ' Mbps', ' Gbps', ' Tbps', ' Pbps', ' Ebps', ' Zbps', ' Ybps']); + kbn.valueFormats.short = kbn.formatFuncCreator(1000, ['', ' K', ' Mil', ' Bil', ' Tri', ' Qaudr', ' Quint', ' Sext', ' Sept']); + kbn.valueFormats.none = kbn.toFixed; + + kbn.valueFormats.ms = function(size, decimals, scaledDecimals) { if (Math.abs(size) < 1000) { return kbn.toFixed(size, decimals) + " ms"; } // Less than 1 min else if (Math.abs(size) < 60000) { - return kbn.toFixed(size / 1000, decimals) + " s"; + return kbn.toFixed(size / 1000, scaledDecimals + 3) + " s"; } // Less than 1 hour, devide in minutes else if (Math.abs(size) < 3600000) { - return (size / 60000).toFixed(decimals) + " min"; + return kbn.toFixed(size / 60000, scaledDecimals + 5) + " min"; } // Less than one day, devide in hours else if (Math.abs(size) < 86400000) { - return (size / 3600000).toFixed(decimals) + " hour"; + return kbn.toFixed(size / 3600000, scaledDecimals + 7) + " hour"; } // Less than one year, devide in days else if (Math.abs(size) < 31536000000) { - return (size / 86400000).toFixed(decimals) + " day"; + return kbn.toFixed(size / 86400000, scaledDecimals + 8) + " day"; } - return (size / 31536000000).toFixed(decimals) + " year"; + return kbn.toFixed(size / 31536000000, scaledDecimals + 10) + " year"; }; - kbn.sFormat = function(size, decimals) { + kbn.valueFormats.s = function(size, decimals, scaledDecimals) { if (Math.abs(size) < 600) { - return size.toFixed(decimals) + " s"; + return kbn.toFixed(size, decimals) + " s"; } // Less than 1 hour, devide in minutes else if (Math.abs(size) < 3600) { - return (size / 60).toFixed(decimals) + " min"; + return kbn.toFixed(size / 60, scaledDecimals + 1) + " min"; } // Less than one day, devide in hours else if (Math.abs(size) < 86400) { - return (size / 3600).toFixed(decimals) + " hour"; + return kbn.toFixed(size / 3600, scaledDecimals + 4) + " hour"; } // Less than one week, devide in days else if (Math.abs(size) < 604800) { - return (size / 86400).toFixed(decimals) + " day"; + return kbn.toFixed(size / 86400, scaledDecimals + 5) + " day"; } // Less than one year, devide in week else if (Math.abs(size) < 31536000) { - return (size / 604800).toFixed(decimals) + " week"; + return kbn.toFixed(size / 604800, scaledDecimals + 6) + " week"; } - return (size / 3.15569e7).toFixed(decimals) + " year"; + return kbn.toFixed(size / 3.15569e7, scaledDecimals + 7) + " year"; }; - kbn.microsFormat = function(size, decimals) { + kbn.valueFormats['µs'] = function(size, decimals, scaledDecimals) { if (Math.abs(size) < 1000) { return kbn.toFixed(size, decimals) + " µs"; } else if (Math.abs(size) < 1000000) { - return (size / 1000).toFixed(decimals) + " ms"; + return kbn.toFixed(size / 1000, scaledDecimals + 3) + " ms"; } else { - return (size / 1000000).toFixed(decimals) + " s"; + return kbn.toFixed(size / 1000000, scaledDecimals + 6) + " s"; } }; - kbn.nanosFormat = function(size, decimals) { - if (Math.abs(size) < 1) { - return size.toFixed(decimals) + " ns"; - } - else if (Math.abs(size) < 1000) { - return size.toFixed(0) + " ns"; + kbn.valueFormats.ns = function(size, decimals, scaledDecimals) { + if (Math.abs(size) < 1000) { + return kbn.toFixed(size, decimals) + " ns"; } else if (Math.abs(size) < 1000000) { - return (size / 1000).toFixed(decimals) + " µs"; + return kbn.toFixed(size / 1000, scaledDecimals + 3) + " µs"; } else if (Math.abs(size) < 1000000000) { - return (size / 1000000).toFixed(decimals) + " ms"; + return kbn.toFixed(size / 1000000, scaledDecimals + 6) + " ms"; } else if (Math.abs(size) < 60000000000){ - return (size / 1000000000).toFixed(decimals) + " s"; + return kbn.toFixed(size / 1000000000, scaledDecimals + 9) + " s"; } else { - return (size / 60000000000).toFixed(decimals) + " m"; + return kbn.toFixed(size / 60000000000, scaledDecimals + 12) + " m"; } }; diff --git a/src/app/components/timeSeries.js b/src/app/components/timeSeries.js index 576e8be8e8a..a448a93649c 100644 --- a/src/app/components/timeSeries.js +++ b/src/app/components/timeSeries.js @@ -54,7 +54,7 @@ function (_, kbn) { } }; - TimeSeries.prototype.getFlotPairs = function (fillStyle, yFormats) { + TimeSeries.prototype.getFlotPairs = function (fillStyle) { var result = []; this.color = this.info.color; @@ -107,12 +107,12 @@ function (_, kbn) { return result; }; - TimeSeries.prototype.updateLegendValues = function(formater, decimals) { - this.info.avg = this.info.avg != null ? formater(this.info.avg, decimals) : null; - this.info.current = this.info.current != null ? formater(this.info.current, decimals) : null; - this.info.min = this.info.min != null ? formater(this.info.min, decimals) : null; - this.info.max = this.info.max != null ? formater(this.info.max, decimals) : null; - this.info.total = this.info.total != null ? formater(this.info.total, decimals) : null; + TimeSeries.prototype.updateLegendValues = function(formater, decimals, scaledDecimals) { + this.info.avg = this.info.avg != null ? formater(this.info.avg, decimals, scaledDecimals) : null; + this.info.current = this.info.current != null ? formater(this.info.current, decimals, scaledDecimals) : null; + this.info.min = this.info.min != null ? formater(this.info.min, decimals, scaledDecimals) : null; + this.info.max = this.info.max != null ? formater(this.info.max, decimals, scaledDecimals) : null; + this.info.total = this.info.total != null ? formater(this.info.total, decimals, scaledDecimals) : null; }; return TimeSeries; diff --git a/src/app/directives/grafanaGraph.js b/src/app/directives/grafanaGraph.js index 53edcf1bc13..df48de89c6f 100755 --- a/src/app/directives/grafanaGraph.js +++ b/src/app/directives/grafanaGraph.js @@ -90,12 +90,12 @@ function (angular, $, kbn, moment, _) { function updateLegendValues(plot) { var yaxis = plot.getYAxes(); - console.log('drawSeries', yaxis); for (var i = 0; i < data.length; i++) { var series = data[i]; - var formater = kbn.formatFunctions[scope.panel.y_formats[series.yaxis - 1]]; - series.updateLegendValues(formater, yaxis[series.yaxis - 1].tickDecimals); + var axis = yaxis[series.yaxis - 1]; + var formater = kbn.valueFormats[scope.panel.y_formats[series.yaxis - 1]]; + series.updateLegendValues(formater, axis.tickDecimals, axis.scaledDecimals); } } @@ -327,7 +327,7 @@ function (angular, $, kbn, moment, _) { function configureAxisMode(axis, format) { axis.tickFormatter = function(val, axis) { - return kbn.formatFunctions[format](val, axis.tickDecimals); + return kbn.valueFormats[format](val, axis.tickDecimals, axis.scaledDecimals); }; } @@ -378,7 +378,7 @@ function (angular, $, kbn, moment, _) { value = item.datapoint[1]; } - value = kbn.getFormatFunction(format, 2)(value, item.series.yaxis); + value = kbn.valueFormats[format](value, item.series.yaxis.tickDecimals); timestamp = dashboard.formatDate(item.datapoint[0]); $tooltip.html(group + value + " @ " + timestamp).place_tt(pos.pageX, pos.pageY); diff --git a/src/test/specs/kbn-format-specs.js b/src/test/specs/kbn-format-specs.js index 85807c277ec..e92e6d0ceec 100644 --- a/src/test/specs/kbn-format-specs.js +++ b/src/test/specs/kbn-format-specs.js @@ -3,76 +3,31 @@ define([ ], function(kbn) { 'use strict'; - describe('millisecond formating', function() { + function describeValueFormat(desc, value, tickSize, tickDecimals, result) { - it('should translate 4378634603 as 1.67 years', function() { - var str = kbn.msFormat(4378634603, 2); - expect(str).to.be('50.68 day'); + describe('value format: ' + desc, function() { + it('should translate ' + value + ' as ' + result, function() { + var scaledDecimals = tickDecimals - Math.floor(Math.log(tickSize) / Math.LN10); + var str = kbn.valueFormats[desc](value, tickDecimals, scaledDecimals); + expect(str).to.be(result); + }); }); - it('should translate 3654454 as 1.02 hour', function() { - var str = kbn.msFormat(3654454, 2); - expect(str).to.be('1.02 hour'); - }); + } - it('should not downscale when value is zero', function() { - var str = kbn.msFormat(0, 2); - expect(str).to.be('0.00 ms'); - }); + describeValueFormat('ms', 0.0024, 0.0005, 4, '0.0024 ms'); + describeValueFormat('ms', 100, 1, 0, '100 ms'); + describeValueFormat('ms', 1250, 10, 0, '1.25 s'); + describeValueFormat('ms', 1250, 300, 0, '1.3 s'); + describeValueFormat('ms', 65150, 10000, 0, '1.1 min'); + describeValueFormat('ms', 6515000, 1500000, 0, '1.8 hour'); + describeValueFormat('ms', 651500000, 150000000, 0, '8 day'); + describeValueFormat('none', 2.75e-10, 0, 10, '3e-10'); + describeValueFormat('none', 0, 0, 2, '0'); - it('should translate 365445 as 6.09 min', function() { - var str = kbn.msFormat(365445, 2); - expect(str).to.be('6.09 min'); - }); - - }); - - describe('high negative exponent, issue #696', function() { - it('should ignore decimal correction if exponent', function() { - var str = kbn.getFormatFunction('')(2.75e-10, { tickDecimals: 12 }); - expect(str).to.be('2.75e-10'); - }); - it('should format 0 correctly', function() { - var str = kbn.getFormatFunction('')(0.0, { tickDecimals: 12 }); - expect(str).to.be('0'); - }); - }); - - describe('none format tests', function() { - it('should translate 2 as 2.0000 if axis decimals is 4', function() { - var str = kbn.getFormatFunction('')(2, { tickDecimals: 4 }); - expect(str).to.be('2.0000'); - }); - }); - - describe('nanosecond formatting', function () { - it('should translate 25 to 25 ns', function () { - var str = kbn.nanosFormat(25, 2); - expect(str).to.be("25 ns"); - }); - - it('should translate 2558 to 2.56 µs', function () { - var str = kbn.nanosFormat(2558, 2); - expect(str).to.be("2.56 µs"); - }); - - it('should translate 2558000 to 2.56 ms', function () { - var str = kbn.nanosFormat(2558000, 2); - expect(str).to.be("2.56 ms"); - }); - - it('should translate 2019962000 to 2.02 s', function () { - var str = kbn.nanosFormat(2049962000, 2); - expect(str).to.be("2.05 s"); - }); - - it('should translate 95199620000 to 1.59 m', function () { - var str = kbn.nanosFormat(95199620000, 2); - expect(str).to.be("1.59 m"); - }); - - }); + describeValueFormat('ns', 25, 1, 0, '25 ns'); + describeValueFormat('ns', 2558, 50, 0, '2.56 µs'); describe('calculateInterval', function() { it('1h 100 resultion', function() { diff --git a/src/vendor/jquery/jquery.flot.js b/src/vendor/jquery/jquery.flot.js index c97ceb1bf44..87a1b17c742 100644 --- a/src/vendor/jquery/jquery.flot.js +++ b/src/vendor/jquery/jquery.flot.js @@ -1728,6 +1728,8 @@ Licensed under the MIT license. axis.delta = delta; axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec); axis.tickSize = opts.tickSize || size; + // grafana addition + axis.scaledDecimals = axis.tickDecimals - Math.floor(Math.log(axis.tickSize) / Math.LN10); // Time mode was moved to a plug-in in 0.8, and since so many people use it // we'll add an especially friendly reminder to make sure they included it.