diff --git a/app/dao/date_util.py b/app/dao/date_util.py index b2c4d32bc..1e93e845f 100644 --- a/app/dao/date_util.py +++ b/app/dao/date_util.py @@ -11,7 +11,7 @@ def get_months_for_financial_year(year): get_months_for_year(4, 13, year) + get_months_for_year(1, 4, year + 1) ) - if month < datetime.now() + if convert_bst_to_utc(month) < datetime.now() ] diff --git a/app/dao/fact_notification_status_dao.py b/app/dao/fact_notification_status_dao.py index 9d0041348..9884e2cbb 100644 --- a/app/dao/fact_notification_status_dao.py +++ b/app/dao/fact_notification_status_dao.py @@ -8,7 +8,7 @@ from sqlalchemy.types import DateTime from app import db from app.models import Notification, NotificationHistory, FactNotificationStatus -from app.utils import convert_bst_to_utc, get_london_midnight_in_utc, get_london_month_from_utc_column +from app.utils import convert_bst_to_utc, get_london_midnight_in_utc def fetch_notification_status_for_day(process_day, service_id=None): diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index c9c86cc4c..4aff00390 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -34,9 +34,7 @@ from app.models import ( EMAIL_TYPE, INTERNATIONAL_SMS_TYPE, KEY_TYPE_TEST, - NOTIFICATION_STATUS_TYPES, SMS_TYPE, - TEMPLATE_TYPES, LETTER_TYPE, ) from app.utils import get_london_month_from_utc_column, get_london_midnight_in_utc diff --git a/tests/app/dao/test_fact_notification_status_dao.py b/tests/app/dao/test_fact_notification_status_dao.py index 84fa0eae1..c09fb31a7 100644 --- a/tests/app/dao/test_fact_notification_status_dao.py +++ b/tests/app/dao/test_fact_notification_status_dao.py @@ -1,9 +1,14 @@ -from datetime import timedelta, datetime +from datetime import timedelta, datetime, date from uuid import UUID -from app.dao.fact_notification_status_dao import update_fact_notification_status, fetch_notification_status_for_day +from app.dao.fact_notification_status_dao import ( + update_fact_notification_status, + fetch_notification_status_for_day, + fetch_notification_status_for_service_by_month, + fetch_notification_status_for_service_for_day, +) from app.models import FactNotificationStatus -from tests.app.db import create_notification, create_service, create_template +from tests.app.db import create_notification, create_service, create_template, create_ft_notification_status def test_update_fact_notification_status(notify_db_session): @@ -80,3 +85,87 @@ def test__update_fact_notification_status_updates_row(notify_db_session): ).all() assert len(updated_fact_data) == 1 assert updated_fact_data[0].notification_count == 2 + + +def test_fetch_notification_status_for_service_by_month(notify_db_session): + service_1 = create_service(service_name='service_1') + service_2 = create_service(service_name='service_2') + + create_ft_notification_status(date(2018, 1, 1), 'sms', service_1, count=4) + create_ft_notification_status(date(2018, 1, 2), 'sms', service_1, count=10) + create_ft_notification_status(date(2018, 1, 2), 'sms', service_1, notification_status='created') + create_ft_notification_status(date(2018, 1, 3), 'email', service_1) + + create_ft_notification_status(date(2018, 2, 2), 'sms', service_1) + + # not included - too early + create_ft_notification_status(date(2017, 12, 31), 'sms', service_1) + # not included - too late + create_ft_notification_status(date(2017, 3, 1), 'sms', service_1) + # not included - wrong service + create_ft_notification_status(date(2018, 1, 3), 'sms', service_2) + + results = sorted( + fetch_notification_status_for_service_by_month(date(2018, 1, 1), date(2018, 2, 28), service_1.id), + key=lambda x: (x.month, x.notification_type, x.notification_status) + ) + + assert len(results) == 4 + + assert results[0].month.date() == date(2018, 1, 1) + assert results[0].notification_type == 'email' + assert results[0].notification_status == 'delivered' + assert results[0].count == 1 + + assert results[1].month.date() == date(2018, 1, 1) + assert results[1].notification_type == 'sms' + assert results[1].notification_status == 'created' + assert results[1].count == 1 + + assert results[2].month.date() == date(2018, 1, 1) + assert results[2].notification_type == 'sms' + assert results[2].notification_status == 'delivered' + assert results[2].count == 14 + + assert results[3].month.date() == date(2018, 2, 1) + assert results[3].notification_type == 'sms' + assert results[3].notification_status == 'delivered' + assert results[3].count == 1 + + +def test_fetch_notification_status_for_service_for_day(notify_db_session): + service_1 = create_service(service_name='service_1') + service_2 = create_service(service_name='service_2') + + create_template(service=service_1) + create_template(service=service_2) + + # too early + create_notification(service_1.templates[0], created_at=datetime(2018, 5, 31, 22, 59, 0)) + + # included + create_notification(service_1.templates[0], created_at=datetime(2018, 5, 31, 23, 0, 0)) + create_notification(service_1.templates[0], created_at=datetime(2018, 6, 1, 22, 59, 0)) + create_notification(service_1.templates[0], created_at=datetime(2018, 6, 1, 22, 59, 0), status='delivered') + + # wrong service + create_notification(service_2.templates[0], created_at=datetime(2018, 5, 31, 23, 0, 0)) + + # tomorrow (somehow) + create_notification(service_1.templates[0], created_at=datetime(2018, 6, 1, 23, 0, 0)) + + results = sorted( + fetch_notification_status_for_service_for_day(datetime(2018, 6, 1), service_1.id), + key=lambda x: x.notification_status + ) + assert len(results) == 2 + + assert results[0].month == datetime(2018, 6, 1, 0, 0) + assert results[0].notification_type == 'sms' + assert results[0].notification_status == 'created' + assert results[0].count == 2 + + assert results[1].month == datetime(2018, 6, 1, 0, 0) + assert results[1].notification_type == 'sms' + assert results[1].notification_status == 'delivered' + assert results[1].count == 1 diff --git a/tests/app/db.py b/tests/app/db.py index c2535e47d..826fa58e5 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -35,6 +35,7 @@ from app.models import ( LetterRate, InvitedOrganisationUser, FactBilling, + FactNotificationStatus, Complaint ) from app.dao.users_dao import save_model_user @@ -565,6 +566,36 @@ def create_ft_billing(bst_date, return data +def create_ft_notification_status( + bst_date, + notification_type, + service=None, + template=None, + job=None, + key_type='normal', + notification_status='delivered', + count=1 +): + if not service: + service = create_service() + if not template: + template = create_template(service=service, template_type=notification_type) + + data = FactNotificationStatus( + bst_date=bst_date, + template_id=template.id, + service_id=service.id, + job_id=job.id if job else uuid.UUID(int=0), + notification_type=notification_type, + key_type=key_type, + notification_status=notification_status, + notification_count=count + ) + db.session.add(data) + db.session.commit() + return data + + def create_complaint(service=None, notification=None, created_at=None): diff --git a/tests/app/service/test_statistics.py b/tests/app/service/test_statistics.py index 856bee499..a1aea04d4 100644 --- a/tests/app/service/test_statistics.py +++ b/tests/app/service/test_statistics.py @@ -1,5 +1,8 @@ import collections +from datetime import datetime +from unittest.mock import Mock +from freezegun import freeze_time import pytest from app.service.statistics import ( @@ -7,6 +10,8 @@ from app.service.statistics import ( format_statistics, create_stats_dict, create_zeroed_stats_dicts, + create_empty_monthly_notification_status_stats_dict, + add_monthly_notification_status_stats ) StatsRow = collections.namedtuple('row', ('notification_type', 'status', 'count')) @@ -129,3 +134,73 @@ def test_format_admin_stats_counts_non_test_key_notifications_correctly(): assert stats_dict['sms']['failures']['permanent-failure'] == 0 assert stats_dict['letter']['total'] == 1 + + +def _stats(requested, delivered, failed): + return {'requested': requested, 'delivered': delivered, 'failed': failed} + + +@pytest.mark.parametrize('year, expected_years', [ + ( + 2018, + [ + '2018-04', + '2018-05', + '2018-06' + ] + ), + ( + 2017, + [ + '2017-04', + '2017-05', + '2017-06', + '2017-07', + '2017-08', + '2017-09', + '2017-10', + '2017-11', + '2017-12', + '2018-01', + '2018-02', + '2018-03' + ] + ) +]) +@freeze_time('2018-05-31 23:59:59') +def test_create_empty_monthly_notification_status_stats_dict(year, expected_years): + output = create_empty_monthly_notification_status_stats_dict(year) + assert sorted(output.keys()) == expected_years + for v in output.values(): + assert v == {'sms': {}, 'email': {}, 'letter': {}} + + +@freeze_time('2018-05-31 23:59:59') +def test_add_monthly_notification_status_stats(): + row_data = [ + {'month': datetime(2018, 4, 1), 'notification_type': 'sms', 'notification_status': 'sending', 'count': 1}, + {'month': datetime(2018, 4, 1), 'notification_type': 'sms', 'notification_status': 'delivered', 'count': 2}, + {'month': datetime(2018, 4, 1), 'notification_type': 'email', 'notification_status': 'sending', 'count': 4}, + {'month': datetime(2018, 5, 1), 'notification_type': 'sms', 'notification_status': 'sending', 'count': 8}, + ] + rows = [] + for r in row_data: + m = Mock(spec=[]) + for k, v in r.items(): + setattr(m, k, v) + rows.append(m) + + data = create_empty_monthly_notification_status_stats_dict(2018) + # this data won't be affected + data['2018-05']['email']['sending'] = 32 + + # this data will get overwritten + data['2018-05']['sms']['sending'] = 16 + + add_monthly_notification_status_stats(data, rows) + + assert data == { + '2018-04': {'sms': {'sending': 1, 'delivered': 2}, 'email': {'sending': 4}, 'letter': {}}, + '2018-05': {'sms': {'sending': 8}, 'email': {'sending': 32}, 'letter': {}}, + '2018-06': {'sms': {}, 'email': {}, 'letter': {}}, + }