Source code for otree_redwood.consumers

from channels import Group
from channels.generic.websockets import WebsocketConsumer
from django.contrib.contenttypes.models import ContentType
from django.db.models.signals import post_save
import django.dispatch
import importlib
import json
from otree.models.participant import Participant

from otree_redwood.models import Event, Connection
from otree_redwood import stats


[docs]def get_group(app_name, group_pk): with stats.track('fetch group') as obs: models_module = importlib.import_module('{}.models'.format(app_name)) return models_module.Group.objects.get(pk=group_pk)
[docs]class EventConsumer(WebsocketConsumer): url_pattern = ( r'^/redwood' + '/app-name/(?P<app_name>[^/]+)' '/group/(?P<group>[0-9]+)' + '/participant/(?P<participant_code>[a-zA-Z0-9_-]+)' + '/$')
[docs] def connection_groups(self, **kwargs): """ Called to return the list of groups to automatically add/remove this connection to/from. """ return [str(kwargs['group'])]
[docs] def connect(self, message, **kwargs): self.message.reply_channel.send({'accept': True}) group = get_group(kwargs['app_name'], kwargs['group']) participant = Participant.objects.get(code=kwargs['participant_code']) try: last_state = Event.objects.filter( channel='state', content_type=ContentType.objects.get_for_model(group), group_pk=group.pk).order_by('timestamp')[0] self.send({ 'channel': 'state', 'payload': last_state.value }) except IndexError: pass Connection.objects.get_or_create(participant=participant) group._on_connect(participant)
[docs] def disconnect(self, message, **kwargs): group = get_group(kwargs['app_name'], kwargs['group']) participant = Participant.objects.get(code=kwargs['participant_code']) try: # TODO: Clean out stale connections if not terminated cleanly. Connection.objects.get(participant=participant).delete() except Connection.DoesNotExist: pass group._on_disconnect(participant)
[docs] def raw_receive(self, message, **kwargs): content = json.loads(message['text']) for (key, value) in kwargs.items(): content[key] = value if content['channel'] == 'ping': with stats.track('recv_channel=ping'): if content['avg_ping_time']: stats.update('avg_ping_time', content['avg_ping_time']) self.send({ 'channel': 'ping', 'timestamp': content['timestamp'], }) return with stats.track('recv_channel=' + content['channel']): group = get_group(kwargs['app_name'], kwargs['group']) participant = Participant.objects.get(code=kwargs['participant_code']) with stats.track('saving event object to database'): event = Event.objects.create( group=group, participant=participant, channel=content['channel'], value=content['payload']) with stats.track('handing event to group'): try: event_handler = getattr(group, '_on_{}_event'.format(content['channel'])) event_handler(event) except AttributeError: pass
[docs] def send(self, content): self.message.reply_channel.send({'text': json.dumps(content)}, immediately=True)
[docs]class EventWatcher(WebsocketConsumer): url_pattern = r'^/redwood/events/session/(?P<session_code>[a-zA-Z0-9_-]+)/$'
[docs] def connection_groups(self, **kwargs): """ Called to return the list of groups to automatically add/remove this connection to/from. """ return ['events-' + kwargs['session_code']]
[docs] def connect(self, message, **kwargs): self.message.reply_channel.send({'accept': True})
[docs]@django.dispatch.receiver(post_save, sender=Event) def on_event_save(sender, instance, **kwargs): Group('events-' + instance.group.session.code).send({'text': json.dumps(instance.message)})