diff --git a/app/assets/stylesheets/components/table.scss b/app/assets/stylesheets/components/table.scss index c748bdbcc..8f908ddde 100644 --- a/app/assets/stylesheets/components/table.scss +++ b/app/assets/stylesheets/components/table.scss @@ -118,6 +118,10 @@ } + &-noborder { + border: 0px; + } + &-index { width: 15px; } @@ -159,6 +163,12 @@ text-align: right; } + +.table-row-group { + border-top: 1px solid $border-colour; + border-bottom: 1px solid $border-colour; +} + .table-empty-message { @include core-16; color: $secondary-text-colour; diff --git a/app/main/views/platform_admin.py b/app/main/views/platform_admin.py index 7c883d7e9..46bb030d8 100644 --- a/app/main/views/platform_admin.py +++ b/app/main/views/platform_admin.py @@ -1,7 +1,5 @@ import itertools -from datetime import datetime -import pytz from flask import render_template from flask_login import login_required @@ -63,13 +61,10 @@ def create_global_stats(services): def format_stats_by_service(services): for service in services: - stats = service['statistics'].values() yield { 'id': service['id'], 'name': service['name'], - 'sending': sum((stat['requested'] - stat['delivered'] - stat['failed']) for stat in stats), - 'delivered': sum(stat['delivered'] for stat in stats), - 'failed': sum(stat['failed'] for stat in stats), + 'stats': service['statistics'], 'restricted': service['restricted'], 'research_mode': service['research_mode'], 'created_at': service['created_at'] diff --git a/app/templates/components/table.html b/app/templates/components/table.html index 97783ff93..9b0eaf8bc 100644 --- a/app/templates/components/table.html +++ b/app/templates/components/table.html @@ -49,10 +49,20 @@ {%- endmacro %} -{% macro field(align='left', status='') -%} - - {{ caller() }} - +{% macro row_group(id=None) %} + + {{ caller() }} + +{%- 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' %} + + + {{ caller() }} + {%- endmacro %} {% macro row_heading() -%} diff --git a/app/templates/views/platform-admin.html b/app/templates/views/platform-admin.html index 38902bc7d..d8b8e6569 100644 --- a/app/templates/views/platform-admin.html +++ b/app/templates/views/platform-admin.html @@ -2,49 +2,76 @@ {% from "components/big-number.html" import big_number, big_number_with_status %} {% from "components/message-count-label.html" import message_count_label %} {% 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) %} + {{ channel.title() }} + {% 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) %} - {% call(item, row_number) list_table( - services, + {% call(item, row_number) mapping_table( caption=caption, caption_visible=True, field_headings=[ - 'Service', - hidden_field_heading('Status'), - right_aligned_field_heading('Sending'), - right_aligned_field_heading('Delivered'), - right_aligned_field_heading('Failed') + 'Service', + hidden_field_heading('Type'), + right_aligned_field_heading('Sending'), + right_aligned_field_heading('Delivered'), + right_aligned_field_heading('Failed') ], field_headings_visible=True ) %} - {% call field() %} -
- {{ item['name'] }} -
- {% endcall %} - {% if item['research_mode'] %} - {% call field() %} - research mode + + {% for service in services %} + + {% call row_group() %} + + {% call row() %} + {% call field(border=False) %} + {{ service['name'] }} + {% endcall %} + + {{ stats_fields('email', service['stats']) }} + {% endcall %} + + {% call row() %} + {% if service['research_mode'] %} + {% call field(border=False) %} + research mode + {% endcall %} + {% elif not service['restricted'] %} + {% call field(status='error') %} + + Live + + {% endcall %} + {% else %} + {{ text_field('') }} + {% endif %} + + {{ stats_fields('sms', service['stats']) }} + {% endcall %} + {% endcall %} - {% elif not item['restricted'] %} - {% call field(status='error') %} - - Live - - {% 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 %} + + {% endfor %} + {% endcall %} {% endmacro %} diff --git a/tests/app/main/views/test_platform_admin.py b/tests/app/main/views/test_platform_admin.py index ca95d704a..74ba43a35 100644 --- a/tests/app/main/views/test_platform_admin.py +++ b/tests/app/main/views/test_platform_admin.py @@ -60,8 +60,10 @@ def test_should_show_research_and_restricted_mode( assert response.status_code == 200 mock_get_detailed_services.assert_called_once_with({'detailed': True}) page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') - # get second column, which contains flags as text. - assert page.find_all('tbody')[table_index].find_all('td')[1].text.strip() == displayed + # get first column in second row, which contains flags as text. + 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( @@ -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[0]['statistics'] = create_stats( 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)) - assert len(ret) == 1 - assert ret[0]['sending'] == 34 - assert ret[0]['delivered'] == 10 - assert ret[0]['failed'] == 16 + assert ret[0]['stats']['email']['requested'] == 10 + assert ret[0]['stats']['email']['delivered'] == 3 + 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