Files
qt5-legatus-legacy/Lts/Tools/TimelineOverview/server.js
T
2026-08-13 18:33:10 +02:00

576 lines
22 KiB
JavaScript
Executable File

"use strict";
var path = require("path");
var bodyParser = require("body-parser");
var util = require("util");
var urlencodedParser = bodyParser.urlencoded({ extended: false });
// ----- SERVER
var express = require("express");
var app = express();
const port = 8080;
// ----- DATA
var ejs = require("ejs");
app.set("view engine", "ejs");
var mysql = require("mysql");
var { Client } = require("pg");
var con;
var query;
app.use(express.static(path.join(__dirname, 'views')));
/*
host: "ctcap09",
user: "ets_exm",
password: "",
database: "guez1",
*/
// Hostname: ctcap02
// Username: ets_exm
// Database: guez_20210926
// If the user presses "GetData" button, they get saved here:
let data = {};
let tempData = {};
// If the user presses "GetAllData" button, they get saved here:
let allData = [];
// Timelabels (01:00, 01:30, 02:00, ...) get saved here:
let time = [];
// Inputfields get saved here:
let inputs = {
// Database settings
host: "ctcap09",
user: "ets_exm",
password: "",
database: "guez1",
startDay: 1,
endDay: 1,
selectedEvents: [],
eventtypes: [],
exercise: {},
};
let processStatus = "";
let progressCount = {
cur: 0,
max: 0
};
// All Exercises get saved here:
let allExercises = [];
let exerciseTable = "";
// All Eventtypes get saved here:
let allEventtypes = [];
// All distinct eventtypes on selected exercise
let exerciseEventtypes = [];
// Days that the user can select get saved here:
let days = ["YYYY-MM-DD"];
// If user presses "Connect" button to connect to database. If successfull it gets set to true:
let connectedToDatabase = false;
let selectedExercise = {};
// Error array (should be 0)
let error = 0;
/*
1 = Database settings error
2 =
3 =
4 =
5 =
*/
// Settings that can be changed in /settings
let settings = {};
const color = {
reset: "\x1b[0m",
bright: "\x1b[1m",
dim: "\x1b[2m",
underscore: "\x1b[4m",
blink: "\x1b[5m",
reverse: "\x1b[7m",
hidden: "\x1b[8m",
fg_black: "\x1b[30m",
fg_red: "\x1b[31m",
fg_green: "\x1b[32m",
fg_yellow: "\x1b[33m",
fg_blue: "\x1b[34m",
fg_magenta: "\x1b[35m",
fg_cyan: "\x1b[36m",
fg_white: "\x1b[37m",
bg_black: "\x1b[40m",
bg_red: "\x1b[41m",
bg_green: "\x1b[42m",
bg_yellow: "\x1b[43m",
bg_blue: "\x1b[44m",
bg_magenta: "\x1b[45m",
bg_cyan: "\x1b[46m",
bg_white: "\x1b[47m"
}
function loadSiteFunc(siteRenderer){
loadSite(siteRenderer, data, time, inputs, days, allExercises, error);
}
async function loadSite(siteRenderer, dataSet, timeSet, inputsSet, daysSet, allExercisesSet, errorNum) {
siteRenderer.render("index", {
data: dataSet,
time: timeSet,
inputs: inputsSet,
days: daysSet,
allExercises: allExercisesSet,
exerciseEventtypes: exerciseEventtypes,
error: errorNum
});
}
async function getExercises() {
try {
let allExercises = [];
const result = await query("SELECT * FROM exm.exercise ORDER BY start_time DESC");
for (let i = 0; i < result.rows.length; i++) {
allExercises[i] = {};
allExercises[i]["name"] = result.rows[i].name;
if(result.rows[i].start_time == 0){
allExercises[i]["start_time"] = await getStringDay(result.rows[i].begin_time);
}else{
allExercises[i]["start_time"] = await getStringDay(result.rows[i].start_time);
}
if(result.rows[i].stop_time == 0){
allExercises[i]["stop_time"] = await getStringDay(0)
}else{
allExercises[i]["stop_time"] = await getStringDay(result.rows[i].stop_time);
}
allExercises[i]["id"] = result.rows[i].id;
}
return allExercises;
//loadSite(siteRenderer, {}, time, inputs, days, allExercises, error);
} catch (err) {
errOutput(err);
return err
}
}
async function getExerciseDays() {
days = [];
// Converting selected exercise start time to unix timestamp
let mintime = new Date(inputs.exercise.start_time) * 1;
// Converting selected exercise stop time to unix timestamp
let maxtime = new Date(inputs.exercise.stop_time) * 1;
// Calculate time difference
let timeDifference = maxtime - mintime;
// Calculate day difference from time difference
let dayDifference = Math.floor(timeDifference / (1000 * 3600 * 24));
days[0] = await getStringDay(mintime);
// Filling the day difference with days
for (let i = 0; i < dayDifference ; i++) {
let day = new Date(days[i])
day.setDate(day.getDate() + 1);
// Converting Unixtimestamp to readable format (YYYY-MM-DD) and inserting it
days[i + 1] = await getStringDay(parseInt(day * 1));
}
return days;
}
async function getData(startTime, endTime, dayDif) {
try {
// Gets correct timelabels for x axis
time = [];
// 1 Day: 00:00 - 24:00, 2 Days: 00:00 - 24:00 - 00:00 - 24:00, ...
for (let i = 0; i < dayDif; i++) {
time = time.concat(await getTimeInSteps(startTime, endTime, 23, i));
}
data = {};
// For every eventtype in exercise
progressCount.max = inputs.eventtypes.length;
for (const el of inputs.eventtypes) {
if(inputs.selectedEvents.includes(el.id.toString())){
processStatus = el.name; // For showing what the server does on the client
progressCount.cur++; // For showing what the server does on the client
process.stdout.clearLine(0);
process.stdout.cursorTo(0);
process.stdout.write("Adding \"" + el.name + "\" to dataset..."); // For showing what the server does on the server
data[el.id] = [[], el.name];
// Init data set to zero
for (let i = 0; i < 24 * dayDif; i++) {
data[el.id][0][i] = 0;
}
// Get start and end day from user inputs
let startDay = new Date(days[parseInt(inputs.startDay)]);
let endDay = new Date(days[parseInt(inputs.endDay)]);
// Convert to unixtimestamp
startDay *= 1;
endDay = (endDay * 1) + 86400000; // Plus 24 hours
// Sql query to database. Every sample_time from the selected exercise table where the event type is el.id and the sample time between start and endday from user inputs
const res2 = await query("SELECT sample_time FROM " + exerciseTable + " WHERE event_type_id = " + el.id + " AND sample_time BETWEEN " + startDay + " AND " + endDay);
// For every row from the query result
for (const row of res2.rows) {
let date = new Date(parseInt(row.sample_time)); // Converts current sample_time to javascript date
data[el.id][0][date.getHours() + (24 * Math.floor(Math.abs(date - startDay) / (1000 * 60 * 60 * 24)))]++;
/*
* date.getHours() - gives a value between 0 and 23
*
* Math.floor(Math.abs(date - startDay) / (1000 * 60 * 60 * 24)) - gives the day difference between the start day and the currentday
*
* The day difference is then multiplied by 24 to give the correct index in the data array
*
* Datenstruktur:
* data = {event_type.id: [[0,0,0,0,0,0,0,0,0,0,0,0,...], event_type.name, [latitude, longitude]], ...}
* 24 zeros, every zero stands for a time (first zero = 0:00, second = 1:00,...), if an
* event is found at 1:00 that zero is incremented to 1, if another event is found the 1 is incremented to 2.
* This is done for every zero on every event_type.
*/
}
}
}
process.stdout.clearLine(0);
process.stdout.cursorTo(0);
successOutput("Filled \"dataset\" with data!");
successOutput("Sending data to \"index.ejs\"...");
progressCount.cur = 0;
progressCount.max = 0;
return data;
} catch (err) {
console.log(err);
errOutput(err);
data.errorcode = 600;
data.errorinfo = "Something went wrong while the Server processed the data! Please try again later!"
return data;
}
}
app.get("/", (req, res2) => {
//sendFile(__dirname + "/views/index.html") // -> input field sind nicht richtig
loadSiteFunc(res2);
//loadSite(res2, data, time, inputs, days, allExercises, error);
});
app.get("/settings", (req, res) => {
res.render("settings", {
settings: settings,
error: error
});
})
app.get("/processstatus", async (req, res) => {
let data = {
curElement: processStatus,
count: {
cur: parseInt(progressCount.cur),
max: parseInt(progressCount.max)
}
}
res.send(data);
processStatus = "";
});
app.get("/connectdatabase", async function(req, res){
let data = {};
if (req.query.host && req.query.user && req.query.database) {
inputs["host"] = req.query.host;
inputs["user"] = req.query.user;
inputs["password"] = req.query.password;
inputs["database"] = req.query.database;
// Create connection from user inputs
con = new Client({
user: inputs["user"],
host: inputs["host"],
database: inputs["database"],
password: inputs["password"]
});
// Check connection to database
infoOutput("Connecting to \"" + inputs["database"] + "\"...")
await con.connect()
.then(async () => {
successOutput("Connected to new Database (\"" + inputs["database"] + "\")!");
query = util.promisify(con.query).bind(con);
data.errorcode = 0;
data.allExercises = await getExercises();
allExercises = data.allExercises; // Save all Exercises
})
.catch((err) => {
console.log(err);
if(err.code == "ENOTFOUND"){ // Hostname wrong
data.errorcode = 501;
data.errorinfo = "Hostname (" + err.hostname + ") is incorrect! Please check your inputfields and try again!";
}else if(err.code == "28000"){ // Username wrong
data.errorcode = 502;
data.errorinfo = "Username (" + inputs.user + ") is incorrect! Please check your inputfields and try again!";
}else if(err.code == "WRONG PASSWORD"){ // Not working (Has to be correct err.code)
data.errorcode = 503;
data.errorinfo = "Password is incorrect! Please check your inputfields and try again!";
}else if(err.code == "3D000"){ // Databasename wrong
data.errorcode = 504;
data.errorinfo = "Database (" + inputs.database + ") is incorrect! Please check your inputfields and try again!";
}
errOutput(err);
});
}else{
if(!req.query.host){ // Host inputfield empty
data.errorcode = 505;
data.errorinfo = "Host inputfield is empty! Please try again!";
}else if(!req.query.user){ // User inputfield empty
data.errorcode = 506;
data.errorinfo = "User inputfield is empty! Please try again!";
}else if(!req.query.database){ // Database inputfield empty
data.errorcode = 508;
data.errorinfo = "Database inputfield is empty! Please try again!";
}else{ // Something else
data.errorcode = 500;
data.errorinfo = "Internal server error!";
}
}
// Send data to client
res.send(data);
});
app.get("/selectexercise", async function(req, res){
let data = {};
inputs.startDay = req.query.startDay;
inputs.endDay = req.query.endDay;
inputs.eventtypes = await getEventtypes(req.query.id);
inputs.days = await getExerciseDays();
data.errorcode = 0;
data.data = await getData(0, 23, (inputs.endDay - inputs.startDay)+1);
data.time = time;
data.inputs = inputs;
res.send(data);
});
app.get("/getdata", async function(req, res){
let data = {};
inputs.startDay = req.query.startDay;
inputs.endDay = req.query.endDay;
data.errorcode = 0;
inputs.selectedEvents = req.query.eventtypes;
data.data = await getData(0, 23, (inputs.endDay - inputs.startDay)+1);
data.time = time;
data.inputs = inputs;
res.send(data);
});
app.post("/", urlencodedParser, async function (req, res) {
let button_id = req.body.button_id; // 1 -> Connect, 2 -> Get Data, 3 -> Get All Data, 6 -> Select Exercise
if (button_id == 1) { // Connect Button
if (req.body.host && req.body.user && req.body.database) {
inputs["host"] = req.body.host;
inputs["user"] = req.body.user;
inputs["password"] = req.body.password;
inputs["database"] = req.body.database;
// Create connection from user inputs
con = new Client({
user: inputs["user"],
host: inputs["host"],
database: inputs["database"],
password: inputs["password"]
});
// Check connection to database
infoOutput("Connecting to \"" + inputs["database"] + "\"...")
con.connect()
.then(async () => {
connectedToDatabase = true;
inputs["button_id"] = button_id;
successOutput("Connected to new Database (\"" + inputs["database"] + "\")!");
query = util.promisify(con.query).bind(con);
error = 0;
getExercises(res);
})
.catch((err) => {
connectedToDatabase = false;
errOutput(err);
error = 1;
loadSiteFunc(res);
});
}
}
if (connectedToDatabase) {
if (button_id == 2) { // Get Data Button
let startTime = 0, endTime = 23;
if (req.body.startTime && req.body.endTime) {
startTime = parseInt(req.body.startTime);
endTime = parseInt(req.body.endTime);
// If end time is smaller/earlier than start time
if (endTime < startTime) {
endTime = startTime + 1;
}
} else if (req.body.startTime && !req.body.endTime) {
startTime = parseInt(req.body.startTime);
} else if (!req.body.start && req.body.endTime) {
endTime = parseInt(req.body.endTime);
}
// If user selected eventtypes
if (req.body.eventtypes) {
// Insert them into inputs
inputs["selectedEvents"] = req.body.eventtypes;
} else {
// Insert "random" number so .length is not 0
inputs["selectedEvents"] = ["1", "7"];
}
// Save user inputs into inputs{}
inputs["startTime"] = startTime;
inputs["endTime"] = endTime;
inputs["days"][0] = req.body.startDay;
inputs["days"][1] = req.body.endDay;
inputs["button_id"] = button_id;
// Check if start day is higher than end days
if (inputs["days"][0] > inputs["days"][1]) {
inputs["days"][0] = inputs["days"][1];
}
if (inputs["days"][0] == inputs["days"][1]) {
getData(startTime, endTime, 1, res);
} else {
let dayDif = inputs["days"][1] - inputs["days"][0] + 1;
getData(startTime, 23, dayDif, res);
}
}
if (button_id == 6) { // Select exercise
inputs["button_id"] = button_id;
// Find exercise information
selectedExercise = allExercises.find(el => el.id == req.body.exercise)
selectedExerciseTable = "ex_" + selectedExercise.id + ".eem_event";
inputs["selectedExercise"] = selectedExercise.name + ", " + selectedExercise.id + ", " + selectedExercise.start_time + ", " + selectedExercise.stop_time;
successOutput("Selected exercise: \"" + selectedExercise.name + ", " + selectedExercise.id + ", " + selectedExercise.start_time + ", " + selectedExercise.stop_time + "\"");
// Select all event types
const RESULTeventtypes = await query("SELECT * FROM pdm.pdm_event_type"); // all event types
allEventtypes = RESULTeventtypes.rows;
/*
exerciseEventtypes = allEventtypes;
let count = 0;
for(let el in allEventtypes){
inputs["selectedEvents"][count] = el.toString();
count++;
}*/
// Get all distinct event types from selected exercise
const RESULTexerciseEventtypesid = await query("SELECT DISTINCT event_type_id FROM " + selectedExerciseTable + " ORDER BY event_type_id ASC" ); // all event types in selected exercise
const exerciseEventtypesid = RESULTexerciseEventtypesid.rows;
exerciseEventtypes = [];
for (let i = 0; i < exerciseEventtypesid.length; i++) {
// Activate all checkboxes
inputs["selectedEvents"][i] = parseInt(exerciseEventtypesid[i].event_type_id).toString();
exerciseEventtypes[i] = {};
// Get name via id from allEventtypes
exerciseEventtypes[i] = allEventtypes.find(el => el.id == exerciseEventtypesid[i].event_type_id);
}
// Empty allExercises so pop up window does not show up
allExercises = [];
getExerciseDays(res);
}
}
});
async function getEventtypes(exerciseId){
let eventtypes = [];
// Set exercise table
exerciseTable = "ex_" + exerciseId + ".eem_event";
// Find selected exercise information
let exercise = allExercises.find(el => el.id == exerciseId)
// Save information in inputs
inputs.exercise.name = exercise.name;
inputs.exercise.id = exercise.id;
inputs.exercise.start_time = exercise.start_time;
inputs.exercise.stop_time = exercise.stop_time;
successOutput("Selected exercise: \"" + inputs.exercise.name + ", " + inputs.exercise.id + ", " + inputs.exercise.start_time + ", " + inputs.exercise.stop_time + "\"");
// Select all event types
allEventtypes = (await query("SELECT * FROM pdm.pdm_event_type")).rows;
// Get all distinct event types from selected exercise
const exerciseEventtypesid = (await query("SELECT DISTINCT event_type_id FROM " + exerciseTable + " ORDER BY event_type_id ASC" )).rows;
for (let i = 0; i < exerciseEventtypesid.length; i++) {
// Activate all checkboxes
inputs.selectedEvents[i] = parseInt(exerciseEventtypesid[i].event_type_id).toString();
eventtypes[i] = {};
// Get name via id from allEventtypes
eventtypes[i] = allEventtypes.find(el => el.id == exerciseEventtypesid[i].event_type_id);
}
return eventtypes;
}
async function getTimeInSteps(start, end, steps, curDayIndex) {
let times = []; // Where times are stored
let hour = start;
let startDay = new Date(days[parseInt(inputs["startDay"])])
let dayDate = await getStringDay(startDay.setDate(startDay.getDate() + curDayIndex) * 1);
// First and last entry are the start and end time
times[0] = dayDate + " " +("0" + hour).substr(-2) + ":00";
times[steps] = ("0" + end).substr(-2) + ":00";
let dif = end - start; // Difference between start time and end time
let step = dif / steps * 60; // Calculate steps
let minutes = 0, printMin, printHour;
for (let i = 0; i < steps - 1; i++) {
minutes += step;
if (minutes >= 60) { // If minutes are over or 60 minutes
hour++; // Hour gets incremented
minutes -= 60; // Minutes minus 60
}
printMin = ("0" + Math.trunc(minutes)).substr(-2);
printHour = ("0" + Math.trunc(hour)).substr(-2);
times[i + 1] = printHour + ":" + printMin;
}
return times; // Return times
}
async function getStringDay(InputTime) {
let unixTimestamp = 0;
let seperator = "-";
if (InputTime > 0) {
unixTimestamp = new Date(parseInt(InputTime));
} else {
unixTimestamp = new Date(new Date().getTime())
}
return unixTimestamp.getFullYear() + seperator + ("0" + (unixTimestamp.getMonth() + 1)).substr(-2) + seperator + ("0" + unixTimestamp.getDate()).substr(-2);
}
function infoOutput(message, returnText){
let firstWord = message.split(' ')[0];
let quoteIndex = message.indexOf("\"");
let text = color.fg_yellow + color.bg_black + "Message:" + color.reset + " " + color.fg_green + firstWord + color.fg_green + message.substring(firstWord.length, quoteIndex) +
"\"" +color.fg_white + message.substring(quoteIndex+1, message.indexOf("\"", quoteIndex+1)) + color.fg_green + message.substring(message.indexOf("\"", quoteIndex+1), message.length+1) + color.reset + color.fg_white;
if(returnText)
return text;
console.log(text);
}
function successOutput(message){
let firstWord = message.split(' ')[0];
let quoteIndex = message.indexOf("\"");
console.log(color.fg_white + color.bg_green + "Successfully:" + color.bg_black + " " + color.fg_yellow + firstWord + color.fg_green + message.substring(firstWord.length, quoteIndex) +
"\"" +color.fg_white + message.substring(quoteIndex+1, message.indexOf("\"", quoteIndex+1)) + color.fg_green + message.substring(message.indexOf("\"", quoteIndex+1), message.length+1) + color.reset + color.fg_white);
}
function errOutput(error) {
// Prints error message with red color
console.log(color.bg_red + "ERROR CAUGHT:" + color.bg_black + " " + color.fg_red + error + color.fg_white + color.reset);
}
app.listen(port, () => {
infoOutput("Server: \"http://localhost:" + port + "/\"");
infoOutput("Settings: \"http://localhost:" + port + "/settings\"");
});