diff --git a/app/__init__.py b/app/__init__.py
index 5f637cdd8..a3b5f5619 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -782,6 +782,7 @@ def add_template_filters(application):
format_date,
format_date_human,
format_date_normal,
+ format_date_numeric,
format_date_short,
format_datetime_human,
format_datetime_relative,
diff --git a/app/main/views/jobs.py b/app/main/views/jobs.py
index e95c3d61f..0b2d3d39b 100644
--- a/app/main/views/jobs.py
+++ b/app/main/views/jobs.py
@@ -64,7 +64,7 @@ def view_job(service_id, job_id):
just_sent_message = 'Your {} been sent. Printing starts {} at 5:30pm.'.format(
'letter has' if job.notification_count == 1 else 'letters have',
- printing_today_or_tomorrow()
+ printing_today_or_tomorrow(job.created_at)
)
return render_template(
diff --git a/app/main/views/uploads.py b/app/main/views/uploads.py
index 510c8fed7..f17a839a4 100644
--- a/app/main/views/uploads.py
+++ b/app/main/views/uploads.py
@@ -3,6 +3,7 @@ import itertools
import json
import urllib
import uuid
+from datetime import datetime
from io import BytesIO
from zipfile import BadZipFile
@@ -87,6 +88,7 @@ def uploads(service_id):
jobs=listed_uploads,
prev_page=prev_page,
next_page=next_page,
+ now=datetime.utcnow().isoformat(),
)
diff --git a/app/models/job.py b/app/models/job.py
index 02d40159e..9c987a44c 100644
--- a/app/models/job.py
+++ b/app/models/job.py
@@ -1,5 +1,6 @@
-from datetime import datetime
+from datetime import datetime, timedelta
+import pytz
from notifications_utils.letter_timings import (
CANCELLABLE_JOB_LETTER_STATUSES,
get_letter_timings,
@@ -15,7 +16,7 @@ from app.models import JSONModel, ModelList
from app.notify_client.job_api_client import job_api_client
from app.notify_client.notification_api_client import notification_api_client
from app.notify_client.service_api_client import service_api_client
-from app.utils import set_status_filters
+from app.utils import get_letter_printing_statement, set_status_filters
class Job(JSONModel):
@@ -159,6 +160,20 @@ class Job(JSONModel):
return True
+ @property
+ def letter_printing_statement(self):
+ if self.upload_type != 'letter_day':
+ raise TypeError()
+ return get_letter_printing_statement(
+ 'created',
+ # We have to make the time just before 5:30pm because a
+ # letter uploaded at 5:30pm will be printed the next day
+ (
+ utc_string_to_aware_gmt_datetime(self.created_at) - timedelta(minutes=1)
+ ).astimezone(pytz.utc).isoformat(),
+ long_form=False,
+ )
+
@cached_property
def all_notifications(self):
return self.get_notifications(set_status_filters({}))['notifications']
diff --git a/app/templates/views/dashboard/_jobs.html b/app/templates/views/dashboard/_jobs.html
index f7d6ab271..cfda4345b 100644
--- a/app/templates/views/dashboard/_jobs.html
+++ b/app/templates/views/dashboard/_jobs.html
@@ -18,7 +18,9 @@
) %}
{% call row_heading() %}
- {% if item.upload_type == 'letter' %}
+ {% if item.upload_type == 'letter_day' %}
+
{{ item.original_file_name }}
+ {% elif item.upload_type == 'letter' %}
{{ item.original_file_name }}
{% elif item.upload_type == 'contact_list' %}
{{ item.original_file_name }}
@@ -37,6 +39,10 @@
item.created_at|format_datetime_relative
}}
+ {% elif item.upload_type == 'letter_day' %}
+
+ {{ item.letter_printing_statement }}
+
{% else %}
Sent {{
diff --git a/app/utils.py b/app/utils.py
index b4a92d5a8..6de6baf3b 100644
--- a/app/utils.py
+++ b/app/utils.py
@@ -2,7 +2,7 @@ import csv
import os
import re
import unicodedata
-from datetime import datetime, time, timedelta, timezone
+from datetime import datetime, timedelta, timezone
from functools import wraps
from io import BytesIO, StringIO
from itertools import chain
@@ -14,6 +14,7 @@ import ago
import dateutil
import pyexcel
import pyexcel_xlsx
+import pytz
from dateutil import parser
from flask import abort, current_app, redirect, request, session, url_for
from flask_login import current_user, login_required
@@ -33,6 +34,7 @@ from notifications_utils.template import (
SMSPreviewTemplate,
)
from notifications_utils.timezones import (
+ convert_bst_to_utc,
convert_utc_to_bst,
utc_string_to_aware_gmt_datetime,
)
@@ -550,11 +552,13 @@ def get_default_sms_sender(sms_senders):
), "None"))
-def printing_today_or_tomorrow():
- now_utc = datetime.utcnow()
- now_bst = convert_utc_to_bst(now_utc)
+def printing_today_or_tomorrow(created_at):
+ print_cutoff = convert_bst_to_utc(
+ convert_utc_to_bst(datetime.utcnow()).replace(hour=17, minute=30)
+ ).replace(tzinfo=pytz.utc)
+ created_at = utc_string_to_aware_gmt_datetime(created_at)
- if now_bst.time() < time(17, 30):
+ if created_at < print_cutoff:
return 'today'
else:
return 'tomorrow'
@@ -569,10 +573,11 @@ def redact_mobile_number(mobile_number, spacing=""):
return "".join(mobile_number_list)
-def get_letter_printing_statement(status, created_at):
+def get_letter_printing_statement(status, created_at, long_form=True):
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())
+ decription = 'Printing starts' if long_form else 'Printing'
+ return f'{decription} {printing_today_or_tomorrow(created_at)} at 5:30pm'
else:
printed_datetime = utc_string_to_aware_gmt_datetime(created_at) + timedelta(hours=6, minutes=30)
if printed_datetime.date() == datetime.now().date():
@@ -581,8 +586,9 @@ def get_letter_printing_statement(status, created_at):
return 'Printed yesterday at 5:30pm'
printed_date = printed_datetime.strftime('%d %B').lstrip('0')
+ description = 'Printed on' if long_form else 'Printed'
- return 'Printed on {} at 5:30pm'.format(printed_date)
+ return f'{description} {printed_date} at 5:30pm'
LETTER_VALIDATION_MESSAGES = {
diff --git a/tests/app/main/views/test_uploads.py b/tests/app/main/views/test_uploads.py
index 528ef0f17..3a6985a2b 100644
--- a/tests/app/main/views/test_uploads.py
+++ b/tests/app/main/views/test_uploads.py
@@ -91,6 +91,7 @@ def test_get_upload_hub_with_no_uploads(
assert not page.select('.file-list-filename')
+@freeze_time('2017-10-10 10:10:10')
def test_get_upload_hub_page(
mocker,
client_request,
@@ -108,28 +109,41 @@ def test_get_upload_hub_page(
uploads = page.select('tbody tr')
+ assert len(uploads) == 3
+
assert normalize_spaces(uploads[0].text.strip()) == (
+ 'Uploaded letters '
+ 'Printing today at 5:30pm '
+ '33 letters'
+ )
+ assert uploads[0].select_one('a.file-list-filename-large')['href'] == url_for(
+ 'main.uploaded_letters',
+ service_id=SERVICE_ONE_ID,
+ letter_print_day='2017-10-10',
+ )
+
+ assert normalize_spaces(uploads[1].text.strip()) == (
'some.csv '
'Sent 1 January 2016 at 11:09am '
'0 sending 8 delivered 2 failed'
)
- assert uploads[0].select_one('a.file-list-filename-large')['href'] == (
+ assert uploads[1].select_one('a.file-list-filename-large')['href'] == (
'/services/{}/jobs/job_id_1'.format(SERVICE_ONE_ID)
)
- assert normalize_spaces(uploads[1].text.strip()) == (
+ assert normalize_spaces(uploads[2].text.strip()) == (
'some.pdf '
'Sent 1 January 2016 at 11:09am '
'Firstname Lastname '
'123 Example Street'
)
- assert normalize_spaces(str(uploads[1].select_one('.govuk-body'))) == (
+ assert normalize_spaces(str(uploads[2].select_one('.govuk-body'))) == (
' '
'Firstname Lastname
'
'123 Example Street
'
'
'
)
- assert uploads[1].select_one('a.file-list-filename-large')['href'] == (
+ assert uploads[2].select_one('a.file-list-filename-large')['href'] == (
'/services/{}/notification/letter_id_1'.format(SERVICE_ONE_ID)
)
diff --git a/tests/app/test_utils.py b/tests/app/test_utils.py
index 061282eff..bedb4eb97 100644
--- a/tests/app/test_utils.py
+++ b/tests/app/test_utils.py
@@ -353,27 +353,27 @@ def test_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',
+ '2018-08-01T23:00:00+00:00',
+ '2018-08-01T16:29:00+00:00',
+ '2018-11-01T00:00:00+00:00',
+ '2018-11-01T10:00:00+00:00',
+ '2018-11-01T17:29:00+00:00',
])
def test_printing_today_or_tomorrow_returns_today(utc_datetime):
with freeze_time(utc_datetime):
- assert printing_today_or_tomorrow() == 'today'
+ assert printing_today_or_tomorrow(utc_datetime) == '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',
+@pytest.mark.parametrize('utc_datetime', [
+ '2018-08-01T22:59:00+00:00',
+ '2018-08-01T16:30:00+00:00',
+ '2018-11-01T17:30:00+00:00',
+ '2018-11-01T21:00:00+00:00',
+ '2018-11-01T23:59:00+00:00',
])
-def test_printing_today_or_tomorrow_returns_tomorrow(datetime):
- with freeze_time(datetime):
- assert printing_today_or_tomorrow() == 'tomorrow'
+def test_printing_today_or_tomorrow_returns_tomorrow(utc_datetime):
+ with freeze_time(utc_datetime):
+ assert printing_today_or_tomorrow(utc_datetime) == 'tomorrow'
@pytest.mark.parametrize('created_at, current_datetime', [
diff --git a/tests/conftest.py b/tests/conftest.py
index 48578599b..bcf826850 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1795,28 +1795,46 @@ def mock_get_jobs(mocker, api_user_active):
@pytest.fixture(scope='function')
def mock_get_uploads(mocker, api_user_active):
def _get_uploads(service_id, limit_days=None, statuses=None, page=1):
- uploads = [{'id': 'job_id_1',
- 'original_file_name': 'some.csv',
- 'notification_count': 10,
- 'created_at': '2016-01-01 11:09:00.061258',
- 'statistics': [{'count': 8, 'status': 'delivered'}, {'count': 2, 'status': 'temporary-failure'}],
- 'upload_type': 'job',
- 'template_type': 'sms',
- 'recipient': None},
- {'id': 'letter_id_1',
- 'original_file_name': 'some.pdf',
- 'notification_count': 1,
- 'created_at': '2016-01-01 11:09:00.061258',
- 'statistics': [{'count': 1, 'status': 'delivered'}],
- 'upload_type': 'letter',
- 'template_type': None,
- 'recipient': (
- 'Firstname Lastname\n'
- '123 Example Street\n'
- 'City of Town\n'
- 'XM4 5QQ'
- )}
- ]
+ uploads = [
+ {
+ 'id': None,
+ 'original_file_name': 'Uploaded letters',
+ 'recipient': None,
+ 'notification_count': 33,
+ 'template_type': 'letter',
+ 'created_at': '2017-10-10 16:30:00',
+ 'statistics': [],
+ 'upload_type': 'letter_day',
+ },
+ {
+ 'id': 'job_id_1',
+ 'original_file_name': 'some.csv',
+ 'notification_count': 10,
+ 'created_at': '2016-01-01 11:09:00.061258',
+ 'statistics': [
+ {'count': 8, 'status': 'delivered'},
+ {'count': 2, 'status': 'temporary-failure'}
+ ],
+ 'upload_type': 'job',
+ 'template_type': 'sms',
+ 'recipient': None,
+ },
+ {
+ 'id': 'letter_id_1',
+ 'original_file_name': 'some.pdf',
+ 'notification_count': 1,
+ 'created_at': '2016-01-01 11:09:00.061258',
+ 'statistics': [{'count': 1, 'status': 'delivered'}],
+ 'upload_type': 'letter',
+ 'template_type': None,
+ 'recipient': (
+ 'Firstname Lastname\n'
+ '123 Example Street\n'
+ 'City of Town\n'
+ 'XM4 5QQ'
+ ),
+ },
+ ]
return {
'data': uploads,
'links': {