From 00030bc254f2dc7bd47c318c905227a202b6377a Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Wed, 20 Apr 2016 15:37:17 +0100 Subject: [PATCH] Add usage to the dashboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Takes the number of emails and SMS fragments sent from: https://github.com/alphagov/notifications-api/pull/273 Using these numbers it’s possible to show: - how much of your allowance is left - or how much you have spent For now the allowance and rates are hard coded. Only for users that have manage service. --- app/assets/stylesheets/_grids.scss | 5 ++ .../stylesheets/components/big-number.scss | 6 ++ app/assets/stylesheets/main.scss | 1 + app/assets/stylesheets/views/dashboard.scss | 20 ++++++ app/main/views/dashboard.py | 26 ++++++- app/notify_client/api_client.py | 3 + app/templates/components/big-number.html | 8 ++- .../dashboard/all-template-statistics.html | 2 +- app/templates/views/dashboard/dashboard.html | 4 ++ app/templates/views/dashboard/today.html | 34 +++++++++- app/templates/views/usage.html | 67 +++++++++++++++++++ tests/app/main/views/test_dashboard.py | 18 +++-- tests/app/main/views/test_sign_out.py | 3 +- tests/conftest.py | 13 ++++ 14 files changed, 197 insertions(+), 13 deletions(-) create mode 100644 app/assets/stylesheets/views/dashboard.scss create mode 100644 app/templates/views/usage.html diff --git a/app/assets/stylesheets/_grids.scss b/app/assets/stylesheets/_grids.scss index 946e9d207..4b390feab 100644 --- a/app/assets/stylesheets/_grids.scss +++ b/app/assets/stylesheets/_grids.scss @@ -35,6 +35,11 @@ padding-right: 2px; } +.align-with-heading-copy { + display: block; + margin-top: 45px; +} + .global-cookie-message { p { @extend %site-width-container; diff --git a/app/assets/stylesheets/components/big-number.scss b/app/assets/stylesheets/components/big-number.scss index 8031ad980..f4ef4cac6 100644 --- a/app/assets/stylesheets/components/big-number.scss +++ b/app/assets/stylesheets/components/big-number.scss @@ -11,6 +11,12 @@ } +.big-number-right-aligned { + @extend %big-number; + @include bold-36($tabular-numbers: true); + text-align: right; +} + .big-number-with-status { @extend %big-number; diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index a8ba3e209..356110614 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -54,6 +54,7 @@ $path: '/static/images/'; @import 'views/job'; @import 'views/edit-template'; @import 'views/documenation'; +@import 'views/dashboard'; // TODO: break this up @import 'app'; diff --git a/app/assets/stylesheets/views/dashboard.scss b/app/assets/stylesheets/views/dashboard.scss new file mode 100644 index 000000000..b79e6db4e --- /dev/null +++ b/app/assets/stylesheets/views/dashboard.scss @@ -0,0 +1,20 @@ +.dashboard { + table th { + @include bold-19; + } +} + +.keyline-block { + + border-top: 1px solid $border-colour; + padding-top: $gutter-half; + + &-show-more-link { + @include core-16; + border-top: 1px solid $border-colour; + border-bottom: 1px solid $border-colour; + padding: 10px 0; + text-align: center; + } + +} diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 8cfa4258c..2bf54a49f 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -80,6 +80,16 @@ def template_history(service_id): ) +@main.route("/services//usage") +@login_required +@user_has_permissions('manage_settings', admin_override=True) +def usage(service_id): + return render_template( + 'views/usage.html', + **get_dashboard_statistics_for_service(service_id) + ) + + def add_rates_to(delivery_statistics): keys = [ @@ -148,11 +158,25 @@ def aggregate_usage(template_statistics): def get_dashboard_statistics_for_service(service_id): + + usage = service_api_client.get_service_usage(service_id) + sms_free_allowance = 250000 + sms_rate = 0.018 + + sms_sent = usage['data'].get('sms_count', 0) + emails_sent = usage['data'].get('email_count', 0) + return { 'statistics': add_rates_to( statistics_api_client.get_statistics_for_service(service_id, limit_days=7)['data'] ), 'template_statistics': aggregate_usage( template_statistics_client.get_template_statistics_for_service(service_id, limit_days=7) - ) + ), + 'emails_sent': emails_sent, + 'sms_free_allowance': sms_free_allowance, + 'sms_sent': sms_sent, + 'sms_allowance_remaining': max(0, (sms_free_allowance - sms_sent)), + 'sms_chargeable': max(0, sms_sent - sms_free_allowance), + 'sms_rate': sms_rate, } diff --git a/app/notify_client/api_client.py b/app/notify_client/api_client.py index 9151a87d5..550821799 100644 --- a/app/notify_client/api_client.py +++ b/app/notify_client/api_client.py @@ -159,6 +159,9 @@ class ServiceAPIClient(NotificationsAPIClient): def get_service_history(self, service_id): return self.get('/service/{0}/history'.format(service_id)) + def get_service_usage(self, service_id): + return self.get('/service/{0}/fragment/aggregate_statistics'.format(service_id)) + class ServicesBrowsableItem(BrowsableItem): diff --git a/app/templates/components/big-number.html b/app/templates/components/big-number.html index c856544a4..dbc12bd40 100644 --- a/app/templates/components/big-number.html +++ b/app/templates/components/big-number.html @@ -1,7 +1,11 @@ -{% macro big_number(number, label, label_link=None) %} +{% macro big_number(number, label, label_link=None, currency='', right_aligned=False) %}
{% if number is number %} - {{ "{:,}".format(number) }} + {% if currency %} + {{ "{}{:,.2f}".format(currency, number) }} + {% else %} + {{ "{}{:,}".format(currency, number) }} + {% endif %} {% else %} {{ number }} {% endif %} diff --git a/app/templates/views/dashboard/all-template-statistics.html b/app/templates/views/dashboard/all-template-statistics.html index 3404df5f5..7ec88eeb6 100644 --- a/app/templates/views/dashboard/all-template-statistics.html +++ b/app/templates/views/dashboard/all-template-statistics.html @@ -6,7 +6,7 @@ {% block maincolumn_content %}

