Skip to content

Commit 61bdf2c

Browse files
authored
Merge pull request #872 from NHSDigital/DTOSS-11856-mastectomy-lumpectomy-card
Medical information: presenter updates to better align with prototype
2 parents 019e75c + ec71eb5 commit 61bdf2c

16 files changed

+319
-80
lines changed

manage_breast_screening/core/utils/date_formatting.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,42 @@ def format_relative_date(value: datetime | date):
4444
return f"in {amount}"
4545

4646

47+
def format_relative_year(value: int | None):
48+
"""
49+
Format a year value relative to the current year.
50+
51+
Use it for approximate dates where only the year is known.
52+
"""
53+
if value is None or not 1900 < value < 2100:
54+
return ""
55+
56+
year_diff = date.today().year - value
57+
if year_diff == 0:
58+
return "this year"
59+
elif year_diff == 1:
60+
return "last year"
61+
elif year_diff == -1:
62+
return "next year"
63+
elif year_diff > 1:
64+
return f"{year_diff} years ago"
65+
elif year_diff < 1:
66+
return f"in {abs(year_diff)} years"
67+
68+
69+
def format_year_with_relative(value: int | None):
70+
"""
71+
Format a year with the relative number of years in brackets
72+
"""
73+
if value is None:
74+
return ""
75+
76+
relative = format_relative_year(value)
77+
if relative:
78+
return f"{value} ({relative})"
79+
else:
80+
return str(value)
81+
82+
4783
def _format_date_difference(date1, date2):
4884
diff = relativedelta(date1, date2) if date1 > date2 else relativedelta(date2, date1)
4985

manage_breast_screening/core/utils/tests/test_date_formatting.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44
import pytest
55
import time_machine
66

7-
from ..date_formatting import format_approximate_date, format_relative_date
7+
from ..date_formatting import (
8+
format_approximate_date,
9+
format_relative_date,
10+
format_relative_year,
11+
format_year_with_relative,
12+
)
813

914

1015
@time_machine.travel(datetime(2025, 5, 2, 10, tzinfo=ZoneInfo("Europe/London")))
@@ -27,6 +32,29 @@ def test_relative_dates(dateiso, output):
2732
assert format_relative_date(datetime.fromisoformat(dateiso)) == output
2833

2934

