Browse Source

improve WebSocket handling

Davide Alberani 7 years ago
parent
commit
c183595124

+ 1 - 1
angular_app/event-tickets.html

@@ -51,7 +51,7 @@
                                                 <a ui-sref="event.ticket.edit({id: event._id, ticket_id: ticket._id})"><span>{{ticket.name}}</span>&nbsp;<span>{{ticket.surname}}</span></a>
                                             </strong>
                                         </span>
-                                        <span ng-if="ticket.email">&nbsp;&lt;{{ticket.email}}&gt;</span>
+                                        <span ng-if="ticket.email">&nbsp;&lt;{{ticket.email}}&gt;</span><span ng-if="ticket.cancelled">&nbsp;({{'cancelled' | translate}})</span>
                                         <p ng-if="ticket.company || ticket.job_title"><i ng-if="ticket.job_title">{{ticket.job_title}}</i><span ng-if="ticket.company && ticket.job_title">&nbsp;@&nbsp;</span><i ng-if="ticket.company">{{ticket.company}}</i></p>
                                     </td>
                                     <td class="text-center">

+ 42 - 11
angular_app/js/controllers.js

@@ -179,7 +179,7 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
         $scope.formData = {};
         $scope.guiOptions = {dangerousActionsEnabled: false};
         $scope.customFields = Setting.query({setting: 'ticket_custom_field', in_event_details: true});
-        $scope.registeredFilterOptions = {all: true};
+        $scope.registeredFilterOptions = {all: false};
 
         $scope.formFieldsMap = {};
         $scope.formFieldsMapRev = {};
@@ -224,10 +224,12 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
                         return $scope.EventUpdates.data;
                     }, function(new_collection, old_collection) {
                         if (!($scope.EventUpdates.data && $scope.EventUpdates.data.update)) {
+                            $log.debug('no data received from the WebSocket');
                             return;
                         }
                         var data = $scope.EventUpdates.data.update;
-                        $log.debug('received ' + data.action + ' action from websocket source ' + data.uuid);
+                        $log.debug('received ' + data.action + ' action from websocket source ' + data.uuid + ' . Full data:');
+                        $log.debug(data);
                         if ($rootScope.app_uuid == data.uuid) {
                             $log.debug('do not process our own message');
                             return false;
@@ -235,8 +237,9 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
                         if (!$scope.event.tickets) {
                             $scope.event.tickets = [];
                         }
+                        var ticket_id = data._id || (data.ticket && data.ticket._id);
                         var ticket_idx = $scope.event.tickets.findIndex(function(el, idx, array) {
-                            return data._id == el._id;
+                            return ticket_id && (ticket_id == el._id);
                         });
                         if (ticket_idx != -1) {
                             $log.debug('_id ' + data._id + ' found');
@@ -415,17 +418,46 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
             });
         };
 
