Commit effe6e19 authored by Sli's avatar Sli

Modification de la structure de la base de donnée, modification de l'api et ajout d'une webapp

parent 363310da
No preview for this file type
......@@ -169,10 +169,10 @@ public class Read_QR_Code extends ActionBarActivity implements View.OnClickListe
idFound = true;
if (key.getIs_child()) {
showChildDialog(Read_QR_Code.this, keyTicket, nbrPlaceTot, nbrPlaceSelect);
showChildDialog(Read_QR_Code.this, keyTicket, nbrPlaceTot, nbrPlaceSelect, String.valueOf(idTicket));
}
else {
sendRequest(keyTicket, nbrPlaceTot, nbrPlaceSelect);
sendRequest(keyTicket, String.valueOf(idTicket), nbrPlaceTot, nbrPlaceSelect);
}
}
}
......@@ -183,12 +183,12 @@ public class Read_QR_Code extends ActionBarActivity implements View.OnClickListe
}
private AlertDialog showChildDialog(final Activity act, final String hash, final int nbrPlaceTot,
final int nbrPlaceSelect) {
final int nbrPlaceSelect, final String type) {
AlertDialog.Builder childDialog = new AlertDialog.Builder(act);
childDialog.setMessage(R.string.ebillet_mineur);
childDialog.setPositiveButton(R.string.check, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int i) {
sendRequest(hash, nbrPlaceTot, nbrPlaceSelect);
sendRequest(hash, type, nbrPlaceTot, nbrPlaceSelect);
}
});
childDialog.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
......@@ -199,7 +199,7 @@ public class Read_QR_Code extends ActionBarActivity implements View.OnClickListe
return childDialog.show();
}
private void sendRequest(String hash, int nbrPlaceTot, int nbrPlaceSelect) {
private void sendRequest(String hash, String type, int nbrPlaceTot, int nbrPlaceSelect) {
JSONObject jsonParams = new JSONObject();
StringEntity entity = null;
......@@ -208,6 +208,7 @@ public class Read_QR_Code extends ActionBarActivity implements View.OnClickListe
jsonParams.put("verif", hash);
jsonParams.put("nb", nbrPlaceTot);
jsonParams.put("qt", nbrPlaceSelect);
jsonParams.put("type", type);
// Set JSON parameters for Post request
entity = new StringEntity(jsonParams.toString());
......
......@@ -16,7 +16,7 @@ Cette application permet de centraliser les données relevées par les différen
│ Réception des clefs │
│ │ │ │
│ ┌───────────────────────────────────┐ │ │ │
│ │ Validate │ │ │ ┌───────────────────────────────────┐ │
│ │ ValidateApi │ │ │ ┌───────────────────────────────────┐ │
│ │ ┌──────────────────────────────┐ │ │ │ │ │ │
│ │ │ │ │ │ │ │ Vérification du code bar │ │
│ │ │ Teste d'existence en BDD │◀┼────┼────┐ │ │ │ │
......@@ -74,7 +74,8 @@ Requête vers /validate : (utilise la méthode POST)
```json
{
"verif": "EXAMPLE", // Code de vérification du billet
"verif": "EXAMPLE", // Clef de vérification
"type": "89", // Type de produit
"nb": 9, // Nombre de places totales du billet
"qt": 2 // Quantité à valider
}
......
CREATE Table ticket (
id INTEGER PRIMARY KEY AUTOINCREMENT,
verifKey TEXT UNIQUE NOT NULL,
verifKey TEXT NOT NULL,
productType TEXT NOT NULL,
availablePlaces INTEGER NOT NULL,
totalPlaces INTEGER NOT NULL,
validationDate Datetime
......
(function($) {
jQuery.fn.extend({
html5_qrcode: function(qrcodeSuccess, qrcodeError, videoError) {
return this.each(function() {
var currentElem = $(this);
var height = currentElem.height();
var width = currentElem.width();
if (height == null) {
height = 250;
}
if (width == null) {
width = 300;
}
var vidElem = $('<video width="' + width + 'px" height="' + height + 'px"></video>').appendTo(currentElem);
var canvasElem = $('<canvas id="qr-canvas" width="' + (width - 2) + 'px" height="' + (height - 2) + 'px" style="display:none;"></canvas>').appendTo(currentElem);
var video = vidElem[0];
var canvas = canvasElem[0];
var context = canvas.getContext('2d');
var localMediaStream;
var scan = function() {
if (localMediaStream) {
context.drawImage(video, 0, 0, 307, 250);
try {
qrcode.decode();
} catch (e) {
qrcodeError(e, localMediaStream);
}
$.data(currentElem[0], "timeout", setTimeout(scan, 500));
} else {
$.data(currentElem[0], "timeout", setTimeout(scan, 500));
}
};//end snapshot function
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
var successCallback = function(stream) {
video.src = (window.URL && window.URL.createObjectURL(stream)) || stream;
localMediaStream = stream;
$.data(currentElem[0], "stream", stream);
video.play();
$.data(currentElem[0], "timeout", setTimeout(scan, 1000));
};
// Call the getUserMedia method with our callback functions
if (navigator.getUserMedia) {
navigator.getUserMedia({video: true}, successCallback, function(error) {
videoError(error, localMediaStream);
});
} else {
console.log('Native web camera streaming (getUserMedia) not supported in this browser.');
// Display a friendly "sorry" message to the user
}
qrcode.callback = function (result) {
qrcodeSuccess(result, localMediaStream);
};
}); // end of html5_qrcode
},
html5_qrcode_stop: function() {
return this.each(function() {
//stop the stream and cancel timeouts
$(this).data('stream').getVideoTracks().forEach(function(videoTrack) {
videoTrack.stop();
});
clearTimeout($(this).data('timeout'));
});
}
});
})(jQuery);
This diff is collapsed.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Author: klmp200
# @Author: Bartuccio Antoine (Sli) (klmp200)
# @Date: 2016-07-03 17:57:28
# @Last Modified by: klmp200
# @Last Modified time: 2016-11-08 00:46:33
# @Last Modified time: 2016-11-09 01:00:25
from bottle import Bottle, static_file, request, template, redirect
from bottle.ext import sqlite
import hmac
import hashlib
import json
import datetime
import settings
KEYS = []
with open('../data/keys.json', 'r') as json_data:
KEYS = json.load(json_data)
def SafeInt(string):
if string.isdecimal():
return int(string)
else:
return 0
def find_product(keys, id_product):
product = SafeInt(id_product)
for obj in keys:
if obj['id'] == product:
return obj
return None
app = Bottle()
plugin = sqlite.Plugin(dbfile='../data/sqliteDB.db')
app.install(plugin)
......@@ -103,6 +125,63 @@ def DisplayAdminAjax(db):
return dict(data=tickets)
@app.route('/webscan', method='GET')
def ScanTicketView(status=None):
"""
A web app to check tickets
"""
response = ObtainGetArgs(request.query, ['av', 'valid', 'child'])
return template('scan.simple', response=response)
@app.route('/webscan/form', method='POST')
def CheckTicketPost(db):
"""
Recieve form and validate data
"""
code = request.forms.get('code').upper()
code_list = code.split()
is_child = False
if len(code_list) >= 4:
verif_key = code_list.pop(-1)
place_tot = SafeInt(code_list.pop(-1))
product_type = code_list.pop(1)
product = find_product(KEYS, product_type)
else:
verif_key = ""
product = {}
if CheckHmac(code, product, verif_key) and place_tot > 0:
print("HMAC")
used_qt = SafeInt(request.forms.get('qt'))
is_child = product['is_child']
if used_qt < 1 or used_qt > place_tot:
status = {'available': 0, 'valid': False}
else:
status = Validate(db, place_tot, verif_key, 1, product_type)
else:
status = {'available': 0, 'valid': False}
if request.forms.get('ajax') == "True":
return dict({'av': status['available'], 'valid': status['valid'], 'child': is_child})
else:
redirect('/webscan?av={}&valid={}&child={}'.format(status['available'],
status['valid'], is_child))
def CheckHmac(code, product, verif_key):
new_code = code.split(' ')
new_code.pop(-1)
code = ' '.join(new_code)
if product:
true_key = hmac.new(bytes(product['key'], 'utf-8'),
bytes(code, 'utf-8'), hashlib.sha1).hexdigest()
return hmac.compare_digest(true_key[:8].upper(), verif_key)
else:
return False
def SearchDb(db, args):
"""
Qwery used in db
......@@ -148,8 +227,24 @@ def EditTicketQuantity(db, id_ticket, nb):
redirect('/admin')
def Validate(db, place_tot, verif_key, place_used, product_type):
"""
Verify in bdd if the ticket exists and create it
Return if it's valid and quantity avaliable
"""
ticket = db.execute('SELECT * from ticket where verifKey=:key and totalPlaces=:nb and productType=:type',
{"key": verif_key, "nb": place_used, 'type': product_type}).fetchone()
data = {'nb': place_tot, 'qt': place_used, 'verif': verif_key, 'type': product_type}
if (ticket is None):
message = NewEntry(db, data)
else:
message = UpdateEntry(db, data, ticket)
return message
@app.route('/validate', method='POST')
def Validate(db):
def ValidateApi(db):
"""
Verify in bdd if the ticket exists and create it
Return a json to the app
......@@ -157,13 +252,8 @@ def Validate(db):
try:
send = request.json
ticket = db.execute('SELECT * from ticket where verifKey=:key and totalPlaces=:nb',
{"key": send['verif'], "nb": send['nb']}).fetchone()
if (ticket is None):
message = NewEntry(db, send)
else:
message = UpdateEntry(db, send, ticket)
print(send)
message = Validate(db, send['nb'], send['verif'], send['qt'], send['type'])
except:
message = '<p>Error processing data</p>'
......@@ -180,8 +270,8 @@ def NewEntry(db, data):
"available": available,
"valid": True
}
db.execute('INSERT into ticket(verifKey, availablePlaces, totalPlaces, validationDate) values (?, ?, ?, ?)',
(data['verif'], available, data['nb'], datetime.datetime.now().strftime('%Hh %Mmin %Ss')))
db.execute('INSERT into ticket(verifKey, availablePlaces, totalPlaces, validationDate, productType) values (?, ?, ?, ?, ?)',
(data['verif'], available, data['nb'], datetime.datetime.now().strftime('%Hh %Mmin %Ss'), data['type']))
return dict(response)
......
......@@ -25,6 +25,7 @@
<thead>
<tr>
<th>Clef de vérification</th>
<th>Type de produit</th>
<th>Places restantes</th>
<th>Places total disponible</th>
<th>Heure d'ajout</th>
......@@ -36,6 +37,7 @@
% for element in table:
<tr>
<td>{{element['verifKey']}}</td>
<td>{{element['productType']}}</td>
<td>{{element['availablePlaces']}}</td>
<td>{{element['totalPlaces']}}</td>
<td>{{element['validationDate']}}</td>
......@@ -80,6 +82,7 @@
data['data'].forEach(function(ticket){
to_write += "<tr>";
to_write += "<td>" + ticket['verifKey'] + "</td>";
to_write += "<td>" + ticket['productType'] + "</td>";
to_write += "<td>" + ticket['availablePlaces'] + "</td>";
to_write += "<td>" + ticket['totalPlaces'] + "</td>";
to_write += "<td>" + ticket['validationDate'] + "</td>";
......
......@@ -15,6 +15,9 @@
<div id="accordion">
<h2>Section de téléchargement</h2>
<div>
<p>
Application de scan version web : <a class="pure-button pure-button-primary" href="/webscan">Application web</a>
</p>
<p>
Application de vérification : <a class="pure-button pure-button-primary" href="/gala.apk">Gala de prestige</a>
</p>
......
<!DOCTYPE html>
<html>
<head>
<title>Webscan</title>
<link rel="stylesheet" type="text/css" href="/media/css/jquery-ui.min.css">
<link rel="stylesheet" type="text/css" href="/media/css/pure-min.css">
<link rel="stylesheet" type="text/css" href="/media/css/colored-buttons.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="pure-g">
<div class="pure-u-1-24 pure-u-md-1-12 pure-u-sm-1-24"></div>
<div class="pure-u-22-24 pure-u-md-10-12 pure-u-sm-22-24">
<h1>Serveur de validation de ebillets du gala</h1>
<div id="response">
% if response['valid']:
% if response['valid'] == "True" and response['av']:
% if response['child'] and response['child'] == 'True':
<p class="pure-button button-warning" style="font-size: 30px">Ceci est une place pour mineur !</p><br>
% end
<p class="pure-button button-success">Billet valide : {{response['av']}} places restantes</p>
% else:
<p class="pure-button button-error">Billet invalide ou déjà utilisé</p>
% end
% end
</div>
<div id="reader" style="width:300px;height:250px">
</div>
<p id="reader-status"></p>
<form class="pure-form" method="post" action="/webscan/form">
<p>
Code
<input type="text" name="code" id="code">
</p>
<p>
Places à valider
<input type="number" name="qt" id="qt" value="1">
</p>
<input type="hidden" name="ajax" value="False">
<button type="submit" id="validate" class="pure-button pure-button-primary">Vérifier</button>
</form>
</div>
<div class="pure-u-1-24 pure-u-md-1-12 pure-u-sm-1-24"></div>
</div>
<footer>
<script type="text/javascript" src="/media/js/jquery.js"></script>
<script type="text/javascript" src="/media/js/jsqrcode-combined.min.js"></script>
<script type="text/javascript" src="/media/js/html5-qrcode.js"></script>
<script>
$('#reader').html5_qrcode(function(data){
// do something when code is read
$('#code').val(data);
$('#reader-status').empty().prepend("Lecture réusise").attr('class', 'pure-button button-success');
$('#validate').prop('disabled', true);
$('#response').empty();
$.post("/webscan/form", {
code: $("#code").val(),
qt: $("#qt").val(),
ajax: "True"
}).done(function(data){
if (data['valid']){
if (data['child']){
$("#response").prepend('<p class="pure-button button-warning" style="font-size: 30px">Ceci est une place pour mineur !</p><br>');
}
$("#response").prepend('<p class="pure-button button-success">Billet valide : ' + data['av'] + ' places restantes</p>');
} else {
$("#response").prepend('<p class="pure-button button-error">Billet invalide ou déjà utilisé</p>');
}
});
$('#validate').prop('disabled', false);
},
function(error){
//show read errors
$('#reader-status').empty().prepend("Erreur de lecture du qr code, veuillez réessayer").attr('class', 'pure-button button-warning');
}, function(videoError){
$('#reader').prepend("Accès à la caméra impossible").attr('class', 'button-error');
}
);
</script>
</footer>
</body>
</html>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment