Merge pull request #4183 from alphagov/volume_reports

Report for total notifications sent per day for each channel.
This commit is contained in:
Rebecca Law
2022-03-09 15:15:03 +00:00
committed by GitHub
9 changed files with 305 additions and 0 deletions

View File

@@ -313,6 +313,74 @@ def get_billing_report():
return render_template('views/platform-admin/get-billing-report.html', form=form) return render_template('views/platform-admin/get-billing-report.html', form=form)
@main.route("/platform-admin/reports/volumes-by-service", methods=['GET', 'POST'])
@user_is_platform_admin
def get_volumes_by_service():
form = BillingReportDateFilterForm()
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",
"free allowance", "sms notifications", "sms chargeable units", "email totals",
"letter totals", "letter cost", "letter sheet totals"
]
result = billing_api_client.get_data_for_volumes_by_service_report(start_date, end_date)
rows = [
[
r["organisation_id"], r["organisation_name"], r["service_id"], r["service_name"],
r["free_allowance"], r["sms_notifications"], r["sms_chargeable_units"], r["email_totals"],
r["letter_totals"], r["letter_cost"], r["letter_sheet_totals"]
]
for r in result
]
if rows:
return Spreadsheet.from_rows([headers] + rows).as_csv_data, 200, {
'Content-Type': 'text/csv; charset=utf-8',
'Content-Disposition': 'attachment; filename="Volumes by service report from {} to {}.csv"'.format(
start_date, end_date
)
}
else:
flash('No results for dates')
return render_template('views/platform-admin/volumes-by-service-report.html', form=form)
@main.route("/platform-admin/reports/daily-volumes-report", methods=['GET', 'POST'])
@user_is_platform_admin
def get_daily_volumes():
form = BillingReportDateFilterForm()
if form.validate_on_submit():
start_date = form.start_date.data
end_date = form.end_date.data
headers = [
"day", "sms totals", "sms fragment totals", "sms chargeable units",
"email totals", "letter totals", "letter sheet totals"
]
result = billing_api_client.get_data_for_daily_volumes_report(start_date, end_date)
rows = [
[
r["day"], r["sms_totals"], r["sms_fragment_totals"], r["sms_chargeable_units"],
r["email_totals"], r["letter_totals"], r["letter_sheet_totals"]
]
for r in result
]
if rows:
return Spreadsheet.from_rows([headers] + rows).as_csv_data, 200, {
'Content-Type': 'text/csv; charset=utf-8',
'Content-Disposition': 'attachment; filename="Daily volumes report from {} to {}.csv"'.format(
start_date, end_date
)
}
else:
flash('No results for dates')
return render_template('views/platform-admin/daily-volumes-report.html', form=form)
@main.route("/platform-admin/complaints") @main.route("/platform-admin/complaints")
@user_is_platform_admin @user_is_platform_admin
def platform_admin_list_complaints(): def platform_admin_list_complaints():

View File

@@ -92,6 +92,8 @@ class HeaderNavigation(Navigation):
'live_services_csv', 'live_services_csv',
'notifications_sent_by_service', 'notifications_sent_by_service',
'get_billing_report', 'get_billing_report',
'get_daily_volumes',
'get_volumes_by_service',
'organisations', 'organisations',
'platform_admin', 'platform_admin',
'platform_admin_list_complaints', 'platform_admin_list_complaints',

View File

@@ -41,5 +41,19 @@ class BillingAPIClient(NotifyAdminAPIClient):
'end_date': str(end_date), 'end_date': str(end_date),
}) })
def get_data_for_volumes_by_service_report(self, start_date, end_date):
return self.get(url='/platform-stats/volumes-by-service',
params={
'start_date': str(start_date),
'end_date': str(end_date),
})
def get_data_for_daily_volumes_report(self, start_date, end_date):
return self.get(url='/platform-stats/daily-volumes-report',
params={
'start_date': str(start_date),
'end_date': str(end_date),
})
billing_api_client = BillingAPIClient() billing_api_client = BillingAPIClient()

View File

