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

985 lines
55 KiB
Plaintext
Executable File

<!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>