Skip to content

Commit 19150f4

Browse files
authored
Merge pull request #523 from NHSDigital/DTOSS-11211-fix-area-details
[DTOSS-11211] fix area details
2 parents ea94878 + d4b3396 commit 19150f4

File tree

16 files changed

+443
-187
lines changed

16 files changed

+443
-187
lines changed

.github/workflows/stage-2-test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ jobs:
226226
run: make dependencies
227227

228228
- name: Install Playwright browsers
229-
run: poetry run playwright install chromium --with-deps
229+
run: poetry run playwright install chromium --with-deps --only-shell
230230

231231
- name: 'Run Playwright system tests'
232232
run: make test-ui

manage_breast_screening/mammograms/forms/symptom_forms.py

Lines changed: 108 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
from dataclasses import dataclass
21
from datetime import date
3-
from typing import Any
42

53
from django.db.models import TextChoices
6-
from django.forms import CheckboxSelectMultiple, Form, ValidationError
4+
from django.forms import CheckboxSelectMultiple
75
from django.forms.widgets import Textarea
86

97
from manage_breast_screening.core.services.auditor import Auditor
@@ -17,6 +15,7 @@
1715
MultipleChoiceField,
1816
RadioSelectWithoutFieldset,
1917
)
18+
from manage_breast_screening.nhsuk_forms.forms import FormWithConditionalFields
2019
from manage_breast_screening.nhsuk_forms.utils import YesNo, yes_no, yes_no_field
2120
from manage_breast_screening.participants.models.symptom import (
2221
NippleChangeChoices,
@@ -97,7 +96,11 @@ def area_radios(symptom_name="symptom"):
9796
)
9897

9998
@staticmethod
100-
def area_description(symptom_name="symptom", hint="For example, the left armpit"):
99+
def area_description(
100+
symptom_name="symptom",
101+
hint="For example, the left armpit",
102+
visually_hidden_label_suffix=None,
103+
):
101104
return CharField(
102105
required=False,
103106
label="Describe the specific area",
@@ -106,24 +109,18 @@ def area_description(symptom_name="symptom", hint="For example, the left armpit"
106109
"required": f"Describe the specific area where the {symptom_name} is located"
107110
},
108111
classes="nhsuk-u-width-two-thirds",
112+
visually_hidden_label_suffix=visually_hidden_label_suffix,
109113
)
110114

111115

112-
class SymptomForm(Form):
116+
class SymptomForm(FormWithConditionalFields):
113117
"""
114118
A base form class for entering symptoms. To be overriden for different symptom types.
115119
"""
116120

117-
@dataclass
118-
class ConditionalRequirement:
119-
conditionally_required_field: str
120-
predicate_field: str
121-
predicate_field_value: Any
122-
123121
def __init__(self, symptom_type, instance=None, **kwargs):
124122
self.instance = instance
125123
self.symptom_type = symptom_type
126-
self.conditional_requirements = []
127124

