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
Expand Up @@ -179,7 +179,32 @@ class RecallChoices(TextChoices):
error_messages={"max_words": "Notes for reader must be 500 words or less"},
)

def __init__(self, *args, **kwargs):
def __init__(self, *args, instance=None, **kwargs):
if instance:
series_counts = instance.series_counts()
match instance.completeness:
case StudyCompleteness.INCOMPLETE:
should_recall = self.RecallChoices.TO_BE_RECALLED
case StudyCompleteness.PARTIAL:
should_recall = self.RecallChoices.PARTIAL_MAMMOGRAPHY
case _:
should_recall = None

kwargs["initial"] = {
"rmlo_count": series_counts.get(("MLO", "R"), 0),
"lmlo_count": series_counts.get(("MLO", "L"), 0),
"rcc_count": series_counts.get(("CC", "R"), 0),
"lcc_count": series_counts.get(("CC", "L"), 0),
"right_eklund_count": series_counts.get(("EKLUND", "R"), 0),
"left_eklund_count": series_counts.get(("EKLUND", "L"), 0),
"imperfect_but_best_possible": instance.imperfect_but_best_possible,
"not_all_mammograms_taken": bool(instance.reasons_incomplete),
"reasons_incomplete": instance.reasons_incomplete,
"reasons_incomplete_details": instance.reasons_incomplete_details,
"should_recall": should_recall,
"additional_details": instance.additional_details,
}

super().__init__(*args, **kwargs)

