Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
Executable
+985
@@ -0,0 +1,985 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="static/libarys/chartjs/chart.js"></script>
|
||||
<script src="static/libarys/chartjs/zoom-plugin/chartjs-plugin-zoom.js"></script>
|
||||
<link rel="stylesheet" href="static/style.css">
|
||||
<link rel="stylesheet" href="static/libarys/bootstrap/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="static/libarys/leaflet/leaflet.css">
|
||||
<link rel="stylesheet" href="static/libarys/jquery/jquery-ui-1.13.1.min.css">
|
||||
<link rel="icon" type="image/x-icon" href="static/images/favicon/favicon.png">
|
||||
<script src="static/libarys/favicon/favloader.min.js"></script>
|
||||
<script src="static/libarys/favicon/parseGIF.min.js"></script>
|
||||
<script type="text/javascript" src="static/libarys/jquery/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="static/libarys/jquery/jquery-ui-1.13.1.min.js"></script>
|
||||
<script type="application/javascript" src="static/libarys/chartjs/resizeObserver/resizeObserver.js"></script>
|
||||
|
||||
<script>
|
||||
let matched = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
if (matched) {
|
||||
favloader.init({
|
||||
gif: "static/images/favicon/Favicon2ForDarkmode.gif"
|
||||
});
|
||||
} else {
|
||||
favloader.init({
|
||||
gif: "static/images/favicon/Favicon2.gif"
|
||||
});
|
||||
}
|
||||
favloader.start();
|
||||
</script>
|
||||
<title id="title">Noah's Timeline | RME</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main style="margin-bottom: 30px;">
|
||||
<div id="selectexercisediv"></div>
|
||||
<div id="processStatus-container"></div>
|
||||
<div id="info-container"></div>
|
||||
|
||||
<div class="d-flex flex-column flex-shrink-0 ps-3 border-end border-secondary pe-2" style="width: 280px; position: fixed;">
|
||||
<p href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-decoration-none">
|
||||
<svg class="bi me-2" width="40" height="32">
|
||||
<use xlink:href="#bootstrap"></use>
|
||||
</svg>
|
||||
<span class="fs-4">Settings</span>
|
||||
</p>
|
||||
<hr>
|
||||
<ul class="nav nav-pills flex-column mb-auto ">
|
||||
<li>
|
||||
<div class="container text-center">
|
||||
<button class="btn btn-primary mb-2" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggleExternalContent"
|
||||
aria-expanded="false" aria-controls="navbarToggleExternalContent">
|
||||
Database settings
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="row mb-1">
|
||||
<div class="collapse" style="height: 160px;" id="navbarToggleExternalContent">
|
||||
<div class="pt-2">
|
||||
<div class="col"></div>
|
||||
<div class="col-10 mx-auto">
|
||||
<form action="/" method="POST">
|
||||
<div id="" class="row mb-0">
|
||||
<div class="row mt-1 mx-auto">
|
||||
<div class="input-group input-group-sm mb-2">
|
||||
<span class="input-group-text" id="inputGroup-sizing-sm">Host</span>
|
||||
<input type="text" id="host" name="host" title="Host" value="<%- inputs['host'] %>" class="form-control" aria-label="Sizing example input"
|
||||
aria-describedby="inputGroup-sizing-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mx-auto">
|
||||
<div class="input-group input-group-sm mb-2">
|
||||
<span class="input-group-text" id="inputGroup-sizing-sm">Username</span>
|
||||
<input type="text" id="user" name="user" title="Username" value="<%- inputs['user'] %>" class="form-control" aria-label="Sizing example input"
|
||||
aria-describedby="inputGroup-sizing-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mx-auto">
|
||||
<div class="input-group input-group-sm mb-2">
|
||||
<span class="input-group-text" id="inputGroup-sizing-sm">Password</span>
|
||||
<input type="password" id="password" name="password" title="Password for user" value="<%- inputs['password'] %>" class="form-control"
|
||||
aria-label="Sizing example input" aria-describedby="inputGroup-sizing-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mx-auto">
|
||||
<div class="input-group input-group-sm mb-1">
|
||||
<span class="input-group-text" id="inputGroup-sizing-sm">Database</span>
|
||||
<input type="text" id="database" name="database" title="Databasename" value="<%- inputs['database'] %>" class="form-control"
|
||||
aria-label="Sizing example input" aria-describedby="inputGroup-sizing-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div id="inputs">
|
||||
<button id="connect" class="btn btn-primary mt-1" type="submit" title="Connect to database!" name="button_id" value="1">Connect</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<hr>
|
||||
<li>
|
||||
<div id="mb-3">
|
||||
<div id="inputs">
|
||||
<button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target="#toggleEventTypes" aria-expanded="true"
|
||||
aria-controls="toggleEventTypes">Eventtypes</button>
|
||||
<!-- <button class="btn btn-primary " type="submit" id="getAllData" title="Connect to database!" name="button_id" value="3" <%
|
||||
if(inputs[ "getAllData"]) { %> disabled
|
||||
<% } %> >Get all data</button> -->
|
||||
</div>
|
||||
</div>
|
||||
<form action="/" method="POST">
|
||||
<div class="container p-0 show collapse scrollable" id="toggleEventTypes">
|
||||
|
||||
</div>
|
||||
</li>
|
||||
<hr>
|
||||
<li class="nav-item">
|
||||
<div id="allInputs" class="container mb-1">
|
||||
<div id="day-select" class="row container p-0 text-center mx-auto" style="width: 100%">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="inputs">
|
||||
<button id="getdata" class="btn btn-success mt-3" style="width: 80%;" type="submit" title="Gets data from database and shows them!" name="button_id"
|
||||
value="2">Get data</button>
|
||||
</div>
|
||||
|
||||
</li>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="container mx-auto mt-4 window">
|
||||
<div class="row">
|
||||
<div class="col"></div>
|
||||
<div class="col-15">
|
||||
<h1 class="text-center pt-1 mb-2 mt-0">Noah's Timeline</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col"></div>
|
||||
<div class="col-15 mx-2 mt-0" style="max-width: 100%;">
|
||||
<p class="text-muted text-center fs-5 mb-1">
|
||||
<small id="info-waiting">Connect to database</small>
|
||||
</p>
|
||||
<div class="chart-containter">
|
||||
<div class="container ">
|
||||
<canvas id="myChartAll"></canvas>
|
||||
</div>
|
||||
<div id="reset_zoom" class="container text-end">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pb-2">
|
||||
<div class="col">
|
||||
<div class="container" style="width: 80%; margin-right: 15px;">
|
||||
<canvas id="myChartDoughnut"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="container mx-auto" style="padding-top: 25%;">
|
||||
<canvas id="myChartBar"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="row mb-5">
|
||||
<div class="col"></div>
|
||||
<div class="col-10">
|
||||
<div id="map"></div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="container" id="singleCharts">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="static/libarys/leaflet/leaflet.js"></script>
|
||||
<script>
|
||||
|
||||
let progress = { // For progress bar
|
||||
current: 0,
|
||||
max: 0
|
||||
}
|
||||
function updateProgress(current, max){
|
||||
if(current == -1 && max == -1){
|
||||
progress.current = (progress.max - 4);
|
||||
}else{
|
||||
progress.current = current;
|
||||
progress.max = max;
|
||||
}
|
||||
updateProgressbar()
|
||||
}
|
||||
|
||||
function updateProgressbar(){
|
||||
console.log("CURRENT: " + progress.current + "\nMAX: " + progress.max);
|
||||
let greyPortion = progress.max - progress.current;
|
||||
let grey = "";
|
||||
for(let i = 0; i < greyPortion; i++){
|
||||
grey += ", #cecece"
|
||||
}
|
||||
let green = "";
|
||||
for(let i = 0; i < progress.current; i++){
|
||||
green += " #7afe52,"
|
||||
}
|
||||
green += "#10ff00";
|
||||
document.getElementById("processStatus").style.backgroundImage = "linear-gradient(to right," + green + grey + ")";
|
||||
}
|
||||
|
||||
let getDataButton = document.getElementById("getdata"); // Get Data Button
|
||||
getDataButton.addEventListener("click", function(e){ // Eventlistener for Get Data Button
|
||||
e.preventDefault();
|
||||
let eventtypesValues = [];
|
||||
document.querySelectorAll("input[name=eventtypes]:checked").forEach((checkbox) => {
|
||||
eventtypesValues.push(checkbox.value); // Get all checked eventtypes
|
||||
});
|
||||
|
||||
let data = {};
|
||||
data.startDay = document.getElementById("startday-select").value;
|
||||
data.endDay = document.getElementById("endday-select").value;
|
||||
data.eventtypes = eventtypesValues;
|
||||
|
||||
$.ajax({ // Getdata
|
||||
url: "/getdata",
|
||||
type: "GET",
|
||||
data: data,
|
||||
contentType: "application/json",
|
||||
success: function(data){
|
||||
if(data.errorcode == 0){
|
||||
console.log(data);
|
||||
updateCharts(data);
|
||||
clearInterval(processStatusInterval);
|
||||
updateProgress(-1, -1); // -> progress.current = progress.max - 4 // (Loading main linechart, Loading doughnutchart, Loading barchart, Loading all singlecharts)
|
||||
}else{
|
||||
info("error", data.errorcode, data.errorinfo);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
info("error", textStatus, errorThrown);
|
||||
}
|
||||
});
|
||||
document.getElementById("processStatus-container").innerHTML = '<div id="processStatus" class="pt-3 text-center">Waiting for server...</div>'
|
||||
let processStatusElement = document.getElementById("processStatus");
|
||||
let processStatusInterval = setInterval(
|
||||
() => {
|
||||
$.ajax({ // Get currentstatus of server process
|
||||
url: "/processstatus",
|
||||
type: "GET",
|
||||
contentType: "application/json",
|
||||
success: function(data){ // If data successfully comes back
|
||||
if(data.curElement){
|
||||
processStatusElement.innerHTML = "Adding " + data.curElement + "...";
|
||||
}
|
||||
if(data.count.cur > 0){
|
||||
updateProgress(data.count.cur, data.count.max + 4) // + 4 (Loading main linechart, Loading doughnutchart, Loading barchart, Loading all singlecharts)
|
||||
}
|
||||
}
|
||||
})
|
||||
}, 10);
|
||||
});
|
||||
|
||||
let connectButton = document.getElementById("connect");
|
||||
connectButton.addEventListener("click", function(e){
|
||||
e.preventDefault();
|
||||
|
||||
let data = {};
|
||||
data.host = document.getElementById("host").value;
|
||||
data.user = document.getElementById("user").value;
|
||||
data.password = document.getElementById("password").value;
|
||||
data.database = document.getElementById("database").value;
|
||||
|
||||
$.ajax({ // Connect to database
|
||||
url: "/connectdatabase",
|
||||
type: "GET",
|
||||
data: data,
|
||||
contentType: "application/json",
|
||||
success: function(data){
|
||||
if(data.errorcode == 0){ // If no error occurred
|
||||
function getExerciseOptions(options){
|
||||
let str = "";
|
||||
for(let i = 0; i < options.length; i++){
|
||||
str += '<option value="' + options[i].id + '" class="text-center">' +
|
||||
options[i].name + "|" + options[i].id + "|" + options[i].start_time + " - " + options[i].stop_time + '</option>';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
document.getElementById("selectexercisediv").innerHTML =
|
||||
'<div id="exercise" class="p-2 container mx-auto text-center position-fixed">' +
|
||||
'<button id="exercise-close">X</button>' +
|
||||
'<label for="day-select" class="mb-1 px-5 mx-5" style="display: block;">Select an exercise:</label>' +
|
||||
'<select class="btn btn-outline-primary mb-2" name="exercise" id="exercise-select">' +
|
||||
getExerciseOptions(data.allExercises) +
|
||||
'</select>' +
|
||||
'<div class="input-group input-group-sm my-3 px-5">' +
|
||||
'<span class="input-group-text" id="day-range-sm">Day Range</span>' +
|
||||
'<input type="text" id="day-range" class="form-control" aria-label="day-range-sm" aria-describedby="day-range-sm" disabled>' +
|
||||
'</div>' +
|
||||
'<div class="container px-3 my-2">' +
|
||||
'<div id="slider-range"></div>' +
|
||||
'</div>' +
|
||||
'<button class="btn btn-primary d-block mx-auto mt-3" id="selectexercise">Select exercise</button>' +
|
||||
'</div>';
|
||||
|
||||
$("#exercise").draggable();
|
||||
|
||||
document.getElementById("exercise-close").addEventListener("click", (e) => {
|
||||
document.getElementById("selectexercisediv").innerHTML = "";
|
||||
});
|
||||
|
||||
async function getExerciseDays(exercise){ // Get days between start and stop time
|
||||
let days = [];
|
||||
// Converting selected exercise start time to unix timestamp
|
||||
let mintime = new Date(exercise.start_time) * 1;
|
||||
// Converting selected exercise stop time to unix timestamp
|
||||
let maxtime = new Date(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));
|
||||
}
|
||||
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);
|
||||
}
|
||||
return days;
|
||||
}
|
||||
|
||||
let startDaySlider = 0;
|
||||
let endDaySlider = 0;
|
||||
|
||||
let exerciseSelect = document.getElementById("exercise-select");
|
||||
let exercise = data.allExercises.find(el => el.id == exerciseSelect.value); // Get current selected exercise
|
||||
async function slider(){
|
||||
let days = await getExerciseDays(exercise); // Get days between start and stop time
|
||||
$("#slider-range").slider({
|
||||
range: true,
|
||||
min: 0,
|
||||
max: days.length-1,
|
||||
values: [ 0, 1 ],
|
||||
slide: function( event, ui ) {
|
||||
$("#day-range").val( days[ui.values[ 0 ]] + " - " + days[ui.values[ 1 ]]);
|
||||
},
|
||||
stop: function(event, ui){ // When user stops slider:
|
||||
startDaySlider = ui.values[0]; // Save values
|
||||
endDaySlider = ui.values[1];
|
||||
}
|
||||
});
|
||||
$("#day-range").val( days[$("#slider-range").slider("values", 0)] +
|
||||
" - " + days[$("#slider-range").slider("values", 1)] );
|
||||
}
|
||||
slider();
|
||||
|
||||
|
||||
exerciseSelect.addEventListener("change", async (e) =>{ // On user change exercise
|
||||
let exercise = data.allExercises.find(el => el.id == exerciseSelect.value); // Get current selected exercise
|
||||
let days = await getExerciseDays(exercise); // Get days between start and stop time
|
||||
$("#slider-range").slider({
|
||||
range: true,
|
||||
min: 0,
|
||||
max: days.length - 1,
|
||||
values: [ 0, 1 ],
|
||||
slide: function(event, ui) {
|
||||
$("#day-range").val( days[ui.values[ 0 ]] + " - " + days[ui.values[ 1 ]]); // Show date values
|
||||
},
|
||||
stop: function(event, ui){ // When user stops slider:
|
||||
startDaySlider = ui.values[0]; // Save values
|
||||
endDaySlider = ui.values[1];
|
||||
}
|
||||
});
|
||||
$("#day-range").val( days[$("#slider-range").slider("values", 0)] +
|
||||
" - " + days[$("#slider-range").slider("values", 1)] ); // Show date values
|
||||
});
|
||||
|
||||
let selectexerciseButton = document.getElementById("selectexercise");
|
||||
selectexerciseButton.addEventListener("click", function(e){ // On "Select Exercise"-Button click
|
||||
e.preventDefault();
|
||||
let exercise = document.getElementById("exercise-select").value; // Get exercise id
|
||||
document.getElementById("selectexercisediv").innerHTML = ""; // Clear pop up
|
||||
|
||||
let data = {}; // Data object that gets send to server
|
||||
data.startDay = startDaySlider;
|
||||
data.endDay = endDaySlider;
|
||||
data.id = exercise;
|
||||
|
||||
|
||||
let gotData = false;
|
||||
$.ajax({ // Select exercise
|
||||
url: "/selectexercise",
|
||||
type: "GET",
|
||||
data: data,
|
||||
contentType: "application/json",
|
||||
success: function(data){ // If data successfully comes back
|
||||
gotData = true;
|
||||
clearInterval(processStatusInterval);
|
||||
if(data.errorcode == 0){
|
||||
let str =
|
||||
'<div class="row">' +
|
||||
'<div class="checkboxList">' +
|
||||
'<input type="checkbox" name="eventtypes" id="checkbox_all" checked>' +
|
||||
'<label for="checkbox_all" class="ms-1"><b>Select all</b></label>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
let eventtypes = data.inputs.eventtypes;
|
||||
function checkChecked(eventtypeId){ // For checking if the checkbox is checked or not
|
||||
if(data.inputs.selectedEvents.includes(eventtypeId.toString()))
|
||||
return "checked";
|
||||
}
|
||||
for(let i = 0; i < eventtypes.length; i++){ // Checkbox eventtypes
|
||||
let eventtype = eventtypes[i];
|
||||
str +=
|
||||
'<div class="row">' +
|
||||
'<div class="checkboxList">' +
|
||||
'<input type="checkbox" name="eventtypes" id="checkbox_' + i + '" value="' + eventtype.id + '"' + checkChecked(eventtype.id) + '>' +
|
||||
'<label for="checkbox_' + i + '" class="ms-1">' +
|
||||
eventtype.name + ' (id: ' + eventtype.id + ')' +
|
||||
'</label>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
document.getElementById("toggleEventTypes").innerHTML = str;
|
||||
let selectAllCheckbox = document.getElementById("checkbox_all");
|
||||
selectAllCheckbox.addEventListener("click", (e) => {
|
||||
let checkboxes = document.getElementsByName("eventtypes");
|
||||
for(let checkbox of checkboxes){
|
||||
checkbox.checked = selectAllCheckbox.checked;
|
||||
}
|
||||
});
|
||||
str = "";
|
||||
function getDayOptions(end_start_day){ // Day select options (Current start Day, Current end Day)
|
||||
let days = data.inputs.days;
|
||||
let str_options = "";
|
||||
for(let i = 0; i < days.length; i++){ // For every day
|
||||
let selected = "";
|
||||
if(end_start_day == i){
|
||||
selected = "selected"
|
||||
}
|
||||
str_options +=
|
||||
'<option value="' + i + '" class="text-center"' + selected + '>' +
|
||||
days[i] +
|
||||
'</option>'; // All day options
|
||||
}
|
||||
return str_options;
|
||||
}
|
||||
str =
|
||||
'<div class="col p-0 mt-1">' +
|
||||
'<label for="startday-select">Start Day</label>' +
|
||||
'<select class="btn btn-outline-primary p-1" name="startDay" id="startday-select" style="width: 100%; margin-right: 2px;">' +
|
||||
'<option value="0" class="text-center">Start day</option>' +
|
||||
getDayOptions(data.inputs.startDay) +
|
||||
'</select>' +
|
||||
'</div>' +
|
||||
'<div class="col p-0 mt-1">' +
|
||||
'<label for="endday-select">End Day</label>' +
|
||||
'<select class="btn btn-outline-primary p-1" name="endDay" id="endday-select"style="width: 100%; margin-left: 2px;">' +
|
||||
'<option value="0" class="text-center">End day</option>' +
|
||||
getDayOptions(data.inputs.endDay) +
|
||||
'</select>' +
|
||||
'</div>';
|
||||
document.getElementById("day-select").innerHTML = str;
|
||||
updateProgress(-1, -1); // -> progress.current = progress.max - 4 // (Loading main linechart, Loading doughnutchart, Loading barchart, Loading all singlecharts)
|
||||
|
||||
updateCharts(data); // Update all charts with new data
|
||||
}else{
|
||||
info("error", data.errorcode, data.errorinfo);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
info("error", textStatus, errorThrown);
|
||||
}
|
||||
}); // Vlt gradient von rechts nach links in grün, wie weit der process ist...
|
||||
document.getElementById("processStatus-container").innerHTML = '<div id="processStatus" class="pt-3 text-center">Waiting for server...</div>'
|
||||
let processStatusElement = document.getElementById("processStatus");
|
||||
let processStatusInterval = setInterval(
|
||||
() => {
|
||||
$.ajax({ // Get currentstatus of server process
|
||||
url: "/processstatus",
|
||||
type: "GET",
|
||||
contentType: "application/json",
|
||||
success: function(data){ // If data successfully comes back
|
||||
if(data.curElement){
|
||||
processStatusElement.innerHTML = "Adding " + data.curElement + "...";
|
||||
}
|
||||
if(data.count.cur > 0){
|
||||
updateProgress(data.count.cur, data.count.max + 4) // + 4 (Loading main linechart, Loading doughnutchart, Loading barchart, Loading all singlecharts)
|
||||
}
|
||||
}
|
||||
})
|
||||
}, 10);
|
||||
loadingSpinner();
|
||||
});
|
||||
}else{ // If error occurred
|
||||
info("error", data.errorcode, data.errorinfo);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
info("error", textStatus, errorThrown);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ----------========================================================----------
|
||||
// ----< Chartjs >----
|
||||
// ----------========================================================----------
|
||||
|
||||
// Chart infos
|
||||
let charts = {};
|
||||
charts.chartAll = {}; // Main chart
|
||||
charts.doughnutChart = {}; // Pie chart
|
||||
charts.barChart = {}; // Bar chart
|
||||
charts.singleCharts = {}; // All single charts
|
||||
charts.singleCharts.chart = []; // Array for all single charts
|
||||
|
||||
charts.chartAll.init = true; // If chart has to be initialized
|
||||
charts.doughnutChart.init = true; // "
|
||||
charts.barChart.init = true; // "
|
||||
|
||||
function getInitColors(){
|
||||
let opacity = 0.4;
|
||||
const colors = ['rgba(255, 61, 61,' + opacity + ')', 'rgba(255, 145, 61,' + opacity + ')',
|
||||
'rgba(255, 213, 61,' + opacity + ')', 'rgba(232, 255, 61,' + opacity + ')',
|
||||
'rgba(168, 255, 61,' + opacity + ')', 'rgba(93, 255, 61,' + opacity + ')',
|
||||
'rgba(61, 255, 197,' + opacity + ')', 'rgba(61, 236, 255,' + opacity + ')',
|
||||
'rgba(61, 168, 255,' + opacity + ')', 'rgba(61, 67, 255,' + opacity + ')',
|
||||
'rgba(158, 61, 255,' + opacity + ')', 'rgba(236, 61, 255,' + opacity + ')',
|
||||
'rgba(255, 61, 210,' + opacity + ')', 'rgba(255, 61, 152,' + opacity + ')',
|
||||
'rgba(255, 209, 209,' + opacity + ')', 'rgba(255, 239, 186,' + opacity + ')',
|
||||
'rgba(211, 255, 161,' + opacity + ')', 'rgba(161, 255, 210,' + opacity + ')',
|
||||
'rgba(161, 214, 255,' + opacity + ')', 'rgba(255, 161, 255,' + opacity + ')'
|
||||
];
|
||||
const colorsOutline = ['rgba(255, 61, 61, 1)', 'rgba(255, 145, 61, 1)',
|
||||
'rgba(255, 213, 61, 1)', 'rgba(232, 255, 61, 1)',
|
||||
'rgba(168, 255, 61, 1)', 'rgba(93, 255, 61, 1)',
|
||||
'rgba(61, 255, 197, 1)', 'rgba(61, 236, 255, 1)',
|
||||
'rgba(61, 168, 255, 1)', 'rgba(61, 67, 255, 1)',
|
||||
'rgba(158, 61, 255, 1)', 'rgba(236, 61, 255, 1)',
|
||||
'rgba(255, 61, 210, 1)', 'rgba(255, 61, 152, 1)',
|
||||
'rgba(255, 209, 209, 1)', 'rgba(255, 239, 186, 1)',
|
||||
'rgba(211, 255, 161, 1)', 'rgba(161, 255, 210, 1)',
|
||||
'rgba(161, 214, 255, 1)', 'rgba(255, 161, 255, 1)'
|
||||
];
|
||||
return [colors, colorsOutline];
|
||||
}
|
||||
const [colors, colorsOutline] = getInitColors();
|
||||
|
||||
async function updateCharts(allData){ // Initialized/Update all charts with new data
|
||||
let processStatusElement = document.getElementById("processStatus");
|
||||
loadingSpinner(); // Loading spinner (Loading data...)
|
||||
let timeout = 25;
|
||||
setTimeout(() => { // Timeout so progress can load
|
||||
let data = allData.data; // Data
|
||||
let exercise = allData.inputs.exercise; // Exercise informations
|
||||
let timeLabels = allData.time; // Time labels
|
||||
|
||||
/* Update or init charts with data */
|
||||
let lineConfig = getLineChartConfig(data); // Get config for all line charts
|
||||
processStatusElement.innerHTML = "Loading main linechart...";
|
||||
|
||||
setTimeout(() => { // Timeout so progress can load
|
||||
|
||||
myChartAll(timeLabels, getDataset(data), lineConfig); // Update or init main chart
|
||||
updateProgress(progress.current+1, progress.max);
|
||||
let [dataLabels, dataCount] = getDataLables_Count(data);
|
||||
processStatusElement.innerHTML = "Loading doughnutchart...";
|
||||
|
||||
setTimeout(() => { // Timeout so progress can load
|
||||
|
||||
doughnutChart(dataLabels, dataCount); // Update or init doughnut/pie chart
|
||||
updateProgress(progress.current+1, progress.max);
|
||||
processStatusElement.innerHTML = "Loading barchart...";
|
||||
|
||||
setTimeout(() => { // Timeout so progress can load
|
||||
|
||||
barChart(dataLabels, dataCount); // Update or init bar chart
|
||||
updateProgress(progress.current+1, progress.max);
|
||||
processStatusElement.innerHTML = "Loading all singlecharts...";
|
||||
|
||||
setTimeout(() => { // Timeout so progress can load
|
||||
|
||||
singleCharts(timeLabels, data, lineConfig); // Update or init all single charts
|
||||
updateProgress(progress.current+1, progress.max);
|
||||
resetZoomButton();
|
||||
/* Header with exercise information */
|
||||
$("#info-waiting").text(exercise.name + ", " + exercise.id + ", " + exercise.start_time + " - " + exercise.stop_time + " | Enable/disable by clicking event names");
|
||||
|
||||
document.getElementById("processStatus").style.backgroundImage = "linear-gradient(to right, grey, grey)";
|
||||
document.getElementById("processStatus-container").innerHTML = "";
|
||||
|
||||
}, timeout);
|
||||
}, timeout);
|
||||
}, timeout);
|
||||
}, timeout);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
function loadingSpinner(){ // Loading spinner
|
||||
document.getElementById("info-waiting").innerHTML =
|
||||
'<div class="spinner-border" role="status">' +
|
||||
'<span class="sr-only"></span>' +
|
||||
'</div>' +
|
||||
'<div class="container text-center my-auto">' +
|
||||
' Loading data...' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function countEvents(dataArr) { // Sum all numbers in array
|
||||
return dataArr.reduce((partialSum, a) => partialSum + a, 0); // Sum
|
||||
}
|
||||
function getDataLables_Count(data){ // Get all labels and total count of each eventtype
|
||||
let dataLabels = [];
|
||||
let dataCount = [];
|
||||
|
||||
let counter = 0;
|
||||
for (let i in data) {
|
||||
dataLabels[counter] = data[i][1]; // All labels
|
||||
dataCount[counter] = countEvents(data[i][0]); // Total count of event
|
||||
counter++;
|
||||
}
|
||||
return [dataLabels, dataCount];
|
||||
}
|
||||
function getDataset(data){
|
||||
let dataset = [];
|
||||
let counter = 1;
|
||||
for(let i in data){
|
||||
dataset.push({
|
||||
label: data[i][1], // Event Type
|
||||
data: data[i][0], // Event Count
|
||||
backgroundColor: [
|
||||
colors[counter - 1]
|
||||
],
|
||||
borderColor: [
|
||||
colorsOutline[counter - 1]
|
||||
],
|
||||
borderWidth: 1,
|
||||
fill: true
|
||||
});
|
||||
counter++;
|
||||
}
|
||||
return dataset;
|
||||
}
|
||||
function getHighest(data){ // Get highest value
|
||||
let totalHighest = 0;
|
||||
let curHighest = 0;
|
||||
for(let i in data){
|
||||
curHighest = Math.max.apply(null, data[i][0]); // Get current max from data[eventtype]
|
||||
if(curHighest > totalHighest) // If current highest if bigger than total highest
|
||||
totalHighest = curHighest
|
||||
}
|
||||
return totalHighest;
|
||||
}
|
||||
function getLineChartConfig(data){ // Config for all line charts
|
||||
var config = {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: "Number of events",
|
||||
font: {
|
||||
size: 20
|
||||
}
|
||||
},
|
||||
max: getYHeight(getHighest(data)), // Sets the maximum and adds some ontop
|
||||
stepSize: 5
|
||||
},
|
||||
x: {
|
||||
title: {
|
||||
display: true,
|
||||
text: "Time",
|
||||
font: {
|
||||
size: 15
|
||||
}
|
||||
},
|
||||
ticks: {
|
||||
//autoSkip: false, // <- Shows every label
|
||||
maxRotation: 180
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: { // https://www.chartjs.org/chartjs-plugin-zoom/samples/fetch-data.html <-- Zoom und dabei Daten besser auflösen
|
||||
tooltip: true,
|
||||
zoom: {
|
||||
pan: {
|
||||
enabled: true,
|
||||
mode: 'x',
|
||||
modifierKey: 'ctrl'
|
||||
},
|
||||
zoom: {
|
||||
drag: {
|
||||
enabled: true
|
||||
},
|
||||
mode: 'x'
|
||||
}
|
||||
}
|
||||
},
|
||||
animations: {
|
||||
tension: {
|
||||
to: 0.3 // Corners of lines are round
|
||||
}
|
||||
},
|
||||
maintainAspectRatio: false
|
||||
};
|
||||
return config;
|
||||
}
|
||||
function getYHeight(highestValue) { // Get Y height for charts
|
||||
if (highestValue == 0) {
|
||||
return 1;
|
||||
}
|
||||
for (let i = highestValue; i < highestValue + 200; i++) {
|
||||
if (i % 100 == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return highestValue + 1;
|
||||
}
|
||||
function getMaxValue(arr) { // Get max value for bar chart
|
||||
return Math.max.apply(null, arr);
|
||||
}
|
||||
|
||||
async function myChartAll(timelabel, dataset, config){ // Main big chart
|
||||
if(charts.chartAll.init){
|
||||
const ctx = document.getElementById('myChartAll').getContext('2d');
|
||||
let myChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: timelabel,
|
||||
datasets: dataset
|
||||
},
|
||||
options: config
|
||||
});
|
||||
charts.chartAll.chart = myChart;
|
||||
charts.chartAll.init = false;
|
||||
}else{
|
||||
const chart = charts.chartAll.chart;
|
||||
chart.data.labels = timelabel;
|
||||
chart.data.datasets = dataset;
|
||||
chart.options = config;
|
||||
chart.update();
|
||||
}
|
||||
}
|
||||
async function doughnutChart(datalabels, datacount){ // Round pie chart
|
||||
if(charts.doughnutChart.init){
|
||||
const ctx = document.getElementById("myChartDoughnut").getContext("2d");
|
||||
const myChartDonut = new Chart(ctx, {
|
||||
type: "pie",
|
||||
data: {
|
||||
labels: datalabels,
|
||||
datasets: [{
|
||||
label: "Total Event Count",
|
||||
data: datacount,
|
||||
backgroundColor: colors,
|
||||
borderColor: colorsOutline,
|
||||
borderWidth: 1,
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true
|
||||
}
|
||||
});
|
||||
charts.doughnutChart.init = false;
|
||||
charts.doughnutChart.chart = myChartDonut;
|
||||
}else{
|
||||
let chart = charts.doughnutChart.chart;
|
||||
chart.data = {
|
||||
labels: datalabels,
|
||||
datasets: [{
|
||||
label: "Total Event Count",
|
||||
data: datacount,
|
||||
backgroundColor: colors,
|
||||
borderColor: colorsOutline,
|
||||
borderWidth: 1,
|
||||
}]
|
||||
};
|
||||
chart.update();
|
||||
}
|
||||
}
|
||||
async function barChart(datalabels, datacount){ // Bar chart
|
||||
if(charts.barChart.init){
|
||||
const ctx3 = document.getElementById("myChartBar").getContext("2d");
|
||||
const myChartBar = new Chart(ctx3, {
|
||||
type: "bar",
|
||||
data: {
|
||||
labels: datalabels,
|
||||
datasets: [{
|
||||
label: "Total events for event type",
|
||||
data: datacount,
|
||||
backgroundColor: colors,
|
||||
borderColor: colorsOutline,
|
||||
borderWidth: 1,
|
||||
borderRadius: 5,
|
||||
borderSkipped: false
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: "Number of events",
|
||||
font: {
|
||||
size: 15
|
||||
}
|
||||
},
|
||||
max: getYHeight(getMaxValue(datacount)), // sets the maximum and adds some ontop
|
||||
stepSize: 5
|
||||
},
|
||||
maintainAspectRatio: false,
|
||||
responsive: true
|
||||
}
|
||||
}
|
||||
});
|
||||
charts.barChart.init = false;
|
||||
charts.barChart.chart = myChartBar;
|
||||
}else{
|
||||
const chart = charts.barChart.chart;
|
||||
chart.data = {
|
||||
labels: datalabels,
|
||||
datasets: [{
|
||||
label: "Total events for event type",
|
||||
data: datacount,
|
||||
backgroundColor: colors,
|
||||
borderColor: colorsOutline,
|
||||
borderWidth: 1,
|
||||
borderRadius: 5,
|
||||
borderSkipped: false
|
||||
}]
|
||||
}
|
||||
chart.options.scales.y.max = getYHeight(getMaxValue(datacount));
|
||||
chart.update();
|
||||
}
|
||||
}
|
||||
async function singleCharts(timelabel, data, config){ // All single charts
|
||||
let str = "";
|
||||
let singleCharts = document.getElementById("singleCharts");
|
||||
for(let i = 0; i < Object.keys(data).length; i+=2){
|
||||
str +=
|
||||
'<div class="row pb-2">' +
|
||||
'<div class="col"></div>' +
|
||||
'<div class="col-5 ms-2 me-1 ">' +
|
||||
'<canvas id="myChart' + (i+1) + '"></canvas>' +
|
||||
'</div>' +
|
||||
'<div class="col-5 ms-1 me-2 ">' +
|
||||
'<canvas id="myChart' + (i+2) + '"></canvas>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
singleCharts.innerHTML = str;
|
||||
|
||||
let counter = 0;
|
||||
for (let i in data) {
|
||||
const ctx = document.getElementById('myChart' + (counter+1)).getContext('2d');
|
||||
const myChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: timelabel, // Timestamp
|
||||
datasets: [{
|
||||
label: data[i][1], // Event Type
|
||||
data: data[i][0], // Event Count
|
||||
backgroundColor: [
|
||||
colors[counter]
|
||||
],
|
||||
borderColor: [
|
||||
colorsOutline[counter]
|
||||
],
|
||||
borderWidth: 1,
|
||||
fill: true
|
||||
}]
|
||||
},
|
||||
options: config
|
||||
});
|
||||
charts.singleCharts.chart[counter] = myChart;
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
async function map(){
|
||||
var map = L.map('map', {
|
||||
crs: L.CRS.Simple,
|
||||
minZoom: -5,
|
||||
});
|
||||
// horiz verti
|
||||
// top-right : 52.95249, 10.40041 (1000, 1000)
|
||||
// bottom-left : 52.82840, 10.21173 (0, 0)
|
||||
// - 0.12409, 0.18868
|
||||
var bounds = [[52.82840, 10.21173], [52.95249, 10.40041]];
|
||||
var image = L.imageOverlay("static/map/unterluess1.jpeg", bounds).addTo(map);
|
||||
map.fitBounds(bounds);
|
||||
|
||||
// 52.91522, 10.28567
|
||||
var guez = L.latLng([52.91522, 10.28567]);
|
||||
L.marker(guez).addTo(map);
|
||||
|
||||
/*for (let i = 1; i <= Object.keys(data).length; i++) {
|
||||
var polygon = L.polygon([
|
||||
data[i][2][0],
|
||||
data[i][2][1],
|
||||
data[i][2][2],
|
||||
data[i][2][3]
|
||||
]).addTo(map);
|
||||
}*/
|
||||
}
|
||||
|
||||
async function info(type, code, text){ // If user should get an information (Error, Info)
|
||||
/*
|
||||
(type) => "error", "info", "clear".
|
||||
(code) => Distinct number.
|
||||
(text) => Short text that should help with error or short information text.
|
||||
|
||||
TYPES:
|
||||
- error => Red-Orange error message.
|
||||
- info => Green info message.
|
||||
- clear => "Deletes" the info popup.
|
||||
*/
|
||||
let container = document.getElementById("info-container");
|
||||
if(type == "error"){ // Error
|
||||
container.innerHTML =
|
||||
'<div id="info" class="error p-2 container mx-auto text-center position-fixed">' +
|
||||
'<p>Erorr-' + code + '</p>' +
|
||||
'<p>' + text + '</p>' +
|
||||
'</div>';
|
||||
setTimeout(() => { // After 5000ms error slides to right
|
||||
info("clear");
|
||||
}, 5000);
|
||||
|
||||
}else if(type == "info"){ // Info
|
||||
container.innerHTML =
|
||||
'<div id="info" class="info p-2 container mx-auto text-center position-fixed">' +
|
||||
'<p>Info-' + code + '</p>' +
|
||||
'<p>' + text + '</p>' +
|
||||
'</div>';
|
||||
setTimeout(() => { // After 5000ms info slides to right
|
||||
info("clear");
|
||||
}, 5000);
|
||||
|
||||
}else if(type == "clear"){ // Clear
|
||||
if(document.getElementById("info")){
|
||||
document.getElementById("info").classList.add("slideToRight");
|
||||
setTimeout(() => {
|
||||
container.innerHTML = "";
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
container.addEventListener("mousewheel", (e) => { // If user clicks on info popup (mousewheel click)
|
||||
info("clear");
|
||||
});
|
||||
container.addEventListener("mousedown", (e) => { // If user clicks on info popup (mouse click)
|
||||
info("clear");
|
||||
});
|
||||
}
|
||||
async function resetZoomButton(){ // Reset zoom button
|
||||
document.getElementById("reset_zoom").innerHTML = '<button id="reset_zoom_btn" class="btn btn-secondary ">Reset Zoom</button>';
|
||||
document.getElementById("reset_zoom_btn").addEventListener("click", (e) => { // If button is clicked
|
||||
charts.chartAll.chart.resetZoom();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
<script src="static/libarys/bootstrap/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in new issue
Block a user