<html lang="de">
<head>
<title>EGENS Tool</title>
<!-- Meta -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Parser for egens-CSV files">
<meta name="author" content="David Mayer">
<link rel="shortcut icon" href="favicon.png">
<!-- Boostrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Boostrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
<!-- Boostrap 5 JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js"></script>
<!-- Papa Parser JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.2/papaparse.min.js"></script>
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.6.1.min.js" integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"></script>
<!-- DataTables Lib -->
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/bs5/jszip-2.5.0/dt-1.13.1/b-2.3.3/b-html5-2.3.3/date-1.2.0/r-2.4.0/sl-1.5.0/datatables.min.css"/>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.36/pdfmake.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.36/vfs_fonts.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/v/bs5/jszip-2.5.0/dt-1.13.1/b-2.3.3/b-html5-2.3.3/date-1.2.0/r-2.4.0/sl-1.5.0/datatables.min.js"></script>
<!-- DataTable Plugins -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.4/moment.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/plug-ins/1.13.1/sorting/datetime-moment.js"></script>
<!-- Chart.JS -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body class="p-3 overflow-hidden">
<div id="spinner" class="w-100 h-100 position-absolute justify-content-center align-items-center bg-white" style="z-index: 999; top: 0; left: 0;">
<div class="spinner-border text-primary" style="width: 4rem; height: 4rem; font-size: 1.5rem;" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<div class="modal fade" id="infoModal" tabindex="-1" aria-labelledby="infoModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="modalLabel">Lädt...</h1>
<button id="closeInfoModal1" type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body pb-4 d-flex align-items-center justify-content-center">
<div id="infoModalSpin" class="spinner-border text-primary" style="width: 2rem; height: 2rem; font-size: 1rem;" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p id="infoModalCont" class="text-start p-0 m-0"></p>
</div>
</div>
</div>
</div>
<div class="container bg-white rounded shadow p-4" style="z-index: 0;">
<ul class="nav nav-pills nav-fill" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="profile-tab" data-bs-toggle="tab" data-bs-target="#profile-tab-pane" type="button" role="tab" aria-controls="profile-tab-pane" aria-selected="true">Übersicht</a>
</li>
<li class="nav-item">
<a class="nav-link" id="stats-tab" data-bs-toggle="tab" data-bs-target="#stats-tab-pane" type="button" role="tab" aria-controls="stats-tab-pane" aria-selected="false">Stats</a>
</li>
<li class="nav-item">
<a class="nav-link" id="home-tab" data-bs-toggle="tab" data-bs-target="#home-tab-pane" type="button" role="tab" aria-controls="home-tab-pane" aria-selected="false">Upload</a>
</li>
</ul><!--//Nav Bar-->
<div class="tab-content pt-5" id="myTabContent">
<div class="tab-pane fade show active" id="profile-tab-pane" role="tabpanel" aria-labelledby="profile-tab" tabindex="0">
<div>
<div class="d-flex flex-row align-items-center justify-content-end gap-2 pb-2 ms-auto" style="max-width: 40rem;">
Zeitraum wählen:
<input class="form-control form-control-sm w-25" id="min" type="text" placeholder="Start Datum" style="z-index: 998;">
<input class="form-control form-control-sm w-25" id="max" type="text" placeholder="End Datum" style="z-index: 998;">
</div>
<table id="resultTable" class="table table-striped w-100">
</table>
</div>
</div><!--//Tab-Panel Overview-->
<div class="tab-pane fade" id="stats-tab-pane" role="tabpanel" aria-labelledby="stats-tab" tabindex="0">
<div class="container text-center p-0 d-flex flex-row align-items-start">
<div class="w-50">
<canvas id="myChart"></canvas>
</div>
<div class="statsTable text-start w-50 d-flex justify-content-center">
<div class="w-75">
<div class="row">
<div class="col text-center pb-3">
<h1>EGENS Zahlen</h1>
</div>
</div>
<div class="row border-bottom">
<div class="col fw-bold">
<p>Durchläufe Gesamt:</p>
</div>
<div class="col text-center">
<p></p>
</div>
</div>
<div class="row border-bottom">
<div class="col fw-bold">
<p>Negative Tests:</p>
</div>
<div class="col text-center">
<p style="color: rgb(69, 90, 100)"></p>
</div>
</div>
<div class="row border-bottom">
<div class="col fw-bold">
<p>Positive Tests:</p>
</div>
<div class="col text-center">
<p style="color: rgb(75, 108, 129)"></p>
</div>
</div>
<div class="row border-bottom">
<div class="col fw-bold">
<p>NC:</p>
</div>
<div class="col text-center">
<p style="color: rgb(81, 126, 158)"></p>
</div>
</div>
<div class="row border-bottom">
<div class="col fw-bold">
<p>PC:</p>
</div>
<div class="col text-center">
<p style="color: rgb(88, 145, 188)"></p>
</div>
</div>
<div class="row border-bottom">
<div class="col fw-bold">
<p>Invalide Tests:</p>
</div>
<div class="col text-center">
<p style="color: rgb(94, 163, 217)"></p>
</div>
</div>
<div class="row border-bottom">
<div class="col fw-bold">
<p>Invalide NC:</p>
</div>
<div class="col text-center">
<p style="color: rgb(100, 181, 246)"></p>
</div>
</div>
<div class="row border-bottom">
<div class="col fw-bold">
<p>Invalide PC:</p>
</div>
<div class="col text-center">
<p style="color: rgb(114, 200, 246)"></p>
</div>
</div>
<div class="row ">
<div class="col fw-bold">
<p>Retests:</p>
</div>
<div class="col text-center">
<p></p>
</div>
</div>
</div>
</div>
</div>
</div><!--//Tab-Panel Statistics-->
<div class="tab-pane fade" id="home-tab-pane" role="tabpanel" aria-labelledby="home-tab" tabindex="0">
<div class="d-flex flex-column">
<form id="uploadForm" class="w-100 h-100 d-flex flex-column justify-content-start align-items-center mx-auto needs-validation" style="max-width: 40rem;" novalidate>
<div class="w-100 mb-4">
<label class="form-label" for="formCSV">EGENS-CSV Datei</label>
<input class="form-control" id="formCSV" type="file" name="files" accept=".csv" required>
<div class="invalid-feedback">
Bitte wähle eine .CSV Datei aus.
</div><!--//Invalid val response-->
</div><!--//File Upload Wrapper-->
<div class="w-100 mb-5">
<label class="form-label" for="locationSelect">Stations Standort</label>
<select class="form-select " id="locationSelect" aria-label=".form-select" required>
<option selected disabled value="">Wähle deinen Standort aus</option>
<option value="1">Berlin, alle Stationen</option>
<option value="2">Leipzig Airport (LEJ)</option>
<option value="3">München, Hauptbahnhof</option>
</select>
<div class="invalid-feedback">
Bitte wähle deinen Standort aus.
</div><!--//Invalid val response-->
</div><!--//Dropdown Wrapper-->
<button id="uploadBtn" class="btn btn-primary w-50 rounded-pill mt-4" type="submit">
Hochladen <span><i class="bi bi-cloud-upload"></i></span>
</button><!--//Submit form-->
</form><!--//Upload Form-->
</div>
</div><!--//Tab-Panel Upload-->
</div><!--//Tab-Content Wrapper-->
</div><!--//Container-sm-->
</body>
</html>
html, body {height: 100%;}
.logo {width: 100%; max-width: 2.8rem;}
.btn-primary {
display: flex;
align-items: center;
justify-content: center;
gap: .6rem;
}
.btn-primary span{font-size: 1.2rem;}
.statsTable .col {margin-top: 1rem;}
#resultTable_info {padding: 0;}
#spinner {display: flex;}
//var SheetArr = JSON.parse(<?= sheetData ?>);
//var SheetStats = JSON.parse(<?= sheetStats ?>);
//Papa Parse
var data;
var resArray;
function handleFileSelect(evt) {
var file = evt.target.files[0];
Papa.parse(file, {
header: false,
dynamicTyping: false,
delimiter: "",
delimitersToGuess: [',', '\n', ';', Papa.RECORD_SEP, Papa.UNIT_SEP],
complete: function(results) {
data = results;
resArray = results.data;
resArray.shift();
resArray.pop();
console.log(resArray);
}
});
}
$(document).ready(function(){
$("#formCSV").change(handleFileSelect);
$('a[data-toggle="tab"]').on('shown.bs.tab', function(e){
$($.fn.dataTable.tables(true)).DataTable()
.columns.adjust();
});
});
$(window).on('load', function () {
$('#spinner').fadeOut(400);
})
// DataTable setup
var minDate, maxDate;
$.fn.dataTable.ext.search.push(
function( settings, data, dataIndex ) {
var min = minDate.val();
var max = maxDate.val();
var date = new Date( data[9] );
if (
( min === null && max === null ) ||
( min === null && date <= max ) ||
( min <= date && max === null ) ||
( min <= date && date <= max )
) {
return true;
}
return false;
}
);
$(document).ready(function () {
minDate = new DateTime($('#min'), {
format: 'YYYY-MM-DD HH:mm'
});
maxDate = new DateTime($('#max'), {
format: 'YYYY-MM-DD HH:mm'
});
$.fn.dataTable.moment( 'YYYY-MM-DD HH:mm:ss' );
var table = $('#resultTable').DataTable({
scroller: true,
scrollY: '50vh',
scrollX: true,
scrollCollapse: true,
data: SheetArr,
width: '100%',
paging: true,
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.1/i18n/de-DE.json'
},
order: [[9, 'desc']],
dom:'<<"d-flex flex-row align-items-center justify-content-between pb-2" <"d-flex flex-row align-items-center gap-3" Bl> <"d-flex flex-row align-items-center gap-3" f <"refresh">> > <t> <"d-flex flex-row align-items-start justify-content-between pt-3" i p>>',
lengthMenu: [ 10, 25, 50, 100 ],
fnInitComplete: function(){
$('div.refresh').html('<a class="btn btn-light" href="https://admin.coronatest.de/pending-egens-tool" target=\"_top\">Refresh <i class="bi bi-arrow-clockwise"></i></a>');
},
buttons: [
{ extend: 'csv', text: 'CSV <i class="bi bi-filetype-csv"></i>' },
{ extend: 'excel', text: 'XLSX <i class="bi bi-filetype-xlsx"></i>' },
{ extend: 'pdf', text: 'PDF <i class="bi bi-filetype-pdf"></i>' }
],
columns: [
{
title: 'Seriennummer',
},
{
title: 'Slot',
},
{
title: 'Test',
},
{
title: 'ID',
},
{
title: 'Ergebnis',
},
{
title: 'FAM',
},
{
title: 'VIC',
},
{
title: 'ROX',
},
{
title: 'CY5',
},
{
title: 'Uhrzeit',
},
],
columnDefs: [{
targets: '_all',
defaultContent: "<i class='text-muted' >N/A<i>"
}],
});
// Refilter the table
$('#min, #max').on('change', function () {
table.draw();
});
});
// Custom Bootstrap validation
(() => {
'use strict'
// Fetch form
const forms = document.querySelectorAll('.needs-validation')
// Loop, prevent submission
Array.from(forms).forEach(form => {
form.addEventListener('submit', event => {
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
}
form.classList.add('was-validated')
}, false)
})
})()
//Handle Submit
document.querySelector("#uploadForm").addEventListener("submit",
function(e) {
e.preventDefault();
if ( $('#uploadForm')[0].checkValidity() ) {
$("#infoModal").modal("show");
$('#infoModalSpin').fadeIn(400);
google.script.run.withSuccessHandler(function (value) {
showModal(value);
} ).uploadArray(resArray);
} else {
return
}
});
//Upload Modal
function showModal(value) {
$('#uploadForm').get(0).reset()
$('#infoModalSpin').fadeOut(1);
$("#infoModalCont").fadeIn(400);
$("#modalLabel").text("Hinweis");
$("#infoModalCont").text(value);
};
$("#closeInfoModal1").click(function () {
$("#infoModalCont").fadeOut(400);
$("#modalLabel").text("Lädt...");
});
//Appscripts Reload
function reLoad() {
google.script.run
.withSuccessHandler(function(url){
window.open(url,'_top');
})
.getScriptURL();
}
//Chart.JS
const ctx = document.getElementById('myChart');
new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Negative Tests', 'Positive Tests', 'NC', 'PC', 'Invalide Tests', 'Invalide NC', 'Invalide PC'],
datasets: [{
data: SheetStats,
backgroundColor: ['rgb(69, 90, 100)','rgb(75, 108, 129)','rgb(81, 126, 158)','rgb(88, 145, 188)','rgb(94, 163, 217)','rgb(100, 181, 246)','rgb(114, 200, 246)'],
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'none',
}
}
},
});
You can jump to the latest bin by adding /latest
to your URL
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + [ | Indents selected lines |
ctrl + ] | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Clone Bin |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |