Skip to content

Commit 81b0dbe

Browse files
committed
Add asbestos exposure form
1 parent fd1201f commit 81b0dbe

File tree

11 files changed

+308
-4
lines changed

11 files changed

+308
-4
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from django import forms
2+
from ...nhsuk_forms.choice_field import ChoiceField
3+
from ..models.response_set import ResponseSet, AsbestosExposureValues
4+
5+
6+
class AsbestosExposureForm(forms.ModelForm):
7+
def __init__(self, *args, **kwargs):
8+
self.participant = kwargs.pop('participant')
9+
super().__init__(*args, **kwargs)
10+
self.instance.participant = self.participant
11+
12+
self.fields["asbestos_exposure"] = ChoiceField(
13+
choices=AsbestosExposureValues.choices,
14+
widget=forms.RadioSelect,
15+
label="Have you ever worked in a job where you might have been exposed to asbestos?",
16+
label_classes="nhsuk-fieldset__legend--m",
17+
error_messages={
18+
'required': 'Select if you have been exposed to asbestos.'
19+
}
20+
)
21+
22+
class Meta:
23+
model = ResponseSet
24+
fields = ['asbestos_exposure']
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{% extends 'layout.jinja' %}
2+
{% from 'nhsuk/components/button/macro.jinja' import button %}
3+
{% from 'nhsuk/components/back-link/macro.jinja' import backLink %}
4+
{% from 'nhsuk/components/details/macro.jinja' import details %}
5+
6+
{% block beforeContent %}
7+
{{
8+
backLink({
9+
"href": url("questions:ethnicity"),
10+
"text": "Back"
11+
})
12+
}}
13+
{% endblock beforeContent %}
14+
15+
{% block page_content %}
16+
<div class="nhsuk-grid-row">
17+
<div class="nhsuk-grid-column-two-thirds">
18+
<h1 class="nhsuk-heading-l">Tell us if you might have been exposed to asbestos at work</h1>
19+
20+
<p>You may have been exposed to asbestos if you worked in an industry such as building or construction, particularly from the 1950s to the 1990s.</p>
21+
22+
<p>You could be exposed to asbestos today if your job involves working in certain roles in old buildings.</p>
23+
24+
<p>Examples include:</p>
25+
<ul>
26+
<li>heating and ventilation engineers</li>
27+
<li>demolition workers</li>
28+
<li>plumbers</li>
29+
<li>construction workers</li>
30+
<li>electricians</li>
31+
</ul>
32+
33+
<p>You may have come into contact with asbestos from existing asbestos-containing materials in buildings and products. If they are intact or undamaged, they pose very little risk.</p>
34+
35+
{{
36+
details({
37+
"text": "What is asbestos?",
38+
"HTML": "
39+
<p>Asbestos was used in a number of building materials and products. For example:</p>
40+
<ul>
41+
<li>boilers and pipes</li>
42+
<li>car brakes</li>
43+
<li>cement for roofing sheets</li>
44+
<li>floor tiles</li>
45+
<li>insulating board to protect buildings and ships against fire</li>
46+
</ul>
47+
<p>If you worked in an industry such as building or construction you are more likely to have come into contact with damaged asbestos materials and products.</p>
48+
"
49+
})
50+
}}
51+
52+
<form action="{{ request.path }}" method="POST">
53+
{{ csrf_input }}
54+
55+
{{ form }}
56+
57+
{{ button({
58+
"text": "Continue"
59+
}) }}
60+
</form>
61+
</div>
62+
</div>
63+
{% 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:ethnicity"),
8+
"href": url("questions:asbestos_exposure"),
99
"text": "Back"
1010
})
1111
}}
@@ -23,6 +23,7 @@
2323
<li>What was your sex at birth? {{ response_set.get_sex_at_birth_display() }}</li>
2424
<li>Which of these best describes you? {{ response_set.get_gender_display() }}</li>
2525
<li>What is your ethnic background? {{ response_set.get_ethnicity_display() }}</li>
26+
<li>Have you ever worked in a job where you might have been exposed to asbestos? {{ response_set.get_asbestos_exposure_display() }}</li>
2627
</ul>
2728

2829
<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.8 on 2025-11-12 08:26
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('questions', '0015_responseset_ethnicity_alter_responseset_gender'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='responseset',
15+
name='asbestos_exposure',
16+
field=models.CharField(blank=True, choices=[('Y', 'Yes'), ('N', 'No')], max_length=1, null=True),
17+
),
18+
]

