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:
Pea M. Tyczynska
2020-07-08 11:49:21 +01:00
committed by GitHub
3 changed files with 106 additions and 43 deletions

View File

@@ -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:

View File

@@ -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()

View File

@@ -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'