diff --git a/app/dao/returned_letters_dao.py b/app/dao/returned_letters_dao.py index 09dc95e00..c7a445156 100644 --- a/app/dao/returned_letters_dao.py +++ b/app/dao/returned_letters_dao.py @@ -13,6 +13,7 @@ from app.models import ( Template, User, ) +from app.utils import midnight_n_days_ago def _get_notification_ids_for_references(references): @@ -50,6 +51,25 @@ def insert_or_update_returned_letters(references): db.session.connection().execute(stmt) +def fetch_returned_letter_count(service_id): + return db.session.query( + func.count(ReturnedLetter.notification_id).label('returned_letter_count'), + ).filter( + ReturnedLetter.service_id == service_id, + ReturnedLetter.reported_at > midnight_n_days_ago(7), + ).one() + + +def fetch_most_recent_returned_letter(service_id): + return db.session.query( + ReturnedLetter.reported_at, + ).filter( + ReturnedLetter.service_id == service_id, + ).order_by( + desc(ReturnedLetter.reported_at) + ).first() + + def fetch_returned_letter_summary(service_id): return db.session.query( func.count(ReturnedLetter.notification_id).label('returned_letter_count'), diff --git a/app/service/rest.py b/app/service/rest.py index d0c46607d..2ea5a0e53 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -31,8 +31,10 @@ from app.dao.fact_notification_status_dao import ( from app.dao.inbound_numbers_dao import dao_allocate_number_for_service from app.dao.organisation_dao import dao_get_organisation_by_service_id from app.dao.returned_letters_dao import ( + fetch_most_recent_returned_letter, + fetch_returned_letter_count, fetch_returned_letter_summary, - fetch_returned_letters + fetch_returned_letters, ) from app.dao.service_data_retention_dao import ( fetch_service_data_retention, @@ -946,6 +948,25 @@ def check_if_reply_to_address_already_in_use(service_id, email_address): ) +@service_blueprint.route('//returned-letter-statistics', methods=['GET']) +def returned_letter_statistics(service_id): + + most_recent = fetch_most_recent_returned_letter(service_id) + + if not most_recent: + return jsonify({ + 'returned_letter_count': 0, + 'most_recent_report': None, + }) + + count = fetch_returned_letter_count(service_id) + + return jsonify({ + 'returned_letter_count': count.returned_letter_count, + 'most_recent_report': most_recent.reported_at.strftime(DATETIME_FORMAT_NO_TIMEZONE), + }) + + @service_blueprint.route('//returned-letter-summary', methods=['GET']) def returned_letter_summary(service_id): results = fetch_returned_letter_summary(service_id) diff --git a/tests/app/dao/test_returned_letters_dao.py b/tests/app/dao/test_returned_letters_dao.py index 17312ddfa..368f89175 100644 --- a/tests/app/dao/test_returned_letters_dao.py +++ b/tests/app/dao/test_returned_letters_dao.py @@ -1,14 +1,22 @@ +import uuid from datetime import datetime, timedelta, date from freezegun import freeze_time from app.dao.returned_letters_dao import ( insert_or_update_returned_letters, + fetch_most_recent_returned_letter, + fetch_returned_letter_count, fetch_returned_letter_summary, fetch_returned_letters ) from app.models import ReturnedLetter, NOTIFICATION_RETURNED_LETTER -from tests.app.db import create_notification, create_notification_history, create_returned_letter +from tests.app.db import ( + create_notification, + create_notification_history, + create_returned_letter, + create_service, +) def test_insert_or_update_returned_letters_inserts(sample_letter_template): @@ -90,6 +98,62 @@ def test_insert_or_update_returned_letters_with_duplicates_in_reference_list(sam assert x.notification_id in [notification_1.id, notification_2.id] +def test_get_returned_letter_count(sample_service): + # Before 7 days – don’t count + create_returned_letter( + sample_service, + reported_at=datetime(2001, 1, 1) + ) + create_returned_letter( + sample_service, + reported_at=datetime(2010, 11, 1, 23, 59, 59), + ) + # In the last 7 days – count + create_returned_letter( + sample_service, + reported_at=datetime(2010, 11, 2, 0, 0, 0), + ) + create_returned_letter( + sample_service, + reported_at=datetime(2010, 11, 8, 10, 0), + ) + create_returned_letter( + sample_service, + reported_at=datetime(2010, 11, 8, 10, 0), + ) + # Different service – don’t count + create_returned_letter( + create_service(service_id=uuid.uuid4(), service_name='Other service'), + reported_at=datetime(2010, 11, 8, 10, 0), + ) + + with freeze_time('2010-11-08 10:10'): + result = fetch_returned_letter_count(sample_service.id) + + assert result.returned_letter_count == 3 + + +def test_fetch_most_recent_returned_letter_for_service(sample_service): + # Older + create_returned_letter( + sample_service, + reported_at=datetime(2009, 9, 9, 9, 9), + ) + # Newer + create_returned_letter( + sample_service, + reported_at=datetime(2010, 10, 10, 10, 10), + ) + # Newest, but different service + create_returned_letter( + create_service(service_id=uuid.uuid4(), service_name='Other service'), + reported_at=datetime(2011, 11, 11, 11, 11), + ) + result = fetch_most_recent_returned_letter(sample_service.id) + + assert str(result.reported_at) == '2010-10-10' + + def test_get_returned_letter_summary(sample_service): now = datetime.utcnow() create_returned_letter(sample_service, reported_at=now) diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 5d55b2572..fc643520c 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -3382,6 +3382,20 @@ def test_get_monthly_notification_data_by_service(mocker, admin_request): assert response == [] +@freeze_time('2019-12-11 13:30') +def test_get_returned_letter_statistics(admin_request, sample_service): + create_returned_letter(sample_service, reported_at=datetime.utcnow() - timedelta(days=3)) + create_returned_letter(sample_service, reported_at=datetime.utcnow() - timedelta(days=2)) + create_returned_letter(sample_service, reported_at=datetime.utcnow() - timedelta(days=1)) + + response = admin_request.get('service.returned_letter_statistics', service_id=sample_service.id) + + assert response == { + 'returned_letter_count': 3, + 'most_recent_report': '2019-12-10 00:00:00.000000' + } + + @freeze_time('2019-12-11 13:30') def test_get_returned_letter_summary(admin_request, sample_service): create_returned_letter(sample_service, reported_at=datetime.utcnow() - timedelta(days=3))