diff --git a/app/main/forms.py b/app/main/forms.py index 57599ee78..45b40abb5 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -16,8 +16,8 @@ from wtforms import ( HiddenField, IntegerField, RadioField, - FieldList -) + FieldList, + DateField) from wtforms.fields.html5 import EmailField, TelField from wtforms.validators import (DataRequired, Email, Length, Regexp, Optional) @@ -516,3 +516,9 @@ class Whitelist(Form): max_entries=5, label="Mobile numbers" ) + + +class DateFilterForm(Form): + start_date = DateField("Start Date", [validators.optional()]) + end_date = DateField("End Date", [validators.optional()]) + include_from_test_key = BooleanField("Include test keys", default="checked", false_values={"N"}) diff --git a/app/main/views/platform_admin.py b/app/main/views/platform_admin.py index d6494620f..462320c26 100644 --- a/app/main/views/platform_admin.py +++ b/app/main/views/platform_admin.py @@ -1,5 +1,6 @@ import itertools +from datetime import datetime from flask import ( render_template, request @@ -8,6 +9,7 @@ from flask_login import login_required from app import service_api_client from app.main import main +from app.main.forms import DateFilterForm from app.utils import user_has_permissions from app.statistics_utils import get_formatted_percentage @@ -16,15 +18,21 @@ from app.statistics_utils import get_formatted_percentage @login_required @user_has_permissions(admin_override=True) def platform_admin(): - include_from_test_key = request.args.get('include_from_test_key') != 'False' - # specifically DO get inactive services - api_args = {'detailed': True} - if not include_from_test_key: - api_args['include_from_test_key'] = False + form = DateFilterForm(request.args) + api_args = {'detailed': True, # specifically DO get inactive services + 'include_from_test_key': form.include_from_test_key.data + } + + if form.start_date.data: + api_args['start_date'] = form.start_date.data + api_args['end_date'] = form.end_date.data or datetime.utcnow().date() + services = service_api_client.get_services(api_args)['data'] + return render_template( 'views/platform-admin.html', - include_from_test_key=include_from_test_key, + include_from_test_key=form.include_from_test_key.data, + form=form, **get_statistics(sorted( services, key=lambda service: (service['active'], service['created_at']), @@ -64,7 +72,6 @@ def create_global_stats(services): for stat in stats.values(): stat['failure_rate'] = get_formatted_percentage(stat['failed'], stat['requested']) - return stats diff --git a/app/templates/views/platform-admin.html b/app/templates/views/platform-admin.html index 52eb48d4b..94b1ae7dc 100644 --- a/app/templates/views/platform-admin.html +++ b/app/templates/views/platform-admin.html @@ -1,4 +1,7 @@ {% extends "withoutnav_template.html" %} +{% from "components/textbox.html" import textbox %} +{% from "components/checkbox.html" import checkbox %} +{% from "components/page-footer.html" import page_footer %} {% from "components/big-number.html" import big_number, big_number_with_status %} {% from "components/message-count-label.html" import message_count_label %} {% from "components/table.html" import mapping_table, field, stats_fields, row_group, row, right_aligned_field_heading, hidden_field_heading, text_field %} @@ -87,14 +90,16 @@ Platform admin -

- Showing stats for today  - {% if include_from_test_key %} - Including test keys (change) - {% else %} - Excluding test keys (change) - {% endif %} -

+
+ Apply filters +
+ {{ textbox(form.start_date, hint="Enter start date in format YYYY-MM-DD") }} + {{ textbox(form.end_date, hint="Enter end date in format YYYY-MM-DD") }} + {{ checkbox(form.include_from_test_key) }} +
+ +
+
diff --git a/tests/app/main/views/test_platform_admin.py b/tests/app/main/views/test_platform_admin.py index 6f6fd5411..0ac1aafea 100644 --- a/tests/app/main/views/test_platform_admin.py +++ b/tests/app/main/views/test_platform_admin.py @@ -1,14 +1,13 @@ -from datetime import date +import datetime from flask import url_for -from freezegun import freeze_time import pytest from bs4 import BeautifulSoup from tests.conftest import mock_get_user from tests import service_json -from app.main.views.platform_admin import get_statistics, format_stats_by_service, create_global_stats +from app.main.views.platform_admin import format_stats_by_service, create_global_stats def test_should_redirect_if_not_logged_in(app_): @@ -58,7 +57,7 @@ def test_should_show_research_and_restricted_mode( response = client.get(url_for('main.platform_admin')) 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, 'include_from_test_key': True}) page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') # get first column in second row, which contains flags as text. table_body = page.find_all('table')[table_index].find_all('tbody')[0] @@ -81,20 +80,17 @@ def test_should_render_platform_admin_page( assert response.status_code == 200 resp_data = response.get_data(as_text=True) assert 'Platform admin' in resp_data - assert 'Showing stats for today' in resp_data assert 'Live services' in resp_data assert 'Trial mode services' in resp_data - mock_get_detailed_services.assert_called_once_with({'detailed': True}) + mock_get_detailed_services.assert_called_once_with({'detailed': True, 'include_from_test_key': True}) -@pytest.mark.parametrize('include_from_test_key, expected_text, unexpected_text, api_args', [ - (True, 'Including test keys', 'Excluding test keys', {'detailed': True}), - (False, 'Excluding test keys', 'Including test keys', {'detailed': True, 'include_from_test_key': False}) +@pytest.mark.parametrize('include_from_test_key, api_args', [ + ("Y", {'detailed': True, 'include_from_test_key': True}), + ("N", {'detailed': True, 'include_from_test_key': False}) ]) def test_platform_admin_toggle_including_from_test_key( include_from_test_key, - expected_text, - unexpected_text, api_args, app_, platform_admin_user, @@ -105,23 +101,33 @@ def test_platform_admin_toggle_including_from_test_key( 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', include_from_test_key=str(include_from_test_key))) + response = client.get(url_for('main.platform_admin', include_from_test_key=include_from_test_key)) + + assert response.status_code == 200 + mock_get_detailed_services.assert_called_once_with(api_args) + + +def test_platform_admin_with_date_filter( + app_, + platform_admin_user, + mocker, + mock_get_detailed_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', start_date='2016-12-20', end_date='2016-12-28')) assert response.status_code == 200 resp_data = response.get_data(as_text=True) - assert expected_text in resp_data - assert unexpected_text not in resp_data - - page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') - change_link = page.find('a', text='change') - assert change_link['href'] - query_param = 'include_from_test_key=False' - if include_from_test_key: - assert query_param in change_link['href'] - else: - assert query_param not in change_link['href'] - - mock_get_detailed_services.assert_called_once_with(api_args) + assert 'Platform admin' in resp_data + assert 'Live services' in resp_data + assert 'Trial mode services' in resp_data + mock_get_detailed_services.assert_called_once_with({'include_from_test_key': False, + 'start_date': datetime.date(2016, 12, 20), + 'end_date': datetime.date(2016, 12, 28), + 'detailed': True}) def test_create_global_stats_sets_failure_rates(fake_uuid): @@ -235,7 +241,7 @@ def test_should_show_email_and_sms_stats_for_all_service_types( response = client.get(url_for('main.platform_admin')) 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, 'include_from_test_key': True}) page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') table_body = page.find_all('table')[table_index].find_all('tbody')[0] @@ -281,7 +287,7 @@ def test_should_show_archived_services_last( response = client.get(url_for('main.platform_admin')) 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, 'include_from_test_key': True}) page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') table_body = page.find_all('table')[table_index].find_all('tbody')[0]