diff --git a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss index e3ef2be5d..902e90ee5 100644 --- a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss +++ b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss @@ -464,7 +464,7 @@ td.table-empty-message { width: 25%; } td.time-sent { - width: 30%; + width: 15%; } td.sender { width: 20%; @@ -474,12 +474,20 @@ td.table-empty-message { width: 5%; } td.report { - width: 5%; + width: 2%; + text-align: center; + } + td.delivered { + width: 2%; + text-align: center; + } + td.failed { + width: 2%; text-align: center; } td.report img { padding-top: 5px; - } + } th { padding: 0.5rem 1rem } diff --git a/app/main/views/activity.py b/app/main/views/activity.py index f9b32e9db..38863eb59 100644 --- a/app/main/views/activity.py +++ b/app/main/views/activity.py @@ -13,14 +13,14 @@ from app.utils.user import user_has_permissions @main.route("/activity/services/") -@user_has_permissions() +@user_has_permissions("view_activity") def all_jobs_activity(service_id): service_data_retention_days = 7 page = get_page_from_request() jobs = job_api_client.get_page_of_jobs(service_id, page=page) all_jobs_dict = generate_job_dict(jobs) prev_page, next_page, pagination = handle_pagination(jobs, service_id, page) - + message_type = ("sms",) return render_template( "views/activity/all-activity.html", all_jobs_dict=all_jobs_dict, @@ -28,6 +28,27 @@ def all_jobs_activity(service_id): next_page=next_page, prev_page=prev_page, pagination=pagination, + 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_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", + ), ) @@ -75,6 +96,22 @@ def generate_job_dict(jobs): "processing_started": job["processing_started"], "created_by": job["created_by"], "template_name": job["template_name"], + "delivered_count": next( + ( + stat["count"] + for stat in job.get("statistics", []) + if stat["status"] == "delivered" + ), + None, + ), + "failed_count": next( + ( + stat["count"] + for stat in job.get("statistics", []) + if stat["status"] == "failed" + ), + None, + ), } for job in jobs["data"] ] diff --git a/app/templates/views/activity/all-activity.html b/app/templates/views/activity/all-activity.html index 7eb7cb3c5..9714dfc16 100644 --- a/app/templates/views/activity/all-activity.html +++ b/app/templates/views/activity/all-activity.html @@ -84,6 +84,12 @@ Report + + Delivered + + + Failed + @@ -108,6 +114,8 @@ N/A {% endif %} + {{ job.delivered_count if job.delivered_count is not none else '0' }} + {{ job.failed_count if job.failed_count is not none else '0' }} {% endfor %} {% else %} @@ -121,6 +129,17 @@

Note: Report data is only available for 7 days after your message has been sent

{{show_pagination}} + {% if current_user.has_permissions('view_activity') %} +

Download recent reports

+

+ Download all data last 24 hours (CSV) +

+

+ Download all data last 5 days (CSV) +

+

+ Download all data last 7 days (CSV) +

+ {% endif %} - {% endblock %} diff --git a/tests/app/main/views/test_jobs_activity.py b/tests/app/main/views/test_jobs_activity.py index 60502c957..962a2a378 100644 --- a/tests/app/main/views/test_jobs_activity.py +++ b/tests/app/main/views/test_jobs_activity.py @@ -21,7 +21,7 @@ MOCK_JOBS = { "scheduled_for": None, "service": "21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3", "service_name": {"name": "Mock Texting Service"}, - "statistics": [{"count": 1, "status": "sending"}], + "statistics": [{"count": 1, "status": "delivered"}, {"count": 5, "status": "failed"}], "template": "6a456418-498c-4c86-b0cd-9403c14a216c", "template_name": "Mock Template Name", "template_type": "sms", @@ -63,7 +63,7 @@ def test_all_activity( assert table is not None, "Table not found in the response" headers = [th.get_text(strip=True) for th in table.find_all("th")] - expected_headers = ["Job ID#", "Template", "Time sent", "Sender", "Report"] + expected_headers = ["Job ID#", "Template", "Time sent", "Sender", "Report", "Delivered", "Failed"] assert ( headers == expected_headers @@ -74,7 +74,7 @@ def test_all_activity( job_row = rows[0] cells = job_row.find_all("td") - assert len(cells) == 5, "Expected five columns in the job row" + assert len(cells) == 7, "Expected five columns in the job row" job_id_cell = cells[0].find("a").get_text(strip=True) @@ -97,6 +97,15 @@ def test_all_activity( report_cell = cells[4].find("span").get_text(strip=True) assert report_cell == "N/A", f"Expected report 'N/A', but got '{report_cell}'" + delivered_cell = cells[5].get_text(strip=True) + assert ( + delivered_cell == "1" + ), f"Expected delivered count '1', but got '{delivered_cell}'" + + failed_cell = cells[6].get_text(strip=True) + assert ( + failed_cell == "5" + ), f"Expected failed count '5', but got '{failed_cell}'" mock_get_page_of_jobs.assert_called_with(SERVICE_ONE_ID, page=current_page)