lib/visage-app/public/javascripts/graph.js in visage-app-2.0.5 vs lib/visage-app/public/javascripts/graph.js in visage-app-2.1.0

- old
+ new

@@ -119,10 +119,11 @@ var label = value, unit = ''; break } + if (!(label)) { label = 0; } return label.format({decimals: precision, suffix: unit}) } function formatDate(d) { var datetime = new Date(d) @@ -166,10 +167,11 @@ this.getData(); // calls graphData }, dataURL: function() { var url = ['data', this.options.host, this.options.plugin]; + // if the data exists on another host (useful for embedding) if (this.options.baseurl) { url.unshift(this.options.baseurl.replace(/\/$/, '')) } // for specific plugin instances @@ -178,11 +180,18 @@ } // if no url is specified if (!url[0].match(/http\:\/\//)) { url[0] = '/' + url[0] } - return url.join('/') + var options = ''; + if (this.options.percentiles) { + if (this.options.percentiles.length > 0) { + options = '?percentiles=true'; + this.options.percentile95 = true; + } + } + return url.join('/') + options; }, getData: function() { this.request = new Request.JSONP({ url: this.dataURL(), data: this.requestData, @@ -248,13 +257,15 @@ this.chart.xAxis.concat(this.chart.yAxis).each(function(axis) { axis.setExtremes(null,null,false); }); this.chart.redraw(); + this.drawPercentiles(this.chart) break; default: this.drawChart() + this.drawPercentiles(this.chart) break; } }, buildDataStructures: function (data) { var series = this.series = [] @@ -262,12 +273,12 @@ var plugin = this.options.plugin var data = data ? data : this.response $each(data[host][plugin], function(instance, instanceName) { $each(instance, function(metric, metricName) { - var start = metric.start, - finish = metric.finish, + var start = metric.start, + finish = metric.finish, interval = (finish - start) / metric.data.length; var data = metric.data.map(function(value, index) { var x = (start + index * interval) * 1000, y = value; @@ -276,16 +287,16 @@ }); var set = { name: [ host, plugin, instanceName, metricName ], data: data, + percentile95: metric.percentile_95 }; series.push(set) }, this); }, this); - return series }, getSeriesMinMax: function(series) { var min, max; @@ -311,11 +322,44 @@ } }); return {'min': min, 'max': max}; }, + removePercentiles: function(chart) { + + var series = this.series; + + series.each(function(set) { + chart.yAxis[0].removePlotLine('95e_' + set.name[2] + set.name[3]); + }); + }, + drawPercentiles: function(chart) { + var series = this.series; + + /* Get the maximum value across all sets. + * Used later on to determine the decimal place in the label. */ + meta = this.getSeriesMinMax(series); + var min = meta.min, + max = meta.max; + + series.each(function(set) { + formattedValue = formatValue(set.percentile95, { 'precision': 2, 'min': min, 'max': max }); + chart.yAxis[0].removePlotLine('95e_' + set.name[2] + set.name[3]); + chart.yAxis[0].addPlotLine({ + id: '95e_' + set.name[2] + set.name[3], + value: set.percentile95, + color: '#ff0000', + width: 1, + zIndex: 5, + label: { + text: '95e ' + set.name[3] + ": " + formattedValue + } + }) + }); + }, drawChart: function() { + var series = this.series, title = this.title(), element = this.parentElement, ytitle = formatPluginName(this.options.plugin), min, @@ -342,10 +386,13 @@ if (this.options.live) { var data = { 'start': this.lastFinish / 1000, 'finish': this.lastFinish / 1000 + 10, 'live': true }; this.requestData = data; + // FIXME: for 95e plotLines - need to update them each data retrieval + // but perhaps 'live' just sends incremental data and so won't cause + // a recalculation of the 95e figures on the server side? this.getData() } }.bind(this), 10000); }.bind(this) } @@ -398,11 +445,11 @@ 'min': min, 'max': max }); return value } - } + }, }, plotOptions: { series: { shadow: false, lineWidth: 1, @@ -445,12 +492,13 @@ }, legend: { layout: 'horizontal', align: 'center', verticalAlign: 'top', - y: 275, + y: 255, borderWidth: 0, + floating: true, labelFormatter: function() { return formatSeriesLabel(this.name) }, itemStyle: { cursor: 'pointer', @@ -520,11 +568,11 @@ '24 hours': 24, '3 days': 72, '7 days': 168, '2 weeks': 336, '1 month': 774, - '3 month': 2322, + '3 months': 2322, '6 months': 4368, '1 year': 8760, '2 years': 17520 }); timescales.each(function(hour, label) { @@ -538,18 +586,49 @@ 'selected': (current ? 'selected' : ''), }); select.grab(option) }); - var liveToggler = new Element('input', { + /* Calendar month timescales dropdown */ + var monthlyTimescales = new Hash({ 'current month': 0, + 'previous month': 1, + 'two months ago': 2, + 'three months ago': 3}); + + monthlyTimescales.each(function(monthsAgo, label) { + var current = this.currentTimePeriod == label; + var value = "start=" + (new Date().decrement('month', monthsAgo).set('date', 1).set('hr', 0).set('min', 0).set('sec', 0).getTime() / 1000); + value += '&finish=' + (new Date().decrement('month', monthsAgo - 1).set('date', 1).set('hr', 0).set('min', 0).set('sec', 0).getTime() / 1000); + + var option = new Element('option', { + 'html': label, + 'value': value, + 'selected': (current ? 'selected' : ''), + }); + select.grab(option) + }); + + var liveToggler = this.liveToggler = new Element('input', { 'type': 'checkbox', 'id': this.parentElement + '-live', 'name': 'live', 'checked': this.options.live, + 'disabled': this.options.percentile95, 'events': { - 'click': function() { + 'click': function(e) { this.options.live = !this.options.live + if (this.options.live) { + // tell percentiles95Toggler to be unchecked + if (this.options.percentile95) { + this.percentile95Toggler.fireEvent('click'); + } + this.percentile95Toggler.set('disabled', true); + } else { + this.requestData.live = false; + this.percentile95Toggler.set('disabled', false); + e.target.form.fireEvent('submit', e) + } }.bind(this) }, 'styles': { 'margin-right': '4px', 'cursor': 'pointer' @@ -565,10 +644,64 @@ 'margin-right': '8px', 'cursor': 'pointer' } }); + var percentile95Toggler = this.percentile95Toggler = new Element('input', { + 'type': 'checkbox', + 'id': this.parentElement + '-percentile95', + 'name': 'percentile95', + 'checked': this.options.percentile95, + 'events': { + 'click': function() { + this.options.percentile95 = !this.options.percentile95; + if (!(this.options.percentiles)) { + this.options.percentiles = []; + } + if (this.options.percentile95) { + this.options.percentiles.push('95'); + if ((this.chart) && (this.series[0].percentile95)) { + this.drawPercentiles(this.chart); + } else { + this.getData(); + } + // tell liveToggler to be unchecked + if (this.options.live) { + this.liveToggler.fireEvent('click'); + } + this.liveToggler.set('disabled', true); + } else { + // FIXME - when adding support for 5th and 50th percentiles + // etc we'll need to switch the options.percentiles array + // to a hash for easy enabling / disabling of percentiles + this.options.percentiles = []; + if (this.chart) { + this.removePercentiles(this.chart); + } else { + this.getData(); + } + this.liveToggler.set('disabled', false); + } + }.bind(this) + }, + 'styles': { + 'margin-right': '4px', + 'cursor': 'pointer' + } + }); + + var percentile95Label = new Element('label', { + 'for': this.parentElement + '-percentile95', + 'html': '95th Percentile', + 'styles': { + 'font-family': 'sans-serif', + 'font-size': '11px', + 'margin-right': '8px', + 'cursor': 'pointer' + } + }); + var exportLink = new Element('a', { 'href': this.dataURL(), 'html': 'Export data', 'styles': { 'font-family': 'sans-serif', @@ -592,9 +725,11 @@ }.bind(this) } }); form.grab(exportLink) + form.grab(percentile95Toggler) + form.grab(percentile95Label) form.grab(liveToggler) form.grab(liveLabel) form.grab(select) container.grab(form, 'top') },