35+
@time_machine.travel(datetime(2025, 5, 2, 10, tzinfo=ZoneInfo("Europe/London")))
36+
@pytest.mark.parametrize(
37+
("year", "output"),
38+
(
39+
(2025, "this year"),
40+
(2026, "next year"),
41+
(2027, "in 2 years"),
42+
(2024, "last year"),
43+
(2015, "10 years ago"),
44+
(None, ""),
45+
),
46+
)
47+
def test_relative_years(year, output):
48+
assert format_relative_year(year) == output
49+
50+
51+
@time_machine.travel(datetime(2025, 5, 2, 10, tzinfo=ZoneInfo("Europe/London")))
52+
def test_year_with_relative():
53+
assert format_year_with_relative(2000) == "2000 (25 years ago)"
54+
assert format_year_with_relative(1066) == "1066"
55+
assert format_year_with_relative(None) == ""
56+
57+
3058
@pytest.mark.parametrize(
3159
("today", "year", "month", "output"),
3260
(

manage_breast_screening/mammograms/jinja2/mammograms/medical_information/medical_history/cards/breast_cancer_history_card.jinja

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@
3131
"html": paragraph(presented_item.cancer_location)
3232
}
3333
},
34+
{
35+
"key": "Diagnosis year",
36+
"value": {
37+
"html": paragraph(presented_item.diagnosis_year)
38+
}
39+
} if presented_item.diagnosis_year,
3440
{
3541
"key": "Procedures",
3642
"value": {

manage_breast_screening/mammograms/presenters/medical_history/benign_lump_history_item_presenter.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from django.urls import reverse
22

33
from manage_breast_screening.core.template_helpers import multiline_content, nl2br
4+
from manage_breast_screening.core.utils.date_formatting import format_year_with_relative
45
from manage_breast_screening.participants.models.medical_history.benign_lump_history_item import (
56
BenignLumpHistoryItem,
67
)
@@ -10,13 +11,17 @@ class BenignLumpHistoryItemPresenter:
1011
def __init__(self, benign_lump_history_item, counter=None):
1112
self._item = benign_lump_history_item
1213
self.counter = counter
13-
self.right_breast_procedures = self._format_multiple_choices(
14-
self._item.right_breast_procedures, BenignLumpHistoryItem.Procedure
15-
)
16-
self.left_breast_procedures = self._format_multiple_choices(
17-
self._item.left_breast_procedures, BenignLumpHistoryItem.Procedure
18-
)
19-
self.procedure_year = str(self._item.procedure_year)
14+
15+
self.right_breast_procedures = [
16+
BenignLumpHistoryItem.Procedure(choice).label
17+
for choice in self._item.right_breast_procedures
18+
]
19+
self.left_breast_procedures = [
20+
BenignLumpHistoryItem.Procedure(choice).label
21+
for choice in self._item.left_breast_procedures
22+
]
23+
24+
self.procedure_year = format_year_with_relative(self._item.procedure_year)
2025
self.procedure_location = self._item.get_procedure_location_display()
2126
self.procedure_location_details = self._item.procedure_location_details
2227
self.additional_details = nl2br(self._item.additional_details)
@@ -30,8 +35,8 @@ def summary_list_params(self):
3035
"value": {
3136
"html": multiline_content(
3237
[
33-
f"Right breast: {self.right_breast_procedures}",
34-
f"Left breast: {self.left_breast_procedures}",
38+
f"Right breast: {', '.join(self.right_breast_procedures)}",
39+
f"Left breast: {', '.join(self.left_breast_procedures)}",
3540
]
3641
)
3742
},
@@ -53,6 +58,13 @@ def summary_list_params(self):
5358
]
5459
}
5560

61+
@property
62+
def treatment_location(self):
63+
return {
64+
"type": self._item.get_procedure_location_display(),
65+
"details": self._item.procedure_location_details,
66+
}
67+
5668
@property
5769
def change_link(self):
5870
return {
@@ -70,6 +82,3 @@ def change_link(self):
7082
else " benign lump item"
7183
),
7284
}
73-
74-
def _format_multiple_choices(self, choices, ChoiceClass):
75-
return ", ".join(ChoiceClass(choice).label for choice in choices)

manage_breast_screening/mammograms/presenters/medical_history/breast_augmentation_history_item_presenter.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from django.urls import reverse
22

33
from manage_breast_screening.core.template_helpers import multiline_content, nl2br
4+
from manage_breast_screening.core.utils.date_formatting import format_year_with_relative
45
from manage_breast_screening.participants.models.medical_history.breast_augmentation_history_item import (
56
BreastAugmentationHistoryItem,
67
)
@@ -11,13 +12,16 @@ def __init__(self, breast_augmentation_history_item, counter=None):
1112
self._item = breast_augmentation_history_item
1213
self.counter = counter
1314

14-
self.right_breast_procedures = self._format_multiple_choices(
15-
self._item.right_breast_procedures, BreastAugmentationHistoryItem.Procedure
16-
)
17-
self.left_breast_procedures = self._format_multiple_choices(
18-
self._item.left_breast_procedures, BreastAugmentationHistoryItem.Procedure
19-
)
20-
self.procedure_year = str(self._item.procedure_year)
15+
self.right_breast_procedures = [
16+
BreastAugmentationHistoryItem.Procedure(choice).label
17+
for choice in self._item.right_breast_procedures
18+
]
19+
self.left_breast_procedures = [
20+
BreastAugmentationHistoryItem.Procedure(choice).label
21+
for choice in self._item.left_breast_procedures
22+
]
23+
24+
self.procedure_year = format_year_with_relative(self._item.procedure_year)
2125
self.implants_have_been_removed = (
2226
"Yes" if self._item.implants_have_been_removed else "No"
2327
)
@@ -31,8 +35,8 @@ def summary_list_params(self):
3135
# This is a placeholder until we have a properly formatted table.
3236

