More stats goodies
- upgraded Highcharts - js-hint run on code - more documentation - new two-axis overview chart
This commit is contained in:
Родитель
b5e5c867a2
Коммит
d21cb9955a
|
@ -1,55 +1,40 @@
|
|||
{% extends "stats/stats.html" %}
|
||||
|
||||
{% block chart_menu %}
|
||||
<ul id='series-select'>
|
||||
<li class="selected">
|
||||
<a href="#"
|
||||
data-report='downloads'
|
||||
data-series='count'>
|
||||
{{_('Downloads')}}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"
|
||||
data-report='usage'
|
||||
data-series='count'>
|
||||
{{_('Daily Users')}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
{% block stats %}
|
||||
<div class="featured">
|
||||
<div class="featured-inner two-up">
|
||||
<div><a href="downloads/">{{ _('{0} Downloads')|f(addon.total_downloads|numberfmt) }}</a><br/><small>{{ _('{0} in last 30 days')|f('<span id="sum_downloads_range"></span>')|safe }}</small></div>
|
||||
<div><div><a href="usage/">{{ _('{0} Daily Users')|f(addon.average_daily_users|numberfmt) }}</a><br/><small>{{ _('{0} in last 30 days')|f('<span id="sum_usage_range"></span>')|safe }}</small></div></div>
|
||||
<section class="island two-up c">
|
||||
<div>
|
||||
<a href="downloads/">{{ _('{0} Downloads')|f(addon.total_downloads|numberfmt) }}</a>
|
||||
<small id="downloads-in-range"></small>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<a href="usage/">{{ _('{0} Average Daily Users')|f(addon.average_daily_users|numberfmt) }}</a>
|
||||
<small id="users-in-range"></small>
|
||||
</div>
|
||||
</section>
|
||||
<div class="toplists">
|
||||
<div class="toplist loading">
|
||||
<div class="statbox">
|
||||
<h3>{{ _('Most Used Applications') }}</h3>
|
||||
<div class="toplist">
|
||||
<div class="island statbox">
|
||||
<h2>{{ _('Most Used Applications') }}</h3>
|
||||
<div class="piechart"></div>
|
||||
<table data-report="apps" data-field="applications">
|
||||
<table data-metric="apps">
|
||||
</table>
|
||||
<a href="apps/">{{ _('See more applications…') }}</a>
|
||||
<a href="applications/">{{ _('See more applications…') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toplist loading">
|
||||
<div class="statbox">
|
||||
<h3>{{ _('Most Used Languages') }}</h3>
|
||||
<div class="toplist">
|
||||
<div class="island statbox">
|
||||
<h2>{{ _('Most Used Languages') }}</h3>
|
||||
<div class="piechart"></div>
|
||||
<table data-report="locales" data-field="locales">
|
||||
<table data-metric="locales">
|
||||
</table>
|
||||
<a href="locales/">{{ _('See more languages…') }}</a>
|
||||
<a href="languages/">{{ _('See more languages…') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toplist loading">
|
||||
<div class='statbox'>
|
||||
<h3>{{ _('Most Used OSes') }}</h3>
|
||||
<div class="toplist">
|
||||
<div class='island statbox'>
|
||||
<h2>{{ _('Most Used OSes') }}</h3>
|
||||
<div class="piechart"></div>
|
||||
<table data-report="os" data-field="oses">
|
||||
<table data-metric="os">
|
||||
</table>
|
||||
<a href="os/">{{ _('See more operating systems…') }}</a>
|
||||
</div>
|
||||
|
|
|
@ -84,9 +84,8 @@
|
|||
data-end_date="{{ view.end }}"
|
||||
{% endif %}
|
||||
data-base_url="{{ stats_base_url }}">
|
||||
<div class="island chart c">
|
||||
<div id="head-chart" style="background:#fff;height:384px">
|
||||
</div>
|
||||
<div class="island chart">
|
||||
<div id="head-chart"></div>
|
||||
</div>
|
||||
{% block stats %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
@import 'lib';
|
||||
|
||||
/* Clearfix! */
|
||||
.statistics #page {
|
||||
max-width: 1280px;
|
||||
width: auto;
|
||||
min-width: 1024px;
|
||||
padding-bottom: 500px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.statbox:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
clear: both;
|
||||
height: 0;
|
||||
visibility: hidden;
|
||||
.statistics {
|
||||
#page {
|
||||
max-width: 1280px;
|
||||
width: auto;
|
||||
min-width: 1024px;
|
||||
padding-bottom: 500px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
.island {
|
||||
float: none;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.statbox .pagination {
|
||||
|
@ -61,16 +59,6 @@ table {
|
|||
* common styles
|
||||
**/
|
||||
|
||||
.statbox .listing-header {
|
||||
-moz-border-radius:0 0 4px 4px;
|
||||
background-color:#F0F8FC;
|
||||
background-image: -moz-linear-gradient(#daf0f6,#fdfefe);
|
||||
border:0;
|
||||
border-top:1px solid #A5BFCE;
|
||||
line-height:2.5em;
|
||||
overflow:hidden;
|
||||
padding:0.1em 0 0.1em 0.25em;
|
||||
}
|
||||
|
||||
table tbody tr {
|
||||
border-top: 1px dotted #B5D9E5;
|
||||
|
@ -80,14 +68,6 @@ table tbody tr {
|
|||
background: -moz-linear-gradient(#ffffff, #eff8fb);
|
||||
}
|
||||
|
||||
div.statbox {
|
||||
border: 1px solid #C9E8F3;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
.island {
|
||||
float: none;
|
||||
|
@ -102,7 +82,7 @@ div.statbox {
|
|||
* Rules for date criteria selection
|
||||
**/
|
||||
|
||||
.criteria {
|
||||
.island.criteria {
|
||||
padding: 0 0 0 12px;
|
||||
margin: 0 0 0 1em;
|
||||
z-index: 1000;
|
||||
|
@ -133,32 +113,31 @@ div.statbox {
|
|||
* Three-up stats
|
||||
**/
|
||||
|
||||
.two-up > div {
|
||||
float: left;
|
||||
width: 50%;
|
||||
margin: 10px 0;
|
||||
text-align:center;
|
||||
}
|
||||
.two-up > div > div {
|
||||
border-left: 1px dotted #aaa;
|
||||
}
|
||||
.two-up div a {
|
||||
font-weight: bold;
|
||||
font-size: 140%;
|
||||
}
|
||||
.two-up div small {
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
.listing-header ul.chart_legend {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.listing-header ul.chart_legend li {
|
||||
margin: 0 .5em;
|
||||
line-height: 2.5em;
|
||||
display: block;
|
||||
float: right;
|
||||
.two-up {
|
||||
div {
|
||||
float: left;
|
||||
text-align: center;
|
||||
width: 50%;
|
||||
height: 48px;
|
||||
position: relative;
|
||||
&:first-child:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border-left: 1px dotted @medium-gray;
|
||||
height: 100%;
|
||||
}
|
||||
a {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
font-size: 140%;
|
||||
}
|
||||
small {
|
||||
font-size: 110%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -413,10 +392,13 @@ table.stats-aggregate tbody span.change.minus {
|
|||
width: 648px;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
#head-chart {
|
||||
border-radius: 1em;
|
||||
position: relative;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
height: 384px;
|
||||
}
|
||||
|
||||
.loadmessage {
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
"jquery" : true,
|
||||
|
||||
"predef" : [ // AMO's globals
|
||||
"z"
|
||||
"z",
|
||||
"_",
|
||||
"Highcharts"
|
||||
]
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
(function () {
|
||||
// "use strict";
|
||||
var $win = $(window),
|
||||
$chart = $('#head-chart');
|
||||
baseConfig = {
|
||||
chart: {
|
||||
renderTo: 'head-chart',
|
||||
zoomType: 'x',
|
||||
zoomType: 'x'
|
||||
},
|
||||
credits: { enabled: false },
|
||||
title: {
|
||||
|
@ -69,6 +71,10 @@
|
|||
"sources" : "downloads"
|
||||
};
|
||||
|
||||
$win.bind("changeview", function() {
|
||||
$chart.addClass('loading');
|
||||
});
|
||||
|
||||
$win.bind("dataready", function(e, obj) {
|
||||
var view = obj.view,
|
||||
metric = view.metric,
|
||||
|
@ -120,7 +126,7 @@
|
|||
}
|
||||
|
||||
// Populate the chart config object.
|
||||
var chartData = [], id
|
||||
var chartData = [], id;
|
||||
for (i = 0; i < fields.length; i++) {
|
||||
field = fields[i];
|
||||
id = field.split("|").slice(-1)[0];
|
||||
|
@ -149,28 +155,79 @@
|
|||
} else {
|
||||
xFormatter = dayFormatter;
|
||||
}
|
||||
if (metricTypes[metric] == "users") {
|
||||
yFormatter = userFormatter;
|
||||
if (metric == 'overview') {
|
||||
return function() {
|
||||
return "<b>" + xFormatter(this.x) + "</b><br>" +
|
||||
downloadFormatter(this.points[0].y) + "<br>" +
|
||||
userFormatter(this.points[1].y);
|
||||
};
|
||||
} else {
|
||||
yFormatter = downloadFormatter;
|
||||
}
|
||||
return function() {
|
||||
return "<b>" + this.series.name + "</b><br>" +
|
||||
xFormatter(this.x) + "<br>" +
|
||||
yFormatter(this.y);
|
||||
if (metricTypes[metric] == "users") {
|
||||
yFormatter = userFormatter;
|
||||
} else {
|
||||
yFormatter = downloadFormatter;
|
||||
}
|
||||
return function() {
|
||||
return "<b>" + this.series.name + "</b><br>" +
|
||||
xFormatter(this.x) + "<br>" +
|
||||
yFormatter(this.y);
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
// Set up the new chart's configuration.
|
||||
var newConfig = $.extend(baseConfig, { series: chartData });
|
||||
if (fields.length == 1) {
|
||||
newConfig.legend.enabled = false;
|
||||
newConfig.chart.margin = [50, 50, 50, 80]
|
||||
// set up dual-axes for the overview chart.
|
||||
if (metric == "overview" && newConfig.series.length) {
|
||||
newConfig.yAxis = [
|
||||
{ // Downloads
|
||||
title: {
|
||||
text: gettext('Downloads')
|
||||
},
|
||||
// min: 0,
|
||||
labels: {
|
||||
formatter: function() {
|
||||
return Highcharts.numberFormat(this.value, 0);
|
||||
}
|
||||
}
|
||||
}, { // Daily Users
|
||||
title: {
|
||||
text: gettext('Daily Users')
|
||||
},
|
||||
labels: {
|
||||
formatter: function() {
|
||||
return Highcharts.numberFormat(this.value, 0);
|
||||
}
|
||||
},
|
||||
// min: 0,
|
||||
opposite: true
|
||||
}
|
||||
];
|
||||
// set Daily Users series to use the right yAxis.
|
||||
newConfig.series[1].yAxis = 1;
|
||||
newConfig.tooltip.shared = true;
|
||||
}
|
||||
newConfig.tooltip.formatter = tooltipFormatter;
|
||||
|
||||
if (fields.length == 1) {
|
||||
newConfig.legend.enabled = false;
|
||||
// newConfig.chart.margin = [50, 50, 50, 80];
|
||||
}
|
||||
|
||||
// Generate a pretty title for the chart.
|
||||
var title;
|
||||
if (typeof obj.view.range == 'string') {
|
||||
title = format(csv_keys.chartTitle[metric][0], obj.view.range);
|
||||
} else {
|
||||
title = format(csv_keys.chartTitle[metric][1], [z.date.date_string(new Date(start), '-'),
|
||||
z.date.date_string(new Date(end), '-')]);
|
||||
}
|
||||
newConfig.title = {
|
||||
text: title
|
||||
};
|
||||
|
||||
if (chart) chart.destroy();
|
||||
chart = new Highcharts.Chart(newConfig);
|
||||
$chart.removeClass('loading');
|
||||
});
|
||||
})();
|
|
@ -52,10 +52,13 @@
|
|||
if (newState.range) {
|
||||
if (!newState.range.custom) {
|
||||
var newRange = newState.range,
|
||||
$rangeEl = $('[data-range="' + newRange + '"]');
|
||||
$rangeEl = $('li[data-range="' + newRange + '"]');
|
||||
if ($rangeEl.length) {
|
||||
$rangeSelector.children("li.selected").removeClass("selected");
|
||||
$rangeEl.addClass("selected");
|
||||
} else {
|
||||
$rangeSelector.children("li.selected").removeClass("selected");
|
||||
$('li[data-range="custom"]').addClass("selected");
|
||||
}
|
||||
} else {
|
||||
$rangeSelector.children("li.selected").removeClass("selected");
|
||||
|
@ -65,12 +68,11 @@
|
|||
}
|
||||
if (newState.group) {
|
||||
$groupSelector.children('.selected').removeClass('selected');
|
||||
$('[data-group="' + newState.group + '"]').addClass('selected');
|
||||
$('li[data-group="' + newState.group + '"]').addClass('selected');
|
||||
}
|
||||
});
|
||||
|
||||
$("#date-range-form").submit(function(e) {
|
||||
e.preventDefault();
|
||||
$("#date-range-form").submit(_pd(function(e) {
|
||||
var start = new Date($("#date-range-start").val()),
|
||||
end = new Date($("#date-range-end").val()),
|
||||
newRange = {
|
||||
|
@ -78,9 +80,7 @@
|
|||
start: z.date.date(start),
|
||||
end: z.date.date(end)
|
||||
};
|
||||
|
||||
$rangeSelector.trigger('changeview', {range: newRange});
|
||||
$customModal.trigger('close');
|
||||
return false;
|
||||
});
|
||||
}));
|
||||
})();
|
|
@ -1,7 +1,11 @@
|
|||
csv_keys = {
|
||||
var csv_keys = {
|
||||
sources: {
|
||||
"null": gettext('unknown')
|
||||
},
|
||||
overview: {
|
||||
'downloads' : gettext('Downloads'),
|
||||
'updates' : gettext('Daily Users')
|
||||
},
|
||||
apps : {
|
||||
'{ec8030f7-c20a-464f-9b0e-13a3a9e97384}' : gettext('Firefox'),
|
||||
'{86c18b42-e466-45a9-ae7a-9b95ba6f5640}' : gettext('Mozilla'),
|
||||
|
@ -9,5 +13,43 @@ csv_keys = {
|
|||
'{718e30fb-e89b-41dd-9da7-e25a45638b28}' : gettext('Sunbird'),
|
||||
'{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}' : gettext('SeaMonkey'),
|
||||
'{a23983c0-fd0e-11dc-95ff-0800200c9a66}' : gettext('Fennec')
|
||||
},
|
||||
chartTitle: {
|
||||
"overview" : [
|
||||
gettext("Downloads and Daily Users, last {0}"),
|
||||
gettext("Downloads and Daily Users from {0} to {1}")
|
||||
],
|
||||
"downloads" : [
|
||||
gettext("Downloads, last {0}"),
|
||||
gettext("Downloads from {0} to {1}")
|
||||
],
|
||||
"usage" : [
|
||||
gettext("Daily Users, last {0}"),
|
||||
gettext("Daily Users from {0} to {1}")
|
||||
],
|
||||
"apps" : [
|
||||
gettext("Applications, last {0}"),
|
||||
gettext("Applications from {0} to {1}")
|
||||
],
|
||||
"os" : [
|
||||
gettext("Operating Systems, last {0}"),
|
||||
gettext("Operating Systems from {0} to {1}")
|
||||
],
|
||||
"locales" : [
|
||||
gettext("Languages, last {0}"),
|
||||
gettext("Languages from {0} to {1}")
|
||||
],
|
||||
"versions" : [
|
||||
gettext("Add-on Versions, last {0}"),
|
||||
gettext("Add-on Versions from {0} to {1}")
|
||||
],
|
||||
"statuses" : [
|
||||
gettext("Add-on Status, last {0}"),
|
||||
gettext("Add-on Status from {0} to {1}")
|
||||
],
|
||||
"sources" : [
|
||||
gettext("Download Sources, last {0}"),
|
||||
gettext("Download Sources from {0} to {1}")
|
||||
]
|
||||
}
|
||||
};
|
||||
|
|
|
@ -12,13 +12,13 @@ z.date = (function() {
|
|||
// millis("2 days")
|
||||
// > 172800000
|
||||
function millis(str) {
|
||||
var tokens = str.split(/\s+/);
|
||||
n = parseInt(tokens[0]);
|
||||
var tokens = str.split(/\s+/),
|
||||
n = parseInt(tokens[0], 10);
|
||||
if (!tokens[1]) throw "Invalid duration string";
|
||||
unit = tokens[1].replace(/s$/,'').toLowerCase();
|
||||
var unit = tokens[1].replace(/s$/,'').toLowerCase();
|
||||
if (!_millis[ unit ]) throw "Invalid time unit";
|
||||
return n * _millis[ unit ];
|
||||
};
|
||||
}
|
||||
|
||||
// pads a number with a preceding zero.
|
||||
// pad2(2)
|
||||
|
@ -28,32 +28,31 @@ z.date = (function() {
|
|||
function pad2(n) {
|
||||
var str = n.toString();
|
||||
return ('0' + str).substr(-2);
|
||||
};
|
||||
}
|
||||
|
||||
// Takes a date object and converts it to a time-less
|
||||
// representation of today's date.
|
||||
function date(d) {
|
||||
return Date.parse(date_string(d, '-'));
|
||||
};
|
||||
}
|
||||
function date_string(d, del) {
|
||||
del = del || '-';
|
||||
return [d.getFullYear(), pad2(d.getMonth()+1), pad2(d.getDate())].join(del);
|
||||
};
|
||||
}
|
||||
function datepicker_format(d) {
|
||||
return [pad2(d.getMonth()+1), pad2(d.getDate()), d.getFullYear()].join('/');
|
||||
};
|
||||
}
|
||||
|
||||
// Truncates the current time off today's date.
|
||||
function today() {
|
||||
var d = new Date();
|
||||
return date(d);
|
||||
};
|
||||
}
|
||||
|
||||
// returns a millisecond timestamp for a specified duration in the past.
|
||||
function ago(str, times) {
|
||||
times = (times !== undefined) ? times : 1;
|
||||
return today() - millis(str) * times;
|
||||
};
|
||||
}
|
||||
|
||||
// takes a range object and normalizes it to have a `start` and `end` property.
|
||||
function normalizeRange(range) {
|
||||
|
@ -65,7 +64,7 @@ z.date = (function() {
|
|||
ret.start = range.start;
|
||||
ret.end = range.end;
|
||||
} else {
|
||||
throw "Invalid range values found."
|
||||
throw "Invalid range values found.";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -7,11 +7,11 @@ function dbg() {
|
|||
z.hasPushState = (typeof history.replaceState === "function");
|
||||
|
||||
z.StatsManager = (function() {
|
||||
"use strict";
|
||||
// "use strict";
|
||||
|
||||
// The version of the stats localStorage we are using.
|
||||
// If you increment this number, you cache-bust everyone!
|
||||
var STATS_VERSION = 21;
|
||||
var STATS_VERSION = 22;
|
||||
|
||||
var storage = z.Storage("stats"),
|
||||
storageCache = z.Storage("statscache"),
|
||||
|
@ -30,7 +30,8 @@ z.StatsManager = (function() {
|
|||
"os": true,
|
||||
"sources": true,
|
||||
"versions": true,
|
||||
"statuses": true
|
||||
"statuses": true,
|
||||
"overview": true
|
||||
};
|
||||
|
||||
// is a metric an average or a sum?
|
||||
|
@ -46,7 +47,7 @@ z.StatsManager = (function() {
|
|||
};
|
||||
|
||||
// Initialize from localStorage when dom is ready.
|
||||
$(function() {
|
||||
function init() {
|
||||
dbg("looking for local data");
|
||||
if (verifyLocalStorage()) {
|
||||
var cacheObject = storageCache.get(addonId);
|
||||
|
@ -58,8 +59,8 @@ z.StatsManager = (function() {
|
|||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
$(init);
|
||||
|
||||
// These functions deal with our localStorage cache.
|
||||
|
||||
|
@ -80,6 +81,7 @@ z.StatsManager = (function() {
|
|||
return true;
|
||||
} else {
|
||||
dbg("wrong offline data verion");
|
||||
clearLocalStorage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -93,11 +95,12 @@ z.StatsManager = (function() {
|
|||
currentView = $.extend(currentView, newView);
|
||||
|
||||
// Fetch the data from the server or storage, and notify other components.
|
||||
getDataRange(currentView, function(data) {
|
||||
$.when( getDataRange(currentView) )
|
||||
.then( function(data) {
|
||||
$(window).trigger("dataready", {
|
||||
'view': currentView,
|
||||
'view' : currentView,
|
||||
'fields': getAvailableFields(currentView),
|
||||
'data': data
|
||||
'data' : data
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -153,17 +156,18 @@ z.StatsManager = (function() {
|
|||
// the range currently stored locally. Once all server requests return,
|
||||
// we move on.
|
||||
function getDataRange(view, callback) {
|
||||
dbg("enter getDataRange");
|
||||
dbg("enter getDataRange", view.metric);
|
||||
var range = z.date.normalizeRange(view.range),
|
||||
metric = view.metric,
|
||||
ds,
|
||||
needed = 0;
|
||||
needed = 0,
|
||||
$def = $.Deferred();
|
||||
|
||||
function finished() {
|
||||
needed--;
|
||||
dbg(pendingFetches, " fetches pending");
|
||||
if (needed < 1) {
|
||||
var ret = {}, i, row,
|
||||
var ret = {}, row,
|
||||
step = z.date.millis("1 day");
|
||||
ds = dataStore[metric];
|
||||
for (var i=range.start; i<range.end; i+= step) {
|
||||
|
@ -172,13 +176,14 @@ z.StatsManager = (function() {
|
|||
}
|
||||
}
|
||||
ret = groupData(ret, view);
|
||||
callback.call(this, ret);
|
||||
ret.metric = metric;
|
||||
$def.resolve(ret);
|
||||
}
|
||||
}
|
||||
|
||||
if (dataStore[metric]) {
|
||||
ds = dataStore[metric];
|
||||
dbg("range", range.start, range.end)
|
||||
dbg("range", range.start, range.end);
|
||||
if (ds.maxdate < range.end) {
|
||||
needed++;
|
||||
fetchData(metric, ds.maxdate, range.end, finished);
|
||||
|
@ -196,10 +201,11 @@ z.StatsManager = (function() {
|
|||
needed++;
|
||||
fetchData(metric, range.start, range.end, finished);
|
||||
}
|
||||
return $def;
|
||||
}
|
||||
|
||||
|
||||
// Aggregate data based on our view's `group` setting.
|
||||
// Aggregate data based on view's `group` setting.
|
||||
function groupData(data, view) {
|
||||
var metric = view.metric,
|
||||
range = z.date.normalizeRange(view.range),
|
||||
|
@ -216,17 +222,20 @@ z.StatsManager = (function() {
|
|||
d = new Date(i);
|
||||
row = data[i];
|
||||
// Here's where grouping points are caluculated.
|
||||
if ((group == 'week' && d.getDay() == 0) || (group == 'month' && d.getDate() == 1)) {
|
||||
if ((group == 'week' && d.getDay() === 0) || (group == 'month' && d.getDate() == 1)) {
|
||||
// we drop the some days of data from the result set
|
||||
// if they are not a complete grouping.
|
||||
if (groupKey && groupVal) {
|
||||
// average `count` for mean metrics
|
||||
if (metricTypes[metric] == 'mean') {
|
||||
groupVal['count'] /= groupCount;
|
||||
groupVal.count /= groupCount;
|
||||
}
|
||||
if (metric in breakdownMetrics) {
|
||||
// overview gets special treatment. Only average ADUs.
|
||||
if (metric == "overview") {
|
||||
groupVal.data.updates /= groupCount;
|
||||
} else if (metric in breakdownMetrics) {
|
||||
// average for mean metrics.
|
||||
_.each(groupVal.data, function(val, field) {
|
||||
// average for mean metrics.
|
||||
if (metricTypes[metric] == 'mean') {
|
||||
groupVal.data[field] /= groupCount;
|
||||
}
|
||||
|
@ -264,8 +273,8 @@ z.StatsManager = (function() {
|
|||
|
||||
// The beef. Negotiates with the server for data.
|
||||
function fetchData(metric, start, end, callback) {
|
||||
var seriesStart = start;
|
||||
var seriesEnd = end;
|
||||
var seriesStart = start,
|
||||
seriesEnd = end;
|
||||
|
||||
pendingFetches++;
|
||||
|
||||
|
@ -291,9 +300,8 @@ z.StatsManager = (function() {
|
|||
dataStore[metric].maxdate = 0;
|
||||
}
|
||||
|
||||
var ds = dataStore[metric], data;
|
||||
|
||||
data = JSON.parse(raw_data);
|
||||
var ds = dataStore[metric],
|
||||
data = JSON.parse(raw_data);
|
||||
|
||||
var i, datekey;
|
||||
for (i=0; i<data.length; i++) {
|
||||
|
@ -318,7 +326,7 @@ z.StatsManager = (function() {
|
|||
}
|
||||
|
||||
setTimeout(function () {
|
||||
AMO.fetchData(metric, start, end, callback);
|
||||
fetchData(metric, start, end, callback);
|
||||
}, retry_delay);
|
||||
|
||||
}
|
||||
|
@ -363,7 +371,10 @@ z.StatsManager = (function() {
|
|||
var parts = field.split('|'),
|
||||
val = row;
|
||||
|
||||
// give up if the row is falsy.
|
||||
if (!val) return null;
|
||||
// drill into the row object for a nested key.
|
||||
// `data|api` means row['data']['api']
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
val = val[parts[i]];
|
||||
if (!val) {
|
||||
|
@ -376,8 +387,8 @@ z.StatsManager = (function() {
|
|||
|
||||
|
||||
function getPrettyName(metric, field) {
|
||||
var parts = field.split('_');
|
||||
var key = parts[0];
|
||||
var parts = field.split('_'),
|
||||
key = parts[0];
|
||||
parts = parts.slice(1);
|
||||
|
||||
if (metric in csv_keys) {
|
||||
|
|
|
@ -6,9 +6,10 @@ $(function() {
|
|||
range = view.range;
|
||||
if (range) {
|
||||
if (typeof range == "string") {
|
||||
queryParams['last'] = range.split(/\s+/)[0];
|
||||
queryParams.last = range.split(/\s+/)[0];
|
||||
} else if (typeof range == "object") {
|
||||
|
||||
// queryParams.start = z.date.date_string(new Date(range.start), '');
|
||||
// queryParams.end = z.date.date_string(new Date(range.end), '');
|
||||
}
|
||||
}
|
||||
queryParams = $.param(queryParams);
|
||||
|
@ -17,10 +18,11 @@ $(function() {
|
|||
}
|
||||
});
|
||||
|
||||
// Set up initial default view.
|
||||
var initView = {
|
||||
metric: $('.primary').attr('data-report'),
|
||||
range: $('.primary').attr('data-range'),
|
||||
group: 'day'
|
||||
group: 'week'
|
||||
};
|
||||
|
||||
$(window).trigger('changeview', initView);
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -727,7 +727,7 @@ MINIFY_BUNDLES = {
|
|||
'js/lib/jquery-datepicker.js',
|
||||
'js/lib/highcharts.src.js',
|
||||
'js/impala/stats/csv_keys.js',
|
||||
# 'js/zamboni/stats/helpers.js',
|
||||
'js/zamboni/stats/helpers.js',
|
||||
# 'js/zamboni/stats/stats_manager.js',
|
||||
# 'js/zamboni/stats/stats_tables.js',
|
||||
# 'js/zamboni/stats/stats.js',
|
||||
|
|
Загрузка…
Ссылка в новой задаче