Vincent Batts
e32d3c9233
All checks were successful
Basic Checking / Explore-Gitea-Actions (push) Successful in 30s
The tests are currently working on my laptop, but failing on the CI
actions. 🤔
There have always been errors on it inserting animals to the database,
and now seem like it fails to get routes before the async promise is
fufilled? I'm not sure.
Switch everything to the bole logger, and not the `console.`
stuff.
Signed-off-by: Vincent Batts <vbatts@hashbangbash.com>
213 lines
6.2 KiB
JavaScript
213 lines
6.2 KiB
JavaScript
const express = require("express");
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const morgan = require("morgan");
|
|
const bodyParser = require("body-parser");
|
|
const sqlite3 = require("sqlite3").verbose();
|
|
const bole = require('bole');
|
|
const log = bole('app');
|
|
|
|
const config = require('./config');
|
|
const app = express();
|
|
|
|
// Create an SQLite database and initialize tables
|
|
const db = new sqlite3.Database(config.db_path, (err) => {
|
|
if (err) {
|
|
log.error("Error opening SQLite database:", err.message);
|
|
} else {
|
|
log.info("Connected to SQLite database", config.db_path);
|
|
db.run(`
|
|
CREATE TABLE IF NOT EXISTS button_clicks (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
session_id TEXT,
|
|
animal_name TEXT,
|
|
button_name TEXT,
|
|
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
|
time_difference INTEGER -- Add this column for time difference
|
|
)
|
|
`);
|
|
db.run(`
|
|
CREATE TABLE IF NOT EXISTS animals (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL UNIQUE,
|
|
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL
|
|
)
|
|
`);
|
|
}
|
|
});
|
|
|
|
var accessLogStream = fs.createWriteStream(
|
|
path.join(__dirname, "log", "access.log"),
|
|
{ flags: "a" },
|
|
);
|
|
|
|
app.use(bodyParser.json());
|
|
app.use(morgan("combined", { stream: accessLogStream }));
|
|
|
|
var animals;
|
|
// check and load animals into redis
|
|
try {
|
|
fs.readFile("./animals.json", function (err, data) {
|
|
if (err) {
|
|
throw err;
|
|
}
|
|
var jsondata = JSON.parse(data);
|
|
animals = jsondata.animals;
|
|
for (const animal of animals) {
|
|
db.run(`
|
|
INSERT INTO animals(name)
|
|
SELECT ?
|
|
WHERE NOT EXISTS(SELECT 1 FROM animals WHERE name = ?);
|
|
`,
|
|
[animal, animal],
|
|
(err) => {
|
|
if (err) {
|
|
log.error(`Error inserting animal ${animal}: `, err.message);
|
|
} else {
|
|
log.info(`Success inserting animal ${animal}`);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
});
|
|
} catch (error) {
|
|
log.error("Error loading animals:", error);
|
|
animals = ["Dog", "Cat", "Elephant", "Lion", "Giraffe"];
|
|
}
|
|
|
|
// Serve the HTML file
|
|
app.get("/", (req, res) => {
|
|
res.sendFile(__dirname + "/index.html");
|
|
});
|
|
app.get("/asset/frontend.js", (req, res) => {
|
|
res.sendFile(__dirname + "/asset/frontend.js");
|
|
});
|
|
|
|
// Route to get a random animal name
|
|
app.get("/getNextAnimal", async (req, res) => {
|
|
try {
|
|
// TODO this is currently random, and should have a bit of reasoning behind the next choice
|
|
const randomIndex = Math.floor(Math.random() * animals.length);
|
|
const randomAnimal = animals[randomIndex];
|
|
res.json({ animalName: randomAnimal });
|
|
} catch (error) {
|
|
log.error("Error fetching random animal:", error);
|
|
res.status(500).json({ error: "Internal server error" });
|
|
}
|
|
});
|
|
|
|
// Route to record button clicks along with session IDs in SQLite
|
|
app.post("/recordButtonClick", (req, res) => {
|
|
try {
|
|
//const { buttonName, sessionId } = req.body;
|
|
const result = req.body;
|
|
log.error(result);
|
|
|
|
db.run(
|
|
"INSERT INTO button_clicks (session_id, animal_name, button_name, timestamp, time_difference) VALUES (?, ?, ?, ?, ?)",
|
|
[
|
|
result.session,
|
|
result.animal,
|
|
result.button,
|
|
result.time,
|
|
result.difference,
|
|
],
|
|
(err) => {
|
|
if (err) {
|
|
log.error("Error recording button click:", err.message);
|
|
res.status(500).json({ error: "Internal server error" });
|
|
} else {
|
|
res.sendStatus(200);
|
|
}
|
|
},
|
|
);
|
|
} catch (error) {
|
|
log.error("Error recording button click:", error);
|
|
res.status(500).json({ error: "Internal server error" });
|
|
}
|
|
});
|
|
|
|
// Route to show the current results from SQLite
|
|
app.get("/results", async (req, res) => {
|
|
try {
|
|
const results = {
|
|
count: {},
|
|
avgTimes: {},
|
|
};
|
|
const getCount = new Promise((resolve, reject) => {
|
|
db.all(
|
|
`
|
|
SELECT animal_name, button_name, COUNT(*) AS count
|
|
FROM button_clicks
|
|
GROUP BY button_name, animal_name
|
|
`,
|
|
(err, rows) => {
|
|
if (err) {
|
|
reject("getCount: " + err.message);
|
|
} else {
|
|
rows.forEach((row) => {
|
|
if (typeof results.count[row.animal_name] == "undefined") {
|
|
results.count[row.animal_name] = {};
|
|
}
|
|
results.count[row.animal_name][row.button_name] = row.count;
|
|
});
|
|
resolve();
|
|
}
|
|
},
|
|
);
|
|
});
|
|
const getAvgTime = new Promise((resolve, reject) => {
|
|
db.all(
|
|
`
|
|
SELECT animal_name, button_name, AVG(time_difference) AS time
|
|
FROM button_clicks
|
|
GROUP BY animal_name, button_name;
|
|
`,
|
|
(err, rows) => {
|
|
if (err) {
|
|
reject("getAvgTime: " + err.message);
|
|
} else {
|
|
rows.forEach((row) => {
|
|
if (typeof results.avgTimes[row.animal_name] == "undefined") {
|
|
results.avgTimes[row.animal_name] = {};
|
|
}
|
|
results.avgTimes[row.animal_name][row.button_name] = row.time;
|
|
});
|
|
resolve();
|
|
}
|
|
},
|
|
);
|
|
});
|
|
const getTotalAvgTime = new Promise((resolve, reject) => {
|
|
db.all(
|
|
`
|
|
SELECT animal_name, AVG(time_difference) AS time
|
|
FROM button_clicks
|
|
GROUP BY animal_name;
|
|
`,
|
|
(err, rows) => {
|
|
if (err) {
|
|
reject("getTotalAvgTime: " + err.message);
|
|
} else {
|
|
rows.forEach((row) => {
|
|
if (typeof results.avgTimes[row.animal_name] == "undefined") {
|
|
results.avgTimes[row.animal_name] = {};
|
|
}
|
|
results.avgTimes[row.animal_name]["total"] = row.time;
|
|
});
|
|
resolve();
|
|
}
|
|
},
|
|
);
|
|
});
|
|
await Promise.all([getCount, getTotalAvgTime, getAvgTime]);
|
|
res.json(results);
|
|
} catch (error) {
|
|
log.error("Error fetching results:", error);
|
|
res.status(500).json({ error: "Internal server error" });
|
|
}
|
|
});
|
|
|
|
module.exports = app;
|
|
|
|
// vim:set sts=2 sw=2 et:
|