Merge branch 'main' of https://github.com/GSA/notifications-admin into 2391-youtube-video-not-playing

This commit is contained in:
Jonathan Bobel
2025-03-13 14:24:13 -04:00
11 changed files with 45 additions and 694 deletions

View File

@@ -4,32 +4,22 @@ from functools import partial
from itertools import groupby
from zoneinfo import ZoneInfo
from flask import Response, abort, jsonify, render_template, request, session, url_for
from flask import abort, jsonify, render_template, request, session, url_for
from flask_login import current_user
from werkzeug.utils import redirect
from app import (
billing_api_client,
current_service,
job_api_client,
service_api_client,
template_statistics_client,
)
from app.formatters import format_date_numeric, format_datetime_numeric
from app.main import main
from app.main.views.user_profile import set_timezone
from app.statistics_utils import get_formatted_percentage
from app.utils import (
DELIVERED_STATUSES,
FAILURE_STATUSES,
REQUESTED_STATUSES,
service_has_permission,
)
from app.utils.csv import Spreadsheet
from app.utils.pagination import generate_next_dict, generate_previous_dict
from app.utils import DELIVERED_STATUSES, FAILURE_STATUSES, REQUESTED_STATUSES
from app.utils.time import get_current_financial_year
from app.utils.user import user_has_permissions
from notifications_utils.recipients import format_phone_number_human_readable
@main.route("/services/<uuid:service_id>/dashboard")
@@ -62,28 +52,25 @@ def service_dashboard(service_id):
total_messages = service_api_client.get_service_message_ratio(service_id)
messages_remaining = total_messages.get("messages_remaining", 0)
messages_sent = total_messages.get("messages_sent", 0)
all_statistics = template_statistics_client.get_template_statistics_for_service(
service_id, limit_days=7
)
template_statistics = aggregate_template_usage(all_statistics)
return render_template(
"views/dashboard/dashboard.html",
updates_url=url_for(".service_dashboard_updates", service_id=service_id),
partials=get_dashboard_partials(service_id),
jobs=job_lists,
service_data_retention_days=service_data_retention_days,
messages_remaining=messages_remaining,
messages_sent=messages_sent,
template_statistics=template_statistics,
most_used_template_count=max(
[row["count"] for row in template_statistics] or [0]
),
)
def job_is_finished(job_dict):
done_statuses = [
"delivered",
"sent",
"failed",
"technical-failure",
"temporary-failure",
"permanent-failure",
"cancelled",
]
done_statuses = DELIVERED_STATUSES + FAILURE_STATUSES + ["cancelled"]
processed_count = sum(
stat["count"]
for stat in job_dict["statistics"]
@@ -154,18 +141,6 @@ def get_daily_stats_by_user(service_id):
return jsonify(stats)
@main.route("/services/<uuid:service_id>/dashboard.json")
@user_has_permissions("view_activity")
def service_dashboard_updates(service_id):
return jsonify(**get_dashboard_partials(service_id))
@main.route("/services/<uuid:service_id>/template-activity")
@user_has_permissions("view_activity")
def template_history(service_id):
return redirect(url_for("main.template_usage", service_id=service_id), code=301)
@main.route("/services/<uuid:service_id>/template-usage")
@user_has_permissions("view_activity")
def template_usage(service_id):
@@ -251,103 +226,6 @@ def usage(service_id):
)
@main.route("/services/<uuid:service_id>/monthly")
@user_has_permissions("view_activity")
def monthly(service_id):
year, current_financial_year = requested_and_current_financial_year(request)
return render_template(
"views/dashboard/monthly.html",
months=format_monthly_stats_to_list(
service_api_client.get_monthly_notification_stats(service_id, year)["data"]
),
years=get_tuples_of_financial_years(
partial_url=partial(url_for, ".monthly", service_id=service_id),
start=current_financial_year - 2,
end=current_financial_year,
),
selected_year=year,
)
@main.route("/services/<uuid:service_id>/inbox")
@user_has_permissions("view_activity")
@service_has_permission("inbound_sms")
def inbox(service_id):
return render_template(
"views/dashboard/inbox.html",
partials=get_inbox_partials(service_id),
updates_url=url_for(
".inbox_updates", service_id=service_id, page=request.args.get("page")
),
)
@main.route("/services/<uuid:service_id>/inbox.json")
@user_has_permissions("view_activity")
@service_has_permission("inbound_sms")
def inbox_updates(service_id):
return jsonify(get_inbox_partials(service_id))
@main.route("/services/<uuid:service_id>/inbox.csv")
@user_has_permissions("view_activity")
def inbox_download(service_id):
return Response(
Spreadsheet.from_rows(
[
[
"Phone number",
"Message",
"Received",
]
]
+ [
[
format_phone_number_human_readable(message["user_number"]),
message["content"].lstrip(("=+-@")),
format_datetime_numeric(message["created_at"]),
]
for message in service_api_client.get_inbound_sms(service_id)["data"]
]
).as_csv_data,
mimetype="text/csv",
headers={
"Content-Disposition": 'inline; filename="Received text messages {}.csv"'.format(
format_date_numeric(datetime.utcnow().isoformat())
)
},
)
def get_inbox_partials(service_id):
page = int(request.args.get("page", 1))
inbound_messages_data = service_api_client.get_most_recent_inbound_sms(
service_id, page=page
)
inbound_messages = inbound_messages_data["data"]
if not inbound_messages:
inbound_number = current_service.inbound_number
else:
inbound_number = None
prev_page = None
if page > 1:
prev_page = generate_previous_dict("main.inbox", service_id, page)
next_page = None
if inbound_messages_data["has_next"]:
next_page = generate_next_dict("main.inbox", service_id, page)
return {
"messages": render_template(
"views/dashboard/_inbox_messages.html",
messages=inbound_messages,
inbound_number=inbound_number,
prev_page=prev_page,
next_page=next_page,
)
}
def filter_out_cancelled_stats(template_statistics):
return [s for s in template_statistics if s["status"] != "cancelled"]
@@ -379,71 +257,6 @@ def aggregate_template_usage(template_statistics, sort_key="count"):
return sorted(templates, key=lambda x: x[sort_key], reverse=True)
def aggregate_notifications_stats(template_statistics):
template_statistics = filter_out_cancelled_stats(template_statistics)
notifications = {
template_type: {status: 0 for status in ("requested", "delivered", "failed")}
for template_type in ["sms", "email"]
}
for stat in template_statistics:
notifications[stat["template_type"]]["requested"] += stat["count"]
if stat["status"] in DELIVERED_STATUSES:
notifications[stat["template_type"]]["delivered"] += stat["count"]
elif stat["status"] in FAILURE_STATUSES:
notifications[stat["template_type"]]["failed"] += stat["count"]
return notifications
def get_dashboard_partials(service_id):
all_statistics = template_statistics_client.get_template_statistics_for_service(
service_id, limit_days=7
)
template_statistics = aggregate_template_usage(all_statistics)
stats = aggregate_notifications_stats(all_statistics)
dashboard_totals = (get_dashboard_totals(stats),)
free_sms_allowance = billing_api_client.get_free_sms_fragment_limit_for_year(
current_service.id,
)
# These 2 calls will update the dashboard sms allowance count while in trial mode.
billing_api_client.get_monthly_usage_for_service(
service_id, get_current_financial_year()
)
billing_api_client.create_or_update_free_sms_fragment_limit(
service_id, free_sms_fragment_limit=free_sms_allowance
)
yearly_usage = billing_api_client.get_annual_usage_for_service(
service_id,
get_current_financial_year(),
)
return {
"upcoming": render_template(
"views/dashboard/_upcoming.html",
),
"inbox": render_template(
"views/dashboard/_inbox.html",
),
"totals": render_template(
"views/dashboard/_totals.html",
service_id=service_id,
statistics=dashboard_totals[0],
),
"template-statistics": render_template(
"views/dashboard/template-statistics.html",
template_statistics=template_statistics,
most_used_template_count=max(
[row["count"] for row in template_statistics] or [0]
),
),
"usage": render_template(
"views/dashboard/_usage.html",
**get_annual_usage_breakdown(yearly_usage, free_sms_allowance),
),
}
def get_dashboard_totals(statistics):
for msg_type in statistics.values():

