diff --git a/README.md b/README.md
index 7fbafb1ae..07914c8a7 100644
--- a/README.md
+++ b/README.md
@@ -72,7 +72,7 @@ See [INSTALL.md](INSTALL.md) for setup instructions.
This application can be translated in to different languages.
-This is done using [Django's standard translation system](https://docs.djangoproject.com/en/3.2/topics/i18n/translation/).
+This is done using [Django's standard translation system](https://docs.djangoproject.com/en/4.2/topics/i18n/translation/).
## TranslatedTemplateView
diff --git a/locale/cy/LC_MESSAGES/django.po b/locale/cy/LC_MESSAGES/django.po
index 0b88ee4f9..c87fd4185 100644
--- a/locale/cy/LC_MESSAGES/django.po
+++ b/locale/cy/LC_MESSAGES/django.po
@@ -13,7 +13,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-08-01 17:19+0100\n"
+"POT-Creation-Date: 2023-09-19 15:01+0100\n"
"PO-Revision-Date: 2022-03-24 09:04+0000\n"
"Last-Translator: Sym Roe , 2022\n"
"Language-Team: Welsh (https://www.transifex.com/democracy-club/teams/61326/"
@@ -29,21 +29,21 @@ msgstr ""
msgid "Enter your postcode"
msgstr "Rhowch eich cod post"
-#: wcivf/apps/elections/models.py:656
+#: wcivf/apps/elections/models.py:672
msgid "First-past-the-post"
msgstr "Y Cyntaf i’r Felin"
-#: wcivf/apps/elections/models.py:658
+#: wcivf/apps/elections/models.py:674
#, fuzzy
#| msgid "The Additional Member System"
msgid "Additional Member System"
msgstr "Y System Aelodau Ychwanegol"
-#: wcivf/apps/elections/models.py:660
+#: wcivf/apps/elections/models.py:676
msgid "Supplementary Vote"
msgstr "Pleidlais Atodol"
-#: wcivf/apps/elections/models.py:662
+#: wcivf/apps/elections/models.py:678
msgid "Single Transferable Vote"
msgstr "Pleidlais Sengl Drosglwyddadwy"
@@ -228,7 +228,7 @@ msgstr "Rydych chi yma"
#: wcivf/apps/parties/templates/parties/independent_candidate.html:17
#: wcivf/apps/parties/templates/parties/party_detail.html:17
#: wcivf/apps/parties/templates/parties/speaker_seeking_reelection.html:17
-#: wcivf/apps/people/templates/people/person_detail.html:27
+#: wcivf/apps/people/templates/people/person_detail.html:32
#: wcivf/templates/base.html:101
msgid "Home"
msgstr "Cartref"
@@ -752,7 +752,23 @@ msgstr ""
"helpu i wella'r dudalen hon: ychwanegwch wybodaeth "
"am ymgeiswyr i'n cronfa ddata."
-#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:136
+#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:130
+msgid "Electorate"
+msgstr "Nifer etholwyr"
+
+#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:137
+msgid "Ballot Papers Issued"
+msgstr "Bapurau pleidleisio"
+
+#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:144
+msgid "Spoilt Ballots"
+msgstr "Nifer y bleidleisiau a ddifethwyd"
+
+#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:151
+msgid "Turnout"
+msgstr "Cyfrif pleidleisiau"
+
+#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:171
#, python-format
msgid ""
"The official candidate list has been published."
@@ -760,32 +776,32 @@ msgstr ""
"Mae'r rhestr swyddogol o ymgeiswyr wedi'i "
"chyhoeddi."
-#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:141
+#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:176
msgid "The official candidate list should have been published on"
msgstr "Dylai'r rhestr swyddogol o ymgeiswyr fod wedi'i chyhoeddi ar "
-#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:143
+#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:178
msgid "The official candidate list should be published on"
msgstr "Dylai'r rhestr swyddogol o ymgeiswyr gael ei chyhoeddi ar"
-#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:151
+#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:186
msgid "Read the official candidate booklet for this election."
msgstr "Darllenwch y llyfryn ymgeiswyr swyddogol ar gyfer yr etholiad hwn."
-#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:155
+#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:190
#, python-format
msgid " %(description)s "
msgstr " %(description)s "
-#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:163
+#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:198
msgid "Can you vote in this election?"
msgstr "A allwch chi bleidleisio yn yr etholiad hwn?"
-#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:164
+#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:199
msgid "Age"
msgstr "Oedran"
-#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:166
+#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:201
#, python-format
msgid ""
"You need to be over %(voter_age)s on the %(voter_age_date)s of "
@@ -794,16 +810,16 @@ msgstr ""
"Mae angen i chi fod dros %(voter_age)s ar %(voter_age_date)s "
"%(election_date)s i bleidleisio yn yr etholiad hwn."
-#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:171
+#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:206
msgid "Citizenship"
msgstr "Dinasyddiaeth"
-#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:191
+#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:226
#: wcivf/apps/people/templates/people/includes/_person_about_card.html:9
msgid "Wikipedia"
msgstr "Wikipedia"
-#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:193
+#: wcivf/apps/elections/templates/elections/includes/_single_ballot.html:228
#: wcivf/apps/people/templates/people/includes/_person_about_card.html:11
msgid "Read more on Wikipedia"
msgstr "Darllenwch fwy ar Wikipedia"
@@ -841,15 +857,37 @@ msgstr "Gweld y canlyniadau"
msgid "We don't know of any upcoming elections in your area."
msgstr "Ni wyddwn am unrhyw etholiadau sydd i ddod"
-#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:12
+#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:7
+msgid ""
+"Local and devolved elections in the UK typically happen on the first "
+"Thursday in May. By-elections and parliamentary general elections can happen "
+"at any time. Not all areas have elections each year."
+msgstr ""
+
+#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:8
+#, fuzzy
+#| msgid ""
+#| "For more information, visit The Electoral Commission's website"
+#| "a> or ask an official at your polling station."
+msgid ""
+"Learn more about elections in the UK on the Electoral "
+"Commission website."
+msgstr ""
+"Am fwy o wybodaeth, ewch i Wefan y Comisiwn Etholiadol neu "
+"gofynnwch i swyddog yn eich gorsaf bleidleisio."
+
+#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:14
msgid "Elections in your area"
msgstr "Etholiadau yn eich ardal"
-#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:30
+#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:32
msgid "Recently past elections"
msgstr "Etholiadau diweddar yn y gorffennol"
-#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:43
+#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:45
#, python-format
msgid ""
"You will have %(num_ballots)s ballot paper%(ballots_pluralised)s to fill out."
@@ -857,16 +895,16 @@ msgstr ""
"Bydd gennych %(num_ballots)s papur pleidleisio%(ballots_pluralised)s i'w "
"lenwi."
-#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:59
+#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:61
#, python-format
msgid "%(ballot.short_cancelled_message_html)s"
msgstr "%(ballot.short_cancelled_message_html)s"
-#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:69
+#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:71
msgid "(uncontested)"
msgstr "(dim cystadleuaeth)"
-#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:70
+#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:72
msgid "(may be contested)"
msgstr "(efallai y bydd cystadleuaeth)"
@@ -880,28 +918,6 @@ msgstr ""
"Efallai y bydd etholiadau cyngor plwyf, tref neu gymunedol mewn rhai "
"ardaloedd."
-#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:86
-msgid ""
-"Local and devolved elections in the UK typically happen on the first "
-"Thursday in May. By-elections and parliamentary general elections can happen "
-"at any time. Not all areas have elections each year."
-msgstr ""
-
-#: wcivf/apps/elections/templates/elections/includes/inline_elections_nav_list.html:87
-#, fuzzy
-#| msgid ""
-#| "For more information, visit The Electoral Commission's website"
-#| "a> or ask an official at your polling station."
-msgid ""
-"Learn more about elections in the UK on the Electoral "
-"Commission website."
-msgstr ""
-"Am fwy o wybodaeth, ewch i Wefan y Comisiwn Etholiadol neu "
-"gofynnwch i swyddog yn eich gorsaf bleidleisio."
-
#: wcivf/apps/elections/templates/elections/party_list_view.html:14
msgid "emblem"
msgstr "symbol"
@@ -949,11 +965,11 @@ msgstr "Trydariadau gan"
msgid "Candidates"
msgstr "Ymgeiswyr"
-#: wcivf/apps/elections/templates/elections/post_view.html:27
+#: wcivf/apps/elections/templates/elections/post_view.html:31
msgid "Next election"
msgstr "Yr etholiad nesaf"
-#: wcivf/apps/elections/templates/elections/post_view.html:29
+#: wcivf/apps/elections/templates/elections/post_view.html:33
#, python-format
msgid ""
"The next election for %(postelection)s "
@@ -962,11 +978,11 @@ msgstr ""
"Cynhelir yr etholiad nesaf ar gyfer "
"%(postelection)s"
-#: wcivf/apps/elections/templates/elections/post_view.html:31
+#: wcivf/apps/elections/templates/elections/post_view.html:35
msgid "being held today"
msgstr "heddiw"
-#: wcivf/apps/elections/templates/elections/post_view.html:33
+#: wcivf/apps/elections/templates/elections/post_view.html:37
msgid "due to take place on"
msgstr "Mae disgwyl iddo gael ei gynnal ar"
@@ -1784,11 +1800,6 @@ msgstr ""
msgid "Position"
msgstr ""
-#: wcivf/apps/people/templates/people/includes/_person_previous_elections_card.html:25
-#, python-format
-msgid "%(post_label)s: %(election)s"
-msgstr "%(post_label)s: %(election)s"
-
#: wcivf/apps/people/templates/people/includes/intros/_constituency.html:5
#, python-format
msgid ""
@@ -1967,11 +1978,11 @@ msgstr ""
msgid "profile photo of %(object.name)s"
msgstr "Llun proffil %(object.name)s"
-#: wcivf/apps/people/templates/people/person_detail.html:24
+#: wcivf/apps/people/templates/people/person_detail.html:29
msgid "You are here:"
msgstr "Rydych chi yma:"
-#: wcivf/apps/people/templates/people/person_detail.html:58
+#: wcivf/apps/people/templates/people/person_detail.html:65
msgid "Back to candidates in"
msgstr "Dychwelyd at ymgeiswyr yn"
@@ -2173,6 +2184,26 @@ msgstr "Etholiadau i ddod"
msgid "Join our mailing list"
msgstr "Ymunwch â'n rhestr bostio"
+#, fuzzy
+#~| msgid "Elected (vote count not available)"
+#~ msgid "Electorate not available"
+#~ msgstr "Etholwyr ddim ar gael"
+
+#, fuzzy
+#~| msgid "Elected (vote count not available)"
+#~ msgid "Turnout not available"
+#~ msgstr "Nid yw'r cyfrif pleidleisiau ar gael"
+
+#~ msgid "Number of ballot papers issued not available"
+#~ msgstr "Nid yw nifer y papurau pleidleisio a roddwyd ar gael"
+
+#~ msgid "Number of spoilt ballots not available"
+#~ msgstr "Nifer y pleidleisiau a ddifethwyd ddim ar gael"
+
+#, python-format
+#~ msgid "%(post_label)s: %(election)s"
+#~ msgstr "%(post_label)s: %(election)s"
+
#~ msgid "Where do I vote?"
#~ msgstr "Ble rydw i'n pleidleisio?"
diff --git a/wcivf/apps/elections/import_helpers.py b/wcivf/apps/elections/import_helpers.py
index f0454a6b8..241702ccc 100644
--- a/wcivf/apps/elections/import_helpers.py
+++ b/wcivf/apps/elections/import_helpers.py
@@ -103,6 +103,24 @@ def update_or_create_from_ballot_dict(self, ballot_dict):
},
)
+ if ballot_dict["results"]:
+ election, created = Election.objects.update_or_create(
+ slug=slug,
+ election_type=election_type,
+ defaults={
+ "ballot_papers_issued": ballot_dict["results"][
+ "num_turnout_reported"
+ ],
+ "electorate": ballot_dict["results"][
+ "total_electorate"
+ ],
+ "turnout": ballot_dict["results"]["turnout_percentage"],
+ "spoilt_ballots": ballot_dict["results"][
+ "num_spoilt_ballots"
+ ],
+ },
+ )
+
self.import_metadata_from_ee(election)
self.election_cache[election.slug] = election
return self.election_cache[slug]
diff --git a/wcivf/apps/elections/migrations/0041_election_ballot_papers_issued_election_electorate_and_more.py b/wcivf/apps/elections/migrations/0041_election_ballot_papers_issued_election_electorate_and_more.py
new file mode 100644
index 000000000..318eb5cc6
--- /dev/null
+++ b/wcivf/apps/elections/migrations/0041_election_ballot_papers_issued_election_electorate_and_more.py
@@ -0,0 +1,32 @@
+# Generated by Django 4.2.3 on 2023-09-06 11:30
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("elections", "0040_postelection_requires_voter_id"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="election",
+ name="ballot_papers_issued",
+ field=models.IntegerField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="election",
+ name="electorate",
+ field=models.IntegerField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="election",
+ name="spoilt_ballots",
+ field=models.IntegerField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="election",
+ name="turnout",
+ field=models.IntegerField(blank=True, null=True),
+ ),
+ ]
diff --git a/wcivf/apps/elections/models.py b/wcivf/apps/elections/models.py
index 53640be42..d86373df6 100644
--- a/wcivf/apps/elections/models.py
+++ b/wcivf/apps/elections/models.py
@@ -48,6 +48,10 @@ class Election(models.Model):
election_weight = models.IntegerField(default=10)
metadata = JSONField(null=True)
any_non_by_elections = models.BooleanField(default=False)
+ ballot_papers_issued = models.IntegerField(blank=True, null=True)
+ electorate = models.IntegerField(blank=True, null=True)
+ turnout = models.IntegerField(blank=True, null=True)
+ spoilt_ballots = models.IntegerField(blank=True, null=True)
objects = ElectionManager()
@@ -239,6 +243,19 @@ def pluralized_division_name(self):
return pluralise.get(suffix, f"{suffix}s")
+ @property
+ def has_results(self):
+ """
+ Returns a boolean for if the election has results
+ """
+ return bool(
+ self.spoilt_ballots
+ or self.ballot_papers_issued
+ or self.turnout
+ or self.electorate
+ or self.postelection_set.filter(personpost__elected=True).exists()
+ )
+
class Post(models.Model):
"""
diff --git a/wcivf/apps/elections/templates/elections/includes/_people_list.html b/wcivf/apps/elections/templates/elections/includes/_people_list.html
index dcf4534f9..3a1d69ca9 100644
--- a/wcivf/apps/elections/templates/elections/includes/_people_list.html
+++ b/wcivf/apps/elections/templates/elections/includes/_people_list.html
@@ -1,4 +1,4 @@
-
+
{% for person_post in people %}
{% include "elections/includes/_person_card.html" with person_post=person_post %}
{% endfor %}
diff --git a/wcivf/apps/elections/templates/elections/includes/_single_ballot.html b/wcivf/apps/elections/templates/elections/includes/_single_ballot.html
index 1e2926656..d4ed6790a 100644
--- a/wcivf/apps/elections/templates/elections/includes/_single_ballot.html
+++ b/wcivf/apps/elections/templates/elections/includes/_single_ballot.html
@@ -129,7 +129,42 @@ {% if postelection.is_london_assembly_additional %}{% trans "Additional memb
{% endif %}
{% endif %}
+ {% if postelection.election.in_past and postelection.election.has_results %}
+
+
+
+ {% if postelection.election.electorate %}
+
+ {% trans "Electorate" %} |
+ {{ postelection.election.electorate }} |
+
+ {% endif %}
+
+ {% if postelection.election.ballot_papers_issued %}
+
+ {% trans "Ballot Papers Issued" %} |
+ {{ postelection.election.ballot_papers_issued}} |
+
+ {% endif %}
+
+ {% if postelection.election.spoilt_ballots %}
+
+ {% trans "Spoilt Ballots" %} |
+ {{ postelection.election.spoilt_ballots }} |
+
+ {% endif %}
+ {% if postelection.election.turnout %}
+
+ {% trans "Turnout" %} |
+ {{ postelection.election.turnout|stringformat:"d%%" }} |
+
+ {% endif %}
+
+
+
+
+ {% endif %}
{% if postelection.people and postelection.should_show_candidates %}
{% if postelection.display_as_party_list %}
{% include "elections/includes/_people_list_with_lists.html" with people=postelection.people %}
diff --git a/wcivf/apps/elections/tests/test_election_views.py b/wcivf/apps/elections/tests/test_election_views.py
index aceec0d5f..34cf76336 100644
--- a/wcivf/apps/elections/tests/test_election_views.py
+++ b/wcivf/apps/elections/tests/test_election_views.py
@@ -176,6 +176,25 @@ def test_pre_sopn_text_with_candidates(self):
"""Once nomination papers are published, we will manually verify each candidate.""",
)
+ def test_results_table(self):
+ """check that the table containing the electorate,
+ turnout, spoilt ballots, ballot papers exist
+ for past elections"""
+ self.election.electorate = 100
+ self.election.spoilt_ballots = 5
+ self.election.save()
+
+ response = self.client.get(
+ self.post_election.get_absolute_url(), follow=True
+ )
+ self.assertEqual(response.status_code, 200)
+
+ self.assertTrue(self.post_election.election.has_results)
+ self.assertContains(response, "Electorate")
+ self.assertNotContains(response, "Turnout")
+ self.assertContains(response, "Spoilt Ballots")
+ self.assertNotContains(response, "Ballot Papers Issued")
+
def test_zero_candidates(self):
response = self.client.get(
self.post_election.get_absolute_url(), follow=True
diff --git a/wcivf/apps/elections/tests/test_models.py b/wcivf/apps/elections/tests/test_models.py
index f3a14a036..9395a25d3 100644
--- a/wcivf/apps/elections/tests/test_models.py
+++ b/wcivf/apps/elections/tests/test_models.py
@@ -31,6 +31,26 @@ def test_in_past(self):
assert election.in_past is True
+ def test_electorate(self):
+ election = Election(electorate=1000)
+ assert election.electorate == 1000
+ assert type(election.electorate) == int
+
+ def test_turnout(self):
+ election = Election(turnout=1000)
+ assert election.turnout == 1000
+ assert type(election.turnout) == int
+
+ def test_spoilt_ballots(self):
+ election = Election(spoilt_ballots=1000)
+ assert election.spoilt_ballots == 1000
+ assert type(election.spoilt_ballots) == int
+
+ def test_ballot_papers_issued(self):
+ election = Election(ballot_papers_issued=1000)
+ assert election.ballot_papers_issued == 1000
+ assert type(election.ballot_papers_issued) == int
+
def test_is_city_of_london(self, election, city_of_london_election):
assert election.is_city_of_london is False
assert city_of_london_election.is_city_of_london is True
diff --git a/wcivf/assets/scss/style.scss b/wcivf/assets/scss/style.scss
index e10a5ecdc..fe67ef107 100644
--- a/wcivf/assets/scss/style.scss
+++ b/wcivf/assets/scss/style.scss
@@ -156,3 +156,10 @@ input[type=radio] {
.ds-status-message p {
color: $white;
}
+
+#results-table {
+ td:nth-child(2) {
+ text-align: right;
+ }
+ th, td { border: 1px solid black; }
+}