diff --git a/manage_breast_screening/mammograms/forms/symptom_forms.py b/manage_breast_screening/mammograms/forms/symptom_forms.py
index d5ffbbaa0..1b51ff36d 100644
--- a/manage_breast_screening/mammograms/forms/symptom_forms.py
+++ b/manage_breast_screening/mammograms/forms/symptom_forms.py
@@ -19,6 +19,7 @@
from manage_breast_screening.nhsuk_forms.forms import FormWithConditionalFields
from manage_breast_screening.nhsuk_forms.utils import YesNo, yes_no, yes_no_field
from manage_breast_screening.participants.models.symptom import (
+ HighlightToReaderChoices,
NippleChangeChoices,
RelativeDateChoices,
SkinChangeChoices,
@@ -80,6 +81,13 @@ class CommonFields:
widget=Textarea(attrs={"rows": 5}),
error_messages={"required": "Enter details of any investigations"},
)
+ highlight_to_readers = ChoiceField(
+ choices=HighlightToReaderChoices,
+ label="Highlight this symptom to readers?",
+ error_messages={
+ "required": "Select whether this symptom should be highlighted to image readers"
+ },
+ )
additional_information = CharField(
required=False,
label="Additional info (optional)",
@@ -151,6 +159,9 @@ def initial_values(self, instance):
"when_resolved": instance.when_resolved,
"investigated": yes_no(instance.investigated),
"investigation_details": instance.investigation_details,
+ "highlight_to_readers": HighlightToReaderChoices.YES
+ if instance.highlight_to_readers
+ else HighlightToReaderChoices.NO,
"additional_information": instance.additional_information,
}
@@ -184,6 +195,10 @@ def model_values(self):
intermittent = self.cleaned_data.get("intermittent", False)
recently_resolved = self.cleaned_data.get("recently_resolved", False)
when_resolved = self.cleaned_data.get("when_resolved", "")
+ highlight_to_readers = (
+ self.cleaned_data.get("highlight_to_readers", HighlightToReaderChoices.YES)
+ == HighlightToReaderChoices.YES
+ )
additional_information = self.cleaned_data.get("additional_information", "")
return dict(
@@ -199,6 +214,7 @@ def model_values(self):
intermittent=intermittent,
recently_resolved=recently_resolved,
when_resolved=when_resolved,
+ highlight_to_readers=highlight_to_readers,
additional_information=additional_information,
)
@@ -470,6 +486,7 @@ class OtherSymptomForm(SymptomForm):
when_resolved = CommonFields.when_resolved
investigated = CommonFields.investigated
investigation_details = CommonFields.investigation_details
+ highlight_to_readers = CommonFields.highlight_to_readers
additional_information = CommonFields.additional_information
def __init__(self, instance=None, **kwargs):
@@ -514,6 +531,7 @@ class BreastPainForm(SymptomForm):
when_resolved = CommonFields.when_resolved
investigated = CommonFields.investigated
investigation_details = CommonFields.investigation_details
+ highlight_to_readers = CommonFields.highlight_to_readers
additional_information = CommonFields.additional_information
def __init__(self, instance=None, **kwargs):
diff --git a/manage_breast_screening/mammograms/jinja2/mammograms/medical_information/symptoms/forms/breast_pain.jinja b/manage_breast_screening/mammograms/jinja2/mammograms/medical_information/symptoms/forms/breast_pain.jinja
index ccc32eddd..4ab81904d 100644
--- a/manage_breast_screening/mammograms/jinja2/mammograms/medical_information/symptoms/forms/breast_pain.jinja
+++ b/manage_breast_screening/mammograms/jinja2/mammograms/medical_information/symptoms/forms/breast_pain.jinja
@@ -30,6 +30,8 @@
{{ form.investigated.as_field_group() }}
+ {{ form.highlight_to_readers.as_field_group() }}
+
{{ form.additional_information.as_field_group() }}
diff --git a/manage_breast_screening/mammograms/jinja2/mammograms/medical_information/symptoms/forms/other.jinja b/manage_breast_screening/mammograms/jinja2/mammograms/medical_information/symptoms/forms/other.jinja
index c4e781c46..bd68cd981 100644
--- a/manage_breast_screening/mammograms/jinja2/mammograms/medical_information/symptoms/forms/other.jinja
+++ b/manage_breast_screening/mammograms/jinja2/mammograms/medical_information/symptoms/forms/other.jinja
@@ -32,6 +32,8 @@
{{ form.investigated.as_field_group() }}
+ {{ form.highlight_to_readers.as_field_group() }}
+
{{ form.additional_information.as_field_group() }}
diff --git a/manage_breast_screening/mammograms/presenters/symptom_presenter.py b/manage_breast_screening/mammograms/presenters/symptom_presenter.py
index 4ac80c590..0db9e1d9a 100644
--- a/manage_breast_screening/mammograms/presenters/symptom_presenter.py
+++ b/manage_breast_screening/mammograms/presenters/symptom_presenter.py
@@ -1,5 +1,5 @@
from django.urls import reverse
-from django.utils.html import escape
+from django.utils.html import escape, format_html
from django.utils.safestring import mark_safe
from manage_breast_screening.core.template_helpers import (
@@ -139,8 +139,17 @@ def build_summary_list_row(self, include_actions=True):
]
)
+ if self._symptom.highlight_to_readers:
+ key = {
+ "html": format_html(
+ '{}
Highlight to image readers',
+ self._symptom.symptom_type.name,
+ )
+ }
+ else:
+ key = {"text": self._symptom.symptom_type.name}
result = {
- "key": {"text": self._symptom.symptom_type.name},
+ "key": key,
"value": {"html": html},
}
diff --git a/manage_breast_screening/mammograms/tests/forms/test_symptom_forms.py b/manage_breast_screening/mammograms/tests/forms/test_symptom_forms.py
index 71a228e02..8f7d2f462 100644
--- a/manage_breast_screening/mammograms/tests/forms/test_symptom_forms.py
+++ b/manage_breast_screening/mammograms/tests/forms/test_symptom_forms.py
@@ -19,6 +19,7 @@
)
from manage_breast_screening.nhsuk_forms.choices import YesNo
from manage_breast_screening.participants.models.symptom import (
+ HighlightToReaderChoices,
NippleChangeChoices,
SkinChangeChoices,
SymptomAreas,
@@ -588,6 +589,7 @@ def test_valid_form(self):
"symptom_sub_type": SkinChangeChoices.COLOUR_CHANGE,
"when_started": RelativeDateChoices.LESS_THAN_THREE_MONTHS,
"investigated": YesNo.NO,
+ "highlight_to_readers": HighlightToReaderChoices.YES,
}
)
)
@@ -603,6 +605,9 @@ def test_missing_required_fields(self):
"symptom_sub_type_details": ["Enter a description of the symptom"],
"investigated": ["Select whether the symptom has been investigated or not"],
"area": ["Select the location of the symptom"],
+ "highlight_to_readers": [
+ "Select whether this symptom should be highlighted to image readers"
+ ],
}
def test_missing_conditionally_required_fields(self):
@@ -614,6 +619,7 @@ def test_missing_conditionally_required_fields(self):
"symptom_sub_type_details": "abc symptom",
"when_started": RelativeDateChoices.SINCE_A_SPECIFIC_DATE,
"investigated": YesNo.YES,
+ "highlight_to_readers": HighlightToReaderChoices.YES,
}
)
)
@@ -641,6 +647,7 @@ def test_valid_form_with_conditionally_required_fields(self):
"specific_date_0": "2",
"specific_date_1": "2025",
"investigation_details": "def",
+ "highlight_to_readers": HighlightToReaderChoices.YES,
}
)
)
@@ -658,6 +665,7 @@ def test_valid_form(self):
"area_description_left_breast": "uoq",
"when_started": RelativeDateChoices.LESS_THAN_THREE_MONTHS,
"investigated": YesNo.NO,
+ "highlight_to_readers": HighlightToReaderChoices.YES,
}
)
)
@@ -672,6 +680,9 @@ def test_missing_required_fields(self):
"when_started": ["Select how long the symptom has existed"],
"investigated": ["Select whether the symptom has been investigated or not"],
"area": ["Select the location of the pain"],
+ "highlight_to_readers": [
+ "Select whether this symptom should be highlighted to image readers"
+ ],
}
def test_missing_conditionally_required_fields(self):
@@ -683,6 +694,7 @@ def test_missing_conditionally_required_fields(self):
"when_started": RelativeDateChoices.SINCE_A_SPECIFIC_DATE,
"investigated": YesNo.YES,
"recently_resolved": True,
+ "highlight_to_readers": HighlightToReaderChoices.YES,
}
)
)
@@ -712,6 +724,7 @@ def test_valid_form_with_conditionally_required_fields(self):
"investigation_details": "def",
"recently_resolved": True,
"when_resolved": "3 months ago",
+ "highlight_to_readers": HighlightToReaderChoices.YES,
}
)
)
diff --git a/manage_breast_screening/mammograms/tests/presenters/test_medical_information_presenter.py b/manage_breast_screening/mammograms/tests/presenters/test_medical_information_presenter.py
index 0dce526cc..e0b2a12ed 100644
--- a/manage_breast_screening/mammograms/tests/presenters/test_medical_information_presenter.py
+++ b/manage_breast_screening/mammograms/tests/presenters/test_medical_information_presenter.py
@@ -51,6 +51,23 @@ def test_formats_symptoms_summary_list(self):
area=SymptomAreas.BOTH_BREASTS,
)
+ symptom3 = SymptomFactory.create(
+ other=True,
+ appointment=appointment,
+ when_started=RelativeDateChoices.LESS_THAN_THREE_MONTHS,
+ area=SymptomAreas.RIGHT_BREAST,
+ symptom_sub_type_details="abc",
+ )
+
+ symptom4 = SymptomFactory.create(
+ other=True,
+ appointment=appointment,
+ when_started=RelativeDateChoices.LESS_THAN_THREE_MONTHS,
+ area=SymptomAreas.LEFT_BREAST,
+ highlight_to_readers=False,
+ symptom_sub_type_details="xyz",
+ )
+
presenter = MedicalInformationPresenter(appointment)
assert presenter.symptom_rows == [
@@ -66,12 +83,46 @@ def test_formats_symptoms_summary_list(self):
],
},
"key": {
- "text": "Lump",
+ "html": 'Lump
Highlight to image readers',
},
"value": {
"html": "Left breast
Not sure
Symptom is intermittent
Stopped: resolved date
Not investigated
Additional information: abc",
},
},
+ {
+ "actions": {
+ "items": [
+ {
+ "text": "Change",
+ "classes": "nhsuk-link--no-visited-state",
+ "visuallyHiddenText": "other",
+ "href": f"/mammograms/{appointment.id}/record-medical-information/other/{symptom3.id}/",
+ },
+ ],
+ },
+ "key": {
+ "html": 'Other
Highlight to image readers'
+ },
+ "value": {
+ "html": "Description: abc
Right breast
Less than 3 months ago
Not investigated"
+ },
+ },
+ {
+ "actions": {
+ "items": [
+ {
+ "text": "Change",
+ "classes": "nhsuk-link--no-visited-state",
+ "visuallyHiddenText": "other",
+ "href": f"/mammograms/{appointment.id}/record-medical-information/other/{symptom4.id}/",
+ }
+ ]
+ },
+ "key": {"text": "Other"},
+ "value": {
+ "html": "Description: xyz
Left breast
Less than 3 months ago
Not investigated"
+ },
+ },
{
"actions": {
"items": [
@@ -84,7 +135,7 @@ def test_formats_symptoms_summary_list(self):
],
},
"key": {
- "text": "Swelling or shape change",
+ "html": 'Swelling or shape change
Highlight to image readers',
},
"value": {
"html": "Both breasts
Less than 3 months ago
Not investigated",
diff --git a/manage_breast_screening/mammograms/tests/presenters/test_symptom_presenter.py b/manage_breast_screening/mammograms/tests/presenters/test_symptom_presenter.py
index 85a61aad7..46fde1eff 100644
--- a/manage_breast_screening/mammograms/tests/presenters/test_symptom_presenter.py
+++ b/manage_breast_screening/mammograms/tests/presenters/test_symptom_presenter.py
@@ -135,7 +135,7 @@ def test_formats_intermittent_stopped_and_additional_information(self):
assert presenter.intermittent_line == "Symptom is intermittent"
assert presenter.additional_information_line == "Additional information: abc"
- def test_formats_for_summary_list(self):
+ def test_formats_lump_for_summary_list(self):
symptom = SymptomFactory.create(
lump=True,
when_started=RelativeDateChoices.NOT_SURE,
@@ -159,13 +159,48 @@ def test_formats_for_summary_list(self):
],
},
"key": {
- "text": "Lump",
+ "html": 'Lump
Highlight to image readers',
},
"value": {
"html": "Left breast
Not sure
Symptom is intermittent
Stopped: resolved date
Not investigated
Additional information: abc",
},
}
+ @pytest.mark.parametrize("highlight_to_readers", [True, False])
+ def test_formats_other_for_summary_list(self, highlight_to_readers):
+ symptom = SymptomFactory.create(
+ breast_pain=True,
+ when_started=RelativeDateChoices.LESS_THAN_THREE_MONTHS,
+ area=SymptomAreas.LEFT_BREAST,
+ highlight_to_readers=highlight_to_readers,
+ )
+
+ presenter = SymptomPresenter(symptom)
+
+ if highlight_to_readers:
+ expected_key = {
+ "html": 'Breast pain
Highlight to image readers',
+ }
+ else:
+ expected_key = {"text": "Breast pain"}
+
+ assert presenter.summary_list_row == {
+ "actions": {
+ "items": [
+ {
+ "text": "Change",
+ "classes": "nhsuk-link--no-visited-state",
+ "visuallyHiddenText": "breast pain",
+ "href": f"/mammograms/{symptom.appointment_id}/record-medical-information/breast-pain/{symptom.id}/",
+ }
+ ]
+ },
+ "key": expected_key,
+ "value": {
+ "html": "Left breast
Less than 3 months ago
Not investigated"
+ },
+ }
+
def test_delete_message_html(self):
lump = SymptomFactory.create(lump=True)
presenter = SymptomPresenter(lump)
diff --git a/manage_breast_screening/mammograms/tests/views/test_symptom_views.py b/manage_breast_screening/mammograms/tests/views/test_symptom_views.py
index e6ed3608e..0edd0f4bf 100644
--- a/manage_breast_screening/mammograms/tests/views/test_symptom_views.py
+++ b/manage_breast_screening/mammograms/tests/views/test_symptom_views.py
@@ -5,6 +5,7 @@
from manage_breast_screening.nhsuk_forms.choices import YesNo
from manage_breast_screening.participants.models.symptom import (
+ HighlightToReaderChoices,
NippleChangeChoices,
RelativeDateChoices,
SkinChangeChoices,
@@ -409,6 +410,7 @@ def test_valid_post_redirects_to_appointment(
"symptom_sub_type_details": "abc",
"when_started": RelativeDateChoices.LESS_THAN_THREE_MONTHS.value,
"investigated": YesNo.NO.value,
+ "highlight_to_readers": HighlightToReaderChoices.YES.value,
},
)
assertRedirects(
@@ -457,6 +459,7 @@ def test_valid_post_redirects_to_appointment(
"symptom_sub_type_details": "abc",
"when_started": RelativeDateChoices.LESS_THAN_THREE_MONTHS.value,
"investigated": YesNo.NO.value,
+ "highlight_to_readers": HighlightToReaderChoices.YES.value,
},
)
assertRedirects(
@@ -494,6 +497,7 @@ def test_valid_post_redirects_to_appointment(
"area_description_right_breast": "uiq",
"when_started": RelativeDateChoices.LESS_THAN_THREE_MONTHS.value,
"investigated": YesNo.NO.value,
+ "highlight_to_readers": HighlightToReaderChoices.YES.value,
},
)
assertRedirects(
@@ -542,6 +546,7 @@ def test_valid_post_redirects_to_appointment(
"area_description_right_breast": "uiq",
"when_started": RelativeDateChoices.LESS_THAN_THREE_MONTHS.value,
"investigated": YesNo.NO.value,
+ "highlight_to_readers": HighlightToReaderChoices.YES.value,
},
)
assertRedirects(
diff --git a/manage_breast_screening/mammograms/views/symptom_views.py b/manage_breast_screening/mammograms/views/symptom_views.py
index 3e69aa461..711ea9983 100644
--- a/manage_breast_screening/mammograms/views/symptom_views.py
+++ b/manage_breast_screening/mammograms/views/symptom_views.py
@@ -48,7 +48,7 @@ def get_back_link_params(self):
}
def get_context_data(self, **kwargs):
- context = super().get_context_data()
+ context = super().get_context_data(**kwargs)
participant = self.appointment.participant
@@ -58,11 +58,18 @@ def get_context_data(self, **kwargs):
"caption": participant.full_name,
"heading": f"Details of the {self.symptom_type_name.lower()}",
"page_title": f"Details of the {self.symptom_type_name.lower()}",
+ "heading_description": self._get_heading_description(),
},
)
return context
+ def _get_heading_description(self):
+ suffix = " a recognised symptom of breast cancer. Information recorded here will be highlighted during image reading."
+ if self.symptom_type_name == "lump":
+ return "Lumps are" + suffix
+ return f"{self.symptom_type_name.capitalize()} is" + suffix
+
class AddSymptomView(BaseSymptomFormView):
"""
@@ -137,13 +144,6 @@ class AddSymptomLumpView(AddSymptomView):
form_class = LumpForm
template_name = "mammograms/medical_information/symptoms/forms/simple_symptom.jinja"
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context["heading_description"] = (
- "Lumps are a recognised symptom of breast cancer. Information recorded here will be highlighted during image reading."
- )
- return context
-
class AddSymptomSwellingOrShapeChangeView(AddSymptomView):
"""
@@ -212,13 +212,6 @@ class UpdateSymptomLumpView(UpdateSymptomView):
def extra_filters(self):
return {"symptom_type_id": SymptomType.LUMP}
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context["heading_description"] = (
- "Record whether the lump is in the left breast, right breast or both."
- )
- return context
-
class UpdateSymptomSwellingOrShapeChangeView(UpdateSymptomView):
"""
diff --git a/manage_breast_screening/participants/migrations/0068_symptom_highlight_to_readers.py b/manage_breast_screening/participants/migrations/0068_symptom_highlight_to_readers.py
new file mode 100644
index 000000000..28872ee37
--- /dev/null
+++ b/manage_breast_screening/participants/migrations/0068_symptom_highlight_to_readers.py
@@ -0,0 +1,18 @@
+# Generated by Django 6.0.4 on 2026-04-28 11:49
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('participants', '0001_squashed_0067_participantreportedmammogram_created_by_and_more'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='symptom',
+ name='highlight_to_readers',
+ field=models.BooleanField(default=True),
+ ),
+ ]
diff --git a/manage_breast_screening/participants/migrations/max_migration.txt b/manage_breast_screening/participants/migrations/max_migration.txt
index bb7026a49..8cf7e2d60 100644
--- a/manage_breast_screening/participants/migrations/max_migration.txt
+++ b/manage_breast_screening/participants/migrations/max_migration.txt
@@ -1 +1 @@
-0001_squashed_0067_participantreportedmammogram_created_by_and_more
+0068_symptom_highlight_to_readers
diff --git a/manage_breast_screening/participants/models/symptom.py b/manage_breast_screening/participants/models/symptom.py
index d69a0b732..937dae35a 100644
--- a/manage_breast_screening/participants/models/symptom.py
+++ b/manage_breast_screening/participants/models/symptom.py
@@ -58,6 +58,11 @@ class RelativeDateChoices(models.TextChoices):
NOT_SURE = "NOT_SURE", "Not sure"
+class HighlightToReaderChoices(models.TextChoices):
+ YES = "YES", "Yes, highlight to image readers"
+ NO = "NO", "No, just record information"
+
+
class Symptom(BaseModel):
symptom_type = models.ForeignKey(SymptomType, on_delete=models.PROTECT)
symptom_sub_type = models.ForeignKey(
@@ -89,6 +94,8 @@ class Symptom(BaseModel):
recently_resolved = models.BooleanField(null=False, default=False)
when_resolved = models.CharField(blank=True, null=False)
+ highlight_to_readers = models.BooleanField(null=False, default=True)
+
additional_information = models.CharField(blank=True, null=False)
@property
diff --git a/manage_breast_screening/participants/tests/factories.py b/manage_breast_screening/participants/tests/factories.py
index 7783182e7..fb222e85b 100644
--- a/manage_breast_screening/participants/tests/factories.py
+++ b/manage_breast_screening/participants/tests/factories.py
@@ -292,6 +292,7 @@ class Meta:
intermittent = False
investigated = False
recently_resolved = False
+ highlight_to_readers = True
appointment = SubFactory(AppointmentFactory)
area_description = LazyAttribute(
lambda o: "" if o.area == models.SymptomAreas.BOTH_BREASTS else "abc"
@@ -329,6 +330,8 @@ class Params:
symptom_type_id=models.SymptomType.OTHER, symptom_sub_type_details="abc"
)
+ breast_pain = Trait(symptom_type_id=models.SymptomType.BREAST_PAIN)
+
class HormoneReplacementTherapyFactory(DjangoModelFactory):
class Meta:
diff --git a/manage_breast_screening/tests/system/clinical/test_recording_symptoms.py b/manage_breast_screening/tests/system/clinical/test_recording_symptoms.py
index 6aae12aa2..8cf414855 100644
--- a/manage_breast_screening/tests/system/clinical/test_recording_symptoms.py
+++ b/manage_breast_screening/tests/system/clinical/test_recording_symptoms.py
@@ -173,7 +173,7 @@ def then_i_am_back_on_the_medical_information_page(self):
def and_the_lump_on_the_right_breast_is_listed(self):
key = self.page.locator(
- ".nhsuk-summary-list__key", has=self.page.get_by_text("Lump", exact=True)
+ ".nhsuk-summary-list__key", has=self.page.get_by_text("Lump")
)
row = self.page.locator(".nhsuk-summary-list__row").filter(has=key)
expect(row).to_contain_text("Right breast")
@@ -181,7 +181,7 @@ def and_the_lump_on_the_right_breast_is_listed(self):
def when_i_click_on_change(self):
key = self.page.locator(
".nhsuk-summary-list__key",
- has=self.page.get_by_text("Swelling or shape change", exact=True),
+ has=self.page.get_by_text("Swelling or shape change"),
)
row = self.page.locator(".nhsuk-summary-list__row").filter(has=key)
row.locator(".nhsuk-summary-list__actions").get_by_text(
@@ -194,7 +194,7 @@ def then_less_than_three_months_should_be_selected(self):
def and_i_see_three_months_to_a_year(self):
key = self.page.locator(
".nhsuk-summary-list__key",
- has=self.page.get_by_text("Swelling or shape change", exact=True),
+ has=self.page.get_by_text("Swelling or shape change"),
)
row = self.page.locator(".nhsuk-summary-list__row").filter(has=key)
expect(row).to_contain_text("3 months to a year")
@@ -207,7 +207,7 @@ def and_i_confirm_i_want_to_delete_the_symptom(self):
def and_the_lump_is_no_longer_listed(self):
locator = self.page.locator(
- ".nhsuk-summary-list__key", has=self.page.get_by_text("Lump", exact=True)
+ ".nhsuk-summary-list__key", has=self.page.get_by_text("Lump")
)
expect(locator).not_to_be_attached()