fixes #165: show a warning when multiple tickets are being updated

This commit is contained in:
Davide Alberani 2017-04-15 11:07:27 +02:00
parent d42284a50c
commit aa07aff247
5 changed files with 133 additions and 30 deletions

View file

@ -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);
};

View file

@ -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>'
};
}]
);

View file

@ -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
View 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}

View file

@ -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...')