Repository: TabbycatDebate/tabbycat

Issue:
Use sockets for self checkin
Using `POST` messages for self checkin has no broadcast for the updated status, causing the change to not be reflected in the checkin views. This also has the reverse consequence that changes in status through the checkins sheet will not be reflected in the landing page (if an event happens while the participant has the page loaded). This is less important though. Don't think it is crucial but for the personal page, the events feed should be filtered to only include events relating to the subject.


Discussion:
The use of websockets themselves is not strictly required in order to broadcast an updated status. A POST message could do the same — it just needs to notify the relevant [consumer](https://channels.readthedocs.io/en/latest/topics/consumers.html) which then broadcasts the status.

In this case the `CheckInEventConsumer` should be notified in the original `POST`. If your interested in implementing this the code will be similar to that in `results/forms.py`:

```
                group_name = BallotResultConsumer.group_prefix + "_" + t.slug
                async_to_sync(get_channel_layer().group_send)(group_name, {
                    "type": "send_json",
                    "data": self.ballotsub.serialize_like_actionlog
                })
```

...but adapted to provide data in the same way that the `broadcast_checkin` method of `CheckInEventConsumer` does at present.
Thank you! I'll try.

Resolving patch:
diff --git a/tabbycat/checkins/views.py b/tabbycat/checkins/views.py
--- a/tabbycat/checkins/views.py
+++ b/tabbycat/checkins/views.py
@@ -1,5 +1,7 @@
 import json
 
+from asgiref.sync import async_to_sync
+from channels.layers import get_channel_layer
 from django.contrib import messages
 from django.core.exceptions import ObjectDoesNotExist
 from django.views.generic.base import TemplateView
@@ -15,6 +17,7 @@
 from utils.views import PostOnlyRedirectView
 from tournaments.mixins import PublicTournamentPageMixin, TournamentMixin
 
+from .consumers import CheckInEventConsumer
 from .models import Event, PersonIdentifier, VenueIdentifier
 from .utils import create_identifiers, get_unexpired_checkins
 
@@ -266,7 +269,6 @@ def post(self, request, *args, **kwargs):
         existing_checkin = checkins.filter(identifier=identifier)
         if action == 'revoke':
             if existing_checkin.exists():
-                existing_checkin.delete()
                 messages.success(self.request, _("You have revoked your check-in."))
             else:
                 messages.error(self.request, _("Whoops! Looks like your check-in was already revoked."))
@@ -274,11 +276,20 @@ def post(self, request, *args, **kwargs):
             if existing_checkin.exists():
                 messages.error(self.request, _("Whoops! Looks like you're already checked in."))
             else:
-                Event(identifier=identifier, tournament=t).save()
                 messages.success(self.request, _("You are now checked in."))
         else:
             return TemplateResponse(request=self.request, template='400.html', status=400)
 
+        group_name = CheckInEventConsumer.group_prefix + "_" + t.slug
+
+        # Override permissions check - no user but authenticated through URL
+        async_to_sync(get_channel_layer().group_send)(
+            group_name, {
+                'type': 'broadcast_checkin',
+                'content': { 'barcodes': [identifier.barcode], 'status': action == 'checkin', 'type': 'people', 'component_id': None }
+            }
+        )
+
         return super().post(request, *args, **kwargs)
 
     def get_redirect_url(self, *args, **kwargs):


Test patch: