mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-10 18:22:37 -04:00
Merge pull request #1025 from alphagov/show-email-sms-stats-platform-admin
Show email and sms counts for each service on platform admin
This commit is contained in:
@@ -118,6 +118,10 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-noborder {
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
&-index {
|
&-index {
|
||||||
width: 15px;
|
width: 15px;
|
||||||
}
|
}
|
||||||
@@ -159,6 +163,12 @@
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.table-row-group {
|
||||||
|
border-top: 1px solid $border-colour;
|
||||||
|
border-bottom: 1px solid $border-colour;
|
||||||
|
}
|
||||||
|
|
||||||
.table-empty-message {
|
.table-empty-message {
|
||||||
@include core-16;
|
@include core-16;
|
||||||
color: $secondary-text-colour;
|
color: $secondary-text-colour;
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import itertools
|
import itertools
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
import pytz
|
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
from flask_login import login_required
|
from flask_login import login_required
|
||||||
|
|
||||||
@@ -63,13 +61,10 @@ def create_global_stats(services):
|
|||||||
|
|
||||||
def format_stats_by_service(services):
|
def format_stats_by_service(services):
|
||||||
for service in services:
|
for service in services:
|
||||||
stats = service['statistics'].values()
|
|
||||||
yield {
|
yield {
|
||||||
'id': service['id'],
|
'id': service['id'],
|
||||||
'name': service['name'],
|
'name': service['name'],
|
||||||
'sending': sum((stat['requested'] - stat['delivered'] - stat['failed']) for stat in stats),
|
'stats': service['statistics'],
|
||||||
'delivered': sum(stat['delivered'] for stat in stats),
|
|
||||||
'failed': sum(stat['failed'] for stat in stats),
|
|
||||||
'restricted': service['restricted'],
|
'restricted': service['restricted'],
|
||||||
'research_mode': service['research_mode'],
|
'research_mode': service['research_mode'],
|
||||||
'created_at': service['created_at']
|
'created_at': service['created_at']
|
||||||
|
|||||||
@@ -49,10 +49,20 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{% macro field(align='left', status='') -%}
|
{% macro row_group(id=None) %}
|
||||||
<td class="table-field{% if align == 'right' %}-right-aligned{% endif %}">
|
<tbody class="table-row-group" {% if id %}id="{{id}}"{% endif %}>
|
||||||
<span class="{{ 'table-field-status-' + status if status }}">{{ caller() }}</span>
|
{{ caller() }}
|
||||||
</td>
|
</tbody>
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro field(align='left', status='', border=True) -%}
|
||||||
|
|
||||||
|
{% set field_alignment = 'table-field-right-aligned' if align == 'right' else 'table-field-center-aligned' %}
|
||||||
|
{% set border = '' if border else 'table-field-noborder' %}
|
||||||
|
|
||||||
|
<td class="{{ [field_alignment, border]|join(' ') }}">
|
||||||
|
<span class="{{ 'table-field-status-' + status if status }}">{{ caller() }}</span>
|
||||||
|
</td>
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{% macro row_heading() -%}
|
{% macro row_heading() -%}
|
||||||
|
|||||||
@@ -2,49 +2,76 @@
|
|||||||
{% from "components/big-number.html" import big_number, big_number_with_status %}
|
{% from "components/big-number.html" import big_number, big_number_with_status %}
|
||||||
{% from "components/message-count-label.html" import message_count_label %}
|
{% from "components/message-count-label.html" import message_count_label %}
|
||||||
{% from "components/browse-list.html" import browse_list %}
|
{% from "components/browse-list.html" import browse_list %}
|
||||||
{% from "components/table.html" import list_table, field, right_aligned_field_heading, hidden_field_heading, text_field %}
|
{% from "components/table.html" import mapping_table, field, stats_fields, row_group, row, right_aligned_field_heading, hidden_field_heading, text_field %}
|
||||||
|
|
||||||
|
{% macro stats_fields(channel, data) -%}
|
||||||
|
|
||||||
|
{% call field(border=False) %}
|
||||||
|
<span class="heading-medium">{{ channel.title() }}</span>
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
|
{% call field(align='right', border=False) %}
|
||||||
|
{{ big_number(data[channel]['requested'], smaller=True) }}
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
|
{% call field(align='right', border=False) %}
|
||||||
|
{{ big_number(data[channel]['delivered'], smaller=True) }}
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
|
{% call field(align='right', status='error' if data[channel]['failed'], border=False) %}
|
||||||
|
{{ big_number(data[channel]['failed'], smaller=True) }}
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
{% macro services_table(services, caption) %}
|
{% macro services_table(services, caption) %}
|
||||||
{% call(item, row_number) list_table(
|
{% call(item, row_number) mapping_table(
|
||||||
services,
|
|
||||||
caption=caption,
|
caption=caption,
|
||||||
caption_visible=True,
|
caption_visible=True,
|
||||||
field_headings=[
|
field_headings=[
|
||||||
'Service',
|
'Service',
|
||||||
hidden_field_heading('Status'),
|
hidden_field_heading('Type'),
|
||||||
right_aligned_field_heading('Sending'),
|
right_aligned_field_heading('Sending'),
|
||||||
right_aligned_field_heading('Delivered'),
|
right_aligned_field_heading('Delivered'),
|
||||||
right_aligned_field_heading('Failed')
|
right_aligned_field_heading('Failed')
|
||||||
],
|
],
|
||||||
field_headings_visible=True
|
field_headings_visible=True
|
||||||
) %}
|
) %}
|
||||||
{% call field() %}
|
|
||||||
<div>
|
{% for service in services %}
|
||||||
<a href="{{ url_for('main.service_dashboard', service_id=item['id']) }}" class="browse-list-link">{{ item['name'] }}</a>
|
|
||||||
</div>
|
{% call row_group() %}
|
||||||
{% endcall %}
|
|
||||||
{% if item['research_mode'] %}
|
{% call row() %}
|
||||||
{% call field() %}
|
{% call field(border=False) %}
|
||||||
<span class="research-mode">research mode</span>
|
<a href="{{ url_for('main.service_dashboard', service_id=service['id']) }}" class="browse-list-link">{{ service['name'] }}</a>
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
|
{{ stats_fields('email', service['stats']) }}
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
|
{% call row() %}
|
||||||
|
{% if service['research_mode'] %}
|
||||||
|
{% call field(border=False) %}
|
||||||
|
<span class="research-mode">research mode</span>
|
||||||
|
{% endcall %}
|
||||||
|
{% elif not service['restricted'] %}
|
||||||
|
{% call field(status='error') %}
|
||||||
|
<span class="heading-medium">
|
||||||
|
Live
|
||||||
|
</span>
|
||||||
|
{% endcall %}
|
||||||
|
{% else %}
|
||||||
|
{{ text_field('') }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{{ stats_fields('sms', service['stats']) }}
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
{% elif not item['restricted'] %}
|
|
||||||
{% call field(status='error') %}
|
{% endfor %}
|
||||||
<span class="heading-medium">
|
|
||||||
Live
|
|
||||||
</span>
|
|
||||||
{% endcall %}
|
|
||||||
{% else %}
|
|
||||||
{{ text_field('') }}
|
|
||||||
{% endif %}
|
|
||||||
{% call field(align='right') %}
|
|
||||||
{{ big_number(item['sending'], smaller=True) }}
|
|
||||||
{% endcall %}
|
|
||||||
{% call field(align='right') %}
|
|
||||||
{{ big_number(item['delivered'], smaller=True) }}
|
|
||||||
{% endcall %}
|
|
||||||
{% call field(align='right', status='error' if 0 else '') %}
|
|
||||||
{{ big_number(item['failed'], smaller=True) }}
|
|
||||||
{% endcall %}
|
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|||||||
@@ -60,8 +60,10 @@ def test_should_show_research_and_restricted_mode(
|
|||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
mock_get_detailed_services.assert_called_once_with({'detailed': True})
|
mock_get_detailed_services.assert_called_once_with({'detailed': True})
|
||||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
# get second column, which contains flags as text.
|
# get first column in second row, which contains flags as text.
|
||||||
assert page.find_all('tbody')[table_index].find_all('td')[1].text.strip() == displayed
|
table_body = page.find_all('table')[table_index].find_all('tbody')[0]
|
||||||
|
service_mode = table_body.find_all('tbody')[0].find_all('tr')[1].find_all('td')[0].text.strip()
|
||||||
|
assert service_mode == displayed
|
||||||
|
|
||||||
|
|
||||||
def test_should_render_platform_admin_page(
|
def test_should_render_platform_admin_page(
|
||||||
@@ -141,7 +143,7 @@ def create_stats(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_format_stats_by_service_sums_values_for_sending(fake_uuid):
|
def test_format_stats_by_service_returns_correct_values(fake_uuid):
|
||||||
services = [service_json(fake_uuid, 'a', [])]
|
services = [service_json(fake_uuid, 'a', [])]
|
||||||
services[0]['statistics'] = create_stats(
|
services[0]['statistics'] = create_stats(
|
||||||
emails_requested=10,
|
emails_requested=10,
|
||||||
@@ -153,8 +155,62 @@ def test_format_stats_by_service_sums_values_for_sending(fake_uuid):
|
|||||||
)
|
)
|
||||||
|
|
||||||
ret = list(format_stats_by_service(services))
|
ret = list(format_stats_by_service(services))
|
||||||
|
|
||||||
assert len(ret) == 1
|
assert len(ret) == 1
|
||||||
assert ret[0]['sending'] == 34
|
assert ret[0]['stats']['email']['requested'] == 10
|
||||||
assert ret[0]['delivered'] == 10
|
assert ret[0]['stats']['email']['delivered'] == 3
|
||||||
assert ret[0]['failed'] == 16
|
assert ret[0]['stats']['email']['failed'] == 5
|
||||||
|
|
||||||
|
assert ret[0]['stats']['sms']['requested'] == 50
|
||||||
|
assert ret[0]['stats']['sms']['delivered'] == 7
|
||||||
|
assert ret[0]['stats']['sms']['failed'] == 11
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('restricted, table_index, research_mode', [
|
||||||
|
(True, 1, False),
|
||||||
|
(False, 0, False)
|
||||||
|
])
|
||||||
|
def test_should_show_email_and_sms_stats_for_all_service_types(
|
||||||
|
restricted,
|
||||||
|
table_index,
|
||||||
|
research_mode,
|
||||||
|
app_,
|
||||||
|
platform_admin_user,
|
||||||
|
mocker,
|
||||||
|
mock_get_detailed_services,
|
||||||
|
fake_uuid
|
||||||
|
):
|
||||||
|
services = [service_json(fake_uuid, 'My Service', [], restricted=restricted, research_mode=research_mode)]
|
||||||
|
services[0]['statistics'] = create_stats(
|
||||||
|
emails_requested=10,
|
||||||
|
emails_delivered=3,
|
||||||
|
emails_failed=5,
|
||||||
|
sms_requested=50,
|
||||||
|
sms_delivered=7,
|
||||||
|
sms_failed=11
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_get_detailed_services.return_value = {'data': services}
|
||||||
|
with app_.test_request_context():
|
||||||
|
with app_.test_client() as client:
|
||||||
|
mock_get_user(mocker, user=platform_admin_user)
|
||||||
|
client.login(platform_admin_user)
|
||||||
|
response = client.get(url_for('main.platform_admin'))
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
mock_get_detailed_services.assert_called_once_with({'detailed': True})
|
||||||
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
|
|
||||||
|
table_body = page.find_all('table')[table_index].find_all('tbody')[0]
|
||||||
|
service_row_group = table_body.find_all('tbody')[0].find_all('tr')
|
||||||
|
email_stats = service_row_group[0].find_all('td')[2:]
|
||||||
|
sms_stats = service_row_group[1].find_all('td')[2:]
|
||||||
|
|
||||||
|
email_sending, email_delivered, email_failed = [int(stat.text.split()[0]) for stat in email_stats]
|
||||||
|
sms_sending, sms_delivered, sms_failed = [int(stat.text.split()[0]) for stat in sms_stats]
|
||||||
|
|
||||||
|
assert email_sending == 10
|
||||||
|
assert email_delivered == 3
|
||||||
|
assert email_failed == 5
|
||||||
|
assert sms_sending == 50
|
||||||
|
assert sms_delivered == 7
|
||||||
|
assert sms_failed == 11
|
||||||
|
|||||||
Reference in New Issue
Block a user