Merge pull request #628 from alphagov/notification-statistics

Platform admin notification query
This commit is contained in:
Leo Hemsted
2016-08-25 14:37:46 +01:00
committed by GitHub
8 changed files with 168 additions and 41 deletions

View File

@@ -58,7 +58,7 @@ def create_app(app_name=None):
encryption.init_app(application)
clients.init_app(sms_clients=[firetext_client, mmg_client, loadtest_client], email_clients=[aws_ses_client])
from app.service.rest import service as service_blueprint
from app.service.rest import service_blueprint
from app.user.rest import user as user_blueprint
from app.template.rest import template as template_blueprint
from app.status.healthcheck import status as status_blueprint

View File

@@ -180,3 +180,25 @@ def dao_fetch_weekly_historical_stats_for_service(service_id):
).order_by(
asc(monday_of_notification_week), NotificationHistory.status
).all()
@statsd(namespace='dao')
def dao_fetch_todays_stats_for_all_services():
return db.session.query(
Notification.notification_type,
Notification.status,
Notification.service_id,
func.count(Notification.id).label('count')
).select_from(
Service
).join(
Notification
).filter(
func.date(Notification.created_at) == date.today()
).group_by(
Notification.notification_type,
Notification.status,
Notification.service_id
).order_by(
Notification.service_id
)

View File

