Compare commits

...

9 Commits

7 changed files with 55 additions and 27 deletions

View File

@ -6,6 +6,6 @@
# Corona time tracker # Corona time tracker
VERSION = (1, 0, 0) VERSION = (1, 1, 0)
__version__ = '.'.join(map(str, VERSION)) __version__ = '.'.join(map(str, VERSION))

View File

@ -1,4 +1,5 @@
import json import json
import copy
from slugify import slugify from slugify import slugify
from threading import Thread, Event from threading import Thread, Event
from datetime import datetime, timedelta from datetime import datetime, timedelta
@ -33,7 +34,7 @@ class Notifier(Thread):
ps = ps[0] ps = ps[0]
print("Sending notification", arrival, ps) print("Sending notification", arrival, ps, self.vapid_creds)
subscription = ps['sub'] subscription = ps['sub']
notification = { notification = {
@ -41,13 +42,15 @@ class Notifier(Thread):
'body': "You didn't sign out of ftracker yet", 'body': "You didn't sign out of ftracker yet",
'arr': arrival 'arr': arrival
} }
privkey = self.vapid_creds['private_key']
claims = copy.copy(self.vapid_creds['claims'])
try: try:
webpush( webpush(
subscription, subscription,
json.dumps(notification), json.dumps(notification),
vapid_private_key = self.vapid_creds['private_key'], vapid_private_key = privkey,
vapid_claims = self.vapid_creds['claims'] vapid_claims = claims
) )
print("Notification sent") print("Notification sent")
return None return None

View File

@ -8,7 +8,7 @@ with open("LICENSE.md", "r") as f:
st.setup( st.setup(
name="ftracker", name="ftracker",
version="1.0.0", version="1.1.0",
author="Oskar @ FaSTTUBe", author="Oskar @ FaSTTUBe",
author_email="o.winkels@fasttube.de", author_email="o.winkels@fasttube.de",
description="Small webapp to track who was in which room at which time to backtrace potential viral infections", description="Small webapp to track who was in which room at which time to backtrace potential viral infections",

View File

@ -12,7 +12,7 @@
'arrival': 'I have read and will adhere to the <a href="/guidelines" target="_blank">protection guidelines</a>', 'arrival': 'I have read and will adhere to the <a href="/guidelines" target="_blank">protection guidelines</a>',
'departure': 'I have cleaned my workspace' 'departure': 'I have cleaned my workspace'
} }
var testCheckBox = '<label class="checkbox"><input type="checkbox" name="tested" id="tested"><span>I have been tested negative for COVID in the last 24 hours</span></label>' var testCheckBox = '<label class="checkbox"><input type="checkbox" name="tested" id="tested"><span>I fullfill one of the <a href="https://www.bundesregierung.de/breg-de/aktuelles/bund-laender-beratung-corona-1949606">3G requirements</a></span></label>'
var editTimeBox = '<label>Departure Date/Time:<input type="datetime-local" name="datetime" id="datetime" required></label>' var editTimeBox = '<label>Departure Date/Time:<input type="datetime-local" name="datetime" id="datetime" required></label>'
function getParams() { function getParams() {
var qparams = document.location.search.substr(1) var qparams = document.location.search.substr(1)
@ -33,27 +33,26 @@
</head> </head>
<body> <body>
<h1><script> <h1><script>
document.write(qp.action ? (qp.action + "<br>Room " + qp.room) : 'FTracker<br>V1') document.write(qp.action ? (qp.action + "<br>Room " + qp.room) : 'FTracker<br>V1.1')
</script></h1> </script></h1>
<div id="startpage"> <div id="startpage">
This is a web app to track which people This is a web app to track which people were in the same rooms at
were in the same rooms at which times in order to backtrace which times in order to backtrace potential viral infections.<br><br>
potential viral infections.<br><br> If you've reached this page that either means your're testing
If you've reached this page that either means your're things or something has gone quite wrong with the URL.<br>
testing things or something has gone quite wrong with the\
URL.<br>
In the former case: Yay it works! In the latter you should In the former case: Yay it works! In the latter you should
probably contact an admin or a dev nearby :(<br><br> probably contact an admin or a dev nearby :(<br><br>
Here are a few links for testing:<br> Here are a few links for testing:<br>
<a href="/view">View Data</a>, <a href="/view">View Data</a>,
<a href="/QRgen">Door Sign Generator</a>, <a href="/QRgen">Door Sign Generator</a>,
<a href="/?arrival=42">Test Arrival</a>, <a href="/?arrival=42">Test Arrival</a>,
<a href="/?departure=42">Test Departure</a><br><br> <a href="/?departure=42">Test Departure</a>,
<a href="javascript:localStorage.removeItem('pushsub')">Reset Push Subscription</a><br><br>
&copy; 2020 made by <a target="_blank" href="mailto:&#111;&#46;&#119;&#105;&#110;&#107;&#101;&#108;&#115;&#64;&#102;&#97;&#115;&#116;&#116;&#117;&#98;&#101;&#46;&#100;&#101;">Oskar</a> &copy; 2020 made by <a target="_blank" href="mailto:&#111;&#46;&#119;&#105;&#110;&#107;&#101;&#108;&#115;&#64;&#102;&#97;&#115;&#116;&#116;&#117;&#98;&#101;&#46;&#100;&#101;">Oskar</a>
for <a target="_blank" href="//fasttube.de">FaSTTUBe</a>.<br> for <a target="_blank" href="//fasttube.de">FaSTTUBe</a>.<br>
For source code & licensing see <a href="//git.fasttube.de/FaSTTUBe/ftracker">git repo</a> For source code & licensing see <a href="//git.fasttube.de/FaSTTUBe/ftracker">git repo</a>
</div> </div>
<form id="mainform" style="display: none"> <form id="mainform" action="javascript:void(0);" style="display: none">
<label> <label>
Full Name:<br> Full Name:<br>
<input type="text" name="name" id="name" placeholder="John Doe" required> <input type="text" name="name" id="name" placeholder="John Doe" required>

View File

@ -6,10 +6,17 @@ if (qp.action) {
mform.style.display = 'block' mform.style.display = 'block'
} }
// Prefill the name field if it was successfully entered before // Get the name field if it was successfully entered before
var savedName = localStorage.getItem('name') var savedName = localStorage.getItem('name')
if (savedName && qp) if (qp && qp.name) {
// Forced Admin checkout - prefill qp name and auto-agree
document.getElementById('name').value = qp.name.replace(/-/g, ' ').toUpperCase();
document.getElementById('agree').checked = true
document.getElementById('agree').parentElement.style.display = 'none'
} else if (savedName && qp) {
// Prefill the client's locally saved name
document.getElementById('name').value = savedName document.getElementById('name').value = savedName
}
// 2nd script, server API communication // 2nd script, server API communication
var name, datetime, agreed, tested var name, datetime, agreed, tested
@ -43,7 +50,7 @@ function sendMainData() {
'name': name, 'name': name,
'arrival': datetime, 'arrival': datetime,
'agreetoguidelines': agreed, 'agreetoguidelines': agreed,
'tested': tested 'tested': tested // = 3G
} : } :
{ {
'name': name, 'name': name,
@ -104,7 +111,7 @@ function handleRequestSubmit(e, json) {
var iso = new Date(input).toISOString() var iso = new Date(input).toISOString()
if (e.srcElement.length > 1) if (e.srcElement.length > 1)
tested = e.srcElement[1].checked tested = e.srcElement[1].checked // = 3G
// POST JSON. See docs/API.md // POST JSON. See docs/API.md
var payload = (json.request == 'arrival') ? var payload = (json.request == 'arrival') ?
@ -113,7 +120,7 @@ function handleRequestSubmit(e, json) {
'name': name, 'name': name,
'arrival': iso, 'arrival': iso,
'agreetoguidelines': agreed, 'agreetoguidelines': agreed,
'tested': tested 'tested': tested // = 3G
} : } :
{ {
'name': name, 'name': name,
@ -261,16 +268,18 @@ function registerPush(name, pushServerPublicKey) {
}).then(function(subscription) { }).then(function(subscription) {
console.log("User is subscribed:", subscription); console.log("User is subscribed:", subscription);
var jsonSub = JSON.stringify({
name: name,
sub: subscription
});
fetch('/pushsubscribe', { fetch('/pushsubscribe', {
method: "POST", method: "POST",
headers: {"Content-Type": "application/json"}, headers: {"Content-Type": "application/json"},
body: JSON.stringify({ body: jsonSub
name: name,
sub: subscription
})
}).then(function(res) { }).then(function(res) {
if (res.ok) if (res.ok)
localStorage.setItem('pushsub', subscription); localStorage.setItem('pushsub', jsonSub);
}); });
}); });
}); });

