Skip to content

Commit 214a0b0

Browse files
authored
Merge pull request #229 from NHSDigital/PPHA-483-education-signoff
Ppha 483 education signoff
2 parents 6e1d4ce + 102130e commit 214a0b0

17 files changed

Lines changed: 142 additions & 46 deletions

features/education.feature

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@ Feature: Education page
1717
Given I am logged in
1818
When I go to "/education"
1919
Then I see a back link to "/ethnicity"
20-
When I fill in and submit my education with "A-levels"
20+
When I check "A-levels"
21+
And I click "Continue"
2122
Then I am on "/respiratory-conditions"
2223

2324
Scenario: Checking responses and changing them
2425
Given I am logged in
2526
When I go to "/education"
26-
And I fill in and submit my education with "A-levels"
27+
And I check "A-levels"
28+
And I check "GCSEs"
29+
And I click "Continue"
2730
When I go to "/check-your-answers"
28-
Then I see "A-levels" as a response to "Highest level of education" under "About you"
31+
Then I see "GCSEs and A-levels" as a response to "Highest level of education" under "About you"
2932
And I see "/education?change=True" as a link to change "Highest level of education" under "About you"
3033
When I click the link to change "Highest level of education" under "About you"
3134
Then I am on "/education?change=True"

features/steps/form_steps.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ def then_i_see_a_date_x_years_ago_filled_in(context, years):
120120
assert context.page.get_by_label('Month').input_value() == str(date_of_birth.month)
121121
assert context.page.get_by_label('Year').input_value() == str(date_of_birth.year)
122122

123+
123124
@then(u'I see "{value}" filled in for "{label}"')
124125
def then_i_see_value_filled_in_for_label(context, value, label):
125126
assert context.page.get_by_label(label, exact=True).input_value() == value
127+
128+
129+
@when('I check "{label}"')
130+
def when_i_check_label(context, label):
131+
context.page.get_by_label(label, exact=True).check()

lung_cancer_screening/questions/forms/education_form.py

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from django import forms
22

3-
from ...nhsuk_forms.choice_field import ChoiceField
3+
from ...nhsuk_forms.choice_field import MultipleChoiceField
44
from ..models.education_response import EducationResponse, EducationValues
55

66

@@ -9,17 +9,42 @@ class EducationForm(forms.ModelForm):
99
def __init__(self, *args, **kwargs):
1010
super().__init__(*args, **kwargs)
1111

12-
self.fields["value"] = ChoiceField(
12+
self.fields["value"] = MultipleChoiceField(
1313
choices=EducationValues.choices,
1414
label="What level of education have you completed?",
15-
widget=forms.RadioSelect,
15+
widget=forms.CheckboxSelectMultiple,
1616
label_classes="nhsuk-fieldset__legend--m",
17-
hint=(
18-
"Select all that apply"
19-
),
17+
hint="Select all that apply",
2018
error_messages={
21-
'required': 'Select your level of education'
22-
}
19+
"required": "Select your level of education",
20+
"singleton_option": (
21+
"Select your level of education, or select "
22+
"'Prefer not to say'"
23+
),
24+
},
25+
)
26+
27+
# Add hints for each choice
28+
education_field = self["value"]
29+
education_field.add_hint_for_choice(
30+
EducationValues.GCSES,
31+
"Previously O-levels",
32+
)
33+
education_field.add_hint_for_choice(
34+
EducationValues.A_LEVELS,
35+
"Previously Higher School Certificate (HSC)",
36+
)
37+
education_field.add_hint_for_choice(
38+
EducationValues.FURTHER_EDUCATION,
39+
"For example, apprenticeships or Higher National Certificates (HNC)",
40+
)
41+
education_field.add_hint_for_choice(
42+
EducationValues.BACHELORS_DEGREE,
43+
"A university degree, also known as an undergraduate degree",
44+
)
45+
education_field.add_hint_for_choice(
46+
EducationValues.POSTGRADUATE_DEGREE,
47+
"For example, a Masters or PhD",
2348
)
2449

2550
self["value"].add_divider_after(

lung_cancer_screening/questions/migrations/0020_responseset_user_and_more.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@ class Migration(migrations.Migration):
2828
migrations.AlterField(
2929
model_name='responseset',
3030
name='respiratory_conditions',
31-
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('P', 'Pneumonia'), ('E', 'Emphysema'), ('B', 'Bronchitis'), ('T', 'Tuberculosis (TB)'), ('C', 'Chronic obstructive pulmonary disease (COPD)'), ('N', 'No, I have not had any of these respiratory conditions')], max_length=1), blank=True, null=True, size=None, validators=[lung_cancer_screening.questions.models.respiratory_conditions_response.validate_singleton_option]),
31+
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('P', 'Pneumonia'), ('E', 'Emphysema'), ('B', 'Bronchitis'), ('T', 'Tuberculosis (TB)'), ('C', 'Chronic obstructive pulmonary disease (COPD)'), ('N', 'No, I have not had any of these respiratory conditions')], max_length=1), blank=True, null=True, size=None, validators=[lung_cancer_screening.questions.models.validators.singleton_option.validate_singleton_option]),
3232
),
3333
]