View File

@@ -58,8 +58,6 @@ class HeaderNavigation(Navigation):
},
"accounts-or-dashboard": {
"conversation",
"inbox",
"monthly",
"service_dashboard",
"template_usage",
"view_notification",
@@ -161,8 +159,6 @@ class MainNavigation(Navigation):
},
"dashboard": {
"conversation",
"inbox",
"monthly",
"service_dashboard",
"template_usage",
"view_notification",

View File

@@ -8,7 +8,7 @@
{% endblock %}
{% block backLink %}
{{ usaBackLink({ "href": url_for("main.inbox", service_id=current_service.id) }) }}
{{ usaBackLink({ "href": url_for("main.service_dashboard", service_id=current_service.id) }) }}
{% endblock %}
{% block maincolumn_content %}

View File

@@ -1,17 +0,0 @@
<div class="ajax-block">
{% if current_service.inbound_sms_summary != None %}
<a id="total-received" class="usa-link banner-dashboard" class="banner-dashboard" href="{{ url_for('.inbox', service_id=current_service.id) }}">
<span class="banner-dashboard-count">
{{ current_service.inbound_sms_summary.count|format_thousands }}
</span>
<span class="banner-dashboard-count-label">
{{ current_service.inbound_sms_summary.count|message_count_label('sms', suffix='received') }}
</span>
{% if current_service.inbound_sms_summary.most_recent %}
<span class="banner-dashboard-meta">
latest message {{ current_service.inbound_sms_summary.most_recent | format_delta }}
</span>
{% endif %}
</a>
{% endif %}
</div>

View File

@@ -1,38 +0,0 @@
{% from "components/table.html" import list_table, field, hidden_field_heading, right_aligned_field_heading, row_heading %}
{% from "components/previous-next-navigation.html" import previous_next_navigation %}
<div class="ajax-block-container">
{% if messages %}
<p class="bottom-gutter-2-3 top-gutter-1-2">
<a href="{{ url_for('.inbox_download', service_id=current_service.id) }}" download class="usa-link bold">Download these messages</a>
</p>
{% endif %}
{% call(item, row_number) list_table(
messages,
caption="Inbox",
caption_visible=False,
empty_message='When users text your services phone number ({}) youll see the messages here'.format(inbound_number),
field_headings=[
'From',
'First two lines of message'
],
field_headings_visible=False
) %}
{% call field() %}
<a
class="usa-link file-list-filename"
href="{{ url_for('.conversation', service_id=current_service.id, notification_id=item.id) }}#n{{ item.id }}"
>
{{ item.user_number | format_phone_number_human_readable }}
</a>
<span class="file-list-hint">{{ item.content }}</span>
{% endcall %}
{% call field(align='right') %}
<span class="align-with-message-body">
{{ item.created_at | format_delta }}
</span>
{% endcall %}
{% endcall %}
{{ previous_next_navigation(prev_page, next_page) }}
</div>

View File

@@ -1,25 +0,0 @@
<div class="ajax-block-container">
<div class="grid-row grid-gap">
<div id="total-sms" class="grid-col-12 margin-top-2">
<span class="big-number-with-status display-block margin-bottom-2">
<p>
<span class="big-number-smaller">
<span class="big-number-number">
{% if statistics['sms']['requested'] is number %}
{{ "{:,}".format(statistics['sms']['requested']) }}
{% else %}
{{ statistics['sms']['requested'] }}
{% endif %}
</span>
<span class="big-number-label">{{ statistics['sms']['requested']|message_count_label('sms', suffix='sent') }} in the last seven days</span>
</span>
</p>
<a class="usa-button usa-button--outline" href="{{ url_for('.view_notifications', service_id=service_id, message_type='sms', status='sending,delivered,failed') }}">
Details
</a>
{# Removing the failures area for now, as the user can click on the above link to see all the details.
In the future state of the dashboard, the all statuses will be more apparent with data visualizations #}
</span>
</div>
</div>
</div>

View File

@@ -1,12 +1,12 @@
{% extends "withnav_template.html" %}
{% from "components/table.html" import list_table, field, text_field, link_field, right_aligned_field_heading, hidden_field_heading, row_heading, notification_status_field, notification_carrier_field, notification_carrier_message_field %}
{% from "components/table.html" import list_table, field, text_field, link_field, right_aligned_field_heading,
hidden_field_heading, row_heading, notification_status_field, notification_carrier_field,
notification_carrier_message_field %}
{% from "components/ajax-block.html" import ajax_block %}
{% block service_page_title %}
Dashboard
Dashboard
{% endblock %}
{% block maincolumn_content %}
@@ -15,40 +15,38 @@
<div class="dashboard margin-top-0 margin-bottom-2">
<h1 class="usa-sr-only">Dashboard</h1>
{% if current_user.has_permissions('manage_templates') and not current_service.all_templates %}
<h1 class="usa-sr-only">Dashboard</h1>
{% if current_user.has_permissions('manage_templates') and not current_service.all_templates %}
{% include 'views/dashboard/write-first-messages.html' %}
{% endif %}
{% endif %}
{{ ajax_block(partials, updates_url, 'upcoming') }}
{% include 'views/dashboard/_upcoming.html' %}
<h2 class="font-body-2xl line-height-sans-2 margin-top-0">{{ current_service.name }} Dashboard</h2>
<h2 class="font-body-2xl line-height-sans-2 margin-top-0">{{ current_service.name }} Dashboard</h2>
{{ ajax_block(partials, updates_url, 'inbox') }}
<div id="totalMessageChartContainer" data-messages-sent="{{ messages_sent }}" data-messages-remaining="{{ messages_remaining }}">
<div class="grid-row flex-align-center">
<h2 id="chartTitle" class="margin-right-1">Total messages</h2>
<button
type="button"
class="usa-tooltip usa-tooltip__information margin-right-0"
data-position="top"
title="Total messages track the sum of messages for the service: pending, failed, or delivered"
>
<span class="usa-sr-only">More information</span>
i
</button>
</div>
<svg id="totalMessageChart"></svg>
<div id="message"></div>
</div>
<div id="totalMessageTable" class="margin-0"></div>
{% include 'views/dashboard/activity-table.html' %}
<div id="totalMessageChartContainer" data-messages-sent="{{ messages_sent }}" data-messages-remaining="{{ messages_remaining }}">
<div class="grid-row flex-align-center">
<h2 id="chartTitle" class="margin-right-1">Total messages</h2>
<button
type="button"
class="usa-tooltip usa-tooltip__information margin-right-0"
data-position="top"
title="Total messages track the sum of messages for the service: pending, failed, or delivered"
>
<span class="usa-sr-only">More information</span>
i
</button>
</div>
<svg id="totalMessageChart"></svg>
<div id="message"></div>
</div>
<div id="totalMessageTable" class="margin-0"></div>
{% if current_user.has_permissions('manage_service') %}{% endif %}
{% include 'views/dashboard/activity-table.html' %}
{% include 'views/dashboard/most-used-templates.html' %}
{% if current_user.has_permissions('manage_service') %}{% endif %}
{{ ajax_block(partials, updates_url, 'template-statistics') }}
</div>
{% endblock %}

View File

@@ -1,25 +0,0 @@
{% extends "withnav_template.html" %}
{% from "components/ajax-block.html" import ajax_block %}
{% from "components/page-header.html" import page_header %}
{% from "components/components/back-link/macro.njk" import usaBackLink %}
{% block service_page_title %}
Received text messages
{% endblock %}
{% block backLink %}
{{ usaBackLink({ "href": url_for('main.service_dashboard', service_id=current_service.id) }) }}
{% endblock %}
{% block maincolumn_content %}
{{ page_header('Received text messages') }}
{{ ajax_block(
partials,
updates_url,
'messages',
) }}
{% endblock %}

View File

@@ -34,12 +34,13 @@
<td><p>{{ item.template_folder }}</p></td>
<td><p>{{ item.last_used|format_datetime_table}}</p></td>
<td><p>{{ item.created_by }}</p></td>
<td><p>{{ item.count }}</p></td>
<td><p>{{ '{:,.0f}'.format(item.count) }}</p></td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="/services/78409625-0c0a-485e-b82c-b19c8f4b1bdb/template-usage" class="usa-link show-more-no-border"><span>See templates by month</span></a>
<a
href="{{ url_for('.template_usage', service_id=current_service.id) }}" class="usa-link show-more-no-border"><span>See templates by month</span></a>
</div>
{% endif %}
</div>

View File

@@ -1,5 +1,4 @@
import copy
import json
from datetime import datetime
from unittest.mock import patch
from zoneinfo import ZoneInfo
@@ -9,7 +8,6 @@ from flask import url_for
from freezegun import freeze_time
from app.main.views.dashboard import (
aggregate_notifications_stats,
aggregate_status_types,
aggregate_template_usage,
format_monthly_stats_to_list,
@@ -17,12 +15,7 @@ from app.main.views.dashboard import (
get_local_daily_stats_for_last_x_days,
get_tuples_of_financial_years,
)
from tests import (
organization_json,
service_json,
validate_route_permission,
validate_route_permission_with_client,
)
from tests import organization_json, service_json, validate_route_permission
from tests.conftest import (
ORGANISATION_ID,
SERVICE_ONE_ID,
@@ -330,287 +323,6 @@ def test_inbound_messages_not_visible_to_service_without_permissions(
assert mock_get_inbound_sms_summary.called is False
def test_inbound_messages_shows_count_of_messages_when_there_are_messages(
client_request,
mocker,
service_one,
mock_get_service_templates_when_no_templates_exist,
mock_get_jobs,
mock_get_scheduled_job_stats,
mock_get_service_statistics,
mock_get_template_statistics,
mock_get_annual_usage_for_service,
mock_get_free_sms_fragment_limit,
mock_get_inbound_sms_summary,
):
service_one["permissions"] = ["inbound_sms"]
mocker.patch(
"app.service_api_client.get_service_message_ratio",
return_value=mock_service_message_ratio,
)
page = client_request.get(
"main.service_dashboard",
service_id=SERVICE_ONE_ID,
)
mock_get_jobs.assert_called_with(SERVICE_ONE_ID)
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
banner = page.select("a.banner-dashboard")[1]
assert (
normalize_spaces(banner.text)
== "9,999 text messages received latest message just now"
)
assert banner["href"] == url_for("main.inbox", service_id=SERVICE_ONE_ID)
def test_inbound_messages_shows_count_of_messages_when_there_are_no_messages(
client_request,
mocker,
service_one,
mock_get_service_templates_when_no_templates_exist,
mock_get_jobs,
mock_get_scheduled_job_stats,
mock_get_service_statistics,
mock_get_template_statistics,
mock_get_annual_usage_for_service,
mock_get_free_sms_fragment_limit,
mock_get_inbound_sms_summary_with_no_messages,
):
service_one["permissions"] = ["inbound_sms"]
mocker.patch(
"app.service_api_client.get_service_message_ratio",
return_value=mock_service_message_ratio,
)
page = client_request.get(
"main.service_dashboard",
service_id=SERVICE_ONE_ID,
)
mock_get_jobs.assert_called_with(SERVICE_ONE_ID)
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
banner = page.select("a.banner-dashboard")[1]
assert normalize_spaces(banner.text) == "0 text messages received"
assert banner["href"] == url_for("main.inbox", service_id=SERVICE_ONE_ID)
@pytest.mark.parametrize(
("index", "expected_row"),
enumerate(
[
"(202) 867-5300 message-1 1 hour ago",
"(202) 867-5300 message-2 1 hour ago",
"(202) 867-5300 message-3 1 hour ago",
"(202) 867-5302 message-4 3 hours ago",
"+33(0)1 12345678 message-5 5 hours ago",
"(202) 555-0104 message-6 7 hours ago",
"(202) 555-0104 message-7 9 hours ago",
]
),
)
def test_inbox_showing_inbound_messages(
client_request,
service_one,
mock_get_service_templates_when_no_templates_exist,
mock_get_service_statistics,
mock_get_template_statistics,
mock_get_annual_usage_for_service,
mock_get_most_recent_inbound_sms,
index,
expected_row,
):
service_one["permissions"] = ["inbound_sms"]
page = client_request.get(
"main.inbox",
service_id=SERVICE_ONE_ID,
)
rows = page.select("tbody tr")
assert len(rows) == 8
assert normalize_spaces(rows[index].text) == expected_row
assert page.select_one("a[download]")["href"] == url_for(
"main.inbox_download",
service_id=SERVICE_ONE_ID,
)
def test_get_inbound_sms_shows_page_links(
client_request,
service_one,
mock_get_service_templates_when_no_templates_exist,
mock_get_service_statistics,
mock_get_template_statistics,
mock_get_annual_usage_for_service,
mock_get_most_recent_inbound_sms,
mock_get_inbound_number_for_service,
):
service_one["permissions"] = ["inbound_sms"]
page = client_request.get(
"main.inbox",
service_id=SERVICE_ONE_ID,
page=2,
)
assert "Next page" in page.find("li", {"class": "next-page"}).text
assert "Previous page" in page.find("li", {"class": "previous-page"}).text
def test_empty_inbox(
client_request,
service_one,
mock_get_service_templates_when_no_templates_exist,
mock_get_service_statistics,
mock_get_template_statistics,
mock_get_annual_usage_for_service,
mock_get_most_recent_inbound_sms_with_no_messages,
mock_get_inbound_number_for_service,
):
service_one["permissions"] = ["inbound_sms"]
page = client_request.get(
"main.inbox",
service_id=SERVICE_ONE_ID,
)
assert normalize_spaces(page.select("tbody tr")) == (
"When users text your services phone number (2028675301) youll see the messages here"
)
assert not page.select("a[download]")
assert not page.select("li.next-page")
assert not page.select("li.previous-page")
@pytest.mark.parametrize(
"endpoint",
[
"main.inbox",
"main.inbox_updates",
],
)
def test_inbox_not_accessible_to_service_without_permissions(
client_request,
service_one,
endpoint,
):
service_one["permissions"] = []
client_request.get(
endpoint,
service_id=SERVICE_ONE_ID,
_expected_status=403,
)
def test_anyone_can_see_inbox(
client_request,
api_user_active,
service_one,
mocker,
mock_get_most_recent_inbound_sms_with_no_messages,
mock_get_inbound_number_for_service,
):
service_one["permissions"] = ["inbound_sms"]
validate_route_permission_with_client(
mocker,
client_request,
"GET",
200,
url_for("main.inbox", service_id=service_one["id"]),
["view_activity"],
api_user_active,
service_one,
)
def test_view_inbox_updates(
client_request,
service_one,
mocker,
mock_get_most_recent_inbound_sms_with_no_messages,
):
service_one["permissions"] += ["inbound_sms"]
mock_get_partials = mocker.patch(
"app.main.views.dashboard.get_inbox_partials",
return_value={"messages": "foo"},
)
response = client_request.get_response(
"main.inbox_updates",
service_id=SERVICE_ONE_ID,
)
assert json.loads(response.get_data(as_text=True)) == {"messages": "foo"}
mock_get_partials.assert_called_once_with(SERVICE_ONE_ID)
@freeze_time("2016-07-01 16:00")
def test_download_inbox(
client_request,
mock_get_inbound_sms,
):
response = client_request.get_response(
"main.inbox_download",
service_id=SERVICE_ONE_ID,
)
assert response.headers["Content-Type"] == ("text/csv; " "charset=utf-8")
assert response.headers["Content-Disposition"] == (
"inline; " 'filename="Received text messages 07-01-2016.csv"'
)
assert response.get_data(as_text=True) == (
"Phone number,Message,Received\r\n"
"(202) 867-5300,message-1,07-01-2016 11:00 America/New_York\r\n"
"(202) 867-5300,message-2,07-01-2016 10:59 America/New_York\r\n"
"(202) 867-5300,message-3,07-01-2016 10:59 America/New_York\r\n"
"(202) 867-5302,message-4,07-01-2016 08:59 America/New_York\r\n"
"+33(0)1 12345678,message-5,07-01-2016 06:59 America/New_York\r\n"
"(202) 555-0104,message-6,07-01-2016 04:59 America/New_York\r\n"
"(202) 555-0104,message-7,07-01-2016 02:59 America/New_York\r\n"
"+68212345,message-8,07-01-2016 02:59 America/New_York\r\n"
)
@freeze_time("2016-07-01 13:00")
@pytest.mark.parametrize(
("message_content", "expected_cell"),
[
("=2+5", "2+5"),
("==2+5", "2+5"),
("-2+5", "2+5"),
("+2+5", "2+5"),
("@2+5", "2+5"),
("looks safe,=2+5", '"looks safe,=2+5"'),
],
)
def test_download_inbox_strips_formulae(
mocker,
client_request,
fake_uuid,
message_content,
expected_cell,
):
mocker.patch(
"app.service_api_client.get_inbound_sms",
return_value={
"has_next": False,
"data": [
{
"user_number": "elevenchars",
"notify_number": "foo",
"content": message_content,
"created_at": datetime.utcnow().isoformat(),
"id": fake_uuid,
}
],
},
)
response = client_request.get_response(
"main.inbox_download",
service_id=SERVICE_ONE_ID,
)
assert expected_cell in response.get_data(as_text=True).split("\r\n")[1]
def test_should_show_recent_templates_on_dashboard(
client_request,
mocker,
@@ -697,26 +409,6 @@ def test_should_not_show_recent_templates_on_dashboard_if_only_one_template_used
assert expected_count == 50, f"Expected count to be 50, but got {expected_count}"
@freeze_time("2016-07-01 12:00") # 4 months into 2016 financial year
@pytest.mark.parametrize(
"extra_args",
[
{},
{"year": "2016"},
],
)
def test_should_show_redirect_from_template_history(
client_request,
extra_args,
):
client_request.get(
"main.template_history",
service_id=SERVICE_ONE_ID,
_expected_status=301,
**extra_args,
)
@freeze_time("2017-01-01 12:00")
@pytest.mark.parametrize(
"extra_args",
@@ -761,29 +453,9 @@ def test_should_show_monthly_breakdown_of_template_usage(
)
def test_anyone_can_see_monthly_breakdown(
client_request,
api_user_active,
service_one,
mocker,
mock_get_monthly_notification_stats,
):
validate_route_permission_with_client(
mocker,
client_request,
"GET",
200,
url_for("main.monthly", service_id=service_one["id"]),
["view_activity"],
api_user_active,
service_one,
)
@pytest.mark.parametrize(
"endpoint",
[
"main.monthly",
"main.template_usage",
],
)
@@ -806,16 +478,6 @@ def test_stats_pages_show_last_3_years(
)
def test_monthly_has_equal_length_tables(
client_request,
service_one,
mock_get_monthly_notification_stats,
):
page = client_request.get("main.monthly", service_id=service_one["id"])
assert page.select_one(".table-field-headings th").get("width") == "33%"
@freeze_time("2016-01-01 11:09:00.061258")
def test_should_show_upcoming_jobs_on_dashboard(
mocker,
@@ -1463,14 +1125,6 @@ def test_aggregate_template_stats():
assert expected[1]["template_type"] == "sms"
def test_aggregate_notifications_stats():
expected = aggregate_notifications_stats(copy.deepcopy(stub_template_stats))
assert expected == {
"sms": {"requested": 100, "delivered": 50, "failed": 0},
"email": {"requested": 200, "delivered": 0, "failed": 100},
}
def test_get_dashboard_totals_adds_percentages():
stats = {
"sms": {"requested": 3, "delivered": 0, "failed": 2},

View File

@@ -116,9 +116,6 @@ EXCLUDED_ENDPOINTS = tuple(
"history",
"how_to_pay",
"inbound_sms_admin",
"inbox",
"inbox_download",
"inbox_updates",
"index",
"information_risk_management",
"information_security",
@@ -134,7 +131,6 @@ EXCLUDED_ENDPOINTS = tuple(
"manage_template_folder",
"manage_users",
"message_status",
"monthly",
"multiple_languages",
"new_password",
"notifications_sent_by_service",
@@ -183,7 +179,6 @@ EXCLUDED_ENDPOINTS = tuple(
"service_confirm_delete_email_reply_to",
"service_confirm_delete_sms_sender",
"service_dashboard",
"service_dashboard_updates",
"service_delete_email_reply_to",
"service_delete_sms_sender",
"service_edit_email_reply_to",
@@ -217,7 +212,6 @@ EXCLUDED_ENDPOINTS = tuple(
"studio",
"support",
"suspend_service",
"template_history",
"template_usage",
"test_feature_flags",
"tour_step",