Skip to content

Commit 54143dd

Browse files
authored
Merge pull request #125 from NHSDigital/PPHA-265-about-you-gender-page
PPHA-265: Add gender page
2 parents 8833d4e + ef15669 commit 54143dd

File tree

15 files changed

+312
-7
lines changed

15 files changed

+312
-7
lines changed

lung_cancer_screening/core/tests/acceptance/helpers/user_interaction_helpers.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,11 @@ def fill_in_and_submit_sex_at_birth(page, sex):
6060
page.get_by_label(sex, exact=True).check()
6161

6262
page.click("text=Continue")
63+
64+
def fill_in_and_submit_gender(page, gender):
65+
expect(page.locator("legend")).to_have_text(
66+
"Which of these best describes you?")
67+
68+
page.get_by_label(gender, exact=True).check()
69+
70+
page.click("text=Continue")

lung_cancer_screening/core/tests/acceptance/test_cannot_change_answers_after_submission.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
fill_in_and_submit_participant_id,
1111
fill_in_and_submit_smoking_eligibility,
1212
fill_in_and_submit_date_of_birth,
13-
fill_in_and_submit_sex_at_birth
13+
fill_in_and_submit_sex_at_birth,
14+
fill_in_and_submit_gender,
1415
)
1516

1617
class TestQuestionnaire(StaticLiveServerTestCase):
@@ -42,6 +43,7 @@ def test_cannot_change_responses_once_checked_and_submitted(self):
4243
fill_in_and_submit_height_metric(page, "170")
4344
fill_in_and_submit_weight_metric(page, "25.4")
4445
fill_in_and_submit_sex_at_birth(page, "Male")
46+
fill_in_and_submit_gender(page, "Male")
4547

4648
page.click("text=Submit")
4749

lung_cancer_screening/core/tests/acceptance/test_questionnaire.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
fill_in_and_submit_date_of_birth,
1313
fill_in_and_submit_weight_metric,
1414
fill_in_and_submit_weight_imperial,
15-
fill_in_and_submit_sex_at_birth
15+
fill_in_and_submit_sex_at_birth,
16+
fill_in_and_submit_gender
1617
)
1718

1819
from .helpers.assertion_helpers import expect_back_link_to_have_url
@@ -79,8 +80,12 @@ def test_full_questionnaire_user_journey(self):
7980
expect_back_link_to_have_url(page, "/weight")
8081
fill_in_and_submit_sex_at_birth(page, "Male")
8182

82-
expect(page).to_have_url(f"{self.live_server_url}/responses")
83+
expect(page).to_have_url(f"{self.live_server_url}/gender")
8384
expect_back_link_to_have_url(page, "/sex-at-birth")
85+
fill_in_and_submit_gender(page, "Male")
86+
87+
expect(page).to_have_url(f"{self.live_server_url}/responses")
88+
expect_back_link_to_have_url(page, "/gender")
8489

8590
responses = page.locator(".responses")
8691
expect(responses).to_contain_text("Have you ever smoked? Yes, I used to smoke regularly")
@@ -89,6 +94,7 @@ def test_full_questionnaire_user_journey(self):
8994
expect(responses).to_contain_text(f"What is your height? {feet} feet {inches} inches")
9095
expect(responses).to_contain_text(f"What is your weight? {weight_stone} stone {weight_pound} pound")
9196
expect(responses).to_contain_text("What was your sex at birth? Male")
97+
expect(responses).to_contain_text("Which of these best describes you? Male")
9298

9399
page.click("text=Submit")
94100

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from django import forms
2+
3+
from ...nhsuk_forms.typed_choice_field import TypedChoiceField
4+
from ..models.response_set import ResponseSet, GenderValues
5+
6+
class GenderForm(forms.ModelForm):
7+
8+
def __init__(self, *args, **kwargs):
9+
self.participant = kwargs.pop('participant')
10+
super().__init__(*args, **kwargs)
11+
self.instance.participant = self.participant
12+
13+
self.fields["gender"] = TypedChoiceField(
14+
choices=GenderValues.choices,
15+
widget=forms.RadioSelect,
16+
label="Which of these best describes you?",
17+
label_classes="nhsuk-fieldset__legend--m",
18+
hint="This information is used to find your NHS number and match with your GP record.",
19+
error_messages={
20+
'required': 'Select the option that best describes your gender.'
21+
}
22+
)
23+
24+
class Meta:
25+
model = ResponseSet
26+
fields = ['gender']
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{% extends 'layout.jinja' %}
2+
{% from 'nhsuk/components/button/macro.jinja' import button %}
3+
{% from 'nhsuk/components/back-link/macro.jinja' import backLink %}
4+
5+
{% block beforeContent %}
6+
{{
7+
backLink({
8+
"href": url("questions:sex_at_birth"),
9+
"text": "Back"
10+
})
11+
}}
12+
{% endblock beforeContent %}
13+
14+
{% block page_content %}
15+
<div class="nhsuk-grid-row">
16+
<div class="nhsuk-grid-column-two-thirds">
17+
<form action="{{ request.path }}" method="POST">
18+
{{ csrf_input }}
19+
20+
{{ form }}
21+
22+
{{ button({
23+
"text": "Continue"
24+
}) }}
25+
</form>
26+
</div>
27+
</div>
28+
{% endblock %}

