htdocs/index.html (244 lines of code) (raw):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Download Statistics</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.16.0/d3.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.18/c3.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.18/c3.min.css" rel="stylesheet"/>
<style>
body {
font-family: sans-serif;
text-align: center;
}
.loader {
margin: 100px auto;
font-size: 25px;
width: 1em;
height: 1em;
border-radius: 50%;
position: relative;
text-indent: -9999em;
-webkit-animation: load5 1.1s infinite ease;
animation: load5 1.1s infinite ease;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
}
@-webkit-keyframes load5 {
0%,
100% {
box-shadow: 0em -2.6em 0em 0em #122040, 1.8em -1.8em 0 0em rgba(18,32,64, 0.2), 2.5em 0em 0 0em rgba(18,32,64, 0.2), 1.75em 1.75em 0 0em rgba(18,32,64, 0.2), 0em 2.5em 0 0em rgba(18,32,64, 0.2), -1.8em 1.8em 0 0em rgba(18,32,64, 0.2), -2.6em 0em 0 0em rgba(18,32,64, 0.5), -1.8em -1.8em 0 0em rgba(18,32,64, 0.7);
}
12.5% {
box-shadow: 0em -2.6em 0em 0em rgba(18,32,64, 0.7), 1.8em -1.8em 0 0em #122040, 2.5em 0em 0 0em rgba(18,32,64, 0.2), 1.75em 1.75em 0 0em rgba(18,32,64, 0.2), 0em 2.5em 0 0em rgba(18,32,64, 0.2), -1.8em 1.8em 0 0em rgba(18,32,64, 0.2), -2.6em 0em 0 0em rgba(18,32,64, 0.2), -1.8em -1.8em 0 0em rgba(18,32,64, 0.5);
}
25% {
box-shadow: 0em -2.6em 0em 0em rgba(18,32,64, 0.5), 1.8em -1.8em 0 0em rgba(18,32,64, 0.7), 2.5em 0em 0 0em #122040, 1.75em 1.75em 0 0em rgba(18,32,64, 0.2), 0em 2.5em 0 0em rgba(18,32,64, 0.2), -1.8em 1.8em 0 0em rgba(18,32,64, 0.2), -2.6em 0em 0 0em rgba(18,32,64, 0.2), -1.8em -1.8em 0 0em rgba(18,32,64, 0.2);
}
37.5% {
box-shadow: 0em -2.6em 0em 0em rgba(18,32,64, 0.2), 1.8em -1.8em 0 0em rgba(18,32,64, 0.5), 2.5em 0em 0 0em rgba(18,32,64, 0.7), 1.75em 1.75em 0 0em #122040, 0em 2.5em 0 0em rgba(18,32,64, 0.2), -1.8em 1.8em 0 0em rgba(18,32,64, 0.2), -2.6em 0em 0 0em rgba(18,32,64, 0.2), -1.8em -1.8em 0 0em rgba(18,32,64, 0.2);
}
50% {
box-shadow: 0em -2.6em 0em 0em rgba(18,32,64, 0.2), 1.8em -1.8em 0 0em rgba(18,32,64, 0.2), 2.5em 0em 0 0em rgba(18,32,64, 0.5), 1.75em 1.75em 0 0em rgba(18,32,64, 0.7), 0em 2.5em 0 0em #122040, -1.8em 1.8em 0 0em rgba(18,32,64, 0.2), -2.6em 0em 0 0em rgba(18,32,64, 0.2), -1.8em -1.8em 0 0em rgba(18,32,64, 0.2);
}
62.5% {
box-shadow: 0em -2.6em 0em 0em rgba(18,32,64, 0.2), 1.8em -1.8em 0 0em rgba(18,32,64, 0.2), 2.5em 0em 0 0em rgba(18,32,64, 0.2), 1.75em 1.75em 0 0em rgba(18,32,64, 0.5), 0em 2.5em 0 0em rgba(18,32,64, 0.7), -1.8em 1.8em 0 0em #122040, -2.6em 0em 0 0em rgba(18,32,64, 0.2), -1.8em -1.8em 0 0em rgba(18,32,64, 0.2);
}
75% {
box-shadow: 0em -2.6em 0em 0em rgba(18,32,64, 0.2), 1.8em -1.8em 0 0em rgba(18,32,64, 0.2), 2.5em 0em 0 0em rgba(18,32,64, 0.2), 1.75em 1.75em 0 0em rgba(18,32,64, 0.2), 0em 2.5em 0 0em rgba(18,32,64, 0.5), -1.8em 1.8em 0 0em rgba(18,32,64, 0.7), -2.6em 0em 0 0em #122040, -1.8em -1.8em 0 0em rgba(18,32,64, 0.2);
}
87.5% {
box-shadow: 0em -2.6em 0em 0em rgba(18,32,64, 0.2), 1.8em -1.8em 0 0em rgba(18,32,64, 0.2), 2.5em 0em 0 0em rgba(18,32,64, 0.2), 1.75em 1.75em 0 0em rgba(18,32,64, 0.2), 0em 2.5em 0 0em rgba(18,32,64, 0.2), -1.8em 1.8em 0 0em rgba(18,32,64, 0.5), -2.6em 0em 0 0em rgba(18,32,64, 0.7), -1.8em -1.8em 0 0em #122040;
}
}
@keyframes load5 {
0%,
100% {
box-shadow: 0em -2.6em 0em 0em #122040, 1.8em -1.8em 0 0em rgba(18,32,64, 0.2), 2.5em 0em 0 0em rgba(18,32,64, 0.2), 1.75em 1.75em 0 0em rgba(18,32,64, 0.2), 0em 2.5em 0 0em rgba(18,32,64, 0.2), -1.8em 1.8em 0 0em rgba(18,32,64, 0.2), -2.6em 0em 0 0em rgba(18,32,64, 0.5), -1.8em -1.8em 0 0em rgba(18,32,64, 0.7);
}
12.5% {
box-shadow: 0em -2.6em 0em 0em rgba(18,32,64, 0.7), 1.8em -1.8em 0 0em #122040, 2.5em 0em 0 0em rgba(18,32,64, 0.2), 1.75em 1.75em 0 0em rgba(18,32,64, 0.2), 0em 2.5em 0 0em rgba(18,32,64, 0.2), -1.8em 1.8em 0 0em rgba(18,32,64, 0.2), -2.6em 0em 0 0em rgba(18,32,64, 0.2), -1.8em -1.8em 0 0em rgba(18,32,64, 0.5);
}
25% {
box-shadow: 0em -2.6em 0em 0em rgba(18,32,64, 0.5), 1.8em -1.8em 0 0em rgba(18,32,64, 0.7), 2.5em 0em 0 0em #122040, 1.75em 1.75em 0 0em rgba(18,32,64, 0.2), 0em 2.5em 0 0em rgba(18,32,64, 0.2), -1.8em 1.8em 0 0em rgba(18,32,64, 0.2), -2.6em 0em 0 0em rgba(18,32,64, 0.2), -1.8em -1.8em 0 0em rgba(18,32,64, 0.2);
}
37.5% {
box-shadow: 0em -2.6em 0em 0em rgba(18,32,64, 0.2), 1.8em -1.8em 0 0em rgba(18,32,64, 0.5), 2.5em 0em 0 0em rgba(18,32,64, 0.7), 1.75em 1.75em 0 0em #122040, 0em 2.5em 0 0em rgba(18,32,64, 0.2), -1.8em 1.8em 0 0em rgba(18,32,64, 0.2), -2.6em 0em 0 0em rgba(18,32,64, 0.2), -1.8em -1.8em 0 0em rgba(18,32,64, 0.2);
}
50% {
box-shadow: 0em -2.6em 0em 0em rgba(18,32,64, 0.2), 1.8em -1.8em 0 0em rgba(18,32,64, 0.2), 2.5em 0em 0 0em rgba(18,32,64, 0.5), 1.75em 1.75em 0 0em rgba(18,32,64, 0.7), 0em 2.5em 0 0em #122040, -1.8em 1.8em 0 0em rgba(18,32,64, 0.2), -2.6em 0em 0 0em rgba(18,32,64, 0.2), -1.8em -1.8em 0 0em rgba(18,32,64, 0.2);
}
62.5% {
box-shadow: 0em -2.6em 0em 0em rgba(18,32,64, 0.2), 1.8em -1.8em 0 0em rgba(18,32,64, 0.2), 2.5em 0em 0 0em rgba(18,32,64, 0.2), 1.75em 1.75em 0 0em rgba(18,32,64, 0.5), 0em 2.5em 0 0em rgba(18,32,64, 0.7), -1.8em 1.8em 0 0em #122040, -2.6em 0em 0 0em rgba(18,32,64, 0.2), -1.8em -1.8em 0 0em rgba(18,32,64, 0.2);
}
75% {
box-shadow: 0em -2.6em 0em 0em rgba(18,32,64, 0.2), 1.8em -1.8em 0 0em rgba(18,32,64, 0.2), 2.5em 0em 0 0em rgba(18,32,64, 0.2), 1.75em 1.75em 0 0em rgba(18,32,64, 0.2), 0em 2.5em 0 0em rgba(18,32,64, 0.5), -1.8em 1.8em 0 0em rgba(18,32,64, 0.7), -2.6em 0em 0 0em #122040, -1.8em -1.8em 0 0em rgba(18,32,64, 0.2);
}
87.5% {
box-shadow: 0em -2.6em 0em 0em rgba(18,32,64, 0.2), 1.8em -1.8em 0 0em rgba(18,32,64, 0.2), 2.5em 0em 0 0em rgba(18,32,64, 0.2), 1.75em 1.75em 0 0em rgba(18,32,64, 0.2), 0em 2.5em 0 0em rgba(18,32,64, 0.2), -1.8em 1.8em 0 0em rgba(18,32,64, 0.5), -2.6em 0em 0 0em rgba(18,32,64, 0.7), -1.8em -1.8em 0 0em #122040;
}
}
</style>
</head>
<body>
<div>
<b>Project or filename:</b>
<input type="text" id="project">
<b>Timespan:</b>
<select id="duration">
<option value="24h">Past day</option>
<option value="1w">Past week</option>
<option selected value="1M">Past month</option>
<option value="3M">Past quarter</option>
</select>
<input type="button" onclick="fetch_stats();" value="Fetch stats"/>
</div>
<div id="stats" style="display: none;">
<h2>Downloads by Country</h2>
<div id="stats_chart_country"></div>
<div style="width: 48%; display: inline-block;">
<h2>Most downloaded files</h2>
<table id="table_requests_main">
<thead>
<tr><th>Filename:</th><th>Downloads (unique IPs):</tr>
</thead>
<tbody id="table_requests"></tbody>
</table>
<hr/>
<h2>Most traffic</h2>
<table id="table_traffic_main">
<thead>
<tr><th>Filename:</th><th>Traffic in bytes:</tr>
</thead>
<tbody id="table_traffic"></tbody>
</table>
</div>
</div>
<div id="spinner" style="display: none;">
<div class="loader"></div>
Fetching statistics...
</div>
<script type="text/javascript">
Number.prototype.pretty = function(fix) {
if (fix) {
return String(this.toFixed(fix)).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');
}
return String(this.toFixed(0)).replace(/(\d)(?=(\d{3})+$)/g, '$1,');
};
function draw_bars(columns, domains, key) {
let chart = c3.generate({
bindto: '#stats_chart_' + key,
data: {
type: 'bar',
columns: columns
}
});
}
function draw_line(columns, key) {
let chart = c3.generate({
bindto: '#stats_chart_' + key + '_all',
data: {
type: 'area-spline',
x: 'x',
xFormat: '%Y%m%d %t:%M',
columns: columns
},
point: {
show: false
},
axis: {
x: {
type: 'timeseries',
tick: {
format: '%Y-%m-%d %H:%M',
count: 12
}
}
}
});
}
function draw_donut(columns, key) {
let chart = c3.generate({
bindto: '#stats_chart_' + key,
data: {
type: 'donut',
columns: columns
},
tooltip: {
format: {
title: function (d) { return d; },
value: function (value, ratio, id) {
var format = d3.format(',') ;
return format(value);
}
}
}
});
chart.resize({height: 500, width: 800});
}
function compile_chart(js) {
let domains = [];
let timestamps = ['x'];
let timestamps_all = ['x'];
let total_by_country = [];
for (let k in js.by_country) {
total_by_country.push([k,js.by_country[k]]);
}
draw_donut(total_by_country, 'country');
// Most downloaded files
let tbl = document.getElementById('table_requests');
tbl.textContent = "";
let files_sorted = Object.keys(js.by_requests).sort((a,b) => js.by_requests[b]-js.by_requests[a]);
for (let k of files_sorted) {
let value = js.by_requests[k];
let value_unique = js.by_requests_unique[k];
let tr = document.createElement('tr');
let td1 = document.createElement('td');
let td2 = document.createElement('td');
td1.innerText = k;
td1.style.textAlign = 'left';
td2.innerText = `${value.pretty()} (${value_unique.pretty()})`;
td2.style.textAlign = 'right';
tr.appendChild(td1);
tr.appendChild(td2);
tbl.appendChild(tr);
}
// Most downloaded files
tbl = document.getElementById('table_traffic');
tbl.textContent = "";
files_sorted = Object.keys(js.by_bytes).sort((a,b) => js.by_bytes[b]-js.by_bytes[a]);
for (let k of files_sorted) {
let value = js.by_bytes[k];
let tr = document.createElement('tr');
let td1 = document.createElement('td');
let td2 = document.createElement('td');
td1.innerText = k;
td1.style.textAlign = 'left';
td2.innerText = value.pretty();
td2.style.textAlign = 'right';
tr.appendChild(td1);
tr.appendChild(td2);
tbl.appendChild(tr);
}
document.getElementById('spinner').style.display = "none";
document.getElementById('stats').style.display = "block";
// draw_bars(columns, domains, key);
}
function fetch_stats() {
document.getElementById('stats').style.display = "none";
document.getElementById('spinner').style.display = "block";
let project = document.getElementById('project').value;
let duration = document.getElementById('duration').value;
fetch(`stats?project=${project}&duration=${duration}`)
.then(response => response.json())
.then(data=> {
compile_chart(data);
})
.catch(error => console.error(error));
}
</script>
<hr/>
The source code for this stats page can be found at: <a href="https://github.com/apache/infrastructure-dlstats">https://github.com/apache/infrastructure-dlstats</a>.
</body>
</html>