Adds d3.js library and reworked quota infographics
The d3.js library is now added and the quota infographics have been redone to use d3. Added a reusable animated d3 pie chart that is now used to display the quota infographics. Reworked the progress bars (horizon.quota.js) so that project quotas when creating a new Instance / Volume display a progress bar in D3. Also changed progress bar for adding a floating ip so that it shows that 1 will be used up on create just like adding a new volume. Change-Id: I2930c3c80736e50d3f38c001aac1918e1932be5e Implements: blueprint d3
This commit is contained in:
parent
09fc3f1fbb
commit
ccb11b7099
91
horizon/static/horizon/js/horizon.d3piechart.js
Normal file
91
horizon/static/horizon/js/horizon.d3piechart.js
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
Draw pie chart in d3.
|
||||
|
||||
To use, a div is required with the class .d3_pie_chart
|
||||
and a data-used attribute in the div
|
||||
that stores the percentage to fill the chart
|
||||
|
||||
Example:
|
||||
<div class="d3_pie_chart"
|
||||
data-used="{% widthratio current_val max_val 100 %}">
|
||||
</div>
|
||||
*/
|
||||
|
||||
horizon.d3_pie_chart = {
|
||||
w: 100,
|
||||
h: 100,
|
||||
r: 45,
|
||||
bkgrnd: "#F2F2F2",
|
||||
frgrnd: "#4790B2",
|
||||
init: function() {
|
||||
var self = this;
|
||||
|
||||
// Pie Charts
|
||||
var pie_chart_data = $(".d3_pie_chart");
|
||||
self.chart = d3.selectAll(".d3_pie_chart");
|
||||
|
||||
for (var i = 0; i < pie_chart_data.length; i++) {
|
||||
used = parseInt(pie_chart_data[i].dataset.used);
|
||||
self.data = [{"percentage":used}, {"percentage":100 - used}];
|
||||
self.pieChart(i);
|
||||
}
|
||||
},
|
||||
// Draw a pie chart
|
||||
pieChart: function(i) {
|
||||
var self = this;
|
||||
var vis = d3.select(self.chart[0][i]).append("svg:svg")
|
||||
.attr("class", "chart")
|
||||
.attr("width", self.w)
|
||||
.attr("height", self.h)
|
||||
.style("background-color", "white")
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + (self.r + 2) + "," + (self.r + 2) + ")")
|
||||
|
||||
var arc = d3.svg.arc()
|
||||
.outerRadius(self.r)
|
||||
.innerRadius(0)
|
||||
|
||||
var pie = d3.layout.pie()
|
||||
.sort(null)
|
||||
.value(function(d){ return d.percentage; })
|
||||
|
||||
// Draw an empty pie chart
|
||||
var piechart = vis.selectAll(".arc")
|
||||
.data(pie([{"percentage":10}]))
|
||||
.enter()
|
||||
.append("path")
|
||||
.attr("class","arc")
|
||||
.attr("d", arc)
|
||||
.style("fill", self.frgrnd)
|
||||
.style("stroke", "#CCCCCC")
|
||||
.style("stroke-width", 1)
|
||||
.each(function(d) {return self.current = d;})
|
||||
|
||||
// Animate filling the pie chart
|
||||
animate = function(data) {
|
||||
var piechart = vis.selectAll(".arc")
|
||||
.data(pie(data))
|
||||
.enter()
|
||||
.append("path")
|
||||
.attr("class","arc")
|
||||
.attr("d", arc)
|
||||
.style("fill", self.bkgrnd)
|
||||
.style("stroke", "#CCCCCC")
|
||||
.style("stroke-width", 1)
|
||||
.each(function(d) {return self.current = d;})
|
||||
.transition()
|
||||
.duration(500)
|
||||
.attrTween("d", function(a) {
|
||||
var tween = d3.interpolate(self.current, a);
|
||||
self.current = tween(0);
|
||||
return function(t) { return arc(tween(t)); }
|
||||
})
|
||||
}
|
||||
|
||||
animate(self.data)
|
||||
}
|
||||
}
|
||||
|
||||
horizon.addInitFunction(function () {
|
||||
horizon.d3_pie_chart.init();
|
||||
});
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
Used for animating and displaying quota information on forms which use the
|
||||
Bootstrap progress bars. Also used for displaying flavor details on modal-
|
||||
Used for animating and displaying quota information on forms using
|
||||
D3js progress bars. Also used for displaying flavor details on modal-
|
||||
dialogs.
|
||||
|
||||
Usage:
|
||||
@ -8,7 +8,6 @@
|
||||
DOM structure like this in your Django template:
|
||||
|
||||
<div id="your_progress_bar_id" class="quota_bar">
|
||||
{% horizon_progress_bar total_number_used max_number_allowed %}
|
||||
</div>
|
||||
|
||||
With this progress bar, you then need to add some data- HTML attributes
|
||||
@ -67,6 +66,11 @@ horizon.Quota = {
|
||||
return ('#' + $(elm).attr('data-progress-indicator-for'));
|
||||
}));
|
||||
|
||||
// Draw the initial progress bars
|
||||
this._initialCreation(this.user_value_progress_bars)
|
||||
this._initialCreation(this.auto_value_progress_bars)
|
||||
this._initialCreation(this.flavor_progress_bars)
|
||||
|
||||
this._initialAnimations();
|
||||
this._attachInputHandlers();
|
||||
},
|
||||
@ -126,23 +130,6 @@ horizon.Quota = {
|
||||
}
|
||||
},
|
||||
|
||||
// Updates a progress bar, taking care of exceeding quota display as well.
|
||||
update: function(element, percentage_used, percentage_to_update) {
|
||||
var update_width = percentage_to_update;
|
||||
|
||||
if(percentage_to_update + percentage_used > 100) {
|
||||
update_width = 100 - percentage_used;
|
||||
|
||||
if(!element.hasClass('progress_bar_over')) {
|
||||
element.addClass('progress_bar_over');
|
||||
}
|
||||
} else {
|
||||
element.removeClass('progress_bar_over');
|
||||
}
|
||||
|
||||
element.animate({width: parseInt(update_width, 10) + "%"}, 300);
|
||||
},
|
||||
|
||||
/*
|
||||
When a new flavor is selected, this takes care of updating the relevant
|
||||
progress bars associated with the flavor quota usage.
|
||||
@ -176,13 +163,80 @@ horizon.Quota = {
|
||||
updateUsageFor: function(progress_element, increment_by) {
|
||||
progress_element = $(progress_element);
|
||||
|
||||
var update_indicator = progress_element.find('.progress_bar_selected');
|
||||
//var update_indicator = progress_element.find('.progress_bar_selected');
|
||||
var quota_limit = parseInt(progress_element.attr('data-quota-limit'), 10);
|
||||
var quota_used = parseInt(progress_element.attr('data-quota-used'), 10);
|
||||
var percentage_to_update = ((increment_by / quota_limit) * 100);
|
||||
var percentage_used = ((quota_used / quota_limit) * 100);
|
||||
|
||||
this.update(update_indicator, percentage_used, percentage_to_update);
|
||||
this.update($(progress_element).attr('id'), percentage_to_update);
|
||||
},
|
||||
|
||||
// Create a new d3 bar and populate it with the current amount used
|
||||
drawUsed: function(element, used) {
|
||||
var w= "100%";
|
||||
var h= 20;
|
||||
var lvl_curve= 4;
|
||||
var bkgrnd= "#F2F2F2";
|
||||
var frgrnd= "grey";
|
||||
|
||||
// Horizontal Bars
|
||||
var bar = d3.select("#"+element).append("svg:svg")
|
||||
.attr("class", "chart")
|
||||
.attr("width", w)
|
||||
.attr("height", h)
|
||||
.style("background-color", "white")
|
||||
.append("g")
|
||||
|
||||
// background - unused resources
|
||||
bar.append("rect")
|
||||
.attr("y", 0)
|
||||
.attr("width", w)
|
||||
.attr("height", h)
|
||||
.attr("rx", lvl_curve)
|
||||
.attr("ry", lvl_curve)
|
||||
.style("fill", bkgrnd)
|
||||
.style("stroke", "#CCCCCC")
|
||||
.style("stroke-width", 1)
|
||||
|
||||
// new resources
|
||||
bar.append("rect")
|
||||
.attr("y",0)
|
||||
.attr("class", "newbar")
|
||||
.attr("width", 0)
|
||||
.attr("height", h)
|
||||
.attr("rx", lvl_curve)
|
||||
.attr("ry", lvl_curve)
|
||||
.style("fill", "lightgreen")
|
||||
|
||||
// used resources
|
||||
var used_bar = bar.insert("rect")
|
||||
.attr("class", "usedbar")
|
||||
.attr("y", 0)
|
||||
.attr("id", "test")
|
||||
.attr("width", 0)
|
||||
.attr("height", h)
|
||||
.attr("rx", lvl_curve)
|
||||
.attr("ry", lvl_curve)
|
||||
.style("fill", frgrnd)
|
||||
.attr("d", used)
|
||||
.transition()
|
||||
.duration(500)
|
||||
.attr("width", used + "%")
|
||||
},
|
||||
|
||||
// Update the progress Bar
|
||||
update: function(element, value) {
|
||||
var already_used = parseInt(d3.select("#"+element).select(".usedbar").attr("d"))
|
||||
d3.select("#"+element).select(".newbar")
|
||||
.transition()
|
||||
.duration(500)
|
||||
.attr("width", (value + already_used) + "%")
|
||||
.style("fill", function() {
|
||||
if (value > (100 - already_used)) { return "red" }
|
||||
else {return "lightgreen" }
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/*
|
||||
@ -242,5 +296,25 @@ horizon.Quota = {
|
||||
|
||||
scope.updateUsageFor(auto_progress, update_amount);
|
||||
});
|
||||
},
|
||||
|
||||
// Draw the initial d3 bars
|
||||
_initialCreation: function(bars) {
|
||||
// Draw the initial progress bars
|
||||
var scope = this;
|
||||
$(bars).each(function(index, element) {
|
||||
var progress_element = $(element);
|
||||
|
||||
var quota_limit = parseInt(progress_element.attr('data-quota-limit'), 10);
|
||||
var quota_used = parseInt(progress_element.attr('data-quota-used'), 10);
|
||||
|
||||
if (!isNaN(quota_limit) && !isNaN(quota_used)) {
|
||||
var percentage_used = ((quota_used / quota_limit) * 100);
|
||||
} else { // If NaN percentage_used is 0
|
||||
var percentage_used = 0;
|
||||
}
|
||||
|
||||
scope.drawUsed($(element).attr('id'), percentage_used);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
5
horizon/static/horizon/lib/d3.v3.min.js
vendored
Normal file
5
horizon/static/horizon/lib/d3.v3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -16,6 +16,8 @@
|
||||
<script src="{{ STATIC_URL }}horizon/lib/underscore/underscore-min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="{{ STATIC_URL }}horizon/lib/jquery/jquery-ui-1.9.2.custom.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="{{ STATIC_URL }}horizon/lib/d3.v3.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="{{ STATIC_URL }}bootstrap/js/bootstrap.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="{{ STATIC_URL }}horizon/lib/hogan-2.0.0.js" type="text/javascript" charset='utf-8'></script>
|
||||
@ -34,6 +36,7 @@
|
||||
<script src='{{ STATIC_URL }}horizon/js/horizon.utils.js' type='text/javascript' charset='utf-8'></script>
|
||||
<script src='{{ STATIC_URL }}horizon/js/horizon.projects.js' type='text/javascript' charset='utf-8'></script>
|
||||
<script src='{{ STATIC_URL }}horizon/js/horizon.networktopology.js' type='text/javascript' charset='utf-8'></script>
|
||||
<script src='{{ STATIC_URL }}horizon/js/horizon.d3piechart.js' type='text/javascript' charset='utf-8'></script>
|
||||
{% endcompress %}
|
||||
|
||||
{% comment %} Client-side Templates (These should *not* be inside the "compress" tag.) {% endcomment %}
|
||||
|
@ -1,4 +0,0 @@
|
||||
<div class="progress_bar progress">
|
||||
<div class="progress_bar_fill bar progress-warning" data-width="{% widthratio current_val max_val 100 %}" style="width: {% widthratio current_val max_val 100 %}%"></div>
|
||||
<div class="progress_bar_selected bar progress-success"></div>
|
||||
</div>
|
@ -2,20 +2,29 @@
|
||||
|
||||
<div class="quota-dynamic">
|
||||
<h3>{% trans "Quota Summary" %}</h3>
|
||||
<strong>{% trans "Used" %}<span> {{ usage.quotas.instances.used|intcomma }} </span> {% trans "of" %} <span> {{ usage.quotas.instances.quota|intcomma }} </span>{% trans "Available Instances" %} </strong>
|
||||
{% horizon_progress_bar usage.quotas.instances.used usage.quotas.instances.quota %}
|
||||
<div class="d3_quota_bar">
|
||||
<div class="d3_pie_chart" data-used="{% widthratio usage.quotas.instances.used usage.quotas.instances.quota 100 %}"></div>
|
||||
<strong>{% trans "Available Instances" %} <br /> {% trans "Used" %}<span> {{ usage.quotas.instances.used|intcomma }} </span> {% trans "of" %} <span> {{ usage.quotas.instances.quota|intcomma }} </span></strong>
|
||||
</div>
|
||||
|
||||
<strong>{% trans "Used" %} <span> {{ usage.quotas.cores.used|intcomma }} </span>{% trans "of" %}<span> {{ usage.quotas.cores.quota|intcomma }} </span>{% trans "Available vCPUs" %} </strong>
|
||||
{% horizon_progress_bar usage.quotas.cores.used usage.quotas.cores.quota %}
|
||||
<div class="d3_quota_bar">
|
||||
<div class="d3_pie_chart" data-used="{% widthratio usage.quotas.cores.used usage.quotas.cores.quota 100 %}"></div>
|
||||
<strong>{% trans "Available VCPUs" %} <br /> {% trans "Used" %} <span> {{ usage.quotas.cores.used|intcomma }} </span>{% trans "of" %}<span> {{ usage.quotas.cores.quota|intcomma }} </span></strong>
|
||||
</div>
|
||||
|
||||
<strong>{% trans "Used" %} <span> {{ usage.quotas.ram.used|intcomma }} MB </span>{% trans "of" %}<span> {{ usage.quotas.ram.quota|intcomma }} MB </span>{% trans "Available RAM" %} </strong>
|
||||
{% horizon_progress_bar usage.quotas.ram.used usage.quotas.ram.quota %}
|
||||
<div class="d3_quota_bar">
|
||||
<div class="d3_pie_chart" data-used="{% widthratio usage.quotas.ram.used usage.quotas.ram.quota 100 %}"></div>
|
||||
<strong>{% trans "Available RAM" %} <br /> {% trans "Used" %} <span> {{ usage.quotas.ram.used|intcomma }} MB </span>{% trans "of" %}<span> {{ usage.quotas.ram.quota|intcomma }} MB </span></strong>
|
||||
</div>
|
||||
|
||||
{% if usage.quotas.volumes %}
|
||||
<strong>{% trans "Used" %} <span> {{ usage.quotas.volumes.used|intcomma }} </span>{% trans "of" %}<span> {{ usage.quotas.volumes.quota|intcomma }} </span>{% trans "Available volumes" %} </strong>
|
||||
{% horizon_progress_bar usage.quotas.volumes.used usage.quotas.volumes.quota %}
|
||||
|
||||
<strong>{% trans "Used" %} <span> {{ usage.quotas.gigabytes.used|intcomma }} GB </span>{% trans "of" %}<span> {{ usage.quotas.gigabytes.quota|intcomma }} GB </span>{% trans "Available volume storage" %} </strong>
|
||||
{% horizon_progress_bar usage.quotas.gigabytes.used usage.quotas.gigabytes.quota %}
|
||||
<div class="d3_quota_bar">
|
||||
<div class="d3_pie_chart" data-used="{% widthratio usage.quotas.volumes.used usage.quotas.volumes.quota 100 %}"></div>
|
||||
<strong>{% trans "Available Volumes" %} <br /> {% trans "Used" %} <span> {{ usage.quotas.volumes.used|intcomma }} </span>{% trans "of" %}<span> {{ usage.quotas.volumes.quota|intcomma }} </span></strong>
|
||||
</div>
|
||||
<div class="d3_quota_bar">
|
||||
<div class="d3_pie_chart" data-used="{% widthratio usage.quotas.gigabytes.used usage.quotas.gigabytes.quota 100 %}"></div>
|
||||
<strong>{% trans "Available Volume Storage" %} <br /> {% trans "Used" %} <span> {{ usage.quotas.gigabytes.used|intcomma }} GB </span>{% trans "of" %}<span> {{ usage.quotas.gigabytes.quota|intcomma }} GB </span></strong>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -85,24 +85,6 @@ def horizon_dashboard_nav(context):
|
||||
'request': context['request']}
|
||||
|
||||
|
||||
@register.inclusion_tag('horizon/common/_progress_bar.html')
|
||||
def horizon_progress_bar(current_val, max_val):
|
||||
""" Renders a progress bar based on parameters passed to the tag. The first
|
||||
parameter is the current value and the second is the max value.
|
||||
|
||||
Example: ``{% progress_bar 25 50 %}``
|
||||
|
||||
This will generate a half-full progress bar.
|
||||
|
||||
The rendered progress bar will fill the area of its container. To constrain
|
||||
the rendered size of the bar provide a container with appropriate width and
|
||||
height styles.
|
||||
|
||||
"""
|
||||
return {'current_val': current_val,
|
||||
'max_val': max_val}
|
||||
|
||||
|
||||
@register.filter
|
||||
def quota(val, units=None):
|
||||
if val == float("inf"):
|
||||
|
@ -14,7 +14,7 @@
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="right quota-dynamic">
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Allocate a floating IP from a given floating ip pool." %}</p>
|
||||
|
||||
@ -24,8 +24,18 @@
|
||||
<p>{{ usages.floating_ips.available|quota }}</p>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
<div class="quota_bar">{% horizon_progress_bar usages.floating_ips.used usages.floating_ips.quota %}</div>
|
||||
<div id="floating_ip_progress" class="quota_bar" data-quota-used="{{ usages.floating_ips.used }}" data-quota-limit="{{ usages.floating_ips.quota }}" data-progress-indicator-step-by="1"></div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
if(typeof horizon.Quota !== 'undefined') {
|
||||
horizon.Quota.init();
|
||||
} else {
|
||||
addHorizonLoadEvent(function() {
|
||||
horizon.Quota.init();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
|
@ -22,7 +22,6 @@
|
||||
<p>{{ usages.instances.available|quota|intcomma }}</p>
|
||||
</div>
|
||||
<div id="quota_instances" class="quota_bar" data-progress-indicator-flavor data-quota-limit="{{ usages.instances.quota }}" data-quota-used="{{ usages.instances.used }}">
|
||||
{% horizon_progress_bar usages.instances.used usages.instances.quota %}
|
||||
</div>
|
||||
|
||||
<div class="quota_title clearfix">
|
||||
@ -30,7 +29,6 @@
|
||||
<p>{{ usages.cores.available|quota|intcomma }}</p>
|
||||
</div>
|
||||
<div id="quota_vcpus" class="quota_bar" data-progress-indicator-flavor data-quota-limit="{{ usages.cores.quota }}" data-quota-used="{{ usages.cores.used }}">
|
||||
{% horizon_progress_bar usages.cores.used usages.cores.quota %}
|
||||
</div>
|
||||
|
||||
<div class="quota_title clearfix">
|
||||
@ -38,7 +36,6 @@
|
||||
<p>{{ usages.ram.available|quota:"MB"|intcomma }}</p>
|
||||
</div>
|
||||
<div id="quota_ram" data-progress-indicator-flavor data-quota-limit="{{ usages.ram.quota }}" data-quota-used="{{ usages.ram.used }}" class="quota_bar">
|
||||
{% horizon_progress_bar usages.ram.used usages.ram.quota %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -28,7 +28,6 @@
|
||||
</div>
|
||||
|
||||
<div id="quota_size" data-progress-indicator-for="id_size" data-quota-limit="{{ usages.gigabytes.quota }}" data-quota-used="{{ usages.gigabytes.used }}" class="quota_bar">
|
||||
{% horizon_progress_bar usages.gigabytes.used usages.gigabytes.quota %}
|
||||
</div>
|
||||
|
||||
<div class="quota_title clearfix">
|
||||
@ -37,7 +36,6 @@
|
||||
</div>
|
||||
|
||||
<div id="quota_volumes" data-progress-indicator-step-by="1" data-quota-limit="{{ usages.volumes.quota }}" data-quota-used="{{ usages.volumes.used }}" class="quota_bar">
|
||||
{% horizon_progress_bar usages.volumes.used usages.volumes.quota %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1196,6 +1196,19 @@ iframe {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.d3_quota_bar {
|
||||
width: 20%;
|
||||
margin-bottom: 8px;
|
||||
margin-top: 10px;
|
||||
float: left;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.quota-dynamic {
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.quota_title {
|
||||
color: #999;
|
||||
padding-bottom: 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user