lung_cancer_screening/questions/jinja2/responses.jinja

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
{% block beforeContent %}
66
{{
77
backLink({
8-
"href": url("questions:sex_at_birth"),
8+
"href": url("questions:gender"),
99
"text": "Back"
1010
})
1111
}}
@@ -21,6 +21,7 @@
2121
<li>What is your height? {{ response_set.formatted_height }}</li>
2222
<li>What is your weight? {{ response_set.formatted_weight }}</li>
2323
<li>What was your sex at birth? {{ response_set.get_sex_at_birth_display() }}</li>
24+
<li>Which of these best describes you? {{ response_set.get_gender_display() }}</li>
2425
</ul>
2526

2627
<form action="{{ request.path }}" method="post">
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 5.2.7 on 2025-10-31 10:56
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('questions', '0013_responseset_sex_at_birth'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='responseset',
15+
name='gender',
16+
field=models.CharField(blank=True, choices=[('M', 'Male'), ('F', 'Female'), ('N', 'Non-binary'), ('P', 'Prefer not to say'), ('G', 'How I describe myself may not match my GP record')], max_length=1, null=True),
17+
),
18+
]

lung_cancer_screening/questions/models/response_set.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ class SexAtBirthValues(models.TextChoices):
1818
FEMALE = "F", 'Female'
1919
MALE = "M", 'Male'
2020

21+
class GenderValues(models.TextChoices):
22+
FEMALE = "F", 'Female'
23+
MALE = "M", 'Male'
24+
NON_BINARY = "N", 'Non-binary'
25+
PREFER_NOT_TO_SAY = "P", 'Prefer not to say'
26+
GP = "G", 'How I describe myself may not match my GP record'
27+
2128
class ResponseSet(BaseModel):
2229
participant = models.ForeignKey(Participant, on_delete=models.CASCADE)
2330

@@ -66,6 +73,13 @@ class ResponseSet(BaseModel):
6673
blank=True
6774
)
6875

76+
gender = models.CharField(
77+
max_length=1,
78+
choices=GenderValues.choices,
79+
null=True,
80+
blank=True
81+
)
82+
6983
submitted_at = models.DateTimeField(null=True, blank=True)
7084

7185
class Meta:
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from django.test import TestCase
2+
3+
from ....models.response_set import GenderValues
4+
from ....models.participant import Participant
5+
from ....forms.gender_form import GenderForm
6+
7+
class TestGenderForm(TestCase):
8+
def setUp(self):
9+
self.participant = Participant.objects.create(unique_id="1234567890")
10+
11+
def test_is_valid_with_a_valid_value(self):
12+
form = GenderForm(
13+
participant=self.participant,
14+
data={
15+
"gender": GenderValues.MALE
16+
}
17+
)
18+
self.assertTrue(form.is_valid())
19+
self.assertEqual(
20+
form.cleaned_data["gender"],
21+
GenderValues.MALE.value
22+
)
23+
24+
def test_is_invalid_with_an_invalid_value(self):
25+
form = GenderForm(
26+
participant=self.participant,
27+
data={
28+
"gender": "invalid"
29+
}
30+
)
31+
self.assertFalse(form.is_valid())
32+
self.assertEqual(
33+
form.errors["gender"],
34+
["Select a valid choice. invalid is not one of the available choices."]
35+
)
36+
37+
def test_is_invalid_when_no_option_is_selected(self):
38+
form = GenderForm(
39+
participant=self.participant,
40+
data={
41+
"gender": None
42+
}
43+
)
44+
self.assertFalse(form.is_valid())
45+
self.assertEqual(
46+
form.errors["gender"],
47+
["Select the option that best describes your gender."]
48+
)

lung_cancer_screening/questions/tests/unit/models/test_response_set.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from django.core.exceptions import ValidationError
77

8-
from ....models.response_set import ResponseSet, HaveYouEverSmokedValues, SexAtBirthValues
8+
from ....models.response_set import ResponseSet, HaveYouEverSmokedValues, SexAtBirthValues, GenderValues
99
from ....models.participant import Participant
1010

1111
class TestResponseSet(TestCase):
@@ -100,6 +100,15 @@ def test_has_sex_at_birth_as_string(self):
100100
str
101101
)
102102

103+
def test_has_gender_as_string(self):
104+
self.response_set.gender = GenderValues.MALE
105+
self.response_set.save()
106+
107+
self.assertIsInstance(
108+
self.response_set.gender,
109+
str
110+
)
111+
103112
# VALIDATIONS
104113

105114
def test_is_invalid_if_another_unsubmitted_response_set_exists(self):
@@ -231,3 +240,22 @@ def test_is_invalid_if_sex_at_birth_is_not_an_accepted_value(self):
231240
context.exception.messages[0],
232241
"Value 'X' is not a valid choice."
233242
)
243+
244+
def test_is_valid_if_gender_is_null(self):
245+
self.response_set.gender = None
246+
self.response_set.save()
247+
248+
self.assertIsNone(
249+
self.response_set.gender
250+
)
251+
252+
def test_is_invalid_if_gender_is_not_an_accepted_value(self):
253+
self.response_set.gender = "X"
254+
255+
with self.assertRaises(ValidationError) as context:
256+
self.response_set.full_clean()
257+
258+
self.assertEqual(
259+
context.exception.messages[0],
260+
"Value 'X' is not a valid choice."
261+
)

0 commit comments

Comments
 (0)