Add web interface for scanning and checking

master
Julius de Jeu 2018-07-12 12:37:58 +02:00
parent f9cac04003
commit baa602ad90
5 changed files with 11845 additions and 4 deletions

66
app.py
View File

@ -1,11 +1,21 @@
from flask import Flask, request, jsonify, make_response
from flask_httpauth import HTTPBasicAuth
from sqlite3 import connect, OperationalError
import bcrypt
app = Flask(__name__)
import bcrypt
from datetime import datetime
from flask import Flask, request, jsonify, make_response, render_template
from flask_httpauth import HTTPBasicAuth
import flask_excel as excel
from dateutil import tz
def utc_to_local(utc_dt):
return utc_dt.replace(tzinfo=tz.tzutc()).astimezone(tz.gettz("Europe/Amsterdam"))
app = Flask(__name__, static_url_path='')
auth = HTTPBasicAuth()
db = connect("database.db", check_same_thread=False)
excel.init_excel(app)
try:
cursor = db.cursor()
@ -23,6 +33,9 @@ except OperationalError:
@auth.verify_password
def verpass(username, password):
print(username)
if username in (None, '') or password in (None, ''):
return False
# return True
cur = db.cursor()
ret = cur.execute('''select password from users where username = ?''', (username,)).fetchone()
@ -58,6 +71,7 @@ def checknum(username, number, checked=0):
@app.route("/ticket", methods=["POST"])
@auth.login_required
def addticket():
print(request.json)
if not request.json or "number" not in request.json or "function" not in request.json:
return make_response(jsonify({"msg": "missing/faulty request body?"}), 400)
fun = request.json["function"]
@ -119,8 +133,52 @@ def control():
return make_response(jsonify({"msg": "Removed!"}), 200)
@app.route("/scan")
@auth.login_required
def scanlink():
return render_template("scan.html", username=auth.username(), func="add", mode="Toevoegen")
@app.route("/check")
@auth.login_required
def checklink():
return render_template("scan.html", username=auth.username(), func="check", mode="Controleren")
# @app.route("/list")
# @auth.login_required
# def tes():
# cur = db.cursor()
# tickets = cur.execute('''select * from tickets where username = ?;''', (auth.username(),)).fetchall()
# cur.close()
# print(tickets)
# lst = list()
# for (ln, num, dt, inside) in tickets:
# lst.append(
# {"number": num,
# "date": datetime.strptime(dt, "%Y-%m-%d %H:%M:%S").astimezone(tz.gettz("Europe/Amsterdam")).strftime(
# "%H:%M:%S %d-%m-%Y"), "inside": inside == 1})
# return jsonify(lst)
@app.route("/excel")
@auth.login_required
def exc():
cur = db.cursor()
tickets = cur.execute('''select * from tickets where username = ?;''', (auth.username(),)).fetchall()
cur.close()
print(tickets)
lst = list()
for (ln, num, dt, inside) in tickets:
lst.append({"number": num, "date": datetime.strptime(dt, "%Y-%m-%d %H:%M:%S").astimezone(
tz.gettz("Europe/Amsterdam")).strftime(
"%H:%M:%S %d-%m-%Y"), "inside": inside == 1})
return excel.make_response_from_records(lst, 'xlsx', file_name="tickets - " + auth.username())
@app.route("/list")
@auth.login_required
def check():
cur = db.cursor()
num1 = cur.execute('''select * from tickets where username = ?;''', (auth.username(),)).fetchall()

29
static/css/style.css Normal file
View File

@ -0,0 +1,29 @@
body {
font-family: 'Roboto', sans-serif;
background-color: dimgrey;
}
#centerdiv {
padding: 10px 10px 10px 10px;
margin: 5px auto;
width: 600px;
text-align: center;
background-color: white;
border-radius: 10px;
}
#centerdiv #derk {
margin: 0 auto;
width: 300px;
text-align: left;
}
.drawingBuffer {
display: none;
}
#mobileCanvas {
padding: 10px 10px 10px 10px;
}

11581
static/libs/quagga.js Normal file

File diff suppressed because one or more lines are too long

1
static/libs/quagga.min.js vendored Normal file

File diff suppressed because one or more lines are too long

172
templates/scan.html Normal file
View File

