improve WebSocket handling

This commit is contained in:
Davide Alberani 2016-07-10 13:10:00 +02:00
parent 24d6689abe
commit c183595124
5 changed files with 87 additions and 47 deletions

View file

@ -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">

View file

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

View file

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

View file

@ -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
"""

View file

@ -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."""