Skip to content

Commit a2e050f

Browse files
authored
Merge pull request #1125 from NHSDigital/DTOSS-12373-breast-pain
Added breast pain symptom
2 parents f8c3911 + be89240 commit a2e050f

File tree

14 files changed

+349
-12
lines changed

14 files changed

+349
-12
lines changed

manage_breast_screening/mammograms/forms/symptom_forms.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,3 +485,47 @@ def __init__(self, instance=None, **kwargs):
485485
self.given_field_value("investigated", YesNo.YES).require_field(
486486
"investigation_details"
487487
)
488+
489+
490+
class BreastPainForm(SymptomForm):
491+
BREAST_PAIN_NAME = "pain"
492+
493+
area = CommonFields.area_radios(symptom_name=BREAST_PAIN_NAME)
494+
area_description_right_breast = CommonFields.area_description(
495+
BREAST_PAIN_NAME, visually_hidden_label_suffix=": right breast"
496+
)
497+
area_description_left_breast = CommonFields.area_description(
498+
BREAST_PAIN_NAME, visually_hidden_label_suffix=": left breast"
499+
)
500+
area_description_other = CommonFields.area_description(
501+
BREAST_PAIN_NAME,
502+
visually_hidden_label_suffix=": other",
503+
classes="nhsuk-u-width-two-thirds",
504+
)
505+
when_started = CommonFields.when_started
506+
specific_date = CommonFields.specific_date
507+
intermittent = CommonFields.intermittent
508+
recently_resolved = CommonFields.recently_resolved
509+
when_resolved = CommonFields.when_resolved
510+
investigated = CommonFields.investigated
511+
investigation_details = CommonFields.investigation_details
512+
additional_information = CommonFields.additional_information
513+
514+
def __init__(self, instance=None, **kwargs):
515+
super().__init__(
516+
symptom_type=SymptomType.BREAST_PAIN,
517+
instance=instance,
518+
**kwargs,
519+
)
520+
521+
self.given_field("area").require_field_with_prefix("area_description")
522+
523+
self.given_field_value(
524+
"when_started", RelativeDateChoices.SINCE_A_SPECIFIC_DATE
525+
).require_field("specific_date")
526+
527+
self.given_field_value("recently_resolved", True).require_field("when_resolved")
528+
529+
self.given_field_value("investigated", YesNo.YES).require_field(
530+
"investigation_details"
531+
)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{% extends "layout-form.jinja" %}
2+
{% from "nhsuk/components/button/macro.jinja" import button %}
3+
{% from "nhsuk/components/fieldset/macro.jinja" import fieldset %}
4+
5+
{% block form %}
6+
{% include "mammograms/medical_information/symptoms/forms/inline_area_radios.jinja" %}
7+
8+
{% do form.when_started.add_divider_after("OVER_THREE_YEARS", "or") %}
9+
{% do form.when_started.add_divider_after("SINCE_A_SPECIFIC_DATE", "or") %}
10+
{% do form.when_started.add_conditional_html(
11+
'SINCE_A_SPECIFIC_DATE',
12+
form.specific_date.as_field_group()
13+
) %}
14+
15+
{% do form.recently_resolved.add_conditional_html('true', form.when_resolved.as_field_group()) %}
16+
17+
{% do form.investigated.add_conditional_html('YES', form.investigation_details.as_field_group()) %}
18+
19+
{% call fieldset({
20+
"legend": {
21+
"text": form.when_started.label,
22+
"classes": "nhsuk-fieldset__legend--m"
23+
}
24+
}) %}
25+
{{ form.when_started.as_field_group() }}
26+
<br>
27+
{{ form.intermittent.as_field_group() }}
28+
{{ form.recently_resolved.as_field_group() }}
29+
{% endcall %}
30+
31+
{{ form.investigated.as_field_group() }}
32+
33+
{{ form.additional_information.as_field_group() }}
34+
35+
<div class="nhsuk-button-group">
36+
{{ button({
37+
"text": "Save symptom"
38+
}) }}
39+
</div>
40+
41+
{% if delete_link %}
42+
<p class="nhsuk-u-margin-top-4">
43+
<a href="{{ delete_link.href }}" class="{{ delete_link.class }}">
44+
{{ delete_link.text }}
45+
</a>
46+
</p>
47+
{% endif %}
48+
{% endblock %}

