We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Include QR code in private URL distribution pages
It is a bit odd to hand people a physical item directing them to the internet, but typing a random string is not really user-friendly. QR codes that directs to the URL could be added to the URL sheets for ease of use. The type of barcode that is in use can't encode as long a string, but a QR code is recognized to be scanned to lead to a website and seems to be well supported by phones.

This may require another dependency to generate these codes; `jsBarcode` can't generate them.

--- 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/printing/views.py`
Content:
```
1 import json
2 
3 from django.db.models import Q
4 from django.utils.translation import gettext as _
5 from django.views.generic.base import TemplateView
6 
7 from adjfeedback.models import AdjudicatorFeedbackQuestion
8 from adjfeedback.utils import expected_feedback_targets
9 from checkins.models import DebateIdentifier
10 from checkins.utils import create_identifiers
11 from draw.models import Debate, DebateTeam
12 from options.utils import use_team_code_names
13 from participants.models import Person
14 from results.utils import side_and_position_names
15 from tournaments.mixins import (CurrentRoundMixin, OptionalAssistantTournamentPageMixin,
16                                 RoundMixin, TournamentMixin)
17 from tournaments.models import Tournament
18 from utils.mixins import AdministratorMixin
19 from venues.models import VenueCategory
20 
21 
22 class MasterSheetsListView(AdministratorMixin, RoundMixin, TemplateView):
23     template_name = 'division_sheets_list.html'
24 
25     def get_context_data(self, **kwargs):
26         kwargs['standings'] = VenueCategory.objects.all()
27         kwargs['venue_categories'] = VenueCategory.objects.all()
28         return super().get_context_data(**kwargs)
29 
30 
31 class MasterSheetsView(AdministratorMixin, RoundMixin, TemplateView):
32     template_name = 'master_sheets_view.html'
33 
34     def get_context_data(self, **kwargs):
35         venue_category_id = self.kwargs['venue_category_id']
36         base_venue_category = VenueCategory.objects.get(id=venue_category_id)
37         active_tournaments = Tournament.objects.filter(active=True)
38         for tournament in list(active_tournaments):
39             tournament.debates = Debate.objects.select_related(
40                 'division', 'division__venue_category', 'round',
41                 'round__tournament').filter(
42                     # All Debates, with a matching round, at the same venue category name
43                     round__seq=self.round.seq,
44                     round__tournament=tournament,
45                     # Hack - remove when venue category are unified
46                     division__venue_category__name=base_venue_category.name
47             ).order_by('round', 'division__venue_category__name', 'division')
48 
49         kwargs['base_venue_category'] = base_venue_category
50         kwargs['active_tournaments'] = active_tournaments
51         return super().get_context_data(**kwargs)
52 
53 
54 class RoomSheetsView(AdministratorMixin, RoundMixin, TemplateView):
55     template_name = 'room_sheets_view.html'
56 
57     def get_context_data(self, **kwargs):
58         venue_category_id = self.kwargs['venue_category_id']
59         base_venue_category = VenueCategory.objects.get(id=venue_category_id)
60         venues_list = []
61 
62         # Get a unique list of venue names (avoid getting duplicates across tournaments)
63         for venue in set(base_venue_category.venues.order_by('name').values_list('name', flat=True)):
64             venues_list.append({'name': venue, 'debates': []})
65             # All Debates, with a matching round, at the same venue category
66             venues_list[-1]['debates'] = Debate.objects.filter(
67                 round__seq=self.round.seq, venue__name=venue).order_by('round__tournament__seq').all()
68             print(venues_list[-1])
69 
70         kwargs['base_venue_category'] = base_venue_category
71         kwargs['venues'] = venues_list
72         return super().get_context_data(**kwargs)
73 
74 
75 class BasePrintFeedbackFormsView(RoundMixin, TemplateView):
76 
77     template_name = 'feedback_list.html'
78 
79     def add_defaults(self):
80         default_questions = []
81 
82         if self.tournament.pref('feedback_introduction'):
83             default_scale_info = AdjudicatorFeedbackQuestion(
84                 text=self.tournament.pref('feedback_introduction'), seq=0,
85                 answer_type='comment', # Custom type just for print display
86                 required=True, from_team=True, from_adj=True
87             )
88             default_questions.append(default_scale_info.serialize())
89 
90         default_scale_question = AdjudicatorFeedbackQuestion(
91             text=_("Overall Score"), seq=0,
92             answer_type=AdjudicatorFeedbackQuestion.ANSWER_TYPE_INTEGER_SCALE,
93             required=True, from_team=True, from_adj=True,
94             min_value=self.tournament.pref('adj_min_score'),
95             max_value=self.tournament.pref('adj_max_score')
96         )
97         default_questions.append(default_scale_question.serialize())
98 
99         return default_questions
100 
101     def questions_dict(self):
102         questions = self.add_defaults()
103         for question in self.tournament.adj_feedback_questions:
104             questions.append(question.serialize())
105 
106         return questions
107 
108     def construct_info(self, venue, source, source_p, target, target_p):
109         if hasattr(source, 'name'):
110             source_n = source.name
111         elif use_team_code_names(self.tournament, False):
112             source_n = source.code_name
113         else:
114             source_n = source.short_name
115 
116         return {
117             'venue': venue.serialize() if venue else '',
118             'authorInstitution': source.institution.code if source.institution else _("Unaffiliated"),
119             'author': source_n, 'authorPosition': source_p,
120             'target': target.name, 'targetPosition': target_p,
121         }
122 
123     def get_team_feedbacks(self, debate, team):
124         if len(debate.adjudicators) is 0:
125             return []
126 
127         team_paths = self.tournament.pref('feedback_from_teams')
128         ballots = []
129 
130         if team_paths == 'orallist' and debate.adjudicators.chair:
131             ballots.append(self.construct_info(debate.venue, team, _("Team"),
132                                                debate.adjudicators.chair, ""))
133         elif team_paths == 'all-adjs':
134             for target in debate.adjudicators.all():
135                 ballots.append(self.construct_info(debate.venue, team, _("Team"), target, ""))
136 
137         return ballots
138 
139     def get_adj_feedbacks(self, debate):
140         adj_paths = self.tournament.pref('feedback_paths')
141         ballots = []
142 
143         debateadjs = debate.debateadjudicator_set.all()
144         for debateadj in debateadjs:
145             sadj = debateadj.adjudicator
146             spos = debate.adjudicators.get_position(sadj)
147             targets = expected_feedback_targets(debateadj, feedback_paths=adj_paths, debate=debate)
148             for tadj, tpos in targets:
149                 ballots.append(self.construct_info(debate.venue, sadj, spos, tadj, tpos))
150 
151         return ballots
152 
153     def get_context_data(self, **kwargs):
154         draw = self.round.debate_set_with_prefetches(institutions=True)
155         draw = sorted(draw, key=lambda d: d.venue.display_name if d.venue else "")
156 
157         ballots = []
158         for debate in draw:
159             for team in debate.teams:
160                 ballots.extend(self.get_team_feedbacks(debate, team))
161             ballots.extend(self.get_adj_feedbacks(debate))
162 
163         kwargs['ballots'] = json.dumps(ballots)
164         kwargs['questions'] = json.dumps(self.questions_dict())
165 
166         kwargs['team_questions_exist'] = self.tournament.adjudicatorfeedbackquestion_set.filter(from_team=True).exists()
167         kwargs['adj_questions_exist'] = self.tournament.adjudicatorfeedbackquestion_set.filter(from_adj=True).exists()
168 
169         return super().get_context_data(**kwargs)
170 
171 
172 class AdminPrintFeedbackFormsView(AdministratorMixin, BasePrintFeedbackFormsView):
173     pass
174 
175 
176 class AssistantPrintFeedbackFormsView(CurrentRoundMixin, OptionalAssistantTournamentPageMixin, BasePrintFeedbackFormsView):
177     assistant_page_permissions = ['all_areas', 'results_draw']
178 
179 
180 class BasePrintScoresheetsView(RoundMixin, TemplateView):
181 
182     template_name = 'scoresheet_list.html'
183 
184     def get_ballots_dicts(self):
185         draw = self.round.debate_set_with_prefetches()
186 
187         # Create the DebateIdentifiers for the ballots if needed
188         create_identifiers(DebateIdentifier, draw)
189         identifiers = DebateIdentifier.objects.values('debate_id', 'barcode')
190 
191         draw = sorted(draw, key=lambda d: d.venue.display_name if d.venue else "")
192         ballots_dicts = []
193 
194         # Force translation before JSON serialization
195         sides_and_positions = [(side, [str(pos) for pos in positions])
196             for side, positions in side_and_position_names(self.tournament)]
197 
198         for debate in draw:
199             debate_dict = {}
200 
201             if debate.venue:
202                 debate_dict['venue'] = {'display_name': debate.venue.display_name}
203             else:
204                 debate_dict['venue'] = None
205 
206             debate_dict['barcode'] = next((i['barcode'] for i in identifiers if i['debate_id'] == debate.id), None)
207 
208             debate_dict['debateTeams'] = []
209             for side, (side_name, positions) in zip(self.tournament.sides, sides_and_positions):
210                 dt_dict = {'side_name': side_name, 'positions': positions}
211                 try:
212                     team = debate.get_team(side)
213                     dt_dict['team'] = {
214                         'short_name': team.short_name,
215                         'code_name': team.code_name,
216                         'speakers': [{'name': s.name} for s in team.speakers],
217                     }
218                 except DebateTeam.DoesNotExist:
219                     dt_dict['team'] = None
220                 debate_dict['debateTeams'].append(dt_dict)
221 
222             debate_dict['debateAdjudicators'] = []
223             for adj, pos in debate.adjudicators.with_positions():
224                 da_dict = {'position': pos}
225                 da_dict['adjudicator'] = {
226                     'name': adj.name,
227                     'institution': {'code': adj.institution.code if adj.institution else _("Unaffiliated")},
228                 }
229                 debate_dict['debateAdjudicators'].append(da_dict)
230 
231             if self.round.ballots_per_debate == 'per-adj':
232                 authors = list(debate.adjudicators.voting_with_positions())
233             else:
234                 authors = [(debate.adjudicators.chair, debate.adjudicators.POSITION_CHAIR)]
235 
236             blank_author_dict = {
237                 'author': "_______________________________________________",
238                 'authorInstitution': "",
239                 'authorPosition': "",
240             }
241 
242             # Add a ballot for each author
243             for author, pos in authors:
244                 if author:
245                     ballot_dict = {
246                         'author': author.name,
247                         'authorInstitution': author.institution.code if author.institution else _("Unaffiliated"),
248                         'authorPosition': pos,
249                     }
250                 else:
251                     ballot_dict = blank_author_dict
252 
253                 ballot_dict.update(debate_dict)
254                 ballots_dicts.append(ballot_dict)
255 
256             if len(authors) == 0:
257                 ballot_dict = blank_author_dict
258                 ballot_dict.update(debate_dict)
259                 ballots_dicts.append(ballot_dict)
260 
261         return ballots_dicts
262 
263     def get_context_data(self, **kwargs):
264         kwargs['ballots'] = json.dumps(self.get_ballots_dicts())
265         motions = self.round.motion_set.order_by('seq')
266         kwargs['motions'] = json.dumps([{'seq': m.seq, 'text': m.text} for m in motions])
267         kwargs['use_team_code_names'] = use_team_code_names(self.tournament, False)
268         return super().get_context_data(**kwargs)
269 
270 
271 class AdminPrintScoresheetsView(AdministratorMixin, BasePrintScoresheetsView):
272     pass
273 
274 
275 class AssistantPrintScoresheetsView(CurrentRoundMixin, OptionalAssistantTournamentPageMixin, BasePrintScoresheetsView):
276     assistant_page_permissions = ['all_areas']
277 
278 
279 class PrintableRandomisedURLs(TournamentMixin, AdministratorMixin, TemplateView):
280 
281     template_name = 'randomised_url_sheets.html'
282 
283     def get_context_data(self, **kwargs):
284         kwargs['tournament_slug'] = self.tournament.slug
285 
286         if not self.tournament.pref('share_adjs'):
287             kwargs['parts'] = self.tournament.participants.filter(
288                 url_key__isnull=False).select_related(
289                 'speaker', 'speaker__team', 'adjudicator__institution',
290                 'adjudicator')
291         else:
292             kwargs['parts'] = Person.objects.filter(Q(speaker__team__tournament=self.tournament) | Q(adjudicator__tournament__isnull=True) & Q(url_key__isnull=False))
293 
294         kwargs['exists'] = self.tournament.participants.filter(url_key__isnull=False).exists()
295 
296         return super().get_context_data(**kwargs)
297
```
--- 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/printing/views.py b/tabbycat/printing/views.py
--- a/tabbycat/printing/views.py
+++ b/tabbycat/printing/views.py
@@ -1,5 +1,8 @@
 import json
 
+import qrcode
+from qrcode.image import svg
+
 from django.db.models import Q
 from django.utils.translation import gettext as _
 from django.views.generic.base import TemplateView
@@ -15,6 +18,7 @@
 from tournaments.mixins import (CurrentRoundMixin, OptionalAssistantTournamentPageMixin,
                                 RoundMixin, TournamentMixin)
 from tournaments.models import Tournament
+from utils.misc import reverse_tournament
 from utils.mixins import AdministratorMixin
 from venues.models import VenueCategory
 
@@ -280,16 +284,28 @@
 
     template_name = 'randomised_url_sheets.html'
 
+    def add_urls(self, participants):
+        for participant in participants:
+            url = reverse_tournament('privateurls-person-index', self.tournament, kwargs={'url_key': participant['url_key']})
+            abs_url = self.request.build_absolute_uri(url)
+            qr_code = qrcode.make(abs_url, image_factory=svg.SvgPathImage)
+
+            participant['url'] = abs_url
+            participant['qr'] = ' '.join(qr_code._generate_subpaths())
+
+        return participants
+
     def get_context_data(self, **kwargs):
-        kwargs['tournament_slug'] = self.tournament.slug
 
         if not self.tournament.pref('share_adjs'):
-            kwargs['parts'] = self.tournament.participants.filter(
-                url_key__isnull=False).select_related(
-                'speaker', 'speaker__team', 'adjudicator__institution',
-                'adjudicator')
+            participants = self.tournament.participants.filter(url_key__isnull=False)
         else:
-            kwargs['parts'] = Person.objects.filter(Q(speaker__team__tournament=self.tournament) | Q(adjudicator__tournament__isnull=True) & Q(url_key__isnull=False))
+            participants = Person.objects.filter(
+                Q(speaker__team__tournament=self.tournament) | Q(adjudicator__tournament__isnull=True) & Q(url_key__isnull=False))
+
+        participants_array = list(participants.select_related('speaker', 'speaker__team', 'adjudicator__institution', 'adjudicator')
+            .values('name', 'speaker__team__short_name', 'adjudicator__institution__code', 'url_key'))
+        kwargs['parts'] = self.add_urls(participants_array)
 
         kwargs['exists'] = self.tournament.participants.filter(url_key__isnull=False).exists()

{"golden_diff": "diff --git a/tabbycat/printing/views.py b/tabbycat/printing/views.py\n--- a/tabbycat/printing/views.py\n+++ b/tabbycat/printing/views.py\n@@ -1,5 +1,8 @@\n import json\n \n+import qrcode\n+from qrcode.image import svg\n+\n from django.db.models import Q\n from django.utils.translation import gettext as _\n from django.views.generic.base import TemplateView\n@@ -15,6 +18,7 @@\n from tournaments.mixins import (CurrentRoundMixin, OptionalAssistantTournamentPageMixin,\n                                 RoundMixin, TournamentMixin)\n from tournaments.models import Tournament\n+from utils.misc import reverse_tournament\n from utils.mixins import AdministratorMixin\n from venues.models import VenueCategory\n \n@@ -280,16 +284,28 @@\n \n     template_name = 'randomised_url_sheets.html'\n \n+    def add_urls(self, participants):\n+        for participant in participants:\n+            url = reverse_tournament('privateurls-person-index', self.tournament, kwargs={'url_key': participant['url_key']})\n+            abs_url = self.request.build_absolute_uri(url)\n+            qr_code = qrcode.make(abs_url, image_factory=svg.SvgPathImage)\n+\n+            participant['url'] = abs_url\n+            participant['qr'] = ' '.join(qr_code._generate_subpaths())\n+\n+        return participants\n+\n     def get_context_data(self, **kwargs):\n-        kwargs['tournament_slug'] = self.tournament.slug\n \n         if not self.tournament.pref('share_adjs'):\n-            kwargs['parts'] = self.tournament.participants.filter(\n-                url_key__isnull=False).select_related(\n-                'speaker', 'speaker__team', 'adjudicator__institution',\n-                'adjudicator')\n+            participants = self.tournament.participants.filter(url_key__isnull=False)\n         else:\n-            kwargs['parts'] = Person.objects.filter(Q(speaker__team__tournament=self.tournament) | Q(adjudicator__tournament__isnull=True) & Q(url_key__isnull=False))\n+            participants = Person.objects.filter(\n+                Q(speaker__team__tournament=self.tournament) | Q(adjudicator__tournament__isnull=True) & Q(url_key__isnull=False))\n+\n+        participants_array = list(participants.select_related('speaker', 'speaker__team', 'adjudicator__institution', 'adjudicator')\n+            .values('name', 'speaker__team__short_name', 'adjudicator__institution__code', 'url_key'))\n+        kwargs['parts'] = self.add_urls(participants_array)\n \n         kwargs['exists'] = self.tournament.participants.filter(url_key__isnull=False).exists()\n", "issue": "Include QR code in private URL distribution pages\nIt is a bit odd to hand people a physical item directing them to the internet, but typing a random string is not really user-friendly. QR codes that directs to the URL could be added to the URL sheets for ease of use. The type of barcode that is in use can't encode as long a string, but a QR code is recognized to be scanned to lead to a website and seems to be well supported by phones.\r\n\r\nThis may require another dependency to generate these codes; `jsBarcode` can't generate them.\n", "before_files": [{"content": "import json\n\nfrom django.db.models import Q\nfrom django.utils.translation import gettext as _\nfrom django.views.generic.base import TemplateView\n\nfrom adjfeedback.models import AdjudicatorFeedbackQuestion\nfrom adjfeedback.utils import expected_feedback_targets\nfrom checkins.models import DebateIdentifier\nfrom checkins.utils import create_identifiers\nfrom draw.models import Debate, DebateTeam\nfrom options.utils import use_team_code_names\nfrom participants.models import Person\nfrom results.utils import side_and_position_names\nfrom tournaments.mixins import (CurrentRoundMixin, OptionalAssistantTournamentPageMixin,\n                                RoundMixin, TournamentMixin)\nfrom tournaments.models import Tournament\nfrom utils.mixins import AdministratorMixin\nfrom venues.models import VenueCategory\n\n\nclass MasterSheetsListView(AdministratorMixin, RoundMixin, TemplateView):\n    template_name = 'division_sheets_list.html'\n\n    def get_context_data(self, **kwargs):\n        kwargs['standings'] = VenueCategory.objects.all()\n        kwargs['venue_categories'] = VenueCategory.objects.all()\n        return super().get_context_data(**kwargs)\n\n\nclass MasterSheetsView(AdministratorMixin, RoundMixin, TemplateView):\n    template_name = 'master_sheets_view.html'\n\n    def get_context_data(self, **kwargs):\n        venue_category_id = self.kwargs['venue_category_id']\n        base_venue_category = VenueCategory.objects.get(id=venue_category_id)\n        active_tournaments = Tournament.objects.filter(active=True)\n        for tournament in list(active_tournaments):\n            tournament.debates = Debate.objects.select_related(\n                'division', 'division__venue_category', 'round',\n                'round__tournament').filter(\n                    # All Debates, with a matching round, at the same venue category name\n                    round__seq=self.round.seq,\n                    round__tournament=tournament,\n                    # Hack - remove when venue category are unified\n                    division__venue_category__name=base_venue_category.name\n            ).order_by('round', 'division__venue_category__name', 'division')\n\n        kwargs['base_venue_category'] = base_venue_category\n        kwargs['active_tournaments'] = active_tournaments\n        return super().get_context_data(**kwargs)\n\n\nclass RoomSheetsView(AdministratorMixin, RoundMixin, TemplateView):\n    template_name = 'room_sheets_view.html'\n\n    def get_context_data(self, **kwargs):\n        venue_category_id = self.kwargs['venue_category_id']\n        base_venue_category = VenueCategory.objects.get(id=venue_category_id)\n        venues_list = []\n\n        # Get a unique list of venue names (avoid getting duplicates across tournaments)\n        for venue in set(base_venue_category.venues.order_by('name').values_list('name', flat=True)):\n            venues_list.append({'name': venue, 'debates': []})\n            # All Debates, with a matching round, at the same venue category\n            venues_list[-1]['debates'] = Debate.objects.filter(\n                round__seq=self.round.seq, venue__name=venue).order_by('round__tournament__seq').all()\n            print(venues_list[-1])\n\n        kwargs['base_venue_category'] = base_venue_category\n        kwargs['venues'] = venues_list\n        return super().get_context_data(**kwargs)\n\n\nclass BasePrintFeedbackFormsView(RoundMixin, TemplateView):\n\n    template_name = 'feedback_list.html'\n\n    def add_defaults(self):\n        default_questions = []\n\n        if self.tournament.pref('feedback_introduction'):\n            default_scale_info = AdjudicatorFeedbackQuestion(\n                text=self.tournament.pref('feedback_introduction'), seq=0,\n                answer_type='comment', # Custom type just for print display\n                required=True, from_team=True, from_adj=True\n            )\n            default_questions.append(default_scale_info.serialize())\n\n        default_scale_question = AdjudicatorFeedbackQuestion(\n            text=_(\"Overall Score\"), seq=0,\n            answer_type=AdjudicatorFeedbackQuestion.ANSWER_TYPE_INTEGER_SCALE,\n            required=True, from_team=True, from_adj=True,\n            min_value=self.tournament.pref('adj_min_score'),\n            max_value=self.tournament.pref('adj_max_score')\n        )\n        default_questions.append(default_scale_question.serialize())\n\n        return default_questions\n\n    def questions_dict(self):\n        questions = self.add_defaults()\n        for question in self.tournament.adj_feedback_questions:\n            questions.append(question.serialize())\n\n        return questions\n\n    def construct_info(self, venue, source, source_p, target, target_p):\n        if hasattr(source, 'name'):\n            source_n = source.name\n        elif use_team_code_names(self.tournament, False):\n            source_n = source.code_name\n        else:\n            source_n = source.short_name\n\n        return {\n            'venue': venue.serialize() if venue else '',\n            'authorInstitution': source.institution.code if source.institution else _(\"Unaffiliated\"),\n            'author': source_n, 'authorPosition': source_p,\n            'target': target.name, 'targetPosition': target_p,\n        }\n\n    def get_team_feedbacks(self, debate, team):\n        if len(debate.adjudicators) is 0:\n            return []\n\n        team_paths = self.tournament.pref('feedback_from_teams')\n        ballots = []\n\n        if team_paths == 'orallist' and debate.adjudicators.chair:\n            ballots.append(self.construct_info(debate.venue, team, _(\"Team\"),\n                                               debate.adjudicators.chair, \"\"))\n        elif team_paths == 'all-adjs':\n            for target in debate.adjudicators.all():\n                ballots.append(self.construct_info(debate.venue, team, _(\"Team\"), target, \"\"))\n\n        return ballots\n\n    def get_adj_feedbacks(self, debate):\n        adj_paths = self.tournament.pref('feedback_paths')\n        ballots = []\n\n        debateadjs = debate.debateadjudicator_set.all()\n        for debateadj in debateadjs:\n            sadj = debateadj.adjudicator\n            spos = debate.adjudicators.get_position(sadj)\n            targets = expected_feedback_targets(debateadj, feedback_paths=adj_paths, debate=debate)\n            for tadj, tpos in targets:\n                ballots.append(self.construct_info(debate.venue, sadj, spos, tadj, tpos))\n\n        return ballots\n\n    def get_context_data(self, **kwargs):\n        draw = self.round.debate_set_with_prefetches(institutions=True)\n        draw = sorted(draw, key=lambda d: d.venue.display_name if d.venue else \"\")\n\n        ballots = []\n        for debate in draw:\n            for team in debate.teams:\n                ballots.extend(self.get_team_feedbacks(debate, team))\n            ballots.extend(self.get_adj_feedbacks(debate))\n\n        kwargs['ballots'] = json.dumps(ballots)\n        kwargs['questions'] = json.dumps(self.questions_dict())\n\n        kwargs['team_questions_exist'] = self.tournament.adjudicatorfeedbackquestion_set.filter(from_team=True).exists()\n        kwargs['adj_questions_exist'] = self.tournament.adjudicatorfeedbackquestion_set.filter(from_adj=True).exists()\n\n        return super().get_context_data(**kwargs)\n\n\nclass AdminPrintFeedbackFormsView(AdministratorMixin, BasePrintFeedbackFormsView):\n    pass\n\n\nclass AssistantPrintFeedbackFormsView(CurrentRoundMixin, OptionalAssistantTournamentPageMixin, BasePrintFeedbackFormsView):\n    assistant_page_permissions = ['all_areas', 'results_draw']\n\n\nclass BasePrintScoresheetsView(RoundMixin, TemplateView):\n\n    template_name = 'scoresheet_list.html'\n\n    def get_ballots_dicts(self):\n        draw = self.round.debate_set_with_prefetches()\n\n        # Create the DebateIdentifiers for the ballots if needed\n        create_identifiers(DebateIdentifier, draw)\n        identifiers = DebateIdentifier.objects.values('debate_id', 'barcode')\n\n        draw = sorted(draw, key=lambda d: d.venue.display_name if d.venue else \"\")\n        ballots_dicts = []\n\n        # Force translation before JSON serialization\n        sides_and_positions = [(side, [str(pos) for pos in positions])\n            for side, positions in side_and_position_names(self.tournament)]\n\n        for debate in draw:\n            debate_dict = {}\n\n            if debate.venue:\n                debate_dict['venue'] = {'display_name': debate.venue.display_name}\n            else:\n                debate_dict['venue'] = None\n\n            debate_dict['barcode'] = next((i['barcode'] for i in identifiers if i['debate_id'] == debate.id), None)\n\n            debate_dict['debateTeams'] = []\n            for side, (side_name, positions) in zip(self.tournament.sides, sides_and_positions):\n                dt_dict = {'side_name': side_name, 'positions': positions}\n                try:\n                    team = debate.get_team(side)\n                    dt_dict['team'] = {\n                        'short_name': team.short_name,\n                        'code_name': team.code_name,\n                        'speakers': [{'name': s.name} for s in team.speakers],\n                    }\n                except DebateTeam.DoesNotExist:\n                    dt_dict['team'] = None\n                debate_dict['debateTeams'].append(dt_dict)\n\n            debate_dict['debateAdjudicators'] = []\n            for adj, pos in debate.adjudicators.with_positions():\n                da_dict = {'position': pos}\n                da_dict['adjudicator'] = {\n                    'name': adj.name,\n                    'institution': {'code': adj.institution.code if adj.institution else _(\"Unaffiliated\")},\n                }\n                debate_dict['debateAdjudicators'].append(da_dict)\n\n            if self.round.ballots_per_debate == 'per-adj':\n                authors = list(debate.adjudicators.voting_with_positions())\n            else:\n                authors = [(debate.adjudicators.chair, debate.adjudicators.POSITION_CHAIR)]\n\n            blank_author_dict = {\n                'author': \"_______________________________________________\",\n                'authorInstitution': \"\",\n                'authorPosition': \"\",\n            }\n\n            # Add a ballot for each author\n            for author, pos in authors:\n                if author:\n                    ballot_dict = {\n                        'author': author.name,\n                        'authorInstitution': author.institution.code if author.institution else _(\"Unaffiliated\"),\n                        'authorPosition': pos,\n                    }\n                else:\n                    ballot_dict = blank_author_dict\n\n                ballot_dict.update(debate_dict)\n                ballots_dicts.append(ballot_dict)\n\n            if len(authors) == 0:\n                ballot_dict = blank_author_dict\n                ballot_dict.update(debate_dict)\n                ballots_dicts.append(ballot_dict)\n\n        return ballots_dicts\n\n    def get_context_data(self, **kwargs):\n        kwargs['ballots'] = json.dumps(self.get_ballots_dicts())\n        motions = self.round.motion_set.order_by('seq')\n        kwargs['motions'] = json.dumps([{'seq': m.seq, 'text': m.text} for m in motions])\n        kwargs['use_team_code_names'] = use_team_code_names(self.tournament, False)\n        return super().get_context_data(**kwargs)\n\n\nclass AdminPrintScoresheetsView(AdministratorMixin, BasePrintScoresheetsView):\n    pass\n\n\nclass AssistantPrintScoresheetsView(CurrentRoundMixin, OptionalAssistantTournamentPageMixin, BasePrintScoresheetsView):\n    assistant_page_permissions = ['all_areas']\n\n\nclass PrintableRandomisedURLs(TournamentMixin, AdministratorMixin, TemplateView):\n\n    template_name = 'randomised_url_sheets.html'\n\n    def get_context_data(self, **kwargs):\n        kwargs['tournament_slug'] = self.tournament.slug\n\n        if not self.tournament.pref('share_adjs'):\n            kwargs['parts'] = self.tournament.participants.filter(\n                url_key__isnull=False).select_related(\n                'speaker', 'speaker__team', 'adjudicator__institution',\n                'adjudicator')\n        else:\n            kwargs['parts'] = Person.objects.filter(Q(speaker__team__tournament=self.tournament) | Q(adjudicator__tournament__isnull=True) & Q(url_key__isnull=False))\n\n        kwargs['exists'] = self.tournament.participants.filter(url_key__isnull=False).exists()\n\n        return super().get_context_data(**kwargs)\n", "path": "tabbycat/printing/views.py"}], "after_files": [{"content": "import json\n\nimport qrcode\nfrom qrcode.image import svg\n\nfrom django.db.models import Q\nfrom django.utils.translation import gettext as _\nfrom django.views.generic.base import TemplateView\n\nfrom adjfeedback.models import AdjudicatorFeedbackQuestion\nfrom adjfeedback.utils import expected_feedback_targets\nfrom checkins.models import DebateIdentifier\nfrom checkins.utils import create_identifiers\nfrom draw.models import Debate, DebateTeam\nfrom options.utils import use_team_code_names\nfrom participants.models import Person\nfrom results.utils import side_and_position_names\nfrom tournaments.mixins import (CurrentRoundMixin, OptionalAssistantTournamentPageMixin,\n                                RoundMixin, TournamentMixin)\nfrom tournaments.models import Tournament\nfrom utils.misc import reverse_tournament\nfrom utils.mixins import AdministratorMixin\nfrom venues.models import VenueCategory\n\n\nclass MasterSheetsListView(AdministratorMixin, RoundMixin, TemplateView):\n    template_name = 'division_sheets_list.html'\n\n    def get_context_data(self, **kwargs):\n        kwargs['standings'] = VenueCategory.objects.all()\n        kwargs['venue_categories'] = VenueCategory.objects.all()\n        return super().get_context_data(**kwargs)\n\n\nclass MasterSheetsView(AdministratorMixin, RoundMixin, TemplateView):\n    template_name = 'master_sheets_view.html'\n\n    def get_context_data(self, **kwargs):\n        venue_category_id = self.kwargs['venue_category_id']\n        base_venue_category = VenueCategory.objects.get(id=venue_category_id)\n        active_tournaments = Tournament.objects.filter(active=True)\n        for tournament in list(active_tournaments):\n            tournament.debates = Debate.objects.select_related(\n                'division', 'division__venue_category', 'round',\n                'round__tournament').filter(\n                    # All Debates, with a matching round, at the same venue category name\n                    round__seq=self.round.seq,\n                    round__tournament=tournament,\n                    # Hack - remove when venue category are unified\n                    division__venue_category__name=base_venue_category.name\n            ).order_by('round', 'division__venue_category__name', 'division')\n\n        kwargs['base_venue_category'] = base_venue_category\n        kwargs['active_tournaments'] = active_tournaments\n        return super().get_context_data(**kwargs)\n\n\nclass RoomSheetsView(AdministratorMixin, RoundMixin, TemplateView):\n    template_name = 'room_sheets_view.html'\n\n    def get_context_data(self, **kwargs):\n        venue_category_id = self.kwargs['venue_category_id']\n        base_venue_category = VenueCategory.objects.get(id=venue_category_id)\n        venues_list = []\n\n        # Get a unique list of venue names (avoid getting duplicates across tournaments)\n        for venue in set(base_venue_category.venues.order_by('name').values_list('name', flat=True)):\n            venues_list.append({'name': venue, 'debates': []})\n            # All Debates, with a matching round, at the same venue category\n            venues_list[-1]['debates'] = Debate.objects.filter(\n                round__seq=self.round.seq, venue__name=venue).order_by('round__tournament__seq').all()\n            print(venues_list[-1])\n\n        kwargs['base_venue_category'] = base_venue_category\n        kwargs['venues'] = venues_list\n        return super().get_context_data(**kwargs)\n\n\nclass BasePrintFeedbackFormsView(RoundMixin, TemplateView):\n\n    template_name = 'feedback_list.html'\n\n    def add_defaults(self):\n        default_questions = []\n\n        if self.tournament.pref('feedback_introduction'):\n            default_scale_info = AdjudicatorFeedbackQuestion(\n                text=self.tournament.pref('feedback_introduction'), seq=0,\n                answer_type='comment', # Custom type just for print display\n                required=True, from_team=True, from_adj=True\n            )\n            default_questions.append(default_scale_info.serialize())\n\n        default_scale_question = AdjudicatorFeedbackQuestion(\n            text=_(\"Overall Score\"), seq=0,\n            answer_type=AdjudicatorFeedbackQuestion.ANSWER_TYPE_INTEGER_SCALE,\n            required=True, from_team=True, from_adj=True,\n            min_value=self.tournament.pref('adj_min_score'),\n            max_value=self.tournament.pref('adj_max_score')\n        )\n        default_questions.append(default_scale_question.serialize())\n\n        return default_questions\n\n    def questions_dict(self):\n        questions = self.add_defaults()\n        for question in self.tournament.adj_feedback_questions:\n            questions.append(question.serialize())\n\n        return questions\n\n    def construct_info(self, venue, source, source_p, target, target_p):\n        if hasattr(source, 'name'):\n            source_n = source.name\n        elif use_team_code_names(self.tournament, False):\n            source_n = source.code_name\n        else:\n            source_n = source.short_name\n\n        return {\n            'venue': venue.serialize() if venue else '',\n            'authorInstitution': source.institution.code if source.institution else _(\"Unaffiliated\"),\n            'author': source_n, 'authorPosition': source_p,\n            'target': target.name, 'targetPosition': target_p,\n        }\n\n    def get_team_feedbacks(self, debate, team):\n        if len(debate.adjudicators) is 0:\n            return []\n\n        team_paths = self.tournament.pref('feedback_from_teams')\n        ballots = []\n\n        if team_paths == 'orallist' and debate.adjudicators.chair:\n            ballots.append(self.construct_info(debate.venue, team, _(\"Team\"),\n                                               debate.adjudicators.chair, \"\"))\n        elif team_paths == 'all-adjs':\n            for target in debate.adjudicators.all():\n                ballots.append(self.construct_info(debate.venue, team, _(\"Team\"), target, \"\"))\n\n        return ballots\n\n    def get_adj_feedbacks(self, debate):\n        adj_paths = self.tournament.pref('feedback_paths')\n        ballots = []\n\n        debateadjs = debate.debateadjudicator_set.all()\n        for debateadj in debateadjs:\n            sadj = debateadj.adjudicator\n            spos = debate.adjudicators.get_position(sadj)\n            targets = expected_feedback_targets(debateadj, feedback_paths=adj_paths, debate=debate)\n            for tadj, tpos in targets:\n                ballots.append(self.construct_info(debate.venue, sadj, spos, tadj, tpos))\n\n        return ballots\n\n    def get_context_data(self, **kwargs):\n        draw = self.round.debate_set_with_prefetches(institutions=True)\n        draw = sorted(draw, key=lambda d: d.venue.display_name if d.venue else \"\")\n\n        ballots = []\n        for debate in draw:\n            for team in debate.teams:\n                ballots.extend(self.get_team_feedbacks(debate, team))\n            ballots.extend(self.get_adj_feedbacks(debate))\n\n        kwargs['ballots'] = json.dumps(ballots)\n        kwargs['questions'] = json.dumps(self.questions_dict())\n\n        kwargs['team_questions_exist'] = self.tournament.adjudicatorfeedbackquestion_set.filter(from_team=True).exists()\n        kwargs['adj_questions_exist'] = self.tournament.adjudicatorfeedbackquestion_set.filter(from_adj=True).exists()\n\n        return super().get_context_data(**kwargs)\n\n\nclass AdminPrintFeedbackFormsView(AdministratorMixin, BasePrintFeedbackFormsView):\n    pass\n\n\nclass AssistantPrintFeedbackFormsView(CurrentRoundMixin, OptionalAssistantTournamentPageMixin, BasePrintFeedbackFormsView):\n    assistant_page_permissions = ['all_areas', 'results_draw']\n\n\nclass BasePrintScoresheetsView(RoundMixin, TemplateView):\n\n    template_name = 'scoresheet_list.html'\n\n    def get_ballots_dicts(self):\n        draw = self.round.debate_set_with_prefetches()\n\n        # Create the DebateIdentifiers for the ballots if needed\n        create_identifiers(DebateIdentifier, draw)\n        identifiers = DebateIdentifier.objects.values('debate_id', 'barcode')\n\n        draw = sorted(draw, key=lambda d: d.venue.display_name if d.venue else \"\")\n        ballots_dicts = []\n\n        # Force translation before JSON serialization\n        sides_and_positions = [(side, [str(pos) for pos in positions])\n            for side, positions in side_and_position_names(self.tournament)]\n\n        for debate in draw:\n            debate_dict = {}\n\n            if debate.venue:\n                debate_dict['venue'] = {'display_name': debate.venue.display_name}\n            else:\n                debate_dict['venue'] = None\n\n            debate_dict['barcode'] = next((i['barcode'] for i in identifiers if i['debate_id'] == debate.id), None)\n\n            debate_dict['debateTeams'] = []\n            for side, (side_name, positions) in zip(self.tournament.sides, sides_and_positions):\n                dt_dict = {'side_name': side_name, 'positions': positions}\n                try:\n                    team = debate.get_team(side)\n                    dt_dict['team'] = {\n                        'short_name': team.short_name,\n                        'code_name': team.code_name,\n                        'speakers': [{'name': s.name} for s in team.speakers],\n                    }\n                except DebateTeam.DoesNotExist:\n                    dt_dict['team'] = None\n                debate_dict['debateTeams'].append(dt_dict)\n\n            debate_dict['debateAdjudicators'] = []\n            for adj, pos in debate.adjudicators.with_positions():\n                da_dict = {'position': pos}\n                da_dict['adjudicator'] = {\n                    'name': adj.name,\n                    'institution': {'code': adj.institution.code if adj.institution else _(\"Unaffiliated\")},\n                }\n                debate_dict['debateAdjudicators'].append(da_dict)\n\n            if self.round.ballots_per_debate == 'per-adj':\n                authors = list(debate.adjudicators.voting_with_positions())\n            else:\n                authors = [(debate.adjudicators.chair, debate.adjudicators.POSITION_CHAIR)]\n\n            blank_author_dict = {\n                'author': \"_______________________________________________\",\n                'authorInstitution': \"\",\n                'authorPosition': \"\",\n            }\n\n            # Add a ballot for each author\n            for author, pos in authors:\n                if author:\n                    ballot_dict = {\n                        'author': author.name,\n                        'authorInstitution': author.institution.code if author.institution else _(\"Unaffiliated\"),\n                        'authorPosition': pos,\n                    }\n                else:\n                    ballot_dict = blank_author_dict\n\n                ballot_dict.update(debate_dict)\n                ballots_dicts.append(ballot_dict)\n\n            if len(authors) == 0:\n                ballot_dict = blank_author_dict\n                ballot_dict.update(debate_dict)\n                ballots_dicts.append(ballot_dict)\n\n        return ballots_dicts\n\n    def get_context_data(self, **kwargs):\n        kwargs['ballots'] = json.dumps(self.get_ballots_dicts())\n        motions = self.round.motion_set.order_by('seq')\n        kwargs['motions'] = json.dumps([{'seq': m.seq, 'text': m.text} for m in motions])\n        kwargs['use_team_code_names'] = use_team_code_names(self.tournament, False)\n        return super().get_context_data(**kwargs)\n\n\nclass AdminPrintScoresheetsView(AdministratorMixin, BasePrintScoresheetsView):\n    pass\n\n\nclass AssistantPrintScoresheetsView(CurrentRoundMixin, OptionalAssistantTournamentPageMixin, BasePrintScoresheetsView):\n    assistant_page_permissions = ['all_areas']\n\n\nclass PrintableRandomisedURLs(TournamentMixin, AdministratorMixin, TemplateView):\n\n    template_name = 'randomised_url_sheets.html'\n\n    def add_urls(self, participants):\n        for participant in participants:\n            url = reverse_tournament('privateurls-person-index', self.tournament, kwargs={'url_key': participant['url_key']})\n            abs_url = self.request.build_absolute_uri(url)\n            qr_code = qrcode.make(abs_url, image_factory=svg.SvgPathImage)\n\n            participant['url'] = abs_url\n            participant['qr'] = ' '.join(qr_code._generate_subpaths())\n\n        return participants\n\n    def get_context_data(self, **kwargs):\n\n        if not self.tournament.pref('share_adjs'):\n            participants = self.tournament.participants.filter(url_key__isnull=False)\n        else:\n            participants = Person.objects.filter(\n                Q(speaker__team__tournament=self.tournament) | Q(adjudicator__tournament__isnull=True) & Q(url_key__isnull=False))\n\n        participants_array = list(participants.select_related('speaker', 'speaker__team', 'adjudicator__institution', 'adjudicator')\n            .values('name', 'speaker__team__short_name', 'adjudicator__institution__code', 'url_key'))\n        kwargs['parts'] = self.add_urls(participants_array)\n\n        kwargs['exists'] = self.tournament.participants.filter(url_key__isnull=False).exists()\n\n        return super().get_context_data(**kwargs)\n", "path": "tabbycat/printing/views.py"}]}