lung_cancer_screening/questions/models/response_set.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ class EthnicityValues(models.TextChoices):
3333
OTHER = "O", "Other ethnic group"
3434
PREFER_NOT_TO_SAY = "N", "I'd prefer not to say"
3535

36+
class AsbestosExposureValues(models.TextChoices):
37+
YES = "Y", "Yes"
38+
NO = "N", "No"
39+
3640
class ResponseSet(BaseModel):
3741
participant = models.ForeignKey(Participant, on_delete=models.CASCADE)
3842

@@ -95,6 +99,13 @@ class ResponseSet(BaseModel):
9599
blank=True
96100
)
97101

102+
asbestos_exposure = models.CharField(
103+
max_length=1,
104+
choices=AsbestosExposureValues.choices,
105+
null=True,
106+
blank=True
107+
)
108+
98109
submitted_at = models.DateTimeField(null=True, blank=True)
99110

100111
class Meta:
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from django.test import TestCase
2+
3+
from ....models.response_set import AsbestosExposureValues
4+
from ....models.participant import Participant
5+
from ....forms.asbestos_exposure_form import AsbestosExposureForm
6+
7+
8+
class TestAsbestosExposureForm(TestCase):
9+
def setUp(self):
10+
self.participant = Participant.objects.create(unique_id="1234567890")
11+
12+
def test_is_valid_with_yes(self):
13+
form = AsbestosExposureForm(
14+
participant=self.participant,
15+
data={
16+
"asbestos_exposure": AsbestosExposureValues.YES
17+
}
18+
)
19+
self.assertTrue(form.is_valid())
20+
self.assertEqual(
21+
form.cleaned_data["asbestos_exposure"],
22+
AsbestosExposureValues.YES.value
23+
)
24+
25+
def test_is_valid_with_no(self):
26+
form = AsbestosExposureForm(
27+
participant=self.participant,
28+
data={
29+
"asbestos_exposure": AsbestosExposureValues.NO
30+
}
31+
)
32+
self.assertTrue(form.is_valid())
33+
self.assertEqual(
34+
form.cleaned_data["asbestos_exposure"],
35+
AsbestosExposureValues.NO.value
36+
)
37+
38+
def test_is_invalid_with_an_invalid_value(self):
39+
form = AsbestosExposureForm(
40+
participant=self.participant,
41+
data={
42+
"asbestos_exposure": "invalid"
43+
}
44+
)
45+
self.assertFalse(form.is_valid())
46+
self.assertEqual(
47+
form.errors["asbestos_exposure"],
48+
["Select a valid choice. invalid is not one of the available choices."]
49+
)
50+
51+
def test_is_invalid_when_no_option_is_selected(self):
52+
form = AsbestosExposureForm(
53+
participant=self.participant,
54+
data={
55+
"asbestos_exposure": None
56+
}
57+
)
58+
self.assertFalse(form.is_valid())
59+
self.assertEqual(
60+
form.errors["asbestos_exposure"],
61+
["Select if you have been exposed to asbestos."]
62+
)
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
from django.test import TestCase
2+
from django.urls import reverse
3+
4+
from lung_cancer_screening.questions.models.participant import Participant
5+
from lung_cancer_screening.questions.models.response_set import AsbestosExposureValues
6+
7+
8+
class TestAsbestosExposure(TestCase):
9+
def setUp(self):
10+
self.participant = Participant.objects.create(unique_id="12345")
11+
self.participant.responseset_set.create()
12+
self.valid_params = {"asbestos_exposure": AsbestosExposureValues.YES}
13+
14+
session = self.client.session
15+
session['participant_id'] = self.participant.unique_id
16+
session.save()
17+
18+
def test_get_redirects_if_the_participant_does_not_exist(self):
19+
session = self.client.session
20+
session['participant_id'] = "somebody none existant participant"
21+
session.save()
22+
23+
response = self.client.get(
24+
reverse("questions:asbestos_exposure")
25+
)
26+
27+
self.assertRedirects(response, reverse("questions:start"))
28+
29+
def test_get_responds_successfully(self):
30+
response = self.client.get(reverse("questions:asbestos_exposure"))
31+
self.assertEqual(response.status_code, 200)
32+
33+
def test_get_contains_the_correct_form_fields(self):
34+
response = self.client.get(reverse("questions:asbestos_exposure"))
35+
self.assertContains(response, "Have you ever worked in a job where you might have been exposed to asbestos?")
36+
37+
def test_post_redirects_if_the_participant_does_not_exist(self):
38+
session = self.client.session
39+
session['participant_id'] = "somebody none existant participant"
40+
session.save()
41+
42+
response = self.client.post(
43+
reverse("questions:asbestos_exposure"),
44+
self.valid_params
45+
)
46+
47+
self.assertRedirects(response, reverse("questions:start"))
48+
49+
def test_post_stores_a_valid_response_for_the_participant(self):
50+
self.client.post(
51+
reverse("questions:asbestos_exposure"),
52+
self.valid_params
53+
)
54+
55+
response_set = self.participant.responseset_set.first()
56+
self.assertEqual(
57+
response_set.asbestos_exposure,
58+
self.valid_params["asbestos_exposure"]
59+
)
60+
self.assertEqual(response_set.participant, self.participant)
61+
62+
def test_post_redirects_to_the_next_page(self):
63+
response = self.client.post(
64+
reverse("questions:asbestos_exposure"),
65+
self.valid_params
66+
)
67+
68+
# Assuming it redirects to the next question page - adjust as needed
69+
self.assertEqual(response.status_code, 302)
70+
71+
def test_post_responds_with_422_if_the_response_fails_to_create(self):
72+
response = self.client.post(
73+
reverse("questions:asbestos_exposure"),
74+
{"asbestos_exposure": "something not in list"}
75+
)
76+
77+
self.assertEqual(response.status_code, 422)
78+
79+
def test_post_does_not_update_response_set_on_invalid_data(self):
80+
self.client.post(
81+
reverse("questions:asbestos_exposure"),
82+
{"asbestos_exposure": "invalid"}
83+
)
84+
85+
self.assertEqual(
86+
self.participant.responseset_set.first().asbestos_exposure,
87+
None
88+
)

