Content changes for notification pages

This commit adds content pages for the notifications pages, particularly
the letter pages, which will make things clearer now that we will soon be allowing
letters to be cancelled.

The main changes are:
* The confirmation banner for letters sent from a CSV file now states when
printing will start.
* We state the CSV file that notifications were sent from on the
notifications page
* The notification page for letters shows when printing starts (today,
tomorrow, or that date that the letter was printed)
This commit is contained in:
Katie Smith
2018-11-27 16:49:01 +00:00
parent 07a1d903eb
commit 088d6ee4b0
8 changed files with 166 additions and 22 deletions

View File

@@ -32,6 +32,7 @@ from app.utils import (
get_page_from_request,
get_time_left,
parse_filter_args,
printing_today_or_tomorrow,
set_status_filters,
user_has_permissions,
)
@@ -108,7 +109,8 @@ def view_job(service_id, job_id):
just_sent=bool(
request.args.get('just_sent') == 'yes' and
template['template_type'] == 'letter'
)
),
printing_day=printing_today_or_tomorrow()
)

View File

@@ -2,8 +2,9 @@
import base64
import io
import os
from datetime import datetime
from datetime import datetime, timedelta
from dateutil import parser
from flask import (
Response,
abort,
@@ -16,10 +17,15 @@ 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.letter_timings import (
get_letter_timings,
letter_can_be_cancelled,
)
from notifications_utils.pdf import pdf_page_count
from notifications_utils.timezones import utc_string_to_aware_gmt_datetime
from app import (
_format_datetime_short,
current_service,
format_date_numeric,
job_api_client,
@@ -36,6 +42,7 @@ from app.utils import (
get_help_argument,
get_template,
parse_filter_args,
printing_today_or_tomorrow,
set_status_filters,
user_has_permissions,
)
@@ -76,6 +83,8 @@ def view_notification(service_id, notification_id):
else:
job = None
letter_print_day = get_letter_printing_statement(notification['status'], notification['created_at'])
return render_template(
'views/notifications/notification.html',
finished=(notification['status'] in (DELIVERED_STATUSES + FAILURE_STATUSES)),
@@ -102,10 +111,23 @@ def view_notification(service_id, notification_id):
notification_id=notification['id'],
postage=notification['postage'],
can_receive_inbound=(current_service.has_permission('inbound_sms')),
is_precompiled_letter=notification['template']['is_precompiled_letter']
is_precompiled_letter=notification['template']['is_precompiled_letter'],
letter_print_day=letter_print_day
)
def get_letter_printing_statement(status, created_at):
created_at_dt = parser.parse(created_at).replace(tzinfo=None)
if letter_can_be_cancelled(status, created_at_dt):
return 'Printing starts {} at 5.30pm'.format(printing_today_or_tomorrow())
else:
printed_datetime = utc_string_to_aware_gmt_datetime(created_at) + timedelta(hours=6, minutes=30)
printed_date = _format_datetime_short(printed_datetime)
return 'Printed on {}'.format(printed_date)
def get_preview_error_image():
path = os.path.join(os.path.dirname(__file__), "..", "..", "static", "images", "preview_error.png")
with open(path, "rb") as file:

View File

@@ -14,7 +14,9 @@
</h1>
{% if just_sent %}
{{ banner('Weve started printing your letters', type='default', with_tick=True) }}
{{ banner('Your letter has been sent. Printing starts {} at 5.30pm.'.format(printing_day),
type='default',
with_tick=True) }}
{% else %}
{{ ajax_block(partials, updates_url, 'status', finished=finished) }}
{% endif %}

View File

@@ -19,14 +19,16 @@
Provided as PDF, sent
{% else %}
{% if help %}
{{ template.name }}
{{ template.name }}
{% else %}
<a href="{{ url_for('.view_template', service_id=current_service.id, template_id=template.id) }}">{{ template.name }}</a>
<a href="{{ url_for('.view_template', service_id=current_service.id, template_id=template.id) }}">{{ template.name }}</a>
{% endif %}
sent
was sent
{% endif %}
{% if job and job.original_file_name != 'Report' %}
from
{% set destination =
{'letter': 'an address', 'email': 'an email address', 'sms': 'a phone number'} %}
to {{ destination[template.template_type] }} from
<a href="{{ url_for('.view_job', service_id=current_service.id, job_id=job.id) }}">{{ job.original_file_name }}</a>
{% elif created_by %}
by {{ created_by.name }}
@@ -45,6 +47,9 @@
(letter has content outside the printable area)
</p>
{% else %}
<p>
{{ letter_print_day }}
</p>
<p>
Postage: {{ postage }} class
</p>

View File

@@ -2,7 +2,7 @@ import csv
import os
import re
import unicodedata
from datetime import datetime, timedelta, timezone
from datetime import datetime, time, timedelta, timezone
from functools import wraps
from io import StringIO
from itertools import chain
@@ -33,6 +33,7 @@ from notifications_utils.template import (
LetterPreviewTemplate,
SMSPreviewTemplate,
)
from notifications_utils.timezones import convert_utc_to_bst
from orderedset._orderedset import OrderedSet
from werkzeug.datastructures import MultiDict
@@ -643,3 +644,13 @@ def get_default_sms_sender(sms_senders):
Field(x['sms_sender'], html='escape')
for x in sms_senders if x['is_default']
), "None"))
def printing_today_or_tomorrow():
now_utc = datetime.utcnow()
now_bst = convert_utc_to_bst(now_utc)
if now_bst.time() < time(17, 30):
return 'today'
else:
return 'tomorrow'

