Add downloadable report for org usage

This is so org level users can use this data easier for things
like determining spending per service.

We do not include sms fragments sent column and remove other sms columns

consistency.

Do not add sms fragments sent column for now until we agree on an
unambiguous name for it. The data in this column is sms billing units
multiplied by international sms weighing. My favourite for a clear
name would be 'text message credits used', but we need a naming
strategy for this.
This commit is contained in:
Pea Tyczynska
2021-11-12 18:32:35 +00:00
parent 00a629befc
commit 47e303b8c3
4 changed files with 95 additions and 6 deletions

View File

@@ -1,4 +1,5 @@
from collections import OrderedDict
from datetime import datetime
from functools import partial
from flask import flash, redirect, render_template, request, session, url_for
@@ -43,6 +44,7 @@ from app.main.views.dashboard import (
from app.main.views.service_settings import get_branding_as_value_and_label
from app.models.organisation import Organisation, Organisations
from app.models.user import InvitedOrgUser, User
from app.utils.csv import Spreadsheet
from app.utils.user import user_has_permissions, user_is_platform_admin
@@ -158,16 +160,46 @@ def organisation_dashboard(org_id):
for key in ('emails_sent', 'sms_cost', 'letter_cost')
},
download_link=url_for(
'.download_services_report_for_org',
'.download_organisation_usage_report',
org_id=org_id,
selected_year=year
)
)
@main.route("/organisations/<uuid:org_id>", methods=['GET'])
@main.route("/organisations/<uuid:org_id>/download-usage-report.csv", methods=['GET'])
@user_has_permissions()
def download_services_report_for_org(org_id):
pass
def download_organisation_usage_report(org_id):
selected_year = request.args.get('selected_year')
services_usage = current_organisation.services_and_usage(
financial_year=selected_year
)['services']
column_names = OrderedDict([
('service_id', 'Service ID'),
('service_name', 'Service Name'),
('emails_sent', 'Emails sent'),
('sms_remainder', 'Free text message allowance remaining'),
('sms_cost', 'Spent on text messages (£)'),
('letter_cost', 'Spent on letters (£)')
])
org_usage_data = [[x for x in column_names.values()]]
for service in services_usage:
org_usage_data.append([service[attribute] for attribute in column_names.keys()])
return Spreadsheet.from_rows(org_usage_data).as_csv_data, 200, {
'Content-Type': 'text/csv; charset=utf-8',
'Content-Disposition': (
'inline;'
'filename="{} organisation usage report for year {}'
' - generated on {}.csv"'.format(
current_organisation.name,
selected_year,
datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ")
))
}
@main.route("/organisations/<uuid:org_id>/trial-services", methods=['GET'])

View File

@@ -113,7 +113,7 @@
{% endif %}
<div class="js-stick-at-bottom-when-scrolling">
<p class="bottom-gutter">
<p class="govuk-!-margin-bottom-1">
<a href="{{ download_link }}" download="download" class="govuk-link govuk-link--no-visited-state govuk-!-font-weight-bold">Download this report</a>
</p>
</div>

View File

@@ -624,6 +624,7 @@ def test_organisation_services_hides_search_bar_for_7_or_fewer_services(
assert not page.select_one('.live-search')
@freeze_time("2021-11-12 11:09:00.061258")
def test_organisation_services_links_to_downloadable_report(
client_request,
mock_get_organisation,
@@ -651,7 +652,62 @@ def test_organisation_services_links_to_downloadable_report(
page = client_request.get('.organisation_dashboard', org_id=ORGANISATION_ID)
link_to_report = page.find('a', text="Download this report")
assert link_to_report.attrs["href"] == url_for('.download_services_report_for_org', org_id=ORGANISATION_ID)
assert link_to_report.attrs["href"] == url_for(
'.download_organisation_usage_report',
org_id=ORGANISATION_ID,
selected_year=2021
)
@freeze_time("2021-11-12 11:09:00.061258")
def test_download_organisation_usage_report(
client_request,
mock_get_organisation,
mocker,
active_user_with_permissions,
fake_uuid,
):
mocker.patch(
'app.organisations_client.get_services_and_usage',
return_value={"services": [
{
'service_id': SERVICE_ONE_ID,
'service_name': 'Service 1',
'chargeable_billable_sms': 22,
'emails_sent': 13000,
'free_sms_limit': 100,
'letter_cost': 30.50,
'sms_billable_units': 122,
'sms_cost': 1.93,
'sms_remainder': None
},
{
'service_id': SERVICE_TWO_ID,
'service_name': 'Service 1',
'chargeable_billable_sms': 222,
'emails_sent': 23000,
'free_sms_limit': 250000,
'letter_cost': 60.50,
'sms_billable_units': 322,
'sms_cost': 3.93,
'sms_remainder': None
},
]}
)
client_request.login(active_user_with_permissions)
csv_report = client_request.get(
'.download_organisation_usage_report',
org_id=ORGANISATION_ID,
selected_year=2021,
_test_page_title=False
)
assert csv_report.string == (
"Service ID,Service Name,Emails sent,Free text message allowance remaining,"
"Spent on text messages (£),Spent on letters (£)"
"\r\n596364a0-858e-42c8-9062-a8fe822260eb,Service 1,13000,,1.93,30.5"
"\r\n147ad62a-2951-4fa1-9ca0-093cd1a52c52,Service 1,23000,,3.93,60.5\r\n"
)
def test_organisation_trial_mode_services_shows_all_non_live_services(

View File

@@ -89,6 +89,7 @@ EXCLUDED_ENDPOINTS = tuple(map(Navigation.get_endpoint_with_blueprint, {
'documentation',
'download_contact_list',
'download_notifications_csv',
'download_organisation_usage_report',
'edit_and_format_messages',
'edit_data_retention',
'edit_organisation_agreement',