@ -0,0 +1,172 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=720, initial-scale=1">
<title>Ticket Scanner 2.0</title>
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="/css/style.css">
<script>
var siteWidth = 720;
var scale = screen.width / siteWidth;
document.querySelector('meta[name="viewport"]').setAttribute('content', 'width=' + siteWidth + ', initial-scale=' + scale + '');
function postData(url, data, method) {
// Default options are marked with *
return fetch(url, {
body: JSON.stringify(data), // must match 'Content-Type' header
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, same-origin, *omit
headers: {
'content-type': 'application/json',
},
method: method || 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, cors, *same-origin
redirect: 'follow', // manual, *follow, error
referrer: 'no-referrer', // *client, no-referrer
})// parses response to JSON
}
function users() {
return fetch("/list", {
{# body: JSON.stringify(data), // must match 'Content-Type' header#}
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, same-origin, *omit
{#headers: {
'content-type': 'application/json',
},#}
method: 'GET', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, cors, *same-origin
redirect: 'follow', // manual, *follow, error
referrer: 'no-referrer', // *client, no-referrer
}).then(resp => {
return resp.text()
}).then(data => {
console.log(data);
return data;
})
}
function addUser(username) {
document.getElementById("result").innerText = username;
postData("/ticket", {number: username, function: "{{ func }}"})
.then(function (response) {
if (response.ok) {
document.getElementById("centerdiv").style.backgroundColor = "#00D41D";
setTimeout(function () {
document.getElementById("centerdiv").style.backgroundColor = "";
}, 2500)
} else {
document.getElementById("centerdiv").style.backgroundColor = "#D40000";
setTimeout(function () {
document.getElementById("centerdiv").style.backgroundColor = "";
}, 2500)
}
r = response.json();
return r;
})
.then(function (data) {
console.log(data);
document.getElementById("msg").innerText = data.msg;
})
.catch(function (error) {
console.error(error);
document.getElementById("centerdiv").backgroundColor = "#D40000";
setTimeout(function () {
document.getElementById("centerdiv").style.backgroundColor = "#FFFFFF";
}, 5000)
});
}
setInterval(function () {
users().then(data => {
document.getElementById("vb").innerHTML = data
})
}, 1000);
function addLlnr() {
var x = document.getElementById("inputbox").value;
if (x.toString().length !== 6) {
alert("Een leerlingnummer heeft 6 getallen!");
return
}
if (confirm("Wil je " + x.toString() + " Toevoegen?")) {
addUser(x.toString());
}
document.getElementById("inputbox").value="";
}
function checkval(evt) {
if((evt.keyCode === 13) || evt.which === 13 || evt.key === 13){
addLlnr();
return false
}
return true
}
</script>
</head>
<body>
<div id="centerdiv">
<h1>TicketScanner 2.0: {{ username }}</h1>
<h2>{{ mode }}</h2>
<div id="derk">
<p>Binnen/verkocht: <span id="vb"></span></p>
<p>Leerlingnummer: <span id="result"></span></p>
<p>Resultaat: <span id="msg"></span></p>
<p>Voeg handmatig toe: <input type="text" id="inputbox" title="leerlingnummer" placeholder="######" autofocus="autofocus" {#onclick="addLlnr()"#} onkeydown="return checkval(event)">
<button id="addBtn" onclick="addLlnr()">Voeg Toe</button>
</p>
<p><a href="/excel">Download xlsx bestand</a></p>
</div>
<div>
<div id="mobileCanvas"{#style="display: none; float: bottom;"#}></div>
</div>
</div>
</body>
<script src="/libs/quagga.min.js"></script>
<script>
Quagga.init(
{
inputStream: {
name: "Live",
locate: true,
frequency: 5,
type: "LiveStream",
target: document.querySelector('#mobileCanvas'), // Or '#yourElement' (optional)
constraints: {
width: 640,
height: 480,
facingMode: "environment"
},
},
decoder: {
readers: ["codabar_reader"]
}
},
function (err) {
if (err) {
console.log(err);
return
}
console.log("Initialization finished. Ready to start");
let last = "";
Quagga.onDetected(function (data) {
let code = data.codeResult.code;
code = code.slice(1, -1);
if (last !== code) {
last = code;
addUser(code)
}
});
Quagga.start();
});
</script>
</html>