manage_breast_screening/mammograms/presenters/medical_information_presenter.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ def symptom_buttons(self):
182182
self.add_swelling_or_shape_change_button,
183183
self.add_skin_change_button,
184184
self.add_nipple_change_button,
185+
self.add_breast_pain_button,
185186
self.add_other_symptom_button,
186187
]
187188

@@ -232,6 +233,10 @@ def add_nipple_change_button(self):
232233
"mammograms:add_symptom_nipple_change", "Nipple change"
233234
)
234235

236+
@property
237+
def add_breast_pain_button(self):
238+
return self._subpage_button("mammograms:add_symptom_breast_pain", "Breast pain")
239+
235240
@property
236241
def add_other_symptom_button(self):
237242
return self._subpage_button("mammograms:add_symptom_other", "Other")

manage_breast_screening/mammograms/presenters/symptom_presenter.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -165,17 +165,7 @@ def build_summary_list_row(self, include_actions=True):
165165
return result
166166

167167
def change_view(self):
168-
match self._symptom.symptom_type_id:
169-
case SymptomType.LUMP:
170-
return "mammograms:change_symptom_lump"
171-
case SymptomType.SWELLING_OR_SHAPE_CHANGE:
172-
return "mammograms:change_symptom_swelling_or_shape_change"
173-
case SymptomType.SKIN_CHANGE:
174-
return "mammograms:change_symptom_skin_change"
175-
case SymptomType.NIPPLE_CHANGE:
176-
return "mammograms:change_symptom_nipple_change"
177-
case _:
178-
return "mammograms:change_symptom_other"
168+
return f"mammograms:change_symptom_{self._symptom.symptom_type_id.lower()}"
179169

180170
@property
181171
def delete_message_html(self):

manage_breast_screening/mammograms/tests/forms/test_symptom_forms.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from django.test import RequestFactory
66

77
from manage_breast_screening.mammograms.forms.symptom_forms import (
8+
BreastPainForm,
89
LumpForm,
910
NippleChangeForm,
1011
OtherSymptomForm,
@@ -638,3 +639,74 @@ def test_valid_form_with_conditionally_required_fields(self):
638639
)
639640
)
640641
assert form.is_valid()
642+
643+
644+
class TestBreastPainForm:
645+
def test_valid_form(self):
646+
form = BreastPainForm(
647+
data=QueryDict(
648+
urlencode(
649+
{
650+
"area": RightLeftOtherChoices.LEFT_BREAST,
651+
"area_description_left_breast": "uoq",
652+
"when_started": RelativeDateChoices.LESS_THAN_THREE_MONTHS,
653+
"investigated": YesNo.NO,
654+
}
655+
)
656+
)
657+
)
658+
assert form.is_valid()
659+
660+
def test_missing_required_fields(self):
661+
form = BreastPainForm(data=QueryDict())
662+
663+
assert not form.is_valid()
664+
assert form.errors == {
665+
"when_started": ["Select how long the symptom has existed"],
666+
"investigated": ["Select whether the symptom has been investigated or not"],
667+
"area": ["Select the location of the pain"],
668+
}
669+
670+
def test_missing_conditionally_required_fields(self):
671+
form = BreastPainForm(
672+
data=QueryDict(
673+
urlencode(
674+
{
675+
"area": RightLeftOtherChoices.OTHER,
676+
"when_started": RelativeDateChoices.SINCE_A_SPECIFIC_DATE,
677+
"investigated": YesNo.YES,
678+
"recently_resolved": True,
679+
}
680+
)
681+
)
682+
)
683+
684+
assert not form.is_valid()
685+
assert form.errors == {
686+
"area_description_other": [
687+
"Describe the specific area where the pain is located"
688+
],
689+
"specific_date": ["Enter the date the symptom started"],
690+
"investigation_details": ["Enter details of any investigations"],
691+
"when_resolved": ["Enter when the symptom was resolved"],
692+
}
693+
694+
def test_valid_form_with_conditionally_required_fields(self):
695+
form = BreastPainForm(
696+
data=QueryDict(
697+
urlencode(
698+
{
699+
"area": RightLeftOtherChoices.OTHER,
700+
"area_description_other": "abc",
701+
"when_started": RelativeDateChoices.SINCE_A_SPECIFIC_DATE,
702+
"investigated": YesNo.YES,
703+
"specific_date_0": "2",
704+
"specific_date_1": "2025",
705+
"investigation_details": "def",
706+
"recently_resolved": True,
707+
"when_resolved": "3 months ago",
708+
}
709+
)
710+
)
711+
)
712+
assert form.is_valid()