128125
if instance:
129126
kwargs["initial"] = self.initial_values(instance)
@@ -136,7 +133,7 @@ def initial_values(self, instance):
136133
"""
137134
return {
138135
"area": instance.area,
139-
"area_description": instance.area_description,
136+
f"area_description_{instance.area.lower()}": instance.area_description,
140137
"symptom_sub_type": instance.symptom_sub_type_id,
141138
"symptom_sub_type_details": instance.symptom_sub_type_details,
142139
"when_started": instance.when_started,
@@ -162,8 +159,14 @@ def model_values(self):
162159
area = one
163160
case _:
164161
area = self.cleaned_data["area"]
162+
area_description_field_name = f"area_description_{area.lower()}"
163+
area_description = self.cleaned_data.get(
164+
area_description_field_name, ""
165+
)
166+
167+
area_description_field_name = f"area_description_{area.lower()}"
168+
area_description = self.cleaned_data.get(area_description_field_name, "")
165169

166-
area_description = self.cleaned_data.get("area_description", "")
167170
symptom_sub_type = self.cleaned_data.get("symptom_sub_type")
168171
symptom_sub_type_details = self.cleaned_data.get("symptom_sub_type_details", "")
169172
when_started = self.cleaned_data.get("when_started")
@@ -191,53 +194,6 @@ def model_values(self):
191194
additional_information=additional_information,
192195
)
193196

194-
def set_conditionally_required(
195-
self, conditionally_required_field, predicate_field, predicate_field_value
196-
):
197-
"""
198-
Mark a field as conditionally required if and only if another field (the predicate field)
199-
is set to a specific value.
200-
If the predicate field is set to the predicate value, this field will require a value.
201-
If the predicate field is set to a different value, this field's value will be ignored.
202-
"""
203-
if conditionally_required_field not in self.fields:
204-
raise ValueError(f"{conditionally_required_field} is not a valid field")
205-
if predicate_field not in self.fields:
206-
raise ValueError(f"{predicate_field} is not a valid field")
207-
208-
self.conditional_requirements.append(
209-
self.ConditionalRequirement(
210-
conditionally_required_field=conditionally_required_field,
211-
predicate_field=predicate_field,
212-
predicate_field_value=predicate_field_value,
213-
)
214-
)
215-
216-
self.fields[conditionally_required_field].required = False
217-
218-
def clean(self):
219-
for requirement in self.conditional_requirements:
220-
field = requirement.conditionally_required_field
221-
222-
if (
223-
self.cleaned_data.get(requirement.predicate_field)
224-
== requirement.predicate_field_value
225-
):
226-
cleaned_value = self.cleaned_data.get(field)
227-
if isinstance(cleaned_value, str):
228-
cleaned_value = cleaned_value.strip()
229-
230-
if not cleaned_value:
231-
self.add_error(
232-
field,
233-
ValidationError(
234-
message=self.fields[field].error_messages["required"],
235-
code="required",
236-
),
237-
)
238-
else:
239-
del self.cleaned_data[field]
240-
241197
def update(self, request):
242198
auditor = Auditor.from_request(request)
243199
field_values = self.model_values()
@@ -273,7 +229,15 @@ def create(self, appointment, request):
273229

274230
class LumpForm(SymptomForm):
275231
area = CommonFields.area_radios(symptom_name="lump")
276-
area_description = CommonFields.area_description(symptom_name="lump")
232+
area_description_right_breast = CommonFields.area_description(
233+
"lump", visually_hidden_label_suffix="right breast"
234+
)
235+
area_description_left_breast = CommonFields.area_description(
236+
"lump", visually_hidden_label_suffix="left breast"
237+
)
238+
area_description_other = CommonFields.area_description(
239+
"lump", visually_hidden_label_suffix="other"
240+
)
277241
when_started = CommonFields.when_started
278242
specific_date = CommonFields.specific_date
279243
intermittent = CommonFields.intermittent
@@ -286,32 +250,29 @@ class LumpForm(SymptomForm):
286250
def __init__(self, instance=None, **kwargs):
287251
super().__init__(symptom_type=SymptomType.LUMP, instance=instance, **kwargs)
288252

289-
self.set_conditionally_required(
290-
conditionally_required_field="area_description",
291-
predicate_field="area",
292-
predicate_field_value=RightLeftOtherChoices.OTHER,
293-
)
294-
self.set_conditionally_required(
295-
conditionally_required_field="specific_date",
296-
predicate_field="when_started",
297-
predicate_field_value=RelativeDateChoices.SINCE_A_SPECIFIC_DATE,
298-
)
299-
self.set_conditionally_required(
300-
conditionally_required_field="when_resolved",
301-
predicate_field="recently_resolved",
302-
predicate_field_value=True,
303-
)
304-
self.set_conditionally_required(
305-
conditionally_required_field="investigation_details",
306-
predicate_field="investigated",
307-
predicate_field_value=YesNo.YES,
253+
self.given_field("area").require_field_with_prefix("area_description")
254+
255+
self.given_field_value(
256+
"when_started", RelativeDateChoices.SINCE_A_SPECIFIC_DATE
257+
).require_field("specific_date")
258+
259+
self.given_field_value("recently_resolved", True).require_field("when_resolved")
260+
261+
self.given_field_value("investigated", YesNo.YES).require_field(
262+
"investigation_details"
308263
)
309264

310265

311266
class SwellingOrShapeChangeForm(SymptomForm):
312267
area = CommonFields.area_radios(symptom_name="swelling or shape change")
313-
area_description = CommonFields.area_description(
314-
symptom_name="swelling or shape change"
268+
area_description_right_breast = CommonFields.area_description(
269+
"swelling or shape change", visually_hidden_label_suffix="right breast"
270+
)
271+
area_description_left_breast = CommonFields.area_description(
272+
"swelling or shape change", visually_hidden_label_suffix="left breast"
273+
)
274+
area_description_other = CommonFields.area_description(
275+
"swelling or shape change", visually_hidden_label_suffix="other"
315276
)
316277
when_started = CommonFields.when_started
317278
specific_date = CommonFields.specific_date
@@ -329,31 +290,30 @@ def __init__(self, instance=None, **kwargs):
329290
**kwargs,
330291
)
331292

332-
self.set_conditionally_required(
333-
conditionally_required_field="area_description",
334-
predicate_field="area",
335-
predicate_field_value=RightLeftOtherChoices.OTHER,
336-
)
337-
self.set_conditionally_required(
338-
conditionally_required_field="specific_date",
339-
predicate_field="when_started",
340-
predicate_field_value=RelativeDateChoices.SINCE_A_SPECIFIC_DATE,
341-
)
342-
self.set_conditionally_required(
343-
conditionally_required_field="when_resolved",
344-
predicate_field="recently_resolved",
345-
predicate_field_value=True,
346-
)
347-
self.set_conditionally_required(
348-
conditionally_required_field="investigation_details",
349-
predicate_field="investigated",
350-
predicate_field_value=YesNo.YES,
293+
self.given_field("area").require_field_with_prefix("area_description")
294+
295+
self.given_field_value(
296+
"when_started", RelativeDateChoices.SINCE_A_SPECIFIC_DATE
297+
).require_field("specific_date")
298+
299+
self.given_field_value("recently_resolved", True).require_field("when_resolved")
300+
301+
self.given_field_value("investigated", YesNo.YES).require_field(
302+
"investigation_details"
351303
)
352304

353305

354306
class SkinChangeForm(SymptomForm):
355307
area = CommonFields.area_radios(symptom_name="skin change")
356-
area_description = CommonFields.area_description(symptom_name="skin change")
308+
area_description_right_breast = CommonFields.area_description(
309+
"skin change", visually_hidden_label_suffix="right breast"
310+
)
311+
area_description_left_breast = CommonFields.area_description(
312+
"skin change", visually_hidden_label_suffix="left breast"
313+
)
314+
area_description_other = CommonFields.area_description(
315+
"skin change", visually_hidden_label_suffix="other"
316+
)
357317
symptom_sub_type = ChoiceField(
358318
choices=SkinChangeChoices,
359319
label="How has the skin changed?",
@@ -381,27 +341,22 @@ def __init__(self, instance=None, **kwargs):
381341
**kwargs,
382342
)
383343

384-
self.set_conditionally_required(
385-
conditionally_required_field="area_description",
386-
predicate_field="area",
387-
predicate_field_value=RightLeftOtherChoices.OTHER,
388-
)
389-
self.set_conditionally_required(
390-
conditionally_required_field="symptom_sub_type_details",
391-
predicate_field="symptom_sub_type",
392-
predicate_field_value=SkinChangeChoices.OTHER,
393-
)
394-
self.set_conditionally_required(
395-
conditionally_required_field="specific_date",
396-
predicate_field="when_started",
397-
predicate_field_value=RelativeDateChoices.SINCE_A_SPECIFIC_DATE,
398-
)
399-
self.set_conditionally_required(
400-
conditionally_required_field="investigation_details",
401-
predicate_field="investigated",
402-
predicate_field_value=YesNo.YES,
344+
self.given_field("area").require_field_with_prefix("area_description")
345+
346+
self.given_field_value(
347+
"when_started", RelativeDateChoices.SINCE_A_SPECIFIC_DATE
348+
).require_field("specific_date")
349+
350+
self.given_field_value("recently_resolved", True).require_field("when_resolved")
351+
352+
self.given_field_value("investigated", YesNo.YES).require_field(
353+
"investigation_details"
403354
)
404355

356+
self.given_field_value(
357+
"symptom_sub_type", SkinChangeChoices.OTHER
358+
).require_field("symptom_sub_type_details")
359+
405360

406361
class NippleChangeForm(SymptomForm):
407362
area = MultipleChoiceField(
@@ -438,22 +393,20 @@ def __init__(self, instance=None, **kwargs):
438393
**kwargs,
439394
)
440395

441-
self.set_conditionally_required(
442-
conditionally_required_field="symptom_sub_type_details",
443-
predicate_field="symptom_sub_type",
444-
predicate_field_value=NippleChangeChoices.OTHER,
445-
)
446-
self.set_conditionally_required(
447-
conditionally_required_field="specific_date",
448-
predicate_field="when_started",
449-
predicate_field_value=RelativeDateChoices.SINCE_A_SPECIFIC_DATE,
450-
)
451-
self.set_conditionally_required(
452-
conditionally_required_field="investigation_details",
453-
predicate_field="investigated",
454-
predicate_field_value=YesNo.YES,
396+
self.given_field_value(
397+
"when_started", RelativeDateChoices.SINCE_A_SPECIFIC_DATE
398+
).require_field("specific_date")
399+
400+
self.given_field_value("recently_resolved", True).require_field("when_resolved")
401+
402+
self.given_field_value("investigated", YesNo.YES).require_field(
403+
"investigation_details"
455404
)
456405

406+
self.given_field_value(
407+
"symptom_sub_type", NippleChangeChoices.OTHER
408+
).require_field("symptom_sub_type_details")
409+
457410
def initial_values(self, instance):
458411
return {
459412
"area": self.area_initial(instance.area),
@@ -481,7 +434,15 @@ def area_initial(self, area):
481434

482435
class OtherSymptomForm(SymptomForm):
483436
area = CommonFields.area_radios()
484-
area_description = CommonFields.area_description()
437+
area_description_right_breast = CommonFields.area_description(
438+
visually_hidden_label_suffix="right breast"
439+
)
440+
area_description_left_breast = CommonFields.area_description(
441+
visually_hidden_label_suffix="left breast"
442+
)
443+
area_description_other = CommonFields.area_description(
444+
visually_hidden_label_suffix="other"
445+
)
485446
symptom_sub_type_details = CharField(
486447
label="Describe the symptom",
487448
label_classes="nhsuk-label--m",
@@ -504,18 +465,14 @@ def __init__(self, instance=None, **kwargs):
504465
**kwargs,
505466
)
506467

507-
self.set_conditionally_required(
508-
conditionally_required_field="area_description",
509-
predicate_field="area",
510-
predicate_field_value=RightLeftOtherChoices.OTHER,
511-
)
512-
self.set_conditionally_required(
513-
conditionally_required_field="specific_date",
514-
predicate_field="when_started",
515-
predicate_field_value=RelativeDateChoices.SINCE_A_SPECIFIC_DATE,
516-
)
517-
self.set_conditionally_required(
518-
conditionally_required_field="investigation_details",
519-
predicate_field="investigated",
520-
predicate_field_value=YesNo.YES,
468+
self.given_field("area").require_field_with_prefix("area_description")
469+
470+
self.given_field_value(
471+
"when_started", RelativeDateChoices.SINCE_A_SPECIFIC_DATE
472+
).require_field("specific_date")
473+
474+
self.given_field_value("recently_resolved", True).require_field("when_resolved")
475+
476+
self.given_field_value("investigated", YesNo.YES).require_field(
477+
"investigation_details"
521478
)

manage_breast_screening/mammograms/jinja2/mammograms/medical_information/symptoms/nipple_change.jinja renamed to manage_breast_screening/mammograms/jinja2/mammograms/medical_information/symptoms/forms/nipple_change.jinja

File renamed without changes.

0 commit comments

Comments
 (0)