Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
from django.forms import Textarea

from manage_breast_screening.core.services.auditor import Auditor
from manage_breast_screening.nhsuk_forms.fields.char_field import CharField
from manage_breast_screening.nhsuk_forms.fields.choice_fields import (
ChoiceField,
MultipleChoiceField,
)
from manage_breast_screening.nhsuk_forms.fields.integer_field import YearField
from manage_breast_screening.nhsuk_forms.forms import FormWithConditionalFields
from manage_breast_screening.participants.models.benign_lump_history_item import (
BenignLumpHistoryItem,
)


class BenignLumpHistoryItemForm(FormWithConditionalFields):
LOCATION_DETAIL_FIELDS = {
BenignLumpHistoryItem.ProcedureLocation.NHS_HOSPITAL: "nhs_hospital_details",
BenignLumpHistoryItem.ProcedureLocation.PRIVATE_CLINIC_UK: "private_clinic_uk_details",
BenignLumpHistoryItem.ProcedureLocation.OUTSIDE_UK: "outside_uk_details",
BenignLumpHistoryItem.ProcedureLocation.MULTIPLE_LOCATIONS: "multiple_locations_details",
BenignLumpHistoryItem.ProcedureLocation.EXACT_LOCATION_UNKNOWN: "exact_location_unknown_details",
}

left_breast_procedures = MultipleChoiceField(
label="Left breast",
label_classes="nhsuk-fieldset__legend--s",
visually_hidden_label_prefix="What procedure have they had in their ",
visually_hidden_label_suffix="?",
choices=BenignLumpHistoryItem.Procedure,
exclusive_choices={"NO_PROCEDURES"},
error_messages={
"required": "Select which procedures they have had in the left breast",
},
)
right_breast_procedures = MultipleChoiceField(
label="Right breast",
label_classes="nhsuk-fieldset__legend--s",
visually_hidden_label_prefix="What procedure have they had in their ",
visually_hidden_label_suffix="?",
choices=BenignLumpHistoryItem.Procedure,
exclusive_choices={"NO_PROCEDURES"},
error_messages={
"required": "Select which procedures they have had in the right breast",
},
)

procedure_year = YearField(
label="Year of procedure (optional)",
label_classes="nhsuk-label--m",
classes="nhsuk-input--width-4",
hint="Leave blank if unknown",
required=False,
)

procedure_location = ChoiceField(
label="Where were the tests and treatment done?",
choices=BenignLumpHistoryItem.ProcedureLocation,
error_messages={
"required": "Select where the tests and treatment were done",
},
)
nhs_hospital_details = CharField(
label="Provide details",
required=False,
error_messages={
"required": "Provide details about where the surgery and treatment took place"
},
)
private_clinic_uk_details = CharField(
label="Provide details",
required=False,
error_messages={
"required": "Provide details about where the surgery and treatment took place"
},
)
outside_uk_details = CharField(
label="Provide details",
required=False,
error_messages={
"required": "Provide details about where the surgery and treatment took place"
},
)
multiple_locations_details = CharField(
label="Provide details",
required=False,
error_messages={
"required": "Provide details about where the surgery and treatment took place"
},
)
exact_location_unknown_details = CharField(
label="Provide details",
required=False,
error_messages={
"required": "Provide details about where the surgery and treatment took place"
},
)

additional_details = CharField(
label="Additional details (optional)",
label_classes="nhsuk-label--m",
required=False,
widget=Textarea(attrs={"rows": 3}),
hint="Include any other relevant information about the procedure (optional)",
)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

for location, detail_field in self.LOCATION_DETAIL_FIELDS.items():
self.given_field_value("procedure_location", location).require_field(
detail_field
)

def create(self, appointment, request):
auditor = Auditor.from_request(request)

