add stats boxes to platform admin page

moved a couple of stats summary functions from dashboard to a shared statistics_utils file
This commit is contained in:
Leo Hemsted
2016-05-25 16:51:09 +01:00
parent 6d43067688
commit 83b151982e
7 changed files with 128 additions and 52 deletions

View File

@@ -1,8 +1,6 @@
from datetime import datetime, date, timedelta from datetime import datetime, date, timedelta
from dateutil import parser
from collections import namedtuple from collections import namedtuple
from itertools import groupby from itertools import groupby
from functools import reduce
from flask import ( from flask import (
render_template, render_template,
@@ -20,7 +18,7 @@ from app import (
service_api_client, service_api_client,
template_statistics_client template_statistics_client
) )
from app.statistics_utils import sum_of_statistics, add_rates_to
from app.utils import user_has_permissions from app.utils import user_has_permissions
@@ -114,53 +112,6 @@ def weekly(service_id):
) )
def sum_of_statistics(delivery_statistics):
statistics_keys = (
'emails_delivered',
'emails_requested',
'emails_failed',
'sms_requested',
'sms_delivered',
'sms_failed'
)
if not delivery_statistics or not delivery_statistics[0]:
return {
key: 0 for key in statistics_keys
}
return reduce(
lambda x, y: {
key: x.get(key, 0) + y.get(key, 0)
for key in statistics_keys
},
delivery_statistics
)
def add_rates_to(delivery_statistics):
return dict(
emails_failure_rate=(
"{0:.1f}".format(
float(delivery_statistics['emails_failed']) / delivery_statistics['emails_requested'] * 100
)
if delivery_statistics['emails_requested'] else 0
),
sms_failure_rate=(
"{0:.1f}".format(
float(delivery_statistics['sms_failed']) / delivery_statistics['sms_requested'] * 100
)
if delivery_statistics['sms_requested'] else 0
),
week_end_datetime=parser.parse(
delivery_statistics.get('week_end', str(datetime.utcnow()))
),
**delivery_statistics
)
def aggregate_usage(template_statistics): def aggregate_usage(template_statistics):
immutable_template = namedtuple('Template', ['template_type', 'name', 'id']) immutable_template = namedtuple('Template', ['template_type', 'name', 'id'])

View File

@@ -1,8 +1,13 @@
from datetime import datetime
import pytz
from flask import render_template from flask import render_template
from flask_login import login_required from flask_login import login_required
from app import statistics_api_client
from app.main import main from app.main import main
from app.utils import user_has_permissions from app.utils import user_has_permissions
from app.statistics_utils import sum_of_statistics, add_rates_to
@main.route("/platform-admin") @main.route("/platform-admin")
@@ -10,5 +15,12 @@ from app.utils import user_has_permissions
@user_has_permissions(admin_override=True) @user_has_permissions(admin_override=True)
def platform_admin(): def platform_admin():
return render_template( return render_template(
'views/platform-admin.html' 'views/platform-admin.html',
global_stats=get_global_stats()
) )
def get_global_stats():
day = datetime.now(tz=pytz.timezone('Europe/London')).date()
all_stats = statistics_api_client.get_statistics_for_all_services_for_day(day)['data']
return add_rates_to(sum_of_statistics(all_stats))

View File

@@ -31,3 +31,9 @@ class StatisticsApiClient(BaseAPIClient):
url='/service/{}/notifications-statistics/seven_day_aggregate'.format(service_id), url='/service/{}/notifications-statistics/seven_day_aggregate'.format(service_id),
params=params params=params
) )
def get_statistics_for_all_services_for_day(self, day):
params = {
'day': day
}
return self.get(url='/notifications/statistics', params=params)

50
app/statistics_utils.py Normal file
View File

@@ -0,0 +1,50 @@
from datetime import datetime
from dateutil import parser
from functools import reduce
def sum_of_statistics(delivery_statistics):
statistics_keys = (
'emails_delivered',
'emails_requested',
'emails_failed',
'sms_requested',
'sms_delivered',
'sms_failed'
)
if not delivery_statistics or not delivery_statistics[0]:
return {
key: 0 for key in statistics_keys
}
return reduce(
lambda x, y: {
key: x.get(key, 0) + y.get(key, 0)
for key in statistics_keys
},
delivery_statistics
)
def add_rates_to(delivery_statistics):
return dict(
emails_failure_rate=(
"{0:.1f}".format(
float(delivery_statistics['emails_failed']) / delivery_statistics['emails_requested'] * 100
)
if delivery_statistics['emails_requested'] else 0
),
sms_failure_rate=(
"{0:.1f}".format(
float(delivery_statistics['sms_failed']) / delivery_statistics['sms_requested'] * 100
)
if delivery_statistics['sms_requested'] else 0
),
week_end_datetime=parser.parse(
delivery_statistics.get('week_end', str(datetime.utcnow()))
),
**delivery_statistics
)

