mirror of
https://github.com/GSA/notifications-api.git
synced 2026-08-28 18:23:42 -04:00
Merge pull request #333 from alphagov/notification_statistics_endpoint
notification_statistics endpoint
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
from sqlalchemy import (desc, func, Integer, and_, asc)
|
from sqlalchemy import (desc, func, Integer, and_, or_, asc)
|
||||||
from sqlalchemy.sql.expression import cast
|
from sqlalchemy.sql.expression import cast
|
||||||
|
|
||||||
from datetime import (
|
from datetime import (
|
||||||
@@ -12,6 +12,7 @@ from werkzeug.datastructures import MultiDict
|
|||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.models import (
|
from app.models import (
|
||||||
|
Service,
|
||||||
Notification,
|
Notification,
|
||||||
Job,
|
Job,
|
||||||
NotificationStatistics,
|
NotificationStatistics,
|
||||||
@@ -51,6 +52,57 @@ def dao_get_notification_statistics_for_service_and_day(service_id, day):
|
|||||||
).order_by(desc(NotificationStatistics.day)).first()
|
).order_by(desc(NotificationStatistics.day)).first()
|
||||||
|
|
||||||
|
|
||||||
|
def dao_get_notification_statistics_for_day(day):
|
||||||
|
return NotificationStatistics.query.filter_by(
|
||||||
|
day=day
|
||||||
|
).all()
|
||||||
|
|
||||||
|
|
||||||
|
def dao_get_potential_notification_statistics_for_day(day):
|
||||||
|
all_services = db.session.query(
|
||||||
|
Service.id,
|
||||||
|
NotificationStatistics
|
||||||
|
).outerjoin(
|
||||||
|
Service.service_notification_stats
|
||||||
|
).filter(
|
||||||
|
or_(
|
||||||
|
NotificationStatistics.day == day,
|
||||||
|
NotificationStatistics.day == None # noqa
|
||||||
|
)
|
||||||
|
).order_by(
|
||||||
|
asc(Service.created_at)
|
||||||
|
)
|
||||||
|
|
||||||
|
notification_statistics = []
|
||||||
|
for service_notification_stats_pair in all_services:
|
||||||
|
if service_notification_stats_pair.NotificationStatistics:
|
||||||
|
notification_statistics.append(
|
||||||
|
service_notification_stats_pair.NotificationStatistics
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
notification_statistics.append(
|
||||||
|
create_notification_statistics_dict(
|
||||||
|
service_notification_stats_pair,
|
||||||
|
day
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return notification_statistics
|
||||||
|
|
||||||
|
|
||||||
|
def create_notification_statistics_dict(service_id, day):
|
||||||
|
return {
|
||||||
|
'id': None,
|
||||||
|
'emails_requested': 0,
|
||||||
|
'emails_delivered': 0,
|
||||||
|
'emails_failed': 0,
|
||||||
|
'sms_requested': 0,
|
||||||
|
'sms_delivered': 0,
|
||||||
|
'sms_failed': 0,
|
||||||
|
'day': day.isoformat(),
|
||||||
|
'service': service_id
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def dao_get_7_day_agg_notification_statistics_for_service(service_id,
|
def dao_get_7_day_agg_notification_statistics_for_service(service_id,
|
||||||
date_from,
|
date_from,
|
||||||
week_count=52):
|
week_count=52):
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime, date
|
||||||
import statsd
|
import statsd
|
||||||
import itertools
|
import itertools
|
||||||
from flask import (
|
from flask import (
|
||||||
@@ -27,7 +27,9 @@ from app.schemas import (
|
|||||||
email_notification_schema,
|
email_notification_schema,
|
||||||
sms_template_notification_schema,
|
sms_template_notification_schema,
|
||||||
notification_status_schema,
|
notification_status_schema,
|
||||||
notifications_filter_schema
|
notifications_filter_schema,
|
||||||
|
notifications_statistics_schema,
|
||||||
|
day_schema,
|
||||||
)
|
)
|
||||||
from app.celery.tasks import send_sms, send_email
|
from app.celery.tasks import send_sms, send_email
|
||||||
|
|
||||||
@@ -384,3 +386,17 @@ def send_notification(notification_type):
|
|||||||
|
|
||||||
statsd_client.incr('notifications.api.{}'.format(notification_type))
|
statsd_client.incr('notifications.api.{}'.format(notification_type))
|
||||||
return jsonify(data={"notification": {"id": notification_id}}), 201
|
return jsonify(data={"notification": {"id": notification_id}}), 201
|
||||||
|
|
||||||
|
|
||||||
|
@notifications.route('/notifications/statistics')
|
||||||
|
def get_notification_statistics_for_day():
|
||||||
|
data, errors = day_schema.load(request.args)
|
||||||
|
if errors:
|
||||||
|
return jsonify(result='error', message=errors), 400
|
||||||
|
|
||||||
|
statistics = notifications_dao.dao_get_potential_notification_statistics_for_day(
|
||||||
|
day=data['day']
|
||||||
|
)
|
||||||
|
|
||||||
|
data, errors = notifications_statistics_schema.dump(statistics, many=True)
|
||||||
|
return jsonify(data=data), 200
|
||||||
|
|||||||
@@ -168,7 +168,6 @@ class RequestVerifyCodeSchema(ma.Schema):
|
|||||||
|
|
||||||
class NotificationSchema(ma.Schema):
|
class NotificationSchema(ma.Schema):
|
||||||
personalisation = fields.Dict(required=False)
|
personalisation = fields.Dict(required=False)
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class SmsNotificationSchema(NotificationSchema):
|
class SmsNotificationSchema(NotificationSchema):
|
||||||
@@ -360,6 +359,14 @@ class FromToDateSchema(ma.Schema):
|
|||||||
raise ValidationError("date_from needs to be greater than date_to")
|
raise ValidationError("date_from needs to be greater than date_to")
|
||||||
|
|
||||||
|
|
||||||
|
class DaySchema(ma.Schema):
|
||||||
|
day = fields.Date(required=True)
|
||||||
|
|
||||||
|
@validates('day')
|
||||||
|
def validate_day(self, value):
|
||||||
|
_validate_not_in_future(value)
|
||||||
|
|
||||||
|
|
||||||
class WeekAggregateNotificationStatisticsSchema(ma.Schema):
|
class WeekAggregateNotificationStatisticsSchema(ma.Schema):
|
||||||
|
|
||||||
date_from = fields.Date()
|
date_from = fields.Date()
|
||||||
@@ -405,3 +412,4 @@ event_schema = EventSchema()
|
|||||||
from_to_date_schema = FromToDateSchema()
|
from_to_date_schema = FromToDateSchema()
|
||||||
provider_details_schema = ProviderDetailsSchema()
|
provider_details_schema = ProviderDetailsSchema()
|
||||||
week_aggregate_notification_statistics_schema = WeekAggregateNotificationStatisticsSchema()
|
week_aggregate_notification_statistics_schema = WeekAggregateNotificationStatisticsSchema()
|
||||||
|
day_schema = DaySchema()
|
||||||
|
|||||||
0
tests/app/notifications/rest/__init__.py
Normal file
0
tests/app/notifications/rest/__init__.py
Normal file
196
tests/app/notifications/rest/test_notification_statistics.py
Normal file
196
tests/app/notifications/rest/test_notification_statistics.py
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
from datetime import date, timedelta
|
||||||
|
|
||||||
|
from flask import json
|
||||||
|
from freezegun import freeze_time
|
||||||
|
|
||||||
|
from tests import create_authorization_header
|
||||||
|
from tests.app.conftest import (
|
||||||
|
sample_notification_statistics as create_sample_notification_statistics,
|
||||||
|
sample_service as create_sample_service
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_notification_statistics(notify_api, sample_notification_statistics):
|
||||||
|
with notify_api.test_request_context():
|
||||||
|
with notify_api.test_client() as client:
|
||||||
|
auth_header = create_authorization_header(
|
||||||
|
service_id=sample_notification_statistics.service_id
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
'/notifications/statistics?day={}'.format(date.today().isoformat()),
|
||||||
|
headers=[auth_header]
|
||||||
|
)
|
||||||
|
|
||||||
|
notifications = json.loads(response.get_data(as_text=True))
|
||||||
|
|
||||||
|
assert len(notifications['data']) == 1
|
||||||
|
stats = notifications['data'][0]
|
||||||
|
assert stats['emails_requested'] == 2
|
||||||
|
assert stats['emails_delivered'] == 1
|
||||||
|
assert stats['emails_failed'] == 1
|
||||||
|
assert stats['sms_requested'] == 2
|
||||||
|
assert stats['sms_delivered'] == 1
|
||||||
|
assert stats['sms_failed'] == 1
|
||||||
|
assert stats['service'] == str(sample_notification_statistics.service_id)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
@freeze_time('1955-11-05T12:00:00')
|
||||||
|
def test_get_notification_statistics_only_returns_today(notify_api, notify_db, notify_db_session, sample_service):
|
||||||
|
with notify_api.test_request_context():
|
||||||
|
with notify_api.test_client() as client:
|
||||||
|
yesterdays_notification_statistics = create_sample_notification_statistics(
|
||||||
|
notify_db,
|
||||||
|
notify_db_session,
|
||||||
|
service=sample_service,
|
||||||
|
day=date.today() - timedelta(days=1)
|
||||||
|
)
|
||||||
|
todays_notification_statistics = create_sample_notification_statistics(
|
||||||
|
notify_db,
|
||||||
|
notify_db_session,
|
||||||
|
service=sample_service,
|
||||||
|
day=date.today()
|
||||||
|
)
|
||||||
|
tomorrows_notification_statistics = create_sample_notification_statistics(
|
||||||
|
notify_db,
|
||||||
|
notify_db_session,
|
||||||
|
service=sample_service,
|
||||||
|
day=date.today() + timedelta(days=1)
|
||||||
|
)
|
||||||
|
|
||||||
|
auth_header = create_authorization_header(
|
||||||
|
service_id=sample_service.id
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
'/notifications/statistics?day={}'.format(date.today().isoformat()),
|
||||||
|
headers=[auth_header]
|
||||||
|
)
|
||||||
|
|
||||||
|
notifications = json.loads(response.get_data(as_text=True))
|
||||||
|
|
||||||
|
assert len(notifications['data']) == 1
|
||||||
|
assert notifications['data'][0]['day'] == date.today().isoformat()
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_notification_statistics_fails_if_no_date(notify_api, sample_notification_statistics):
|
||||||
|
with notify_api.test_request_context():
|
||||||
|
with notify_api.test_client() as client:
|
||||||
|
auth_header = create_authorization_header(
|
||||||
|
service_id=sample_notification_statistics.service_id
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
'/notifications/statistics',
|
||||||
|
headers=[auth_header]
|
||||||
|
)
|
||||||
|
|
||||||
|
resp = json.loads(response.get_data(as_text=True))
|
||||||
|
assert resp['result'] == 'error'
|
||||||
|
assert resp['message'] == {'day': ['Missing data for required field.']}
|
||||||
|
assert response.status_code == 400
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_notification_statistics_fails_if_invalid_date(notify_api, sample_notification_statistics):
|
||||||
|
with notify_api.test_request_context():
|
||||||
|
with notify_api.test_client() as client:
|
||||||
|
auth_header = create_authorization_header(
|
||||||
|
service_id=sample_notification_statistics.service_id
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
'/notifications/statistics?day=2016-99-99',
|
||||||
|
headers=[auth_header]
|
||||||
|
)
|
||||||
|
|
||||||
|
resp = json.loads(response.get_data(as_text=True))
|
||||||
|
assert resp['result'] == 'error'
|
||||||
|
assert resp['message'] == {'day': ['Not a valid date.']}
|
||||||
|
assert response.status_code == 400
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_notification_statistics_returns_zeros_if_not_in_db(notify_api, sample_service):
|
||||||
|
with notify_api.test_request_context():
|
||||||
|
with notify_api.test_client() as client:
|
||||||
|
auth_header = create_authorization_header(
|
||||||
|
service_id=sample_service.id
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
'/notifications/statistics?day={}'.format(date.today().isoformat()),
|
||||||
|
headers=[auth_header]
|
||||||
|
)
|
||||||
|
|
||||||
|
notifications = json.loads(response.get_data(as_text=True))
|
||||||
|
|
||||||
|
assert len(notifications['data']) == 1
|
||||||
|
stats = notifications['data'][0]
|
||||||
|
assert stats['emails_requested'] == 0
|
||||||
|
assert stats['emails_delivered'] == 0
|
||||||
|
assert stats['emails_failed'] == 0
|
||||||
|
assert stats['sms_requested'] == 0
|
||||||
|
assert stats['sms_delivered'] == 0
|
||||||
|
assert stats['sms_failed'] == 0
|
||||||
|
assert stats['service'] == str(sample_service.id)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_notification_statistics_returns_both_existing_stats_and_generated_zeros(
|
||||||
|
notify_api,
|
||||||
|
notify_db,
|
||||||
|
notify_db_session
|
||||||
|
):
|
||||||
|
with notify_api.test_request_context():
|
||||||
|
with notify_api.test_client() as client:
|
||||||
|
service_with_stats = create_sample_service(
|
||||||
|
notify_db,
|
||||||
|
notify_db_session,
|
||||||
|
service_name='service_with_stats',
|
||||||
|
email_from='service_with_stats'
|
||||||
|
)
|
||||||
|
service_without_stats = create_sample_service(
|
||||||
|
notify_db,
|
||||||
|
notify_db_session,
|
||||||
|
service_name='service_without_stats',
|
||||||
|
email_from='service_without_stats'
|
||||||
|
)
|
||||||
|
notification_statistics = create_sample_notification_statistics(
|
||||||
|
notify_db,
|
||||||
|
notify_db_session,
|
||||||
|
service=service_with_stats,
|
||||||
|
day=date.today()
|
||||||
|
)
|
||||||
|
auth_header = create_authorization_header(
|
||||||
|
service_id=service_with_stats.id
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
'/notifications/statistics?day={}'.format(date.today().isoformat()),
|
||||||
|
headers=[auth_header]
|
||||||
|
)
|
||||||
|
|
||||||
|
notifications = json.loads(response.get_data(as_text=True))
|
||||||
|
|
||||||
|
assert len(notifications['data']) == 2
|
||||||
|
retrieved_stats = notifications['data'][0]
|
||||||
|
generated_stats = notifications['data'][1]
|
||||||
|
|
||||||
|
assert retrieved_stats['emails_requested'] == 2
|
||||||
|
assert retrieved_stats['emails_delivered'] == 1
|
||||||
|
assert retrieved_stats['emails_failed'] == 1
|
||||||
|
assert retrieved_stats['sms_requested'] == 2
|
||||||
|
assert retrieved_stats['sms_delivered'] == 1
|
||||||
|
assert retrieved_stats['sms_failed'] == 1
|
||||||
|
assert retrieved_stats['service'] == str(service_with_stats.id)
|
||||||
|
|
||||||
|
assert generated_stats['emails_requested'] == 0
|
||||||
|
assert generated_stats['emails_delivered'] == 0
|
||||||
|
assert generated_stats['emails_failed'] == 0
|
||||||
|
assert generated_stats['sms_requested'] == 0
|
||||||
|
assert generated_stats['sms_delivered'] == 0
|
||||||
|
assert generated_stats['sms_failed'] == 0
|
||||||
|
assert generated_stats['service'] == str(service_without_stats.id)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
Reference in New Issue
Block a user