From c550d330e172dfacadc15dfe0416a5c2e564efbf Mon Sep 17 00:00:00 2001 From: Imre Farkas Date: Wed, 3 Jul 2013 13:50:36 +0200 Subject: [PATCH] Add interval selector to modal line charts Change-Id: I6b22d120989bbb159bdfba3767dc7d88a1d491f8 --- .../horizon/js/horizon.d3modallinechart.js | 122 +++++++++++------- .../horizon/client_side/_modal_chart.html | 7 + .../resource_management/racks/views.py | 24 +++- .../infrastructure/less/infrastructure.less | 24 ++++ 4 files changed, 126 insertions(+), 51 deletions(-) diff --git a/horizon/static/horizon/js/horizon.d3modallinechart.js b/horizon/static/horizon/js/horizon.d3modallinechart.js index bf976c43b..a4015f517 100644 --- a/horizon/static/horizon/js/horizon.d3modallinechart.js +++ b/horizon/static/horizon/js/horizon.d3modallinechart.js @@ -12,7 +12,6 @@ horizon.d3_modal_line_chart = { init: function() { var self = this; - // Load modals for modal chart links $(document).on('click', '.modal_chart', function (evt) { evt.preventDefault(); var $this = $(this); @@ -23,50 +22,65 @@ horizon.d3_modal_line_chart = { var modal = $('.modal:last'); modal.modal(); + $(modal).on('click', 'ul#interval_selector li a', function(event){ + event.preventDefault(); + + interval = $(event.target).data("interval"); + self.draw('/infrastructure/resource_management/racks/usage_data', {interval: interval}); + + $("ul#interval_selector li").removeClass("active"); + $(event.target).parent().addClass("active"); + }) + + self.init_svg(); self.draw('/infrastructure/resource_management/racks/usage_data') }); }, - draw: function(url) { + init_svg: function() { var self = this; - var margin = {top: 20, right: 80, bottom: 30, left: 50}; - var width = 700 - margin.left - margin.right; - var height = 400 - margin.top - margin.bottom; + self.margin = {top: 20, right: 80, bottom: 30, left: 50}; + self.width = 700 - self.margin.left - self.margin.right; + self.height = 400 - self.margin.top - self.margin.bottom; - var svg = d3.select("#modal_chart") - .append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - .append("g") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + self.svg = d3.select("#modal_chart") + .append("svg") + .attr("width", self.width + self.margin.left + self.margin.right) + .attr("height", self.height + self.margin.top + self.margin.bottom) + .append("g") + .attr("transform", "translate(" + self.margin.left + "," + self.margin.top + ")"); - var x = d3.time.scale().range([0, width]); - var y = d3.scale.linear().range([height, 0]); + self.x = d3.time.scale().range([0, self.width]); + self.y = d3.scale.linear().range([self.height, 0]); - var xAxis = d3.svg.axis() - .scale(x) - .orient("bottom"); - var yAxis = d3.svg.axis() - .scale(y) - .orient("left"); + self.xAxis = d3.svg.axis() + .scale(self.x) + .orient("bottom").ticks(6); + self.yAxis = d3.svg.axis() + .scale(self.y) + .orient("left"); - var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%L").parse; + self.parse_date = d3.time.format("%Y-%m-%dT%H:%M:%S.%L").parse; - var line = d3.svg.line().interpolate("linear") - .x(function(d) { return x(d.date); }) - .y(function(d) { return y(d.value); }); + self.line = d3.svg.line().interpolate("linear") + .x(function(d) { return self.x(d.date); }) + .y(function(d) { return self.y(d.value); }); - var color = d3.scale.category10(); + self.color = d3.scale.category10(); + }, - d3.json(url, function(error, data) { - data.forEach(function(d) { - d.date = parseDate(d.date); - }); + draw: function(url, url_options) { + var self = this; + var url_options = url_options || {}; + url_options.interval = url_options.interval || "1w"; - color.domain(d3.keys(data[0]).filter(function(key) { return key !== 'date'; })); + d3.json(self.data_url(url, url_options), function(error, data) { + data.forEach(function(d) { d.date = self.parse_date(d.date); }); - var usage_values = color.domain().map(function(name) { + self.color.domain(d3.keys(data[0]).filter(function(key) { return key !== 'date'; })); + + var usage_values = self.color.domain().map(function(name) { return { name: name, values: data.map(function(d) { @@ -75,32 +89,42 @@ horizon.d3_modal_line_chart = { }; }); - x.domain(d3.extent(data, function(d) { return d.date; })); - y.domain([ - d3.min(usage_values, function(u) { return d3.min(u.values, function(v) { return v.value; }) - 1; }), - d3.max(usage_values, function(u) { return d3.max(u.values, function(v) { return v.value; }) + 6; }) - ]); + self.svg.selectAll(".axis").remove(); + self.x.domain(d3.extent(data, function(d) { return d.date; })); + self.y.domain([0, 15]); - svg.append("g") - .attr("transform", "translate(0," + height + ")") - .attr("class", "axis") - .call(xAxis); - svg.append("g") - .attr("class", "axis") - .call(yAxis) + self.svg.append("g") + .attr("transform", "translate(0," + self.height + ")") + .attr("class", "axis") + .call(self.xAxis); + self.svg.append("g") + .attr("class", "axis") + .call(self.yAxis) - var usage = svg.selectAll(".usage") - .data(usage_values) - .enter().append("g"); + var usages = self.svg.selectAll(".usage") + .data(usage_values, function(d) { return self.key(d) }); + + var usage = usages.enter().append("g") + .attr("class", "usage"); usage.append("path") - .attr("d", function(d) { return line(d.values); }) - .style("stroke", function(d) { return color(d.name); }) - .style("fill", "none") - .style("stroke-width", 3); + .attr("d", function(d) { return self.line(d.values); }) + .style("stroke", function(d) { return self.color(d.name); }) + .style("fill", "none") + .style("stroke-width", 3); + + usages.exit().remove(); }); }, + data_url: function(url, options) { + return url + '?' + $.param(options); + }, + + key: function(data){ + return data.name + data.values.length + data.values[0].date + data.values[data.values.length-1].date + }, + }; horizon.addInitFunction(function () { diff --git a/horizon/templates/horizon/client_side/_modal_chart.html b/horizon/templates/horizon/client_side/_modal_chart.html index cf1ea8820..53b9fb4c4 100644 --- a/horizon/templates/horizon/client_side/_modal_chart.html +++ b/horizon/templates/horizon/client_side/_modal_chart.html @@ -6,6 +6,13 @@ {% block template %} {% jstemplate %} {% endjstemplate %} diff --git a/openstack_dashboard/dashboards/infrastructure/resource_management/racks/views.py b/openstack_dashboard/dashboards/infrastructure/resource_management/racks/views.py index 32ef6ce25..a835cbad2 100644 --- a/openstack_dashboard/dashboards/infrastructure/resource_management/racks/views.py +++ b/openstack_dashboard/dashboards/infrastructure/resource_management/racks/views.py @@ -108,9 +108,29 @@ class DetailView(tabs.TabView): class UsageDataView(View): def get(self, request, *args, **kwargs): + interval = request.GET.get('interval', '1w') + + if interval == '12h': + data_count = 12 + timedelta_param = 'hours' + elif interval == '24h': + data_count = 24 + timedelta_param = 'hours' + elif interval == '1m': + data_count = 30 + timedelta_param = 'days' + elif interval == '1y': + data_count = 52 + timedelta_param = 'weeks' + else: + # default is 1 week + data_count = 7 + timedelta_param = 'days' + values = [] - for i in range(10): - values.append({'date': datetime.now() + timedelta(days=i), + for i in range(data_count): + timediff = timedelta(**{timedelta_param: i}) + values.append({'date': datetime.now() - timediff, 'ram': random.randint(1, 9)}) return HttpResponse(json.dumps(values, cls=DjangoJSONEncoder), diff --git a/openstack_dashboard/dashboards/infrastructure/static/infrastructure/less/infrastructure.less b/openstack_dashboard/dashboards/infrastructure/static/infrastructure/less/infrastructure.less index c398fbb62..723f0134c 100644 --- a/openstack_dashboard/dashboards/infrastructure/static/infrastructure/less/infrastructure.less +++ b/openstack_dashboard/dashboards/infrastructure/static/infrastructure/less/infrastructure.less @@ -19,6 +19,30 @@ table.capacities{ } +#interval_selector { + position: absolute; + right: 15px; + top: 15px; + + li { + display: inline; + list-style-type: none; + padding-right: 5px; + + &.active { + font-weight: bold; + + a { + color: black; + + &:hover { + text-decoration:none; + } + } + } + } +} + svg { .axis { path, line {