Add web interface for scanning and checking
This commit is contained in:
parent
f9cac04003
commit
baa602ad90
66
app.py
66
app.py
|
@ -1,11 +1,21 @@
|
||||||
from flask import Flask, request, jsonify, make_response
|
|
||||||
from flask_httpauth import HTTPBasicAuth
|
|
||||||
from sqlite3 import connect, OperationalError
|
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()
|
auth = HTTPBasicAuth()
|
||||||
db = connect("database.db", check_same_thread=False)
|
db = connect("database.db", check_same_thread=False)
|
||||||
|
excel.init_excel(app)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cursor = db.cursor()
|
cursor = db.cursor()
|
||||||
|
@ -23,6 +33,9 @@ except OperationalError:
|
||||||
|
|
||||||
@auth.verify_password
|
@auth.verify_password
|
||||||
def verpass(username, password):
|
def verpass(username, password):
|
||||||
|
print(username)
|
||||||
|
if username in (None, '') or password in (None, ''):
|
||||||
|
return False
|
||||||
# return True
|
# return True
|
||||||
cur = db.cursor()
|
cur = db.cursor()
|
||||||
ret = cur.execute('''select password from users where username = ?''', (username,)).fetchone()
|
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"])
|
@app.route("/ticket", methods=["POST"])
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
def addticket():
|
def addticket():
|
||||||
|
print(request.json)
|
||||||
if not request.json or "number" not in request.json or "function" not in 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)
|
return make_response(jsonify({"msg": "missing/faulty request body?"}), 400)
|
||||||
fun = request.json["function"]
|
fun = request.json["function"]
|
||||||
|
@ -119,8 +133,52 @@ def control():
|
||||||
return make_response(jsonify({"msg": "Removed!"}), 200)
|
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")
|
@app.route("/check")
|
||||||
@auth.login_required
|
@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():
|
def check():
|
||||||
cur = db.cursor()
|
cur = db.cursor()
|
||||||
num1 = cur.execute('''select * from tickets where username = ?;''', (auth.username(),)).fetchall()
|
num1 = cur.execute('''select * from tickets where username = ?;''', (auth.username(),)).fetchall()
|
||||||
|
|
29
static/css/style.css
Normal file
29
static/css/style.css
Normal 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
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
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
172
templates/scan.html
Normal 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>
|
Loading…
Reference in a new issue