mirror of
https://github.com/GSA/notifications-api.git
synced 2026-08-19 14:08:47 -04:00
Merge pull request #2796 from alphagov/split-letters-into-zips-based-on-postage
Split letters into zips based on postage
This commit is contained in:
@@ -42,6 +42,8 @@ from app.models import (
|
|||||||
NOTIFICATION_TECHNICAL_FAILURE,
|
NOTIFICATION_TECHNICAL_FAILURE,
|
||||||
NOTIFICATION_VALIDATION_FAILED,
|
NOTIFICATION_VALIDATION_FAILED,
|
||||||
NOTIFICATION_VIRUS_SCAN_FAILED,
|
NOTIFICATION_VIRUS_SCAN_FAILED,
|
||||||
|
POSTAGE_TYPES,
|
||||||
|
RESOLVE_POSTAGE_FOR_FILE_NAME
|
||||||
)
|
)
|
||||||
from app.cronitor import cronitor
|
from app.cronitor import cronitor
|
||||||
|
|
||||||
@@ -134,41 +136,41 @@ def collate_letter_pdfs_to_be_sent():
|
|||||||
print_run_deadline = print_run_date.replace(
|
print_run_deadline = print_run_date.replace(
|
||||||
hour=17, minute=30, second=0, microsecond=0
|
hour=17, minute=30, second=0, microsecond=0
|
||||||
)
|
)
|
||||||
|
for postage in POSTAGE_TYPES:
|
||||||
|
letters_to_print = get_key_and_size_of_letters_to_be_sent_to_print(print_run_deadline, postage)
|
||||||
|
|
||||||
letters_to_print = get_key_and_size_of_letters_to_be_sent_to_print(print_run_deadline)
|
for i, letters in enumerate(group_letters(letters_to_print)):
|
||||||
|
filenames = [letter['Key'] for letter in letters]
|
||||||
|
|
||||||
for i, letters in enumerate(group_letters(letters_to_print)):
|
hash = urlsafe_b64encode(sha512(''.join(filenames).encode()).digest())[:20].decode()
|
||||||
filenames = [letter['Key'] for letter in letters]
|
# eg NOTIFY.2018-12-31.001.Wjrui5nAvObjPd-3GEL-.ZIP
|
||||||
|
dvla_filename = 'NOTIFY.{date}.{postage}.{num:03}.{hash}.ZIP'.format(
|
||||||
hash = urlsafe_b64encode(sha512(''.join(filenames).encode()).digest())[:20].decode()
|
date=print_run_deadline.strftime("%Y-%m-%d"),
|
||||||
# eg NOTIFY.2018-12-31.001.Wjrui5nAvObjPd-3GEL-.ZIP
|
postage=RESOLVE_POSTAGE_FOR_FILE_NAME[postage],
|
||||||
dvla_filename = 'NOTIFY.{date}.{num:03}.{hash}.ZIP'.format(
|
num=i + 1,
|
||||||
date=print_run_deadline.strftime("%Y-%m-%d"),
|
hash=hash
|
||||||
num=i + 1,
|
)
|
||||||
hash=hash
|
|
||||||
)
|
current_app.logger.info(
|
||||||
|
'Calling task zip-and-send-letter-pdfs for {} pdfs to upload {} with total size {:,} bytes'.format(
|
||||||
current_app.logger.info(
|
len(filenames),
|
||||||
'Calling task zip-and-send-letter-pdfs for {} pdfs to upload {} with total size {:,} bytes'.format(
|
dvla_filename,
|
||||||
len(filenames),
|
sum(letter['Size'] for letter in letters)
|
||||||
dvla_filename,
|
)
|
||||||
sum(letter['Size'] for letter in letters)
|
)
|
||||||
|
notify_celery.send_task(
|
||||||
|
name=TaskNames.ZIP_AND_SEND_LETTER_PDFS,
|
||||||
|
kwargs={
|
||||||
|
'filenames_to_zip': filenames,
|
||||||
|
'upload_filename': dvla_filename
|
||||||
|
},
|
||||||
|
queue=QueueNames.PROCESS_FTP,
|
||||||
|
compression='zlib'
|
||||||
)
|
)
|
||||||
)
|
|
||||||
notify_celery.send_task(
|
|
||||||
name=TaskNames.ZIP_AND_SEND_LETTER_PDFS,
|
|
||||||
kwargs={
|
|
||||||
'filenames_to_zip': filenames,
|
|
||||||
'upload_filename': dvla_filename
|
|
||||||
},
|
|
||||||
queue=QueueNames.PROCESS_FTP,
|
|
||||||
compression='zlib'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_key_and_size_of_letters_to_be_sent_to_print(print_run_deadline):
|
def get_key_and_size_of_letters_to_be_sent_to_print(print_run_deadline, postage):
|
||||||
letters_awaiting_sending = dao_get_letters_to_be_printed(print_run_deadline)
|
letters_awaiting_sending = dao_get_letters_to_be_printed(print_run_deadline, postage)
|
||||||
|
|
||||||
letter_pdfs = []
|
letter_pdfs = []
|
||||||
for letter in letters_awaiting_sending:
|
for letter in letters_awaiting_sending:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -733,7 +733,7 @@ def notifications_not_yet_sent(should_be_sending_after_seconds, notification_typ
|
|||||||
return notifications
|
return notifications
|
||||||
|
|
||||||
|
|
||||||
def dao_get_letters_to_be_printed(print_run_deadline):
|
def dao_get_letters_to_be_printed(print_run_deadline, postage):
|
||||||
"""
|
"""
|
||||||
Return all letters created before the print run deadline that have not yet been sent
|
Return all letters created before the print run deadline that have not yet been sent
|
||||||
"""
|
"""
|
||||||
@@ -741,7 +741,8 @@ def dao_get_letters_to_be_printed(print_run_deadline):
|
|||||||
Notification.created_at < convert_bst_to_utc(print_run_deadline),
|
Notification.created_at < convert_bst_to_utc(print_run_deadline),
|
||||||
Notification.notification_type == LETTER_TYPE,
|
Notification.notification_type == LETTER_TYPE,
|
||||||
Notification.status == NOTIFICATION_CREATED,
|
Notification.status == NOTIFICATION_CREATED,
|
||||||
Notification.key_type == KEY_TYPE_NORMAL
|
Notification.key_type == KEY_TYPE_NORMAL,
|
||||||
|
Notification.postage == postage,
|
||||||
).order_by(
|
).order_by(
|
||||||
Notification.created_at
|
Notification.created_at
|
||||||
).all()
|
).all()
|
||||||
|
|||||||
@@ -155,20 +155,19 @@ def test_update_billable_units_for_letter_doesnt_update_if_sent_with_test_key(mo
|
|||||||
|
|
||||||
@freeze_time('2020-02-17 18:00:00')
|
@freeze_time('2020-02-17 18:00:00')
|
||||||
def test_get_key_and_size_of_letters_to_be_sent_to_print(notify_api, mocker, sample_letter_template):
|
def test_get_key_and_size_of_letters_to_be_sent_to_print(notify_api, mocker, sample_letter_template):
|
||||||
|
# second class
|
||||||
create_notification(
|
create_notification(
|
||||||
template=sample_letter_template,
|
template=sample_letter_template,
|
||||||
status='created',
|
status='created',
|
||||||
reference='ref0',
|
reference='ref0',
|
||||||
created_at=(datetime.now() - timedelta(hours=2))
|
created_at=(datetime.now() - timedelta(hours=2))
|
||||||
)
|
)
|
||||||
|
|
||||||
create_notification(
|
create_notification(
|
||||||
template=sample_letter_template,
|
template=sample_letter_template,
|
||||||
status='created',
|
status='created',
|
||||||
reference='ref1',
|
reference='ref1',
|
||||||
created_at=(datetime.now() - timedelta(hours=3))
|
created_at=(datetime.now() - timedelta(hours=3))
|
||||||
)
|
)
|
||||||
|
|
||||||
create_notification(
|
create_notification(
|
||||||
template=sample_letter_template,
|
template=sample_letter_template,
|
||||||
status='created',
|
status='created',
|
||||||
@@ -208,7 +207,7 @@ def test_get_key_and_size_of_letters_to_be_sent_to_print(notify_api, mocker, sam
|
|||||||
{'ContentLength': 3},
|
{'ContentLength': 3},
|
||||||
])
|
])
|
||||||
|
|
||||||
results = get_key_and_size_of_letters_to_be_sent_to_print(datetime.now() - timedelta(minutes=30))
|
results = get_key_and_size_of_letters_to_be_sent_to_print(datetime.now() - timedelta(minutes=30), postage='second')
|
||||||
|
|
||||||
assert mock_s3.call_count == 3
|
assert mock_s3.call_count == 3
|
||||||
mock_s3.assert_has_calls(
|
mock_s3.assert_has_calls(
|
||||||
@@ -220,6 +219,7 @@ def test_get_key_and_size_of_letters_to_be_sent_to_print(notify_api, mocker, sam
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert len(results) == 3
|
assert len(results) == 3
|
||||||
|
|
||||||
assert results == [
|
assert results == [
|
||||||
{'Key': '2020-02-16/NOTIFY.REF2.D.2.C.C.20200215180000.PDF', 'Size': 2},
|
{'Key': '2020-02-16/NOTIFY.REF2.D.2.C.C.20200215180000.PDF', 'Size': 2},
|
||||||
{'Key': '2020-02-17/NOTIFY.REF1.D.2.C.C.20200217150000.PDF', 'Size': 1},
|
{'Key': '2020-02-17/NOTIFY.REF1.D.2.C.C.20200217150000.PDF', 'Size': 1},
|
||||||
@@ -256,7 +256,7 @@ def test_get_key_and_size_of_letters_to_be_sent_to_print_catches_exception(
|
|||||||
ClientError(error_response, "File not found")
|
ClientError(error_response, "File not found")
|
||||||
])
|
])
|
||||||
|
|
||||||
results = get_key_and_size_of_letters_to_be_sent_to_print(datetime.now() - timedelta(minutes=30))
|
results = get_key_and_size_of_letters_to_be_sent_to_print(datetime.now() - timedelta(minutes=30), postage='second')
|
||||||
|
|
||||||
assert mock_head_s3_object.call_count == 2
|
assert mock_head_s3_object.call_count == 2
|
||||||
mock_head_s3_object.assert_has_calls(
|
mock_head_s3_object.assert_has_calls(
|
||||||
@@ -275,20 +275,19 @@ def test_get_key_and_size_of_letters_to_be_sent_to_print_catches_exception(
|
|||||||
])
|
])
|
||||||
def test_collate_letter_pdfs_to_be_sent(notify_api, sample_letter_template, mocker, time_to_run_task):
|
def test_collate_letter_pdfs_to_be_sent(notify_api, sample_letter_template, mocker, time_to_run_task):
|
||||||
with freeze_time("2020-02-17 18:00:00"):
|
with freeze_time("2020-02-17 18:00:00"):
|
||||||
|
# second class
|
||||||
create_notification(
|
create_notification(
|
||||||
template=sample_letter_template,
|
template=sample_letter_template,
|
||||||
status='created',
|
status='created',
|
||||||
reference='ref0',
|
reference='ref0',
|
||||||
created_at=(datetime.now() - timedelta(hours=2))
|
created_at=(datetime.now() - timedelta(hours=2))
|
||||||
)
|
)
|
||||||
|
|
||||||
create_notification(
|
create_notification(
|
||||||
template=sample_letter_template,
|
template=sample_letter_template,
|
||||||
status='created',
|
status='created',
|
||||||
reference='ref1',
|
reference='ref1',
|
||||||
created_at=(datetime.now() - timedelta(hours=3))
|
created_at=(datetime.now() - timedelta(hours=3))
|
||||||
)
|
)
|
||||||
|
|
||||||
create_notification(
|
create_notification(
|
||||||
template=sample_letter_template,
|
template=sample_letter_template,
|
||||||
status='created',
|
status='created',
|
||||||
@@ -296,10 +295,38 @@ def test_collate_letter_pdfs_to_be_sent(notify_api, sample_letter_template, mock
|
|||||||
created_at=(datetime.now() - timedelta(days=2))
|
created_at=(datetime.now() - timedelta(days=2))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# first class
|
||||||
|
create_notification(
|
||||||
|
template=sample_letter_template,
|
||||||
|
status='created',
|
||||||
|
reference='first_class',
|
||||||
|
created_at=(datetime.now() - timedelta(hours=4)),
|
||||||
|
postage="first"
|
||||||
|
)
|
||||||
|
|
||||||
|
# international
|
||||||
|
create_notification(
|
||||||
|
template=sample_letter_template,
|
||||||
|
status='created',
|
||||||
|
reference='international',
|
||||||
|
created_at=(datetime.now() - timedelta(days=3)),
|
||||||
|
postage="europe"
|
||||||
|
)
|
||||||
|
create_notification(
|
||||||
|
template=sample_letter_template,
|
||||||
|
status='created',
|
||||||
|
reference='international',
|
||||||
|
created_at=(datetime.now() - timedelta(days=4)),
|
||||||
|
postage="rest-of-world"
|
||||||
|
)
|
||||||
|
|
||||||
mocker.patch('app.celery.tasks.s3.head_s3_object', side_effect=[
|
mocker.patch('app.celery.tasks.s3.head_s3_object', side_effect=[
|
||||||
|
{'ContentLength': 1},
|
||||||
|
{'ContentLength': 1},
|
||||||
{'ContentLength': 2},
|
{'ContentLength': 2},
|
||||||
{'ContentLength': 1},
|
{'ContentLength': 1},
|
||||||
{'ContentLength': 3},
|
{'ContentLength': 3},
|
||||||
|
{'ContentLength': 1},
|
||||||
])
|
])
|
||||||
|
|
||||||
mock_celery = mocker.patch('app.celery.letters_pdf_tasks.notify_celery.send_task')
|
mock_celery = mocker.patch('app.celery.letters_pdf_tasks.notify_celery.send_task')
|
||||||
@@ -308,26 +335,59 @@ def test_collate_letter_pdfs_to_be_sent(notify_api, sample_letter_template, mock
|
|||||||
with freeze_time(time_to_run_task):
|
with freeze_time(time_to_run_task):
|
||||||
collate_letter_pdfs_to_be_sent()
|
collate_letter_pdfs_to_be_sent()
|
||||||
|
|
||||||
assert len(mock_celery.call_args_list) == 2
|
assert len(mock_celery.call_args_list) == 5
|
||||||
assert mock_celery.call_args_list[0] == call(
|
assert mock_celery.call_args_list[0] == call(
|
||||||
name='zip-and-send-letter-pdfs',
|
name='zip-and-send-letter-pdfs',
|
||||||
kwargs={
|
kwargs={
|
||||||
'filenames_to_zip': [
|
'filenames_to_zip': [
|
||||||
'2020-02-16/NOTIFY.REF2.D.2.C.C.20200215180000.PDF',
|
'2020-02-17/NOTIFY.FIRST_CLASS.D.1.C.C.20200217140000.PDF'
|
||||||
'2020-02-17/NOTIFY.REF1.D.2.C.C.20200217150000.PDF'
|
|
||||||
],
|
],
|
||||||
'upload_filename': 'NOTIFY.2020-02-17.001.k3x_WqC5KhB6e2DWv9Ma.ZIP'
|
'upload_filename': 'NOTIFY.2020-02-17.1.001.kHh01fdUxT9iEIYUt5Wx.ZIP'
|
||||||
},
|
},
|
||||||
queue='process-ftp-tasks',
|
queue='process-ftp-tasks',
|
||||||
compression='zlib'
|
compression='zlib'
|
||||||
)
|
)
|
||||||
assert mock_celery.call_args_list[1] == call(
|
assert mock_celery.call_args_list[1] == call(
|
||||||
|
name='zip-and-send-letter-pdfs',
|
||||||
|
kwargs={
|
||||||
|
'filenames_to_zip': [
|
||||||
|
'2020-02-16/NOTIFY.REF2.D.2.C.C.20200215180000.PDF',
|
||||||
|
'2020-02-17/NOTIFY.REF1.D.2.C.C.20200217150000.PDF'
|
||||||
|
],
|
||||||
|
'upload_filename': 'NOTIFY.2020-02-17.2.001.k3x_WqC5KhB6e2DWv9Ma.ZIP'
|
||||||
|
},
|
||||||
|
queue='process-ftp-tasks',
|
||||||
|
compression='zlib'
|
||||||
|
)
|
||||||
|
assert mock_celery.call_args_list[2] == call(
|
||||||
name='zip-and-send-letter-pdfs',
|
name='zip-and-send-letter-pdfs',
|
||||||
kwargs={
|
kwargs={
|
||||||
'filenames_to_zip': [
|
'filenames_to_zip': [
|
||||||
'2020-02-17/NOTIFY.REF0.D.2.C.C.20200217160000.PDF'
|
'2020-02-17/NOTIFY.REF0.D.2.C.C.20200217160000.PDF'
|
||||||
],
|
],
|
||||||
'upload_filename': 'NOTIFY.2020-02-17.002.J85cUw-FWlKuAIOcwdLS.ZIP'
|
'upload_filename': 'NOTIFY.2020-02-17.2.002.J85cUw-FWlKuAIOcwdLS.ZIP'
|
||||||
|
},
|
||||||
|
queue='process-ftp-tasks',
|
||||||
|
compression='zlib'
|
||||||
|
)
|
||||||
|
assert mock_celery.call_args_list[3] == call(
|
||||||
|
name='zip-and-send-letter-pdfs',
|
||||||
|
kwargs={
|
||||||
|
'filenames_to_zip': [
|
||||||
|
'2020-02-15/NOTIFY.INTERNATIONAL.D.E.C.C.20200214180000.PDF'
|
||||||
|
],
|
||||||
|
'upload_filename': 'NOTIFY.2020-02-17.E.001.4YajCZzgzIl7zf8bjWK2.ZIP'
|
||||||
|
},
|
||||||
|
queue='process-ftp-tasks',
|
||||||
|
compression='zlib'
|
||||||
|
)
|
||||||
|
assert mock_celery.call_args_list[4] == call(
|
||||||
|
name='zip-and-send-letter-pdfs',
|
||||||
|
kwargs={
|
||||||
|
'filenames_to_zip': [
|
||||||
|
'2020-02-14/NOTIFY.INTERNATIONAL.D.N.C.C.20200213180000.PDF',
|
||||||
|
],
|
||||||
|
'upload_filename': 'NOTIFY.2020-02-17.N.001.eSvP8Ph6EBKhh3k7BSA2.ZIP'
|
||||||
},
|
},
|
||||||
queue='process-ftp-tasks',
|
queue='process-ftp-tasks',
|
||||||
compression='zlib'
|
compression='zlib'
|
||||||
|
|||||||
Reference in New Issue
Block a user