@@ -0,0 +1,48 @@
{% extends "views/platform-admin/_base_template.html" %}
{% from "components/form.html" import form_wrapper %}
{% from "components/table.html" import mapping_table, row, text_field %}
{% block per_page_title %}
Daily volumes report
{% endblock %}
{% block platform_admin_content %}
<h1 class="heading-large">
Daily volumes Report
</h1>
{% call form_wrapper() %}
{{ form.start_date(param_extensions={"hint": {"text": "Use the format YYYY-MM-DD"}}) }}
{{ form.end_date(param_extensions={"hint": {"text": "Use the format YYYY-MM-DD"}}) }}
{{ page_footer('Download report') }}
{% endcall %}
<h2 class="heading-medium">
Data included in the report
</h2>
<div class="bottom-gutter-3-2">
{% call mapping_table(
caption='Descriptions of daily volumes data',
field_headings=['Name', 'Description'],
field_headings_visible=True,
caption_visible=False
) %}
{% for message_length, charge in [
('day', 'The whole business day in BST.'),
('sms totals', 'The number of text messages sent'),
('sms fragments', 'The number of text message fragments sent times the rate multiplier'),
('sms chargeable units', 'The number of text message fragments sent'),
('email totals', 'The number of emails sent'),
('letter totals', 'The number of letters sent'),
('letter sheet totals', 'The number of sheets sent')
] %}
{% call row() %}
{{ text_field(message_length) }}
{{ text_field(charge | safe) }}
{% endcall %}
{% endfor %}
{% endcall %}
</div>
{% endblock %}

View File

@@ -19,4 +19,10 @@
<p class="govuk-body"> <p class="govuk-body">
<a class="govuk-link govuk-link--no-visited-state" href="{{ url_for('main.get_billing_report') }}">Billing Report</a> <a class="govuk-link govuk-link--no-visited-state" href="{{ url_for('main.get_billing_report') }}">Billing Report</a>
</p> </p>
<p class="govuk-body">
<a class="govuk-link govuk-link--no-visited-state" href="{{ url_for('main.get_volumes_by_service') }}">Volumes by service Report</a>
</p>
<p class="govuk-body">
<a class="govuk-link govuk-link--no-visited-state" href="{{ url_for('main.get_daily_volumes') }}">Daily volumes Report</a>
</p>
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,48 @@
{% extends "views/platform-admin/_base_template.html" %}
{% from "components/form.html" import form_wrapper %}
{% from "components/table.html" import mapping_table, row, text_field %}
{% block per_page_title %}
Volumes by service report
{% endblock %}
{% block platform_admin_content %}
<h1 class="heading-large">
Volumes by service Report
</h1>
{% call form_wrapper() %}
{{ form.start_date(param_extensions={"hint": {"text": "Use the format YYYY-MM-DD"}}) }}
{{ form.end_date(param_extensions={"hint": {"text": "Use the format YYYY-MM-DD"}}) }}
{{ page_footer('Download report') }}
{% endcall %}
<h2 class="heading-medium">
Data included in the report
</h2>
<div class="bottom-gutter-3-2">
{% call mapping_table(
caption='Descriptions of volumes by service data',
field_headings=['Name', 'Description'],
field_headings_visible=True,
caption_visible=False
) %}
{% for message_length, charge in [
('free allowance', 'Free allowance set for the service. This is the latest free allowance for the date range given'),
('sms notifications', 'The number of text messages sent by the service.'),
('sms chargeable units', 'The number of text message fragments times the rate multiplier sent by the service.'),
('email totals', 'The number of emails sent by a service'),
('letter totals', 'The number of letters sent by a service'),
('letter costs', 'The cost of the letters, rate * letter totals'),
('letter sheet totals', 'The number of sheet sent by a service')
] %}
{% call row() %}
{{ text_field(message_length) }}
{{ text_field(charge | safe) }}
{% endcall %}
{% endfor %}
{% endcall %}
</div>
{% endblock %}

View File

