mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-02-05 19:03:30 -05:00
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:
@@ -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()
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
</h1>
|
||||
|
||||
{% if just_sent %}
|
||||
{{ banner('We’ve 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 %}
|
||||
|
||||
@@ -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>
|
||||
|
||||
13
app/utils.py
13
app/utils.py
@@ -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'
|
||||
|
||||
@@ -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) == (
|
||||
'We’ve 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.'
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user