Add page to export files from Stopwatch, save files in better format and with better filename

pull/205/head
Gordon Williams 2020-04-03 15:15:06 +01:00
parent 9b918055da
commit f66aab5823
6 changed files with 128 additions and 28 deletions

View File

@ -392,7 +392,8 @@
{ "id": "swatch", { "id": "swatch",
"name": "Stopwatch", "name": "Stopwatch",
"icon": "stopwatch.png", "icon": "stopwatch.png",
"version":"0.03", "version":"0.04",
"interface": "interface.html",
"description": "Simple stopwatch with Lap Time logging to a JSON file", "description": "Simple stopwatch with Lap Time logging to a JSON file",
"tags": "health", "tags": "health",
"allow_emulator":true, "allow_emulator":true,

View File

@ -11,17 +11,7 @@ var domRecords = document.getElementById("records");
function saveRecord(record,name) { function saveRecord(record,name) {
var csv = `${record.map(rec=>[rec.time, rec.bpm, rec.confidence].join(",")).join("\n")}`; var csv = `${record.map(rec=>[rec.time, rec.bpm, rec.confidence].join(",")).join("\n")}`;
var a = document.createElement("a"), Util.saveCSV(name, csv);
file = new Blob([csv], {type: "Comma-separated value file"});
var url = URL.createObjectURL(file);
a.href = url;
a.download = name+".csv";
document.body.appendChild(a);
a.click();
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
} }

View File

@ -3,3 +3,4 @@
Lap log now scrolls into 2nd column after 18th entry, able to display 36 entries before going off screen Lap log now scrolls into 2nd column after 18th entry, able to display 36 entries before going off screen
0.03: Added ability to save Lap log as a date named JSON file into memory 0.03: Added ability to save Lap log as a date named JSON file into memory
Fixed bug from 0.01 where BN1 (reset) could clear the lap log when timer is running Fixed bug from 0.01 where BN1 (reset) could clear the lap log when timer is running
0.04: Changed save file filename, add interface.html to allow laps to be loaded

View File

@ -0,0 +1,90 @@
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
</head>
<body>
<div id="records"></div>
<script src="../../lib/interface.js"></script>
<script>
var domRecords = document.getElementById("records");
function getLapTimes() {
Util.showModal("Loading Lap Times...");
domRecords.innerHTML = "";
Puck.eval('require("Storage").list(/^swatch.*\.json/).map(fn=>({n:fn,d:require("Storage").readJSON(fn,1)}))',lapData=>{
var html = `<div class="container">
<div class="columns">\n`;
lapData.forEach((lap,lapIndex) => {
lap.date = lap.n.substr(7,16).replace("_"," ");
html += `
<div class="column col-12">
<div class="card-header">
<div class="card-title h5">${lap.date}</div>
<div class="card-subtitle text-gray">${lap.d.length} Laps</div>
</div>
<div class="card-body">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>lap</th>
<th>time</th>
</tr>
</thead>
<tbody>
${ lap.d.map((d,n)=>`<tr><td>${n+1}</td><td>${d}</td></tr>`).join("\n") }
</tbody>
</table>
</div>
<div class="card-footer">
<button class="btn btn-primary" idx="${lapIndex}" task="download">Download</button>
<button class="btn btn-default" idx="${lapIndex}" task="delete">Delete</button>
</div>
</div>
`;
});
if (lapData.length==0) {
html += `
<div class="column col-12">
<div class="card-header">
<div class="card-title h5">No record</div>
<div class="card-subtitle text-gray">No laps recorded</div>
</div>
</div>
`;
}
html += `
</div>
</div>`;
domRecords.innerHTML = html;
Util.hideModal();
var buttons = domRecords.querySelectorAll("button");
for (var i=0;i<buttons.length;i++) {
buttons[i].addEventListener("click",event => {
var button = event.currentTarget;
var lapIndex = parseInt(button.getAttribute("idx"));
var lap = lapData[lapIndex];
if (!lap) throw new Error("Invalid index!");
var task = button.getAttribute("task");
if (task=="delete") {
Util.showModal("Deleting lap time...");
Util.eraseStorage(lap.n,()=>{
Util.hideModal();
getLapTimes();
});
}
if (task=="download") {
Util.saveCSV(lap.n.slice(0,-5)+".csv", lap.d.map((d,n)=>[n+1,d].join(",")).join("\n"));
}
});
}
})
}
function onInit() {
getLapTimes();
}
</script>
</body>
</html>

View File

@ -4,7 +4,6 @@ var started = false;
var timeY = 60; var timeY = 60;
var hsXPos = 0; var hsXPos = 0;
var lapTimes = []; var lapTimes = [];
var saveTimes = [];
var displayInterval; var displayInterval;
function timeToText(t) { function timeToText(t) {
@ -51,10 +50,8 @@ function drawms() {
g.clearRect(hsXPos,timeY,220,timeY+20); g.clearRect(hsXPos,timeY,220,timeY+20);
g.drawString("."+("0"+hs).substr(-2),hsXPos,timeY+10); g.drawString("."+("0"+hs).substr(-2),hsXPos,timeY+10);
} }
function saveconvert() { function getLapTimesArray() {
for (var v in lapTimes){ return lapTimes.map(timeToText).reverse();
saveTimes[v]=v+1+"-"+timeToText(lapTimes[(lapTimes.length-1)-v]);
}
} }
setWatch(function() { // Start/stop setWatch(function() { // Start/stop
@ -80,16 +77,21 @@ setWatch(function() { // Start/stop
}, BTN2, {repeat:true}); }, BTN2, {repeat:true});
setWatch(function() { // Lap setWatch(function() { // Lap
Bangle.beep(); Bangle.beep();
if (started) tCurrent = Date.now(); if (started) {
tCurrent = Date.now();
lapTimes.unshift(tCurrent-tStart); lapTimes.unshift(tCurrent-tStart);
tStart = tCurrent;
if (!started)
{
var timenow= Date();
saveconvert();
require("Storage").writeJSON("StpWch-"+timenow.toString(), saveTimes);
} }
tStart = tCurrent;
if (!started) { // save
var timenow= Date();
var filename = "swatch-"+(new Date()).toISOString().substr(0,16).replace("T","_")+".json";
// this maxes out the 28 char maximum
require("Storage").writeJSON(filename, getLapTimesArray());
E.showMessage("Laps Saved","Stopwatch");
setTimeout(updateLabels, 1000);
} else {
updateLabels(); updateLabels();
}
}, BTN1, {repeat:true}); }, BTN1, {repeat:true});
setWatch(function() { // Reset setWatch(function() { // Reset
if (!started) { if (!started) {

View File

@ -39,7 +39,10 @@ var Util = {
window.postMessage({type:"readstoragefile",data:filename,id:__id}); window.postMessage({type:"readstoragefile",data:filename,id:__id});
}, },
eraseStorageFile : function(filename,callback) { eraseStorageFile : function(filename,callback) {
Puck.write(`\x10require("Storage").open(${JSON.stringify(filename)}","r").erase()\n`,callback); Puck.write(`\x10require("Storage").open(${JSON.stringify(filename)},"r").erase()\n`,callback);
},
eraseStorage : function(filename,callback) {
Puck.write(`\x10require("Storage").erase(${JSON.stringify(filename)})\n`,callback);
}, },
showModal : function(title) { showModal : function(title) {
if (!Util.domModal) { if (!Util.domModal) {
@ -66,6 +69,19 @@ var Util = {
hideModal : function() { hideModal : function() {
if (!Util.domModal) return; if (!Util.domModal) return;
Util.domModal.classList.remove("active"); Util.domModal.classList.remove("active");
},
saveCSV : function(filename, csvData) {
var a = document.createElement("a"),
file = new Blob([csvData], {type: "Comma-separated value file"});
var url = URL.createObjectURL(file);
a.href = url;
a.download = filename+".csv";
document.body.appendChild(a);
a.click();
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
} }
}; };
window.addEventListener("message", function(event) { window.addEventListener("message", function(event) {