From b6edc6c91351532b1fe1d1367ae159a4c2a5c289 Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Sat, 15 Apr 2017 13:00:00 +0200 Subject: [PATCH 1/6] avoid multiple messages --- angular_app/js/controllers.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/angular_app/js/controllers.js b/angular_app/js/controllers.js index 6a2b5ab..8a50418 100644 --- a/angular_app/js/controllers.js +++ b/angular_app/js/controllers.js @@ -293,7 +293,7 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event', return false; } if (data.error && data.message) { - toaster.pop({type: 'error', title: 'Error', body: data.message, timeout: 5000}); + toaster.pop({type: 'error', title: 'Error', body: data.message, timeout: 0, showCloseButton: true}); return; } if (!$scope.event.tickets) { @@ -545,7 +545,7 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event', $log.debug('addTicket'); $log.debug(ret_ticket); $rootScope.$emit('event:ticket:new', ret_ticket, function() { - $rootScope.$emit('event:ticket:set-attr', ret_ticket, 'attended', true, null, false); + $rootScope.$emit('event:ticket:set-attr', ret_ticket, 'attended', true, null, true); }); if (cb) { cb(ticket); @@ -600,7 +600,9 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event', toaster.pop({type: 'info', title: 'ticket successfully updated'}); }); } else { - $scope.addTicket($scope.ticket); + $scope.addTicket($scope.ticket, function() { + $scope.showAttendedMessage($scope.ticket, true); + }); } }; From d0e4f2188f9a987164c3d9a9c889e078ab6ee825 Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Sat, 15 Apr 2017 13:35:15 +0200 Subject: [PATCH 2/6] show message for no match --- angular_app/js/app.js | 1 + eventman_server.py | 10 +++++++--- static/css/eventman.css | 5 +++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/angular_app/js/app.js b/angular_app/js/app.js index e015b68..7f29b4d 100644 --- a/angular_app/js/app.js +++ b/angular_app/js/app.js @@ -17,6 +17,7 @@ /* Register our fantastic app. */ var eventManApp = angular.module('eventManApp', [ 'ngRoute', + 'ngAnimate', 'eventManServices', 'eventManControllers', 'ui.bootstrap', diff --git a/eventman_server.py b/eventman_server.py index 9b4d536..dd560ff 100755 --- a/eventman_server.py +++ b/eventman_server.py @@ -816,10 +816,14 @@ class EventsHandler(CollectionHandler): 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] + elif nr_matches == 0: + ret = {'error': True, 'message': 'no 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 else: - old_ticket_data = {} + old_ticket_data = matching_tickets[0] # 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'): diff --git a/static/css/eventman.css b/static/css/eventman.css index 2438f85..1ae6e4e 100644 --- a/static/css/eventman.css +++ b/static/css/eventman.css @@ -103,3 +103,8 @@ input[type=text].form-control, input[type=search].form-control { #toast-container.toast-bottom-center>div, #toast-container.toast-center>div, #toast-container.toast-top-center>div { margin-bottom: 4px; } + +:not(.no-enter)#toast-container > div.ng-enter +{ + transition-duration: .1s; +} From 1427ba1623de983df0564434ff9752fd1ae064e9 Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Sat, 15 Apr 2017 14:22:05 +0200 Subject: [PATCH 3/6] add extra error information --- angular_app/js/controllers.js | 10 +++++++--- eventman_server.py | 8 ++++++-- static/css/eventman.css | 7 +++++-- tools/qrcode_reader.py | 15 +++++++++------ 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/angular_app/js/controllers.js b/angular_app/js/controllers.js index 8a50418..71e024a 100644 --- a/angular_app/js/controllers.js +++ b/angular_app/js/controllers.js @@ -508,6 +508,9 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event', ticket_id: ticket._id }, function() { $scope._localRemoveTicket(ticket); + var msg = $scope.buildTicketLabel(ticket); + msg += ' successfully removed from event ' + $scope.event.title; + toaster.pop({type: 'error', title: msg}); }); }; @@ -558,6 +561,9 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event', // Close the Quick ticket modal. $scope.$close(); } + var msg = $scope.buildTicketLabel(ret_ticket); + msg += ' successfully added to event ' + $scope.event.title; + toaster.pop({type: 'success', title: msg}); } }); }; @@ -600,9 +606,7 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event', toaster.pop({type: 'info', title: 'ticket successfully updated'}); }); } else { - $scope.addTicket($scope.ticket, function() { - $scope.showAttendedMessage($scope.ticket, true); - }); + $scope.addTicket($scope.ticket); } }; diff --git a/eventman_server.py b/eventman_server.py index dd560ff..8cdf7a5 100755 --- a/eventman_server.py +++ b/eventman_server.py @@ -793,6 +793,10 @@ class EventsHandler(CollectionHandler): # Update an existing entry for a ticket registered at this event. self._clean_dict(data) uuid, arguments = self.uuid_arguments + _errorMessage = '' + if '_errorMessage' in arguments: + _errorMessage = arguments['_errorMessage'] + del arguments['_errorMessage'] query = dict([('tickets.%s' % k, v) for k, v in arguments.items()]) query['_id'] = id_ if ticket_id is not None: @@ -811,13 +815,13 @@ class EventsHandler(CollectionHandler): 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, + ret = {'error': True, 'message': 'more than one ticket matched. %s' % _errorMessage, '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 == 0: - ret = {'error': True, 'message': 'no ticket matched', 'query': query, + ret = {'error': True, 'message': 'no ticket matched. %s' % _errorMessage, '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) diff --git a/static/css/eventman.css b/static/css/eventman.css index 1ae6e4e..4bb67c5 100644 --- a/static/css/eventman.css +++ b/static/css/eventman.css @@ -104,7 +104,10 @@ input[type=text].form-control, input[type=search].form-control { margin-bottom: 4px; } -:not(.no-enter)#toast-container > div.ng-enter -{ +:not(.no-enter)#toast-container > div.ng-enter { transition-duration: .1s; } + +:not(.no-leave)#toast-container > div.ng-leave { + transition-duration: .2s; +} diff --git a/tools/qrcode_reader.py b/tools/qrcode_reader.py index b091d5a..ea04f94 100755 --- a/tools/qrcode_reader.py +++ b/tools/qrcode_reader.py @@ -60,16 +60,20 @@ class Connector(): sys.exit(1) def checkin(self, code): + msg = 'scanning code %s: ' % code 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) + params = {cfg['event']['field']: code, '_errorMessage': 'code: %s' % code} + checkin_url = self.checkin_url + '?' + urllib.parse.urlencode(params) + json = dict(self.cfg['actions']) + req = self.session.put(checkin_url, json=json) try: req.raise_for_status() + msg += 'ok' except requests.exceptions.HTTPError as ex: - print('error: %s' % req.json().get('message')) + msg += 'error: %s' % req.json().get('message') + print(msg) req.connection.close() @@ -81,7 +85,7 @@ def scan(port): ser = serial.Serial(port=port, timeout=1) break except serial.serialutil.SerialException as ex: - if retry >= 10: + if retry >= 20: print('unable to connect: %s' % ex) sys.exit(2) time.sleep(1) @@ -101,7 +105,6 @@ if __name__ == '__main__': connector = Connector(cfg) try: for code in scan(port=cfg['connection']['port']): - print('received code %s' % code) connector.checkin(code) except KeyboardInterrupt: print('exiting...') From 160cec769162ebecfb2864955fa2f4c21224f5c4 Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Sat, 15 Apr 2017 15:02:48 +0200 Subject: [PATCH 4/6] improve logging --- tools/qrcode_reader.ini | 3 +++ tools/qrcode_reader.py | 28 ++++++++++++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/tools/qrcode_reader.ini b/tools/qrcode_reader.ini index bca8f0f..46175cb 100644 --- a/tools/qrcode_reader.ini +++ b/tools/qrcode_reader.ini @@ -1,3 +1,6 @@ +[qrcode_reader] +debug = true + [connection] port = /dev/ttyACM0 diff --git a/tools/qrcode_reader.py b/tools/qrcode_reader.py index ea04f94..1ee3c57 100755 --- a/tools/qrcode_reader.py +++ b/tools/qrcode_reader.py @@ -24,8 +24,17 @@ import sys import time import serial import urllib +import logging import requests import configparser +from requests.packages.urllib3.exceptions import InsecureRequestWarning + + +logger = logging.getLogger('qrcode_reader') +logging.basicConfig(level=logging.INFO) +logging.getLogger('requests').setLevel(logging.WARNING) +logging.getLogger('urllib3').setLevel(logging.WARNING) +requests.packages.urllib3.disable_warnings(InsecureRequestWarning) class Connector(): @@ -56,7 +65,7 @@ class Connector(): req.raise_for_status() req.connection.close() except requests.exceptions.ConnectionError as ex: - print('unable to connect to %s: %s' % (self.login_url, ex)) + logger.error('unable to connect to %s: %s' % (self.login_url, ex)) sys.exit(1) def checkin(self, code): @@ -68,29 +77,34 @@ class Connector(): checkin_url = self.checkin_url + '?' + urllib.parse.urlencode(params) json = dict(self.cfg['actions']) req = self.session.put(checkin_url, json=json) + error = False try: req.raise_for_status() msg += 'ok' except requests.exceptions.HTTPError as ex: + error = True msg += 'error: %s' % req.json().get('message') - print(msg) + if not error: + logger.info(msg) + else: + logger.warning(msg) req.connection.close() def scan(port): retry = 1 while True: - print('waiting for connection on port %s...' % port) + logger.debug('waiting for connection on port %s...' % port) try: ser = serial.Serial(port=port, timeout=1) break except serial.serialutil.SerialException as ex: if retry >= 20: - print('unable to connect: %s' % ex) + logger.error('unable to connect: %s' % ex) sys.exit(2) time.sleep(1) retry += 1 - print('connected to %s' % port) + logger.info('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() @@ -102,9 +116,11 @@ def scan(port): if __name__ == '__main__': cfg = configparser.ConfigParser() cfg.read('qrcode_reader.ini') + if cfg['qrcode_reader'].getboolean('debug'): + logging.basicConfig(level=logging.DEBUG) connector = Connector(cfg) try: for code in scan(port=cfg['connection']['port']): connector.checkin(code) except KeyboardInterrupt: - print('exiting...') + logger.info('exiting...') From f75e6f6302e9cdb36d78fc5437e6f338eadd2871 Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Sat, 15 Apr 2017 15:12:57 +0200 Subject: [PATCH 5/6] strip extra arguments --- eventman_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eventman_server.py b/eventman_server.py index 8cdf7a5..72372b3 100755 --- a/eventman_server.py +++ b/eventman_server.py @@ -803,7 +803,7 @@ class EventsHandler(CollectionHandler): query['tickets._id'] = ticket_id ticket_query = {'_id': ticket_id} else: - ticket_query = self.arguments + ticket_query = arguments old_ticket_data = {} current_event = self.db.query(self.collection, query) if current_event: From d44217542cd1916cd7d85dfd5466b1977ae8da12 Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Sat, 15 Apr 2017 16:20:26 +0200 Subject: [PATCH 6/6] ability to specify a single code from the command line --- tools/qrcode_reader.py | 51 +++++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/tools/qrcode_reader.py b/tools/qrcode_reader.py index 1ee3c57..b52287c 100755 --- a/tools/qrcode_reader.py +++ b/tools/qrcode_reader.py @@ -25,6 +25,7 @@ import time import serial import urllib import logging +import argparse import requests import configparser from requests.packages.urllib3.exceptions import InsecureRequestWarning @@ -37,6 +38,31 @@ logging.getLogger('urllib3').setLevel(logging.WARNING) requests.packages.urllib3.disable_warnings(InsecureRequestWarning) +def convert_obj(obj): + try: + return int(obj) + except: + pass + if isinstance(obj, str): + obj_l = obj.lower() + if obj_l in ['true', 'on', 'yes']: + return True + elif obj_l in ['false', 'off', 'no']: + return False + return obj + + +def convert(seq): + if isinstance(seq, dict): + d = {} + for key, item in seq.items(): + d[key] = convert(item) + return d + if isinstance(seq, (list, tuple)): + return [convert(x) for x in seq] + return convert_obj(seq) + + class Connector(): def __init__(self, cfg): self.cfg = cfg @@ -75,7 +101,7 @@ class Connector(): code = code[:limit_field] params = {cfg['event']['field']: code, '_errorMessage': 'code: %s' % code} checkin_url = self.checkin_url + '?' + urllib.parse.urlencode(params) - json = dict(self.cfg['actions']) + json = convert(dict(self.cfg['actions'])) req = self.session.put(checkin_url, json=json) error = False try: @@ -114,13 +140,22 @@ def scan(port): if __name__ == '__main__': - cfg = configparser.ConfigParser() - cfg.read('qrcode_reader.ini') + parser = argparse.ArgumentParser() + parser.add_argument('-c', '--code', help='specify a single code', action='store') + parser.add_argument('--config', help='user a different configuration file (default: qrcode_reader.ini)', + action='store', default='qrcode_reader.ini') + args = parser.parse_args() + + cfg = configparser.ConfigParser(interpolation=configparser.ExtendedInterpolation()) + cfg.read(args.config) if cfg['qrcode_reader'].getboolean('debug'): logging.basicConfig(level=logging.DEBUG) connector = Connector(cfg) - try: - for code in scan(port=cfg['connection']['port']): - connector.checkin(code) - except KeyboardInterrupt: - logger.info('exiting...') + if args.code: + connector.checkin(args.code) + else: + try: + for code in scan(port=cfg['connection']['port']): + connector.checkin(code) + except KeyboardInterrupt: + logger.info('exiting...')