diff --git a/app/dao/uploads_dao.py b/app/dao/uploads_dao.py index 0bed6a1fa..994acbbcc 100644 --- a/app/dao/uploads_dao.py +++ b/app/dao/uploads_dao.py @@ -117,3 +117,23 @@ def dao_get_uploads_by_service_id(service_id, limit_days=None, page=1, page_size ).order_by( desc("processing_started"), desc("created_at") ).paginate(page=page, per_page=page_size) + + +def dao_get_uploaded_letters_by_print_date(service_id, letter_print_date, page=1, page_size=50): + return db.session.query( + Notification, + ).join( + Template, Notification.template_id == Template.id + ).filter( + Notification.service_id == service_id, + Notification.notification_type == LETTER_TYPE, + Notification.api_key_id.is_(None), + Notification.status != NOTIFICATION_CANCELLED, + Template.hidden.is_(True), + _get_printing_day(Notification.created_at) == letter_print_date.date(), + ).order_by( + desc(Notification.created_at) + ).paginate( + page=page, + per_page=page_size, + ) diff --git a/app/upload/rest.py b/app/upload/rest.py index 7ba0dc8fc..fe013d21a 100644 --- a/app/upload/rest.py +++ b/app/upload/rest.py @@ -1,5 +1,7 @@ +from datetime import datetime from flask import ( Blueprint, + abort, jsonify, request, current_app @@ -9,10 +11,14 @@ from app.dao.fact_notification_status_dao import fetch_notification_statuses_for from app.dao.jobs_dao import ( dao_get_notification_outcomes_for_job ) -from app.dao.uploads_dao import dao_get_uploads_by_service_id +from app.dao.uploads_dao import ( + dao_get_uploaded_letters_by_print_date, + dao_get_uploads_by_service_id, +) from app.errors import ( register_errors, ) +from app.schemas import notification_with_template_schema from app.utils import pagination_links, midnight_n_days_ago upload_blueprint = Blueprint('upload', __name__, url_prefix='/service//upload') @@ -74,3 +80,31 @@ def get_paginated_uploads(service_id, limit_days, page): service_id=service_id ) } + + +@upload_blueprint.route('/uploaded-letters/', methods=['GET']) +def get_uploaded_letter_by_service_and_print_day(service_id, letter_print_date): + try: + letter_print_datetime = datetime.strptime(letter_print_date, '%Y-%m-%d') + except ValueError: + abort(404) + pagination = dao_get_uploaded_letters_by_print_date( + service_id, + letter_print_date=letter_print_datetime, + page=request.args.get('page', type=int), + page_size=current_app.config['PAGE_SIZE'] + ) + return jsonify({ + 'notifications': notification_with_template_schema.dump( + pagination.items, + many=True, + ).data, + 'page_size': pagination.per_page, + 'total': pagination.total, + 'links': pagination_links( + pagination, + '.get_uploaded_letter_by_service_and_print_day', + service_id=service_id, + letter_print_date=letter_print_date, + ), + }) diff --git a/tests/app/dao/test_uploads_dao.py b/tests/app/dao/test_uploads_dao.py index e9c8d5cb7..201904674 100644 --- a/tests/app/dao/test_uploads_dao.py +++ b/tests/app/dao/test_uploads_dao.py @@ -1,7 +1,7 @@ from datetime import datetime, timedelta from freezegun import freeze_time -from app.dao.uploads_dao import dao_get_uploads_by_service_id +from app.dao.uploads_dao import dao_get_uploads_by_service_id, dao_get_uploaded_letters_by_print_date from app.models import LETTER_TYPE, JOB_STATUS_IN_PROGRESS from tests.app.db import create_job, create_service, create_service_data_retention, create_template, create_notification @@ -302,3 +302,56 @@ def test_get_uploads_is_paginated(sample_template): def test_get_uploads_returns_empty_list(sample_service): items = dao_get_uploads_by_service_id(sample_service.id).items assert items == [] + + +@freeze_time('2020-02-02 14:00') +def test_get_uploaded_letters_by_print_date(sample_template): + letter_template = create_uploaded_template(sample_template.service) + + # Letters for the previous day’s run + for i in range(3): + create_uploaded_letter( + letter_template, sample_template.service, status='delivered', + created_at=datetime.utcnow().replace(day=1, hour=17, minute=29, second=59) + ) + + # Letters from yesterday that rolled into today’s run + for i in range(30): + create_uploaded_letter( + letter_template, sample_template.service, status='delivered', + created_at=datetime.utcnow().replace(day=1, hour=17, minute=30, second=0) + ) + + # Letters that just made today’s run + for i in range(30): + create_uploaded_letter( + letter_template, sample_template.service, status='delivered', + created_at=datetime.utcnow().replace(hour=17, minute=29, second=59) + ) + + # Letters that just missed today’s run + for i in range(3): + create_uploaded_letter( + letter_template, sample_template.service, status='delivered', + created_at=datetime.utcnow().replace(hour=17, minute=30, second=0) + ) + + result = dao_get_uploaded_letters_by_print_date( + sample_template.service_id, + datetime.utcnow(), + ) + assert result.total == 60 + assert len(result.items) == 50 + assert result.has_next is True + assert result.has_prev is False + + result = dao_get_uploaded_letters_by_print_date( + sample_template.service_id, + datetime.utcnow(), + page=10, + page_size=2, + ) + assert result.total == 60 + assert len(result.items) == 2 + assert result.has_next is True + assert result.has_prev is True diff --git a/tests/app/upload/test_rest.py b/tests/app/upload/test_rest.py index d9cb78ba9..814e15575 100644 --- a/tests/app/upload/test_rest.py +++ b/tests/app/upload/test_rest.py @@ -171,3 +171,79 @@ def test_get_uploads_should_retrieve_from_ft_notification_status_for_old_jobs(ad assert resp_json[1]['statistics'] == [{'status': 'created', 'count': 1}] assert resp_json[2]['id'] == str(job_1.id) assert resp_json[2]['statistics'] == [{'status': 'delivered', 'count': 6}] + + +@freeze_time('2020-02-02 14:00') +def test_get_uploaded_letters_by_print_date(admin_request, sample_template): + letter_template = create_precompiled_template(sample_template.service) + + letter_1 = create_uploaded_letter( + letter_template, sample_template.service, status='delivered', + created_at=datetime.utcnow() - timedelta(minutes=1) + ) + letter_2 = create_uploaded_letter( + letter_template, sample_template.service, status='delivered', + created_at=datetime.utcnow() - timedelta(minutes=2) + ) + + service_id = sample_template.service.id + + resp_json = admin_request.get( + 'upload.get_uploaded_letter_by_service_and_print_day', + service_id=service_id, + letter_print_date='2020-02-02', + ) + assert resp_json['total'] == 2 + assert resp_json['page_size'] == 50 + assert len(resp_json['notifications']) == 2 + + assert resp_json['notifications'][0]['id'] == str(letter_1.id) + assert resp_json['notifications'][0]['created_at'] == letter_1.created_at.strftime( + '%Y-%m-%dT%H:%M:%S+00:00' + ) + + assert resp_json['notifications'][1]['id'] == str(letter_2.id) + assert resp_json['notifications'][1]['created_at'] == letter_2.created_at.strftime( + '%Y-%m-%dT%H:%M:%S+00:00' + ) + + +@freeze_time('2020-02-02 14:00') +def test_get_uploaded_letters_by_print_date_paginates(admin_request, sample_template): + letter_template = create_precompiled_template(sample_template.service) + + for i in range(101): + create_uploaded_letter( + letter_template, sample_template.service, status='delivered', + created_at=datetime.utcnow() - timedelta(minutes=1) + ) + + service_id = sample_template.service.id + + resp_json = admin_request.get( + 'upload.get_uploaded_letter_by_service_and_print_day', + service_id=service_id, + letter_print_date='2020-02-02', + page=2, + ) + assert resp_json['total'] == 101 + assert resp_json['page_size'] == 50 + assert len(resp_json['notifications']) == 50 + assert resp_json['links']['prev'] == ( + f'/service/{service_id}/upload/uploaded-letters/2020-02-02?page=1' + ) + assert resp_json['links']['next'] == ( + f'/service/{service_id}/upload/uploaded-letters/2020-02-02?page=3' + ) + + +def test_get_uploaded_letters_by_print_date_404s_for_bad_date( + admin_request, + sample_service, +): + admin_request.get( + 'upload.get_uploaded_letter_by_service_and_print_day', + service_id=sample_service.id, + letter_print_date='foo', + _expected_status=404, + )