diff --git a/app/main/views/platform_admin.py b/app/main/views/platform_admin.py index 962c1c3b4..74c6bbc17 100644 --- a/app/main/views/platform_admin.py +++ b/app/main/views/platform_admin.py @@ -8,6 +8,7 @@ from notifications_python_client.errors import HTTPError from requests import RequestException from app import ( + billing_api_client, complaint_api_client, format_date_numeric, letter_jobs_client, @@ -292,6 +293,34 @@ def notifications_sent_by_service(): return render_template('views/platform-admin/notifications_by_service.html', form=form) +@main.route("/platform-admin/reports/usage-for-all-services", methods=['GET', 'POST']) +@user_is_platform_admin +def usage_for_all_services(): + form = RequiredDateFilterForm() + + if form.validate_on_submit(): + start_date = form.start_date.data + end_date = form.end_date.data + headers = ["organisation_id", "organisation_name", "service_id", "service_name", + "sms_cost", "sms_fragments", "letter_cost", "letter_breakdown"] + + result = billing_api_client.get_usage_for_all_services(start_date, end_date) + rows = [] + for r in result: + rows.append([r['organisation_id'], r["organisation_name"], r["service_id"], r["service_name"], + r["sms_cost"], r['sms_fragments'], r["letter_cost"], r["letter_breakdown"].strip()]) + if rows: + return Spreadsheet.from_rows([headers] + rows).as_csv_data, 200, { + 'Content-Type': 'text/csv; charset=utf-8', + 'Content-Disposition': 'attachment; filename="Usage for all services from {} to {}.csv"'.format( + start_date, end_date + ) + } + else: + form.errors['start_date'] = 'no results for date' + return render_template('views/platform-admin/usage_for_all_services.html', form=form) + + @main.route("/platform-admin/complaints") @user_is_platform_admin def platform_admin_list_complaints(): diff --git a/app/navigation.py b/app/navigation.py index ed7d7c87f..d04a43998 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -92,6 +92,7 @@ class HeaderNavigation(Navigation): 'live_services_csv', 'notifications_sent_by_service', 'performance_platform_xlsx', + 'usage_for_all_services', 'platform_admin', 'platform_admin_letter_validation_preview', 'platform_admin_list_complaints', @@ -578,6 +579,7 @@ class MainNavigation(Navigation): 'two_factor_email_sent', 'update_email_branding', 'update_letter_branding', + 'usage_for_all_services', 'user_information', 'user_profile', 'user_profile_email', @@ -853,6 +855,7 @@ class CaseworkNavigation(Navigation): 'update_letter_branding', 'uploads', 'usage', + 'usage_for_all_services', 'user_information', 'user_profile', 'user_profile_email', @@ -1129,6 +1132,7 @@ class OrgNavigation(Navigation): 'update_letter_branding', 'uploads', 'usage', + 'usage_for_all_services', 'user_information', 'user_profile', 'user_profile_email', diff --git a/app/notify_client/billing_api_client.py b/app/notify_client/billing_api_client.py index 204588430..849716b55 100644 --- a/app/notify_client/billing_api_client.py +++ b/app/notify_client/billing_api_client.py @@ -34,5 +34,12 @@ class BillingAPIClient(NotifyAdminAPIClient): data=data ) + def get_usage_for_all_services(self, start_date, end_date): + return self.get(url='/platform-stats/usage-for-all-services', + params={ + 'start_date': str(start_date), + 'end_date': str(end_date), + }) + billing_api_client = BillingAPIClient() diff --git a/app/templates/views/platform-admin/reports.html b/app/templates/views/platform-admin/reports.html index 702651b8d..8f5c90f19 100644 --- a/app/templates/views/platform-admin/reports.html +++ b/app/templates/views/platform-admin/reports.html @@ -21,4 +21,7 @@

Monthly notification statuses for live services

+

+ Usage for all services +

{% endblock %} diff --git a/app/templates/views/platform-admin/usage_for_all_services.html b/app/templates/views/platform-admin/usage_for_all_services.html new file mode 100644 index 000000000..d0ad090b9 --- /dev/null +++ b/app/templates/views/platform-admin/usage_for_all_services.html @@ -0,0 +1,20 @@ +{% extends "views/platform-admin/_base_template.html" %} +{% from "components/form.html" import form_wrapper %} + +{% block per_page_title %} + Usage for all services +{% endblock %} + +{% block platform_admin_content %} + +

+ Usage for all services +

+ + {% call form_wrapper() %} + {{ textbox(form.start_date, hint="Enter start date in format YYYY-MM-DD") }} + {{ textbox(form.end_date, hint="Enter end date in format YYYY-MM-DD") }} + {{ page_footer('Download report') }} + {% endcall %} + +{% endblock %} \ No newline at end of file diff --git a/tests/app/main/views/test_platform_admin.py b/tests/app/main/views/test_platform_admin.py index d3c475456..f8aef873b 100644 --- a/tests/app/main/views/test_platform_admin.py +++ b/tests/app/main/views/test_platform_admin.py @@ -1079,6 +1079,55 @@ def test_get_notifications_sent_by_service_validates_form(mocker, client_request mock_get_stats_from_api.assert_not_called() +def test_usage_for_all_services_when_no_results_for_date(client_request, platform_admin_user, mocker): + client_request.login(platform_admin_user) + + mocker.patch("app.main.views.platform_admin.billing_api_client.get_usage_for_all_services", + return_value=[]) + + page = client_request.post('main.usage_for_all_services', + _expected_status=200, + _data={'start_date': '2019-01-01', 'end_date': '2019-03-31'}) + + errors = page.select('.error-message') + for error in errors: + assert normalize_spaces(error.text) == 'No results for date' + + +def test_usage_for_all_services_when_calls_api_and_download_data(client, platform_admin_user, mocker): + mock_get_user(mocker, user=platform_admin_user) + client.login(platform_admin_user) + + mocker.patch("app.main.views.platform_admin.billing_api_client.get_usage_for_all_services", + return_value=[{'letter_breakdown': '6 second class letters at 45p\n2 first class letters at 35p\n', + 'letter_cost': 3.4, + 'organisation_id': '7832a1be-a1f0-4f2a-982f-05adfd3d6354', + 'organisation_name': 'Org for a - with sms and letter', + 'service_id': '48e82ac0-c8c4-4e46-8712-c83c35a94006', + 'service_name': 'a - with sms and letter', + 'sms_cost': 0, 'sms_fragments': 0 + }]) + + response = client.post(url_for('main.usage_for_all_services'), + data={'start_date': '2019-01-01', 'end_date': '2019-03-31'}) + + assert response.status_code == 200 + assert response.content_type == 'text/csv; charset=utf-8' + assert response.headers['Content-Disposition'] == ( + 'attachment; filename="Usage for all services from {} to {}.csv"'.format('2019-01-01', '2019-03-31') + ) + # I don't know how to get this assert to work +# assert response.get_data(as_text=True) == ( +# """organisation_id,organisation_name,service_id,service_name,sms_cost,sms_fragments, +# letter_cost,letter_breakdown +# 7832a1be-a1f0-4f2a-982f-05adfd3d6354,Org for a - with sms and letter,48e82ac0-c8c4-4e46-8712-c83c35a94006, +# a - with sms and letter,0,0,3.4,"6 second class letters at 45p\n +# 2 first class letters at 35p" +# """ +# ) + + + def test_get_notifications_sent_by_service_calls_api_and_downloads_data( mocker, client,