+        /* event listners; needed because otherwise, adding a ticket with the Quick add form,
+         * we'd be changing the $scope outside of the AngularJS's $digest. */
+
+        $rootScope.$on('event:ticket:new', function(evt, ticket, callback) {
+            $scope._localAddTicket(ticket);
+            if (callback) {
+                callback(ticket);
+            }
+        });
+
+        $rootScope.$on('event:ticket:update', function(evt, ticket) {
+            if (!$scope.event.tickets) {
+                $scope.event.tickets = [];
+            }
+            var ticket_idx = $scope.event.tickets.findIndex(function(el, idx, array) {
+                    return ticket._id == el._id;
+            });
+            if (ticket_idx == -1) {
+                $log.debug('ticket not present: not updated');
+                return false;
+            }
+            $scope.event.tickets[ticket_idx] = ticket;
+        });
+
+        $rootScope.$on('event:ticket:set-attr', function(evt, ticket, key, value, callback, hideMessage) {
+            $scope.setTicketAttribute(ticket, key, value, callback, hideMessage);
+        });
+
         $scope.addTicket = function(ticket) {
             ticket.event_id = $state.params.id;
             EventTicket.add(ticket, function(ret_ticket) {
                 $log.debug('addTicket');
                 $log.debug(ret_ticket);
-                $scope._localAddTicket(ret_ticket, ticket);
+                $rootScope.$emit('event:ticket:new', ret_ticket, function() {
+                    $rootScope.$emit('event:ticket:set-attr', ret_ticket, 'attended', true, null, true);
+                });
                 if (!$state.is('event.tickets')) {
                     $state.go('event.ticket.edit', {id: $scope.event._id, ticket_id: ret_ticket._id});
                 } else {
                     $scope.query = '';
-                    $scope._setAttended(ret_ticket);
                     if ($scope.$close) {
                         // Close the Quick ticket modal.
                         $scope.$close();
@@ -437,7 +469,7 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
         $scope.updateTicket = function(ticket, cb) {
             ticket.event_id = $state.params.id;
             EventTicket.update(ticket, function(t) {
-                $scope._localUpdateTicket(t.ticket);
+                $rootScope.$emit('event:ticket:update', t.ticket);
                 if (cb) {
                     cb(t);
                 }
@@ -459,8 +491,7 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
                 templateUrl: 'modal-quick-add-ticket.html',
                 controller: 'EventTicketsCtrl'
             });
-            modalInstance.result.then(function() {
-            });
+            modalInstance.result.then(function() {});
         };
 
         $scope.submitForm = function(dataModelSubmitted) {
@@ -468,10 +499,10 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
                 key = $scope.formFieldsMap[key] || key;
                 $scope.ticket[key] = value;
             });
-            if (!$state.params.ticket_id) {
-                $scope.addTicket($scope.ticket);
-            } else {
+            if ($state.is('event.ticket.edit')) {
                 $scope.updateTicket($scope.ticket);
+            } else {
+                $scope.addTicket($scope.ticket);
             }
         };
 

+ 8 - 7
angular_app/js/services.js

@@ -241,8 +241,8 @@ eventManServices.factory('User', ['$resource', '$rootScope',
 
 
 /* WebSocket collection used to update the list of tickets of an Event. */
-eventManApp.factory('EventUpdates', ['$websocket', '$location', '$log',
-    function($websocket, $location, $log) {
+eventManApp.factory('EventUpdates', ['$websocket', '$location', '$log', '$rootScope',
+    function($websocket, $location, $log, $rootScope) {
         var dataStream = null;
         var data = {};
 
@@ -253,18 +253,19 @@ eventManApp.factory('EventUpdates', ['$websocket', '$location', '$log',
                 dataStream.close();
             },
             open: function() {
-                $log.debug('open WebSocket connection');
-                dataStream && dataStream.close();
                 var proto = $location.protocol() == 'https' ? 'wss' : 'ws';
-                dataStream = $websocket(proto + '://' + $location.host() + ':' + $location.port() +
-                                    '/ws/' + $location.path() + '/updates');
+                var url = proto + '://' + $location.host() + ':' + $location.port() +
+                          '/ws/' + $location.path() + '/updates?uuid=' + $rootScope.app_uuid;
+                $log.debug('open WebSocket connection to ' + url);
+                //dataStream && dataStream.close();
+                dataStream = $websocket(url);
+
                 dataStream.onMessage(function(message) {
                     $log.debug('EventUpdates message received');
                     data.update = angular.fromJson(message.data);
                 });
             }
         };
-
         return methods;
     }]
 );

+ 4 - 4
data/triggers-available/print_label.py

@@ -1,9 +1,9 @@
 #!/usr/bin/env python
-"""print_label.py - print a label with the name, the company and the person_id (in a barcode) of an attendee
+"""print_label.py - print a label with the name, the company and SEQ_HEX (in a barcode) of an attendee
 
-Copyright 2015 Emiliano Mattioli <oloturia AT gmail.com>
-               Davide Alberani <da@erlug.linux.it>
-               RaspiBO <info@raspibo.org>
+Copyright 2015-2016 Emiliano Mattioli <oloturia AT gmail.com>
+                    Davide Alberani <da@erlug.linux.it>
+                    RaspiBO <info@raspibo.org>
 
 Licensed under the Apache License 2.0
 """

+ 32 - 24
eventman_server.py

@@ -540,7 +540,11 @@ class CollectionHandler(BaseHandler):
 
     def build_ws_url(self, path, proto='ws', host=None):
         """Return a WebSocket url from a path."""
-        return 'ws://127.0.0.1:%s/ws/%s' % (self.listen_port + 1, path)
+        try:
+            args = '?uuid=%s' % self.get_argument('uuid')
+        except:
+            args = ''
+        return 'ws://127.0.0.1:%s/ws/%s%s' % (self.listen_port + 1, path, args)
 
     @gen.coroutine
     def send_ws_message(self, path, message):
@@ -647,12 +651,7 @@ class EventsHandler(CollectionHandler):
                 operation='appendUnique',
                 create=False)
         if doc:
-            msg_ret = ret.copy()
-            msg_ret['ticket'] = msg_ret['ticket'].copy()
-            # Do not send ticket IDs over the WebSocket.
-            if '_id' in msg_ret['ticket']:
-                del msg_ret['ticket']['_id']
-            self.send_ws_message('event/%s/tickets/updates' % id_, json.dumps(msg_ret))
+            self.send_ws_message('event/%s/tickets/updates' % id_, json.dumps(ret))
         return ret
 
     def handle_put_tickets(self, id_, ticket_id, data):
@@ -877,33 +876,42 @@ class InfoHandler(BaseHandler):
 
 
 class WebSocketEventUpdatesHandler(tornado.websocket.WebSocketHandler):
-    """Manage websockets."""
+    """Manage WebSockets."""
     def _clean_url(self, url):
-        return re_slashes.sub('/', url)
+        url = re_slashes.sub('/', url)
+        ridx = url.rfind('?')
+        if ridx != -1:
+            url = url[:ridx]
+        return url
 
     def open(self, event_id, *args, **kwargs):
-        logging.debug('WebSocketEventUpdatesHandler.on_open event_id:%s' % event_id)
-        _ws_clients.setdefault(self._clean_url(self.request.uri), set()).add(self)
-        logging.debug('WebSocketEventUpdatesHandler.on_open %s clients connected' % len(_ws_clients))
+        self.uuid = self.get_argument('uuid')
+        url = self._clean_url(self.request.uri)
+        logging.debug('WebSocketEventUpdatesHandler.on_open event_id:%s url:%s' % (event_id, url))
+        _ws_clients.setdefault(url, {})
+        if self.uuid not in _ws_clients[url]:
+            _ws_clients[url][self.uuid] = self
+        logging.debug('WebSocketEventUpdatesHandler.on_open %s clients connected' % len(_ws_clients[url]))
 
     def on_message(self, message):
-        logging.debug('WebSocketEventUpdatesHandler.on_message')
+        url = self._clean_url(self.request.uri)
+        logging.debug('WebSocketEventUpdatesHandler.on_message url:%s' % url)
         count = 0
-        for client in _ws_clients.get(self._clean_url(self.request.uri), []):
-            if client == self:
+        _to_delete = set()
+        for uuid, client in _ws_clients.get(url, {}).iteritems():
+            try:
+                client.write_message(message)
+            except:
+                _to_delete.add(uuid)
                 continue
-            client.write_message(message)
             count += 1
+        for uuid in _to_delete:
+            try:
+                del _ws_clients[url][uuid]
+            except KeyError:
+                pass
         logging.debug('WebSocketEventUpdatesHandler.on_message sent message to %d clients' % count)
 
-    def on_close(self):
-        logging.debug('WebSocketEventUpdatesHandler.on_close')
-        try:
-            if self in _ws_clients.get(self._clean_url(self.request.uri), []):
-                _ws_clients[self._clean_url(self.request.uri)].remove(self)
-        except Exception as e:
-            logging.warn('WebSocketEventUpdatesHandler.on_close error closing websocket: %s', str(e))
-
 
 class LoginHandler(RootHandler):
     """Handle user authentication requests."""