Skip to content

Commit cd4f323

Browse files
authored
Merge pull request #318 from NHSDigital/DTOSS-10643-holding-clinic-appointments
DTOSS 10643: do not save "holding clinic" appointments
2 parents 10eb021 + d3b1ed3 commit cd4f323

4 files changed

Lines changed: 59 additions & 27 deletions

File tree

manage_breast_screening/notifications/management/commands/create_appointments.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,26 @@ def handle(self, *args, **options):
3838
for blob in self.container_client.list_blobs(
3939
name_starts_with=options["date_str"]
4040
):
41-
blob_client = self.container_client.get_blob_client(blob)
41+
blob_client = self.container_client.get_blob_client(blob.name)
4242
blob_content = blob_client.download_blob(
4343
max_concurrency=1, encoding="ASCII"
4444
).readall()
4545

4646
data_frame = self.raw_data_to_data_frame(blob_content)
4747

4848
for idx, row in data_frame.iterrows():
49-
clinic, clinic_created = self.find_or_create_clinic(row)
50-
if clinic_created:
51-
self.stdout.write(f"{clinic} created")
52-
53-
appt, appt_created = self.update_or_create_appointment(row, clinic)
54-
if appt_created:
55-
self.stdout.write(f"{appt} created")
56-
else:
57-
self.stdout.write(f"{appt} updated")
49+
if row.get("Holding Clinic") != "Y":
50+
clinic, clinic_created = self.find_or_create_clinic(row)
51+
if clinic_created:
52+
self.stdout.write(f"{clinic} created")
53+
54+
appt, appt_created = self.update_or_create_appointment(
55+
row, clinic
56+
)
57+
if appt_created:
58+
self.stdout.write(f"{appt} created")
59+
else:
60+
self.stdout.write(f"{appt} updated")
5861

5962
self.stdout.write(f"Processed {len(data_frame)} rows from {blob.name}")
6063
except Exception as e:
@@ -71,7 +74,7 @@ def raw_data_to_data_frame(self, raw_data: str) -> pandas.DataFrame:
7174
skipfooter=1,
7275
)
7376

74-
def find_or_create_clinic(self, row: dict) -> tuple[Clinic, bool]:
77+
def find_or_create_clinic(self, row: pandas.Series) -> tuple[Clinic, bool]:
7578
return Clinic.objects.get_or_create(
7679
bso_code=row["BSO"],
7780
code=row["Clinic Code"],
@@ -90,7 +93,7 @@ def find_or_create_clinic(self, row: dict) -> tuple[Clinic, bool]:
9093
)
9194

