mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-05-31 03:20:28 -04:00
Add usage to the dashboard
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.
This commit is contained in:
@@ -35,6 +35,11 @@
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.align-with-heading-copy {
|
||||
display: block;
|
||||
margin-top: 45px;
|
||||
}
|
||||
|
||||
.global-cookie-message {
|
||||
p {
|
||||
@extend %site-width-container;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
20
app/assets/stylesheets/views/dashboard.scss
Normal file
20
app/assets/stylesheets/views/dashboard.scss
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -80,6 +80,16 @@ def template_history(service_id):
|
||||
)
|
||||
|
||||
|
||||
@main.route("/services/<service_id>/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,
|
||||
}
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
{% macro big_number(number, label, label_link=None) %}
|
||||
{% macro big_number(number, label, label_link=None, currency='', right_aligned=False) %}
|
||||
<div class="big-number{% if right_aligned %}-right-aligned{% endif %}">
|
||||
{% if number is number %}
|
||||
{{ "{:,}".format(number) }}
|
||||
{% if currency %}
|
||||
{{ "{}{:,.2f}".format(currency, number) }}
|
||||
{% else %}
|
||||
{{ "{}{:,}".format(currency, number) }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ number }}
|
||||
{% endif %}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
{% block maincolumn_content %}
|
||||
<h1 class='heading-large'>
|
||||
Templates used this year
|
||||
Templates sent this year
|
||||
</h1>
|
||||
<p>
|
||||
1 April 2016 to date
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="dashboard">
|
||||
|
||||
{% 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' %}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
<div
|
||||
data-module="update-content"
|
||||
@@ -38,6 +38,36 @@
|
||||
{% include 'views/dashboard/template-statistics.html' %}
|
||||
{% endwith %}
|
||||
<p class='table-show-more-link'>
|
||||
<a href="{{ url_for('.template_history', service_id=current_service.id) }}">See all templates used this year</a>
|
||||
<a href="{{ url_for('.template_history', service_id=current_service.id) }}">See all templates sent this year</a>
|
||||
</p>
|
||||
|
||||
{% if current_user.has_permissions(['manage_settings'], admin_override=True) %}
|
||||
<h2 class='heading-medium' style="margin-top: 30px;">This year</h2>
|
||||
|
||||
<div class='grid-row'>
|
||||
<div class='column-half'>
|
||||
<div class="keyline-block">
|
||||
{{ big_number("Unlimited", 'free email allowance', right_aligned=True) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class='column-half'>
|
||||
<div class="keyline-block">
|
||||
{% if sms_chargeable %}
|
||||
{{ big_number(
|
||||
(sms_chargeable * sms_rate),
|
||||
'spent on text messages',
|
||||
currency="£",
|
||||
right_aligned=True
|
||||
) }}
|
||||
{% else %}
|
||||
{{ big_number(sms_allowance_remaining, 'free text messages left', right_aligned=True) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="keyline-block-show-more-link">
|
||||
<a href="{{ url_for(".usage", service_id=current_service['id']) }}">See usage</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
67
app/templates/views/usage.html
Normal file
67
app/templates/views/usage.html
Normal file
@@ -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 %}
|
||||
|
||||
<div class='grid-row'>
|
||||
<div class='column-half'>
|
||||
<h2 class='heading-large'>Usage</h2>
|
||||
</div>
|
||||
<div class='column-half'>
|
||||
<span class="align-with-heading-copy">1 April 2016 to date</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='grid-row'>
|
||||
<div class='column-half'>
|
||||
<h2 class='heading-small'>Emails</h2>
|
||||
<div class="keyline-block">
|
||||
{{ big_number(emails_sent, 'sent', right_aligned=True) }}
|
||||
{{ big_number("Unlimited", 'free allowance', right_aligned=True) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class='column-half'>
|
||||
<h2 class='heading-small'>Text messages</h2>
|
||||
<div class="keyline-block">
|
||||
{{ 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 %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='grid-row'>
|
||||
<div class='column-half'>
|
||||
<div class="keyline-block">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class='column-half'>
|
||||
<div class="keyline-block bottom-gutter">
|
||||
{{ big_number(
|
||||
(sms_chargeable * sms_rate),
|
||||
'spent',
|
||||
currency="£",
|
||||
right_aligned=True
|
||||
) }}
|
||||
</div>
|
||||
<p>
|
||||
What counts as 1 text message?<br />
|
||||
See <a href="{{ url_for('.pricing') }}">pricing</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -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():
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user