We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `tabbycat/checkins/views.py`
Content:
```
1 import json
2 
3 from django.contrib import messages
4 from django.core.exceptions import ObjectDoesNotExist
5 from django.views.generic.base import TemplateView
6 from django.template.response import TemplateResponse
7 from django.utils.translation import gettext as _
8 
9 from actionlog.mixins import LogActionMixin
10 from actionlog.models import ActionLogEntry
11 from options.utils import use_team_code_names
12 from participants.models import Person, Speaker
13 from utils.misc import reverse_tournament
14 from utils.mixins import AdministratorMixin, AssistantMixin
15 from utils.views import PostOnlyRedirectView
16 from tournaments.mixins import PublicTournamentPageMixin, TournamentMixin
17 
18 from .models import Event, PersonIdentifier, VenueIdentifier
19 from .utils import create_identifiers, get_unexpired_checkins
20 
21 
22 class CheckInPreScanView(TournamentMixin, TemplateView):
23     template_name = 'checkin_scan.html'
24     page_title = _('Scan Identifiers')
25     page_emoji = '📷'
26 
27     def get_context_data(self, **kwargs):
28         kwargs["scan_url"] = self.tournament.slug + '/checkins/'
29         return super().get_context_data(**kwargs)
30 
31 
32 class AdminCheckInPreScanView(AdministratorMixin, CheckInPreScanView):
33     scan_view = 'admin-checkin-scan'
34 
35 
36 class AssistantCheckInPreScanView(AssistantMixin, CheckInPreScanView):
37     scan_view = 'assistant-checkin-scan'
38 
39 
40 class BaseCheckInStatusView(TournamentMixin, TemplateView):
41     template_name = 'checkin_status.html'
42     scan_view = False
43 
44     def get_context_data(self, **kwargs):
45         events = get_unexpired_checkins(self.tournament, self.window_preference)
46         kwargs["events"] = json.dumps([e.serialize() for e in events])
47         if self.scan_view:
48             kwargs["scan_url"] = self.tournament.slug + '/checkins/'
49         return super().get_context_data(**kwargs)
50 
51 
52 class CheckInPeopleStatusView(BaseCheckInStatusView):
53     page_emoji = '⌚️'
54     page_title = _("People's Check-In Statuses")
55     window_preference = 'checkin_window_people'
56 
57     def get_context_data(self, **kwargs):
58 
59         for_admin = True
60         if hasattr(self, '_user_role') and self._user_role == 'public':
61             for_admin = False
62 
63         team_codes = use_team_code_names(self.tournament, admin=for_admin)
64         kwargs["team_codes"] = json.dumps(team_codes)
65 
66         adjudicators = []
67         for adj in self.tournament.relevant_adjudicators.all().select_related('institution', 'checkin_identifier'):
68             try:
69                 code = adj.checkin_identifier.barcode
70             except ObjectDoesNotExist:
71                 code = None
72 
73             adjudicators.append({
74                 'id': adj.id, 'name': adj.name, 'type': 'Adjudicator',
75                 'identifier': [code], 'locked': False, 'independent': adj.independent,
76                 'institution': adj.institution.serialize if adj.institution else None,
77             })
78         kwargs["adjudicators"] = json.dumps(adjudicators)
79 
80         speakers = []
81         for speaker in Speaker.objects.filter(team__tournament=self.tournament).select_related('team', 'team__institution', 'checkin_identifier'):
82             try:
83                 code = speaker.checkin_identifier.barcode
84             except ObjectDoesNotExist:
85                 code = None
86 
87             speakers.append({
88                 'id': speaker.id, 'name': speaker.name, 'type': 'Speaker',
89                 'identifier': [code], 'locked': False,
90                 'team': speaker.team.code_name if team_codes else speaker.team.short_name,
91                 'institution': speaker.team.institution.serialize if speaker.team.institution else None,
92             })
93         kwargs["speakers"] = json.dumps(speakers)
94 
95         return super().get_context_data(**kwargs)
96 
97 
98 class AdminCheckInPeopleStatusView(AdministratorMixin, CheckInPeopleStatusView):
99     scan_view = 'admin-checkin-scan'
100 
101 
102 class AssistantCheckInPeopleStatusView(AssistantMixin, CheckInPeopleStatusView):
103     scan_view = 'assistant-checkin-scan'
104 
105 
106 class PublicCheckInPeopleStatusView(PublicTournamentPageMixin, CheckInPeopleStatusView):
107     public_page_preference = 'public_checkins'
108 
109 
110 class CheckInVenuesStatusView(BaseCheckInStatusView):
111     page_emoji = '👜'
112     page_title = _("Venue's Check-In Statuses")
113     window_preference = 'checkin_window_venues'
114 
115     def get_context_data(self, **kwargs):
116         venues = []
117         for venue in self.tournament.relevant_venues.select_related('checkin_identifier').prefetch_related('venuecategory_set').all():
118             item = venue.serialize()
119             item['locked'] = False
120             try:
121                 item['identifier'] = [venue.checkin_identifier.barcode]
122             except ObjectDoesNotExist:
123                 item['identifier'] = [None]
124             venues.append(item)
125         kwargs["venues"] = json.dumps(venues)
126         kwargs["team_codes"] = json.dumps(False)
127 
128         return super().get_context_data(**kwargs)
129 
130 
131 class AdminCheckInVenuesStatusView(AdministratorMixin, CheckInVenuesStatusView):
132     scan_view = 'admin-checkin-scan'
133 
134 
135 class AssistantCheckInVenuesStatusView(AssistantMixin, CheckInVenuesStatusView):
136     scan_view = 'assistant-checkin-scan'
137 
138 
139 class SegregatedCheckinsMixin(TournamentMixin):
140 
141     def t_speakers(self):
142         return Speaker.objects.filter(
143             team__tournament=self.tournament).values_list(
144             'person_ptr_id', flat=True)
145 
146     def speakers_with_barcodes(self):
147         identifiers = PersonIdentifier.objects.all()
148         return identifiers.filter(person_id__in=self.t_speakers())
149 
150     def t_adjs(self):
151         return self.tournament.adjudicator_set.values_list(
152             'person_ptr_id', flat=True)
153 
154     def adjs_with_barcodes(self):
155         identifiers = PersonIdentifier.objects.all()
156         return identifiers.filter(person_id__in=self.t_adjs())
157 
158 
159 class CheckInIdentifiersView(SegregatedCheckinsMixin, TemplateView):
160     template_name = 'checkin_ids.html'
161     page_title = _('Make Identifiers')
162     page_emoji = '📛'
163 
164     def get_context_data(self, **kwargs):
165         t = self.tournament
166         kwargs["check_in_info"] = {
167             "speakers": {
168                 "title": _("Speakers"),
169                 "total": self.t_speakers().count(),
170                 "in":  self.speakers_with_barcodes().count()
171             },
172             "adjudicators": {
173                 "title": _("Adjudicators"),
174                 "total": self.t_adjs().count(),
175                 "in":  self.adjs_with_barcodes().count()
176             },
177             "venues": {
178                 "title": _("Venues"),
179                 "total": t.venue_set.count(),
180                 "in":  VenueIdentifier.objects.filter(venue__tournament=t).count(),
181             }
182         }
183         return super().get_context_data(**kwargs)
184 
185 
186 class AdminCheckInIdentifiersView(AdministratorMixin, CheckInIdentifiersView):
187     pass
188 
189 
190 class AssistantCheckInIdentifiersView(AssistantMixin, CheckInIdentifiersView):
191     pass
192 
193 
194 class AdminCheckInGenerateView(AdministratorMixin, LogActionMixin,
195                                TournamentMixin, PostOnlyRedirectView):
196 
197     def get_action_log_type(self):
198         if self.kwargs["kind"] == "speakers":
199             return ActionLogEntry.ACTION_TYPE_CHECKIN_SPEAK_GENERATE
200         elif self.kwargs["kind"] == "adjudicators":
201             return ActionLogEntry.ACTION_TYPE_CHECKIN_ADJ_GENERATE
202         elif self.kwargs["kind"] == "venues":
203             return ActionLogEntry.ACTION_TYPE_CHECKIN_VENUES_GENERATE
204 
205     # Providing tournament_slug_url_kwarg isn't working for some reason; so use:
206     def get_redirect_url(self, *args, **kwargs):
207         return reverse_tournament('admin-checkin-identifiers', self.tournament)
208 
209     def post(self, request, *args, **kwargs):
210         t = self.tournament
211 
212         if self.kwargs["kind"] == "speakers":
213             create_identifiers(PersonIdentifier, Speaker.objects.filter(team__tournament=t))
214         elif self.kwargs["kind"] == "adjudicators":
215             create_identifiers(PersonIdentifier, t.adjudicator_set.all())
216         elif self.kwargs["kind"] == "venues":
217             create_identifiers(VenueIdentifier, t.venue_set.all())
218 
219         messages.success(request, _("Generated identifiers for %s" % self.kwargs["kind"]))
220         self.log_action()  # Need to call explicitly
221         return super().post(request, *args, **kwargs)
222 
223 
224 class CheckInPrintablesView(SegregatedCheckinsMixin, TemplateView):
225     template_name = 'checkin_printables.html'
226     page_title = _('Identifiers')
227     page_emoji = '📛'
228 
229     def get_context_data(self, **kwargs):
230         if self.kwargs["kind"] == "speakers":
231             kwargs["identifiers"] = self.speakers_with_barcodes().order_by('person__name')
232         elif self.kwargs["kind"] == "adjudicators":
233             kwargs["identifiers"] = self.adjs_with_barcodes().order_by('person__name')
234         elif self.kwargs["kind"] == "venues":
235             venues = self.tournament.relevant_venues
236             kwargs["identifiers"] = VenueIdentifier.objects.filter(venue__in=venues)
237 
238         return super().get_context_data(**kwargs)
239 
240 
241 class AdminCheckInPrintablesView(AdministratorMixin, CheckInPrintablesView):
242     pass
243 
244 
245 class AssistantCheckInPrintablesView(AssistantMixin, CheckInPrintablesView):
246     pass
247 
248 
249 class ParticipantCheckinView(PublicTournamentPageMixin, PostOnlyRedirectView):
250 
251     public_page_preference = 'public_checkins_submit'
252 
253     def post(self, request, *args, **kwargs):
254         t = self.tournament
255 
256         action = request.POST['action']
257 
258         try:
259             person = Person.objects.get(url_key=kwargs['url_key'])
260             identifier = PersonIdentifier.objects.get(person=person)
261         except ObjectDoesNotExist:
262             messages.error(self.request, _("Could not check you in as you do not have an identifying code — your tab director may need to make you an identifier."))
263             return super().post(request, *args, **kwargs)
264 
265         checkins = get_unexpired_checkins(t, 'checkin_window_people')
266         existing_checkin = checkins.filter(identifier=identifier)
267         if action == 'revoke':
268             if existing_checkin.exists():
269                 existing_checkin.delete()
270                 messages.success(self.request, _("You have revoked your check-in."))
271             else:
272                 messages.error(self.request, _("Whoops! Looks like your check-in was already revoked."))
273         elif action == 'checkin':
274             if existing_checkin.exists():
275                 messages.error(self.request, _("Whoops! Looks like you're already checked in."))
276             else:
277                 Event(identifier=identifier, tournament=t).save()
278                 messages.success(self.request, _("You are now checked in."))
279         else:
280             return TemplateResponse(request=self.request, template='400.html', status=400)
281 
282         return super().post(request, *args, **kwargs)
283 
284     def get_redirect_url(self, *args, **kwargs):
285         return reverse_tournament('privateurls-person-index', self.tournament, kwargs={'url_key': kwargs['url_key']})
286
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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 @@
         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 @@
             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):

{"golden_diff": "diff --git a/tabbycat/checkins/views.py b/tabbycat/checkins/views.py\n--- a/tabbycat/checkins/views.py\n+++ b/tabbycat/checkins/views.py\n@@ -1,5 +1,7 @@\n import json\n \n+from asgiref.sync import async_to_sync\n+from channels.layers import get_channel_layer\n from django.contrib import messages\n from django.core.exceptions import ObjectDoesNotExist\n from django.views.generic.base import TemplateView\n@@ -15,6 +17,7 @@\n from utils.views import PostOnlyRedirectView\n from tournaments.mixins import PublicTournamentPageMixin, TournamentMixin\n \n+from .consumers import CheckInEventConsumer\n from .models import Event, PersonIdentifier, VenueIdentifier\n from .utils import create_identifiers, get_unexpired_checkins\n \n@@ -266,7 +269,6 @@\n         existing_checkin = checkins.filter(identifier=identifier)\n         if action == 'revoke':\n             if existing_checkin.exists():\n-                existing_checkin.delete()\n                 messages.success(self.request, _(\"You have revoked your check-in.\"))\n             else:\n                 messages.error(self.request, _(\"Whoops! Looks like your check-in was already revoked.\"))\n@@ -274,11 +276,20 @@\n             if existing_checkin.exists():\n                 messages.error(self.request, _(\"Whoops! Looks like you're already checked in.\"))\n             else:\n-                Event(identifier=identifier, tournament=t).save()\n                 messages.success(self.request, _(\"You are now checked in.\"))\n         else:\n             return TemplateResponse(request=self.request, template='400.html', status=400)\n \n+        group_name = CheckInEventConsumer.group_prefix + \"_\" + t.slug\n+\n+        # Override permissions check - no user but authenticated through URL\n+        async_to_sync(get_channel_layer().group_send)(\n+            group_name, {\n+                'type': 'broadcast_checkin',\n+                'content': { 'barcodes': [identifier.barcode], 'status': action == 'checkin', 'type': 'people', 'component_id': None }\n+            }\n+        )\n+\n         return super().post(request, *args, **kwargs)\n \n     def get_redirect_url(self, *args, **kwargs):\n", "issue": "Use sockets for self checkin\nUsing `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.\n", "before_files": [{"content": "import json\n\nfrom django.contrib import messages\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.views.generic.base import TemplateView\nfrom django.template.response import TemplateResponse\nfrom django.utils.translation import gettext as _\n\nfrom actionlog.mixins import LogActionMixin\nfrom actionlog.models import ActionLogEntry\nfrom options.utils import use_team_code_names\nfrom participants.models import Person, Speaker\nfrom utils.misc import reverse_tournament\nfrom utils.mixins import AdministratorMixin, AssistantMixin\nfrom utils.views import PostOnlyRedirectView\nfrom tournaments.mixins import PublicTournamentPageMixin, TournamentMixin\n\nfrom .models import Event, PersonIdentifier, VenueIdentifier\nfrom .utils import create_identifiers, get_unexpired_checkins\n\n\nclass CheckInPreScanView(TournamentMixin, TemplateView):\n    template_name = 'checkin_scan.html'\n    page_title = _('Scan Identifiers')\n    page_emoji = '\ud83d\udcf7'\n\n    def get_context_data(self, **kwargs):\n        kwargs[\"scan_url\"] = self.tournament.slug + '/checkins/'\n        return super().get_context_data(**kwargs)\n\n\nclass AdminCheckInPreScanView(AdministratorMixin, CheckInPreScanView):\n    scan_view = 'admin-checkin-scan'\n\n\nclass AssistantCheckInPreScanView(AssistantMixin, CheckInPreScanView):\n    scan_view = 'assistant-checkin-scan'\n\n\nclass BaseCheckInStatusView(TournamentMixin, TemplateView):\n    template_name = 'checkin_status.html'\n    scan_view = False\n\n    def get_context_data(self, **kwargs):\n        events = get_unexpired_checkins(self.tournament, self.window_preference)\n        kwargs[\"events\"] = json.dumps([e.serialize() for e in events])\n        if self.scan_view:\n            kwargs[\"scan_url\"] = self.tournament.slug + '/checkins/'\n        return super().get_context_data(**kwargs)\n\n\nclass CheckInPeopleStatusView(BaseCheckInStatusView):\n    page_emoji = '\u231a\ufe0f'\n    page_title = _(\"People's Check-In Statuses\")\n    window_preference = 'checkin_window_people'\n\n    def get_context_data(self, **kwargs):\n\n        for_admin = True\n        if hasattr(self, '_user_role') and self._user_role == 'public':\n            for_admin = False\n\n        team_codes = use_team_code_names(self.tournament, admin=for_admin)\n        kwargs[\"team_codes\"] = json.dumps(team_codes)\n\n        adjudicators = []\n        for adj in self.tournament.relevant_adjudicators.all().select_related('institution', 'checkin_identifier'):\n            try:\n                code = adj.checkin_identifier.barcode\n            except ObjectDoesNotExist:\n                code = None\n\n            adjudicators.append({\n                'id': adj.id, 'name': adj.name, 'type': 'Adjudicator',\n                'identifier': [code], 'locked': False, 'independent': adj.independent,\n                'institution': adj.institution.serialize if adj.institution else None,\n            })\n        kwargs[\"adjudicators\"] = json.dumps(adjudicators)\n\n        speakers = []\n        for speaker in Speaker.objects.filter(team__tournament=self.tournament).select_related('team', 'team__institution', 'checkin_identifier'):\n            try:\n                code = speaker.checkin_identifier.barcode\n            except ObjectDoesNotExist:\n                code = None\n\n            speakers.append({\n                'id': speaker.id, 'name': speaker.name, 'type': 'Speaker',\n                'identifier': [code], 'locked': False,\n                'team': speaker.team.code_name if team_codes else speaker.team.short_name,\n                'institution': speaker.team.institution.serialize if speaker.team.institution else None,\n            })\n        kwargs[\"speakers\"] = json.dumps(speakers)\n\n        return super().get_context_data(**kwargs)\n\n\nclass AdminCheckInPeopleStatusView(AdministratorMixin, CheckInPeopleStatusView):\n    scan_view = 'admin-checkin-scan'\n\n\nclass AssistantCheckInPeopleStatusView(AssistantMixin, CheckInPeopleStatusView):\n    scan_view = 'assistant-checkin-scan'\n\n\nclass PublicCheckInPeopleStatusView(PublicTournamentPageMixin, CheckInPeopleStatusView):\n    public_page_preference = 'public_checkins'\n\n\nclass CheckInVenuesStatusView(BaseCheckInStatusView):\n    page_emoji = '\ud83d\udc5c'\n    page_title = _(\"Venue's Check-In Statuses\")\n    window_preference = 'checkin_window_venues'\n\n    def get_context_data(self, **kwargs):\n        venues = []\n        for venue in self.tournament.relevant_venues.select_related('checkin_identifier').prefetch_related('venuecategory_set').all():\n            item = venue.serialize()\n            item['locked'] = False\n            try:\n                item['identifier'] = [venue.checkin_identifier.barcode]\n            except ObjectDoesNotExist:\n                item['identifier'] = [None]\n            venues.append(item)\n        kwargs[\"venues\"] = json.dumps(venues)\n        kwargs[\"team_codes\"] = json.dumps(False)\n\n        return super().get_context_data(**kwargs)\n\n\nclass AdminCheckInVenuesStatusView(AdministratorMixin, CheckInVenuesStatusView):\n    scan_view = 'admin-checkin-scan'\n\n\nclass AssistantCheckInVenuesStatusView(AssistantMixin, CheckInVenuesStatusView):\n    scan_view = 'assistant-checkin-scan'\n\n\nclass SegregatedCheckinsMixin(TournamentMixin):\n\n    def t_speakers(self):\n        return Speaker.objects.filter(\n            team__tournament=self.tournament).values_list(\n            'person_ptr_id', flat=True)\n\n    def speakers_with_barcodes(self):\n        identifiers = PersonIdentifier.objects.all()\n        return identifiers.filter(person_id__in=self.t_speakers())\n\n    def t_adjs(self):\n        return self.tournament.adjudicator_set.values_list(\n            'person_ptr_id', flat=True)\n\n    def adjs_with_barcodes(self):\n        identifiers = PersonIdentifier.objects.all()\n        return identifiers.filter(person_id__in=self.t_adjs())\n\n\nclass CheckInIdentifiersView(SegregatedCheckinsMixin, TemplateView):\n    template_name = 'checkin_ids.html'\n    page_title = _('Make Identifiers')\n    page_emoji = '\ud83d\udcdb'\n\n    def get_context_data(self, **kwargs):\n        t = self.tournament\n        kwargs[\"check_in_info\"] = {\n            \"speakers\": {\n                \"title\": _(\"Speakers\"),\n                \"total\": self.t_speakers().count(),\n                \"in\":  self.speakers_with_barcodes().count()\n            },\n            \"adjudicators\": {\n                \"title\": _(\"Adjudicators\"),\n                \"total\": self.t_adjs().count(),\n                \"in\":  self.adjs_with_barcodes().count()\n            },\n            \"venues\": {\n                \"title\": _(\"Venues\"),\n                \"total\": t.venue_set.count(),\n                \"in\":  VenueIdentifier.objects.filter(venue__tournament=t).count(),\n            }\n        }\n        return super().get_context_data(**kwargs)\n\n\nclass AdminCheckInIdentifiersView(AdministratorMixin, CheckInIdentifiersView):\n    pass\n\n\nclass AssistantCheckInIdentifiersView(AssistantMixin, CheckInIdentifiersView):\n    pass\n\n\nclass AdminCheckInGenerateView(AdministratorMixin, LogActionMixin,\n                               TournamentMixin, PostOnlyRedirectView):\n\n    def get_action_log_type(self):\n        if self.kwargs[\"kind\"] == \"speakers\":\n            return ActionLogEntry.ACTION_TYPE_CHECKIN_SPEAK_GENERATE\n        elif self.kwargs[\"kind\"] == \"adjudicators\":\n            return ActionLogEntry.ACTION_TYPE_CHECKIN_ADJ_GENERATE\n        elif self.kwargs[\"kind\"] == \"venues\":\n            return ActionLogEntry.ACTION_TYPE_CHECKIN_VENUES_GENERATE\n\n    # Providing tournament_slug_url_kwarg isn't working for some reason; so use:\n    def get_redirect_url(self, *args, **kwargs):\n        return reverse_tournament('admin-checkin-identifiers', self.tournament)\n\n    def post(self, request, *args, **kwargs):\n        t = self.tournament\n\n        if self.kwargs[\"kind\"] == \"speakers\":\n            create_identifiers(PersonIdentifier, Speaker.objects.filter(team__tournament=t))\n        elif self.kwargs[\"kind\"] == \"adjudicators\":\n            create_identifiers(PersonIdentifier, t.adjudicator_set.all())\n        elif self.kwargs[\"kind\"] == \"venues\":\n            create_identifiers(VenueIdentifier, t.venue_set.all())\n\n        messages.success(request, _(\"Generated identifiers for %s\" % self.kwargs[\"kind\"]))\n        self.log_action()  # Need to call explicitly\n        return super().post(request, *args, **kwargs)\n\n\nclass CheckInPrintablesView(SegregatedCheckinsMixin, TemplateView):\n    template_name = 'checkin_printables.html'\n    page_title = _('Identifiers')\n    page_emoji = '\ud83d\udcdb'\n\n    def get_context_data(self, **kwargs):\n        if self.kwargs[\"kind\"] == \"speakers\":\n            kwargs[\"identifiers\"] = self.speakers_with_barcodes().order_by('person__name')\n        elif self.kwargs[\"kind\"] == \"adjudicators\":\n            kwargs[\"identifiers\"] = self.adjs_with_barcodes().order_by('person__name')\n        elif self.kwargs[\"kind\"] == \"venues\":\n            venues = self.tournament.relevant_venues\n            kwargs[\"identifiers\"] = VenueIdentifier.objects.filter(venue__in=venues)\n\n        return super().get_context_data(**kwargs)\n\n\nclass AdminCheckInPrintablesView(AdministratorMixin, CheckInPrintablesView):\n    pass\n\n\nclass AssistantCheckInPrintablesView(AssistantMixin, CheckInPrintablesView):\n    pass\n\n\nclass ParticipantCheckinView(PublicTournamentPageMixin, PostOnlyRedirectView):\n\n    public_page_preference = 'public_checkins_submit'\n\n    def post(self, request, *args, **kwargs):\n        t = self.tournament\n\n        action = request.POST['action']\n\n        try:\n            person = Person.objects.get(url_key=kwargs['url_key'])\n            identifier = PersonIdentifier.objects.get(person=person)\n        except ObjectDoesNotExist:\n            messages.error(self.request, _(\"Could not check you in as you do not have an identifying code \u2014 your tab director may need to make you an identifier.\"))\n            return super().post(request, *args, **kwargs)\n\n        checkins = get_unexpired_checkins(t, 'checkin_window_people')\n        existing_checkin = checkins.filter(identifier=identifier)\n        if action == 'revoke':\n            if existing_checkin.exists():\n                existing_checkin.delete()\n                messages.success(self.request, _(\"You have revoked your check-in.\"))\n            else:\n                messages.error(self.request, _(\"Whoops! Looks like your check-in was already revoked.\"))\n        elif action == 'checkin':\n            if existing_checkin.exists():\n                messages.error(self.request, _(\"Whoops! Looks like you're already checked in.\"))\n            else:\n                Event(identifier=identifier, tournament=t).save()\n                messages.success(self.request, _(\"You are now checked in.\"))\n        else:\n            return TemplateResponse(request=self.request, template='400.html', status=400)\n\n        return super().post(request, *args, **kwargs)\n\n    def get_redirect_url(self, *args, **kwargs):\n        return reverse_tournament('privateurls-person-index', self.tournament, kwargs={'url_key': kwargs['url_key']})\n", "path": "tabbycat/checkins/views.py"}], "after_files": [{"content": "import json\n\nfrom asgiref.sync import async_to_sync\nfrom channels.layers import get_channel_layer\nfrom django.contrib import messages\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.views.generic.base import TemplateView\nfrom django.template.response import TemplateResponse\nfrom django.utils.translation import gettext as _\n\nfrom actionlog.mixins import LogActionMixin\nfrom actionlog.models import ActionLogEntry\nfrom options.utils import use_team_code_names\nfrom participants.models import Person, Speaker\nfrom utils.misc import reverse_tournament\nfrom utils.mixins import AdministratorMixin, AssistantMixin\nfrom utils.views import PostOnlyRedirectView\nfrom tournaments.mixins import PublicTournamentPageMixin, TournamentMixin\n\nfrom .consumers import CheckInEventConsumer\nfrom .models import Event, PersonIdentifier, VenueIdentifier\nfrom .utils import create_identifiers, get_unexpired_checkins\n\n\nclass CheckInPreScanView(TournamentMixin, TemplateView):\n    template_name = 'checkin_scan.html'\n    page_title = _('Scan Identifiers')\n    page_emoji = '\ud83d\udcf7'\n\n    def get_context_data(self, **kwargs):\n        kwargs[\"scan_url\"] = self.tournament.slug + '/checkins/'\n        return super().get_context_data(**kwargs)\n\n\nclass AdminCheckInPreScanView(AdministratorMixin, CheckInPreScanView):\n    scan_view = 'admin-checkin-scan'\n\n\nclass AssistantCheckInPreScanView(AssistantMixin, CheckInPreScanView):\n    scan_view = 'assistant-checkin-scan'\n\n\nclass BaseCheckInStatusView(TournamentMixin, TemplateView):\n    template_name = 'checkin_status.html'\n    scan_view = False\n\n    def get_context_data(self, **kwargs):\n        events = get_unexpired_checkins(self.tournament, self.window_preference)\n        kwargs[\"events\"] = json.dumps([e.serialize() for e in events])\n        if self.scan_view:\n            kwargs[\"scan_url\"] = self.tournament.slug + '/checkins/'\n        return super().get_context_data(**kwargs)\n\n\nclass CheckInPeopleStatusView(BaseCheckInStatusView):\n    page_emoji = '\u231a\ufe0f'\n    page_title = _(\"People's Check-In Statuses\")\n    window_preference = 'checkin_window_people'\n\n    def get_context_data(self, **kwargs):\n\n        for_admin = True\n        if hasattr(self, '_user_role') and self._user_role == 'public':\n            for_admin = False\n\n        team_codes = use_team_code_names(self.tournament, admin=for_admin)\n        kwargs[\"team_codes\"] = json.dumps(team_codes)\n\n        adjudicators = []\n        for adj in self.tournament.relevant_adjudicators.all().select_related('institution', 'checkin_identifier'):\n            try:\n                code = adj.checkin_identifier.barcode\n            except ObjectDoesNotExist:\n                code = None\n\n            adjudicators.append({\n                'id': adj.id, 'name': adj.name, 'type': 'Adjudicator',\n                'identifier': [code], 'locked': False, 'independent': adj.independent,\n                'institution': adj.institution.serialize if adj.institution else None,\n            })\n        kwargs[\"adjudicators\"] = json.dumps(adjudicators)\n\n        speakers = []\n        for speaker in Speaker.objects.filter(team__tournament=self.tournament).select_related('team', 'team__institution', 'checkin_identifier'):\n            try:\n                code = speaker.checkin_identifier.barcode\n            except ObjectDoesNotExist:\n                code = None\n\n            speakers.append({\n                'id': speaker.id, 'name': speaker.name, 'type': 'Speaker',\n                'identifier': [code], 'locked': False,\n                'team': speaker.team.code_name if team_codes else speaker.team.short_name,\n                'institution': speaker.team.institution.serialize if speaker.team.institution else None,\n            })\n        kwargs[\"speakers\"] = json.dumps(speakers)\n\n        return super().get_context_data(**kwargs)\n\n\nclass AdminCheckInPeopleStatusView(AdministratorMixin, CheckInPeopleStatusView):\n    scan_view = 'admin-checkin-scan'\n\n\nclass AssistantCheckInPeopleStatusView(AssistantMixin, CheckInPeopleStatusView):\n    scan_view = 'assistant-checkin-scan'\n\n\nclass PublicCheckInPeopleStatusView(PublicTournamentPageMixin, CheckInPeopleStatusView):\n    public_page_preference = 'public_checkins'\n\n\nclass CheckInVenuesStatusView(BaseCheckInStatusView):\n    page_emoji = '\ud83d\udc5c'\n    page_title = _(\"Venue's Check-In Statuses\")\n    window_preference = 'checkin_window_venues'\n\n    def get_context_data(self, **kwargs):\n        venues = []\n        for venue in self.tournament.relevant_venues.select_related('checkin_identifier').prefetch_related('venuecategory_set').all():\n            item = venue.serialize()\n            item['locked'] = False\n            try:\n                item['identifier'] = [venue.checkin_identifier.barcode]\n            except ObjectDoesNotExist:\n                item['identifier'] = [None]\n            venues.append(item)\n        kwargs[\"venues\"] = json.dumps(venues)\n        kwargs[\"team_codes\"] = json.dumps(False)\n\n        return super().get_context_data(**kwargs)\n\n\nclass AdminCheckInVenuesStatusView(AdministratorMixin, CheckInVenuesStatusView):\n    scan_view = 'admin-checkin-scan'\n\n\nclass AssistantCheckInVenuesStatusView(AssistantMixin, CheckInVenuesStatusView):\n    scan_view = 'assistant-checkin-scan'\n\n\nclass SegregatedCheckinsMixin(TournamentMixin):\n\n    def t_speakers(self):\n        return Speaker.objects.filter(\n            team__tournament=self.tournament).values_list(\n            'person_ptr_id', flat=True)\n\n    def speakers_with_barcodes(self):\n        identifiers = PersonIdentifier.objects.all()\n        return identifiers.filter(person_id__in=self.t_speakers())\n\n    def t_adjs(self):\n        return self.tournament.adjudicator_set.values_list(\n            'person_ptr_id', flat=True)\n\n    def adjs_with_barcodes(self):\n        identifiers = PersonIdentifier.objects.all()\n        return identifiers.filter(person_id__in=self.t_adjs())\n\n\nclass CheckInIdentifiersView(SegregatedCheckinsMixin, TemplateView):\n    template_name = 'checkin_ids.html'\n    page_title = _('Make Identifiers')\n    page_emoji = '\ud83d\udcdb'\n\n    def get_context_data(self, **kwargs):\n        t = self.tournament\n        kwargs[\"check_in_info\"] = {\n            \"speakers\": {\n                \"title\": _(\"Speakers\"),\n                \"total\": self.t_speakers().count(),\n                \"in\":  self.speakers_with_barcodes().count()\n            },\n            \"adjudicators\": {\n                \"title\": _(\"Adjudicators\"),\n                \"total\": self.t_adjs().count(),\n                \"in\":  self.adjs_with_barcodes().count()\n            },\n            \"venues\": {\n                \"title\": _(\"Venues\"),\n                \"total\": t.venue_set.count(),\n                \"in\":  VenueIdentifier.objects.filter(venue__tournament=t).count(),\n            }\n        }\n        return super().get_context_data(**kwargs)\n\n\nclass AdminCheckInIdentifiersView(AdministratorMixin, CheckInIdentifiersView):\n    pass\n\n\nclass AssistantCheckInIdentifiersView(AssistantMixin, CheckInIdentifiersView):\n    pass\n\n\nclass AdminCheckInGenerateView(AdministratorMixin, LogActionMixin,\n                               TournamentMixin, PostOnlyRedirectView):\n\n    def get_action_log_type(self):\n        if self.kwargs[\"kind\"] == \"speakers\":\n            return ActionLogEntry.ACTION_TYPE_CHECKIN_SPEAK_GENERATE\n        elif self.kwargs[\"kind\"] == \"adjudicators\":\n            return ActionLogEntry.ACTION_TYPE_CHECKIN_ADJ_GENERATE\n        elif self.kwargs[\"kind\"] == \"venues\":\n            return ActionLogEntry.ACTION_TYPE_CHECKIN_VENUES_GENERATE\n\n    # Providing tournament_slug_url_kwarg isn't working for some reason; so use:\n    def get_redirect_url(self, *args, **kwargs):\n        return reverse_tournament('admin-checkin-identifiers', self.tournament)\n\n    def post(self, request, *args, **kwargs):\n        t = self.tournament\n\n        if self.kwargs[\"kind\"] == \"speakers\":\n            create_identifiers(PersonIdentifier, Speaker.objects.filter(team__tournament=t))\n        elif self.kwargs[\"kind\"] == \"adjudicators\":\n            create_identifiers(PersonIdentifier, t.adjudicator_set.all())\n        elif self.kwargs[\"kind\"] == \"venues\":\n            create_identifiers(VenueIdentifier, t.venue_set.all())\n\n        messages.success(request, _(\"Generated identifiers for %s\" % self.kwargs[\"kind\"]))\n        self.log_action()  # Need to call explicitly\n        return super().post(request, *args, **kwargs)\n\n\nclass CheckInPrintablesView(SegregatedCheckinsMixin, TemplateView):\n    template_name = 'checkin_printables.html'\n    page_title = _('Identifiers')\n    page_emoji = '\ud83d\udcdb'\n\n    def get_context_data(self, **kwargs):\n        if self.kwargs[\"kind\"] == \"speakers\":\n            kwargs[\"identifiers\"] = self.speakers_with_barcodes().order_by('person__name')\n        elif self.kwargs[\"kind\"] == \"adjudicators\":\n            kwargs[\"identifiers\"] = self.adjs_with_barcodes().order_by('person__name')\n        elif self.kwargs[\"kind\"] == \"venues\":\n            venues = self.tournament.relevant_venues\n            kwargs[\"identifiers\"] = VenueIdentifier.objects.filter(venue__in=venues)\n\n        return super().get_context_data(**kwargs)\n\n\nclass AdminCheckInPrintablesView(AdministratorMixin, CheckInPrintablesView):\n    pass\n\n\nclass AssistantCheckInPrintablesView(AssistantMixin, CheckInPrintablesView):\n    pass\n\n\nclass ParticipantCheckinView(PublicTournamentPageMixin, PostOnlyRedirectView):\n\n    public_page_preference = 'public_checkins_submit'\n\n    def post(self, request, *args, **kwargs):\n        t = self.tournament\n\n        action = request.POST['action']\n\n        try:\n            person = Person.objects.get(url_key=kwargs['url_key'])\n            identifier = PersonIdentifier.objects.get(person=person)\n        except ObjectDoesNotExist:\n            messages.error(self.request, _(\"Could not check you in as you do not have an identifying code \u2014 your tab director may need to make you an identifier.\"))\n            return super().post(request, *args, **kwargs)\n\n        checkins = get_unexpired_checkins(t, 'checkin_window_people')\n        existing_checkin = checkins.filter(identifier=identifier)\n        if action == 'revoke':\n            if existing_checkin.exists():\n                messages.success(self.request, _(\"You have revoked your check-in.\"))\n            else:\n                messages.error(self.request, _(\"Whoops! Looks like your check-in was already revoked.\"))\n        elif action == 'checkin':\n            if existing_checkin.exists():\n                messages.error(self.request, _(\"Whoops! Looks like you're already checked in.\"))\n            else:\n                messages.success(self.request, _(\"You are now checked in.\"))\n        else:\n            return TemplateResponse(request=self.request, template='400.html', status=400)\n\n        group_name = CheckInEventConsumer.group_prefix + \"_\" + t.slug\n\n        # Override permissions check - no user but authenticated through URL\n        async_to_sync(get_channel_layer().group_send)(\n            group_name, {\n                'type': 'broadcast_checkin',\n                'content': { 'barcodes': [identifier.barcode], 'status': action == 'checkin', 'type': 'people', 'component_id': None }\n            }\n        )\n\n        return super().post(request, *args, **kwargs)\n\n    def get_redirect_url(self, *args, **kwargs):\n        return reverse_tournament('privateurls-person-index', self.tournament, kwargs={'url_key': kwargs['url_key']})\n", "path": "tabbycat/checkins/views.py"}]}