diff --git a/app/main/views/jobs.py b/app/main/views/jobs.py index 9d0b22ab0..644e2d5bf 100644 --- a/app/main/views/jobs.py +++ b/app/main/views/jobs.py @@ -12,6 +12,7 @@ from flask import ( url_for, ) from flask_login import current_user, login_required +from notifications_utils.letter_timings import get_letter_timings from notifications_utils.template import Template, WithSubjectTemplate from app import ( @@ -28,7 +29,6 @@ from app.utils import ( generate_next_dict, generate_notifications_csv, generate_previous_dict, - get_letter_timings, get_page_from_request, get_time_left, parse_filter_args, @@ -380,10 +380,16 @@ def get_job_partials(job, template): ) if template['template_type'] == 'letter': + # there might be no notifications if the job has only just been created and the tasks haven't run yet + if notifications['notifications']: + postage = notifications['notifications'][0]['postage'] + else: + postage = current_service.postage + counts = render_template( 'partials/jobs/count-letters.html', total=job.get('notification_count', 0), - delivery_estimate=get_letter_timings(job['created_at']).earliest_delivery, + delivery_estimate=get_letter_timings(job['created_at'], postage=postage).earliest_delivery, ) else: counts = render_template( diff --git a/app/main/views/notifications.py b/app/main/views/notifications.py index e0385f56d..f1e594357 100644 --- a/app/main/views/notifications.py +++ b/app/main/views/notifications.py @@ -16,6 +16,7 @@ from flask import ( ) from flask_login import login_required from notifications_python_client.errors import APIError +from notifications_utils.letter_timings import get_letter_timings from notifications_utils.pdf import pdf_page_count from app import ( @@ -33,7 +34,6 @@ from app.utils import ( FAILURE_STATUSES, generate_notifications_csv, get_help_argument, - get_letter_timings, get_template, parse_filter_args, set_status_filters, @@ -90,7 +90,10 @@ def view_notification(service_id, notification_id): created_by=notification.get('created_by'), created_at=notification['created_at'], help=get_help_argument(), - estimated_letter_delivery_date=get_letter_timings(notification['created_at']).earliest_delivery, + estimated_letter_delivery_date=get_letter_timings( + notification['created_at'], + postage=notification['postage'] + ).earliest_delivery, notification_id=notification['id'], postage=notification['postage'], can_receive_inbound=(current_service.has_permission('inbound_sms')), diff --git a/app/utils.py b/app/utils.py index 8ddc976ca..330be9b59 100644 --- a/app/utils.py +++ b/app/utils.py @@ -2,7 +2,6 @@ import csv import os import re import unicodedata -from collections import namedtuple from datetime import datetime, timedelta, timezone from functools import wraps from io import StringIO @@ -356,37 +355,6 @@ def email_or_sms_not_enabled(template_type, permissions): return (template_type in ['email', 'sms']) and (template_type not in permissions) -def get_letter_timings(upload_time): - - LetterTimings = namedtuple( - 'LetterTimings', - 'printed_by, is_printed, earliest_delivery, latest_delivery' - ) - - # shift anything after 5pm to the next day - processing_day = gmt_timezones(upload_time) + timedelta(hours=(7)) - - print_day, earliest_delivery, latest_delivery = ( - processing_day + timedelta(days=days) - for days in { - 'Wednesday': (1, 3, 5), - 'Thursday': (1, 4, 5), - 'Friday': (3, 5, 6), - 'Saturday': (2, 4, 5), - }.get(processing_day.strftime('%A'), (1, 3, 4)) - ) - - printed_by = print_day.astimezone(pytz.timezone('Europe/London')).replace(hour=15, minute=0) - now = datetime.utcnow().replace(tzinfo=pytz.timezone('Europe/London')) - - return LetterTimings( - printed_by=printed_by, - is_printed=(now > printed_by), - earliest_delivery=earliest_delivery, - latest_delivery=latest_delivery, - ) - - def gmt_timezones(date): date = dateutil.parser.parse(date) forced_utc = date.replace(tzinfo=pytz.utc) diff --git a/requirements-app.txt b/requirements-app.txt index 82491e722..2a7af9c06 100644 --- a/requirements-app.txt +++ b/requirements-app.txt @@ -20,5 +20,7 @@ notifications-python-client==5.2.0 # PaaS awscli-cwlogs>=1.4,<1.5 +awscli==1.15.82 +botocore<1.11.0 -git+https://github.com/alphagov/notifications-utils.git@30.3.1#egg=notifications-utils==30.3.1 +git+https://github.com/alphagov/notifications-utils.git@30.4.0#egg=notifications-utils==30.4.0 diff --git a/requirements.txt b/requirements.txt index d38cebef9..dd8debd87 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,17 +22,17 @@ notifications-python-client==5.2.0 # PaaS awscli-cwlogs>=1.4,<1.5 +awscli==1.15.82 +botocore<1.11.0 -git+https://github.com/alphagov/notifications-utils.git@30.3.1#egg=notifications-utils==30.3.1 +git+https://github.com/alphagov/notifications-utils.git@30.4.0#egg=notifications-utils==30.4.0 ## The following requirements were added by pip freeze: -awscli==1.16.20 bleach==2.1.3 boto3==1.6.16 -botocore==1.12.10 certifi==2018.8.24 chardet==3.0.4 -click==6.7 +Click==7.0 colorama==0.3.9 dnspython==1.15.0 docopt==0.6.2 @@ -52,7 +52,7 @@ lxml==4.2.5 MarkupSafe==1.0 mistune==0.8.3 monotonic==1.5 -openpyxl==2.5.7 +openpyxl==2.5.8 orderedset==2.0.1 phonenumbers==8.9.4 pyasn1==0.4.4 @@ -61,7 +61,7 @@ PyJWT==1.6.4 PyPDF2==1.26.0 python-dateutil==2.7.3 python-json-logger==0.1.8 -PyYAML==3.12 +PyYAML==3.13 redis==2.10.6 requests==2.19.1 rsa==3.4.2 diff --git a/tests/__init__.py b/tests/__init__.py index 1fc6e83db..15a90e7a0 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -390,7 +390,7 @@ def notification_json( status = 'delivered' links = {} if template_type == 'letter': - postage = 'second' + postage = postage or 'second' if with_links: links = { diff --git a/tests/app/main/views/test_jobs.py b/tests/app/main/views/test_jobs.py index a9eb287df..3c5a6b9fb 100644 --- a/tests/app/main/views/test_jobs.py +++ b/tests/app/main/views/test_jobs.py @@ -440,3 +440,48 @@ def test_should_show_updates_for_one_job_as_json( @freeze_time("2016-01-10 12:00:00.000000") def test_time_left(job_created_at, expected_message): assert get_time_left(job_created_at) == expected_message + + +@freeze_time("2016-01-01 11:09:00.061258") +def test_should_show_letter_job_with_first_class_if_notifications_are_first_class( + client_request, + mock_get_service_letter_template, + mock_get_job, + fake_uuid, + active_user_with_permissions, + mocker, +): + mock_get_notifications( + mocker, + active_user_with_permissions, + diff_template_type='letter', + postage='first' + ) + + page = client_request.get( + 'main.view_job', + service_id=SERVICE_ONE_ID, + job_id=fake_uuid, + ) + + assert normalize_spaces(page.select('.keyline-block')[1].text) == '5 January Estimated delivery date' + + +@freeze_time("2016-01-01 11:09:00.061258") +def test_should_show_letter_job_with_first_class_if_no_notifications( + client_request, + mock_get_service_letter_template, + mock_get_job, + fake_uuid, + mock_get_notifications_with_no_notifications, + mocker +): + mocker.patch('app.main.views.jobs.current_service', postage='first') + + page = client_request.get( + 'main.view_job', + service_id=SERVICE_ONE_ID, + job_id=fake_uuid, + ) + + assert normalize_spaces(page.select('.keyline-block')[1].text) == '5 January Estimated delivery date' diff --git a/tests/app/main/views/test_notifications.py b/tests/app/main/views/test_notifications.py index 3c53ebf9f..5a6bdd418 100644 --- a/tests/app/main/views/test_notifications.py +++ b/tests/app/main/views/test_notifications.py @@ -141,7 +141,7 @@ def test_notification_page_shows_page_for_letter_notification( count_of_pages = 3 - mock_get_notification(mocker, fake_uuid, template_type='letter') + mock_get_notification(mocker, fake_uuid, template_type='letter', postage='second') mocker.patch( 'app.main.views.notifications.get_page_count_for_letter', return_value=count_of_pages @@ -174,6 +174,25 @@ def test_notification_page_shows_page_for_letter_notification( ) +@freeze_time("2016-01-01 01:01") +def test_notification_page_shows_page_for_first_class_letter_notification( + client_request, + mocker, + fake_uuid, +): + mock_get_notification(mocker, fake_uuid, template_type='letter', postage='first') + mocker.patch('app.main.views.notifications.get_page_count_for_letter', return_value=3) + + page = client_request.get( + 'main.view_notification', + service_id=SERVICE_ONE_ID, + notification_id=fake_uuid, + ) + + assert normalize_spaces(page.select('main p:nth-of-type(2)')[0].text) == 'Postage: first class' + assert normalize_spaces(page.select('main p:nth-of-type(3)')[0].text) == 'Estimated delivery date: 5 January' + + @pytest.mark.parametrize('filetype', [ 'pdf', 'png' ]) diff --git a/tests/app/test_utils.py b/tests/app/test_utils.py index 0ab7cabba..48136844f 100644 --- a/tests/app/test_utils.py +++ b/tests/app/test_utils.py @@ -17,7 +17,6 @@ from app.utils import ( generate_notifications_csv, generate_previous_dict, get_cdn_domain, - get_letter_timings, ) from tests.conftest import fake_uuid @@ -277,140 +276,6 @@ def test_generate_notifications_csv_calls_twice_if_next_link( assert mock_get_notifications.mock_calls[1][2]['page'] == 2 -@freeze_time('2017-07-14 14:59:59') # Friday, before print deadline -@pytest.mark.parametrize('upload_time, expected_print_time, is_printed, expected_earliest, expected_latest', [ - - # BST - # ================================================================== - # First thing Monday - ( - '2017-07-10 00:00:01', - 'Tuesday 15:00', - True, - 'Thursday 2017-07-13', - 'Friday 2017-07-14' - ), - # Monday at 16:59 BST - ( - '2017-07-10 15:59:59', - 'Tuesday 15:00', - True, - 'Thursday 2017-07-13', - 'Friday 2017-07-14' - ), - # Monday at 17:00 BST - ( - '2017-07-10 16:00:01', - 'Wednesday 15:00', - True, - 'Friday 2017-07-14', - 'Saturday 2017-07-15' - ), - # Tuesday before 17:00 BST - ( - '2017-07-11 12:00:00', - 'Wednesday 15:00', - True, - 'Friday 2017-07-14', - 'Saturday 2017-07-15' - ), - # Wednesday before 17:00 BST - ( - '2017-07-12 12:00:00', - 'Thursday 15:00', - True, - 'Saturday 2017-07-15', - 'Monday 2017-07-17' - ), - # Thursday before 17:00 BST - ( - '2017-07-13 12:00:00', - 'Friday 15:00', - True, # WRONG - 'Monday 2017-07-17', - 'Tuesday 2017-07-18' - ), - # Friday anytime - ( - '2017-07-14 00:00:00', - 'Monday 15:00', - False, - 'Wednesday 2017-07-19', - 'Thursday 2017-07-20' - ), - ( - '2017-07-14 12:00:00', - 'Monday 15:00', - False, - 'Wednesday 2017-07-19', - 'Thursday 2017-07-20' - ), - ( - '2017-07-14 22:00:00', - 'Monday 15:00', - False, - 'Wednesday 2017-07-19', - 'Thursday 2017-07-20' - ), - # Saturday anytime - ( - '2017-07-14 12:00:00', - 'Monday 15:00', - False, - 'Wednesday 2017-07-19', - 'Thursday 2017-07-20' - ), - # Sunday before 1700 BST - ( - '2017-07-15 15:59:59', - 'Monday 15:00', - False, - 'Wednesday 2017-07-19', - 'Thursday 2017-07-20' - ), - # Sunday after 17:00 BST - ( - '2017-07-16 16:00:01', - 'Tuesday 15:00', - False, - 'Thursday 2017-07-20', - 'Friday 2017-07-21' - ), - - # GMT - # ================================================================== - # Monday at 16:59 GMT - ( - '2017-01-02 16:59:59', - 'Tuesday 15:00', - True, - 'Thursday 2017-01-05', - 'Friday 2017-01-06', - ), - # Monday at 17:00 GMT - ( - '2017-01-02 17:00:01', - 'Wednesday 15:00', - True, - 'Friday 2017-01-06', - 'Saturday 2017-01-07', - ), - -]) -def test_get_estimated_delivery_date_for_letter( - upload_time, - expected_print_time, - is_printed, - expected_earliest, - expected_latest, -): - timings = get_letter_timings(upload_time) - assert timings.printed_by.strftime('%A %H:%M') == expected_print_time - assert timings.is_printed == is_printed - assert timings.earliest_delivery.strftime('%A %Y-%m-%d') == expected_earliest - assert timings.latest_delivery.strftime('%A %Y-%m-%d') == expected_latest - - def test_get_cdn_domain_on_localhost(client, mocker): mocker.patch.dict('app.current_app.config', values={'ADMIN_BASE_URL': 'http://localhost:6012'}) domain = get_cdn_domain() diff --git a/tests/conftest.py b/tests/conftest.py index 4f514fd0c..66cd2ccce 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1838,6 +1838,7 @@ def mock_get_notifications( is_precompiled_letter=False, client_reference=None, noti_status=None, + postage=None, ): def _get_notifications( service_id, @@ -1882,6 +1883,7 @@ def mock_get_notifications( client_reference=client_reference, status=noti_status, created_by_name='Firstname Lastname', + postage=postage ) return mocker.patch( @@ -2643,6 +2645,7 @@ def mock_get_notification( template_name='sample template', is_precompiled_letter=False, key_type=None, + postage=None ): def _get_notification( service_id, @@ -2653,6 +2656,7 @@ def mock_get_notification( rows=1, status=notification_status, template_type=template_type, + postage=postage )['notifications'][0] noti['id'] = notification_id