lung_cancer_screening/questions/migrations/0031_respiratoryconditionsresponse.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class Migration(migrations.Migration):
4040
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
4141
('created_at', models.DateTimeField(auto_now_add=True)),
4242
('updated_at', models.DateTimeField(auto_now=True)),
43-
('value', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('P', 'Pneumonia'), ('E', 'Emphysema'), ('B', 'Bronchitis'), ('T', 'Tuberculosis (TB)'), ('C', 'Chronic obstructive pulmonary disease (COPD)'), ('N', 'No, I have not had any of these respiratory conditions')], max_length=1), size=None, validators=[lung_cancer_screening.questions.models.respiratory_conditions_response.validate_singleton_option])),
43+
('value', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('P', 'Pneumonia'), ('E', 'Emphysema'), ('B', 'Bronchitis'), ('T', 'Tuberculosis (TB)'), ('C', 'Chronic obstructive pulmonary disease (COPD)'), ('N', 'No, I have not had any of these respiratory conditions')], max_length=1), size=None, validators=[lung_cancer_screening.questions.models.validators.singleton_option.validate_singleton_option])),
4444
('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='respiratory_conditions_response', to='questions.responseset')),
4545
],
4646
options={
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 5.2.10 on 2026-01-15 16:41
2+
3+
import django.contrib.postgres.fields
4+
import lung_cancer_screening.questions.models.validators.singleton_option
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('questions', '0040_educationresponse'),
12+
]
13+
14+
operations = [
15+
migrations.AlterField(
16+
model_name='educationresponse',
17+
name='value',
18+
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('X', 'I finished school before the age of 15'), ('G', 'GCSEs'), ('A', 'A-levels'), ('B', "Bachelor's degree"), ('P', 'Postgraduate degree'), ('N', "I'd prefer not to say")], max_length=1), size=None, validators=[lung_cancer_screening.questions.models.validators.singleton_option.validate_singleton_option]),
19+
),
20+
]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 5.2.10 on 2026-01-19 10:47
2+
3+
import django.contrib.postgres.fields
4+
import lung_cancer_screening.questions.models.validators.singleton_option
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('questions', '0041_alter_educationresponse_value'),
12+
]
13+
14+
operations = [
15+
migrations.AlterField(
16+
model_name='educationresponse',
17+
name='value',
18+
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('X', 'I finished school before the age of 15'), ('G', 'GCSEs'), ('A', 'A-levels'), ('F', 'Further education'), ('B', "Bachelor's degree"), ('P', 'Postgraduate degree'), ('N', 'Prefer not to say')], max_length=1), size=None, validators=[lung_cancer_screening.questions.models.validators.singleton_option.validate_singleton_option]),
19+
),
20+
]
Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from django.db import models
2+
from django.contrib.postgres.fields import ArrayField
23

4+
from .validators.singleton_option import validate_singleton_option
35
from .base import BaseModel
46
from .response_set import ResponseSet
57

@@ -8,11 +10,17 @@ class EducationValues(models.TextChoices):
810
FINISHED_SCHOOL_BEFORE_15 = "X", "I finished school before the age of 15"
911
GCSES = "G", "GCSEs"
1012
A_LEVELS = "A", "A-levels"
13+
FURTHER_EDUCATION = "F", "Further education"
1114
BACHELORS_DEGREE = "B", "Bachelor's degree"
1215
POSTGRADUATE_DEGREE = "P", "Postgraduate degree"
13-
PREFER_NOT_TO_SAY = "N", "I'd prefer not to say"
16+
PREFER_NOT_TO_SAY = "N", "Prefer not to say"
1417

1518

1619
class EducationResponse(BaseModel):
1720
response_set = models.OneToOneField(ResponseSet, on_delete=models.CASCADE, related_name='education_response')
18-
value = models.CharField(max_length=1, choices=EducationValues.choices)
21+
value = ArrayField(
22+
models.CharField(max_length=1, choices=EducationValues.choices),
23+
validators=[
24+
validate_singleton_option
25+
]
26+
)
Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from django.db import models
22
from django.contrib.postgres.fields import ArrayField
3-
from django.core.exceptions import ValidationError
43

4+
from .validators.singleton_option import validate_singleton_option
55
from .base import BaseModel
66
from .response_set import ResponseSet
77

@@ -15,17 +15,11 @@ class RespiratoryConditionValues(models.TextChoices):
1515
NONE = "N", "No, I have not had any of these respiratory conditions"
1616

1717

18-
def validate_singleton_option(value):
19-
if value and "N" in value and len(value) > 1:
20-
raise ValidationError(
21-
"Cannot have singleton value and other values selected",
22-
code="singleton_option",
23-
)
24-
25-
2618
class RespiratoryConditionsResponse(BaseModel):
2719
response_set = models.OneToOneField(ResponseSet, on_delete=models.CASCADE, related_name='respiratory_conditions_response')
2820
value = ArrayField(
2921
models.CharField(max_length=1, choices=RespiratoryConditionValues.choices),
30-
validators=[validate_singleton_option]
22+
validators=[
23+
validate_singleton_option
24+
]
3125
)

lung_cancer_screening/questions/models/validators/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)