3337
procedures = [
34-
f"Right breast: {self.right_breast_procedures}",
35-
f"Left breast: {self.left_breast_procedures}",
38+
f"Right breast: {', '.join(self.right_breast_procedures)}",
39+
f"Left breast: {', '.join(self.left_breast_procedures)}",
3640
]
3741

3842
return {
@@ -58,9 +62,6 @@ def summary_list_params(self):
5862
],
5963
}
6064

61-
def _format_multiple_choices(self, choices, ChoiceClass):
62-
return ", ".join(ChoiceClass(choice).label for choice in choices)
63-
6465
@property
6566
def change_link(self):
6667
return {

manage_breast_screening/mammograms/presenters/medical_history/breast_cancer_history_item_presenter.py

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from django.urls import reverse
22

33
from manage_breast_screening.core.template_helpers import nl2br
4+
from manage_breast_screening.core.utils.date_formatting import format_year_with_relative
45
from manage_breast_screening.participants.models.medical_history.breast_cancer_history_item import (
56
BreastCancerHistoryItem,
67
)
@@ -16,26 +17,32 @@ def __init__(self, breast_cancer_history_item, counter=None):
1617
self.right_breast_procedure = self._item.get_right_breast_procedure_display()
1718
self.left_breast_procedure = self._item.get_left_breast_procedure_display()
1819

19-
self.right_breast_other_surgery = self._format_multiple_choices(
20-
self._item.right_breast_other_surgery, BreastCancerHistoryItem.Surgery
21-
)
22-
self.left_breast_other_surgery = self._format_multiple_choices(
23-
self._item.left_breast_other_surgery, BreastCancerHistoryItem.Surgery
24-
)
25-
self.right_breast_treatments = self._format_multiple_choices(
26-
self._item.right_breast_treatment, BreastCancerHistoryItem.Treatment
27-
)
28-
self.left_breast_treatments = self._format_multiple_choices(
29-
self._item.left_breast_treatment, BreastCancerHistoryItem.Treatment
30-
)
31-
self.systemic_treatments = self._format_multiple_choices(
32-
self._item.systemic_treatments,
33-
BreastCancerHistoryItem.SystemicTreatment,
34-
)
35-
self.additional_details = nl2br(self._item.additional_details)
20+
self.diagnosis_year = format_year_with_relative(self._item.diagnosis_year)
21+
22+
self.right_breast_other_surgery = [
23+
BreastCancerHistoryItem.Surgery(choice).label
24+
for choice in self._item.right_breast_other_surgery
25+
]
26+
self.left_breast_other_surgery = [
27+
BreastCancerHistoryItem.Surgery(choice).label
28+
for choice in self._item.left_breast_other_surgery
29+
]
30+
31+
self.right_breast_treatments = [
32+
BreastCancerHistoryItem.Treatment(choice).label
33+
for choice in self._item.right_breast_treatment
34+
]
35+
self.left_breast_treatments = [
36+
BreastCancerHistoryItem.Treatment(choice).label
37+
for choice in self._item.left_breast_treatment
38+
]
3639

37-
def _format_multiple_choices(self, choices, ChoiceClass):
38-
return [ChoiceClass(choice).label for choice in choices]
40+
self.systemic_treatments = [
41+
BreastCancerHistoryItem.SystemicTreatment(choice).label
42+
for choice in self._item.systemic_treatments
43+
]
44+
45+
self.additional_details = nl2br(self._item.additional_details)
3946

4047
@property
4148
def change_link(self):

manage_breast_screening/mammograms/presenters/medical_history/implanted_medical_device_history_item_presenter.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
from django.urls import reverse
22

3-
from manage_breast_screening.core.template_helpers import (
4-
nl2br,
3+
from manage_breast_screening.core.template_helpers import multiline_content, nl2br
4+
from manage_breast_screening.core.utils.date_formatting import (
5+
format_year_with_relative,
6+
)
7+
from manage_breast_screening.participants.models.medical_history.implanted_medical_device_history_item import (
8+
ImplantedMedicalDeviceHistoryItem,
59
)
610

711

@@ -17,14 +21,32 @@ def __init__(self, implanted_medical_device_history_item, counter=None):
1721
self.other_medical_device_details = (
1822
self._item.other_medical_device_details or "N/A"
1923
)
20-
self.procedure_year = str(self._item.procedure_year)
24+
2125
self.device_has_been_removed = (
2226
"Yes" if self._item.device_has_been_removed else "No"
2327
)
2428
if self._item.device_has_been_removed and self._item.removal_year:
2529
self.device_has_been_removed += f" ({self._item.removal_year})"
2630
self.additional_details = nl2br(self._item.additional_details)
2731

32+
@property
33+
def procedure_year(self):
34+
return format_year_with_relative(self._item.procedure_year)
35+
36+
@property
37+
def removal_year(self):
38+
return format_year_with_relative(self._item.removal_year)
39+
40+
@property
41+
def procedure_year_with_removal(self):
42+
lines = [f"Implanted in {self.procedure_year}"]
43+
if self._item.removal_year:
44+
lines.append(f"Device removed in {self.removal_year}")
45+
elif self._item.device_has_been_removed:
46+
lines.append("Device removed")
47+
48+
return multiline_content(lines)
49+
2850
@property
2951
def summary_list_params(self):
3052
# This is a placeholder until we have a properly formatted table.
@@ -55,6 +77,19 @@ def summary_list_params(self):
5577
],
5678
}
5779

