From a013f30600d7e3056d7cff8435d7d90a78f543b8 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 8 Oct 2025 16:37:42 -0700 Subject: [PATCH 1/4] remove view_notifications unsed code --- app/main/forms.py | 28 +- app/main/views/jobs.py | 499 +++++++++++++------------ app/navigation.py | 6 +- app/templates/components/main_nav.html | 6 +- app/templates/views/notifications.html | 92 ----- tests/app/test_navigation.py | 1 - 6 files changed, 271 insertions(+), 361 deletions(-) delete mode 100644 app/templates/views/notifications.html diff --git a/app/main/forms.py b/app/main/forms.py index d952f2cfea..b78d418a07 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -1564,20 +1564,20 @@ class SearchUsersForm(StripWhitespaceForm): search = UsaSearchField("Search by name or email address") -class SearchNotificationsForm(StripWhitespaceForm): - to = UsaSearchField() - - labels = { - "email": "Search by email address", - "sms": "Search by phone number", - } - - def __init__(self, message_type, *args, **kwargs): - super().__init__(*args, **kwargs) - self.to.label.text = self.labels.get( - message_type, - "Search by phone number or email address", - ) +# class SearchNotificationsForm(StripWhitespaceForm): +# to = UsaSearchField() +# +# labels = { +# "email": "Search by email address", +# "sms": "Search by phone number", +# } +# +# def __init__(self, message_type, *args, **kwargs): +# super().__init__(*args, **kwargs) +# self.to.label.text = self.labels.get( +# message_type, +# "Search by phone number or email address", +# ) class SearchTemplatesForm(StripWhitespaceForm): diff --git a/app/main/views/jobs.py b/app/main/views/jobs.py index 367dfbb364..59d83d45ae 100644 --- a/app/main/views/jobs.py +++ b/app/main/views/jobs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from functools import partial +# from functools import partial from flask import ( Response, @@ -12,27 +12,30 @@ stream_with_context, url_for, ) -from flask_login import current_user + +# from flask_login import current_user from markupsafe import Markup -from app import ( +from app import ( # notification_api_client,; service_api_client, current_service, format_datetime_table, - notification_api_client, - service_api_client, ) -from app.enums import NotificationStatus, NotificationType, ServicePermission + +# from app.enums import NotificationStatus, NotificationType, ServicePermission +from app.enums import ServicePermission from app.formatters import get_time_left, message_count_noun from app.main import main -from app.main.forms import SearchNotificationsForm + +# from app.main.forms import SearchNotificationsForm from app.models.job import Job from app.utils import parse_filter_args, set_status_filters from app.utils.csv import generate_notifications_csv -from app.utils.pagination import ( - generate_next_dict, - generate_previous_dict, - get_page_from_request, -) + +# from app.utils.pagination import ( +# generate_next_dict, +# generate_previous_dict, +# get_page_from_request, +# ) from app.utils.user import user_has_permissions from notifications_python_client.errors import HTTPError from notifications_utils.template import EmailPreviewTemplate, SMSBodyPreviewTemplate @@ -197,242 +200,242 @@ def view_job_notifications_table(service_id, job_id): ) -@main.route("/services//notifications", methods=["GET", "POST"]) -@main.route( - "/services//notifications/", - methods=["GET", "POST"], -) -@user_has_permissions() -def view_notifications(service_id, message_type=None): - return render_template( - "views/notifications.html", - partials=get_notifications(service_id, message_type), - message_type=message_type, - status=request.args.get("status") or "sending,delivered,failed", - page=request.args.get("page", 1), - search_form=SearchNotificationsForm( - message_type=message_type, - to=request.form.get("to"), - ), - things_you_can_search_by={ - "email": ["email address"], - "sms": ["phone number"], - None: ["recipient"], - }.get(message_type) - + { - True: ["reference"], - False: [], - }.get(bool(current_service.api_keys)), - download_link_one_day=url_for( - ".download_notifications_csv", - service_id=current_service.id, - message_type=message_type, - status=request.args.get("status"), - number_of_days="one_day", - ), - download_link_today=url_for( - ".download_notifications_csv", - service_id=current_service.id, - message_type=message_type, - status=request.args.get("status"), - number_of_days="today", - ), - download_link_three_day=url_for( - ".download_notifications_csv", - service_id=current_service.id, - message_type=message_type, - status=request.args.get("status"), - number_of_days="three_day", - ), - download_link_five_day=url_for( - ".download_notifications_csv", - service_id=current_service.id, - message_type=message_type, - status=request.args.get("status"), - number_of_days="five_day", - ), - download_link_seven_day=url_for( - ".download_notifications_csv", - service_id=current_service.id, - message_type=message_type, - status=request.args.get("status"), - number_of_days="seven_day", - ), - ) - - -@main.route("/services//notifications.json", methods=["GET", "POST"]) -@main.route( - "/services//notifications/.json", - methods=["GET", "POST"], -) -@user_has_permissions() -def get_notifications_as_json(service_id, message_type=None): - return jsonify( - get_notifications( - service_id, message_type, status_override=request.args.get("status") - ) - ) - - -@main.route( - "/services//notifications.csv", endpoint="view_notifications_csv" -) -@main.route( - "/services//notifications/.csv", - endpoint="view_notifications_csv", -) -@user_has_permissions() -def get_notifications(service_id, message_type, status_override=None): # noqa - # TODO get the api to return count of pages as well. - page = get_page_from_request() - if page is None: - abort(404, "Invalid page argument ({}).".format(request.args.get("page"))) - filter_args = parse_filter_args(request.args) - filter_args["status"] = set_status_filters(filter_args) - service_data_retention_days = None - search_term = request.form.get("to", "") - if message_type is not None: - service_data_retention_days = current_service.get_days_of_retention( - message_type, number_of_days="seven_day" - ) - - if request.path.endswith("csv") and current_user.has_permissions( - ServicePermission.VIEW_ACTIVITY - ): - return Response( - generate_notifications_csv( - service_id=service_id, - page=page, - page_size=5000, - template_type=[message_type], - status=filter_args.get("status"), - limit_days=service_data_retention_days, - ), - mimetype="text/csv", - headers={"Content-Disposition": 'inline; filename="notifications.csv"'}, - ) - notifications = notification_api_client.get_notifications_for_service( - service_id=service_id, - page=page, - template_type=[message_type] if message_type else [], - status=filter_args.get("status"), - limit_days=service_data_retention_days, - to=search_term, - ) - - notifications_list = notifications.get("notifications", []) - - url_args = {"message_type": message_type, "status": request.args.get("status")} - prev_page = None - if "links" in notifications and notifications["links"].get("prev", None): - prev_page = generate_previous_dict( - "main.view_notifications", service_id, page, url_args=url_args - ) - next_page = None - - total_items = notifications.get("total", 0) - page_size = notifications.get("page_size", 50) - total_pages = (total_items + page_size - 1) // page_size - if ( - "links" in notifications - and notifications["links"].get("next", None) - and total_items > 50 - and page < total_pages - ): - next_page = generate_next_dict( - "main.view_notifications", service_id, page, url_args - ) - - if message_type: - download_link = url_for( - ".view_notifications_csv", - service_id=current_service.id, - message_type=message_type, - status=request.args.get("status"), - ) - else: - download_link = None - return { - "service_data_retention_days": service_data_retention_days, - "counts": render_template( - "views/activity/counts.html", - status=request.args.get("status"), - status_filters=get_status_filters( - current_service, - message_type, - service_api_client.get_service_statistics( - service_id, limit_days=service_data_retention_days - ), - ), - ), - "notifications": render_template( - "views/activity/notifications.html", - notifications=list( - add_preview_of_content_to_notifications(notifications_list) - ), - page=page, - limit_days=service_data_retention_days, - prev_page=prev_page, - next_page=next_page, - show_pagination=(not search_term), - status=request.args.get("status"), - message_type=message_type, - download_link=download_link, - single_notification_url=partial( - url_for, - ".view_notification", - service_id=current_service.id, - ), - ), - } - - -def get_status_filters(service, message_type, statistics): - message_types = ( - [message_type] - if message_type - else [NotificationType.EMAIL, NotificationType.SMS] - ) - - stats = { - NotificationStatus.REQUESTED: sum( - statistics[mt].get(NotificationStatus.REQUESTED, 0) for mt in message_types - ), - NotificationStatus.DELIVERED: sum( - statistics[mt].get(NotificationStatus.DELIVERED, 0) for mt in message_types - ), - NotificationStatus.FAILED: sum( - statistics[mt].get(NotificationStatus.FAILED, 0) for mt in message_types - ), - } - - stats[NotificationStatus.PENDING] = ( - stats[NotificationStatus.REQUESTED] - - stats[NotificationStatus.DELIVERED] - - stats[NotificationStatus.FAILED] - ) - - filters = [ - (NotificationStatus.REQUESTED, "total", "sending,delivered,failed"), - (NotificationStatus.PENDING, "pending", "sending,pending"), - (NotificationStatus.DELIVERED, "delivered", "delivered"), - (NotificationStatus.FAILED, "failed", "failed"), - ] - return [ - ( - label, - option, - url_for( - ".view_notifications", - service_id=service.id, - message_type=message_type, - status=option, - ), - stats.get(key), - ) - for key, label, option in filters - ] +# @main.route("/services//notifications", methods=["GET", "POST"]) +# @main.route( +# "/services//notifications/", +# methods=["GET", "POST"], +# ) +# @user_has_permissions() +# def view_notifications(service_id, message_type=None): +# return render_template( +# "views/notifications.html", +# partials=get_notifications(service_id, message_type), +# message_type=message_type, +# status=request.args.get("status") or "sending,delivered,failed", +# page=request.args.get("page", 1), +# search_form=SearchNotificationsForm( +# message_type=message_type, +# to=request.form.get("to"), +# ), +# things_you_can_search_by={ +# "email": ["email address"], +# "sms": ["phone number"], +# None: ["recipient"], +# }.get(message_type) +# + { +# True: ["reference"], +# False: [], +# }.get(bool(current_service.api_keys)), +# download_link_one_day=url_for( +# ".download_notifications_csv", +# service_id=current_service.id, +# message_type=message_type, +# status=request.args.get("status"), +# number_of_days="one_day", +# ), +# download_link_today=url_for( +# ".download_notifications_csv", +# service_id=current_service.id, +# message_type=message_type, +# status=request.args.get("status"), +# number_of_days="today", +# ), +# download_link_three_day=url_for( +# ".download_notifications_csv", +# service_id=current_service.id, +# message_type=message_type, +# status=request.args.get("status"), +# number_of_days="three_day", +# ), +# download_link_five_day=url_for( +# ".download_notifications_csv", +# service_id=current_service.id, +# message_type=message_type, +# status=request.args.get("status"), +# number_of_days="five_day", +# ), +# download_link_seven_day=url_for( +# ".download_notifications_csv", +# service_id=current_service.id, +# message_type=message_type, +# status=request.args.get("status"), +# number_of_days="seven_day", +# ), +# ) + + +# @main.route("/services//notifications.json", methods=["GET", "POST"]) +# @main.route( +# "/services//notifications/.json", +# methods=["GET", "POST"], +# ) +# @user_has_permissions() +# def get_notifications_as_json(service_id, message_type=None): +# return jsonify( +# get_notifications( +# service_id, message_type, status_override=request.args.get("status") +# ) +# ) + + +# @main.route( +# "/services//notifications.csv", endpoint="view_notifications_csv" +# ) +# @main.route( +# "/services//notifications/.csv", +# endpoint="view_notifications_csv", +# ) +# @user_has_permissions() +# def get_notifications(service_id, message_type, status_override=None): # noqa +# # TODO get the api to return count of pages as well. +# page = get_page_from_request() +# if page is None: +# abort(404, "Invalid page argument ({}).".format(request.args.get("page"))) +# filter_args = parse_filter_args(request.args) +# filter_args["status"] = set_status_filters(filter_args) +# service_data_retention_days = None +# search_term = request.form.get("to", "") +# if message_type is not None: +# service_data_retention_days = current_service.get_days_of_retention( +# message_type, number_of_days="seven_day" +# ) +# +# if request.path.endswith("csv") and current_user.has_permissions( +# ServicePermission.VIEW_ACTIVITY +# ): +# return Response( +# generate_notifications_csv( +# service_id=service_id, +# page=page, +# page_size=5000, +# template_type=[message_type], +# status=filter_args.get("status"), +# limit_days=service_data_retention_days, +# ), +# mimetype="text/csv", +# headers={"Content-Disposition": 'inline; filename="notifications.csv"'}, +# ) +# notifications = notification_api_client.get_notifications_for_service( +# service_id=service_id, +# page=page, +# template_type=[message_type] if message_type else [], +# status=filter_args.get("status"), +# limit_days=service_data_retention_days, +# to=search_term, +# ) +# +# notifications_list = notifications.get("notifications", []) +# +# url_args = {"message_type": message_type, "status": request.args.get("status")} +# prev_page = None +# if "links" in notifications and notifications["links"].get("prev", None): +# prev_page = generate_previous_dict( +# "main.view_notifications", service_id, page, url_args=url_args +# ) +# next_page = None +# +# total_items = notifications.get("total", 0) +# page_size = notifications.get("page_size", 50) +# total_pages = (total_items + page_size - 1) // page_size +# if ( +# "links" in notifications +# and notifications["links"].get("next", None) +# and total_items > 50 +# and page < total_pages +# ): +# next_page = generate_next_dict( +# "main.view_notifications", service_id, page, url_args +# ) +# +# if message_type: +# download_link = url_for( +# ".view_notifications_csv", +# service_id=current_service.id, +# message_type=message_type, +# status=request.args.get("status"), +# ) +# else: +# download_link = None +# return { +# "service_data_retention_days": service_data_retention_days, +# "counts": render_template( +# "views/activity/counts.html", +# status=request.args.get("status"), +# status_filters=get_status_filters( +# current_service, +# message_type, +# service_api_client.get_service_statistics( +# service_id, limit_days=service_data_retention_days +# ), +# ), +# ), +# "notifications": render_template( +# "views/activity/notifications.html", +# notifications=list( +# add_preview_of_content_to_notifications(notifications_list) +# ), +# page=page, +# limit_days=service_data_retention_days, +# prev_page=prev_page, +# next_page=next_page, +# show_pagination=(not search_term), +# status=request.args.get("status"), +# message_type=message_type, +# download_link=download_link, +# single_notification_url=partial( +# url_for, +# ".view_notification", +# service_id=current_service.id, +# ), +# ), +# } + + +# def get_status_filters(service, message_type, statistics): +# message_types = ( +# [message_type] +# if message_type +# else [NotificationType.EMAIL, NotificationType.SMS] +# ) +# +# stats = { +# NotificationStatus.REQUESTED: sum( +# statistics[mt].get(NotificationStatus.REQUESTED, 0) for mt in message_types +# ), +# NotificationStatus.DELIVERED: sum( +# statistics[mt].get(NotificationStatus.DELIVERED, 0) for mt in message_types +# ), +# NotificationStatus.FAILED: sum( +# statistics[mt].get(NotificationStatus.FAILED, 0) for mt in message_types +# ), +# } +# +# stats[NotificationStatus.PENDING] = ( +# stats[NotificationStatus.REQUESTED] +# - stats[NotificationStatus.DELIVERED] +# - stats[NotificationStatus.FAILED] +# ) +# +# filters = [ +# (NotificationStatus.REQUESTED, "total", "sending,delivered,failed"), +# (NotificationStatus.PENDING, "pending", "sending,pending"), +# (NotificationStatus.DELIVERED, "delivered", "delivered"), +# (NotificationStatus.FAILED, "failed", "failed"), +# ] +# return [ +# ( +# label, +# option, +# url_for( +# ".view_notifications", +# service_id=service.id, +# message_type=message_type, +# status=option, +# ), +# stats.get(key), +# ) +# for key, label, option in filters +# ] def _get_job_counts(job): diff --git a/app/navigation.py b/app/navigation.py index a539671c86..1a016f0dcf 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -63,7 +63,7 @@ class HeaderNavigation(Navigation): "service_dashboard", "template_usage", "view_notification", - "view_notifications", + # "view_notifications", "action_blocked", "add_service_template", "check_messages", @@ -160,7 +160,7 @@ class MainNavigation(Navigation): "service_dashboard", "template_usage", "view_notification", - "view_notifications", + # "view_notifications", }, "templates": { "action_blocked", @@ -268,7 +268,7 @@ class CaseworkNavigation(Navigation): "send_one_off_to_myself", }, "sent-messages": { - "view_notifications", + # "view_notifications", "view_notification", }, "uploads": { diff --git a/app/templates/components/main_nav.html b/app/templates/components/main_nav.html index f110a94551..51797fe7f6 100644 --- a/app/templates/components/main_nav.html +++ b/app/templates/components/main_nav.html @@ -10,9 +10,9 @@
  • Dashboard
  • Activity
  • {% endif %} - {% if not current_user.has_permissions(ServicePermission.VIEW_ACTIVITY) %} -
  • Sent messages
  • - {% endif %} + {# {% if not current_user.has_permissions(ServicePermission.VIEW_ACTIVITY) %} #} + {#
  • Sent messages
  • #} + {# {% endif %} #} {% elif current_user.has_permissions(allow_org_user=True) %}
  • Usage
  • Team members
  • diff --git a/app/templates/views/notifications.html b/app/templates/views/notifications.html deleted file mode 100644 index 594fa412db..0000000000 --- a/app/templates/views/notifications.html +++ /dev/null @@ -1,92 +0,0 @@ -{% extends "withnav_template.html" %} -{% from "components/ajax-block.html" import ajax_block %} -{% from "components/page-header.html" import page_header %} -{% from "components/page-footer.html" import page_footer %} -{% from "components/form.html" import form_wrapper %} -{% from "components/components/button/macro.njk" import usaButton %} - -{% set page_title = ( - (99|message_count_label(message_type, suffix='')) | capitalize - if current_user.has_permissions(ServicePermission.VIEW_ACTIVITY) - else 'Sent messages' -) %} - -{% block service_page_title %} - {{ page_title }} -{% endblock %} - -{% block maincolumn_content %} - - {{ page_header(page_title) }} - {{ ajax_block( - partials, - url_for('.get_notifications_as_json', service_id=current_service.id, message_type=message_type, status=status), - 'counts' - ) }} - - -

    - Messages will remain in pending state until carrier status is received, typically 5 minutes. -

    - - {% call form_wrapper( - action=url_for('.view_notifications', service_id=current_service.id, message_type=message_type), - class="usa-search margin-bottom-2" - ) %} -
    - {{ search_form.to(param_extensions={ - "label": { - "text": things_you_can_search_by|formatted_list( - conjunction='or', - before_each='', - after_each='', - prefix='Search by', - prefix_plural='Search by' - ) - } - }) }} -
    - - - {% endcall %} - - - - - - {% call form_wrapper(id="search-form") %} - - - {% endcall %} - - {% if current_user.has_permissions(ServicePermission.VIEW_ACTIVITY) %} -

    - Download all data last 7 days (CSV) -   - Data available for {{ partials.service_data_retention_days }} days -

    -

    - Download all data last 5 days (CSV) -   -

    -

    - Download all data last 3 days (CSV) -   -

    -

    - Download all data today (CSV) -   -

    - {% endif %} - - {{ ajax_block( - partials, - url_for('.get_notifications_as_json', service_id=current_service.id, message_type=message_type, status=status, page=page), - 'notifications', - form='search-form' - ) }} - -{% endblock %} diff --git a/tests/app/test_navigation.py b/tests/app/test_navigation.py index e7c4646bb2..7f7d937006 100644 --- a/tests/app/test_navigation.py +++ b/tests/app/test_navigation.py @@ -104,7 +104,6 @@ "get_daily_stats_by_user", "get_volumes_by_service", "get_example_csv", - "get_notifications_as_json", "get_redis_report", "get_started", "get_started_old", From 9efc3b36b34d4a4795adf5d83391890d4b125ebd Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Thu, 9 Oct 2025 17:57:59 -0700 Subject: [PATCH 2/4] removing view_notifications from other templates --- app/main/views/jobs.py | 256 +------------------------------- app/main/views/notifications.py | 14 +- app/navigation.py | 3 - tests/app/test_navigation.py | 1 + 4 files changed, 7 insertions(+), 267 deletions(-) diff --git a/app/main/views/jobs.py b/app/main/views/jobs.py index 59d83d45ae..fe3fd74bac 100644 --- a/app/main/views/jobs.py +++ b/app/main/views/jobs.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# from functools import partial from flask import ( Response, @@ -12,30 +11,15 @@ stream_with_context, url_for, ) - -# from flask_login import current_user from markupsafe import Markup -from app import ( # notification_api_client,; service_api_client, - current_service, - format_datetime_table, -) - -# from app.enums import NotificationStatus, NotificationType, ServicePermission +from app import current_service, format_datetime_table from app.enums import ServicePermission from app.formatters import get_time_left, message_count_noun from app.main import main - -# from app.main.forms import SearchNotificationsForm from app.models.job import Job from app.utils import parse_filter_args, set_status_filters from app.utils.csv import generate_notifications_csv - -# from app.utils.pagination import ( -# generate_next_dict, -# generate_previous_dict, -# get_page_from_request, -# ) from app.utils.user import user_has_permissions from notifications_python_client.errors import HTTPError from notifications_utils.template import EmailPreviewTemplate, SMSBodyPreviewTemplate @@ -200,244 +184,6 @@ def view_job_notifications_table(service_id, job_id): ) -# @main.route("/services//notifications", methods=["GET", "POST"]) -# @main.route( -# "/services//notifications/", -# methods=["GET", "POST"], -# ) -# @user_has_permissions() -# def view_notifications(service_id, message_type=None): -# return render_template( -# "views/notifications.html", -# partials=get_notifications(service_id, message_type), -# message_type=message_type, -# status=request.args.get("status") or "sending,delivered,failed", -# page=request.args.get("page", 1), -# search_form=SearchNotificationsForm( -# message_type=message_type, -# to=request.form.get("to"), -# ), -# things_you_can_search_by={ -# "email": ["email address"], -# "sms": ["phone number"], -# None: ["recipient"], -# }.get(message_type) -# + { -# True: ["reference"], -# False: [], -# }.get(bool(current_service.api_keys)), -# download_link_one_day=url_for( -# ".download_notifications_csv", -# service_id=current_service.id, -# message_type=message_type, -# status=request.args.get("status"), -# number_of_days="one_day", -# ), -# download_link_today=url_for( -# ".download_notifications_csv", -# service_id=current_service.id, -# message_type=message_type, -# status=request.args.get("status"), -# number_of_days="today", -# ), -# download_link_three_day=url_for( -# ".download_notifications_csv", -# service_id=current_service.id, -# message_type=message_type, -# status=request.args.get("status"), -# number_of_days="three_day", -# ), -# download_link_five_day=url_for( -# ".download_notifications_csv", -# service_id=current_service.id, -# message_type=message_type, -# status=request.args.get("status"), -# number_of_days="five_day", -# ), -# download_link_seven_day=url_for( -# ".download_notifications_csv", -# service_id=current_service.id, -# message_type=message_type, -# status=request.args.get("status"), -# number_of_days="seven_day", -# ), -# ) - - -# @main.route("/services//notifications.json", methods=["GET", "POST"]) -# @main.route( -# "/services//notifications/.json", -# methods=["GET", "POST"], -# ) -# @user_has_permissions() -# def get_notifications_as_json(service_id, message_type=None): -# return jsonify( -# get_notifications( -# service_id, message_type, status_override=request.args.get("status") -# ) -# ) - - -# @main.route( -# "/services//notifications.csv", endpoint="view_notifications_csv" -# ) -# @main.route( -# "/services//notifications/.csv", -# endpoint="view_notifications_csv", -# ) -# @user_has_permissions() -# def get_notifications(service_id, message_type, status_override=None): # noqa -# # TODO get the api to return count of pages as well. -# page = get_page_from_request() -# if page is None: -# abort(404, "Invalid page argument ({}).".format(request.args.get("page"))) -# filter_args = parse_filter_args(request.args) -# filter_args["status"] = set_status_filters(filter_args) -# service_data_retention_days = None -# search_term = request.form.get("to", "") -# if message_type is not None: -# service_data_retention_days = current_service.get_days_of_retention( -# message_type, number_of_days="seven_day" -# ) -# -# if request.path.endswith("csv") and current_user.has_permissions( -# ServicePermission.VIEW_ACTIVITY -# ): -# return Response( -# generate_notifications_csv( -# service_id=service_id, -# page=page, -# page_size=5000, -# template_type=[message_type], -# status=filter_args.get("status"), -# limit_days=service_data_retention_days, -# ), -# mimetype="text/csv", -# headers={"Content-Disposition": 'inline; filename="notifications.csv"'}, -# ) -# notifications = notification_api_client.get_notifications_for_service( -# service_id=service_id, -# page=page, -# template_type=[message_type] if message_type else [], -# status=filter_args.get("status"), -# limit_days=service_data_retention_days, -# to=search_term, -# ) -# -# notifications_list = notifications.get("notifications", []) -# -# url_args = {"message_type": message_type, "status": request.args.get("status")} -# prev_page = None -# if "links" in notifications and notifications["links"].get("prev", None): -# prev_page = generate_previous_dict( -# "main.view_notifications", service_id, page, url_args=url_args -# ) -# next_page = None -# -# total_items = notifications.get("total", 0) -# page_size = notifications.get("page_size", 50) -# total_pages = (total_items + page_size - 1) // page_size -# if ( -# "links" in notifications -# and notifications["links"].get("next", None) -# and total_items > 50 -# and page < total_pages -# ): -# next_page = generate_next_dict( -# "main.view_notifications", service_id, page, url_args -# ) -# -# if message_type: -# download_link = url_for( -# ".view_notifications_csv", -# service_id=current_service.id, -# message_type=message_type, -# status=request.args.get("status"), -# ) -# else: -# download_link = None -# return { -# "service_data_retention_days": service_data_retention_days, -# "counts": render_template( -# "views/activity/counts.html", -# status=request.args.get("status"), -# status_filters=get_status_filters( -# current_service, -# message_type, -# service_api_client.get_service_statistics( -# service_id, limit_days=service_data_retention_days -# ), -# ), -# ), -# "notifications": render_template( -# "views/activity/notifications.html", -# notifications=list( -# add_preview_of_content_to_notifications(notifications_list) -# ), -# page=page, -# limit_days=service_data_retention_days, -# prev_page=prev_page, -# next_page=next_page, -# show_pagination=(not search_term), -# status=request.args.get("status"), -# message_type=message_type, -# download_link=download_link, -# single_notification_url=partial( -# url_for, -# ".view_notification", -# service_id=current_service.id, -# ), -# ), -# } - - -# def get_status_filters(service, message_type, statistics): -# message_types = ( -# [message_type] -# if message_type -# else [NotificationType.EMAIL, NotificationType.SMS] -# ) -# -# stats = { -# NotificationStatus.REQUESTED: sum( -# statistics[mt].get(NotificationStatus.REQUESTED, 0) for mt in message_types -# ), -# NotificationStatus.DELIVERED: sum( -# statistics[mt].get(NotificationStatus.DELIVERED, 0) for mt in message_types -# ), -# NotificationStatus.FAILED: sum( -# statistics[mt].get(NotificationStatus.FAILED, 0) for mt in message_types -# ), -# } -# -# stats[NotificationStatus.PENDING] = ( -# stats[NotificationStatus.REQUESTED] -# - stats[NotificationStatus.DELIVERED] -# - stats[NotificationStatus.FAILED] -# ) -# -# filters = [ -# (NotificationStatus.REQUESTED, "total", "sending,delivered,failed"), -# (NotificationStatus.PENDING, "pending", "sending,pending"), -# (NotificationStatus.DELIVERED, "delivered", "delivered"), -# (NotificationStatus.FAILED, "failed", "failed"), -# ] -# return [ -# ( -# label, -# option, -# url_for( -# ".view_notifications", -# service_id=service.id, -# message_type=message_type, -# status=option, -# ), -# stats.get(key), -# ) -# for key, label, option in filters -# ] - - def _get_job_counts(job): job_type = job.template_type return [ diff --git a/app/main/views/notifications.py b/app/main/views/notifications.py index bdae1e33c7..20d5dcc173 100644 --- a/app/main/views/notifications.py +++ b/app/main/views/notifications.py @@ -62,9 +62,9 @@ def view_notification(service_id, notification_id, error_message=None): job = None if get_help_argument() or request.args.get("help") == "0": - # help=0 is set when you’ve just sent a notification. We - # only want to show the back link when you’ve navigated to a - # notification, not when you’ve just sent it. + # help=0 is set when you've just sent a notification. We + # only want to show the back link when you've navigated to a + # notification, not when you've just sent it. back_link = None elif request.args.get("from_job"): back_link = url_for( @@ -74,10 +74,8 @@ def view_notification(service_id, notification_id, error_message=None): ) else: back_link = url_for( - "main.view_notifications", + "main.all_jobs_activity", service_id=current_service.id, - message_type=template.template_type, - status="sending,delivered,failed", ) return render_template( @@ -198,10 +196,8 @@ def download_notifications_csv(service_id): ) return redirect( url_for( - "main.view_notifications", + "main.all_jobs_activity", service_id=service_id, - message_type=filter_args["message_type"][0], - status="sending,delivered,failed", ) ) return Response( diff --git a/app/navigation.py b/app/navigation.py index 1a016f0dcf..798067f8a8 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -63,7 +63,6 @@ class HeaderNavigation(Navigation): "service_dashboard", "template_usage", "view_notification", - # "view_notifications", "action_blocked", "add_service_template", "check_messages", @@ -160,7 +159,6 @@ class MainNavigation(Navigation): "service_dashboard", "template_usage", "view_notification", - # "view_notifications", }, "templates": { "action_blocked", @@ -268,7 +266,6 @@ class CaseworkNavigation(Navigation): "send_one_off_to_myself", }, "sent-messages": { - # "view_notifications", "view_notification", }, "uploads": { diff --git a/tests/app/test_navigation.py b/tests/app/test_navigation.py index 7f7d937006..e7c4646bb2 100644 --- a/tests/app/test_navigation.py +++ b/tests/app/test_navigation.py @@ -104,6 +104,7 @@ "get_daily_stats_by_user", "get_volumes_by_service", "get_example_csv", + "get_notifications_as_json", "get_redis_report", "get_started", "get_started_old", From 0a1136e2b543676dafea7fea3d880c343fd4b880 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Fri, 10 Oct 2025 15:40:40 -0700 Subject: [PATCH 3/4] fixing testing --- tests/app/main/test_permissions.py | 10 +- tests/app/main/views/test_activity.py | 865 ------------------ .../test_download_notifications_csv_s3.py | 2 +- tests/app/main/views/test_jobs_activity.py | 116 +++ tests/app/main/views/test_notifications.py | 4 +- tests/app/test_navigation.py | 3 - tests/app/utils/test_pagination.py | 6 +- 7 files changed, 122 insertions(+), 884 deletions(-) delete mode 100644 tests/app/main/views/test_activity.py diff --git a/tests/app/main/test_permissions.py b/tests/app/main/test_permissions.py index bf1187ea98..35479e5fd9 100644 --- a/tests/app/main/test_permissions.py +++ b/tests/app/main/test_permissions.py @@ -126,18 +126,12 @@ def test_service_navigation_for_org_user( [ ( [], - ( - "Send messages", - "Sent messages", - ), + ("Send messages",), 403, ), ( [ORGANISATION_ID], - ( - "Send messages", - "Sent messages", - ), + ("Send messages",), 200, ), ], diff --git a/tests/app/main/views/test_activity.py b/tests/app/main/views/test_activity.py deleted file mode 100644 index d2e1e57b01..0000000000 --- a/tests/app/main/views/test_activity.py +++ /dev/null @@ -1,865 +0,0 @@ -import json -import uuid -from functools import partial -from urllib.parse import parse_qs, urlparse - -import pytest -from flask import url_for -from freezegun import freeze_time - -from app.main.views.jobs import get_status_filters -from app.models.service import Service -from tests import notification_json -from tests.conftest import ( - SERVICE_ONE_ID, - create_active_caseworking_user, - create_active_user_view_permissions, - create_active_user_with_permissions, - create_notifications, - normalize_spaces, -) - - -@pytest.mark.parametrize( - ( - "user", - "extra_args", - "expected_update_endpoint", - "expected_limit_days", - "page_title", - ), - [ - ( - create_active_user_view_permissions(), - {"message_type": "email"}, - "/email.json", - 7, - "Emails", - ), - ( - create_active_user_view_permissions(), - {"message_type": "sms"}, - "/sms.json", - 7, - "Text message", - ), - ( - create_active_caseworking_user(), - {}, - ".json", - None, - "Sent messages", - ), - ], -) -@pytest.mark.parametrize( - ("status_argument", "expected_api_call"), - [ - ( - "", - [ - "created", - "pending", - "sending", - "delivered", - "sent", - "failed", - "temporary-failure", - "permanent-failure", - "technical-failure", - "validation-failed", - ], - ), - ("sending", ["sending", "created", "pending"]), - ("delivered", ["delivered", "sent"]), - ( - "failed", - [ - "failed", - "temporary-failure", - "permanent-failure", - "technical-failure", - "validation-failed", - ], - ), - ], -) -@pytest.mark.parametrize( - ("page_argument", "expected_page_argument"), [(1, 1), (22, 22), (None, 1)] -) -@pytest.mark.parametrize( - ("to_argument", "expected_to_argument"), - [ - ("", ""), - ("+12029000123", "+12029000123"), - ("test@example.com", "test@example.com"), - ], -) -@freeze_time("2020-01-01 06:00") -def test_can_show_notifications( - client_request, - service_one, - mock_get_notifications, - mock_get_service_statistics, - mock_get_service_data_retention, - mock_has_no_jobs, - mock_get_no_api_keys, - user, - extra_args, - expected_update_endpoint, - expected_limit_days, - page_title, - status_argument, - expected_api_call, - page_argument, - expected_page_argument, - to_argument, - expected_to_argument, - mocker, -): - client_request.login(user) - if expected_to_argument: - page = client_request.post( - "main.view_notifications", - service_id=SERVICE_ONE_ID, - status=status_argument, - page=page_argument, - _data={"to": to_argument}, - _expected_status=200, - **extra_args - ) - else: - page = client_request.get( - "main.view_notifications", - service_id=SERVICE_ONE_ID, - status=status_argument, - page=page_argument, - **extra_args - ) - first_row = page.select_one("tbody tr") - assert normalize_spaces( - first_row.select_one("a.file-list-filename.usa-link").text - ) == ( - # Comes from - # https://github.com/alphagov/notifications-admin/blob/8faffad508f9a087b0006989c197741c693cc2e2/tests/__init__.py#L436 - "2021234567" - ) - assert normalize_spaces( - # We’re doing str() here not .text to make sure there’s no extra - # HTML sneaking in - str(first_row.select_one(".file-list-hint")) - ) == ( - # Comes from - # https://github.com/alphagov/notifications-admin/blob/8faffad508f9a087b0006989c197741c693cc2e2/tests/__init__.py#L271 - "template content" - ) or ( - # Comes from - # https://github.com/alphagov/notifications-admin/blob/8faffad508f9a087b0006989c197741c693cc2e2/tests/__init__.py#L273 - "template subject" - ) - - assert normalize_spaces( - first_row.select_one(".table-field-right-aligned .align-with-message-body").text - ) in [ - "Delivered 01-01-2020 at 01:00 AM", - "Delivered 01-01-2020 at 01:00 AM", - ] - - assert page_title in page.h1.text.strip() - - json_response = client_request.get_response( - "main.get_notifications_as_json", - service_id=SERVICE_ONE_ID, - status=status_argument, - page=expected_page_argument, - to=expected_to_argument, - **extra_args - ) - json_content = json.loads(json_response.get_data(as_text=True)) - assert json_content.keys() == { - "counts", - "notifications", - "service_data_retention_days", - } - - mock_get_notifications.assert_called() - - -def test_can_show_notifications_if_data_retention_not_available( - client_request, - mock_get_notifications, - mock_get_service_statistics, - mock_has_no_jobs, - mock_get_no_api_keys, -): - page = client_request.get( - "main.view_notifications", - service_id=SERVICE_ONE_ID, - status="sending,delivered,failed", - ) - assert page.h1.text.strip() == "Messages" - - -@pytest.mark.parametrize( - ("user", "query_parameters", "expected_download_link"), - [ - ( - create_active_user_with_permissions(), - {}, - partial( - url_for, - ".download_notifications_csv", - message_type=None, - number_of_days="seven_day", - ), - ), - ( - create_active_user_with_permissions(), - {"status": "failed"}, - partial( - url_for, - ".download_notifications_csv", - status="failed", - number_of_days="seven_day", - ), - ), - ( - create_active_user_with_permissions(), - {"message_type": "sms"}, - partial( - url_for, - ".download_notifications_csv", - message_type="sms", - number_of_days="seven_day", - ), - ), - ( - create_active_user_view_permissions(), - {}, - partial(url_for, ".download_notifications_csv", number_of_days="seven_day"), - ), - ( - create_active_caseworking_user(), - {}, - lambda service_id: None, - ), - ], -) -def test_link_to_download_notifications( - client_request, - mock_get_notifications, - mock_get_service_statistics, - mock_get_service_data_retention, - mock_has_no_jobs, - mock_get_no_api_keys, - user, - query_parameters, - expected_download_link, -): - client_request.login(user) - page = client_request.get( - "main.view_notifications", service_id=SERVICE_ONE_ID, **query_parameters - ) - download_link = page.select_one("a[download=download]") - assert (download_link["href"] if download_link else None) == expected_download_link( - service_id=SERVICE_ONE_ID - ) - - -def test_download_links_show_when_data_available( - client_request, - service_one, - active_user_with_permissions, - mocker, -): - - mock_jobs_with_data = { - "data": [{"id": "job1", "created_at": "2020-01-01T00:00:00.000000+00:00"}], - "total": 1, - "page_size": 50, - } - - mocker.patch( - "app.job_api_client.get_page_of_jobs", return_value=mock_jobs_with_data - ) - mocker.patch("app.job_api_client.get_immediate_jobs", return_value=[{"id": "job1"}]) - mocker.patch("app.s3_client.check_s3_file_exists", return_value=True) - mock_obj = mocker.Mock() - mock_obj.content_length = 1024 - mocker.patch("app.s3_client.get_s3_object", return_value=mock_obj) - mocker.patch("app.s3_client.s3_csv_client.get_csv_upload", return_value=mock_obj) - - page = client_request.get( - "main.all_jobs_activity", - service_id=service_one["id"], - ) - - assert "Download recent reports" in page.text - assert "Yesterday" in page.text - assert "Last 3 days" in page.text - assert "Last 5 days" in page.text - assert "Last 7 days" in page.text - - -def test_download_links_partial_data_available( - client_request, - service_one, - active_user_with_permissions, - mocker, -): - mock_jobs_with_data = { - "data": [{"id": "job1", "created_at": "2020-01-01T00:00:00.000000+00:00"}], - "total": 1, - "page_size": 50, - } - mock_jobs_empty = {"data": [], "total": 0, "page_size": 50} - - def mock_get_page_of_jobs(service_id, page=1, limit_days=None): - if limit_days in [1, 5]: - return mock_jobs_with_data - return mock_jobs_empty - - mocker.patch( - "app.job_api_client.get_page_of_jobs", side_effect=mock_get_page_of_jobs - ) - mocker.patch("app.job_api_client.get_immediate_jobs", return_value=[]) - mocker.patch("app.s3_client.check_s3_file_exists", return_value=True) - mock_obj = mocker.Mock() - mock_obj.content_length = 2048 - mocker.patch("app.s3_client.s3_csv_client.get_csv_upload", return_value=mock_obj) - mocker.patch("app.s3_client.get_s3_object", return_value=mock_obj) - - page = client_request.get( - "main.all_jobs_activity", - service_id=service_one["id"], - ) - - assert "Download recent reports" in page.text - assert "Yesterday" in page.text - assert "Last 3 days" in page.text - assert "Last 5 days" in page.text - assert "Last 7 days" in page.text - assert "No recent activity to download" not in page.text - - -def test_download_links_no_data_available( - client_request, - service_one, - active_user_with_permissions, - mocker, -): - mock_jobs_empty = {"data": [], "total": 0, "page_size": 50} - - mocker.patch("app.job_api_client.get_page_of_jobs", return_value=mock_jobs_empty) - mocker.patch("app.job_api_client.get_immediate_jobs", return_value=[]) - mocker.patch("app.s3_client.check_s3_file_exists", return_value=False) - mock_obj = mocker.Mock() - mock_obj.content_length = 0 - mocker.patch("app.s3_client.get_s3_object", return_value=mock_obj) - - page = client_request.get( - "main.all_jobs_activity", - service_id=service_one["id"], - ) - - assert "Download recent reports" in page.text - assert "Yesterday" in page.text - assert "No messages sent" in page.text - assert "Last 3 days - No messages sent" in page.text - assert "Last 5 days - No messages sent" in page.text - assert "Last 7 days - No messages sent" in page.text - - -def test_download_not_available_to_users_without_dashboard( - client_request, - active_caseworking_user, -): - client_request.login(active_caseworking_user) - client_request.get( - "main.download_notifications_csv", - service_id=SERVICE_ONE_ID, - _expected_status=403, - ) - - -def test_shows_message_when_no_notifications( - client_request, - mock_get_service_statistics, - mock_get_service_data_retention, - mock_get_notifications_with_no_notifications, - mock_get_no_api_keys, -): - page = client_request.get( - "main.view_notifications", - service_id=SERVICE_ONE_ID, - message_type="sms", - ) - - assert normalize_spaces(page.select("tbody tr")[0].text) == ( - "No messages found (messages are kept for 7 days)" - ) - - -@pytest.mark.parametrize( - ( - "initial_query_arguments", - "form_post_data", - "expected_search_box_label", - "expected_search_box_contents", - ), - [ - ( - {}, - {}, - "Search by recipient", - None, - ), - ( - { - "message_type": "sms", - }, - {}, - "Search by phone number", - None, - ), - ( - { - "message_type": "sms", - }, - { - "to": "+33(0)5-12-34-56-78", - }, - "Search by phone number", - "+33(0)5-12-34-56-78", - ), - ( - { - "status": "failed", - "message_type": "email", - "page": "99", - }, - { - "to": "test@example.com", - }, - "Search by email address", - "test@example.com", - ), - ], -) -def test_search_recipient_form( - client_request, - mock_get_notifications, - mock_get_service_statistics, - mock_get_service_data_retention, - mock_get_no_api_keys, - initial_query_arguments, - form_post_data, - expected_search_box_label, - expected_search_box_contents, -): - page = client_request.post( - "main.view_notifications", - service_id=SERVICE_ONE_ID, - _data=form_post_data, - _expected_status=200, - **initial_query_arguments - ) - - assert page.find("form")["method"] == "post" - action_url = page.find("form")["action"] - url = urlparse(action_url) - assert url.path == "/services/{}/notifications/{}".format( - SERVICE_ONE_ID, initial_query_arguments.get("message_type", "") - ).rstrip("/") - query_dict = parse_qs(url.query) - assert query_dict == {} - - assert ( - page.select_one("label:contains('Search by')").text.strip() - == expected_search_box_label - ) - - recipient_inputs = page.select("input[name=to]") - assert len(recipient_inputs) == 2 - - for field in recipient_inputs: - assert field.get("value") == expected_search_box_contents - - -@pytest.mark.parametrize( - ("message_type", "expected_search_box_label"), - [ - (None, "Search by recipient or reference"), - ("sms", "Search by phone number or reference"), - ("email", "Search by email address or reference"), - ], -) -def test_api_users_are_told_they_can_search_by_reference_when_service_has_api_keys( - client_request, - mocker, - fake_uuid, - mock_get_notifications, - mock_get_service_statistics, - mock_get_service_data_retention, - message_type, - expected_search_box_label, - mock_get_api_keys, -): - page = client_request.get( - "main.view_notifications", - service_id=SERVICE_ONE_ID, - message_type=message_type, - ) - assert ( - page.select_one("label:contains('Search by')").text.strip() - == expected_search_box_label - ) - - -@pytest.mark.parametrize( - ("message_type", "expected_search_box_label"), - [ - (None, "Search by recipient"), - ("sms", "Search by phone number"), - ("email", "Search by email address"), - ], -) -def test_api_users_are_not_told_they_can_search_by_reference_when_service_has_no_api_keys( - client_request, - mocker, - fake_uuid, - mock_get_notifications, - mock_get_service_statistics, - mock_get_service_data_retention, - message_type, - expected_search_box_label, - mock_get_no_api_keys, -): - page = client_request.get( - "main.view_notifications", - service_id=SERVICE_ONE_ID, - message_type=message_type, - ) - - assert ( - page.select_one("label:contains('Search by')").text.strip() - == expected_search_box_label - ) - - -def test_should_show_notifications_for_a_service_with_next_previous( - client_request, - service_one, - active_user_with_permissions, - mock_get_service_statistics, - mock_get_service_data_retention, - mock_get_no_api_keys, - mocker, -): - mocker.patch( - "app.notification_api_client.get_notifications_for_service", - return_value=notification_json(service_one["id"], rows=50, with_links=True) - | {"total": 150}, - ) - page = client_request.get( - "main.view_notifications", - service_id=service_one["id"], - message_type="sms", - page=2, - ) - - next_page_link = page.find("a", {"rel": "next"}) - prev_page_link = page.find("a", {"rel": "previous"}) - assert ( - url_for( - "main.view_notifications", - service_id=service_one["id"], - message_type="sms", - page=3, - ) - in next_page_link["href"] - ) - assert "Next page" in next_page_link.text.strip() - assert "page 3" in next_page_link.text.strip() - assert ( - url_for( - "main.view_notifications", - service_id=service_one["id"], - message_type="sms", - page=1, - ) - in prev_page_link["href"] - ) - assert "Previous page" in prev_page_link.text.strip() - assert "page 1" in prev_page_link.text.strip() - - -def test_doesnt_show_next_button_on_last_page( - client_request, - service_one, - active_user_with_permissions, - mock_get_service_statistics, - mock_get_service_data_retention, - mock_get_no_api_keys, - mocker, -): - mocker.patch( - "app.notification_api_client.get_notifications_for_service", - return_value=notification_json(service_one["id"], rows=50, with_links=True) - | {"total": 100}, - ) - page = client_request.get( - "main.view_notifications", - service_id=service_one["id"], - message_type="sms", - page=2, - ) - - next_page_link = page.find("a", {"rel": "next"}) - prev_page_link = page.find("a", {"rel": "previous"}) - - assert next_page_link is None - assert prev_page_link is not None - - -def test_doesnt_show_pagination_when_50_or_fewer_items( - client_request, - service_one, - active_user_with_permissions, - mock_get_service_statistics, - mock_get_service_data_retention, - mock_get_no_api_keys, - mocker, -): - mocker.patch( - "app.notification_api_client.get_notifications_for_service", - return_value=notification_json(service_one["id"], rows=50, with_links=False), - ) - page = client_request.get( - "main.view_notifications", - service_id=service_one["id"], - message_type="sms", - ) - - assert not page.find("a", {"rel": "next"}) - assert not page.find("a", {"rel": "previous"}) - assert not page.select_one(".table-show-more-link") - - -def test_doesnt_show_pagination_with_search_term( - client_request, - service_one, - active_user_with_permissions, - mock_get_service_statistics, - mock_get_service_data_retention, - mock_get_no_api_keys, - mocker, -): - mocker.patch( - "app.notification_api_client.get_notifications_for_service", - return_value=notification_json(service_one["id"], rows=50, with_links=True) - | {"total": 100}, - ) - page = client_request.post( - "main.view_notifications", - service_id=service_one["id"], - message_type="sms", - _data={ - "to": "test@example.com", - }, - _expected_status=200, - ) - assert len(page.select("tbody tr")) == 50 - assert not page.find("a", {"rel": "next"}) - assert not page.find("a", {"rel": "previous"}) - assert normalize_spaces(page.select_one(".table-show-more-link").text) == ( - "Only showing the first 50 messages" - ) - - -STATISTICS = {"sms": {"requested": 6, "failed": 2, "delivered": 1}} - - -def test_get_status_filters_calculates_stats(client_request): - ret = get_status_filters(Service({"id": "foo"}), "sms", STATISTICS) - - assert {label: count for label, _option, _link, count in ret} == { - "total": 6, - "pending": 3, - "failed": 2, - "delivered": 1, - } - - -def test_get_status_filters_in_right_order(client_request): - ret = get_status_filters(Service({"id": "foo"}), "sms", STATISTICS) - - assert [label for label, _option, _link, _count in ret] == [ - "total", - "pending", - "delivered", - "failed", - ] - - -def test_get_status_filters_constructs_links(client_request): - ret = get_status_filters(Service({"id": "foo"}), "sms", STATISTICS) - - link = ret[0][2] - assert link == "/services/foo/notifications/sms?status={}".format( - "sending,delivered,failed" - ) - - -def test_html_contains_notification_id( - client_request, - service_one, - active_user_with_permissions, - mock_get_notifications, - mock_get_service_statistics, - mock_get_service_data_retention, - mock_get_no_api_keys, - mocker, -): - page = client_request.get( - "main.view_notifications", - service_id=service_one["id"], - message_type="sms", - status="", - ) - - notifications = page.tbody.find_all("tr") - for tr in notifications: - assert uuid.UUID(tr.attrs["id"]) - - -def test_html_contains_links_for_failed_notifications( - client_request, - mock_get_service_statistics, - mock_get_service_data_retention, - mock_get_no_api_keys, - mocker, -): - notifications = create_notifications(status="technical-failure") - mocker.patch( - "app.notification_api_client.get_notifications_for_service", - return_value=notifications, - ) - - response = client_request.get( - "main.view_notifications", - service_id=SERVICE_ONE_ID, - message_type="sms", - status="sending%2Cdelivered%2Cfailed", - ) - notifications = response.tbody.find_all("tr") - for tr in notifications: - link_text = tr.find("div", class_="table-field-status-error").find("a").text - assert normalize_spaces(link_text) == "Technical failure" - - -@pytest.mark.parametrize( - ("notification_type", "expected_row_contents"), - [ - ("sms", ("2021234567 hello & welcome hidden")), - ("email", ("example@gsa.gov hidden, hello & welcome")), - ], -) -def test_redacts_templates_that_should_be_redacted( - client_request, - mocker, - mock_get_service_statistics, - mock_get_service_data_retention, - mock_get_no_api_keys, - notification_type, - expected_row_contents, -): - notifications = create_notifications( - status="technical-failure", - content="hello & welcome ((name))", - subject="((name)), hello & welcome", - personalisation={"name": "Jo"}, - redact_personalisation=True, - template_type=notification_type, - ) - mocker.patch( - "app.notification_api_client.get_notifications_for_service", - return_value=notifications, - ) - - page = client_request.get( - "main.view_notifications", - service_id=SERVICE_ONE_ID, - message_type=notification_type, - ) - - assert normalize_spaces(page.select("tbody tr td")[0].text) == ( - expected_row_contents - ) - - -@freeze_time("2017-09-27 12:30:00.000000") -@pytest.mark.parametrize( - ("message_type", "status", "expected_hint_status", "single_line"), - [ - ("email", "created", "Sending since 09-27-2017 at 08:30 AM", True), - ("email", "sending", "Sending since 09-27-2017 at 08:30 AM", True), - ( - "email", - "temporary-failure", - "Inbox not accepting messages right now 09-27-2017 at 08:30 AM", - False, - ), - ( - "email", - "permanent-failure", - "Email address does not exist 09-27-2017 at 08:30 AM", - False, - ), - ("email", "delivered", "Delivered 09-27-2017 at 08:30 AM", True), - ("sms", "created", "Sending since 09-27-2017 at 08:30 AM", True), - ("sms", "sending", "Sending since 09-27-2017 at 08:30 AM", True), - ( - "sms", - "temporary-failure", - "Phone not accepting messages right now 09-27-2017 at 08:30 AM", - False, - ), - ( - "sms", - "permanent-failure", - "Not delivered 09-27-2017 at 08:30 AM", - False, - ), - ("sms", "delivered", "Delivered 09-27-2017 at 08:30 AM", True), - ], -) -def test_sending_status_hint_displays_correctly_on_notifications_page( - client_request, - service_one, - mock_get_service_statistics, - mock_get_service_data_retention, - mock_get_no_api_keys, - message_type, - status, - expected_hint_status, - single_line, - mocker, -): - notifications = create_notifications(template_type=message_type, status=status) - mocker.patch( - "app.notification_api_client.get_notifications_for_service", - return_value=notifications, - ) - - page = client_request.get( - "main.view_notifications", - service_id=service_one["id"], - message_type=message_type, - ) - - assert ( - normalize_spaces(page.select(".table-field-right-aligned")[0].text) - == expected_hint_status - ) - assert bool(page.select(".align-with-message-body")) is single_line diff --git a/tests/app/main/views/test_download_notifications_csv_s3.py b/tests/app/main/views/test_download_notifications_csv_s3.py index fa8cc1bfad..1abd999454 100644 --- a/tests/app/main/views/test_download_notifications_csv_s3.py +++ b/tests/app/main/views/test_download_notifications_csv_s3.py @@ -80,7 +80,7 @@ def test_missing_s3_file_redirects_gracefully( service_id=SERVICE_ONE_ID, number_of_days="five_day", message_type="sms", - _expected_redirect=f"/services/{SERVICE_ONE_ID}/notifications/sms?status=sending,delivered,failed", + _expected_redirect=f"/activity/services/{SERVICE_ONE_ID}", ) # The redirect happens, which means no 500 error occurred diff --git a/tests/app/main/views/test_jobs_activity.py b/tests/app/main/views/test_jobs_activity.py index 8c3eb62269..09fcbbf31d 100644 --- a/tests/app/main/views/test_jobs_activity.py +++ b/tests/app/main/views/test_jobs_activity.py @@ -254,3 +254,119 @@ def test_all_activity_filters(client_request, mocker, filter_type, expected_limi ) else: mock_get_page_of_jobs.assert_any_call(SERVICE_ONE_ID, page=current_page) + + +def test_download_links_show_when_data_available( + client_request, + service_one, + active_user_with_permissions, + mocker, +): + + mock_jobs_with_data = { + "data": [{"id": "job1", "created_at": "2020-01-01T00:00:00.000000+00:00"}], + "total": 1, + "page_size": 50, + } + + mocker.patch( + "app.job_api_client.get_page_of_jobs", return_value=mock_jobs_with_data + ) + mocker.patch("app.job_api_client.get_immediate_jobs", return_value=[{"id": "job1"}]) + mocker.patch("app.s3_client.check_s3_file_exists", return_value=True) + mock_obj = mocker.Mock() + mock_obj.content_length = 1024 + mocker.patch("app.s3_client.get_s3_object", return_value=mock_obj) + mocker.patch("app.s3_client.s3_csv_client.get_csv_upload", return_value=mock_obj) + + page = client_request.get( + "main.all_jobs_activity", + service_id=service_one["id"], + ) + + assert "Download recent reports" in page.text + assert "Yesterday" in page.text + assert "Last 3 days" in page.text + assert "Last 5 days" in page.text + assert "Last 7 days" in page.text + + +def test_download_links_partial_data_available( + client_request, + service_one, + active_user_with_permissions, + mocker, +): + mock_jobs_with_data = { + "data": [{"id": "job1", "created_at": "2020-01-01T00:00:00.000000+00:00"}], + "total": 1, + "page_size": 50, + } + mock_jobs_empty = {"data": [], "total": 0, "page_size": 50} + + def mock_get_page_of_jobs(service_id, page=1, limit_days=None): + if limit_days in [1, 5]: + return mock_jobs_with_data + return mock_jobs_empty + + mocker.patch( + "app.job_api_client.get_page_of_jobs", side_effect=mock_get_page_of_jobs + ) + mocker.patch("app.job_api_client.get_immediate_jobs", return_value=[]) + mocker.patch("app.s3_client.check_s3_file_exists", return_value=True) + mock_obj = mocker.Mock() + mock_obj.content_length = 2048 + mocker.patch("app.s3_client.s3_csv_client.get_csv_upload", return_value=mock_obj) + mocker.patch("app.s3_client.get_s3_object", return_value=mock_obj) + + page = client_request.get( + "main.all_jobs_activity", + service_id=service_one["id"], + ) + + assert "Download recent reports" in page.text + assert "Yesterday" in page.text + assert "Last 3 days" in page.text + assert "Last 5 days" in page.text + assert "Last 7 days" in page.text + assert "No recent activity to download" not in page.text + + +def test_download_links_no_data_available( + client_request, + service_one, + active_user_with_permissions, + mocker, +): + mock_jobs_empty = {"data": [], "total": 0, "page_size": 50} + + mocker.patch("app.job_api_client.get_page_of_jobs", return_value=mock_jobs_empty) + mocker.patch("app.job_api_client.get_immediate_jobs", return_value=[]) + mocker.patch("app.s3_client.check_s3_file_exists", return_value=False) + mock_obj = mocker.Mock() + mock_obj.content_length = 0 + mocker.patch("app.s3_client.get_s3_object", return_value=mock_obj) + + page = client_request.get( + "main.all_jobs_activity", + service_id=service_one["id"], + ) + + assert "Download recent reports" in page.text + assert "Yesterday" in page.text + assert "No messages sent" in page.text + assert "Last 3 days - No messages sent" in page.text + assert "Last 5 days - No messages sent" in page.text + assert "Last 7 days - No messages sent" in page.text + + +def test_download_not_available_to_users_without_dashboard( + client_request, + active_caseworking_user, +): + client_request.login(active_caseworking_user) + client_request.get( + "main.download_notifications_csv", + service_id=SERVICE_ONE_ID, + _expected_status=403, + ) diff --git a/tests/app/main/views/test_notifications.py b/tests/app/main/views/test_notifications.py index f1431322f5..f6333c0498 100644 --- a/tests/app/main/views/test_notifications.py +++ b/tests/app/main/views/test_notifications.py @@ -177,9 +177,7 @@ def test_notification_status_page_respects_redaction( {}, partial( url_for, - "main.view_notifications", - message_type="sms", - status="sending,delivered,failed", + "main.all_jobs_activity", ), ), ( diff --git a/tests/app/test_navigation.py b/tests/app/test_navigation.py index e7c4646bb2..014628a315 100644 --- a/tests/app/test_navigation.py +++ b/tests/app/test_navigation.py @@ -104,7 +104,6 @@ "get_daily_stats_by_user", "get_volumes_by_service", "get_example_csv", - "get_notifications_as_json", "get_redis_report", "get_started", "get_started_old", @@ -243,8 +242,6 @@ "view_jobs", "view_notification", "view_notification_updates", - "view_notifications", - "view_notifications_csv", "view_template", "view_template_version", "view_template_versions", diff --git a/tests/app/utils/test_pagination.py b/tests/app/utils/test_pagination.py index d1a3663c11..ad905019f6 100644 --- a/tests/app/utils/test_pagination.py +++ b/tests/app/utils/test_pagination.py @@ -22,10 +22,8 @@ def test_generate_next_dict(client_request): def test_generate_previous_next_dict_adds_other_url_args(client_request): - result = generate_next_dict( - "main.view_notifications", "foo", 2, {"message_type": "blah"} - ) - assert "notifications/blah" in result["url"] + result = generate_next_dict("main.view_jobs", "foo", 2, {"status": "pending"}) + assert "status=pending" in result["url"] @pytest.mark.parametrize( From d44d7e8c1f6219c2bed70c017a9e1ae4a87d561d Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Tue, 14 Oct 2025 00:34:29 -0700 Subject: [PATCH 4/4] remove caseworker --- app/templates/components/main_nav.html | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/templates/components/main_nav.html b/app/templates/components/main_nav.html index 51797fe7f6..efd1978054 100644 --- a/app/templates/components/main_nav.html +++ b/app/templates/components/main_nav.html @@ -10,9 +10,6 @@
  • Dashboard
  • Activity
  • {% endif %} - {# {% if not current_user.has_permissions(ServicePermission.VIEW_ACTIVITY) %} #} - {#
  • Sent messages
  • #} - {# {% endif %} #} {% elif current_user.has_permissions(allow_org_user=True) %}
  • Usage
  • Team members