From 5ffab276ceb2e97f865a73341f136ab16dc10e06 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 12 Aug 2024 10:22:52 -0700 Subject: [PATCH] remove unneeded govuk dependencies --- notifications_utils/letter_timings.py | 177 ----------- poetry.lock | 16 +- pyproject.toml | 1 - .../test_letter_timings.py | 277 ------------------ 4 files changed, 1 insertion(+), 470 deletions(-) delete mode 100644 notifications_utils/letter_timings.py delete mode 100644 tests/notifications_utils/test_letter_timings.py diff --git a/notifications_utils/letter_timings.py b/notifications_utils/letter_timings.py deleted file mode 100644 index b171d6c3b..000000000 --- a/notifications_utils/letter_timings.py +++ /dev/null @@ -1,177 +0,0 @@ -from collections import namedtuple -from datetime import time, timedelta - -import pytz -from govuk_bank_holidays.bank_holidays import BankHolidays - -from app.utils import utc_now -from notifications_utils.countries.data import Postage -from notifications_utils.timezones import utc_string_to_aware_gmt_datetime - -LETTER_PROCESSING_DEADLINE = time(17, 30) -CANCELLABLE_JOB_LETTER_STATUSES = [ - "created", - "cancelled", - "virus-scan-failed", - "validation-failed", - "technical-failure", - "pending-virus-check", -] - - -non_working_days_dvla = BankHolidays( - use_cached_holidays=True, - weekend=(5, 6), -) -non_working_days_royal_mail = BankHolidays( - use_cached_holidays=True, - weekend=(6,), # Only Sunday (day 6 of the week) is a non-working day -) - - -def set_gmt_hour(day, hour): - return ( - day.astimezone(pytz.timezone("Europe/London")) - .replace(hour=hour, minute=0) - .astimezone(pytz.utc) - ) - - -def get_next_work_day(date, non_working_days): - next_day = date + timedelta(days=1) - if non_working_days.is_work_day( - date=next_day.date(), - division=BankHolidays.ENGLAND_AND_WALES, - ): - return next_day - return get_next_work_day(next_day, non_working_days) - - -def get_next_dvla_working_day(date): - """ - Printing takes place monday to friday, excluding bank holidays - """ - return get_next_work_day(date, non_working_days=non_working_days_dvla) - - -def get_next_royal_mail_working_day(date): - """ - Royal mail deliver letters on monday to saturday - """ - return get_next_work_day(date, non_working_days=non_working_days_royal_mail) - - -def get_delivery_day(date, *, days_to_deliver): - next_day = get_next_royal_mail_working_day(date) - if days_to_deliver == 1: - return next_day - return get_delivery_day(next_day, days_to_deliver=(days_to_deliver - 1)) - - -def get_min_and_max_days_in_transit(postage): - return { - # first class post is printed earlier in the day, so will - # actually transit on the printing day, and be delivered the next - # day, so effectively spends no full days in transit - "first": (0, 0), - "second": (1, 2), - Postage.EUROPE: (3, 5), - Postage.REST_OF_WORLD: (5, 7), - }[postage] - - -def get_earliest_and_latest_delivery(print_day, postage): - for days_to_transit in get_min_and_max_days_in_transit(postage): - yield get_delivery_day(print_day, days_to_deliver=1 + days_to_transit) - - -def get_letter_timings(upload_time, postage): - LetterTimings = namedtuple( - "LetterTimings", "printed_by, is_printed, earliest_delivery, latest_delivery" - ) - - # shift anything after 5:30pm to the next day - processing_day = utc_string_to_aware_gmt_datetime(upload_time) + timedelta( - hours=6, minutes=30 - ) - print_day = get_next_dvla_working_day(processing_day) - - earliest_delivery, latest_delivery = get_earliest_and_latest_delivery( - print_day, postage - ) - - # print deadline is 3pm BST - printed_by = set_gmt_hour(print_day, hour=15) - now = utc_now().replace(tzinfo=pytz.utc).astimezone(pytz.timezone("Europe/London")) - - return LetterTimings( - printed_by=printed_by, - is_printed=(now > printed_by), - earliest_delivery=set_gmt_hour(earliest_delivery, hour=16), - latest_delivery=set_gmt_hour(latest_delivery, hour=16), - ) - - -def letter_can_be_cancelled(notification_status, notification_created_at): - """ - If letter does not have status of created or pending-virus-check - => can't be cancelled (it has already been processed) - - If it's after 5.30pm local time and the notification was created today before 5.30pm local time - => can't be cancelled (it will already be zipped up to be sent) - """ - if notification_status not in ("created", "pending-virus-check"): - return False - - if too_late_to_cancel_letter(notification_created_at): - return False - return True - - -def too_late_to_cancel_letter(notification_created_at): - time_created_at = notification_created_at - day_created_on = time_created_at.date() - - current_time = utc_now() - current_day = current_time.date() - if ( - _after_letter_processing_deadline() - and _notification_created_before_today_deadline(notification_created_at) - ): - return True - if ( - _notification_created_before_that_day_deadline(notification_created_at) - and day_created_on < current_day - ): - return True - if (current_day - day_created_on).days > 1: - return True - - -def _after_letter_processing_deadline(): - current_utc_datetime = utc_now() - bst_time = current_utc_datetime.time() - - return bst_time >= LETTER_PROCESSING_DEADLINE - - -def _notification_created_before_today_deadline(notification_created_at): - current_bst_datetime = utc_now() - todays_deadline = current_bst_datetime.replace( - hour=LETTER_PROCESSING_DEADLINE.hour, - minute=LETTER_PROCESSING_DEADLINE.minute, - ) - - notification_created_at_in_bst = notification_created_at - - return notification_created_at_in_bst <= todays_deadline - - -def _notification_created_before_that_day_deadline(notification_created_at): - notification_created_at_bst_datetime = notification_created_at - created_at_day_deadline = notification_created_at_bst_datetime.replace( - hour=LETTER_PROCESSING_DEADLINE.hour, - minute=LETTER_PROCESSING_DEADLINE.minute, - ) - - return notification_created_at_bst_datetime <= created_at_day_deadline diff --git a/poetry.lock b/poetry.lock index cc661a9a1..baab16245 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1562,20 +1562,6 @@ files = [ {file = "geojson-3.1.0.tar.gz", hash = "sha256:58a7fa40727ea058efc28b0e9ff0099eadf6d0965e04690830208d3ef571adac"}, ] -[[package]] -name = "govuk-bank-holidays" -version = "0.14" -description = "Tool to load UK bank holidays from GOV.UK" -optional = false -python-versions = ">=3.6" -files = [ - {file = "govuk-bank-holidays-0.14.tar.gz", hash = "sha256:ce85102423b72908957d25981f616494729686515d5d66c09a1d35a354ce20a6"}, - {file = "govuk_bank_holidays-0.14-py3-none-any.whl", hash = "sha256:da485c4a40c6c874c925916e492e3f20b807cffba7eed5f07fb69327aef6b10b"}, -] - -[package.dependencies] -requests = "*" - [[package]] name = "greenlet" version = "3.0.3" @@ -4739,4 +4725,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.12.2" -content-hash = "89b99841ebd4bd735104048c8f8f0a35bfedb700a7a5648ecabe713816e25579" +content-hash = "8fa243a938720113dd0999656594d7d3660a1e6202a4f26b9e9abe25d19b0cc0" diff --git a/pyproject.toml b/pyproject.toml index 9f9b39ce7..219f98ee6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,6 @@ faker = "^26.0.0" async-timeout = "^4.0.3" bleach = "^6.1.0" geojson = "^3.1.0" -govuk-bank-holidays = "^0.14" numpy = "^1.26.4" ordered-set = "^4.1.0" phonenumbers = "^8.13.42" diff --git a/tests/notifications_utils/test_letter_timings.py b/tests/notifications_utils/test_letter_timings.py deleted file mode 100644 index f93d32e99..000000000 --- a/tests/notifications_utils/test_letter_timings.py +++ /dev/null @@ -1,277 +0,0 @@ -from datetime import datetime - -import pytest -import pytz -from freezegun import freeze_time - -from app.utils import utc_now -from notifications_utils.letter_timings import ( - get_letter_timings, - letter_can_be_cancelled, -) - - -@freeze_time("2017-07-14 13:59:59") # Friday, before print deadline (3PM EST) -@pytest.mark.parametrize( - ( - "upload_time", - "expected_print_time", - "is_printed", - "first_class", - "expected_earliest", - "expected_latest", - ), - [ - # EST - # ================================================================== - # First thing Monday - ( - "Monday 2017-07-10 00:00:01", - "Tuesday 2017-07-11 15:00", - True, - "Wednesday 2017-07-12 16:00", - "Thursday 2017-07-13 16:00", - "Friday 2017-07-14 16:00", - ), - # Monday at 17:29 EST (sent on monday) - ( - "Monday 2017-07-10 16:29:59", - "Tuesday 2017-07-11 15:00", - True, - "Wednesday 2017-07-12 16:00", - "Thursday 2017-07-13 16:00", - "Friday 2017-07-14 16:00", - ), - # Monday at 17:30 EST (sent on tuesday) - ( - "Monday 2017-07-10 16:30:01", - "Wednesday 2017-07-12 15:00", - True, - "Thursday 2017-07-13 16:00", - "Friday 2017-07-14 16:00", - "Saturday 2017-07-15 16:00", - ), - # Tuesday before 17:30 EST - ( - "Tuesday 2017-07-11 12:00:00", - "Wednesday 2017-07-12 15:00", - True, - "Thursday 2017-07-13 16:00", - "Friday 2017-07-14 16:00", - "Saturday 2017-07-15 16:00", - ), - # Wednesday before 17:30 EST - ( - "Wednesday 2017-07-12 12:00:00", - "Thursday 2017-07-13 15:00", - True, - "Friday 2017-07-14 16:00", - "Saturday 2017-07-15 16:00", - "Monday 2017-07-17 16:00", - ), - # Thursday before 17:30 EST - ( - "Thursday 2017-07-13 12:00:00", - "Friday 2017-07-14 15:00", - False, - "Saturday 2017-07-15 16:00", - "Monday 2017-07-17 16:00", - "Tuesday 2017-07-18 16:00", - ), - # Friday anytime - ( - "Friday 2017-07-14 00:00:00", - "Monday 2017-07-17 15:00", - False, - "Tuesday 2017-07-18 16:00", - "Wednesday 2017-07-19 16:00", - "Thursday 2017-07-20 16:00", - ), - ( - "Friday 2017-07-14 12:00:00", - "Monday 2017-07-17 15:00", - False, - "Tuesday 2017-07-18 16:00", - "Wednesday 2017-07-19 16:00", - "Thursday 2017-07-20 16:00", - ), - ( - "Friday 2017-07-14 22:00:00", - "Monday 2017-07-17 15:00", - False, - "Tuesday 2017-07-18 16:00", - "Wednesday 2017-07-19 16:00", - "Thursday 2017-07-20 16:00", - ), - # Saturday anytime - ( - "Saturday 2017-07-14 12:00:00", - "Monday 2017-07-17 15:00", - False, - "Tuesday 2017-07-18 16:00", - "Wednesday 2017-07-19 16:00", - "Thursday 2017-07-20 16:00", - ), - # Sunday before 1730 EST - ( - "Sunday 2017-07-15 15:59:59", - "Monday 2017-07-17 15:00", - False, - "Tuesday 2017-07-18 16:00", - "Wednesday 2017-07-19 16:00", - "Thursday 2017-07-20 16:00", - ), - # Sunday after 17:30 EST - ( - "Sunday 2017-07-16 16:30:01", - "Tuesday 2017-07-18 15:00", - False, - "Wednesday 2017-07-19 16:00", - "Thursday 2017-07-20 16:00", - "Friday 2017-07-21 16:00", - ), - # GMT - # ================================================================== - # Monday at 17:29 GMT - ( - "Monday 2017-01-02 17:29:59", - "Tuesday 2017-01-03 15:00", - True, - "Wednesday 2017-01-04 16:00", - "Thursday 2017-01-05 16:00", - "Friday 2017-01-06 16:00", - ), - # Monday at 17:00 GMT - ( - "Monday 2017-01-02 17:30:01", - "Wednesday 2017-01-04 15:00", - True, - "Thursday 2017-01-05 16:00", - "Friday 2017-01-06 16:00", - "Saturday 2017-01-07 16:00", - ), - ], -) -@pytest.mark.skip(reason="Letters being developed later") -def test_get_estimated_delivery_date_for_letter( - upload_time, - expected_print_time, - is_printed, - first_class, - expected_earliest, - expected_latest, -): - # remove the day string from the upload_time, which is purely informational - - def format_dt(x): - return x.astimezone(pytz.timezone("America/New_York")).strftime( - "%A %Y-%m-%d %H:%M" - ) - - upload_time = upload_time.split(" ", 1)[1] - - timings = get_letter_timings(upload_time, postage="second") - - assert format_dt(timings.printed_by) == expected_print_time - assert timings.is_printed == is_printed - assert format_dt(timings.earliest_delivery) == expected_earliest - assert format_dt(timings.latest_delivery) == expected_latest - - first_class_timings = get_letter_timings(upload_time, postage="first") - - assert format_dt(first_class_timings.printed_by) == expected_print_time - assert first_class_timings.is_printed == is_printed - assert format_dt(first_class_timings.earliest_delivery) == first_class - assert format_dt(first_class_timings.latest_delivery) == first_class - - -@pytest.mark.parametrize("status", ["sending", "pending"]) -def test_letter_cannot_be_cancelled_if_letter_status_is_not_created_or_pending_virus_check( - status, -): - notification_created_at = utc_now() - - assert not letter_can_be_cancelled(status, notification_created_at) - - -@freeze_time("2018-7-7 16:00:00") -@pytest.mark.parametrize( - "notification_created_at", - [ - datetime(2018, 7, 6, 18, 0), # created yesterday after 1730 - datetime(2018, 7, 7, 12, 0), # created today - ], -) -@pytest.mark.skip(reason="Letters not part of release") -def test_letter_can_be_cancelled_if_before_1730_and_letter_created_before_1730( - notification_created_at, -): - notification_status = "pending-virus-check" - - assert letter_can_be_cancelled(notification_status, notification_created_at) - - -@freeze_time("2017-12-12 17:30:00") -@pytest.mark.parametrize( - "notification_created_at", - [ - datetime(2017, 12, 12, 17, 0), - datetime(2017, 12, 12, 17, 30), - ], -) -@pytest.mark.skip(reason="Letters not part of release") -def test_letter_cannot_be_cancelled_if_1730_exactly_and_letter_created_at_or_before_1730( - notification_created_at, -): - notification_status = "pending-virus-check" - - assert not letter_can_be_cancelled(notification_status, notification_created_at) - - -@freeze_time("2018-7-7 19:00:00") -@pytest.mark.parametrize( - "notification_created_at", - [ - datetime(2018, 7, 6, 18, 0), # created yesterday after 1730 - datetime(2018, 7, 7, 12, 0), # created today before 1730 - ], -) -@pytest.mark.skip(reason="Letters not part of release") -def test_letter_cannot_be_cancelled_if_after_1730_and_letter_created_before_1730( - notification_created_at, -): - notification_status = "created" - - assert not letter_can_be_cancelled(notification_status, notification_created_at) - - -@freeze_time("2018-7-7 15:00:00") -@pytest.mark.skip(reason="Letters not part of release") -def test_letter_cannot_be_cancelled_if_before_1730_and_letter_created_before_1730_yesterday(): - notification_status = "created" - - assert not letter_can_be_cancelled(notification_status, datetime(2018, 7, 6, 14, 0)) - - -@freeze_time("2018-7-7 15:00:00") -@pytest.mark.skip(reason="Letters not part of release") -def test_letter_cannot_be_cancelled_if_before_1730_and_letter_created_after_1730_two_days_ago(): - notification_status = "created" - - assert not letter_can_be_cancelled(notification_status, datetime(2018, 7, 5, 19, 0)) - - -@freeze_time("2018-7-7 19:00:00") -@pytest.mark.parametrize( - "notification_created_at", - [ - datetime(2018, 7, 7, 18, 30), - datetime(2018, 7, 7, 19, 0), - ], -) -def test_letter_can_be_cancelled_if_after_1730_and_letter_created_at_1730_today_or_later( - notification_created_at, -): - notification_status = "created" - - assert letter_can_be_cancelled(notification_status, notification_created_at)