9295
def update_or_create_appointment(
93-
self, row: dict, clinic: Clinic
96+
self, row: pandas.Series, clinic: Clinic
9497
) -> tuple[Appointment, bool]:
9598
defaults = {
9699
"batch_id": row["BatchID"],
@@ -131,7 +134,7 @@ def workflow_action_date_and_time(self, timestamp: str) -> datetime:
131134
dt = datetime.strptime(timestamp, "%Y%m%d-%H%M%S")
132135
return dt.replace(tzinfo=TZ_INFO)
133136

134-
def appointment_date_and_time(self, row: dict) -> datetime:
137+
def appointment_date_and_time(self, row: pandas.Series) -> datetime:
135138
dt = datetime.strptime(
136139
f"{row['Appt Date']} {row['Appt Time']}",
137140
"%Y%m%d %H%M",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"NBSSAPPT_HDR"|"00000013"|"20250128"|"170922"|"000003"
2+
"NBSSAPPT_FLDS"|"Sequence"|"BSO"|"Action"|"Clinic Code"|"Holding Clinic"|"Status"|"Attended Not Scr"|"Appointment ID"|"NHS Num"|"Episode Type"|"Episode Start"|"BatchID"|"Screen or Asses"|"Screen Appt num"|"Booked By"|"Cancelled By"|"Appt Date"|"Appt Time"|"Location"|"Clinic Name"|"Clinic Name (Let)"|"Clinic Address 1"|"Clinic Address 2"|"Clinic Address 3"|"Clinic Address 4"|"Clinic Address 5"|"Postcode"|"Action Timestamp"
3+
"NBSSAPPT_DATA"|"000001"|"KMK"|"C"|"BU003"|"N"|"C"|"N"|"BU003-67215-RA1-DN-Z2222-1"|"9449304424"|"S"|"2025-01-02"|"KMKS02441"|"S"|""|"C"|"H"|"20250110"|"0845"|"MKGH"|"BREAST CARE UNIT"|"BREAST CARE UNIT"|"BREAST CARE UNIT"|"MILTON KEYNES HOSPITAL"|"STANDING WAY"|"MILTON KEYNES"|"MK6 5LD"|"MK6 5LD"|"20250123-121433"
4+
"NBSSAPPT_DATA"|"000002"|"KMK"|"B"|"BU011"|"Y"|"B"|"N"|"BU011-67278-RA1-DN-X0000-4"|"9449306625"|"F"|"2025-01-28"|"KMK001326"|"S"|"1"|"H"|""|"20250314"|"1445"|"MKGH"|"BREAST CARE UNIT"|"BREAST CARE UNIT"|"BREAST CARE UNIT"|"MILTON KEYNES HOSPITAL"|"STANDING WAY"|"MILTON KEYNES"|"MK6 5LD"|"MK6 5LD"|"20250128-154003"
5+
"NBSSAPPT_END"|"00000013"|"20250128"|"17:09:22"|"000003"

manage_breast_screening/notifications/tests/integration/test_store_mesh_messages.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def test_retrieve_file(self, helpers):
4949
subject.handle()
5050

5151
for blob in self.container_client.list_blobs():
52-
blob_client = self.container_client.get_blob_client(blob)
52+
blob_client = self.container_client.get_blob_client(blob.name)
5353
blob_content = blob_client.download_blob(
5454
max_concurrency=1, encoding="ASCII"
5555
).readall()
@@ -63,7 +63,7 @@ def test_retrieve_file(self, helpers):
6363
with open(file_path) as test_file:
6464
assert test_file.read() == blob_content
6565

66-
self.container_client.delete_blob(blob)
66+
self.container_client.delete_blob(blob.name)
6767

6868
assert len(client.list_messages()) == 0
6969

manage_breast_screening/notifications/tests/management/commands/test_create_appointments.py

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
ClinicFactory,
1717
)
1818

19+
VALID_DATA_FILE = "ABC_20241202091221_APPT_106.dat"
20+
UPDATED_APPOINTMENT_FILE = "ABC_20241202091321_APPT_107.dat"
21+
HOLDING_CLINIC_APPOINTMENT_FILE = "ABC_20241202091421_APPT_108.dat"
22+
1923

2024
class TestCreateAppointments:
2125
def fixture_file_path(self, filename):
@@ -32,12 +36,10 @@ def test_handle_creates_records(self):
3236

3337
mock_container_client = PropertyMock(spec=ContainerClient)
3438
mock_blob = Mock(spec=BlobProperties)
35-
mock_blob.name = PropertyMock(
36-
return_value=f"{today_dirname}/ABC_20241202091221_APPT_106.dat"
37-
)
39+
mock_blob.name = PropertyMock(return_value=f"{today_dirname}/{VALID_DATA_FILE}")
3840
mock_container_client.list_blobs.return_value = [mock_blob]
3941
mock_container_client.get_blob_client().download_blob().readall.return_value = (
40-
open(self.fixture_file_path("ABC_20241202091221_APPT_106.dat")).read()
42+
open(self.fixture_file_path(VALID_DATA_FILE)).read()
4143
)
4244
subject.container_client = mock_container_client
4345

@@ -93,6 +95,32 @@ def test_handle_creates_records(self):
9395
assert appointments[1].clinic == clinics[1]
9496
assert appointments[2].clinic == clinics[1]
9597

98+
@pytest.mark.django_db
99+
def test_handles_holding_clinics(self):
100+
"""Test does not create appointments for valid NBSS data marked as a Holding Clinic"""
101+
today_dirname = datetime.today().strftime("%Y-%m-%d")
102+
103+
subject = Command()
104+
105+
mock_container_client = PropertyMock(spec=ContainerClient)
106+
mock_blob = Mock(spec=BlobProperties)
107+
mock_blob.name = PropertyMock(
108+
return_value=f"{today_dirname}/{HOLDING_CLINIC_APPOINTMENT_FILE}"
109+
)
110+
mock_container_client.list_blobs.return_value = [mock_blob]
111+
mock_container_client.get_blob_client().download_blob().readall.return_value = (
112+
open(self.fixture_file_path(HOLDING_CLINIC_APPOINTMENT_FILE)).read()
113+
)
114+
subject.container_client = mock_container_client
115+
116+
subject.handle(**{"date_str": today_dirname})
117+
118+
assert Clinic.objects.count() == 1
119+
assert Clinic.objects.filter(code="BU011").first() is None
120+
121+
assert Clinic.objects.count() == 1
122+
assert Appointment.objects.filter(nhs_number=9449306625).first() is None
123+
96124
@pytest.mark.django_db
97125
def test_handle_updates_records(self):
98126
"""Test Appointment record update from valid NBSS data stored in Azure storage blob"""
@@ -117,17 +145,15 @@ def test_handle_updates_records(self):
117145

118146
# Receive a cancellation for existing appointment
119147
today = datetime.now()
120-
raw_data = open(
121-
self.fixture_file_path("ABC_20241202091321_APPT_107.dat")
122-
).read()
148+
raw_data = open(self.fixture_file_path(UPDATED_APPOINTMENT_FILE)).read()
123149
today_dirname = today.strftime("%Y-%m-%d")
124150

125151
subject = Command()
126152

127153
mock_container_client = PropertyMock(spec=ContainerClient)
128154
mock_blob = Mock(spec=BlobProperties)
129155
mock_blob.name = PropertyMock(
130-
return_value=f"{today_dirname}/ABC_20241202091321_APPT_107.dat"
156+
return_value=f"{today_dirname}/{UPDATED_APPOINTMENT_FILE}"
131157
)
132158
mock_container_client.list_blobs.return_value = [mock_blob]
133159
mock_container_client.get_blob_client().download_blob().readall.return_value = (
@@ -155,12 +181,10 @@ def test_handle_accept_date_arg(self):
155181

156182
mock_container_client = PropertyMock(spec=ContainerClient)
157183
mock_blob = Mock(spec=BlobProperties)
158-
mock_blob.name = PropertyMock(
159-
return_value="2025-07-01/ABC_20241202091221_APPT_106.dat"
160-
)
184+
mock_blob.name = PropertyMock(return_value=f"2025-07-01/{VALID_DATA_FILE}")
161185
mock_container_client.list_blobs.return_value = [mock_blob]
162186
mock_container_client.get_blob_client().download_blob().readall.return_value = (
163-
open(self.fixture_file_path("ABC_20241202091221_APPT_106.dat")).read()
187+
open(self.fixture_file_path(VALID_DATA_FILE)).read()
164188
)
165189
subject.container_client = mock_container_client
166190

0 commit comments

Comments
 (0)