View File

@ -67,9 +67,15 @@ main > section.times, #timeheader {
font-weight: bold; font-weight: bold;
-webkit-text-stroke: .4px #c50e1f; -webkit-text-stroke: .4px #c50e1f;
} }
.times span.tested { /* = 3G */
background: rgb(0,136,0);
}
.times span.implausible { .times span.implausible {
background: linear-gradient(to right, #c50e1f, rgba(197,14,31,0.2) 1000px); background: linear-gradient(to right, #c50e1f, rgba(197,14,31,0.2) 1000px);
} }
.times span.implausible.tested { /* = 3G */
background: linear-gradient(to right, rgb(0,136,0), rgba(0,136,0,0.2) 1000px);
}
.viewheader.row { .viewheader.row {
height: 30px; height: 30px;
background: #ddd !important; background: #ddd !important;

View File

@ -88,6 +88,14 @@ function exportCSV() {
} }
function offerUserCheckout(event) {
var name = event.target.getAttribute('data-name')
var room = event.target.textContent
window.open(`/?departure=${room}&edittime=1&name=${name}`, '_blank').focus();
}
function renderData() { function renderData() {
if (data == null) { if (data == null) {
@ -163,10 +171,13 @@ function renderData() {
block.style.left = arr + 'px' // 1px/min block.style.left = arr + 'px' // 1px/min
block.style.width = Math.max(0,(dur-14)) + 'px' // 1px/min block.style.width = Math.max(0,(dur-14)) + 'px' // 1px/min
if (entry.tested) if (entry.tested)
block.style.background = '#080' block.classList.add('tested') // = 3G
if (dur > 60 * 24) if (dur > 60 * 24) {
block.classList.add('implausible') block.classList.add('implausible')
block.setAttribute('data-name', name)
block.addEventListener('click', offerUserCheckout);
}
row.appendChild(block) row.appendChild(block)