self.given_field_value("not_all_mammograms_taken", True).require_field(
Expand Down Expand Up @@ -226,7 +251,7 @@ def completeness(self):
return StudyCompleteness.COMPLETE

def save(self, study_service: StudyService, recall_service: RecallService):
study = study_service.create(
study = study_service.create_or_update(
additional_details=self.cleaned_data.get("additional_details", ""),
imperfect_but_best_possible=self.cleaned_data.get(
"imperfect_but_best_possible", False
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ class StandardImagesChoices(TextChoices):
)

def save(self, study_service):
if (
self.cleaned_data["standard_images"]
== self.StandardImagesChoices.YES_TWO_CC_AND_TWO_MLO
):
study_service.create_with_default_series()
else:
study_service.remove_existing_study_and_series()
match self.cleaned_data["standard_images"]:
case self.StandardImagesChoices.YES_TWO_CC_AND_TWO_MLO:
study_service.create_with_default_series()
case self.StandardImagesChoices.NO_IMAGES_TAKEN:
study_service.delete_if_exists()
case self.StandardImagesChoices.NO_ADD_ADDITIONAL:
# Preserve existing study data.
pass
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def test_save_with_yes_answer(mock_study_service):
mock_study_service.create_with_default_series.assert_called_once()


def test_save_with_no_answer(mock_study_service):
def test_save_with_no_add_additional_answer(mock_study_service):
form = RecordImagesTakenForm(
QueryDict(
urlencode(
Expand All @@ -67,4 +67,20 @@ def test_save_with_no_answer(mock_study_service):
form.is_valid()
form.save(mock_study_service)

mock_study_service.remove_existing_study_and_series.assert_called_once()
mock_study_service.delete_if_exists.assert_not_called()


def test_save_with_no_images_taken_answer(mock_study_service):
form = RecordImagesTakenForm(
QueryDict(
urlencode(
{
"standard_images": RecordImagesTakenForm.StandardImagesChoices.NO_IMAGES_TAKEN
}
)
)
)
form.is_valid()
form.save(mock_study_service)

mock_study_service.delete_if_exists.assert_called_once()
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
)
from manage_breast_screening.manual_images.models import (
IncompleteImagesReason,
Series,
StudyCompleteness,
)
from manage_breast_screening.manual_images.services import StudyService
from manage_breast_screening.manual_images.tests.factories import StudyFactory


@pytest.fixture
Expand Down Expand Up @@ -269,6 +271,41 @@ def test_not_all_mammograms_taken_missing_reason(self):
]
}

def test_initial(self, in_progress_appointment):
study = StudyFactory(
appointment=in_progress_appointment,
additional_details="important note",
completeness=StudyCompleteness.INCOMPLETE,
reasons_incomplete=[IncompleteImagesReason.TECHNICAL_ISSUES],
)
study.series_set.bulk_create(
[
Series(study=study, view_position="CC", laterality="L", count=1),
Series(study=study, view_position="CC", laterality="R", count=0),
Series(study=study, view_position="MLO", laterality="L", count=1),
Series(study=study, view_position="MLO", laterality="R", count=1),
]
)

form = AddImageDetailsForm(instance=study)

assert form.initial == {
"additional_details": "important note",
"imperfect_but_best_possible": False,
"lcc_count": 1,
"left_eklund_count": 0,
"lmlo_count": 1,
"not_all_mammograms_taken": True,
"rcc_count": 0,
"reasons_incomplete": [
IncompleteImagesReason.TECHNICAL_ISSUES,
],
"reasons_incomplete_details": "",
"right_eklund_count": 0,
"rmlo_count": 1,
"should_recall": AddImageDetailsForm.RecallChoices.TO_BE_RECALLED,
}

def _assert_series(
self, series, expected_view_position, expected_laterality, expected_count
):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ def get_context_data(self, **kwargs):
)
return context

def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["instance"] = getattr(self.appointment, "study", None)
return kwargs

@transaction.atomic
def form_valid(self, form):
study = form.save(
Expand Down
8 changes: 8 additions & 0 deletions manage_breast_screening/manual_images/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ def series_with_multiple_images(self):
),
)

def series_counts(self):
return {
(view_position, laterality): count
for view_position, laterality, count in self.series_set.values_list(
"view_position", "laterality", "count"
)
}


class Series(BaseModel):
study = models.ForeignKey(Study, on_delete=models.PROTECT)
Expand Down
29 changes: 18 additions & 11 deletions manage_breast_screening/manual_images/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __init__(self, appointment, current_user):

@transaction.atomic
def create_with_default_series(self):
self.remove_existing_study_and_series()
self.delete_if_exists()

study = Study.objects.create(appointment=self.appointment)
self.auditor.audit_create(study)
Expand All @@ -30,19 +30,20 @@ def create_with_default_series(self):
return study

@transaction.atomic
def create(
def create_or_update(
self,
series_data: list[dict],
**study_kwargs,
):
self.remove_existing_study_and_series()

study = Study.objects.create(
appointment=self.appointment,
**study_kwargs,
study, created = Study.objects.update_or_create(
appointment=self.appointment, defaults=study_kwargs
)
self.auditor.audit_create(study)
if created:
self.auditor.audit_create(study)
else:
self.auditor.audit_update(study)

self._delete_series()
series_set = [
Series(study=study, **data) for data in series_data if data["count"] != 0
]
Expand All @@ -52,12 +53,18 @@ def create(
return study

@transaction.atomic
def remove_existing_study_and_series(self):
def delete_if_exists(self):
if hasattr(self.appointment, "study"):
self._delete_series()

study = self.appointment.study
self.auditor.audit_delete(study)
study.delete()

def _delete_series(self):
if hasattr(self.appointment, "study"):
study = self.appointment.study
series = study.series_set.all()

self.auditor.audit_bulk_delete(series)
series.delete()
self.auditor.audit_delete(study)
study.delete()
10 changes: 5 additions & 5 deletions manage_breast_screening/manual_images/tests/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def test_create_with_default_series_when_existing_series(self, clinical_user):
appointment = AppointmentFactory()
service = StudyService(appointment=appointment, current_user=clinical_user)
study = service.create_with_default_series()
appointment.study.series_set.create(
appointment.study.series_set.update_or_create(
view_position="EKLUND", laterality="L", count=1
)

Expand Down Expand Up @@ -50,7 +50,7 @@ def test_create_with_given_counts_when_all_counts_greater_than_zero(
):
appointment = AppointmentFactory()
service = StudyService(appointment=appointment, current_user=clinical_user)
study = service.create(
study = service.create_or_update(
additional_details="Some details",
series_data=[
{"view_position": "CC", "laterality": "L", "count": 1},
Expand Down Expand Up @@ -79,7 +79,7 @@ def test_create_with_given_counts_when_all_counts_greater_than_zero(
def test_create_with_given_counts_when_some_counts_zero(self, clinical_user):
appointment = AppointmentFactory()
service = StudyService(appointment=appointment, current_user=clinical_user)
study = service.create(
study = service.create_or_update(
additional_details="Some other details",
series_data=[
{"view_position": "CC", "laterality": "L", "count": 0},
Expand All @@ -104,7 +104,7 @@ def test_create_with_given_series_when_existing_series(self, clinical_user):
appointment = AppointmentFactory()
service = StudyService(appointment=appointment, current_user=clinical_user)
study = service.create_with_default_series()
appointment.study.series_set.create(
appointment.study.series_set.update_or_create(
view_position="EKLUND", laterality="L", count=1
)

Expand All @@ -120,7 +120,7 @@ def test_create_with_given_series_when_existing_series(self, clinical_user):
ordered=False,
)

new_study = service.create(
new_study = service.create_or_update(
additional_details="Some more details",
series_data=[
{"view_position": "CC", "laterality": "L", "count": 7},
Expand Down