@@ -1014,3 +1014,102 @@ def test_get_notifications_sent_by_service_calls_api_and_downloads_data(
'2019-01-01,596364a0-858e-42c8-9062-a8fe822260eb,service one,sms,42,0,0,8,0,0\r\n' '2019-01-01,596364a0-858e-42c8-9062-a8fe822260eb,service one,sms,42,0,0,8,0,0\r\n'
'2019-01-01,147ad62a-2951-4fa1-9ca0-093cd1a52c52,service two,email,3,1,0,2,0,0\r\n' '2019-01-01,147ad62a-2951-4fa1-9ca0-093cd1a52c52,service two,email,3,1,0,2,0,0\r\n'
) )
def test_get_volumes_by_service_report_when_calls_api_and_download_data(
client_request,
platform_admin_user,
mocker
):
mocker.patch(
"app.main.views.platform_admin.billing_api_client.get_data_for_volumes_by_service_report",
return_value=[{
"organisation_id": "7832a1be-a1f0-4f2a-982f-05adfd3d6354",
"organisation_name": "Org name",
"service_id": "48e82ac0-c8c4-4e46-8712-c83c35a94006",
"service_name": "service name",
"free_allowance": 10000,
"sms_notifications": 10,
"sms_chargeable_units": 20,
"email_totals": 8,
"letter_totals": 10,
"letter_cost": 4.5,
"letter_sheet_totals": 10
}]
)
client_request.login(platform_admin_user)
response = client_request.post_response(
'main.get_volumes_by_service',
_data={'start_date': '2019-01-01', 'end_date': '2019-03-31'},
_expected_status=200,
)
assert response.content_type == 'text/csv; charset=utf-8'
assert response.headers['Content-Disposition'] == (
'attachment; filename="Volumes by service report from {} to {}.csv"'.format('2019-01-01', '2019-03-31')
)
assert response.get_data(as_text=True) == (
"organisation id,organisation name,service id,service name,free allowance,sms notifications," +
"sms chargeable units,email totals,letter totals,letter cost,letter sheet totals\r\n" +
'7832a1be-a1f0-4f2a-982f-05adfd3d6354,' +
'Org name,' +
'48e82ac0-c8c4-4e46-8712-c83c35a94006,' +
'service name,' +
'10000,' +
'10,' +
'20,' +
'8,' +
'10,' +
'4.5,' +
'10'
'\r\n'
)
def test_get_daily_volumes_report_when_calls_api_and_download_data(
client_request,
platform_admin_user,
mocker
):
mocker.patch(
"app.main.views.platform_admin.billing_api_client.get_data_for_daily_volumes_report",
return_value=[{
"day": '2019-01-01',
"sms_totals": 20,
"sms_fragment_totals": 40,
"sms_chargeable_units": 60,
"email_totals": 100,
"letter_totals": 10,
"letter_sheet_totals": 20
}]
)
client_request.login(platform_admin_user)
response = client_request.post_response(
'main.get_daily_volumes',
_data={'start_date': '2019-01-01', 'end_date': '2019-03-31'},
_expected_status=200,
)
assert response.content_type == 'text/csv; charset=utf-8'
assert response.headers['Content-Disposition'] == (
'attachment; filename="Daily volumes report from {} to {}.csv"'.format('2019-01-01', '2019-03-31')
)
assert response.get_data(as_text=True) == (
"day,sms totals,sms fragment totals,sms chargeable units,email totals,letter totals,letter sheet totals\r\n" +
'2019-01-01,' +
'20,' +
'40,' +
'60,' +
'100,' +
'10,' +
'20'
'\r\n'
)

View File

@@ -41,3 +41,21 @@ def test_post_free_sms_fragment_limit_for_year_endpoint(mocker, api_user_active)
url='/service/{}/billing/free-sms-fragment-limit'.format(service_id), url='/service/{}/billing/free-sms-fragment-limit'.format(service_id),
data=sms_limit_data data=sms_limit_data
) )
def test_get_data_for_volumes_by_service_report(mocker, api_user_active):
mock_get = mocker.patch('app.notify_client.billing_api_client.BillingAPIClient.get')
client = BillingAPIClient()
client.get_data_for_volumes_by_service_report('2022-03-01', '2022-03-31')
mock_get.assert_called_once_with(url='/platform-stats/volumes-by-service',
params={'start_date': '2022-03-01', 'end_date': '2022-03-31'})
def test_get_data_for_daily_volumes_report(mocker, api_user_active):
mock_get = mocker.patch('app.notify_client.billing_api_client.BillingAPIClient.get')
client = BillingAPIClient()
client.get_data_for_daily_volumes_report('2022-03-01', '2022-03-31')
mock_get.assert_called_once_with(url='/platform-stats/daily-volumes-report',
params={'start_date': '2022-03-01', 'end_date': '2022-03-31'})

View File

@@ -131,6 +131,8 @@ EXCLUDED_ENDPOINTS = tuple(map(Navigation.get_endpoint_with_blueprint, {
'find_users_by_email', 'find_users_by_email',
'forgot_password', 'forgot_password',
'get_billing_report', 'get_billing_report',
'get_daily_volumes',
'get_volumes_by_service',
'get_example_csv', 'get_example_csv',
'get_notifications_as_json', 'get_notifications_as_json',
'get_started', 'get_started',