Skip to content

Commit 8926345

Browse files
authored
Merge pull request #383 from NHSDigital/send-notifications-on-working-days
Only send notifications on working days
2 parents 8bc191c + 6f03de0 commit 8926345

File tree

4 files changed

+539
-493
lines changed

4 files changed

+539
-493
lines changed

manage_breast_screening/notifications/management/commands/send_message_batch.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from datetime import datetime, timedelta
22
from zoneinfo import ZoneInfo
33

4+
from business.calendar import Calendar
45
from django.core.management.base import BaseCommand, CommandError
56

67
from manage_breast_screening.notifications.management.commands.helpers.message_batch_helpers import (
@@ -28,6 +29,9 @@ class Command(BaseCommand):
2829

2930
def handle(self, *args, **options):
3031
try:
32+
if not self.bso_working_day():
33+
return
34+
3135
for routing_plan in RoutingPlan.all():
3236
self.stdout.write(
3337
f"Finding appointments of episode type {routing_plan.episode_types} to include in batch."
@@ -46,7 +50,6 @@ def handle(self, *args, **options):
4650
)
4751
continue
4852

49-
5053
self.stdout.write(
5154
f"Found {appointments.count()} appointments to batch."
5255
)
@@ -81,8 +84,11 @@ def handle(self, *args, **options):
8184
except Exception as e:
8285
raise CommandError(e)
8386

87+
def bso_working_day(self):
88+
return Calendar().is_business_day(datetime.now(tz=TZ_INFO))
89+
8490
def schedule_date(self) -> datetime:
8591
today = datetime.now(tz=TZ_INFO)
8692
today_end = today.replace(hour=23, minute=59, second=59, microsecond=999999)
8793

88-
return today_end + timedelta(weeks=4)
94+
return today_end + timedelta(weeks=4)

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

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import pytest
66
import requests
7+
from dateutil import relativedelta
78

89
from manage_breast_screening.notifications.management.commands.helpers.message_batch_helpers import (
910
MessageBatchHelpers,
@@ -25,8 +26,9 @@
2526
ApiClient, "send_message_batch", return_value=MagicMock(spec=requests.Response)
2627
)
2728
@patch.object(MessageBatchHelpers, "mark_batch_as_sent")
29+
@pytest.mark.time_machine(datetime.today() + relativedelta.relativedelta(weekday=0))
30+
@pytest.mark.django_db
2831
class TestSendMessageBatch:
29-
@pytest.mark.django_db
3032
def test_handle_with_a_batch_to_send(
3133
self, mock_mark_batch_as_sent, mock_send_message_batch
3234
):
@@ -48,7 +50,6 @@ def test_handle_with_a_batch_to_send(
4850
)
4951
mock_mark_batch_as_sent.assert_called_once_with(message_batches[0], ANY)
5052

51-
@pytest.mark.django_db
5253
def test_handle_for_all_routing_plans(
5354
self, mock_mark_batch_as_sent, mock_send_message_batch
5455
):
@@ -78,7 +79,6 @@ def test_handle_for_all_routing_plans(
7879
mock_mark_batch_as_sent.assert_any_call(message_batches[0], ANY)
7980
mock_mark_batch_as_sent.assert_any_call(message_batches[1], ANY)
8081

81-
@pytest.mark.django_db
8282
def test_handle_with_nothing_to_send(
8383
self, mock_mark_batch_as_sent, mock_send_message_batch
8484
):
@@ -88,7 +88,6 @@ def test_handle_with_nothing_to_send(
8888
assert MessageBatch.objects.count() == 0
8989
assert Message.objects.count() == 0
9090

91-
@pytest.mark.django_db
9291
def test_handle_with_appointments_inside_schedule_window(
9392
self,
9493
mock_mark_batch_as_sent,
@@ -98,7 +97,7 @@ def test_handle_with_appointments_inside_schedule_window(
9897
"""Test that appointments with date inside the schedule period are notified"""
9998
mock_send_message_batch.return_value.status_code = 201
10099
appointment = AppointmentFactory(
101-
starts_at=datetime.now().replace(tzinfo=TZ_INFO) + timedelta(weeks=4)
100+
starts_at=datetime.now(tz=TZ_INFO) + timedelta(weeks=4)
102101
)
103102
routing_plan_id = RoutingPlan.for_episode_type(appointment.episode_type).id
104103

@@ -114,7 +113,6 @@ def test_handle_with_appointments_inside_schedule_window(
114113
)
115114
mock_mark_batch_as_sent.assert_called_once_with(message_batches[0], ANY)
116115

117-
@pytest.mark.django_db
118116
def test_handle_with_appointments_outside_schedule_window(
119117
self,
120118
mock_mark_batch_as_sent,
@@ -123,8 +121,7 @@ def test_handle_with_appointments_outside_schedule_window(
123121
):
124122
"""Test that appointments with date inside the schedule period are notified"""
125123
AppointmentFactory(
126-
starts_at=datetime.now().replace(tzinfo=TZ_INFO)
127-
+ timedelta(weeks=4, days=1)
124+
starts_at=datetime.now(tz=TZ_INFO) + timedelta(weeks=4, days=1)
128125
)
129126

130127
Command().handle()
@@ -133,7 +130,6 @@ def test_handle_with_appointments_outside_schedule_window(
133130
assert Message.objects.count() == 0
134131
mock_mark_batch_as_sent.assert_not_called()
135132

136-
@pytest.mark.django_db
137133
def test_handle_with_cancelled_appointments(
138134
self,
139135
mock_mark_batch_as_sent,
@@ -143,15 +139,13 @@ def test_handle_with_cancelled_appointments(
143139
"""Test that that cancelled appointments are not notified"""
144140
mock_send_message_batch.return_value.status_code = 201
145141

146-
valid_appointment = AppointmentFactory(
147-
starts_at=datetime.today().replace(tzinfo=TZ_INFO)
148-
)
142+
valid_appointment = AppointmentFactory(starts_at=datetime.now(tz=TZ_INFO))
149143
routing_plan_id = RoutingPlan.for_episode_type(
150144
valid_appointment.episode_type
151145
).id
152146

153147
_cancelled_appointment = AppointmentFactory(
154-
starts_at=datetime.today().replace(tzinfo=TZ_INFO), status="C"
148+
starts_at=datetime.now(tz=TZ_INFO), status="C"
155149
)
156150

157151
Command().handle()
@@ -163,7 +157,6 @@ def test_handle_with_cancelled_appointments(
163157
assert messages[0].appointment == valid_appointment
164158

165159
@pytest.mark.parametrize("status_code", [401, 403, 404, 405, 406, 413, 415, 422])
166-
@pytest.mark.django_db
167160
def test_handle_with_unrecoverable_failures(
168161
self, mark_batch_as_sent, mock_send_message_batch, status_code
169162
):
@@ -172,7 +165,7 @@ def test_handle_with_unrecoverable_failures(
172165
notify_errors = {"errors": [{"some-error": "details"}]}
173166
mock_send_message_batch.return_value.json.return_value = notify_errors
174167
appointment = AppointmentFactory(
175-
starts_at=datetime.now().replace(tzinfo=TZ_INFO) + timedelta(weeks=4)
168+
starts_at=datetime.now(tz=TZ_INFO) + timedelta(weeks=4)
176169
)
177170
routing_plan_id = RoutingPlan.for_episode_type(appointment.episode_type).id
178171

@@ -189,7 +182,6 @@ def test_handle_with_unrecoverable_failures(
189182
assert messages[0].status == "failed"
190183

191184
@pytest.mark.parametrize("status_code", [408, 425, 429, 500, 503, 504])
192-
@pytest.mark.django_db
193185
def test_handle_with_recoverable_failures(
194186
self, mark_batch_as_sent, mock_send_message_batch, status_code
195187
):
@@ -199,7 +191,7 @@ def test_handle_with_recoverable_failures(
199191
mock_send_message_batch.return_value.json.return_value = notify_errors
200192

201193
appointment = AppointmentFactory(
202-
starts_at=datetime.now().replace(tzinfo=TZ_INFO) + timedelta(weeks=4)
194+
starts_at=datetime.now(tz=TZ_INFO) + timedelta(weeks=4)
203195
)
204196
routing_plan_id = RoutingPlan.for_episode_type(appointment.episode_type).id
205197

@@ -220,7 +212,24 @@ def test_handle_with_recoverable_failures(
220212
)
221213
)
222214

223-
def test_handle_with_error(self, mark_batch_as_sent, mock_send_message_batch):
215+
def test_handle_does_nothing_on_weekend(
216+
self, mock_mark_batch_as_sent, mock_send_message_batch, time_machine
217+
):
218+
AppointmentFactory(starts_at=datetime.now(tz=TZ_INFO) + timedelta(weeks=4))
219+
time_machine.move_to(datetime.now() + relativedelta.relativedelta(weekday=5))
220+
221+
Command().handle()
222+
223+
mock_send_message_batch.assert_not_called()
224+
225+
time_machine.move_to(datetime.now() + relativedelta.relativedelta(weekday=6))
226+
227+
Command().handle()
228+
229+
mock_send_message_batch.assert_not_called()
230+
231+
def test_handle_with_error(self, mock_mark_batch_as_sent, mock_send_message_batch):
224232
"""Test that errors are caught and raised as CommandErrors"""
225-
with pytest.raises(CommandError):
226-
Command().handle()
233+
with patch.object(RoutingPlan, "all", side_effect=Exception("Nooooo!")):
234+
with pytest.raises(CommandError):
235+
Command().handle()

0 commit comments

Comments
 (0)