manage_breast_screening/mammograms/tests/presenters/test_check_medical_information_presenter.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,15 @@ def test_many_medical_history(self):
864864
"when_started": RelativeDateChoices.LESS_THAN_THREE_MONTHS,
865865
},
866866
),
867+
(
868+
"Breast pain (both breasts)",
869+
{
870+
"symptom_type_id": SymptomType.BREAST_PAIN,
871+
"symptom_sub_type_details": "user provided details",
872+
"area": SymptomAreas.BOTH_BREASTS,
873+
"when_started": RelativeDateChoices.LESS_THAN_THREE_MONTHS,
874+
},
875+
),
867876
(
868877
"user provided details (both breasts)",
869878
{

manage_breast_screening/mammograms/tests/presenters/test_medical_information_presenter.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,14 @@ def test_add_nipple_change_button(self):
108108
"text": "Nipple change",
109109
}
110110

111+
def test_add_breast_pain_button(self):
112+
appointment = AppointmentFactory()
113+
114+
assert MedicalInformationPresenter(appointment).add_breast_pain_button == {
115+
"href": f"/mammograms/{appointment.pk}/record-medical-information/breast-pain/",
116+
"text": "Breast pain",
117+
}
118+
111119
def test_add_breast_cancer_history_button(self):
112120
appointment = AppointmentFactory()
113121

@@ -499,3 +507,18 @@ def test_review_button(self, in_progress_appointment):
499507
"href": f"/mammograms/{in_progress_appointment.pk}/record-medical-information/mark-reviewed/OTHER_INFORMATION/",
500508
"text": "Mark as reviewed",
501509
}
510+
511+
def test_symptom_buttons(self):
512+
appointment = AppointmentFactory()
513+
presenter = MedicalInformationPresenter(appointment)
514+
buttons = presenter.symptom_buttons
515+
516+
assert [button["text"] for button in buttons] == [
517+
"Lump",
518+
"Swelling or shape change",
519+
"Skin change",
520+
"Nipple change",
521+
"Breast pain",
522+
"Other",
523+
]
524+
assert all(button.get("href") for button in buttons)

manage_breast_screening/mammograms/tests/presenters/test_symptom_presenter.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,8 @@ def test_add_message_html(self):
181181
presenter.add_message_html
182182
== '<h3 class="nhsuk-notification-banner__heading">Symptom added</h3><p>Added lump.</p>'
183183
)
184+
185+
def test_change_view(self):
186+
symptom = SymptomFactory.create(symptom_type_id=SymptomType.BREAST_PAIN)
187+
presenter = SymptomPresenter(symptom)
188+
assert presenter.change_view() == "mammograms:change_symptom_breast_pain"

