diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index 71896a5df..aaefec145 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -262,4 +262,32 @@ def dao_fetch_todays_stats_for_all_services(include_from_test_key=True): if not include_from_test_key: query = query.filter(Notification.key_type != KEY_TYPE_TEST) - return query + return query.all() + + +@statsd(namespace='dao') +def fetch_stats_by_date_range_for_all_services(start_date, end_date, include_from_test_key=True): + query = db.session.query( + NotificationHistory.notification_type, + NotificationHistory.status, + NotificationHistory.service_id, + func.count(NotificationHistory.id).label('count') + ).select_from( + Service + ).join( + NotificationHistory + ).filter( + func.date(NotificationHistory.created_at) >= start_date, + func.date(NotificationHistory.created_at) <= end_date + ).group_by( + NotificationHistory.notification_type, + NotificationHistory.status, + NotificationHistory.service_id + ).order_by( + NotificationHistory.service_id + ) + + if not include_from_test_key: + query = query.filter(NotificationHistory.key_type != KEY_TYPE_TEST) + + return query.all() diff --git a/app/service/rest.py b/app/service/rest.py index 1c1e9b77c..cdca5045b 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -1,4 +1,5 @@ import itertools +from datetime import datetime from flask import ( jsonify, @@ -26,7 +27,8 @@ from app.dao.services_dao import ( dao_fetch_todays_stats_for_service, dao_fetch_weekly_historical_stats_for_service, dao_fetch_todays_stats_for_all_services, - dao_deactive_service + dao_deactive_service, + fetch_stats_by_date_range_for_all_services ) from app.dao.service_whitelist_dao import ( dao_fetch_service_whitelist, @@ -63,11 +65,16 @@ def get_services(): detailed = request.args.get('detailed') == 'True' user_id = request.args.get('user_id', None) include_from_test_key = request.args.get('include_from_test_key', 'True') != 'False' + # If start and end date are not set, we are expecting today's stats. + start_date = request.args.get('start_date', datetime.utcnow().date()) + end_date = request.args.get('end_date', datetime.utcnow().date()) if user_id: services = dao_fetch_all_services_by_user(user_id, only_active) elif detailed: - return jsonify(data=get_detailed_services(only_active, include_from_test_key=include_from_test_key)) + return jsonify(data=get_detailed_services(start_date=start_date, end_date=end_date, + only_active=only_active, include_from_test_key=include_from_test_key + )) else: services = dao_fetch_all_services(only_active) data = service_schema.dump(services, many=True).data @@ -269,9 +276,14 @@ def get_detailed_service(service_id, today_only=False): return detailed_service_schema.dump(service).data -def get_detailed_services(only_active=False, include_from_test_key=True): +def get_detailed_services(start_date, end_date, only_active=False, include_from_test_key=True): services = {service.id: service for service in dao_fetch_all_services(only_active)} - stats = dao_fetch_todays_stats_for_all_services(include_from_test_key=include_from_test_key) + if start_date == datetime.utcnow().date(): + stats = dao_fetch_todays_stats_for_all_services(include_from_test_key=include_from_test_key) + else: + stats = fetch_stats_by_date_range_for_all_services(start_date=start_date, + end_date=end_date, + include_from_test_key=include_from_test_key) for service_id, rows in itertools.groupby(stats, lambda x: x.service_id): services[service_id].statistics = statistics.format_statistics(rows) @@ -280,7 +292,6 @@ def get_detailed_services(only_active=False, include_from_test_key=True): for service in services.values(): if not hasattr(service, 'statistics'): service.statistics = statistics.create_zeroed_stats_dicts() - return detailed_service_schema.dump(services.values(), many=True).data diff --git a/tests/app/dao/test_services_dao.py b/tests/app/dao/test_services_dao.py index eacca3db3..a3f5d1de7 100644 --- a/tests/app/dao/test_services_dao.py +++ b/tests/app/dao/test_services_dao.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timedelta import uuid import functools @@ -21,7 +21,8 @@ from app.dao.services_dao import ( dao_fetch_todays_stats_for_service, dao_fetch_weekly_historical_stats_for_service, fetch_todays_total_message_count, - dao_fetch_todays_stats_for_all_services + dao_fetch_todays_stats_for_all_services, + fetch_stats_by_date_range_for_all_services ) from app.dao.users_dao import save_model_user from app.models import ( @@ -575,7 +576,7 @@ def test_dao_fetch_todays_stats_for_all_services_includes_all_services(notify_db notify_db, notify_db_session, service=service2, template=create_email_template(notify_db, notify_db_session, service=service2)) - stats = dao_fetch_todays_stats_for_all_services().all() + stats = dao_fetch_todays_stats_for_all_services() assert len(stats) == 4 # services are ordered by service id; not explicit on email/sms or status @@ -590,7 +591,7 @@ def test_dao_fetch_todays_stats_for_all_services_only_includes_today(notify_db): just_after_midnight_today = create_notification(notify_db, None, to_field='2', status='failed') with freeze_time('2001-01-02T12:00:00'): - stats = dao_fetch_todays_stats_for_all_services().all() + stats = dao_fetch_todays_stats_for_all_services() stats = {row.status: row.count for row in stats} assert 'delivered' not in stats @@ -610,7 +611,7 @@ def test_dao_fetch_todays_stats_for_all_services_groups_correctly(notify_db, not # service2: 1 sms "created" create_notification(notify_db, notify_db_session, service=service2) - stats = dao_fetch_todays_stats_for_all_services().all() + stats = dao_fetch_todays_stats_for_all_services() assert len(stats) == 4 assert ('sms', 'created', service1.id, 2) in stats @@ -624,7 +625,7 @@ def test_dao_fetch_todays_stats_for_all_services_includes_all_keys_by_default(no create_notification(notify_db, notify_db_session, key_type=KEY_TYPE_TEAM) create_notification(notify_db, notify_db_session, key_type=KEY_TYPE_TEST) - stats = dao_fetch_todays_stats_for_all_services().all() + stats = dao_fetch_todays_stats_for_all_services() assert len(stats) == 1 assert stats[0].count == 3 @@ -635,7 +636,23 @@ def test_dao_fetch_todays_stats_for_all_services_can_exclude_from_test_key(notif create_notification(notify_db, notify_db_session, key_type=KEY_TYPE_TEAM) create_notification(notify_db, notify_db_session, key_type=KEY_TYPE_TEST) - stats = dao_fetch_todays_stats_for_all_services(include_from_test_key=False).all() + stats = dao_fetch_todays_stats_for_all_services(include_from_test_key=False) assert len(stats) == 1 assert stats[0].count == 2 + + +def test_fetch_stats_by_date_range_for_all_services(notify_db, notify_db_session): + create_notification(notify_db, notify_db_session, created_at=datetime.now() - timedelta(days=4)) + create_notification(notify_db, notify_db_session, created_at=datetime.now() - timedelta(days=3)) + result_one = create_notification(notify_db, notify_db_session, created_at=datetime.now() - timedelta(days=2)) + create_notification(notify_db, notify_db_session, created_at=datetime.now() - timedelta(days=1)) + create_notification(notify_db, notify_db_session, created_at=datetime.now()) + + start_date = (datetime.utcnow() - timedelta(days=2)).date() + end_date = (datetime.utcnow() - timedelta(days=1)).date() + + results = fetch_stats_by_date_range_for_all_services(start_date, end_date) + + assert len(results) == 1 + assert results[0] == ('sms', 'created', result_one.service_id, 2) diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 3641982d1..8a291a1a6 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timedelta import json import uuid @@ -1305,7 +1305,7 @@ def test_get_detailed_services_groups_by_service(notify_db, notify_db_session): create_sample_notification(notify_db, notify_db_session, service=service_1, status='delivered') create_sample_notification(notify_db, notify_db_session, service=service_1, status='created') - data = get_detailed_services() + data = get_detailed_services(start_date=datetime.utcnow().date(), end_date=datetime.utcnow().date()) data = sorted(data, key=lambda x: x['name']) assert len(data) == 2 @@ -1329,7 +1329,8 @@ def test_get_detailed_services_includes_services_with_no_notifications(notify_db create_sample_notification(notify_db, notify_db_session, service=service_1) - data = get_detailed_services() + data = get_detailed_services(start_date=datetime.utcnow().date(), + end_date=datetime.utcnow().date()) data = sorted(data, key=lambda x: x['name']) assert len(data) == 2 @@ -1353,7 +1354,7 @@ def test_get_detailed_services_only_includes_todays_notifications(notify_db, not create_sample_notification(notify_db, notify_db_session, created_at=datetime(2015, 10, 10, 12, 0)) with freeze_time('2015-10-10T12:00:00'): - data = get_detailed_services() + data = get_detailed_services(start_date=datetime.utcnow().date(), end_date=datetime.utcnow().date()) data = sorted(data, key=lambda x: x['id']) assert len(data) == 1 @@ -1363,6 +1364,25 @@ def test_get_detailed_services_only_includes_todays_notifications(notify_db, not } +def test_get_detailed_services_for_date_range(notify_db, notify_db_session): + from app.service.rest import get_detailed_services + create_sample_notification(notify_db, notify_db_session, created_at=datetime.now() - timedelta(days=3)) + create_sample_notification(notify_db, notify_db_session, created_at=datetime.now() - timedelta(days=2)) + create_sample_notification(notify_db, notify_db_session, created_at=datetime.now() - timedelta(days=1)) + create_sample_notification(notify_db, notify_db_session, created_at=datetime.now()) + + start_date = (datetime.utcnow() - timedelta(days=2)).date() + end_date = (datetime.utcnow() - timedelta(days=1)).date() + + data = get_detailed_services(only_active=False, include_from_test_key=True, + start_date=start_date, end_date=end_date) + assert len(data) == 1 + assert data[0]['statistics'] == { + 'email': {'delivered': 0, 'failed': 0, 'requested': 0}, + 'sms': {'delivered': 0, 'failed': 0, 'requested': 2} + } + + @freeze_time('2012-12-12T12:00:01') def test_get_notification_billable_unit_count(client, notify_db, notify_db_session): notification = create_sample_notification(notify_db, notify_db_session)