mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-02-05 10:53:28 -05:00
Template statistics now surfaced on dashboard.
Job list removed. Template statistics retrieved at same time as notification stats.
This commit is contained in:
@@ -29,6 +29,8 @@ from app.notify_client.notification_api_client import NotificationApiClient
|
|||||||
from app.notify_client.status_api_client import StatusApiClient
|
from app.notify_client.status_api_client import StatusApiClient
|
||||||
from app.notify_client.invite_api_client import InviteApiClient
|
from app.notify_client.invite_api_client import InviteApiClient
|
||||||
from app.notify_client.statistics_api_client import StatisticsApiClient
|
from app.notify_client.statistics_api_client import StatisticsApiClient
|
||||||
|
from app.notify_client.template_statistics_api_client import TemplateStatisticsApiClient
|
||||||
|
|
||||||
from app.its_dangerous_session import ItsdangerousSessionInterface
|
from app.its_dangerous_session import ItsdangerousSessionInterface
|
||||||
from app.asset_fingerprinter import AssetFingerprinter
|
from app.asset_fingerprinter import AssetFingerprinter
|
||||||
from utils.recipients import validate_phone_number, InvalidPhoneError
|
from utils.recipients import validate_phone_number, InvalidPhoneError
|
||||||
@@ -51,6 +53,7 @@ notification_api_client = NotificationApiClient()
|
|||||||
status_api_client = StatusApiClient()
|
status_api_client = StatusApiClient()
|
||||||
invite_api_client = InviteApiClient()
|
invite_api_client = InviteApiClient()
|
||||||
statistics_api_client = StatisticsApiClient()
|
statistics_api_client = StatisticsApiClient()
|
||||||
|
template_statistics_client = TemplateStatisticsApiClient()
|
||||||
asset_fingerprinter = AssetFingerprinter()
|
asset_fingerprinter = AssetFingerprinter()
|
||||||
|
|
||||||
# The current service attached to the request stack.
|
# The current service attached to the request stack.
|
||||||
@@ -74,6 +77,7 @@ def create_app():
|
|||||||
status_api_client.init_app(application)
|
status_api_client.init_app(application)
|
||||||
invite_api_client.init_app(application)
|
invite_api_client.init_app(application)
|
||||||
statistics_api_client.init_app(application)
|
statistics_api_client.init_app(application)
|
||||||
|
template_statistics_client.init_app(application)
|
||||||
|
|
||||||
login_manager.init_app(application)
|
login_manager.init_app(application)
|
||||||
login_manager.login_view = 'main.sign_in'
|
login_manager.login_view = 'main.sign_in'
|
||||||
@@ -95,6 +99,7 @@ def create_app():
|
|||||||
application.add_template_filter(syntax_highlight_json)
|
application.add_template_filter(syntax_highlight_json)
|
||||||
application.add_template_filter(valid_phone_number)
|
application.add_template_filter(valid_phone_number)
|
||||||
application.add_template_filter(linkable_name)
|
application.add_template_filter(linkable_name)
|
||||||
|
application.add_template_filter(format_date)
|
||||||
|
|
||||||
application.after_request(useful_headers_after_request)
|
application.after_request(useful_headers_after_request)
|
||||||
application.after_request(save_service_after_request)
|
application.after_request(save_service_after_request)
|
||||||
@@ -175,6 +180,11 @@ def format_time(date):
|
|||||||
return native.strftime('%H:%M')
|
return native.strftime('%H:%M')
|
||||||
|
|
||||||
|
|
||||||
|
def format_date(date):
|
||||||
|
date = dateutil.parser.parse(date)
|
||||||
|
return date.strftime('%A %d %B %Y')
|
||||||
|
|
||||||
|
|
||||||
def valid_phone_number(phone_number):
|
def valid_phone_number(phone_number):
|
||||||
try:
|
try:
|
||||||
validate_phone_number(phone_number)
|
validate_phone_number(phone_number)
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
|
from datetime import date
|
||||||
|
|
||||||
from flask import (
|
from flask import (
|
||||||
render_template,
|
render_template,
|
||||||
session,
|
session,
|
||||||
flash,
|
flash,
|
||||||
jsonify,
|
jsonify
|
||||||
request
|
|
||||||
)
|
)
|
||||||
from datetime import date
|
|
||||||
|
|
||||||
from flask_login import login_required
|
from flask_login import login_required
|
||||||
|
|
||||||
from app.main import main
|
from app.main import main
|
||||||
from app import (job_api_client, statistics_api_client, service_api_client, current_service)
|
from app import (
|
||||||
|
job_api_client,
|
||||||
|
statistics_api_client,
|
||||||
|
service_api_client,
|
||||||
|
template_statistics_client,
|
||||||
|
current_service
|
||||||
|
)
|
||||||
|
|
||||||
from app.utils import user_has_permissions
|
from app.utils import user_has_permissions
|
||||||
|
|
||||||
|
|
||||||
@@ -27,6 +35,7 @@ def service_dashboard(service_id):
|
|||||||
flash(message, 'default_with_tick')
|
flash(message, 'default_with_tick')
|
||||||
|
|
||||||
statistics = statistics_api_client.get_statistics_for_service(service_id)['data']
|
statistics = statistics_api_client.get_statistics_for_service(service_id)['data']
|
||||||
|
template_statistics = template_statistics_client.get_template_statistics_for_service(service_id)
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'views/dashboard/dashboard.html',
|
'views/dashboard/dashboard.html',
|
||||||
@@ -36,7 +45,8 @@ def service_dashboard(service_id):
|
|||||||
spent_this_month='0.00',
|
spent_this_month='0.00',
|
||||||
statistics=add_rates_to(statistics),
|
statistics=add_rates_to(statistics),
|
||||||
templates=templates,
|
templates=templates,
|
||||||
service_id=str(service_id))
|
service_id=str(service_id),
|
||||||
|
template_statistics=template_statistics)
|
||||||
|
|
||||||
|
|
||||||
@main.route("/services/<service_id>/dashboard.json")
|
@main.route("/services/<service_id>/dashboard.json")
|
||||||
@@ -44,11 +54,13 @@ def service_dashboard(service_id):
|
|||||||
def service_dashboard_updates(service_id):
|
def service_dashboard_updates(service_id):
|
||||||
|
|
||||||
statistics = statistics_api_client.get_statistics_for_service(service_id)['data']
|
statistics = statistics_api_client.get_statistics_for_service(service_id)['data']
|
||||||
|
template_statistics = template_statistics_client.get_template_statistics_for_service(service_id)
|
||||||
|
|
||||||
return jsonify(**{
|
return jsonify(**{
|
||||||
'today': render_template(
|
'today': render_template(
|
||||||
'views/dashboard/today.html',
|
'views/dashboard/today.html',
|
||||||
statistics=add_rates_to(statistics),
|
statistics=add_rates_to(statistics),
|
||||||
|
template_statistics=template_statistics
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from flask import render_template, url_for, redirect
|
from flask import render_template, url_for, redirect, jsonify
|
||||||
from app.main import main
|
from app.main import main
|
||||||
from flask_login import login_required
|
from flask_login import login_required
|
||||||
|
|
||||||
|
|||||||
18
app/notify_client/template_statistics_api_client.py
Normal file
18
app/notify_client/template_statistics_api_client.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
from notifications_python_client.base import BaseAPIClient
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateStatisticsApiClient(BaseAPIClient):
|
||||||
|
def __init__(self, base_url=None, client_id=None, secret=None):
|
||||||
|
super(self.__class__, self).__init__(base_url=base_url or 'base_url',
|
||||||
|
client_id=client_id or 'client_id',
|
||||||
|
secret=secret or 'secret')
|
||||||
|
|
||||||
|
def init_app(self, app):
|
||||||
|
self.base_url = app.config['API_HOST_NAME']
|
||||||
|
self.client_id = app.config['ADMIN_CLIENT_USER_NAME']
|
||||||
|
self.secret = app.config['ADMIN_CLIENT_SECRET']
|
||||||
|
|
||||||
|
def get_template_statistics_for_service(self, service_id):
|
||||||
|
return self.get(
|
||||||
|
url='/service/{}/template-statistics'.format(service_id),
|
||||||
|
)['data']
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block maincolumn_content %}
|
{% block maincolumn_content %}
|
||||||
|
|
||||||
{% if not templates and current_user.has_permissions(['send_texts', 'send_emails', 'send_letters'], any_=True) %}
|
{% if not templates and current_user.has_permissions(['send_texts', 'send_emails', 'send_letters'], any_=True) %}
|
||||||
{% include 'views/dashboard/get-started.html' %}
|
{% include 'views/dashboard/get-started.html' %}
|
||||||
{% elif current_service.restricted %}
|
{% elif current_service.restricted %}
|
||||||
@@ -22,8 +22,5 @@
|
|||||||
>
|
>
|
||||||
{% include 'views/dashboard/today.html' %}
|
{% include 'views/dashboard/today.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% include 'views/dashboard/jobs.html' %}
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
23
app/templates/views/dashboard/template-statistics.html
Normal file
23
app/templates/views/dashboard/template-statistics.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{% from "components/table.html" import list_table, field, right_aligned_field_heading %}
|
||||||
|
{% call(item) list_table(
|
||||||
|
template_statistics,
|
||||||
|
caption="Recent templates used",
|
||||||
|
empty_message='You haven’t sent any batch messages yet',
|
||||||
|
field_headings=['Template', 'Type','Date', right_aligned_field_heading('Usage')]
|
||||||
|
) %}
|
||||||
|
{% call field() %}
|
||||||
|
<a href="{{ url_for('.edit_service_template', service_id=service_id, template_id=item.template.id) }}">
|
||||||
|
{{ item.template.name }}
|
||||||
|
</a>
|
||||||
|
{% endcall %}
|
||||||
|
{% call field() %}
|
||||||
|
{{item.template.template_type}}
|
||||||
|
{% endcall %}
|
||||||
|
{% call field() %}
|
||||||
|
{{ item.day|format_date }}
|
||||||
|
{% endcall %}
|
||||||
|
{% call field(align='right') %}
|
||||||
|
{{ item.usage_count }}
|
||||||
|
{% endcall %}
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
23
app/templates/views/dashboard/template_statistics.html
Normal file
23
app/templates/views/dashboard/template_statistics.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{% from "components/table.html" import list_table, field, right_aligned_field_heading %}
|
||||||
|
{% call(item) list_table(
|
||||||
|
template_statistics,
|
||||||
|
caption="Recent templates used",
|
||||||
|
empty_message='You haven’t sent any batch messages yet',
|
||||||
|
field_headings=['Template', 'Type','Date', right_aligned_field_heading('Usage')]
|
||||||
|
) %}
|
||||||
|
{% call field() %}
|
||||||
|
<a href="{{ url_for('.edit_service_template', service_id=service_id, template_id=item.template.id) }}">
|
||||||
|
{{ item.template.name }}
|
||||||
|
</a>
|
||||||
|
{% endcall %}
|
||||||
|
{% call field() %}
|
||||||
|
{{item.template.template_type}}
|
||||||
|
{% endcall %}
|
||||||
|
{% call field() %}
|
||||||
|
{{ item.day|format_date }}
|
||||||
|
{% endcall %}
|
||||||
|
{% call field(align='right') %}
|
||||||
|
{{ item.usage_count }}
|
||||||
|
{% endcall %}
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
@@ -23,3 +23,6 @@
|
|||||||
) }}
|
) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% include 'views/dashboard/template-statistics.html' %}
|
||||||
|
|
||||||
|
|||||||
@@ -338,6 +338,7 @@ def test_new_invited_user_verifies_and_added_to_service(app_,
|
|||||||
mock_get_service,
|
mock_get_service,
|
||||||
mock_get_service_templates,
|
mock_get_service_templates,
|
||||||
mock_get_service_statistics,
|
mock_get_service_statistics,
|
||||||
|
mock_get_template_statistics,
|
||||||
mock_get_jobs,
|
mock_get_jobs,
|
||||||
mock_has_permissions):
|
mock_has_permissions):
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
from flask import url_for
|
from flask import url_for
|
||||||
|
|
||||||
from tests import validate_route_permission
|
from tests import validate_route_permission
|
||||||
|
from tests.conftest import SERVICE_ONE_ID
|
||||||
|
|
||||||
|
|
||||||
def test_should_show_recent_jobs_on_dashboard(app_,
|
def test_should_show_recent_jobs_on_dashboard(app_,
|
||||||
@@ -7,20 +9,22 @@ def test_should_show_recent_jobs_on_dashboard(app_,
|
|||||||
mock_get_service,
|
mock_get_service,
|
||||||
mock_get_service_templates,
|
mock_get_service_templates,
|
||||||
mock_get_service_statistics,
|
mock_get_service_statistics,
|
||||||
|
mock_get_template_statistics,
|
||||||
mock_get_user,
|
mock_get_user,
|
||||||
mock_get_user_by_email,
|
mock_get_user_by_email,
|
||||||
mock_login,
|
mock_login,
|
||||||
mock_get_jobs,
|
mock_get_jobs,
|
||||||
mock_has_permissions):
|
mock_has_permissions):
|
||||||
|
|
||||||
with app_.test_request_context():
|
with app_.test_request_context():
|
||||||
with app_.test_client() as client:
|
with app_.test_client() as client:
|
||||||
client.login(api_user_active)
|
client.login(api_user_active)
|
||||||
response = client.get(url_for('main.service_dashboard', service_id=123))
|
response = client.get(url_for('main.service_dashboard', service_id=SERVICE_ONE_ID))
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
text = response.get_data(as_text=True)
|
text = response.get_data(as_text=True)
|
||||||
assert 'Test Service' in text
|
assert 'Test Service' in text
|
||||||
|
mock_get_service_statistics.assert_called_once_with(SERVICE_ONE_ID)
|
||||||
|
mock_get_template_statistics.assert_called_once_with(SERVICE_ONE_ID)
|
||||||
|
|
||||||
|
|
||||||
def _test_dashboard_menu(mocker, app_, usr, service, permissions):
|
def _test_dashboard_menu(mocker, app_, usr, service, permissions):
|
||||||
@@ -37,7 +41,14 @@ def _test_dashboard_menu(mocker, app_, usr, service, permissions):
|
|||||||
return client.get(url_for('main.service_dashboard', service_id=service['id']))
|
return client.get(url_for('main.service_dashboard', service_id=service['id']))
|
||||||
|
|
||||||
|
|
||||||
def test_menu_send_messages(mocker, app_, api_user_active, service_one, mock_get_service_templates, mock_get_jobs):
|
def test_menu_send_messages(mocker,
|
||||||
|
app_,
|
||||||
|
api_user_active,
|
||||||
|
service_one,
|
||||||
|
mock_get_service_templates,
|
||||||
|
mock_get_jobs,
|
||||||
|
mock_get_template_statistics):
|
||||||
|
|
||||||
with app_.test_request_context():
|
with app_.test_request_context():
|
||||||
resp = _test_dashboard_menu(
|
resp = _test_dashboard_menu(
|
||||||
mocker,
|
mocker,
|
||||||
@@ -63,7 +74,13 @@ def test_menu_send_messages(mocker, app_, api_user_active, service_one, mock_get
|
|||||||
assert url_for('main.show_all_services') not in page
|
assert url_for('main.show_all_services') not in page
|
||||||
|
|
||||||
|
|
||||||
def test_menu_manage_service(mocker, app_, api_user_active, service_one, mock_get_service_templates, mock_get_jobs):
|
def test_menu_manage_service(mocker,
|
||||||
|
app_,
|
||||||
|
api_user_active,
|
||||||
|
service_one,
|
||||||
|
mock_get_service_templates,
|
||||||
|
mock_get_jobs,
|
||||||
|
mock_get_template_statistics):
|
||||||
with app_.test_request_context():
|
with app_.test_request_context():
|
||||||
resp = _test_dashboard_menu(
|
resp = _test_dashboard_menu(
|
||||||
mocker,
|
mocker,
|
||||||
@@ -89,7 +106,13 @@ def test_menu_manage_service(mocker, app_, api_user_active, service_one, mock_ge
|
|||||||
assert url_for('main.show_all_services') not in page
|
assert url_for('main.show_all_services') not in page
|
||||||
|
|
||||||
|
|
||||||
def test_menu_manage_api_keys(mocker, app_, api_user_active, service_one, mock_get_service_templates, mock_get_jobs):
|
def test_menu_manage_api_keys(mocker,
|
||||||
|
app_,
|
||||||
|
api_user_active,
|
||||||
|
service_one,
|
||||||
|
mock_get_service_templates,
|
||||||
|
mock_get_jobs,
|
||||||
|
mock_get_template_statistics):
|
||||||
with app_.test_request_context():
|
with app_.test_request_context():
|
||||||
resp = _test_dashboard_menu(
|
resp = _test_dashboard_menu(
|
||||||
mocker,
|
mocker,
|
||||||
@@ -114,8 +137,13 @@ def test_menu_manage_api_keys(mocker, app_, api_user_active, service_one, mock_g
|
|||||||
assert url_for('main.api_keys', service_id=service_one['id']) in page
|
assert url_for('main.api_keys', service_id=service_one['id']) in page
|
||||||
|
|
||||||
|
|
||||||
def test_menu_all_services_for_platform_admin_user(mocker, app_, platform_admin_user, service_one,
|
def test_menu_all_services_for_platform_admin_user(mocker,
|
||||||
mock_get_service_templates, mock_get_jobs):
|
app_,
|
||||||
|
platform_admin_user,
|
||||||
|
service_one,
|
||||||
|
mock_get_service_templates,
|
||||||
|
mock_get_jobs,
|
||||||
|
mock_get_template_statistics):
|
||||||
with app_.test_request_context():
|
with app_.test_request_context():
|
||||||
resp = _test_dashboard_menu(
|
resp = _test_dashboard_menu(
|
||||||
mocker,
|
mocker,
|
||||||
@@ -130,8 +158,8 @@ def test_menu_all_services_for_platform_admin_user(mocker, app_, platform_admin_
|
|||||||
assert url_for('main.manage_users', service_id=service_one['id']) in page
|
assert url_for('main.manage_users', service_id=service_one['id']) in page
|
||||||
assert url_for('main.service_settings', service_id=service_one['id']) in page
|
assert url_for('main.service_settings', service_id=service_one['id']) in page
|
||||||
assert url_for('main.view_notifications', service_id=service_one['id']) in page
|
assert url_for('main.view_notifications', service_id=service_one['id']) in page
|
||||||
assert url_for('main.view_jobs', service_id=service_one['id']) in page
|
|
||||||
assert url_for('main.api_keys', service_id=service_one['id']) not in page
|
assert url_for('main.api_keys', service_id=service_one['id']) not in page
|
||||||
|
assert url_for('main.edit_service_template', service_id=service_one['id'], template_id=1) in page
|
||||||
|
|
||||||
|
|
||||||
def test_route_for_service_permissions(mocker,
|
def test_route_for_service_permissions(mocker,
|
||||||
@@ -142,7 +170,8 @@ def test_route_for_service_permissions(mocker,
|
|||||||
mock_get_user,
|
mock_get_user,
|
||||||
mock_get_service_templates,
|
mock_get_service_templates,
|
||||||
mock_get_jobs,
|
mock_get_jobs,
|
||||||
mock_get_service_statistics):
|
mock_get_service_statistics,
|
||||||
|
mock_get_template_statistics):
|
||||||
routes = [
|
routes = [
|
||||||
'main.service_dashboard']
|
'main.service_dashboard']
|
||||||
with app_.test_request_context():
|
with app_.test_request_context():
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ def test_sign_out_user(app_,
|
|||||||
api_user_active,
|
api_user_active,
|
||||||
mock_get_user,
|
mock_get_user,
|
||||||
mock_get_user_by_email,
|
mock_get_user_by_email,
|
||||||
|
mock_login,
|
||||||
mock_get_service_templates,
|
mock_get_service_templates,
|
||||||
mock_get_service_statistics,
|
mock_get_service_statistics,
|
||||||
mock_login,
|
|
||||||
mock_get_jobs,
|
mock_get_jobs,
|
||||||
mock_has_permissions):
|
mock_has_permissions,
|
||||||
|
mock_get_template_statistics):
|
||||||
with app_.test_request_context():
|
with app_.test_request_context():
|
||||||
with app_.test_client() as client:
|
with app_.test_client() as client:
|
||||||
client.login(api_user_active)
|
client.login(api_user_active)
|
||||||
|
|||||||
@@ -783,7 +783,24 @@ def mock_remove_user_from_service(mocker):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
@pytest.fixture(scope='function')
|
||||||
def mock_get_service_statistics(mocker):
|
def mock_get_template_statistics(mocker, service_one):
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
template = template_json(service_one['id'], 1, "Test template", "sms", "Something very interesting")
|
||||||
|
data = {
|
||||||
|
"usage_count": 1,
|
||||||
|
"template": {
|
||||||
|
"name": template['name'],
|
||||||
|
"template_type": template['template_type'],
|
||||||
|
"id": template['id']
|
||||||
|
},
|
||||||
|
"service": template['service'],
|
||||||
|
"id": str(uuid.uuid4()),
|
||||||
|
"day": "2016-04-04"
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_stats(service_id):
|
||||||
|
return [data]
|
||||||
|
|
||||||
return mocker.patch(
|
return mocker.patch(
|
||||||
'app.statistics_api_client.get_statistics_for_service',
|
'app.template_statistics_client.get_template_statistics_for_service', side_effect=_get_stats)
|
||||||
return_value={'data': [{}]})
|
|
||||||
|
|||||||
Reference in New Issue
Block a user