benign_lump_history_item = BenignLumpHistoryItem.objects.create(
appointment=appointment,
left_breast_procedures=self.cleaned_data.get("left_breast_procedures", []),
right_breast_procedures=self.cleaned_data.get(
"right_breast_procedures", []
),
procedure_year=self.cleaned_data.get("procedure_year"),
procedure_location=self.cleaned_data["procedure_location"],
procedure_location_details=self._get_selected_location_details(),
additional_details=self.cleaned_data.get("additional_details", ""),
)

auditor.audit_create(benign_lump_history_item)

return benign_lump_history_item

@property
def location_detail_fields(self):
return tuple(self.LOCATION_DETAIL_FIELDS.items())

def _get_selected_location_details(self):
location = self.cleaned_data.get("procedure_location")
try:
detail_field = self.LOCATION_DETAIL_FIELDS[location]
except KeyError as exc:
msg = f"Unsupported procedure location '{location}'"
raise ValueError(msg) from exc
return self.cleaned_data.get(detail_field, "")
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{% extends "layout-form.jinja" %}
{% from "nhsuk/components/button/macro.jinja" import button %}
{% from "nhsuk/components/fieldset/macro.jinja" import fieldset %}

{% block form %}

{% do form.left_breast_procedures.add_divider_after("LUMP_REMOVED", "or") %}
{% do form.right_breast_procedures.add_divider_after("LUMP_REMOVED", "or") %}

<h2 aria-hidden="true">
What benign lump procedures has {{ participant_first_name }} had?
</h2>

<div class="nhsuk-grid-row">
<div class="nhsuk-grid-column-one-half">
{{ form.right_breast_procedures.as_field_group() }}
</div>
<div class="nhsuk-grid-column-one-half">
{{ form.left_breast_procedures.as_field_group() }}
</div>
</div>

{{ form.procedure_year.as_field_group() }}

{% for location_value, detail_field_name in form.location_detail_fields %}
{% set detail_field = form[detail_field_name] %}
{% do form.procedure_location.add_conditional_html(
location_value,
detail_field.as_field_group()
) %}
{% endfor %}
{{ form.procedure_location.as_field_group() }}

{{ form.additional_details.as_field_group() }}

<div class="nhsuk-button-group">
{{ button({
"text": "Save"
}) }}
</div>

{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
{% for presented_item in presenter.benign_lump_history %}
{{ summaryList(presented_item.summary_list_params) }}
{% endfor %}
<a href="{{ presenter.add_benign_lump_history_link.href }}" class="nhsuk-link nhsuk-link--no-visited-state">{{ presenter.add_benign_lump_history_link.text }}</a><br>
{% endset %}
{% set chest_procedures_link %}
<a class="nhsuk-link" href="#">Enter other breast or chest procedures</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,15 @@ def add_breast_augmentation_history_link(self):
"href": url,
"text": ("Add breast augmentation history"),
}

@property
def add_benign_lump_history_link(self):
url = reverse(
"mammograms:add_benign_lump_history_item",
kwargs={"pk": self.appointment.pk},
)