manage_breast_screening/mammograms/tests/views/test_symptom_views.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
SkinChangeChoices,
1111
Symptom,
1212
SymptomAreas,
13+
SymptomType,
1314
)
1415
from manage_breast_screening.participants.tests.factories import (
1516
AppointmentFactory,
@@ -446,6 +447,95 @@ def test_valid_post_redirects_to_appointment(
446447
)
447448

448449

450+
@pytest.mark.django_db
451+
class TestAddBreastPainView:
452+
def test_renders_response(self, clinical_user_client):
453+
appointment = AppointmentFactory.create(
454+
clinic_slot__clinic__setting__provider=clinical_user_client.current_provider
455+
)
456+
response = clinical_user_client.http.get(
457+
reverse(
458+
"mammograms:add_symptom_breast_pain",
459+
kwargs={"pk": appointment.pk},
460+
)
461+
)
462+
assert response.status_code == 200
463+
464+
def test_valid_post_redirects_to_appointment(self, clinical_user_client):
465+
appointment = AppointmentFactory.create(
466+
clinic_slot__clinic__setting__provider=clinical_user_client.current_provider
467+
)
468+
response = clinical_user_client.http.post(
469+
reverse(
470+
"mammograms:add_symptom_breast_pain",
471+
kwargs={"pk": appointment.pk},
472+
),
473+
{
474+
"area": SymptomAreas.RIGHT_BREAST.value,
475+
"area_description_right_breast": "uiq",
476+
"when_started": RelativeDateChoices.LESS_THAN_THREE_MONTHS.value,
477+
"investigated": YesNo.NO.value,
478+
},
479+
)
480+
assertRedirects(
481+
response,
482+
reverse(
483+
"mammograms:record_medical_information",
484+
kwargs={"pk": appointment.pk},
485+
),
486+
)
487+
488+
489+
@pytest.mark.django_db
490+
class TestChangeBreastPainView:
491+
@pytest.fixture
492+
def breast_pain(self, clinical_user_client):
493+
appointment = AppointmentFactory.create(
494+
clinic_slot__clinic__setting__provider=clinical_user_client.current_provider
495+
)
496+
return SymptomFactory.create(
497+
symptom_type_id=SymptomType.BREAST_PAIN, appointment=appointment
498+
)
499+
500+
def test_renders_response(self, clinical_user_client, breast_pain):
501+
response = clinical_user_client.http.get(
502+
reverse(
503+
"mammograms:change_symptom_breast_pain",
504+
kwargs={
505+
"pk": breast_pain.appointment.pk,
506+
"symptom_pk": breast_pain.pk,
507+
},
508+
)
509+
)
510+
assert response.status_code == 200
511+
512+
def test_valid_post_redirects_to_appointment(
513+
self, clinical_user_client, breast_pain
514+
):
515+
response = clinical_user_client.http.post(
516+
reverse(
517+
"mammograms:change_symptom_breast_pain",
518+
kwargs={
519+
"pk": breast_pain.appointment.pk,
520+
"symptom_pk": breast_pain.pk,
521+
},
522+
),
523+
{
524+
"area": SymptomAreas.RIGHT_BREAST.value,
525+
"area_description_right_breast": "uiq",
526+
"when_started": RelativeDateChoices.LESS_THAN_THREE_MONTHS.value,
527+
"investigated": YesNo.NO.value,
528+
},
529+
)
530+
assertRedirects(
531+
response,
532+
reverse(
533+
"mammograms:record_medical_information",
534+
kwargs={"pk": breast_pain.appointment.pk},
535+
),
536+
)
537+
538+
449539
@pytest.mark.django_db
450540
class TestDeleteSymptomView:
451541
def test_get_renders_response(self, clinical_user_client, lump):

manage_breast_screening/mammograms/urls.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,16 @@ def not_implemented_view(request, pk):
165165
symptom_views.ChangeNippleChangeView.as_view(),
166166
name="change_symptom_nipple_change",
167167
),
168+
path(
169+
"<uuid:pk>/record-medical-information/breast-pain/",
170+
symptom_views.AddBreastPainView.as_view(),
171+
name="add_symptom_breast_pain",
172+
),
173+
path(
174+
"<uuid:pk>/record-medical-information/breast-pain/<uuid:symptom_pk>/",
175+
symptom_views.ChangeBreastPainView.as_view(),
176+
name="change_symptom_breast_pain",
177+
),
168178
path(
169179
"<uuid:pk>/record-medical-information/other/",
170180
symptom_views.AddOtherSymptomView.as_view(),

0 commit comments

Comments
 (0)