Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

City of London registration info #108

Merged
merged 7 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions postcode_lookup/mock_responses.py
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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": {
Expand Down Expand Up @@ -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,
},
}
88 changes: 75 additions & 13 deletions postcode_lookup/template_sorter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Comment on lines -154 to +158
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a little issue I fixed while I was here.
Previously if htag was h3 we'd render the next heading as a h3 too

context["toc_id"] = self.toc_id
return context

@property
Expand All @@ -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")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically: If we're showing 2 "register to vote" CTAs, we need a way to say "don't show the headers on the second one"

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:
Expand Down Expand Up @@ -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")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declaring this out of scope for this PR 🏃

Expand All @@ -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)]
Copy link
Member Author

@chris48s chris48s Oct 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously we were making a list of classes and then constructing them all into objects at the end with the same kwargs.

I've switched to constructing objects as I go. This allows me to pass in custom timetable into each registration section instead of having on global one.


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:
Expand Down
23 changes: 17 additions & 6 deletions postcode_lookup/templates/includes/registration_timetable.html
Original file line number Diff line number Diff line change
@@ -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 %}
<h3>Register to vote</h3>
<p>You have until {{ section.timetable.registration_deadline|date_filter }}</p>
<p><a href="https://gov.uk/register-to-vote/" class="o-button o-button--primary o-button--dark o-external-link">
Register to vote</a></p>
<{{ section.context.htag_secondary }}>{% trans %}Register to vote{% endtrans %}</{{ section.context.htag_secondary }}>
<p>
{% trans date=section.timetable.registration_deadline|date_filter %}
You have until {{ date }}
{% endtrans %}
</p>
<p>
<a href="https://gov.uk/register-to-vote/" class="o-button o-button--primary o-button--dark o-external-link">
{% trans %}Register to vote{% endtrans %}
</a>
</p>
{% 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
%}
<p>The deadline to register to vote on {{ date }}
was {{ registration_deadline }}.</p>
<p>If you registered to vote before the deadline, then you will be able to vote in this election. <a
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{% if section.context.with_headers %}
<{{ section.context.htag_primary }} id="{{toc_id}}">{% 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 %}
<p>{% trans %}City of London council elections do not use the same electoral register as other elections.{% endtrans %}</p>
<p>
{% 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 %}
</p>
<p>
<a href="https://www.speakforthecity.com/" class="o-button o-button--primary o-button--dark o-external-link">
{% trans %}Register to vote in the City of London{% endtrans %}
</a>
</p>
{% else %}
<{{ section.context.htag_secondary }} id="{{toc_id}}">{% trans %}Registration in the City of London{% endtrans %}</{{ section.context.htag_secondary }}>
<p>{% trans %}City of London council elections do not use the same electoral register as other elections.{% endtrans %}</p>
{%
trans
date=section.data.date|date_filter,
registration_deadline=section.timetable.registration_deadline|date_filter
%}
<p>The deadline to register to vote in City of London local elections on {{ date }}
was {{ registration_deadline }}.</p>
<p>If you registered to vote before the deadline, then you will be able to vote in this election. <a
href="#electoral_services">Contact City of London</a> to check if you are on the register.</p>
<p>You can <a href="https://www.speakforthecity.com/">register to vote in the City of London</a> in future elections.</p>
{% endtrans %}
{% endif %}
50 changes: 50 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
import uvicorn
from app import app
from starlette.testclient import TestClient
from template_sorter import (
ApiModes,
ElectionDateTemplateSorter,
TemplateSorter,
)
from uk_election_timetables.calendars import Country
from uk_election_timetables.election_ids import from_election_id


@pytest.fixture(scope="function")
Expand Down Expand Up @@ -33,3 +40,46 @@ def uvicorn_server():
time.sleep(0.3)
yield f"http://localhost:{port}"
proc.kill()


@pytest.fixture
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I am just moving these fixtures into conftest.py so we can use them in multiple files.

def template_sorter():
def get_template_sorter(mock_response, date):
api_response = mock_response
# The dates exist in the api_response, but not
# in the format that matches the RootBuilder:
# dates = api_response._values["dates"] vs
# dates = api_response.dates so I've set it here.
api_response.dates = api_response._values["dates"]
mode = ApiModes.UPCOMING_ELECTIONS

sorter = TemplateSorter(
api_response=api_response, mode=mode, current_date=date
)
sorter.country = Country.ENGLAND
sorter.dates = api_response.dates

return sorter

return get_template_sorter


@pytest.fixture
def election_date_template_sorter():
def get_election_date_template_sorter(template_sorter, date):
election_date_sorter = ElectionDateTemplateSorter(
date_data=date,
country=template_sorter.country,
current_date=template_sorter.current_date,
response_type=template_sorter.response_type,
)
election_date_sorter.current_date = template_sorter.current_date

election_date_sorter.timetable = from_election_id(
template_sorter.dates[0].ballots[0].election_id,
country=template_sorter.country,
)

return election_date_sorter

return get_election_date_template_sorter
Loading
Loading