- Templates used this year + Templates sent this year

1 April 2016 to date diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index 4e4278bbc..e1d43b16c 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -6,6 +6,8 @@ {% block maincolumn_content %} +

+ {% if not templates and current_user.has_permissions(['send_texts', 'send_emails', 'send_letters'], any_=True) %} {% include 'views/dashboard/get-started.html' %} {% elif current_user.has_permissions([ @@ -20,4 +22,6 @@ {% include 'views/dashboard/today.html' %} +
+ {% endblock %} diff --git a/app/templates/views/dashboard/today.html b/app/templates/views/dashboard/today.html index 42fd196b4..b2c93bd2d 100644 --- a/app/templates/views/dashboard/today.html +++ b/app/templates/views/dashboard/today.html @@ -1,4 +1,4 @@ -{% from "components/big-number.html" import big_number_with_status %} +{% from "components/big-number.html" import big_number, big_number_with_status %} diff --git a/app/templates/views/usage.html b/app/templates/views/usage.html new file mode 100644 index 000000000..47c3e5b8c --- /dev/null +++ b/app/templates/views/usage.html @@ -0,0 +1,67 @@ +{% from "components/big-number.html" import big_number %} + +{% extends "withnav_template.html" %} + +{% block page_title %} + Billing – GOV.UK Notify +{% endblock %} + +{% block maincolumn_content %} + +
+
+

Usage

+
+
+ 1 April 2016 to date +
+
+ +
+
+

Emails

+
+ {{ big_number(emails_sent, 'sent', right_aligned=True) }} + {{ big_number("Unlimited", 'free allowance', right_aligned=True) }} +
+
+
+

Text messages

+
+ {{ big_number(sms_sent, 'sent', right_aligned=True) }} + {{ big_number(sms_free_allowance, 'free allowance', right_aligned=True) }} + {{ big_number(sms_allowance_remaining, 'free allowance remaining', right_aligned=True) }} + {% if sms_chargeable %} + {{ big_number( + sms_chargeable, + 'at {:.1f}p per message'.format(sms_rate * 100), + right_aligned=True + ) }} + {% endif %} +
+
+
+ +
+
+
+   +
+
+
+
+ {{ big_number( + (sms_chargeable * sms_rate), + 'spent', + currency="£", + right_aligned=True + ) }} +
+

+ What counts as 1 text message?
+ See pricing. +

+
+
+ +{% endblock %} diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index e5276b856..35a062ffb 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -65,7 +65,8 @@ def test_should_show_recent_templates_on_dashboard(app_, mock_get_user_by_email, mock_login, mock_get_jobs, - mock_has_permissions): + mock_has_permissions, + mock_get_usage): mock_template_stats = mocker.patch('app.template_statistics_client.get_template_statistics_for_service', return_value=copy.deepcopy(stub_template_stats)) @@ -164,7 +165,8 @@ def test_menu_send_messages(mocker, service_one, mock_get_service_templates, mock_get_jobs, - mock_get_template_statistics): + mock_get_template_statistics, + mock_get_usage): with app_.test_request_context(): resp = _test_dashboard_menu( @@ -197,7 +199,8 @@ def test_menu_manage_service(mocker, service_one, mock_get_service_templates, mock_get_jobs, - mock_get_template_statistics): + mock_get_template_statistics, + mock_get_usage): with app_.test_request_context(): resp = _test_dashboard_menu( mocker, @@ -229,7 +232,8 @@ def test_menu_manage_api_keys(mocker, service_one, mock_get_service_templates, mock_get_jobs, - mock_get_template_statistics): + mock_get_template_statistics, + mock_get_usage): with app_.test_request_context(): resp = _test_dashboard_menu( mocker, @@ -260,7 +264,8 @@ def test_menu_all_services_for_platform_admin_user(mocker, service_one, mock_get_service_templates, mock_get_jobs, - mock_get_template_statistics): + mock_get_template_statistics, + mock_get_usage): with app_.test_request_context(): resp = _test_dashboard_menu( mocker, @@ -295,7 +300,8 @@ def test_route_for_service_permissions(mocker, mock_get_service_templates, mock_get_jobs, mock_get_service_statistics, - mock_get_template_statistics): + mock_get_template_statistics, + mock_get_usage): routes = [ 'main.service_dashboard'] with app_.test_request_context(): diff --git a/tests/app/main/views/test_sign_out.py b/tests/app/main/views/test_sign_out.py index e3f3510c0..33ebc067e 100644 --- a/tests/app/main/views/test_sign_out.py +++ b/tests/app/main/views/test_sign_out.py @@ -20,7 +20,8 @@ def test_sign_out_user(app_, mock_get_service_statistics, mock_get_jobs, mock_has_permissions, - mock_get_template_statistics): + mock_get_template_statistics, + mock_get_usage): with app_.test_request_context(): with app_.test_client() as client: client.login(api_user_active) diff --git a/tests/conftest.py b/tests/conftest.py index 3696aad4a..6bf7330ec 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -898,6 +898,19 @@ def mock_get_template_statistics(mocker, service_one, fake_uuid): 'app.template_statistics_client.get_template_statistics_for_service', side_effect=_get_stats) +@pytest.fixture(scope='function') +def mock_get_usage(mocker, service_one, fake_uuid): + + def _get_usage(service_id): + return {'data': { + "sms_count": 123, + "email_count": 456 + }} + + return mocker.patch( + 'app.service_api_client.get_service_usage', side_effect=_get_usage) + + @pytest.fixture(scope='function') def mock_events(mocker):