80+
@property
81+
def type(self):
82+
device = self._item.get_device_display()
83+
details = self._item.other_medical_device_details
84+
if (
85+
self._item.device
86+
== ImplantedMedicalDeviceHistoryItem.Device.OTHER_MEDICAL_DEVICE
87+
and details
88+
):
89+
return f"Other medical device: {details}"
90+
else:
91+
return device
92+
5893
@property
5994
def change_link(self):
6095
return {

manage_breast_screening/mammograms/presenters/medical_history/mastectomy_or_lumpectomy_history_item_presenter.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from django.urls import reverse
22

33
from manage_breast_screening.core.template_helpers import multiline_content, nl2br
4+
from manage_breast_screening.core.utils.date_formatting import format_year_with_relative
45
from manage_breast_screening.participants.models.medical_history.mastectomy_or_lumpectomy_history_item import (
56
MastectomyOrLumpectomyHistoryItem,
67
)
@@ -17,16 +18,17 @@ def __init__(self, mastectomy_or_lumpectomy_history_item, counter=None):
1718
self.right_breast_procedure = self._item.get_right_breast_procedure_display()
1819
self.left_breast_procedure = self._item.get_left_breast_procedure_display()
1920

20-
self.right_breast_other_surgery = self._format_multiple_choices(
21-
self._item.right_breast_other_surgery,
22-
MastectomyOrLumpectomyHistoryItem.Surgery,
23-
)
24-
self.left_breast_other_surgery = self._format_multiple_choices(
25-
self._item.left_breast_other_surgery,
26-
MastectomyOrLumpectomyHistoryItem.Surgery,
27-
)
21+
self.right_breast_other_surgery = [
22+
MastectomyOrLumpectomyHistoryItem.Surgery(choice).label
23+
for choice in self._item.right_breast_other_surgery
24+
]
25+
self.left_breast_other_surgery = [
26+
MastectomyOrLumpectomyHistoryItem.Surgery(choice).label
27+
for choice in self._item.left_breast_other_surgery
28+
]
29+
2830
self.year_of_surgery = (
29-
str(self._item.year_of_surgery)
31+
format_year_with_relative(self._item.year_of_surgery)
3032
if self._item.year_of_surgery
3133
else "Not specified"
3234
)
@@ -46,9 +48,6 @@ def surgery_reason(self):
4648
else:
4749
return reason
4850

49-
def _format_multiple_choices(self, choices, ChoiceClass):
50-
return [ChoiceClass(choice).label for choice in choices]
51-
5251
@property
5352
def change_link(self):
5453
return {

0 commit comments

Comments
 (0)