fixes #165: show a warning when multiple tickets are being updated
This commit is contained in:
parent
d42284a50c
commit
aa07aff247
5 changed files with 133 additions and 30 deletions
6
angular_app/js/controllers.js
vendored
6
angular_app/js/controllers.js
vendored
|
@ -293,6 +293,10 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
|
|||
$log.debug('do not process our own message');
|
||||
return false;
|
||||
}
|
||||
if (data.error && data.message) {
|
||||
$scope.showMessage({message: data.message, isError: true});
|
||||
return;
|
||||
}
|
||||
if (!$scope.event.tickets) {
|
||||
$scope.event.tickets = [];
|
||||
}
|
||||
|
@ -484,7 +488,7 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
|
|||
msg.message = name + ' successfully added to event ' + $scope.event.title;
|
||||
} else {
|
||||
msg.message = name + ' successfully removed from event ' + $scope.event.title;
|
||||
msg.isError = true;
|
||||
msg.isWarning = true;
|
||||
}
|
||||
$scope.showMessage(msg);
|
||||
};
|
||||
|
|
7
angular_app/js/directives.js
vendored
7
angular_app/js/directives.js
vendored
|
@ -51,7 +51,12 @@ eventManApp.directive('eventmanMessage', ['$timeout',
|
|||
cfg = cfg || {};
|
||||
scope.dControl.isVisible = true;
|
||||
scope.dControl.message = cfg.message;
|
||||
scope.dControl.isSuccess = true;
|
||||
scope.dControl.isError = cfg.isError;
|
||||
scope.dControl.isWarning = cfg.isWarning;
|
||||
if (cfg.isError || cfg.isWarning) {
|
||||
scope.dControl.isSuccess = false;
|
||||
}
|
||||
$timeout(function () {
|
||||
scope.dControl.isVisible = false;
|
||||
}, cfg.timeout || 4000);
|
||||
|
@ -63,7 +68,7 @@ eventManApp.directive('eventmanMessage', ['$timeout',
|
|||
control: '='
|
||||
},
|
||||
link: link,
|
||||
template: '<div ng-if="dControl.isVisible" ng-class="{\'eventman-message\': true, clearfix: true, \'alert\': true, \'alert-success\': !dControl.isError, \'alert-danger\': dControl.isError}">{{dControl.message}}</div>'
|
||||
template: '<div ng-if="dControl.isVisible" ng-class="{\'eventman-message\': true, clearfix: true, \'alert\': true, \'alert-success\': dControl.isSuccess, \'alert-danger\': dControl.isError, \'alert-warning\': dControl.isWarning}">{{dControl.message}}</div>'
|
||||
};
|
||||
}]
|
||||
);
|
||||
|
|
|
@ -668,17 +668,26 @@ class EventsHandler(CollectionHandler):
|
|||
persons += [p for p in (event.get('tickets') or []) if p.get('email') and p.get('email') not in this_emails]
|
||||
return {'persons': persons}
|
||||
|
||||
def _get_ticket_data(self, ticket_id_or_query, tickets):
|
||||
def _get_ticket_data(self, ticket_id_or_query, tickets, only_one=True):
|
||||
"""Filter a list of tickets returning the first item with a given _id
|
||||
or which set of keys specified in a dictionary match their respective values."""
|
||||
matches = []
|
||||
for ticket in tickets:
|
||||
if isinstance(ticket_id_or_query, dict):
|
||||
if all(ticket.get(k) == v for k, v in ticket_id_or_query.items()):
|
||||
return ticket
|
||||
matches.append(ticket)
|
||||
if only_one:
|
||||
break
|
||||
else:
|
||||
if str(ticket.get('_id')) == ticket_id_or_query:
|
||||
return ticket
|
||||
return {}
|
||||
matches.append(ticket)
|
||||
if only_one:
|
||||
break
|
||||
if only_one:
|
||||
if matches:
|
||||
return matches[0]
|
||||
return {}
|
||||
return matches
|
||||
|
||||
def handle_get_tickets(self, id_, resource_id=None):
|
||||
# Return every ticket registered at this event, or the information
|
||||
|
@ -799,7 +808,18 @@ class EventsHandler(CollectionHandler):
|
|||
current_event = {}
|
||||
self._check_sales_datetime(current_event)
|
||||
tickets = current_event.get('tickets') or []
|
||||
old_ticket_data = self._get_ticket_data(ticket_query, tickets)
|
||||
matching_tickets = self._get_ticket_data(ticket_query, tickets, only_one=False)
|
||||
nr_matches = len(matching_tickets)
|
||||
if nr_matches > 1:
|
||||
ret = {'error': True, 'message': 'more than one ticket matched', 'query': query,
|
||||
'uuid': uuid, 'username': self.current_user_info.get('username', '')}
|
||||
self.send_ws_message('event/%s/tickets/updates' % id_, json.dumps(ret))
|
||||
self.set_status(400)
|
||||
return ret
|
||||
elif nr_matches == 1:
|
||||
old_ticket_data = matching_tickets[0]
|
||||
else:
|
||||
old_ticket_data = {}
|
||||
|
||||
# We have changed the "cancelled" status of a ticket to False; check if we still have a ticket available
|
||||
if 'number_of_tickets' in current_event and old_ticket_data.get('cancelled') and not data.get('cancelled'):
|
||||
|
|
17
tools/qrcode_reader.ini
Normal file
17
tools/qrcode_reader.ini
Normal file
|
@ -0,0 +1,17 @@
|
|||
[connection]
|
||||
port = /dev/ttyACM0
|
||||
|
||||
[eventman]
|
||||
url = https://localhost:5242/
|
||||
username = admin
|
||||
password = eventman
|
||||
ca =
|
||||
|
||||
[event]
|
||||
id = 1492099112_2612922-3896-9zwsccuvguz91jtw9y6lwvkud11ba7wt
|
||||
field = order_nr
|
||||
limit_field = 9
|
||||
|
||||
[actions]
|
||||
attended = True
|
||||
checked_in_by = ${eventman:username}
|
|
@ -1,34 +1,92 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""qrcode_reader
|
||||
|
||||
Scan the output of a serial QR Code reader.
|
||||
|
||||
Copyright 2017 Davide Alberani <da@erlug.linux.it>
|
||||
RaspiBO <info@raspibo.org>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import os
|
||||
import io
|
||||
import sys
|
||||
import time
|
||||
import serial
|
||||
import urllib
|
||||
import requests
|
||||
import configparser
|
||||
|
||||
|
||||
class Connector():
|
||||
def __init__(self, login_url, checkin_url, username=None, password=None):
|
||||
self.login_url = login_url
|
||||
self.checkin_url = checkin_url
|
||||
self.session = requests.Session()
|
||||
json = {}
|
||||
if username:
|
||||
json['username'] = username
|
||||
if password:
|
||||
json['password'] = password
|
||||
req = self.session.post(login_url, json=json, verify=False)
|
||||
req.raise_for_status()
|
||||
req.connection.close()
|
||||
def __init__(self, cfg):
|
||||
self.cfg = cfg
|
||||
self.session = None
|
||||
self.url = cfg['eventman']['url']
|
||||
self.login_url = urllib.parse.urljoin(self.url, '/v1.0/login')
|
||||
self.checkin_url = urllib.parse.urljoin(self.url, os.path.join('/v1.0/events/',
|
||||
cfg['event']['id'], 'tickets/'))
|
||||
self.login()
|
||||
|
||||
def login(self):
|
||||
try:
|
||||
self.session = requests.Session()
|
||||
self.session.verify = False
|
||||
ca = cfg['eventman'].get('ca')
|
||||
if ca and os.path.isfile(ca):
|
||||
self.session.verify = ca
|
||||
username = cfg['eventman'].get('username')
|
||||
password = cfg['eventman'].get('password')
|
||||
params = {}
|
||||
if username:
|
||||
params['username'] = username
|
||||
if password:
|
||||
params['password'] = password
|
||||
req = self.session.post(self.login_url, json=params)
|
||||
req.raise_for_status()
|
||||
req.connection.close()
|
||||
except requests.exceptions.ConnectionError as ex:
|
||||
print('unable to connect to %s: %s' % (self.login_url, ex))
|
||||
sys.exit(1)
|
||||
|
||||
def checkin(self, code):
|
||||
req = self.session.put(self.checkin_url + '?order_nr=' + code[:9], json={'attended': True}, verify=False)
|
||||
req.raise_for_status()
|
||||
limit_field = self.cfg['event'].getint('limit_field')
|
||||
if limit_field:
|
||||
code = code[:limit_field]
|
||||
checkin_url = self.checkin_url + '?' + urllib.parse.urlencode({cfg['event']['field']: code})
|
||||
params = dict(self.cfg['actions'])
|
||||
req = self.session.put(checkin_url, json=params)
|
||||
try:
|
||||
req.raise_for_status()
|
||||
except requests.exceptions.HTTPError as ex:
|
||||
print('error: %s' % req.json().get('message'))
|
||||
req.connection.close()
|
||||
|
||||
|
||||
|
||||
def scan():
|
||||
ser = serial.Serial(port='/dev/ttyACM0', timeout=1)
|
||||
def scan(port):
|
||||
retry = 1
|
||||
while True:
|
||||
print('waiting for connection on port %s...' % port)
|
||||
try:
|
||||
ser = serial.Serial(port=port, timeout=1)
|
||||
break
|
||||
except serial.serialutil.SerialException as ex:
|
||||
if retry >= 10:
|
||||
print('unable to connect: %s' % ex)
|
||||
sys.exit(2)
|
||||
time.sleep(1)
|
||||
retry += 1
|
||||
print('connected to %s' % port)
|
||||
ser_io = io.TextIOWrapper(io.BufferedRWPair(ser, ser, 1), newline='\r', line_buffering=True)
|
||||
while True:
|
||||
line = ser_io.readline().strip()
|
||||
|
@ -38,13 +96,12 @@ def scan():
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
connector = Connector(login_url='https://localhost:5242/v1.0/login',
|
||||
checkin_url='https://localhost:5242/v1.0/events/1490640884_8820477-7-7gvft6nlrs2o73fza54a6yeywiowmj8v/tickets/',
|
||||
username='admin',
|
||||
password='eventman')
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg.read('qrcode_reader.ini')
|
||||
connector = Connector(cfg)
|
||||
try:
|
||||
for code in scan():
|
||||
print(code)
|
||||
for code in scan(port=cfg['connection']['port']):
|
||||
print('received code %s' % code)
|
||||
connector.checkin(code)
|
||||
except KeyboardInterrupt:
|
||||
print('exiting...')
|
||||
|
|
Loading…
Reference in a new issue