return {
"href": url,
"text": "Add benign lump history",
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import datetime
from urllib.parse import urlencode

import pytest
from django.http import QueryDict
from django.test import RequestFactory

from manage_breast_screening.core.models import AuditLog
from manage_breast_screening.mammograms.forms.benign_lump_history_item_form import (
BenignLumpHistoryItemForm,
)
from manage_breast_screening.participants.models.benign_lump_history_item import (
BenignLumpHistoryItem,
)
from manage_breast_screening.participants.tests.factories import AppointmentFactory


def _form_data(data):
return QueryDict(urlencode(data, doseq=True))


@pytest.mark.django_db
class TestBenignLumpHistoryItemForm:
def test_missing_required_fields(self):
form = BenignLumpHistoryItemForm(_form_data({}))

assert not form.is_valid()
assert form.errors == {
"left_breast_procedures": [
"Select which procedures they have had in the left breast"
],
"right_breast_procedures": [
"Select which procedures they have had in the right breast"
],
"procedure_location": ["Select where the tests and treatment were done"],
}

@pytest.mark.parametrize(
("location", "detail_field"),
tuple(BenignLumpHistoryItemForm.LOCATION_DETAIL_FIELDS.items()),
)
def test_requires_location_details_for_selected_location(
self, location, detail_field
):
form = BenignLumpHistoryItemForm(
_form_data(
[
(
"procedure_location",
location,
),
]
)
)

assert not form.is_valid()
assert form.errors.get(detail_field) == [
"Provide details about where the surgery and treatment took place"
]

@pytest.mark.parametrize(
("procedure_year", "expected_message"),
[
(
datetime.date.today().year - 80 - 1,
f"Year must be {datetime.date.today().year - 80} or later",
),
(
datetime.date.today().year + 1,
f"Year must be {datetime.date.today().year} or earlier",
),
],
)
def test_procedure_year_must_be_within_range(
self, procedure_year, expected_message
):
form = BenignLumpHistoryItemForm(
_form_data(
[
("procedure_year", procedure_year),
]
)
)

assert not form.is_valid()
assert form.errors.get("procedure_year") == [expected_message]

def test_create_persists_data_and_audits(self, clinical_user):
appointment = AppointmentFactory()
request = RequestFactory().post("/test-form")
request.user = clinical_user

data = [
(
"left_breast_procedures",
BenignLumpHistoryItem.Procedure.NEEDLE_BIOPSY,
),
(
"right_breast_procedures",
BenignLumpHistoryItem.Procedure.NO_PROCEDURES,
),
("procedure_year", datetime.date.today().year - 1),
(
"procedure_location",
BenignLumpHistoryItem.ProcedureLocation.NHS_HOSPITAL,
),
("nhs_hospital_details", "St Thomas' Hospital"),
("additional_details", "Additional details."),
]
form = BenignLumpHistoryItemForm(_form_data(data))
assert form.is_valid()

existing_log_count = AuditLog.objects.count()
obj = form.create(appointment=appointment, request=request)
assert AuditLog.objects.count() == existing_log_count + 1
audit_log = AuditLog.objects.filter(
object_id=obj.pk, operation=AuditLog.Operations.CREATE
).first()
assert audit_log.actor == clinical_user

obj.refresh_from_db()
assert obj.appointment == appointment
assert obj.left_breast_procedures == [
BenignLumpHistoryItem.Procedure.NEEDLE_BIOPSY
]
assert obj.right_breast_procedures == [
BenignLumpHistoryItem.Procedure.NO_PROCEDURES
]
assert obj.procedure_year == data[2][1]
assert obj.procedure_location == (
BenignLumpHistoryItem.ProcedureLocation.NHS_HOSPITAL
)
assert obj.procedure_location_details == "St Thomas' Hospital"
assert obj.additional_details == "Additional details."
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,13 @@ def test_breast_augmentation_history_link(self):
"href": f"/mammograms/{appointment.pk}/record-medical-information/breast-augmentation-history/",
"text": "Add breast augmentation history",
}

def test_add_benign_lump_history_link(self):
appointment = AppointmentFactory()

assert MedicalInformationPresenter(
appointment
).add_benign_lump_history_link == {
"href": f"/mammograms/{appointment.pk}/record-medical-information/benign-lump-history/",
"text": "Add benign lump history",
}
6 changes: 6 additions & 0 deletions manage_breast_screening/mammograms/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from .views import (
appointment_views,
benign_lump_history_item_views,
breast_augmentation_history_view,
cyst_history_view,
implanted_medical_device_history_view,
Expand Down Expand Up @@ -137,4 +138,9 @@
breast_augmentation_history_view.AddBreastAugmentationHistoryView.as_view(),
name="add_breast_augmentation_history_item",
),
path(
"<uuid:pk>/record-medical-information/benign-lump-history/",
benign_lump_history_item_views.AddBenignLumpHistoryItemView.as_view(),
name="add_benign_lump_history_item",
),
]
Loading