You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

260 lines
6.2 KiB

const sqlite3 = require("sqlite3").verbose();
function generateId() {
const buffer = Buffer.alloc(18);
buffer.writeUInt32BE(Date.now() / (2 ** 32), 0);
buffer.writeUInt32BE(Date.now() % (2 ** 32), 4);
for (let i = 8; i < 18; ++i) {
buffer[i] = Math.floor(Math.random() * 255);
}
return buffer.slice(2).toString("hex");
}
class SQLite3Repository {
constructor(path) {
this.db = new sqlite3.Database(path);
}
/**
* @returns {Promise<void>}
*/
setup() {
return new Promise((resolve, reject) => {
this.db.run(SQL_TABLE_BIKE, (err) => {
if (err != null) {
reject(err);
}
this.db.run(SQL_TABLE_PROGRAM, (err) => {
if (err != null) {
reject(err);
}
this.db.run(SQL_TABLE_WORKOUT, (err) => {
if (err != null) {
reject(err);
}
this.db.run(SQL_TABLE_MEASUREMENT, (err) => {
if (err != null) {
reject(err);
}
resolve();
});
});
});
});
});
}
insert(obj, table, params) {
if (!params.includes("id")) {
params.push("id");
obj = {...obj, id: table.charAt(0).toUpperCase() + generateId(0, -1)};
}
let id = obj.id;
return new Promise((resolve, reject) => {
const query = `INSERT INTO ${table} (${params.join(", ")}) VALUES (${params.map(p => ':'+p).join(", ")});`;
const values = params.map(p => obj[p]);
this.db.run(query, values, (err) => {
if (err != null) {
return reject(err);
}
resolve(id);
});
});
}
update(obj, table, key, keys) {
return new Promise((resolve, reject) => {
const query = `UPDATE ${table} SET ${keys.map(k => `${k}=:${k}`).join(" AND ")} WHERE ${key}=:${key}`;
const values = [
...keys.map(k => obj[k]),
obj[key],
];
this.db.run(query, values, function(err) {
if (err != null) {
return reject(err);
}
resolve();
});
});
}
delete(obj, table, key) {
return new Promise((resolve, reject) => {
const query = `DELETE FROM ${table} WHERE ${key}=:${key};`
this.db.run(query, [obj[key]], function(err) {
if (err != null) {
return reject(err);
}
resolve(this.changes);
});
});
}
findOne(table, match) {
return this.find(table, match).then(res => res[0] || null)
}
find(table, match) {
return new Promise((resolve, reject) => {
const keys = Object.keys(match).filter(k => match[k] != null);
const params = keys.map(k => match[k]);
const query = `SELECT * FROM ${table}${keys.length > 0 ? ` WHERE ${keys.map(k => `${k}=:${k}`).join(" AND ")}` : ""};`;
this.db.all(query, params, function(err, rows) {
if (err != null) {
return reject(err);
}
resolve(rows);
});
});
}
findBike(id) {
return this.findOne("bike", {id});
}
listBikes() {
return this.find("bike", {});
}
insertBike(bike) {
return this.insert(bike, "bike", ["name", "driver", "connect", "maxLevel"]);
}
updateBike(bike) {
return this.update(bike, "bike", "id", ["name"]);
}
deleteBike(bike) {
return this.delete(bike, "bike", "id");
}
async findProgram(id) {
const program = await this.findOne("program", {id});
program.level = program.level || 18;
program.warmupLevel = program.warmupLevel || program.level;
program.cooldownCpm = program.cooldownCpm || program.cpm;
program.cooldownLevel = program.cooldownLevel || program.level;
return program;
}
listPrograms() {
return this.find("program", {});
}
insertProgram(program) {
return this.insert(program, "program", ["name", "cpm", "warmupMin", "warmupCpm", "level", "warmupLevel", "cooldownCpm", "cooldownLevel"]);
}
updateProgram(program) {
return this.update(program, "program", "id", ["name"]);
}
deleteProgram(program) {
return this.delete(program, "program", "id");
}
findWorkout(id) {
return this.findOne("workout", {id}).then(d => ({...d, date: new Date(d.date)}));
}
listWorkouts({programId, bikeId} = {}) {
return this.find("workout", {programId, bikeId}).then(l => l.map(d => ({...d, date: new Date(d.date)})));
}
insertWorkout(workout) {
return this.insert(workout, "workout", ["bikeId","programId","date","cooldownMin"]);
}
updateWorkout(workout) {
return this.update(workout, "workout", "id", ["cooldownMin"]);
}
deleteWorkout(workout) {
return this.delete(workout, "workout", "id");
}
findMeasurement(id) {
return this.findOne("measurement", {id});
}
listMeasurements(workoutId) {
return this.find("measurement", {workoutId});
}
insertMeasurement(measurement) {
return this.insert(measurement, "measurement", ["workoutId","minutes","seconds","speed","rpm","distance","calories","pulse","watt","level"]);
}
deleteMeasurement(measurement) {
return this.delete(measurement, "measurement", "id");
}
deleteMeasurements({workoutId}) {
return this.delete({workoutId}, "measurement", "workoutId");
}
}
const SQL_TABLE_BIKE = `
CREATE TABLE IF NOT EXISTS bike (
id CHAR(32) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
driver VARCHAR(255) NOT NULL,
connect VARCHAR(255) NOT NULL,
maxLevel INT
);
`;
const SQL_TABLE_PROGRAM = `
CREATE TABLE IF NOT EXISTS program (
id CHAR(32) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
cpm INT NOT NULL,
level INT NOT NULL,
warmupMin INT NOT NULL,
warmupCpm INT NOT NULL,
warmupLevel INT NOT NULL,
cooldownCpm INT NOT NULL,
cooldownLevel INT NOT NULL
);
`;
const SQL_TABLE_WORKOUT = `
CREATE TABLE IF NOT EXISTS workout (
id CHAR(32) PRIMARY KEY,
bikeId INT NOT NULL,
programId INT NOT NULL,
date DATETIME NOT NULL,
cooldownMin INT NOT NULL
);
`;
const SQL_TABLE_MEASUREMENT = `
CREATE TABLE IF NOT EXISTS measurement (
id CHAR(32) PRIMARY KEY,
workoutId INT NOT NULL,
minutes INT,
seconds INT,
speed REAL,
rpm REAL,
distance REAL,
calories REAL,
pulse INT,
watt REAL,
level INT
);
`;
module.exports = SQLite3Repository;