lung_cancer_screening/questions/tests/unit/views/test_ethnicity.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,13 @@ def test_post_sets_the_participant_id_in_session(self):
7070

7171
self.assertEqual(self.client.session["participant_id"], "12345")
7272

73-
def test_post_redirects_to_the_responses_path(self):
73+
def test_post_redirects_to_the_asbestos_exposure_path(self):
7474
response = self.client.post(
7575
reverse("questions:ethnicity"),
7676
self.valid_params
7777
)
7878

79-
self.assertRedirects(response, reverse("questions:responses"))
79+
self.assertRedirects(response, reverse("questions:asbestos_exposure"))
8080

8181
def test_post_responds_with_422_if_the_date_response_fails_to_create(self):
8282
response = self.client.post(

lung_cancer_screening/questions/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from .views.sex_at_birth import sex_at_birth
2828
from .views.gender import gender
2929
from .views.ethnicity import ethnicity
30+
from .views.asbestos_exposure import asbestos_exposure
3031

3132
urlpatterns = [
3233
path('start', start, name='start'),
@@ -37,6 +38,7 @@
3738
path('sex-at-birth', sex_at_birth, name='sex_at_birth'),
3839
path('gender', gender, name='gender'),
3940
path('ethnicity', ethnicity, name='ethnicity'),
41+
path('asbestos-exposure', asbestos_exposure, name='asbestos_exposure'),
4042
path('responses', responses, name='responses'),
4143
path('age-range-exit', age_range_exit, name='age_range_exit'),
4244
path('non-smoker-exit', non_smoker_exit, name='non_smoker_exit'),
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from django.shortcuts import render, redirect
2+
from django.urls import reverse
3+
from django.views.decorators.http import require_http_methods
4+
5+
from .decorators.participant_decorators import require_participant
6+
from ..forms.asbestos_exposure_form import AsbestosExposureForm
7+
8+
9+
@require_http_methods(["GET", "POST"])
10+
@require_participant
11+
def asbestos_exposure(request):
12+
if request.method == "POST":
13+
form = AsbestosExposureForm(
14+
participant=request.participant,
15+
data=request.POST
16+
)
17+
18+
if form.is_valid():
19+
response_set = request.participant.responseset_set.last()
20+
response_set.asbestos_exposure = form.cleaned_data["asbestos_exposure"]
21+
response_set.save()
22+
return redirect(reverse("questions:responses"))
23+
else:
24+
return render(
25+
request,
26+
"asbestos_exposure.jinja",
27+
{"form": form},
28+
status=422
29+
)
30+
31+
return render(
32+
request,
33+
"asbestos_exposure.jinja",
34+
{"form": AsbestosExposureForm(participant=request.participant)}
35+
)

0 commit comments

Comments
 (0)