View File

@@ -1,4 +1,6 @@
{% extends "withoutnav_template.html" %} {% extends "withoutnav_template.html" %}
{% from "components/big-number.html" import big_number_with_status %}
{% from "components/message-count-label.html" import message_count_label %}
{% from "components/browse-list.html" import browse_list %} {% from "components/browse-list.html" import browse_list %}
{% block page_title %} {% block page_title %}
@@ -22,4 +24,27 @@
}, },
]) }} ]) }}
<div class="grid-row">
<div class="column-half">
{{ big_number_with_status(
global_stats.emails_delivered,
message_count_label(global_stats.emails_delivered, 'email'),
global_stats.emails_failed,
global_stats.emails_failure_rate,
global_stats.emails_failure_rate|float > 3,
) }}
</div>
<div class="column-half">
{{ big_number_with_status(
global_stats.sms_delivered,
message_count_label(global_stats.sms_delivered, 'sms'),
global_stats.sms_failed,
global_stats.sms_failure_rate,
global_stats.sms_failure_rate|float > 3,
) }}
</div>
</div>
{% endblock %} {% endblock %}

View File

@@ -1,7 +1,12 @@
from datetime import date
from flask import url_for from flask import url_for
from freezegun import freeze_time
from tests.conftest import mock_get_user from tests.conftest import mock_get_user
from app.main.views.platform_admin import get_global_stats
def test_should_redirect_if_not_logged_in(app_): def test_should_redirect_if_not_logged_in(app_):
with app_.test_request_context(): with app_.test_request_context():
@@ -22,7 +27,7 @@ def test_should_403_if_not_platform_admin(app_, active_user_with_permissions, mo
assert response.status_code == 403 assert response.status_code == 403
def test_should_render_platform_admin_page(app_, platform_admin_user, mocker): def test_should_render_platform_admin_page(app_, platform_admin_user, mocker, mock_get_all_service_statistics):
with app_.test_request_context(): with app_.test_request_context():
with app_.test_client() as client: with app_.test_client() as client:
mock_get_user(mocker, user=platform_admin_user) mock_get_user(mocker, user=platform_admin_user)
@@ -34,3 +39,21 @@ def test_should_render_platform_admin_page(app_, platform_admin_user, mocker):
assert 'Platform admin' in resp_data assert 'Platform admin' in resp_data
assert 'List all services' in resp_data assert 'List all services' in resp_data
assert 'View providers' in resp_data assert 'View providers' in resp_data
def test_get_global_stats_should_summarise_all_stats(mock_get_all_service_statistics):
resp = get_global_stats()
assert 'emails_delivered' in resp
assert 'emails_failed' in resp
assert 'emails_failure_rate' in resp
assert 'sms_delivered' in resp
assert 'sms_failed' in resp
assert 'sms_failure_rate' in resp
@freeze_time('2000-06-30T23:30:00', tz_offset=0)
def test_get_global_stats_should_query_for_today_forced_to_GMT(mock_get_all_service_statistics):
get_global_stats()
mock_get_all_service_statistics.assert_called_once_with(date(2000, 7, 1))

View File

@@ -202,6 +202,15 @@ def mock_get_aggregate_service_statistics(mocker):
'app.statistics_api_client.get_7_day_aggregate_for_service', side_effect=_create) 'app.statistics_api_client.get_7_day_aggregate_for_service', side_effect=_create)
@pytest.fixture(scope='function')
def mock_get_all_service_statistics(mocker):
def _create(day):
return {'data': [{}]}
return mocker.patch(
'app.statistics_api_client.get_statistics_for_all_services_for_day', side_effect=_create)
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def mock_get_service_template(mocker): def mock_get_service_template(mocker):
def _get(service_id, template_id, version=None): def _get(service_id, template_id, version=None):