diff --git a/api/endpoints/v1/sandbox/sandbox-responses/10035187881.json b/api/endpoints/v1/sandbox/sandbox-responses/10035187881.json index f56e0682..1b0f478b 100644 --- a/api/endpoints/v1/sandbox/sandbox-responses/10035187881.json +++ b/api/endpoints/v1/sandbox/sandbox-responses/10035187881.json @@ -44,6 +44,7 @@ "post_name": "Lancaster Gate", "postal_voting_requirements": null, "candidates_verified": true, + "by_election_reason": "", "voting_system": { "slug": "FPTP", "name": "First-past-the-post", diff --git a/api/endpoints/v1/sandbox/sandbox-responses/10035187882.json b/api/endpoints/v1/sandbox/sandbox-responses/10035187882.json index 46b7036f..667b53c6 100644 --- a/api/endpoints/v1/sandbox/sandbox-responses/10035187882.json +++ b/api/endpoints/v1/sandbox/sandbox-responses/10035187882.json @@ -44,6 +44,7 @@ "post_name": "Lancaster Gate", "postal_voting_requirements": null, "candidates_verified": true, + "by_election_reason": "", "voting_system": { "slug": "FPTP", "name": "First-past-the-post", diff --git a/api/endpoints/v1/sandbox/sandbox-responses/10035187883.json b/api/endpoints/v1/sandbox/sandbox-responses/10035187883.json index 62b00b33..5ec2810b 100644 --- a/api/endpoints/v1/sandbox/sandbox-responses/10035187883.json +++ b/api/endpoints/v1/sandbox/sandbox-responses/10035187883.json @@ -30,6 +30,7 @@ "post_name": "Lancaster Gate", "postal_voting_requirements": null, "candidates_verified": true, + "by_election_reason": "", "voting_system": { "slug": "FPTP", "name": "First-past-the-post", diff --git a/api/endpoints/v1/sandbox/sandbox-responses/AA12AA.json b/api/endpoints/v1/sandbox/sandbox-responses/AA12AA.json index cbc714d2..f3226707 100644 --- a/api/endpoints/v1/sandbox/sandbox-responses/AA12AA.json +++ b/api/endpoints/v1/sandbox/sandbox-responses/AA12AA.json @@ -67,6 +67,7 @@ "post_name": "Lancaster Gate", "postal_voting_requirements": null, "candidates_verified": true, + "by_election_reason": "", "voting_system": { "slug": "FPTP", "name": "First-past-the-post", diff --git a/api/endpoints/v1/sandbox/sandbox-responses/AA12AB.json b/api/endpoints/v1/sandbox/sandbox-responses/AA12AB.json index a7c073f5..997a12a7 100644 --- a/api/endpoints/v1/sandbox/sandbox-responses/AA12AB.json +++ b/api/endpoints/v1/sandbox/sandbox-responses/AA12AB.json @@ -30,6 +30,7 @@ "post_name": "Lancaster Gate", "postal_voting_requirements": null, "candidates_verified": true, + "by_election_reason": "", "voting_system": { "slug": "FPTP", "name": "First-past-the-post", diff --git a/api/endpoints/v1/sandbox/sandbox-responses/AA14AA.json b/api/endpoints/v1/sandbox/sandbox-responses/AA14AA.json index 7c52000b..733c9c60 100644 --- a/api/endpoints/v1/sandbox/sandbox-responses/AA14AA.json +++ b/api/endpoints/v1/sandbox/sandbox-responses/AA14AA.json @@ -30,6 +30,7 @@ "post_name": "London Borough of Lewisham", "postal_voting_requirements": null, "candidates_verified": true, + "by_election_reason": "", "voting_system": { "slug": "sv", "name": "Supplementary Vote", @@ -186,6 +187,7 @@ "post_name": "Blackheath", "postal_voting_requirements": null, "candidates_verified": true, + "by_election_reason": "", "candidates": [ { "list_position": null, @@ -403,6 +405,7 @@ "post_name": "Blackheath", "postal_voting_requirements": null, "candidates_verified": false, + "by_election_reason": "", "candidates": [ { "list_position": null, @@ -613,6 +616,7 @@ "post_name": "Lewisham East", "postal_voting_requirements": null, "candidates_verified": false, + "by_election_reason": "", "voting_system": { "slug": "FPTP", "name": "First-past-the-post", diff --git a/api/endpoints/v1/sandbox/sandbox-responses/AA15AA.json b/api/endpoints/v1/sandbox/sandbox-responses/AA15AA.json index df741943..c0b67c1f 100644 --- a/api/endpoints/v1/sandbox/sandbox-responses/AA15AA.json +++ b/api/endpoints/v1/sandbox/sandbox-responses/AA15AA.json @@ -43,6 +43,7 @@ "post_name": "West Tyrone", "postal_voting_requirements": null, "candidates_verified": false, + "by_election_reason": "", "voting_system": { "slug": "FPTP", "name": "First-past-the-post", diff --git a/api/endpoints/v1/sandbox/sandbox-responses/AB12CD.json b/api/endpoints/v1/sandbox/sandbox-responses/AB12CD.json index 94adfb08..134e0acb 100644 --- a/api/endpoints/v1/sandbox/sandbox-responses/AB12CD.json +++ b/api/endpoints/v1/sandbox/sandbox-responses/AB12CD.json @@ -45,6 +45,7 @@ "post_name": "London Borough of Lewisham", "postal_voting_requirements": null, "candidates_verified": true, + "by_election_reason": "", "wcivf_url": "https://whocanivotefor.co.uk/elections/mayor.lewisham.2018-05-03/post-lewisham/london-borough-of-lewisham", "voting_system": { "slug": "sv", @@ -264,6 +265,7 @@ "post_name": "Blackheath", "postal_voting_requirements": null, "candidates_verified": true, + "by_election_reason": "", "wcivf_url": "https://whocanivotefor.co.uk/elections/local.lewisham.2018-05-03/post-LBW:E05000438/blackheath", "voting_system": { "slug": "FPTP", diff --git a/api/endpoints/v1/voting_information/stitcher.py b/api/endpoints/v1/voting_information/stitcher.py index 1f90f46a..a7d8da62 100644 --- a/api/endpoints/v1/voting_information/stitcher.py +++ b/api/endpoints/v1/voting_information/stitcher.py @@ -363,7 +363,9 @@ def make_result_known_response(self): ballot["postal_voting_requirements"] = wcivf_ballot.get( "postal_voting_requirements", None ) - + ballot["by_election_reason"] = wcivf_ballot.get( + "by_election_reason", "" + ) # We only sort by organisation_type at the moment, but we # could add more values here to sort by more fields self.ballot_sort_keys[ballot["ballot_paper_id"]] = wcivf_ballot[ diff --git a/api/frontend/frontend/apps/api_docs/v1/docs/finder_data_structures.apibp b/api/frontend/frontend/apps/api_docs/v1/docs/finder_data_structures.apibp index 41ec7e30..0db5f343 100644 --- a/api/frontend/frontend/apps/api_docs/v1/docs/finder_data_structures.apibp +++ b/api/frontend/frontend/apps/api_docs/v1/docs/finder_data_structures.apibp @@ -35,6 +35,18 @@ + `UNDER_CONTESTED` + `CANDIDATE_DEATH` +## ByElectionReason (enum) ++ `DEATH` ++ `RESIGNATION` ++ `ELECTORAL_COURT` ++ `FAILURE_TO_ACCEPT` ++ `FAILURE_TO_ATTEND_MEETINGS` ++ `DISQUALIFICATION` ++ `LOSING_QUALIFICATION` ++ `RECALL_PETITION` ++ `OTHER` ++ `UNKNOWN` +* `` - A blank response denotes 'NOT_APPLICABLE' - i.e. neither a by election nor a ballot. ## ValidFinderResponse (object) + `address_picker`: `false` (boolean) - True if we need to show this user an address picker @@ -103,6 +115,7 @@ + `election_name`: `Cardiff local election` (string) - Friendly name for this ballot's parent election group + `post_name`: `Pontprennau/Old St. Mellons` (string) - Name of the division or post the winner(s) of this election will represent + `candidates_verified`: `false` (boolean) - True if the list of candidates for this election has been confirmed against the nomination papers for this ballot. If this property is False, the candidate list is provisional or unconfirmed. + + `by_election_reason`: (ByElectionReason) - reason by election was triggered. A blank response denotes 'NOT_APPLICABLE' - i.e. neither a by election nor a ballot. + `voting_system` (object) - The voting system used in this election + `slug`: `FPTP` (string) - One of `AMS`, `FPTP`, `PR-CL`, `sv`, `STV` + `name`: `First-past-the-post` (string) - The name of this voting system (e.g: "First-past-the-post") diff --git a/api/frontend/frontend/apps/api_docs/v1/templates/api_docs_rendered.html b/api/frontend/frontend/apps/api_docs/v1/templates/api_docs_rendered.html index d646cd29..9bc7d2ce 100644 --- a/api/frontend/frontend/apps/api_docs/v1/templates/api_docs_rendered.html +++ b/api/frontend/frontend/apps/api_docs/v1/templates/api_docs_rendered.html @@ -302,6 +302,7 @@ "election_name": "Cardiff local election", "post_name": "Pontprennau/Old St. Mellons", "candidates_verified": false, + "by_election_reason": "DEATH", "voting_system": { "slug": "FPTP", "name": "First-past-the-post", @@ -669,6 +670,23 @@ "type": "boolean", "description": "True if the list of candidates for this election has been confirmed against the nomination papers for this ballot. If this property is False, the candidate list is provisional or unconfirmed." }, + "by_election_reason": { + "type": "string", + "enum": [ + "DEATH", + "RESIGNATION", + "ELECTORAL_COURT", + "FAILURE_TO_ACCEPT", + "FAILURE_TO_ATTEND_MEETINGS", + "DISQUALIFICATION", + "LOSING_QUALIFICATION", + "RECALL_PETITION", + "OTHER", + "UNKNOWN", + "``" + ], + "description": "reason by election was triggered. A blank response denotes 'NOT_APPLICABLE' - i.e. neither a by election nor a ballot." + }, "voting_system": { "type": "object", "properties": { @@ -1292,6 +1310,7 @@ "election_name": "Cardiff local election", "post_name": "Pontprennau/Old St. Mellons", "candidates_verified": false, + "by_election_reason": "DEATH", "voting_system": { "slug": "FPTP", "name": "First-past-the-post", @@ -1659,6 +1678,23 @@ "type": "boolean", "description": "True if the list of candidates for this election has been confirmed against the nomination papers for this ballot. If this property is False, the candidate list is provisional or unconfirmed." }, + "by_election_reason": { + "type": "string", + "enum": [ + "DEATH", + "RESIGNATION", + "ELECTORAL_COURT", + "FAILURE_TO_ACCEPT", + "FAILURE_TO_ATTEND_MEETINGS", + "DISQUALIFICATION", + "LOSING_QUALIFICATION", + "RECALL_PETITION", + "OTHER", + "UNKNOWN", + "``" + ], + "description": "reason by election was triggered. A blank response denotes 'NOT_APPLICABLE' - i.e. neither a by election nor a ballot." + }, "voting_system": { "type": "object", "properties": { diff --git a/pyproject.toml b/pyproject.toml index 6d32fcad..cdf18a5f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dev = [ ] [tool.uv.sources] -dc-response-builder = { git = "https://github.com/DemocracyClub/dc_response_builder.git", tag = "1.1.2" } +dc-response-builder = { git = "https://github.com/DemocracyClub/dc_response_builder.git", tag = "1.2.0" } [tool.uv] required-version = "==0.8.*" diff --git a/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/local.lewisham.blackheath.2018-05-03.json b/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/local.lewisham.blackheath.2018-05-03.json index 161af873..194199da 100644 --- a/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/local.lewisham.blackheath.2018-05-03.json +++ b/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/local.lewisham.blackheath.2018-05-03.json @@ -194,5 +194,6 @@ } } ], - "ballot_paper_id": "local.lewisham.blackheath.2018-05-03" + "ballot_paper_id": "local.lewisham.blackheath.2018-05-03", + "by_election_reason": "" } diff --git a/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/local.lewisham.blackheath.2018-05-10.json b/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/local.lewisham.blackheath.2018-05-10.json index 61d06154..c1a33ced 100644 --- a/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/local.lewisham.blackheath.2018-05-10.json +++ b/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/local.lewisham.blackheath.2018-05-10.json @@ -194,5 +194,6 @@ } } ], - "ballot_paper_id": "local.lewisham.blackheath.2018-05-10" + "ballot_paper_id": "local.lewisham.blackheath.2018-05-10", + "by_election_reason": "" } diff --git a/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/mayor.lewisham.2018-05-03.json b/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/mayor.lewisham.2018-05-03.json index eb27cdef..c966dabd 100644 --- a/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/mayor.lewisham.2018-05-03.json +++ b/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/mayor.lewisham.2018-05-03.json @@ -138,5 +138,6 @@ } } ], - "ballot_paper_id": "mayor.lewisham.2018-05-03" + "ballot_paper_id": "mayor.lewisham.2018-05-03", + "by_election_reason": "" } diff --git a/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/parl.lewisham-east.by.2018-06-14.json b/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/parl.lewisham-east.by.2018-06-14.json index 36db2db8..ba4555c1 100644 --- a/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/parl.lewisham-east.by.2018-06-14.json +++ b/tests/v1/test_data/addresspc_endpoints/test_multiple_elections/parl.lewisham-east.by.2018-06-14.json @@ -16,5 +16,6 @@ "organisation_type": "parl", "candidates_verified": false, "candidates": [], - "ballot_paper_id": "parl.lewisham-east.by.2018-06-14" + "ballot_paper_id": "parl.lewisham-east.by.2018-06-14", + "by_election_reason": "" } diff --git a/tests/v1/test_data/addresspc_endpoints/test_one_election_station_known_with_candidates/local.westminster.lancaster-gate.by.2018-11-22.json b/tests/v1/test_data/addresspc_endpoints/test_one_election_station_known_with_candidates/local.westminster.lancaster-gate.by.2018-11-22.json index b02f6c9a..8badf5b4 100644 --- a/tests/v1/test_data/addresspc_endpoints/test_one_election_station_known_with_candidates/local.westminster.lancaster-gate.by.2018-11-22.json +++ b/tests/v1/test_data/addresspc_endpoints/test_one_election_station_known_with_candidates/local.westminster.lancaster-gate.by.2018-11-22.json @@ -112,5 +112,6 @@ } } ], - "ballot_paper_id": "local.westminster.lancaster-gate.by.2018-11-22" + "ballot_paper_id": "local.westminster.lancaster-gate.by.2018-11-22", + "by_election_reason": "" } diff --git a/uv.lock b/uv.lock index 0c5c3026..48a7c78c 100644 --- a/uv.lock +++ b/uv.lock @@ -45,7 +45,7 @@ dev = [ { name = "beautifulsoup4", specifier = "==4.13.4" }, { name = "boto3", specifier = "==1.34.91" }, { name = "coveralls", specifier = "==3.3.1" }, - { name = "dc-response-builder", git = "https://github.com/DemocracyClub/dc_response_builder.git?tag=1.1.2" }, + { name = "dc-response-builder", git = "https://github.com/DemocracyClub/dc_response_builder.git?tag=1.2.0" }, { name = "djhtml", specifier = "==3.0.6" }, { name = "moto", extras = ["dynamodb"], specifier = "==5.0.5" }, { name = "mypy-boto3-dynamodb", specifier = "==1.34.91" }, @@ -345,8 +345,8 @@ dependencies = [ [[package]] name = "dc-response-builder" -version = "1.1.2" -source = { git = "https://github.com/DemocracyClub/dc_response_builder.git?tag=1.1.2#4f63e497e10f8ba57dc3dec3b42772667847e6ae" } +version = "1.2.0" +source = { git = "https://github.com/DemocracyClub/dc_response_builder.git?tag=1.2.0#cd645bdd6db2b704bb23d6e077e6700d36f12773" } dependencies = [ { name = "pydantic", extra = ["email"] }, { name = "uk-election-ids" }, @@ -1223,11 +1223,11 @@ wheels = [ [[package]] name = "uk-election-ids" -version = "0.8.0" +version = "0.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/9d/278835079029a73c9cdd02066d3298ab55859771ab9a73bf6bd3cdecca7d/uk_election_ids-0.8.0.tar.gz", hash = "sha256:af0428ecf5d1802d7834ed0b843b67c873f3b02da53d54a822b1a11f9cf8ef31", size = 10797, upload-time = "2024-04-16T06:59:35.515Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/06/0c5388f56583deddd63cb200ed6447943442fbba0834a79a9557996a7fdc/uk_election_ids-0.10.0.tar.gz", hash = "sha256:f024cdfb4bf5688b2c5a3269458a7ff503fe50869a87ad5fe56faebd36b02c34", size = 11121, upload-time = "2025-07-16T11:47:34.142Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/e3/c86328f5c676b69256ded7b3cfb45376cd7117434714bbedc680ed64a93b/uk_election_ids-0.8.0-py3-none-any.whl", hash = "sha256:b53bbda223e65db0e7eedffa38616c62a82f86e7b99b021522934c5b09598a02", size = 12001, upload-time = "2024-04-16T06:59:34.306Z" }, + { url = "https://files.pythonhosted.org/packages/ad/8e/592e156eeffec70751324298a455d486c93d83c9996b4e3edbb1f8785172/uk_election_ids-0.10.0-py3-none-any.whl", hash = "sha256:873cdd297e5977187fb437f82418d8ce4748b1fc66a183eca3142794a949c7d6", size = 12303, upload-time = "2025-07-16T11:47:32.823Z" }, ] [[package]]