diff --git a/Pipfile.lock b/Pipfile.lock index 4f93bb7..5b3a8ad 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -5,7 +5,7 @@ }, "pipfile-spec": 6, "requires": { - "python_version": "3.10" + "python_version": "3.12" }, "sources": [ { @@ -443,10 +443,11 @@ }, "uk-election-timetables": { "hashes": [ - "sha256:303733e644c0c3747dc73cdf522f8105e9393fbff492bda732b73cd8adc676c3" + "sha256:12d7b8b141254fb9a02114438c6db4063dce2867bf42205da0b7d2bea787150e", + "sha256:9a4e857ae1465bd6c6cc0918722b0dc9373577794aa5d60c3ac7363270b66470" ], "index": "pypi", - "version": "==3.0.0" + "version": "==4.0.0" }, "urllib3": { "hashes": [ diff --git a/postcode_lookup/mock_responses.py b/postcode_lookup/mock_responses.py index ccf9b93..f4f0237 100644 --- a/postcode_lookup/mock_responses.py +++ b/postcode_lookup/mock_responses.py @@ -1,3 +1,9 @@ +from response_builder.v1.builders.ballots import ( + LocalBallotBuilder, + ParlBallotBuilder, +) +from response_builder.v1.builders.base import RootBuilder +from response_builder.v1.generated_responses import candidates from response_builder.v1.generated_responses.root_responses import ( CANCELLED_BALLOT_CANDIDATE_DEATH, CANCELLED_BALLOT_EQUAL_CANDIDATES, @@ -11,6 +17,49 @@ SINGLE_LOCAL_FUTURE_BALLOT_WITHOUT_POLLING_STATION, ) + +class CityOfLondonParlBallot(ParlBallotBuilder): + def __init__(self, poll_open_date, **kwargs): + super().__init__(**kwargs) + self.with_ballot_paper_id( + f"parl.cities-of-london-and-westminster.{poll_open_date}" + ) + self.with_ballot_title( + "UK Parliamentary general election Cities of London and Westminster" + ) + self.with_date(poll_open_date) + self.with_post_name("Cities of London and Westminster") + self.with_election_name("UK Parliamentary general election") + self.with_election_id(f"parl.{poll_open_date}") + self.with_candidates(candidates.all_candidates) + + +class CityOfLondonLocalBallot(LocalBallotBuilder): + def __init__(self, poll_open_date, **kwargs): + super().__init__(**kwargs) + self.with_ballot_paper_id( + f"local.city-of-london.aldersgate.{poll_open_date}" + ) + self.with_ballot_title("City of London local election Aldersgate") + self.with_date(poll_open_date) + self.with_post_name("Aldersgate") + self.with_election_name("City of London local election") + self.with_election_id(f"local.city-of-london.{poll_open_date}") + self.with_candidates(candidates.all_candidates) + + +CITY_OF_LONDON_COUNCIL_AND_PARL_DIFFERENT_DAYS = ( + RootBuilder() + .with_ballot(CityOfLondonLocalBallot("2025-03-20").build()) + .with_ballot(CityOfLondonParlBallot("2025-05-01").build()) +) +CITY_OF_LONDON_COUNCIL_AND_PARL_SAME_DAY = ( + RootBuilder() + .with_ballot(CityOfLondonLocalBallot("2025-03-20").build()) + .with_ballot(CityOfLondonParlBallot("2025-03-20").build()) +) + + __ALL__ = ("example_responses",) example_responses = { "AA1 1AA": { @@ -69,4 +118,12 @@ # "description": "Police and Crime Commissioner ballot", # "response": PCC_BALLOT, # }, + "AA1 1AL": { + "description": "City of London (Common Councilman) and UK Parl ballots on different upcoming dates", + "response": CITY_OF_LONDON_COUNCIL_AND_PARL_DIFFERENT_DAYS, + }, + "AA1 1AM": { + "description": "City of London (Common Councilman) and UK Parl ballots on the same date", + "response": CITY_OF_LONDON_COUNCIL_AND_PARL_SAME_DAY, + }, } diff --git a/postcode_lookup/template_sorter.py b/postcode_lookup/template_sorter.py index 6735ce9..eb8b64a 100644 --- a/postcode_lookup/template_sorter.py +++ b/postcode_lookup/template_sorter.py @@ -151,9 +151,12 @@ def context(self): context["can_register_vac"] = self.timetable.is_before( TimetableEvent.VAC_APPLICATION_DEADLINE ) - context["htag"] = "h2" + context["htag_primary"] = "h2" + context["htag_secondary"] = "h3" if self.response_type == ResponseTypes.MULTIPLE_DATES: - context["htag"] = "h3" + context["htag_primary"] = "h3" + context["htag_secondary"] = "h4" + context["toc_id"] = self.toc_id return context @property @@ -162,7 +165,34 @@ def toc_label(self): @property def toc_id(self): - return "voter-registration" + return f"voter-registration-{self.timetable.poll_date}-{self.timetable.registration_deadline}" + + +class CityOfLondonRegistrationDateSection(RegistrationDateSection): + template_name = "includes/registration_timetable_city_of_london.html" + + def __init__(self, *args, **kwargs) -> None: + self.with_headers = kwargs.pop("with_headers") + super().__init__(*args, **kwargs) + + @property + def weight(self): + parent_weight = super().weight + return 0 if parent_weight == 0 else parent_weight + 1 + + @property + def context(self): + context = super().context + context["with_headers"] = self.with_headers + return context + + @property + def toc_label(self): + return _("Voter registration") + + @property + def toc_id(self): + return f"voter-registration-col-{self.timetable.poll_date}-{self.timetable.registration_deadline}" class ElectionDateTemplateSorter: @@ -199,7 +229,7 @@ def __init__( self.polling_station_opening_times_str = _("7am – 10pm") if any( - "city-of-london" in ballot.ballot_paper_id + ballot.ballot_paper_id.startswith("local.city-of-london.") for ballot in self.date_data.ballots ): self.polling_station_opening_times_str = _("8am – 8pm") @@ -217,18 +247,50 @@ def __init__( "timetable": self.timetable, } - enabled_sections = [BallotSection] - if not self.all_cancelled: - enabled_sections.append(RegistrationDateSection) + enabled_sections = [BallotSection(**section_kwargs)] + + city_of_london_ballots = [ + b + for b in self.date_data.ballots + if not b.cancelled + and b.ballot_paper_id.startswith("local.city-of-london.") + ] + other_ballots = [ + b + for b in self.date_data.ballots + if not b.cancelled + and not b.ballot_paper_id.startswith("local.city-of-london.") + ] + if len(other_ballots) > 0: + enabled_sections.append( + RegistrationDateSection( + data=self.date_data, + mode=self.current_mode, + response_type=self.response_type, + current_date=self.current_date, + timetable=from_election_id( + other_ballots[0].election_id, country=country + ), + ) + ) + if len(city_of_london_ballots) > 0: + enabled_sections.append( + CityOfLondonRegistrationDateSection( + data=self.date_data, + mode=self.current_mode, + response_type=self.response_type, + current_date=self.current_date, + timetable=from_election_id( + city_of_london_ballots[0].election_id, country=country + ), + with_headers=len(other_ballots) == 0, + ) + ) if self.first_upcoming_date: - enabled_sections.append(PollingStationSection) + enabled_sections.append(PollingStationSection(**section_kwargs)) - self.sections = sorted( - [section(**section_kwargs) for section in enabled_sections], - key=lambda sec: sec.weight, - # reverse=True, - ) + self.sections = sorted(enabled_sections, key=lambda sec: sec.weight) class TemplateSorter: diff --git a/postcode_lookup/templates/includes/registration_timetable.html b/postcode_lookup/templates/includes/registration_timetable.html index 7e814dc..95cbaa8 100644 --- a/postcode_lookup/templates/includes/registration_timetable.html +++ b/postcode_lookup/templates/includes/registration_timetable.html @@ -1,11 +1,22 @@ -<{{ section.context.htag }} id="voter-registration">{% trans %}Voter registration{% endtrans %}{{ section.context.htag }}> +<{{ section.context.htag_primary }} id="{{toc_id}}">{% trans %}Voter registration{% endtrans %}{{ section.context.htag_primary }}> {% if section.context.can_register %} -
You have until {{ section.timetable.registration_deadline|date_filter }}
- + <{{ section.context.htag_secondary }}>{% trans %}Register to vote{% endtrans %}{{ section.context.htag_secondary }}> ++ {% trans date=section.timetable.registration_deadline|date_filter %} + You have until {{ date }} + {% endtrans %} +
++ + {% trans %}Register to vote{% endtrans %} + +
{% else %} - {% trans date=section.data.date|date_filter, registration_deadline=section.timetable.registration_deadline|date_filter %} + {% + trans + date=section.data.date|date_filter, + registration_deadline=section.timetable.registration_deadline|date_filter + %}The deadline to register to vote on {{ date }} was {{ registration_deadline }}.
If you registered to vote before the deadline, then you will be able to vote in this election. {% trans %}Voter registration{% endtrans %}{{ section.context.htag_primary }}>
+{% endif %}
+{% if section.context.can_register %}
+ {% if section.context.with_headers %}
+ <{{ section.context.htag_secondary }}>{% trans %}Register to vote{% endtrans %}{{ section.context.htag_secondary }}>
+ {% endif %}
+ {% trans %}City of London council elections do not use the same electoral register as other elections.{% endtrans %}
+ {% trans %}
+ Both residents and city workers are eligible to vote. There is one register
+ published annually, and the deadline to apply is 30 November each year.
+ The new register comes into force on 16 February each year,
+ and cannot be modified after that date.
+ {% endtrans %}
+
+
+ {% trans %}Register to vote in the City of London{% endtrans %}
+
+ {% trans %}City of London council elections do not use the same electoral register as other elections.{% endtrans %} The deadline to register to vote in City of London local elections on {{ date }}
+ was {{ registration_deadline }}. If you registered to vote before the deadline, then you will be able to vote in this election. Contact City of London to check if you are on the register. You can register to vote in the City of London in future elections.