
* Introduce AJAX-based drop-downs * Introduced drop-downs for modules, companies and engineers * Introduced AJAX methods for retrieval of modules, companies, engineers, contribution and activity logs * Removed unused scripts and templates Implements blueprint improve-navigation Change-Id: Icef6433dbc2763241c99be83d5549c1e7eac4a76
460 lines
16 KiB
HTML
460 lines
16 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block head %}
|
|
|
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
|
|
|
<title>Stackalytics</title>
|
|
|
|
<link href='http://fonts.googleapis.com/css?family=PT+Sans:400,700,400italic&subset=latin,cyrillic' rel='stylesheet' type='text/css' />
|
|
<link href='http://fonts.googleapis.com/css?family=PT+Sans+Caption&subset=latin,cyrillic' rel='stylesheet' type='text/css' />
|
|
<link href='http://fonts.googleapis.com/css?family=PT+Sans+Narrow:400,700&subset=latin,cyrillic' rel='stylesheet' type='text/css' />
|
|
|
|
<link rel="icon" href="{{ url_for('static', filename='images/favicon.png') }}" type="image/png"/>
|
|
|
|
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/style.css') }}">
|
|
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/jquery.jqplot.min.css') }}">
|
|
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/jquery.dataTables.css') }}">
|
|
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/select2.css') }}">
|
|
|
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-1.9.1.min.js') }}"></script>
|
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.dataTables.min.js') }}"></script>
|
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.jqplot.min.js') }}"></script>
|
|
<!--[if lt IE 9]><script type="text/javascript" src="{{ url_for('static', filename='js/excanvas.min.js') }}"></script><![endif]-->
|
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.json2.min.js') }}"></script>
|
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.pieRenderer.min.js') }}"></script>
|
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.dateAxisRenderer.min.js') }}"></script>
|
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.canvasTextRenderer.min.js') }}"></script>
|
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.canvasAxisTickRenderer.min.js') }}"></script>
|
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.cursor.min.js') }}"></script>
|
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.highlighter.min.js') }}"></script>
|
|
<script type="text/javascript" src="{{ url_for('static', filename='js/select2.min.js') }}"></script>
|
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.tmpl.js') }}"></script>
|
|
|
|
<script type="text/javascript">
|
|
|
|
function showTimeline(data) {
|
|
var plot = $.jqplot('timeline', data, {
|
|
gridPadding: {
|
|
right: 35
|
|
},
|
|
cursor: {
|
|
show: false
|
|
},
|
|
highlighter: {
|
|
show: true,
|
|
sizeAdjust: 6
|
|
},
|
|
axes: {
|
|
xaxis: {
|
|
tickRenderer: $.jqplot.CanvasAxisTickRenderer,
|
|
tickOptions: {
|
|
fontSize: '8pt',
|
|
angle: -90,
|
|
formatString: '%b \'%y'
|
|
},
|
|
renderer: $.jqplot.DateAxisRenderer,
|
|
tickInterval: '1 month'
|
|
},
|
|
yaxis: {
|
|
min: 0,
|
|
label: ''
|
|
},
|
|
y2axis: {
|
|
min: 0,
|
|
label: ''
|
|
}
|
|
},
|
|
series: [
|
|
{
|
|
shadow: false,
|
|
fill: true,
|
|
fillColor: '#4bb2c5',
|
|
fillAlpha: 0.3
|
|
},
|
|
{
|
|
shadow: false,
|
|
fill: true,
|
|
color: '#4bb2c5',
|
|
fillColor: '#4bb2c5'
|
|
},
|
|
{
|
|
shadow: false,
|
|
lineWidth: 1.5,
|
|
showMarker: true,
|
|
markerOptions: { size: 5 },
|
|
yaxis: 'y2axis'
|
|
}
|
|
]
|
|
});
|
|
}
|
|
|
|
function timelineRenderer(options) {
|
|
$(document).ready(function () {
|
|
$.ajax({
|
|
url: make_uri("/data/timeline", options),
|
|
dataType: "json",
|
|
success: function (data) {
|
|
showTimeline(data);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function chartAndTableRenderer(url, table_id, chart_id, link_param, options) {
|
|
|
|
$(document).ready(function () {
|
|
|
|
$.ajax({
|
|
url: make_uri(url, options),
|
|
dataType: "json",
|
|
success: function (data) {
|
|
|
|
var tableData = [];
|
|
var chartData = [];
|
|
|
|
var limit = 10;
|
|
var aggregate = 0;
|
|
var index = 1;
|
|
var i;
|
|
var hasComment = false;
|
|
|
|
for (i = 0; i < data.length; i++) {
|
|
if (i < limit - 1) {
|
|
chartData.push([data[i].name, data[i].metric]);
|
|
} else {
|
|
aggregate += data[i].metric;
|
|
}
|
|
|
|
var index_label = index;
|
|
if (data[i].name == "*independent") {
|
|
index_label = "";
|
|
} else {
|
|
index++;
|
|
}
|
|
var link;
|
|
if (data[i].id) {
|
|
link = make_link(data[i].id, data[i].name, link_param);
|
|
} else {
|
|
link = data[i].name
|
|
}
|
|
var rec = {"index": index_label, "link": link, "metric": data[i].metric};
|
|
if (data[i].comment) {
|
|
rec["comment"] = data[i].comment;
|
|
hasComment = true;
|
|
}
|
|
tableData.push(rec);
|
|
}
|
|
|
|
if (i == limit) {
|
|
chartData.push([data[i-1].name, data[i-1].metric]);
|
|
} else if (i > limit) {
|
|
chartData.push(["others", aggregate]);
|
|
}
|
|
|
|
var tableColumns = [
|
|
{ "mData": "index" },
|
|
{ "mData": "link" },
|
|
{ "mData": "metric" }
|
|
];
|
|
if (hasComment) {
|
|
tableColumns.push({ "mData": "comment"})
|
|
}
|
|
|
|
$("#" + table_id).dataTable({
|
|
"aLengthMenu": [
|
|
[25, 50, -1],
|
|
[25, 50, "All"]
|
|
],
|
|
"aaSorting": [
|
|
[ 2, "desc" ]
|
|
],
|
|
"sPaginationType": "full_numbers",
|
|
"iDisplayLength": 25,
|
|
"aaData": tableData,
|
|
"aoColumns": tableColumns
|
|
});
|
|
|
|
var plot = $.jqplot(chart_id, [chartData], {
|
|
seriesDefaults: {
|
|
renderer: jQuery.jqplot.PieRenderer,
|
|
rendererOptions: {
|
|
showDataLabels: true
|
|
}
|
|
},
|
|
legend: { show: true, location: 'e' }
|
|
});
|
|
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function getUrlVars() {
|
|
var vars = {};
|
|
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) {
|
|
vars[key] = value;
|
|
});
|
|
return vars;
|
|
}
|
|
|
|
$(document).ready(function () {
|
|
$('#metric').val('{{ metric }}');
|
|
$('#release').val('{{ release }}');
|
|
$('#project_type')
|
|
.val('{{ project_type }}')
|
|
.on("change", function(e) {
|
|
$('#module').val('');
|
|
reload();
|
|
});
|
|
|
|
$("#release").select2();
|
|
$("#metric").select2();
|
|
$("#project_type").select2();
|
|
|
|
$("#company").select2({
|
|
allowClear: true,
|
|
ajax: {
|
|
url: make_uri("/data/companies.json"),
|
|
dataType: 'json',
|
|
data: function (term, page) {
|
|
return {
|
|
company_name: term
|
|
};
|
|
},
|
|
results: function (data, page) {
|
|
return {results: data["companies"]};
|
|
}
|
|
},
|
|
initSelection: function (element, callback) {
|
|
var id = $(element).val();
|
|
if (id !== "") {
|
|
$.ajax(make_uri("/data/companies/" + id + ".json"), {
|
|
dataType: "json"
|
|
}).done(function (data) {
|
|
callback(data["company"]);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
$('#company')
|
|
.on("change", function(e) { reload(); });
|
|
|
|
$("#module").select2({
|
|
allowClear: true,
|
|
ajax: {
|
|
url: make_uri("/data/modules.json"),
|
|
dataType: 'json',
|
|
data: function (term, page) {
|
|
return {
|
|
module: term
|
|
};
|
|
},
|
|
results: function (data, page) {
|
|
return {results: data["modules"]};
|
|
}
|
|
},
|
|
initSelection: function (element, callback) {
|
|
var id = $(element).val();
|
|
if (id !== "") {
|
|
$.ajax(make_uri("/data/modules/" + id + ".json"), {
|
|
dataType: "json"
|
|
}).done(function (data) {
|
|
callback(data["module"]);
|
|
});
|
|
}
|
|
} });
|
|
|
|
$('#module')
|
|
.on("change", function(e) { reload(); });
|
|
|
|
$("#user").select2({
|
|
allowClear: true,
|
|
ajax: {
|
|
url: make_uri("/data/users.json"),
|
|
dataType: 'json',
|
|
data: function (term, page) {
|
|
return {
|
|
user_name: term
|
|
};
|
|
},
|
|
results: function (data, page) {
|
|
return {results: data["users"]};
|
|
}
|
|
},
|
|
initSelection: function (element, callback) {
|
|
var id = $(element).val();
|
|
if (id !== "") {
|
|
$.ajax(make_uri("/data/users/" + id + ".json"), {
|
|
dataType: "json"
|
|
}).done(function (data) {
|
|
callback(data["user"]);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
$('#user')
|
|
.on("change", function(e) { reload(); });
|
|
});
|
|
|
|
function make_link(id, title, param_name) {
|
|
var options = {};
|
|
options[param_name] = encodeURIComponent(id).toLowerCase();
|
|
var link = make_uri("/", options);
|
|
return "<a href=\"" + link + "\">" + title + "</a>"
|
|
}
|
|
|
|
function make_uri(uri, options) {
|
|
var ops = {};
|
|
$.extend(ops, getUrlVars());
|
|
if (options != null) {
|
|
$.extend(ops, options);
|
|
}
|
|
var str = $.map(ops,function (val, index) {
|
|
return index + "=" + val;
|
|
}).join("&");
|
|
|
|
return (str == "")? uri: uri + "?" + str;
|
|
}
|
|
|
|
function make_std_options() {
|
|
var options = {};
|
|
options['release'] = getRelease();
|
|
options['metric'] = getMetric();
|
|
options['project_type'] = getProjectType();
|
|
options['module'] = $('#module').val();
|
|
options['company'] = $('#company').val();
|
|
options['user_id'] = $('#user').val();
|
|
|
|
return options;
|
|
}
|
|
|
|
function reload() {
|
|
window.location.search = $.map(make_std_options(),function (val, index) {
|
|
return index + "=" + val;
|
|
}).join("&")
|
|
}
|
|
|
|
$(document).on('change', '#metric', function (evt) {
|
|
reload();
|
|
});
|
|
|
|
$(document).on('change', '#release', function (evt) {
|
|
reload();
|
|
});
|
|
|
|
$(document).on('change', '#project_type', function (evt) {
|
|
reload();
|
|
});
|
|
|
|
function getRelease() {
|
|
return $('#release').val()
|
|
}
|
|
|
|
function getMetric() {
|
|
return $('#metric').val()
|
|
}
|
|
|
|
function getProjectType() {
|
|
return $('#project_type').val()
|
|
}
|
|
|
|
</script>
|
|
|
|
{% block scripts %}{% endblock %}
|
|
|
|
{% endblock %}
|
|
|
|
{% block body %}
|
|
|
|
<div class="page">
|
|
<div class="aheader">
|
|
<div style="float: right; margin-top: 10px; margin-right: 20px;">
|
|
<a href="https://wiki.openstack.org/wiki/Stackalytics">About ↗</a>
|
|
</div>
|
|
<div id="analytics_header">
|
|
<span id="logo"><a href="/?metric={{ metric }}&release={{ release }}&project_type={{ project_type }}">Stackalytics</a></span>
|
|
<span id="slogan">| community heartbeat</span>
|
|
</div>
|
|
|
|
<div class="drops">
|
|
|
|
<div class="drop">
|
|
<label for="release">Release</label>
|
|
<select id="release" name="release" style="min-width: 130px;" data-placeholder="Select release">
|
|
<option></option>
|
|
<option value="all">All times</option>
|
|
{% for release in release_options %}
|
|
<option value="{{ release.release_name }}">{{ release.release_name | capitalize }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="drop">
|
|
<label for="project_type">Projects</label>
|
|
<select id="project_type" name="project_type" style="min-width: 130px;">
|
|
<option value="All">all</option>
|
|
{% for option_type, option_groups in project_type_options.iteritems() %}
|
|
<optgroup label="{{ option_type }}">
|
|
<option value="{{ option_type }}">{{ option_type }}
|
|
- all
|
|
</option>
|
|
{% for option_group in option_groups %}
|
|
<option value="{{ option_group }}">{{ option_group }}</option>
|
|
{% endfor %}
|
|
</optgroup>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="drop">
|
|
<label for="module">Module</label>
|
|
<input type="hidden" id="module" style="width: 130px" data-placeholder="Any module" value="{{ module }}"/>
|
|
</div>
|
|
|
|
<div class="drop">
|
|
<label for="company">Company</label>
|
|
<input type="hidden" id="company" style="width: 130px" data-placeholder="Any company" value="{{ company }}"/>
|
|
</div>
|
|
|
|
<div class="drop">
|
|
<label for="company">Engineer</label>
|
|
<input type="hidden" id="user" style="width: 130px" data-placeholder="Any engineer" value="{{ user_id }}"/>
|
|
</div>
|
|
|
|
<div class="drop">
|
|
<label for="metric">Metric</label>
|
|
<select id="metric" name="metric" style="width: 130px">
|
|
{% for metric in metric_options %}
|
|
<option value="{{ metric[0] }}">{{ metric[1] }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="navigation">
|
|
<div id="timeline" style="width: 100%; height: 120px; margin-top: 15px;"></div>
|
|
</div>
|
|
|
|
<table style="width: 100%" cellspacing="0">
|
|
<tr>
|
|
<td style="width: 50%; vertical-align: top;">
|
|
<div class="body" style="margin-right: 2em;">
|
|
{% block left_frame %}{% endblock %}
|
|
</div>
|
|
</td>
|
|
<td style="width: 50%; vertical-align: top;">
|
|
<div class="body" style="margin-left: 2em;">
|
|
{% block right_frame %}{% endblock %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
</div>
|
|
{% endblock %}
|