@@ -1,3 +1,5 @@
import itertools
from flask import (
jsonify,
request,
@@ -21,7 +23,8 @@ from app.dao.services_dao import (
dao_remove_user_from_service,
dao_fetch_stats_for_service,
dao_fetch_todays_stats_for_service,
dao_fetch_weekly_historical_stats_for_service
dao_fetch_weekly_historical_stats_for_service,
dao_fetch_todays_stats_for_all_services
)
from app.dao import notifications_dao
from app.dao.provider_statistics_dao import get_fragment_count
@@ -42,25 +45,28 @@ from app.errors import (
)
from app.service import statistics
service = Blueprint('service', __name__)
register_errors(service)
service_blueprint = Blueprint('service', __name__)
register_errors(service_blueprint)
@service.route('', methods=['GET'])
@service_blueprint.route('', methods=['GET'])
def get_services():
user_id = request.args.get('user_id', None)
if user_id:
services = dao_fetch_all_services_by_user(user_id)
elif request.args.get('detailed') == 'True':
return jsonify(data=get_detailed_services())
else:
services = dao_fetch_all_services()
data = service_schema.dump(services, many=True).data
return jsonify(data=data)
@service.route('/<uuid:service_id>', methods=['GET'])
@service_blueprint.route('/<uuid:service_id>', methods=['GET'])
def get_service_by_id(service_id):
if request.args.get('detailed') == 'True':
return get_detailed_service(service_id, today_only=request.args.get('today_only') == 'True')
data = get_detailed_service(service_id, today_only=request.args.get('today_only') == 'True')
return jsonify(data=data)
else:
fetched = dao_fetch_service_by_id(service_id)
@@ -68,7 +74,7 @@ def get_service_by_id(service_id):
return jsonify(data=data)
@service.route('', methods=['POST'])
@service_blueprint.route('', methods=['POST'])
def create_service():
data = request.get_json()
if not data.get('user_id', None):
@@ -82,7 +88,7 @@ def create_service():
return jsonify(data=service_schema.dump(valid_service).data), 201
@service.route('/<uuid:service_id>', methods=['POST'])
@service_blueprint.route('/<uuid:service_id>', methods=['POST'])
def update_service(service_id):
fetched_service = dao_fetch_service_by_id(service_id)
current_data = dict(service_schema.dump(fetched_service).data.items())
@@ -92,7 +98,7 @@ def update_service(service_id):
return jsonify(data=service_schema.dump(fetched_service).data), 200
@service.route('/<uuid:service_id>/api-key', methods=['POST'])
@service_blueprint.route('/<uuid:service_id>/api-key', methods=['POST'])
def create_api_key(service_id=None):
fetched_service = dao_fetch_service_by_id(service_id=service_id)
valid_api_key = api_key_schema.load(request.get_json()).data
@@ -102,14 +108,14 @@ def create_api_key(service_id=None):
return jsonify(data=unsigned_api_key), 201
@service.route('/<uuid:service_id>/api-key/revoke/<uuid:api_key_id>', methods=['POST'])
@service_blueprint.route('/<uuid:service_id>/api-key/revoke/<uuid:api_key_id>', methods=['POST'])
def revoke_api_key(service_id, api_key_id):
expire_api_key(service_id=service_id, api_key_id=api_key_id)
return jsonify(), 202
@service.route('/<uuid:service_id>/api-keys', methods=['GET'])
@service.route('/<uuid:service_id>/api-keys/<uuid:key_id>', methods=['GET'])
@service_blueprint.route('/<uuid:service_id>/api-keys', methods=['GET'])
@service_blueprint.route('/<uuid:service_id>/api-keys/<uuid:key_id>', methods=['GET'])
def get_api_keys(service_id, key_id=None):
dao_fetch_service_by_id(service_id=service_id)
@@ -125,14 +131,14 @@ def get_api_keys(service_id, key_id=None):
return jsonify(apiKeys=api_key_schema.dump(api_keys, many=True).data), 200
@service.route('/<uuid:service_id>/users', methods=['GET'])
@service_blueprint.route('/<uuid:service_id>/users', methods=['GET'])
def get_users_for_service(service_id):
fetched = dao_fetch_service_by_id(service_id)
result = user_schema.dump(fetched.users, many=True)
return jsonify(data=result.data)
@service.route('/<uuid:service_id>/users/<user_id>', methods=['POST'])
@service_blueprint.route('/<uuid:service_id>/users/<user_id>', methods=['POST'])
def add_user_to_service(service_id, user_id):
service = dao_fetch_service_by_id(service_id)
user = get_model_users(user_id=user_id)
@@ -147,7 +153,7 @@ def add_user_to_service(service_id, user_id):
return jsonify(data=data), 201
@service.route('/<uuid:service_id>/users/<user_id>', methods=['DELETE'])
@service_blueprint.route('/<uuid:service_id>/users/<user_id>', methods=['DELETE'])
def remove_user_from_service(service_id, user_id):
service = dao_fetch_service_by_id(service_id)
user = get_model_users(user_id=user_id)
@@ -163,7 +169,7 @@ def remove_user_from_service(service_id, user_id):
return jsonify({}), 204
@service.route('/<uuid:service_id>/fragment/aggregate_statistics')
@service_blueprint.route('/<uuid:service_id>/fragment/aggregate_statistics')
def get_service_provider_aggregate_statistics(service_id):
return jsonify(data=get_fragment_count(service_id))
@@ -171,7 +177,7 @@ def get_service_provider_aggregate_statistics(service_id):
# This is placeholder get method until more thought
# goes into how we want to fetch and view various items in history
# tables. This is so product owner can pass stories as done
@service.route('/<uuid:service_id>/history', methods=['GET'])
@service_blueprint.route('/<uuid:service_id>/history', methods=['GET'])
def get_service_history(service_id):
from app.models import (Service, ApiKey, Template, TemplateHistory, Event)
from app.schemas import (
@@ -201,7 +207,7 @@ def get_service_history(service_id):
return jsonify(data=data)
@service.route('/<uuid:service_id>/notifications', methods=['GET'])
@service_blueprint.route('/<uuid:service_id>/notifications', methods=['GET'])
def get_all_notifications_for_service(service_id):
data = notifications_filter_schema.load(request.args).data
page = data['page'] if 'page' in data else 1
@@ -228,7 +234,7 @@ def get_all_notifications_for_service(service_id):
), 200
@service.route('/<uuid:service_id>/notifications/weekly', methods=['GET'])
@service_blueprint.route('/<uuid:service_id>/notifications/weekly', methods=['GET'])
def get_weekly_notification_stats(service_id):
service = dao_fetch_service_by_id(service_id)
stats = dao_fetch_weekly_historical_stats_for_service(service_id)
@@ -243,5 +249,19 @@ def get_detailed_service(service_id, today_only=False):
service.statistics = statistics.format_statistics(stats)
data = detailed_service_schema.dump(service).data
return jsonify(data=data)
return detailed_service_schema.dump(service).data
def get_detailed_services():
services = {service.id: service for service in dao_fetch_all_services()}
stats = dao_fetch_todays_stats_for_all_services()
for service_id, rows in itertools.groupby(stats, lambda x: x.service_id):
services[service_id].statistics = statistics.format_statistics(rows)
# if service has not sent anything, query will not have set statistics correctly
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

View File

@@ -8,7 +8,7 @@ def format_statistics(statistics):
# statistics come in a named tuple with uniqueness from 'notification_type', 'status' - however missing
# statuses/notification types won't be represented and the status types need to be simplified/summed up
# so we can return emails/sms * created, sent, and failed
counts = _create_zeroed_stats_dicts()
counts = create_zeroed_stats_dicts()
for row in statistics:
_update_statuses_from_row(counts[row.notification_type], row)
@@ -20,7 +20,7 @@ def format_weekly_notification_stats(statistics, service_created_at):
# turn a datetime into midnight that day http://stackoverflow.com/a/1937636
preceeding_monday_midnight = datetime.combine(preceeding_monday.date(), datetime.min.time())
week_dict = {
week: _create_zeroed_stats_dicts()
week: create_zeroed_stats_dicts()
for week in _weeks_for_range(preceeding_monday_midnight, datetime.utcnow())
}
for row in statistics:
@@ -29,7 +29,7 @@ def format_weekly_notification_stats(statistics, service_created_at):
return week_dict
def _create_zeroed_stats_dicts():
def create_zeroed_stats_dicts():
return {
template_type: {
status: 0 for status in ('requested', 'delivered', 'failed')