Add bar graphs

pull/2443/head
Marco H 2023-01-03 15:18:45 +01:00
parent d709857ac7
commit a7575ecd9a
3 changed files with 164 additions and 36 deletions

View File

@ -49,3 +49,7 @@ and run `EspruinoDocs/bin/minify.js lib.js lib.min.js`
* Yearly view * Yearly view
* Heart rate 'zone' graph * Heart rate 'zone' graph
* .. other * .. other
## License
The graphs on the web interface use Chart.js, licensed under MIT License.

14
apps/health/chart.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -6,6 +6,7 @@
<div id="content"></div> <div id="content"></div>
<script src="../../core/lib/interface.js"></script> <script src="../../core/lib/interface.js"></script>
<script type="module" src="chart.min.js"></script>
<script> <script>
const DB_RECORD_LEN = 4; const DB_RECORD_LEN = 4;
const DB_RECORDS_PER_HR = 6; const DB_RECORDS_PER_HR = 6;
@ -83,7 +84,8 @@ function getMonthList() {
<td>${f.str}</td> <td>${f.str}</td>
<td> <td>
<button class="btn btn-primary" filename="${f.filename}" date="${f.date}" task="downloadcsv">Download CSV</button> <button class="btn btn-primary" filename="${f.filename}" date="${f.date}" task="downloadcsv">Download CSV</button>
<button class="btn btn-primary" filename="${f.filename}" date="${f.date}" task="viewmonth">View</button> <button class="btn btn-primary" filename="${f.filename}" date="${f.date}" monthstr="${f.str}" task="monthtable">Table</button>
<button class="btn btn-primary" filename="${f.filename}" date="${f.date}" monthstr="${f.str}" task="monthgraph">Graph</button>
<button class="btn btn-error" filename="${f.filename}" date="${f.date}" task="delete" style="float: right;margin-right: 5px;">Delete</button> <button class="btn btn-error" filename="${f.filename}" date="${f.date}" task="delete" style="float: right;margin-right: 5px;">Delete</button>
</td> </td>
</tr> </tr>
@ -109,6 +111,7 @@ function getMonthList() {
var filename = button.getAttribute("filename"); var filename = button.getAttribute("filename");
var date = button.getAttribute("date"); var date = button.getAttribute("date");
if (!filename || !date) return; if (!filename || !date) return;
var monthstr = button.getAttribute("monthstr");
var task = button.getAttribute("task"); var task = button.getAttribute("task");
if (task=="delete") { if (task=="delete") {
Util.showModal("Deleting..."); Util.showModal("Deleting...");
@ -120,21 +123,56 @@ function getMonthList() {
if (task=="downloadcsv") { if (task=="downloadcsv") {
downloadHealth(filename, data => saveCSV(data, date, `Bangle.js Health ${date}`)); downloadHealth(filename, data => saveCSV(data, date, `Bangle.js Health ${date}`));
} }
if (task=="viewmonth") { if (task=="monthtable") {
viewMonthlyData(filename, date); viewMonthDataAsTable(filename, date, monthstr);
}
if (task=="monthgraph") {
viewMonthDataAsGraph(filename, date, monthstr);
} }
}); });
} }
}) })
} }
function viewMonthlyData(filename, date) { function getDailyData(data) {
Util.showModal("Reading Health info..."); var dailyData = [];
Util.readStorage( var idx = DB_HEADER_LEN;
for (var day = 0; day < 31; day++) {
var dayData = {steps: 0, bpm: 0, movement: 0};
for (var hr = 0; hr < 24; hr++) { // actually 25, see below
for (var m = 0; m < DB_RECORDS_PER_HR; m++) {
var h = data.substr(idx, DB_RECORD_LEN);
if (h != "\xFF\xFF\xFF\xFF") {
var h = {
day : day + 1,
hr : hr,
min : m * 10,
steps : (h.charCodeAt(0) << 8) | h.charCodeAt(1),
bpm : h.charCodeAt(2),
movement : h.charCodeAt(3)
};
dayData.steps += h.steps; // sum
dayData.bpm = (dayData.bpm + h.bpm) / 2; // average
dayData.movement += h.movement; // sum
}
idx += DB_RECORD_LEN;
}
}
idx += DB_RECORD_LEN; // +1 because we have an extra record with totals
// for the end of the day
dailyData[day + 1] = dayData;
}
return dailyData;
}
function viewMonthDataAsTable(filename, date, monthstr) {
Util.showModal("Reading Health info...");
Util.readStorage(
filename, data => { filename, data => {
Util.hideModal(); Util.hideModal();
var htmlOverview = `<h1> Month ` + date + `: </ h1> var htmlOverview = `<h1>` + monthstr + `</ h1>
<button class="btn btn-primary" id="backtomonth" style="float: right;margin-right: 5px;">Back</button> <button class="btn btn-primary" id="backtomonth" style="float: right;margin-right: 5px;">Back</button>
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
@ -147,36 +185,16 @@ Util.readStorage(
</thead> </thead>
<tbody>\n`; <tbody>\n`;
var idx = DB_HEADER_LEN; var dailyData = getDailyData(data);
for (var day = 0; day < 31; day++) { for (var i = 1; i < dailyData.length + 1; i++) {
var dayData = {steps: 0, bpm: 0, movement: 0}; var dayData = dailyData[i];
for (var hr = 0; hr < 24; hr++) { // actually 25, see below if (dayData) {
for (var m = 0; m < DB_RECORDS_PER_HR; m++) { htmlOverview += `<tr>
var h = data.substr(idx, DB_RECORD_LEN); <td>${i}</td>
if (h != "\xFF\xFF\xFF\xFF") { <td>${dayData.steps}</td>
var h = { <td>${Math.round(dayData.bpm)}</td>
day : day + 1, <td>${dayData.movement}</td></tr>`
hr : hr,
min : m * 10,
steps : (h.charCodeAt(0) << 8) | h.charCodeAt(1),
bpm : h.charCodeAt(2),
movement : h.charCodeAt(3)
};
dayData.steps += h.steps; // sum
dayData.bpm = (dayData.bpm + h.bpm) / 2; // average
dayData.movement += h.movement; // sum
}
idx += DB_RECORD_LEN;
} }
}
idx += DB_RECORD_LEN; // +1 because we have an extra record with totals
// for the end of the day
htmlOverview += `<tr>
<td>${day + 1}</td>
<td>${dayData.steps}</td>
<td>${Math.round(dayData.bpm)}</td>
<td>${dayData.movement}</td></tr>`
} }
htmlOverview += `</tbody></table>`; htmlOverview += `</tbody></table>`;
domContent.innerHTML = htmlOverview; domContent.innerHTML = htmlOverview;
@ -186,6 +204,98 @@ Util.readStorage(
}); });
} }
function viewMonthDataAsGraph(filename, date, monthstr) {
Util.showModal("Reading Health info...");
Util.readStorage(
filename, data => {
Util.hideModal();
var html = `<h1>` + monthstr + `</ h1>
<button class="btn btn-primary" id="backtomonth" style="float: right;margin-right: 5px;">Back</button>
<h2>Steps</h2>
<canvas id="chartSteps"></canvas>
<h2>BPM</h2>
<canvas id="chartBPM"></canvas>
<h2>Movement</h2>
<canvas id="chartMovement"></canvas>`
domContent.innerHTML = html;
domContent.querySelector("#backtomonth").addEventListener("click",event => {
getMonthList();
});
var labels = [];
var dataSteps = [], dataBPM = [], dataMovement = [];
var dailyData = getDailyData(data);
for (var i = 1; i < dailyData.length + 1; i++) {
var dayData = dailyData[i];
if (dayData) {
labels.push(i);
dataSteps.push(dayData.steps);
dataBPM.push(dayData.bpm);
dataMovement.push(dayData.movement);
}
}
new Chart(document.getElementById("chartSteps"), {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: '# of steps',
data: dataSteps,
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
new Chart(document.getElementById("chartBPM"), {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Beats per minute',
data: dataBPM,
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
new Chart(document.getElementById("chartMovement"), {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Movement',
data: dataMovement,
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
});
}
function onInit() { function onInit() {
getMonthList(); getMonthList();
} }