wakapi/static/index.html

243 lines
8.5 KiB
HTML

<html>
<head>
<title>Coding Stats</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<link rel="icon" data-emoji="📊" type="image/png">
<style>
body {
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
color: #666;
}
.grid-container {
width: 75%;
display: grid;
grid-template-areas:
'header header header'
'sec1 sec1 sec2'
'sec1 sec1 sec3'
'sec1 sec1 sec4'
'footer footer footer';
grid-gap: 10px;
visibility: hidden;
}
.projects-container {
grid-area: sec1
}
.os-container {
grid-area: sec2
}
.editor-container {
grid-area: sec3
}
.language-container {
grid-area: sec4
}
.header-container {
grid-area: header
}
.apikey-input {
width: 300px;
}
</style>
</head>
<body>
<h1>Statistics</h1>
<div class="input-container" id="input-container">
<label for="apikey">API Key: </label>
<input type="text" class="apikey-input" id="apikey-input" name="apikey" placeholder="Enter your API key here">
<button onclick="load('day', true)">Day (live)</button>
<button onclick="load('week', false)">Week</button>
<button onclick="load('month', false)">Month</button>
<button onclick="load('year', false)">Year</button>
</div>
<div class="grid-container" id="grid-container">
<div class="header-container">
<p>
<strong>Total:</strong> <span id="total-span"></span><br>
</p>
</div>
<div class="projects-container" id="projects-container">
<canvas id="chart-projects"></canvas>
</div>
<div class="os-container" id="os-container">
<canvas id="chart-os"></canvas>
</div>
<div class="editor-container" id="editor-container">
<canvas id="chart-editor"></canvas>
</div>
<div class="language-container" id="language-container">
<canvas id="chart-language"></canvas>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.4/seedrandom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.bundle.min.js"></script>
<script>
String.prototype.toHHMMSS = function () {
var sec_num = parseInt(this, 10);
var hours = Math.floor(sec_num / 3600);
var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
var seconds = sec_num - (hours * 3600) - (minutes * 60);
if (hours < 10) { hours = "0" + hours; }
if (minutes < 10) { minutes = "0" + minutes; }
if (seconds < 10) { seconds = "0" + seconds; }
return hours + ':' + minutes + ':' + seconds;
}
function load(interval, live) {
let apiKey = document.getElementById('apikey-input').value
fetch(`${window.location.href}/api/summary?interval=${interval}&live=${live}`, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Authorization': `Basic ${btoa(apiKey)}`
}
})
.then((res) => {
if (res.status === 401) {
console.error('Unauthorized')
alert('Unauthorized')
}
return res
})
.then((res) => res.json())
.then((json) => draw(json))
.catch(err => console.error(err));
}
function draw(data) {
let titleOptions = {
display: true,
fontSize: 16
}
function getTooltipOptions(key, type) {
return {
mode: 'single',
callbacks: {
label: (item) => {
let idx = type === 'pie' ? item.index : item.datasetIndex
let d = data[key][idx]
return `${d.key}: ${d.total.toString().toHHMMSS()}`
}
}
}
}
let projectChart = new Chart(document.getElementById("chart-projects").getContext('2d'), {
type: 'horizontalBar',
data: {
datasets: data.projects
.map(p => {
return {
label: p.key,
data: [parseInt(p.total)],
backgroundColor: getRandomColor(p.key)
}
})
},
options: {
title: Object.assign(titleOptions, { text: 'Projects' }),
tooltips: getTooltipOptions('projects', 'bar'),
legend: {
display: false
}
}
});
let osChart = new Chart(document.getElementById("chart-os").getContext('2d'), {
type: 'pie',
data: {
datasets: [{
data: data.operating_systems.map(p => parseInt(p.total)),
backgroundColor: data.operating_systems.map(p => getRandomColor(p.key))
}],
labels: data.operating_systems.map(p => p.key)
},
options: {
title: Object.assign(titleOptions, { text: 'Operating Systems' }),
tooltips: getTooltipOptions('operating_systems', 'pie')
}
});
let editorChart = new Chart(document.getElementById("chart-editor").getContext('2d'), {
type: 'pie',
data: {
datasets: [{
data: data.editors.map(p => parseInt(p.total)),
backgroundColor: data.editors.map(p => getRandomColor(p.key))
}],
labels: data.editors.map(p => p.key)
},
options: {
title: Object.assign(titleOptions, { text: 'Editors' }),
tooltips: getTooltipOptions('editors', 'pie')
}
});
let languageChart = new Chart(document.getElementById("chart-language").getContext('2d'), {
type: 'pie',
data: {
datasets: [{
data: data.languages
.map(p => parseInt(p.total)),
backgroundColor: data.languages.map(p => getRandomColor(p.key))
}],
labels: data.languages
.map(p => p.key)
},
options: {
title: Object.assign(titleOptions, { text: 'Languages' }),
tooltips: getTooltipOptions('languages', 'pie')
}
});
getTotal(data.operating_systems)
document.getElementById('grid-container').style.visibility = 'visible';
}
function getRandomColor(seed) {
seed = seed ? seed : '1234567';
Math.seedrandom(seed);
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
function getTotal(data) {
let total = data.reduce((acc, d) => acc + d.total, 0)
document.getElementById("total-span").innerText = total.toString().toHHMMSS()
}
// https://koddsson.com/posts/emoji-favicon/
const favicon = document.querySelector("link[rel=icon]");
if (favicon) {
const emoji = favicon.getAttribute("data-emoji");
if (emoji) {
const canvas = document.createElement("canvas");
canvas.height = 64;
canvas.width = 64;
const ctx = canvas.getContext("2d");
ctx.font = "64px serif";
ctx.fillText(emoji, 0, 64);
favicon.href = canvas.toDataURL();
}
}
</script>
</body>
</html>