We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Email Notification: Add additional information to ballot receipts
Thanks to @tienne-B for his hard work on bringing this excellent new feature to life. I was wondering if it would be helpful to add some additional information to the {{ scores }} variable. 

![capture](https://user-images.githubusercontent.com/3196468/46149126-b1a83b00-c2bd-11e8-8d4f-1228af6535d5.PNG)

It might be useful to include the total scores and ranking win/lose as well. I think judges are more likely to notice an issue here than by reading the individual speaker scores. This may be low priority given the work it might take to develop different display formats or may have intentionally not been done.

I can think of different ways to do this but one could possibly be:

BP: 
1st - CG: Sacred Heart 2 (180)
2nd - CO: St Andrews 2 (170)
3rd - OG: Macleans 2 (160)
4th - OO: St Marys 1 (140)

WSDC:
Winner - Proposition: Germany (250)
Loser - Opposition: France (245)

--- 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/notifications/utils.py`
Content:
```
1 from django.db.models import Exists, OuterRef
2 from django.utils.translation import gettext as _
3 
4 from adjallocation.allocation import AdjudicatorAllocation
5 from adjallocation.models import DebateAdjudicator
6 from draw.models import Debate
7 from results.result import DebateResult
8 from options.utils import use_team_code_names
9 from participants.prefetch import populate_win_counts
10 from tournaments.models import Round, Tournament
11 
12 from .models import SentMessageRecord
13 
14 
15 def adjudicator_assignment_email_generator(round_id):
16     emails = []
17     round = Round.objects.get(id=round_id)
18     tournament = round.tournament
19     draw = round.debate_set_with_prefetches(speakers=False, divisions=False).all()
20     use_codes = use_team_code_names(tournament, False)
21 
22     adj_position_names = {
23         AdjudicatorAllocation.POSITION_CHAIR: _("the chair"),
24         AdjudicatorAllocation.POSITION_ONLY: _("the only"),
25         AdjudicatorAllocation.POSITION_PANELLIST: _("a panellist"),
26         AdjudicatorAllocation.POSITION_TRAINEE: _("a trainee"),
27     }
28 
29     def _assemble_panel(adjs):
30         adj_string = []
31         for adj, pos in adjs:
32             adj_string.append("%s (%s)" % (adj.name, adj_position_names[pos]))
33 
34         return ", ".join(adj_string)
35 
36     for debate in draw:
37         matchup = debate.matchup_codes if use_codes else debate.matchup
38         context = {
39             'ROUND': round.name,
40             'VENUE': debate.venue.name,
41             'PANEL': _assemble_panel(debate.adjudicators.with_positions()),
42             'DRAW': matchup
43         }
44 
45         for adj, pos in debate.adjudicators.with_positions():
46             if adj.email is None:
47                 continue
48 
49             context_user = context.copy()
50             context_user['USER'] = adj.name
51             context_user['POSITION'] = adj_position_names[pos]
52 
53             emails.append((context_user, adj))
54 
55     return emails
56 
57 
58 def randomized_url_email_generator(url, tournament_id):
59     emails = []
60     tournament = Tournament.objects.get(id=tournament_id)
61 
62     subquery = SentMessageRecord.objects.filter(
63         event=SentMessageRecord.EVENT_TYPE_URL,
64         tournament=tournament, email=OuterRef('email')
65     )
66     participants = tournament.participants.filter(
67         url_key__isnull=False, email__isnull=False
68     ).exclude(email__exact="").annotate(already_sent=Exists(subquery)).filter(already_sent=False)
69 
70     for instance in participants:
71         url_ind = url + instance.url_key + '/'
72 
73         variables = {'USER': instance.name, 'URL': url_ind, 'KEY': instance.url_key, 'TOURN': str(tournament)}
74 
75         emails.append((variables, instance))
76 
77     return emails
78 
79 
80 def ballots_email_generator(debate_id):
81     emails = []
82     debate = Debate.objects.get(id=debate_id)
83     tournament = debate.round.tournament
84     ballots = DebateResult(debate.confirmed_ballot).as_dicts()
85     round_name = _("%(tournament)s %(round)s @ %(room)s") % {'tournament': str(tournament),
86                                                              'round': debate.round.name, 'room': debate.venue.name}
87 
88     context = {'DEBATE': round_name}
89     use_codes = use_team_code_names(debate.round.tournament, False)
90 
91     for ballot in ballots:
92         if 'adjudicator' in ballot:
93             judge = ballot['adjudicator']
94         else:
95             judge = debate.debateadjudicator_set.get(type=DebateAdjudicator.TYPE_CHAIR).adjudicator
96 
97         if judge.email is None:
98             continue
99 
100         scores = ""
101         for team in ballot['teams']:
102 
103             team_name = team['team'].code_name if use_codes else team['team'].short_name
104             scores += _("(%(side)s) %(team)s\n") % {'side': team['side'], 'team': team_name}
105 
106             for speaker in team['speakers']:
107                 scores += _("- %(debater)s: %(score)s\n") % {'debater': speaker['speaker'], 'score': speaker['score']}
108 
109         context_user = context.copy()
110         context_user['USER'] = judge.name
111         context_user['SCORES'] = scores
112 
113         emails.append((context, judge))
114 
115     return emails
116 
117 
118 def standings_email_generator(url, round_id):
119     emails = []
120     round = Round.objects.get(id=round_id)
121     tournament = round.tournament
122 
123     teams = round.active_teams.prefetch_related('speaker_set')
124     populate_win_counts(teams)
125 
126     context = {
127         'TOURN': str(tournament),
128         'ROUND': round.name,
129         'URL': url if tournament.pref('public_team_standings') else ""
130     }
131 
132     for team in teams:
133         context_team = context.copy()
134         context_team['POINTS'] = str(team.points_count)
135         context_team['TEAM'] = team.short_name
136 
137         for speaker in team.speaker_set.all():
138             if speaker.email is None:
139                 continue
140 
141             context_user = context_team.copy()
142             context_user['USER'] = speaker.name
143 
144             emails.append((context_user, speaker))
145 
146     return emails
147 
148 
149 def motion_release_email_generator(round_id):
150     emails = []
151     round = Round.objects.get(id=round_id)
152 
153     def _create_motion_list():
154         motion_list = ""
155         for motion in round.motion_set.all():
156             motion_list += _(" - %s (%s)\n") % (motion.text, motion.reference)
157 
158             if motion.info_slide:
159                 motion_list += "   %s\n" % (motion.info_slide)
160 
161         return motion_list
162 
163     context = {
164         'TOURN': str(round.tournament),
165         'ROUND': round.name,
166         'MOTIONS': _create_motion_list()
167     }
168 
169     teams = round.tournament.team_set.filter(round_availabilities__round=round).prefetch_related('speaker_set')
170     for team in teams:
171         for speaker in team.speaker_set.all():
172             if speaker.email is None:
173                 continue
174 
175             context_user = context.copy()
176             context_user['USER'] = speaker.name
177 
178             emails.append((context_user, speaker))
179 
180     return emails
181 
182 
183 def team_speaker_email_generator(tournament_id):
184     emails = []
185     tournament = Tournament.objects.get(id=tournament_id)
186 
187     for team in tournament.team_set.all().prefetch_related('speaker_set', 'break_categories').select_related('division', 'institution'):
188         context = {
189             'TOURN': str(tournament),
190             'SHORT': team.short_name,
191             'LONG': team.long_name,
192             'CODE': team.code_name,
193             'DIVISION': team.division.name if team.division is not None else "",
194             'BREAK': _(", ").join([breakq.name for breakq in team.break_categories.all()]),
195             'SPEAKERS': _(", ").join([p.name for p in team.speaker_set.all()]),
196             'INSTITUTION': str(team.institution),
197             'EMOJI': team.emoji
198         }
199 
200         for speaker in team.speakers:
201             if speaker.email is None:
202                 continue
203 
204             context_user = context.copy()
205             context_user['USER'] = speaker.name
206 
207             emails.append((context_user, speaker))
208 
209     return emails
210
```
--- 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/notifications/utils.py b/tabbycat/notifications/utils.py
--- a/tabbycat/notifications/utils.py
+++ b/tabbycat/notifications/utils.py
@@ -2,9 +2,9 @@
 from django.utils.translation import gettext as _
 
 from adjallocation.allocation import AdjudicatorAllocation
-from adjallocation.models import DebateAdjudicator
 from draw.models import Debate
-from results.result import DebateResult
+from results.result import BaseConsensusDebateResultWithSpeakers, DebateResult, VotingDebateResult
+from results.utils import side_and_position_names
 from options.utils import use_team_code_names
 from participants.prefetch import populate_win_counts
 from tournaments.models import Round, Tournament
@@ -81,36 +81,50 @@
     emails = []
     debate = Debate.objects.get(id=debate_id)
     tournament = debate.round.tournament
-    ballots = DebateResult(debate.confirmed_ballot).as_dicts()
+    results = DebateResult(debate.confirmed_ballot)
     round_name = _("%(tournament)s %(round)s @ %(room)s") % {'tournament': str(tournament),
                                                              'round': debate.round.name, 'room': debate.venue.name}
 
-    context = {'DEBATE': round_name}
     use_codes = use_team_code_names(debate.round.tournament, False)
 
-    for ballot in ballots:
-        if 'adjudicator' in ballot:
-            judge = ballot['adjudicator']
-        else:
-            judge = debate.debateadjudicator_set.get(type=DebateAdjudicator.TYPE_CHAIR).adjudicator
+    def _create_ballot(result, scoresheet):
+        ballot = ""
 
-        if judge.email is None:
-            continue
+        for side, (side_name, pos_names) in zip(tournament.sides, side_and_position_names(tournament)):
+            ballot += _("%(side)s: %(team)s (%(points)d - %(rank)s)\n") % {
+                'side': side_name,
+                'team': result.debateteams[side].team.code_name if use_codes else result.debateteams[side].team.short_name,
+                'points': scoresheet.get_total(side),
+                'rank': scoresheet.rank(side)
+            }
 
-        scores = ""
-        for team in ballot['teams']:
+            for pos, pos_name in zip(tournament.positions, pos_names):
+                ballot += _("- %(pos)s: %(speaker)s (%(score)s)\n") % {
+                    'pos': pos_name,
+                    'speaker': result.get_speaker(side, pos).name,
+                    'score': scoresheet.get_score(side, pos)
+                }
 
-            team_name = team['team'].code_name if use_codes else team['team'].short_name
-            scores += _("(%(side)s) %(team)s\n") % {'side': team['side'], 'team': team_name}
+        return ballot
 
-            for speaker in team['speakers']:
-                scores += _("- %(debater)s: %(score)s\n") % {'debater': speaker['speaker'], 'score': speaker['score']}
+    if isinstance(results, VotingDebateResult):
+        for (adj, ballot) in results.scoresheets.items():
+            if adj.email is None:
+                continue
+
+            context = {'DEBATE': round_name, 'USER': adj.name, 'SCORES': _create_ballot(results, ballot)}
+            emails.append((context, adj))
+    elif isinstance(results, BaseConsensusDebateResultWithSpeakers):
+        context = {'DEBATE': round_name, 'SCORES': _create_ballot(results, results.scoresheet)}
 
-        context_user = context.copy()
-        context_user['USER'] = judge.name
-        context_user['SCORES'] = scores
+        for adj in debate.debateadjudicator_set.all():
+            if adj.adjudicator.email is None:
+                continue
+
+            context_user = context.copy()
+            context_user['USER'] = adj.adjudicator.name
 
-        emails.append((context, judge))
+            emails.append((context_user, adj.adjudicator))
 
     return emails

{"golden_diff": "diff --git a/tabbycat/notifications/utils.py b/tabbycat/notifications/utils.py\n--- a/tabbycat/notifications/utils.py\n+++ b/tabbycat/notifications/utils.py\n@@ -2,9 +2,9 @@\n from django.utils.translation import gettext as _\n \n from adjallocation.allocation import AdjudicatorAllocation\n-from adjallocation.models import DebateAdjudicator\n from draw.models import Debate\n-from results.result import DebateResult\n+from results.result import BaseConsensusDebateResultWithSpeakers, DebateResult, VotingDebateResult\n+from results.utils import side_and_position_names\n from options.utils import use_team_code_names\n from participants.prefetch import populate_win_counts\n from tournaments.models import Round, Tournament\n@@ -81,36 +81,50 @@\n     emails = []\n     debate = Debate.objects.get(id=debate_id)\n     tournament = debate.round.tournament\n-    ballots = DebateResult(debate.confirmed_ballot).as_dicts()\n+    results = DebateResult(debate.confirmed_ballot)\n     round_name = _(\"%(tournament)s %(round)s @ %(room)s\") % {'tournament': str(tournament),\n                                                              'round': debate.round.name, 'room': debate.venue.name}\n \n-    context = {'DEBATE': round_name}\n     use_codes = use_team_code_names(debate.round.tournament, False)\n \n-    for ballot in ballots:\n-        if 'adjudicator' in ballot:\n-            judge = ballot['adjudicator']\n-        else:\n-            judge = debate.debateadjudicator_set.get(type=DebateAdjudicator.TYPE_CHAIR).adjudicator\n+    def _create_ballot(result, scoresheet):\n+        ballot = \"\"\n \n-        if judge.email is None:\n-            continue\n+        for side, (side_name, pos_names) in zip(tournament.sides, side_and_position_names(tournament)):\n+            ballot += _(\"%(side)s: %(team)s (%(points)d - %(rank)s)\\n\") % {\n+                'side': side_name,\n+                'team': result.debateteams[side].team.code_name if use_codes else result.debateteams[side].team.short_name,\n+                'points': scoresheet.get_total(side),\n+                'rank': scoresheet.rank(side)\n+            }\n \n-        scores = \"\"\n-        for team in ballot['teams']:\n+            for pos, pos_name in zip(tournament.positions, pos_names):\n+                ballot += _(\"- %(pos)s: %(speaker)s (%(score)s)\\n\") % {\n+                    'pos': pos_name,\n+                    'speaker': result.get_speaker(side, pos).name,\n+                    'score': scoresheet.get_score(side, pos)\n+                }\n \n-            team_name = team['team'].code_name if use_codes else team['team'].short_name\n-            scores += _(\"(%(side)s) %(team)s\\n\") % {'side': team['side'], 'team': team_name}\n+        return ballot\n \n-            for speaker in team['speakers']:\n-                scores += _(\"- %(debater)s: %(score)s\\n\") % {'debater': speaker['speaker'], 'score': speaker['score']}\n+    if isinstance(results, VotingDebateResult):\n+        for (adj, ballot) in results.scoresheets.items():\n+            if adj.email is None:\n+                continue\n+\n+            context = {'DEBATE': round_name, 'USER': adj.name, 'SCORES': _create_ballot(results, ballot)}\n+            emails.append((context, adj))\n+    elif isinstance(results, BaseConsensusDebateResultWithSpeakers):\n+        context = {'DEBATE': round_name, 'SCORES': _create_ballot(results, results.scoresheet)}\n \n-        context_user = context.copy()\n-        context_user['USER'] = judge.name\n-        context_user['SCORES'] = scores\n+        for adj in debate.debateadjudicator_set.all():\n+            if adj.adjudicator.email is None:\n+                continue\n+\n+            context_user = context.copy()\n+            context_user['USER'] = adj.adjudicator.name\n \n-        emails.append((context, judge))\n+            emails.append((context_user, adj.adjudicator))\n \n     return emails\n", "issue": "Email Notification: Add additional information to ballot receipts\nThanks to @tienne-B for his hard work on bringing this excellent new feature to life. I was wondering if it would be helpful to add some additional information to the {{ scores }} variable. \r\n\r\n![capture](https://user-images.githubusercontent.com/3196468/46149126-b1a83b00-c2bd-11e8-8d4f-1228af6535d5.PNG)\r\n\r\nIt might be useful to include the total scores and ranking win/lose as well. I think judges are more likely to notice an issue here than by reading the individual speaker scores. This may be low priority given the work it might take to develop different display formats or may have intentionally not been done.\r\n\r\nI can think of different ways to do this but one could possibly be:\r\n\r\nBP: \r\n1st - CG: Sacred Heart 2 (180)\r\n2nd - CO: St Andrews 2 (170)\r\n3rd - OG: Macleans 2 (160)\r\n4th - OO: St Marys 1 (140)\r\n\r\nWSDC:\r\nWinner - Proposition: Germany (250)\r\nLoser - Opposition: France (245)\n", "before_files": [{"content": "from django.db.models import Exists, OuterRef\nfrom django.utils.translation import gettext as _\n\nfrom adjallocation.allocation import AdjudicatorAllocation\nfrom adjallocation.models import DebateAdjudicator\nfrom draw.models import Debate\nfrom results.result import DebateResult\nfrom options.utils import use_team_code_names\nfrom participants.prefetch import populate_win_counts\nfrom tournaments.models import Round, Tournament\n\nfrom .models import SentMessageRecord\n\n\ndef adjudicator_assignment_email_generator(round_id):\n    emails = []\n    round = Round.objects.get(id=round_id)\n    tournament = round.tournament\n    draw = round.debate_set_with_prefetches(speakers=False, divisions=False).all()\n    use_codes = use_team_code_names(tournament, False)\n\n    adj_position_names = {\n        AdjudicatorAllocation.POSITION_CHAIR: _(\"the chair\"),\n        AdjudicatorAllocation.POSITION_ONLY: _(\"the only\"),\n        AdjudicatorAllocation.POSITION_PANELLIST: _(\"a panellist\"),\n        AdjudicatorAllocation.POSITION_TRAINEE: _(\"a trainee\"),\n    }\n\n    def _assemble_panel(adjs):\n        adj_string = []\n        for adj, pos in adjs:\n            adj_string.append(\"%s (%s)\" % (adj.name, adj_position_names[pos]))\n\n        return \", \".join(adj_string)\n\n    for debate in draw:\n        matchup = debate.matchup_codes if use_codes else debate.matchup\n        context = {\n            'ROUND': round.name,\n            'VENUE': debate.venue.name,\n            'PANEL': _assemble_panel(debate.adjudicators.with_positions()),\n            'DRAW': matchup\n        }\n\n        for adj, pos in debate.adjudicators.with_positions():\n            if adj.email is None:\n                continue\n\n            context_user = context.copy()\n            context_user['USER'] = adj.name\n            context_user['POSITION'] = adj_position_names[pos]\n\n            emails.append((context_user, adj))\n\n    return emails\n\n\ndef randomized_url_email_generator(url, tournament_id):\n    emails = []\n    tournament = Tournament.objects.get(id=tournament_id)\n\n    subquery = SentMessageRecord.objects.filter(\n        event=SentMessageRecord.EVENT_TYPE_URL,\n        tournament=tournament, email=OuterRef('email')\n    )\n    participants = tournament.participants.filter(\n        url_key__isnull=False, email__isnull=False\n    ).exclude(email__exact=\"\").annotate(already_sent=Exists(subquery)).filter(already_sent=False)\n\n    for instance in participants:\n        url_ind = url + instance.url_key + '/'\n\n        variables = {'USER': instance.name, 'URL': url_ind, 'KEY': instance.url_key, 'TOURN': str(tournament)}\n\n        emails.append((variables, instance))\n\n    return emails\n\n\ndef ballots_email_generator(debate_id):\n    emails = []\n    debate = Debate.objects.get(id=debate_id)\n    tournament = debate.round.tournament\n    ballots = DebateResult(debate.confirmed_ballot).as_dicts()\n    round_name = _(\"%(tournament)s %(round)s @ %(room)s\") % {'tournament': str(tournament),\n                                                             'round': debate.round.name, 'room': debate.venue.name}\n\n    context = {'DEBATE': round_name}\n    use_codes = use_team_code_names(debate.round.tournament, False)\n\n    for ballot in ballots:\n        if 'adjudicator' in ballot:\n            judge = ballot['adjudicator']\n        else:\n            judge = debate.debateadjudicator_set.get(type=DebateAdjudicator.TYPE_CHAIR).adjudicator\n\n        if judge.email is None:\n            continue\n\n        scores = \"\"\n        for team in ballot['teams']:\n\n            team_name = team['team'].code_name if use_codes else team['team'].short_name\n            scores += _(\"(%(side)s) %(team)s\\n\") % {'side': team['side'], 'team': team_name}\n\n            for speaker in team['speakers']:\n                scores += _(\"- %(debater)s: %(score)s\\n\") % {'debater': speaker['speaker'], 'score': speaker['score']}\n\n        context_user = context.copy()\n        context_user['USER'] = judge.name\n        context_user['SCORES'] = scores\n\n        emails.append((context, judge))\n\n    return emails\n\n\ndef standings_email_generator(url, round_id):\n    emails = []\n    round = Round.objects.get(id=round_id)\n    tournament = round.tournament\n\n    teams = round.active_teams.prefetch_related('speaker_set')\n    populate_win_counts(teams)\n\n    context = {\n        'TOURN': str(tournament),\n        'ROUND': round.name,\n        'URL': url if tournament.pref('public_team_standings') else \"\"\n    }\n\n    for team in teams:\n        context_team = context.copy()\n        context_team['POINTS'] = str(team.points_count)\n        context_team['TEAM'] = team.short_name\n\n        for speaker in team.speaker_set.all():\n            if speaker.email is None:\n                continue\n\n            context_user = context_team.copy()\n            context_user['USER'] = speaker.name\n\n            emails.append((context_user, speaker))\n\n    return emails\n\n\ndef motion_release_email_generator(round_id):\n    emails = []\n    round = Round.objects.get(id=round_id)\n\n    def _create_motion_list():\n        motion_list = \"\"\n        for motion in round.motion_set.all():\n            motion_list += _(\" - %s (%s)\\n\") % (motion.text, motion.reference)\n\n            if motion.info_slide:\n                motion_list += \"   %s\\n\" % (motion.info_slide)\n\n        return motion_list\n\n    context = {\n        'TOURN': str(round.tournament),\n        'ROUND': round.name,\n        'MOTIONS': _create_motion_list()\n    }\n\n    teams = round.tournament.team_set.filter(round_availabilities__round=round).prefetch_related('speaker_set')\n    for team in teams:\n        for speaker in team.speaker_set.all():\n            if speaker.email is None:\n                continue\n\n            context_user = context.copy()\n            context_user['USER'] = speaker.name\n\n            emails.append((context_user, speaker))\n\n    return emails\n\n\ndef team_speaker_email_generator(tournament_id):\n    emails = []\n    tournament = Tournament.objects.get(id=tournament_id)\n\n    for team in tournament.team_set.all().prefetch_related('speaker_set', 'break_categories').select_related('division', 'institution'):\n        context = {\n            'TOURN': str(tournament),\n            'SHORT': team.short_name,\n            'LONG': team.long_name,\n            'CODE': team.code_name,\n            'DIVISION': team.division.name if team.division is not None else \"\",\n            'BREAK': _(\", \").join([breakq.name for breakq in team.break_categories.all()]),\n            'SPEAKERS': _(\", \").join([p.name for p in team.speaker_set.all()]),\n            'INSTITUTION': str(team.institution),\n            'EMOJI': team.emoji\n        }\n\n        for speaker in team.speakers:\n            if speaker.email is None:\n                continue\n\n            context_user = context.copy()\n            context_user['USER'] = speaker.name\n\n            emails.append((context_user, speaker))\n\n    return emails\n", "path": "tabbycat/notifications/utils.py"}], "after_files": [{"content": "from django.db.models import Exists, OuterRef\nfrom django.utils.translation import gettext as _\n\nfrom adjallocation.allocation import AdjudicatorAllocation\nfrom draw.models import Debate\nfrom results.result import BaseConsensusDebateResultWithSpeakers, DebateResult, VotingDebateResult\nfrom results.utils import side_and_position_names\nfrom options.utils import use_team_code_names\nfrom participants.prefetch import populate_win_counts\nfrom tournaments.models import Round, Tournament\n\nfrom .models import SentMessageRecord\n\n\ndef adjudicator_assignment_email_generator(round_id):\n    emails = []\n    round = Round.objects.get(id=round_id)\n    tournament = round.tournament\n    draw = round.debate_set_with_prefetches(speakers=False, divisions=False).all()\n    use_codes = use_team_code_names(tournament, False)\n\n    adj_position_names = {\n        AdjudicatorAllocation.POSITION_CHAIR: _(\"the chair\"),\n        AdjudicatorAllocation.POSITION_ONLY: _(\"the only\"),\n        AdjudicatorAllocation.POSITION_PANELLIST: _(\"a panellist\"),\n        AdjudicatorAllocation.POSITION_TRAINEE: _(\"a trainee\"),\n    }\n\n    def _assemble_panel(adjs):\n        adj_string = []\n        for adj, pos in adjs:\n            adj_string.append(\"%s (%s)\" % (adj.name, adj_position_names[pos]))\n\n        return \", \".join(adj_string)\n\n    for debate in draw:\n        matchup = debate.matchup_codes if use_codes else debate.matchup\n        context = {\n            'ROUND': round.name,\n            'VENUE': debate.venue.name,\n            'PANEL': _assemble_panel(debate.adjudicators.with_positions()),\n            'DRAW': matchup\n        }\n\n        for adj, pos in debate.adjudicators.with_positions():\n            if adj.email is None:\n                continue\n\n            context_user = context.copy()\n            context_user['USER'] = adj.name\n            context_user['POSITION'] = adj_position_names[pos]\n\n            emails.append((context_user, adj))\n\n    return emails\n\n\ndef randomized_url_email_generator(url, tournament_id):\n    emails = []\n    tournament = Tournament.objects.get(id=tournament_id)\n\n    subquery = SentMessageRecord.objects.filter(\n        event=SentMessageRecord.EVENT_TYPE_URL,\n        tournament=tournament, email=OuterRef('email')\n    )\n    participants = tournament.participants.filter(\n        url_key__isnull=False, email__isnull=False\n    ).exclude(email__exact=\"\").annotate(already_sent=Exists(subquery)).filter(already_sent=False)\n\n    for instance in participants:\n        url_ind = url + instance.url_key + '/'\n\n        variables = {'USER': instance.name, 'URL': url_ind, 'KEY': instance.url_key, 'TOURN': str(tournament)}\n\n        emails.append((variables, instance))\n\n    return emails\n\n\ndef ballots_email_generator(debate_id):\n    emails = []\n    debate = Debate.objects.get(id=debate_id)\n    tournament = debate.round.tournament\n    results = DebateResult(debate.confirmed_ballot)\n    round_name = _(\"%(tournament)s %(round)s @ %(room)s\") % {'tournament': str(tournament),\n                                                             'round': debate.round.name, 'room': debate.venue.name}\n\n    use_codes = use_team_code_names(debate.round.tournament, False)\n\n    def _create_ballot(result, scoresheet):\n        ballot = \"\"\n\n        for side, (side_name, pos_names) in zip(tournament.sides, side_and_position_names(tournament)):\n            ballot += _(\"%(side)s: %(team)s (%(points)d - %(rank)s)\\n\") % {\n                'side': side_name,\n                'team': result.debateteams[side].team.code_name if use_codes else result.debateteams[side].team.short_name,\n                'points': scoresheet.get_total(side),\n                'rank': scoresheet.rank(side)\n            }\n\n            for pos, pos_name in zip(tournament.positions, pos_names):\n                ballot += _(\"- %(pos)s: %(speaker)s (%(score)s)\\n\") % {\n                    'pos': pos_name,\n                    'speaker': result.get_speaker(side, pos).name,\n                    'score': scoresheet.get_score(side, pos)\n                }\n\n        return ballot\n\n    if isinstance(results, VotingDebateResult):\n        for (adj, ballot) in results.scoresheets.items():\n            if adj.email is None:\n                continue\n\n            context = {'DEBATE': round_name, 'USER': adj.name, 'SCORES': _create_ballot(results, ballot)}\n            emails.append((context, adj))\n    elif isinstance(results, BaseConsensusDebateResultWithSpeakers):\n        context = {'DEBATE': round_name, 'SCORES': _create_ballot(results, results.scoresheet)}\n\n        for adj in debate.debateadjudicator_set.all():\n            if adj.adjudicator.email is None:\n                continue\n\n            context_user = context.copy()\n            context_user['USER'] = adj.adjudicator.name\n\n            emails.append((context_user, adj.adjudicator))\n\n    return emails\n\n\ndef standings_email_generator(url, round_id):\n    emails = []\n    round = Round.objects.get(id=round_id)\n    tournament = round.tournament\n\n    teams = round.active_teams.prefetch_related('speaker_set')\n    populate_win_counts(teams)\n\n    context = {\n        'TOURN': str(tournament),\n        'ROUND': round.name,\n        'URL': url if tournament.pref('public_team_standings') else \"\"\n    }\n\n    for team in teams:\n        context_team = context.copy()\n        context_team['POINTS'] = str(team.points_count)\n        context_team['TEAM'] = team.short_name\n\n        for speaker in team.speaker_set.all():\n            if speaker.email is None:\n                continue\n\n            context_user = context_team.copy()\n            context_user['USER'] = speaker.name\n\n            emails.append((context_user, speaker))\n\n    return emails\n\n\ndef motion_release_email_generator(round_id):\n    emails = []\n    round = Round.objects.get(id=round_id)\n\n    def _create_motion_list():\n        motion_list = \"\"\n        for motion in round.motion_set.all():\n            motion_list += _(\" - %s (%s)\\n\") % (motion.text, motion.reference)\n\n            if motion.info_slide:\n                motion_list += \"   %s\\n\" % (motion.info_slide)\n\n        return motion_list\n\n    context = {\n        'TOURN': str(round.tournament),\n        'ROUND': round.name,\n        'MOTIONS': _create_motion_list()\n    }\n\n    teams = round.tournament.team_set.filter(round_availabilities__round=round).prefetch_related('speaker_set')\n    for team in teams:\n        for speaker in team.speaker_set.all():\n            if speaker.email is None:\n                continue\n\n            context_user = context.copy()\n            context_user['USER'] = speaker.name\n\n            emails.append((context_user, speaker))\n\n    return emails\n\n\ndef team_speaker_email_generator(tournament_id):\n    emails = []\n    tournament = Tournament.objects.get(id=tournament_id)\n\n    for team in tournament.team_set.all().prefetch_related('speaker_set', 'break_categories').select_related('division', 'institution'):\n        context = {\n            'TOURN': str(tournament),\n            'SHORT': team.short_name,\n            'LONG': team.long_name,\n            'CODE': team.code_name,\n            'DIVISION': team.division.name if team.division is not None else \"\",\n            'BREAK': _(\", \").join([breakq.name for breakq in team.break_categories.all()]),\n            'SPEAKERS': _(\", \").join([p.name for p in team.speaker_set.all()]),\n            'INSTITUTION': str(team.institution),\n            'EMOJI': team.emoji\n        }\n\n        for speaker in team.speakers:\n            if speaker.email is None:\n                continue\n\n            context_user = context.copy()\n            context_user['USER'] = speaker.name\n\n            emails.append((context_user, speaker))\n\n    return emails\n", "path": "tabbycat/notifications/utils.py"}]}