View File

@@ -343,8 +343,8 @@ def test_should_show_letter_job(
)
@freeze_time("2016-01-01 11:09:00.061258")
def test_should_show_letter_job_with_banner_after_sending(
@freeze_time("2016-01-01 11:09:00")
def test_should_show_letter_job_with_banner_after_sending_before_1730(
client_request,
mock_get_service_letter_template,
mock_get_job,
@@ -362,7 +362,30 @@ def test_should_show_letter_job_with_banner_after_sending(
assert page.select('p.bottom-gutter') == []
assert normalize_spaces(page.select('.banner-default-with-tick')[0].text) == (
'Weve started printing your letters'
'Your letter has been sent. Printing starts today at 5.30pm.'
)
@freeze_time("2016-01-01 18:09:00")
def test_should_show_letter_job_with_banner_after_sending_after_1730(
client_request,
mock_get_service_letter_template,
mock_get_job,
mock_get_notifications,
mock_get_service_data_retention_by_notification_type,
fake_uuid,
):
page = client_request.get(
'main.view_job',
service_id=SERVICE_ONE_ID,
job_id=fake_uuid,
just_sent='yes',
)
assert page.select('p.bottom-gutter') == []
assert normalize_spaces(page.select('.banner-default-with-tick')[0].text) == (
'Your letter has been sent. Printing starts tomorrow at 5.30pm.'
)

View File

@@ -1,4 +1,5 @@
import base64
from datetime import datetime
from functools import partial
from unittest.mock import mock_open
@@ -7,6 +8,7 @@ from flask import url_for
from freezegun import freeze_time
from notifications_python_client.errors import APIError
from app.main.views.notifications import get_letter_printing_statement
from tests.conftest import (
SERVICE_ONE_ID,
active_caseworking_user,
@@ -127,7 +129,7 @@ def test_notification_page_doesnt_link_to_template_in_tour(
)
assert normalize_spaces(page.select('main p:nth-of-type(1)')[0].text) == (
'sample template sent by Test User on 1 January at 1:01am'
"sample template was sent by Test User on 1 January at 1:01am"
)
assert len(page.select('main p:nth-of-type(1) a')) == 0
@@ -141,7 +143,14 @@ def test_notification_page_shows_page_for_letter_notification(
count_of_pages = 3
mock_get_notification(mocker, fake_uuid, template_type='letter', postage='second')
notification = mock_get_notification(
mocker,
fake_uuid,
notification_status='created',
template_type='letter',
postage='second')
notification.created_at = datetime.utcnow()
mock_page_count = mocker.patch(
'app.main.views.notifications.get_page_count_for_letter',
return_value=count_of_pages
@@ -154,12 +163,15 @@ def test_notification_page_shows_page_for_letter_notification(
)
assert normalize_spaces(page.select('main p:nth-of-type(1)')[0].text) == (
'sample template sent by Test User on 1 January at 1:01am'
"sample template was sent by Test User on 1 January at 1:01am"
)
assert normalize_spaces(page.select('main p:nth-of-type(2)')[0].text) == (
'Postage: second class'
'Printing starts today at 5.30pm'
)
assert normalize_spaces(page.select('main p:nth-of-type(3)')[0].text) == (
'Postage: second class'
)
assert normalize_spaces(page.select('main p:nth-of-type(4)')[0].text) == (
'Estimated delivery date: 6 January'
)
assert page.select('p.notification-status') == []
@@ -215,7 +227,7 @@ def test_notification_page_shows_cancelled_letter(
)
assert normalize_spaces(page.select('main p')[0].text) == (
'sample template sent by Test User on 1 January at 1:01am'
"sample template was sent by Test User on 1 January at 1:01am"
)
assert normalize_spaces(page.select('main p')[1].text) == (
expected_message
@@ -225,13 +237,18 @@ def test_notification_page_shows_cancelled_letter(
assert page.select_one('main img')['src'].endswith('.png?page=1')
@freeze_time("2016-01-01 01:01")
@freeze_time("2016-01-01 18:00")
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')
mock_get_notification(
mocker,
fake_uuid,
notification_status='pending-virus-check',
template_type='letter',
postage='first')
mocker.patch('app.main.views.notifications.get_page_count_for_letter', return_value=3)
page = client_request.get(
@@ -240,8 +257,9 @@ def test_notification_page_shows_page_for_first_class_letter_notification(
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'
assert normalize_spaces(page.select('main p:nth-of-type(2)')[0].text) == 'Printing starts tomorrow at 5.30pm'
assert normalize_spaces(page.select('main p:nth-of-type(3)')[0].text) == 'Postage: first class'
assert normalize_spaces(page.select('main p:nth-of-type(4)')[0].text) == 'Estimated delivery date: 5 January'
@pytest.mark.parametrize('filetype', [
@@ -473,3 +491,39 @@ def test_should_show_image_of_precompiled_letter_notification(
assert response.status_code == 200
assert response.get_data(as_text=True) == 'foo'
assert mock_pdf_page_count.called_once()
@pytest.mark.parametrize('created_at, current_datetime', [
('2017-07-07T12:00:00+00:00', '2017-07-07 16:29:00'), # created today, summer
('2017-12-12T12:00:00+00:00', '2017-12-12 17:29:00'), # created today, winter
('2017-12-12T21:30:00+00:00', '2017-12-13 17:29:00'), # created after 5.30 yesterday
('2017-03-25T17:30:00+00:00', '2017-03-26 16:29:00'), # over clock change period on 2017-03-26
])
def test_get_letter_printing_statement_when_letter_prints_today(created_at, current_datetime):
with freeze_time(current_datetime):
statement = get_letter_printing_statement('created', created_at)
assert statement == 'Printing starts today at 5.30pm'
@pytest.mark.parametrize('created_at, current_datetime', [
('2017-07-07T16:31:00+00:00', '2017-07-07 22:59:00'), # created today, summer
('2017-12-12T17:31:00+00:00', '2017-12-12 23:59:00'), # created today, winter
])
def test_get_letter_printing_statement_when_letter_prints_tomorrow(created_at, current_datetime):
with freeze_time(current_datetime):
statement = get_letter_printing_statement('created', created_at)
assert statement == 'Printing starts tomorrow at 5.30pm'
@pytest.mark.parametrize('created_at, print_day', [
('2017-07-06T16:30:00+00:00', '7 July'),
('2017-12-01T00:00:00+00:00', '1 December'),
('2017-03-26T12:00:00+00:00', '26 March'),
])
@freeze_time('2017-07-07 12:00:00')
def test_get_letter_printing_statement_for_letter_that_has_been_sent(created_at, print_day):
statement = get_letter_printing_statement('delivered', created_at)
assert statement == 'Printed on {}'.format(print_day)

View File

@@ -17,6 +17,7 @@ from app.utils import (
generate_notifications_csv,
generate_previous_dict,
get_logo_cdn_domain,
printing_today_or_tomorrow,
)
from tests.conftest import fake_uuid
@@ -452,3 +453,27 @@ def test_validate_email_domain_data():
def test_format_datetime_relative(time, human_readable_datetime):
with freeze_time('2018-03-21 12:00'):
assert format_datetime_relative(time) == human_readable_datetime
@pytest.mark.parametrize('utc_datetime', [
'2018-08-01 23:00',
'2018-08-01 16:29',
'2018-11-01 00:00',
'2018-11-01 10:00',
'2018-11-01 17:29',
])
def test_printing_today_or_tomorrow_returns_today(utc_datetime):
with freeze_time(utc_datetime):
assert printing_today_or_tomorrow() == 'today'
@pytest.mark.parametrize('datetime', [
'2018-08-01 22:59',
'2018-08-01 16:30',
'2018-11-01 17:30',
'2018-11-01 21:00',
'2018-11-01 23:59',
])
def test_printing_today_or_tomorrow_returns_tomorrow(datetime):
with freeze_time(datetime):
assert printing_today_or_tomorrow() == 'tomorrow'