diff --git a/app/__init__.py b/app/__init__.py index 958a324f8..fc9ad0a2d 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -38,6 +38,7 @@ from app.config import configs from app.extensions import antivirus_client, redis_client, zendesk_client from app.formatters import ( convert_to_boolean, + format_billions, format_date, format_date_human, format_date_normal, @@ -100,6 +101,9 @@ from app.notify_client.letter_jobs_client import letter_jobs_client from app.notify_client.notification_api_client import notification_api_client from app.notify_client.org_invite_api_client import org_invite_api_client from app.notify_client.organisations_api_client import organisations_client +from app.notify_client.performance_dashboard_api_client import ( + performance_dashboard_api_client, +) from app.notify_client.platform_stats_api_client import ( platform_stats_api_client, ) @@ -185,6 +189,7 @@ def create_app(application): notification_api_client, org_invite_api_client, organisations_client, + performance_dashboard_api_client, platform_stats_api_client, provider_client, service_api_client, @@ -523,6 +528,7 @@ def setup_event_handlers(): def add_template_filters(application): for fn in [ + format_billions, format_datetime, format_datetime_24h, format_datetime_normal, diff --git a/app/formatters.py b/app/formatters.py index 2f9223a44..612e45113 100644 --- a/app/formatters.py +++ b/app/formatters.py @@ -515,3 +515,7 @@ def format_mobile_network(network): if network in ('three', 'vodafone', 'o2'): return network.capitalize() return 'EE' + + +def format_billions(count): + return humanize.intword(count) diff --git a/app/main/__init__.py b/app/main/__init__.py index d5620b705..8e0542676 100644 --- a/app/main/__init__.py +++ b/app/main/__init__.py @@ -27,6 +27,7 @@ from app.main.views import ( # noqa isort:skip new_password, notifications, organisations, + performance, platform_admin, providers, register, diff --git a/app/main/views/performance.py b/app/main/views/performance.py new file mode 100644 index 000000000..86f3b8bc9 --- /dev/null +++ b/app/main/views/performance.py @@ -0,0 +1,41 @@ +from datetime import datetime, timedelta +from itertools import groupby +from operator import itemgetter +from statistics import mean + +from flask import render_template + +from app import performance_dashboard_api_client, status_api_client +from app.main import main + + +@main.route("/performance") +def performance(): + stats = performance_dashboard_api_client.get_performance_dashboard_stats( + start_date=(datetime.utcnow() - timedelta(days=7)).date(), + end_date=datetime.utcnow().date(), + ) + stats['organisations_using_notify'] = sorted( + [ + { + 'organisation_name': organisation_name, + 'count_of_live_services': len(list(group)), + } + for organisation_name, group in groupby( + stats['services_using_notify'], + itemgetter('organisation_name'), + ) + ], + key=itemgetter('organisation_name'), + ) + stats['average_percentage_under_10_seconds'] = mean([ + row['percentage_under_10_seconds'] + for row in stats['processing_time'] + ] or [0]) + stats['count_of_live_services_and_organisations'] = ( + status_api_client.get_count_of_live_services_and_organisations() + ) + return render_template( + 'views/performance.html', + **stats + ) diff --git a/app/navigation.py b/app/navigation.py index 58027c8c8..a3bd4c29f 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -246,6 +246,7 @@ class HeaderNavigation(Navigation): 'organisation_settings', 'organisation_preview_email_branding', 'organisation_preview_letter_branding', + 'performance', 'privacy', 'public_agreement', 'public_download_agreement', @@ -648,6 +649,7 @@ class MainNavigation(Navigation): 'organisation_preview_letter_branding', 'organisation_settings', 'organisations', + 'performance', 'performance_platform_xlsx', 'platform_admin', 'platform_admin_list_complaints', @@ -916,6 +918,7 @@ class CaseworkNavigation(Navigation): 'organisation_preview_letter_branding', 'organisation_settings', 'organisations', + 'performance', 'performance_platform_xlsx', 'platform_admin_list_complaints', 'platform_admin_reports', @@ -1235,6 +1238,7 @@ class OrgNavigation(Navigation): 'old_terms', 'old_using_notify', 'organisations', + 'performance', 'performance_platform_xlsx', 'platform_admin', 'platform_admin_list_complaints', diff --git a/app/notify_client/performance_dashboard_api_client.py b/app/notify_client/performance_dashboard_api_client.py new file mode 100644 index 000000000..c08734aff --- /dev/null +++ b/app/notify_client/performance_dashboard_api_client.py @@ -0,0 +1,22 @@ +from app.notify_client import NotifyAdminAPIClient, cache + + +class PerformanceDashboardAPIClient(NotifyAdminAPIClient): + + @cache.set('performance-stats-{start_date}-to-{end_date}') + def get_performance_dashboard_stats( + self, + *, + start_date, + end_date, + ): + return self.get( + '/performance-dashboard', + params={ + 'start_date': str(start_date), + 'end_date': str(end_date), + } + ) + + +performance_dashboard_api_client = PerformanceDashboardAPIClient() diff --git a/app/templates/admin_template.html b/app/templates/admin_template.html index a492c9716..f8de636a8 100644 --- a/app/templates/admin_template.html +++ b/app/templates/admin_template.html @@ -172,7 +172,7 @@ "text": "System status" }, { - "href": "https://www.gov.uk/performance/govuk-notify", + "href": url_for('main.performance'), "text": "Performance data" }, { diff --git a/app/templates/views/features.html b/app/templates/views/features.html index 21dcd8ee6..29c84c843 100644 --- a/app/templates/views/features.html +++ b/app/templates/views/features.html @@ -58,7 +58,7 @@

We send messages through several different providers. If one provider fails, Notify switches to another so that your messages are not affected.

-

Visit GOV.UK Performance to see how Notify is performing.

+

Visit our performance data page to see how Notify is performing.

Security

Notify protects and manages data to meet the needs of government services.

diff --git a/app/templates/views/performance.html b/app/templates/views/performance.html new file mode 100644 index 000000000..d8ad5e34a --- /dev/null +++ b/app/templates/views/performance.html @@ -0,0 +1,153 @@ +{% extends "withoutnav_template.html" %} +{% from "components/big-number.html" import big_number %} +{% from "components/page-header.html" import page_header %} +{% from "components/table.html" import field, list_table %} + +{% block per_page_title %} + Performance data +{% endblock %} + +{% block maincolumn_content %} + +
+
+ {{ page_header('Performance data') }} +
+
+ +

+ Messages sent since May 2016 +

+ +
+
+
{{ total_notifications|format_billions }}
+ total +
+
+
+
+ {{ big_number( + email_notifications|format_billions, + label=email_notifications|message_count_noun('email'), + smallest=True, + ) }} +
+
+ {{ big_number( + sms_notifications|format_billions, + label=sms_notifications|message_count_noun('sms'), + smallest=True, + ) }} +
+
+ {{ big_number( + letter_notifications|format_billions, + label=letter_notifications|message_count_noun('letter'), + smallest=True, + ) }} +
+
+
+
+ +
+ {% call(item, row_number) list_table( + notifications_by_type, + caption='Messages sent since May 2016', + caption_visible=False, + field_headings=[ + 'Date', + 99|message_count_noun('email')|capitalize, + 99|message_count_noun('sms')|capitalize, + 99|message_count_noun('letter')|capitalize, + ], + empty_message='No data to show' + ) %} + {% call field() %} + {{ item.date | format_date_normal }} + {% endcall %} + {% call field() %} + {{ item.emails|format_thousands }} + {% endcall %} + {% call field() %} + {{ item.sms|format_thousands }} + {% endcall %} + {% call field() %} + {{ item.letters|format_thousands }} + {% endcall %} + {% endcall %} + +
+ +

+ Messages sent within 10 seconds +

+
+
+ {{ big_number( + '{:.1f}%'.format(average_percentage_under_10_seconds), + label='on average', + ) }} +
+
+
+ {% call(item, row_number) list_table( + processing_time | reverse, + caption='Messages sent within 10 seconds', + caption_visible=False, + field_headings=[ + 'Date', 'Percentage' + ], + empty_message='No data to show' + ) %} + {% call field() %} + {{ item.date | format_date_normal }} + {% endcall %} + {% call field() %} + {{ item.percentage_under_10_seconds }}% + {% endcall %} + {% endcall %} + +
+ +

+ Organisations using Notify +

+
+
+ There are +
{{ count_of_live_services_and_organisations.organisations|format_thousands }}
+ organisations +
+
+ and +
{{ count_of_live_services_and_organisations.services|format_thousands }}
+ services + using Notify. +
+
+
+ {% call(item, row_number) list_table( + organisations_using_notify, + caption='Organisations using Notify', + caption_visible=False, + field_headings=[ + 'Organisation', 'Number of live services' + ], + empty_message='No data to show' + ) %} + {% call field() %} + {{ item.organisation_name }} + {% endcall %} + {% call field() %} + {{ item.count_of_live_services }} + {% endcall %} + {% endcall %} +
+ +{% endblock %} diff --git a/app/templates/views/signedout.html b/app/templates/views/signedout.html index 4f6e6467a..c90321418 100644 --- a/app/templates/views/signedout.html +++ b/app/templates/views/signedout.html @@ -123,19 +123,20 @@

Who’s using GOV.UK Notify

-

Services

-
{{ counts.services|format_thousands }}
- services -
-
-

Organisations

+ There are
{{ counts.organisations|format_thousands }}
organisations
+
+ and +
{{ counts.services|format_thousands }}
+ services + using Notify. +

See the - list of services and organisations. + list of services and organisations.

diff --git a/app/templates/views/terms-of-use.html b/app/templates/views/terms-of-use.html index 358b776a6..60e0ab6fc 100644 --- a/app/templates/views/terms-of-use.html +++ b/app/templates/views/terms-of-use.html @@ -31,7 +31,7 @@