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>