mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-09 15:19:49 -04:00
Merge pull request #4183 from alphagov/volume_reports
Report for total notifications sent per day for each channel.
This commit is contained in:
@@ -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():
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
48
app/templates/views/platform-admin/daily-volumes-report.html
Normal file
48
app/templates/views/platform-admin/daily-volumes-report.html
Normal 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 %}
|
||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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 %}
|
||||||
@@ -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'
|
||||||
|
)
|
||||||
|
|||||||
@@ -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'})
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
Reference in New Issue
Block a user