From 34d727748bfac7ca8ac40eecb77c349721d9d88a Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 26 Jul 2024 09:15:09 -0700 Subject: [PATCH 01/98] cleanup notify-compliance-46 --- app/notify_client/__init__.py | 20 +++++++++++-------- .../test_notify_admin_api_client.py | 19 ++++++++++++++++++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/app/notify_client/__init__.py b/app/notify_client/__init__.py index 5f65e85c6..ea3bd8e61 100644 --- a/app/notify_client/__init__.py +++ b/app/notify_client/__init__.py @@ -56,6 +56,9 @@ class NotifyAdminAPIClient(BaseAPIClient): ): abort(403) + def is_calling_signin_url(self, arg): + return arg[0].startswith("/user") + def check_inactive_user(self, *args): still_signing_in = False @@ -64,14 +67,15 @@ class NotifyAdminAPIClient(BaseAPIClient): # and we only want to check the first arg for arg in args: arg = str(arg) - if ( - "get-login-gov-user" in arg - or "user/email" in arg - or "/activate" in arg - or "/email-code" in arg - or "/verify/code" in arg - or "/user" in arg - ): + if self.is_calling_signin_url(arg): + # if ( + # "get-login-gov-user" in arg + # or "user/email" in arg + # or "/activate" in arg + # or "/email-code" in arg + # or "/verify/code" in arg + # or "/user" in arg + # ): still_signing_in = True # This seems to be a weird edge case that happens intermittently with invites diff --git a/tests/app/notify_client/test_notify_admin_api_client.py b/tests/app/notify_client/test_notify_admin_api_client.py index e68d4669f..ec5de4522 100644 --- a/tests/app/notify_client/test_notify_admin_api_client.py +++ b/tests/app/notify_client/test_notify_admin_api_client.py @@ -41,6 +41,25 @@ def test_active_service_can_be_modified(notify_admin, method, user, service): assert ret == request.return_value +@pytest.mark.parametrize( + ("arg", "expected_result"), + [ + ( + ("/user/c5f8a5c9-56d5-4fa9-8c30-3449ae10c072/verify/code",), + True, + ), + ( + ("/service/blahblahblah",), + False, + ), + ], +) +def test_is_calling_signin_url(arg, expected_result): + api_client = NotifyAdminAPIClient() + result = api_client.is_calling_signin_url(arg) + assert result == expected_result + + @pytest.mark.parametrize("method", ["put", "post", "delete"]) def test_inactive_service_cannot_be_modified_by_normal_user( notify_admin, api_user_active, method From e884f31fa08e86f4d2485672646b51f280a67004 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 26 Jul 2024 09:49:12 -0700 Subject: [PATCH 02/98] write test --- app/notify_client/__init__.py | 2 +- tests/app/notify_client/test_notify_admin_api_client.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/notify_client/__init__.py b/app/notify_client/__init__.py index ea3bd8e61..3d7fd62a8 100644 --- a/app/notify_client/__init__.py +++ b/app/notify_client/__init__.py @@ -57,7 +57,7 @@ class NotifyAdminAPIClient(BaseAPIClient): abort(403) def is_calling_signin_url(self, arg): - return arg[0].startswith("/user") + return arg.startswith("('/user") def check_inactive_user(self, *args): still_signing_in = False diff --git a/tests/app/notify_client/test_notify_admin_api_client.py b/tests/app/notify_client/test_notify_admin_api_client.py index ec5de4522..d15eebfc3 100644 --- a/tests/app/notify_client/test_notify_admin_api_client.py +++ b/tests/app/notify_client/test_notify_admin_api_client.py @@ -45,11 +45,12 @@ def test_active_service_can_be_modified(notify_admin, method, user, service): ("arg", "expected_result"), [ ( - ("/user/c5f8a5c9-56d5-4fa9-8c30-3449ae10c072/verify/code",), + "('/user/c5f8a5c9-56d5-4fa9-8c30-3449ae10c072/verify/code',)", True, ), + ("('/user/get-login-gov-user',)", True), ( - ("/service/blahblahblah",), + "('/service/blahblahblah',)", False, ), ], From 42db90502681c3ebabc7092b498fa56f7d2a619e Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Wed, 31 Jul 2024 16:00:06 -0400 Subject: [PATCH 03/98] Creating 2 tables to be put tied into the dropdown event --- app/templates/views/dashboard/dashboard.html | 123 ++++++++++++------- 1 file changed, 78 insertions(+), 45 deletions(-) diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index eeca2b213..1db058718 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -59,34 +59,72 @@ {% if current_user.has_permissions('manage_service') %}{% endif %} {{ ajax_block(partials, updates_url, 'template-statistics') }} -

Recent Batches

-
- - - - - - - - - - - - - {% if jobs %} - {% for job in jobs[:5] %} +
+
+

My Activity

+
- File name - - Template - - Job status - - Sender - - # of Recipients - - Report -
+ + + + + + + + + + + {% if jobs %} + {% for job in jobs[:5] %} + {% if job.created_by.name == current_user.name %} + {% set notification = job.notifications[0] %} + + + + + + + + {% endif %} + {% endfor %} + {% else %} + + + + {% endif %} + +
File nameTemplateJob status# of RecipientsReport
+ {{ job.original_file_name[:12] if job.original_file_name else 'Manually entered number'}} +
+ View Batch +
{{ job.template_name }} + {{ (job.processing_finished if job.processing_finished else job.processing_started + if job.processing_started else job.created_at)|format_datetime_table }} + {{ job.notification_count }} + {% if job.time_left != "Data no longer available" %} + Download + {{ job.time_left }} + {% elif job %} + {{ job.time_left }} + {% endif %} +
No batched job messages found  (messages are kept for {{ service_data_retention_days }} days).
+
+ +

Recent templates

From 131d99a4df0f0a46fd32c6e75bcaf37c9651aa4c Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 2 Aug 2024 14:02:44 -0400 Subject: [PATCH 04/98] iAdding some logging for Bev. Signed-off-by: Cliff Hill --- app/notify_client/job_api_client.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/notify_client/job_api_client.py b/app/notify_client/job_api_client.py index 538bdd370..825097630 100644 --- a/app/notify_client/job_api_client.py +++ b/app/notify_client/job_api_client.py @@ -1,4 +1,5 @@ import datetime +from flask import current_app from zoneinfo import ZoneInfo from app.extensions import redis_client @@ -28,7 +29,7 @@ class JobApiClient(NotifyAdminAPIClient): def get_job(self, service_id, job_id): params = {} job = self.get( - url="/service/{}/job/{}".format(service_id, job_id), params=params + url=f"/service/{service_id}/job/{job_id}", params=params ) return job @@ -40,13 +41,16 @@ class JobApiClient(NotifyAdminAPIClient): if statuses is not None: params["statuses"] = ",".join(statuses) - return self.get(url="/service/{}/job".format(service_id), params=params) + job = self.get(url=f"/service/{service_id}/job", params=params) + from pprint import pformat + current_app.logger.info(pformat(job)) + return job def get_uploads(self, service_id, limit_days=None, page=1): params = {"page": page} if limit_days is not None: params["limit_days"] = limit_days - return self.get(url="/service/{}/upload".format(service_id), params=params) + return self.get(url=f"/service/{service_id}/upload", params=params) def has_sent_previously( self, service_id, template_id, template_version, original_file_name From 03466d01848cf53dc069d3d8b6cb4daf571ebbd2 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 2 Aug 2024 14:37:06 -0400 Subject: [PATCH 05/98] Removing pformat from the change. Signed-off-by: Cliff Hill --- app/notify_client/job_api_client.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/notify_client/job_api_client.py b/app/notify_client/job_api_client.py index 825097630..c88c97c64 100644 --- a/app/notify_client/job_api_client.py +++ b/app/notify_client/job_api_client.py @@ -42,8 +42,7 @@ class JobApiClient(NotifyAdminAPIClient): params["statuses"] = ",".join(statuses) job = self.get(url=f"/service/{service_id}/job", params=params) - from pprint import pformat - current_app.logger.info(pformat(job)) + current_app.logger.info(job) return job def get_uploads(self, service_id, limit_days=None, page=1): From 27ee039b2d98e81f8dec1addf800ac86ac635ff2 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 2 Aug 2024 14:42:29 -0400 Subject: [PATCH 06/98] iDumping object as a JSON. Signed-off-by: Cliff Hill --- app/notify_client/job_api_client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/notify_client/job_api_client.py b/app/notify_client/job_api_client.py index c88c97c64..5b57edb08 100644 --- a/app/notify_client/job_api_client.py +++ b/app/notify_client/job_api_client.py @@ -42,7 +42,8 @@ class JobApiClient(NotifyAdminAPIClient): params["statuses"] = ",".join(statuses) job = self.get(url=f"/service/{service_id}/job", params=params) - current_app.logger.info(job) + from json import dumps + current_app.logger.info(dumps(job)) return job def get_uploads(self, service_id, limit_days=None, page=1): From 6b8be20ed216996e6413712da2eefc29c7a7e562 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 2 Aug 2024 14:50:52 -0400 Subject: [PATCH 07/98] Black & isort. Signed-off-by: Cliff Hill --- app/notify_client/job_api_client.py | 8 +- tests/app/main/views/test_dashboard.py | 2 +- tests/app/main/views/test_jobs.py | 2 +- tests/app/main/views/test_jobs_activity.py | 110 +++++++++++---------- tests/app/utils/test_pagination.py | 14 ++- 5 files changed, 75 insertions(+), 61 deletions(-) diff --git a/app/notify_client/job_api_client.py b/app/notify_client/job_api_client.py index 5b57edb08..cd15730df 100644 --- a/app/notify_client/job_api_client.py +++ b/app/notify_client/job_api_client.py @@ -1,7 +1,8 @@ import datetime -from flask import current_app from zoneinfo import ZoneInfo +from flask import current_app + from app.extensions import redis_client from app.notify_client import NotifyAdminAPIClient, _attach_current_user, cache from app.utils.csv import get_user_preferred_timezone @@ -28,9 +29,7 @@ class JobApiClient(NotifyAdminAPIClient): def get_job(self, service_id, job_id): params = {} - job = self.get( - url=f"/service/{service_id}/job/{job_id}", params=params - ) + job = self.get(url=f"/service/{service_id}/job/{job_id}", params=params) return job @@ -43,6 +42,7 @@ class JobApiClient(NotifyAdminAPIClient): job = self.get(url=f"/service/{service_id}/job", params=params) from json import dumps + current_app.logger.info(dumps(job)) return job diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index 613ac74d9..fe58dec7d 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -1362,7 +1362,7 @@ def test_menu_all_services_for_platform_admin_user( page = str(page) assert url_for("main.choose_template", service_id=service_one["id"]) in page assert url_for("main.service_settings", service_id=service_one["id"]) in page - assert url_for('main.api_keys', service_id=service_one['id']) not in page + assert url_for("main.api_keys", service_id=service_one["id"]) not in page def test_route_for_service_permissions( diff --git a/tests/app/main/views/test_jobs.py b/tests/app/main/views/test_jobs.py index dd55d7500..c4a756194 100644 --- a/tests/app/main/views/test_jobs.py +++ b/tests/app/main/views/test_jobs.py @@ -494,5 +494,5 @@ def test_should_show_message_note( assert normalize_spaces(page.select_one("main p.notification-status").text) == ( 'Messages are sent immediately to the cell phone carrier, but will remain in "pending" status until we hear ' - 'back from the carrier they have received it and attempted deliver. More information on delivery status.' + "back from the carrier they have received it and attempted deliver. More information on delivery status." ) diff --git a/tests/app/main/views/test_jobs_activity.py b/tests/app/main/views/test_jobs_activity.py index 8cb171d94..60502c957 100644 --- a/tests/app/main/views/test_jobs_activity.py +++ b/tests/app/main/views/test_jobs_activity.py @@ -29,13 +29,13 @@ MOCK_JOBS = { "updated_at": "2024-01-25T23:02:25+00:00", } ], - 'links': { - 'last': '/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=3', - 'next': '/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=3', - 'prev': '/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=1' + "links": { + "last": "/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=3", + "next": "/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=3", + "prev": "/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=1", }, - 'page_size': 50, - 'total': 115 + "page_size": 50, + "total": 115, } @@ -58,59 +58,62 @@ def test_all_activity( assert "All activity" in response.text mock_get_page_of_jobs.assert_called_with(SERVICE_ONE_ID, page=current_page) - page = BeautifulSoup(response.data, 'html.parser') - table = page.find('table') + page = BeautifulSoup(response.data, "html.parser") + table = page.find("table") assert table is not None, "Table not found in the response" - headers = [th.get_text(strip=True) for th in table.find_all('th')] + headers = [th.get_text(strip=True) for th in table.find_all("th")] expected_headers = ["Job ID#", "Template", "Time sent", "Sender", "Report"] - assert headers == expected_headers, f"Expected headers {expected_headers}, but got {headers}" + assert ( + headers == expected_headers + ), f"Expected headers {expected_headers}, but got {headers}" - rows = table.find('tbody').find_all('tr', class_='table-row') + rows = table.find("tbody").find_all("tr", class_="table-row") assert len(rows) == 1, "Expected one job row in the table" job_row = rows[0] - cells = job_row.find_all('td') + cells = job_row.find_all("td") assert len(cells) == 5, "Expected five columns in the job row" - job_id_cell = cells[0].find('a').get_text(strip=True) + job_id_cell = cells[0].find("a").get_text(strip=True) - assert job_id_cell == "55b242b5", f"Expected job ID '55b242b5', but got '{job_id_cell}'" + assert ( + job_id_cell == "55b242b5" + ), f"Expected job ID '55b242b5', but got '{job_id_cell}'" template_cell = cells[1].get_text(strip=True) - assert template_cell == "Mock Template Name", ( - f"Expected template 'Mock Template Name', but got '{template_cell}'" - ) + assert ( + template_cell == "Mock Template Name" + ), f"Expected template 'Mock Template Name', but got '{template_cell}'" time_sent_cell = cells[2].get_text(strip=True) - assert time_sent_cell == "01-25-2024 at 06:02 PM", ( - f"Expected time sent '01-25-2024 at 06:02 PM', but got '{time_sent_cell}'" - ) + assert ( + time_sent_cell == "01-25-2024 at 06:02 PM" + ), f"Expected time sent '01-25-2024 at 06:02 PM', but got '{time_sent_cell}'" sender_cell = cells[3].get_text(strip=True) - assert sender_cell == "mocked_user", f"Expected sender 'mocked_user', but got '{sender_cell}'" + assert ( + sender_cell == "mocked_user" + ), f"Expected sender 'mocked_user', but got '{sender_cell}'" - report_cell = cells[4].find('span').get_text(strip=True) + report_cell = cells[4].find("span").get_text(strip=True) assert report_cell == "N/A", f"Expected report 'N/A', but got '{report_cell}'" mock_get_page_of_jobs.assert_called_with(SERVICE_ONE_ID, page=current_page) -def test_all_activity_no_jobs( - client_request, - mocker -): +def test_all_activity_no_jobs(client_request, mocker): current_page = get_page_from_request() mock_get_page_of_jobs = mocker.patch( "app.job_api_client.get_page_of_jobs", return_value={ "data": [], - 'links': { - 'last': '/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=1', - 'next': None, - 'prev': None + "links": { + "last": "/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=1", + "next": None, + "prev": None, }, - 'page_size': 50, - 'total': 0 - } + "page_size": 50, + "total": 0, + }, ) response = client_request.get_response( "main.all_jobs_activity", @@ -120,17 +123,17 @@ def test_all_activity_no_jobs( assert response.status_code == 200, "Request failed" - page = BeautifulSoup(response.data, 'html.parser') + page = BeautifulSoup(response.data, "html.parser") - no_jobs_message_td = page.find('td', class_='table-empty-message') + no_jobs_message_td = page.find("td", class_="table-empty-message") assert no_jobs_message_td is not None, "No jobs message not found in the response" expected_message = "No batched job messages found (messages are kept for 7 days)." actual_message = no_jobs_message_td.get_text(strip=True) - assert expected_message == actual_message, ( - f"Expected message '{expected_message}', but got '{actual_message}'" - ) + assert ( + expected_message == actual_message + ), f"Expected message '{expected_message}', but got '{actual_message}'" mock_get_page_of_jobs.assert_called_with(SERVICE_ONE_ID, page=current_page) @@ -148,17 +151,18 @@ def test_all_activity_pagination(client_request, mocker): "processing_started": "2024-01-25T23:02:24+00:00", "template_name": "Mock Template Name", "original_file_name": "mocked_file.csv", - "notification_count": 1 - } for i in range(1, 101) + "notification_count": 1, + } + for i in range(1, 101) ], - 'links': { - 'last': '/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=2', - 'next': '/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=2', - 'prev': None + "links": { + "last": "/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=2", + "next": "/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=2", + "prev": None, }, - 'page_size': 50, - 'total': 100 - } + "page_size": 50, + "total": 100, + }, ) response = client_request.get_response( @@ -168,12 +172,12 @@ def test_all_activity_pagination(client_request, mocker): ) mock_get_page_of_jobs.assert_called_with(SERVICE_ONE_ID, page=current_page) - page = BeautifulSoup(response.data, 'html.parser') - pagination_controls = page.find_all('li', class_='usa-pagination__item') + page = BeautifulSoup(response.data, "html.parser") + pagination_controls = page.find_all("li", class_="usa-pagination__item") assert pagination_controls, "Pagination controls not found in the response" pagination_texts = [item.get_text(strip=True) for item in pagination_controls] - expected_pagination_texts = ['1', '2', 'Next'] - assert pagination_texts == expected_pagination_texts, ( - f"Expected pagination controls {expected_pagination_texts}, but got {pagination_texts}" - ) + expected_pagination_texts = ["1", "2", "Next"] + assert ( + pagination_texts == expected_pagination_texts + ), f"Expected pagination controls {expected_pagination_texts}, but got {pagination_texts}" diff --git a/tests/app/utils/test_pagination.py b/tests/app/utils/test_pagination.py index a1264d8f5..d1a3663c1 100644 --- a/tests/app/utils/test_pagination.py +++ b/tests/app/utils/test_pagination.py @@ -36,8 +36,18 @@ def test_generate_previous_next_dict_adds_other_url_args(client_request): (500, 50, 1, {"current": 1, "pages": [1, 2, 3, 4, 5, 6, 7, 8, 9], "last": 10}), (500, 50, 5, {"current": 5, "pages": [1, 2, 3, 4, 5, 6, 7, 8, 9], "last": 10}), (500, 50, 6, {"current": 6, "pages": [2, 3, 4, 5, 6, 7, 8, 9, 10], "last": 10}), - (500, 50, 10, {"current": 10, "pages": [2, 3, 4, 5, 6, 7, 8, 9, 10], "last": 10}), - (950, 50, 15, {"current": 15, "pages": [11, 12, 13, 14, 15, 16, 17, 18, 19], "last": 19}), + ( + 500, + 50, + 10, + {"current": 10, "pages": [2, 3, 4, 5, 6, 7, 8, 9, 10], "last": 10}, + ), + ( + 950, + 50, + 15, + {"current": 15, "pages": [11, 12, 13, 14, 15, 16, 17, 18, 19], "last": 19}, + ), ], ) def test_generate_pagination_pages(total_items, page_size, current_page, expected): From 65da623458da82c0df859cf5b3f7476f228e85a4 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 2 Aug 2024 15:01:55 -0400 Subject: [PATCH 08/98] Done stuff to make serialization work hopefully. Signed-off-by: Cliff Hill --- app/notify_client/job_api_client.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/notify_client/job_api_client.py b/app/notify_client/job_api_client.py index cd15730df..0ee90d464 100644 --- a/app/notify_client/job_api_client.py +++ b/app/notify_client/job_api_client.py @@ -42,8 +42,7 @@ class JobApiClient(NotifyAdminAPIClient): job = self.get(url=f"/service/{service_id}/job", params=params) from json import dumps - - current_app.logger.info(dumps(job)) + current_app.logger.info(dumps(job._dict)) return job def get_uploads(self, service_id, limit_days=None, page=1): From 0fe73f6973652724b89a7f1982ff968e18d19b2e Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 2 Aug 2024 15:14:17 -0400 Subject: [PATCH 09/98] Trying this now. Signed-off-by: Cliff Hill --- app/notify_client/job_api_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/notify_client/job_api_client.py b/app/notify_client/job_api_client.py index 0ee90d464..dfc4fe814 100644 --- a/app/notify_client/job_api_client.py +++ b/app/notify_client/job_api_client.py @@ -41,8 +41,8 @@ class JobApiClient(NotifyAdminAPIClient): params["statuses"] = ",".join(statuses) job = self.get(url=f"/service/{service_id}/job", params=params) - from json import dumps - current_app.logger.info(dumps(job._dict)) + from pprint import pformat + current_app.logger.info(pformat(job)) return job def get_uploads(self, service_id, limit_days=None, page=1): From af59b13836ff069405d0cfff70e09b15c61765a4 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 2 Aug 2024 14:30:35 -0700 Subject: [PATCH 10/98] code review feedback --- app/notify_client/__init__.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/notify_client/__init__.py b/app/notify_client/__init__.py index 3d7fd62a8..8db3425f2 100644 --- a/app/notify_client/__init__.py +++ b/app/notify_client/__init__.py @@ -68,14 +68,6 @@ class NotifyAdminAPIClient(BaseAPIClient): for arg in args: arg = str(arg) if self.is_calling_signin_url(arg): - # if ( - # "get-login-gov-user" in arg - # or "user/email" in arg - # or "/activate" in arg - # or "/email-code" in arg - # or "/verify/code" in arg - # or "/user" in arg - # ): still_signing_in = True # This seems to be a weird edge case that happens intermittently with invites From 9f79ae314fe7fc2d914b64acf700453f686abeaf Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Mon, 5 Aug 2024 11:55:43 -0400 Subject: [PATCH 11/98] Update dependabot.yml config This changeset updates our dependabot.yml configuration to match what the documentation currently says is current: * Adjusts version referenced from 3 to 2 to match the docs * Adjusts the formatting of values to match the docs Signed-off-by: Carlo Costino --- .github/dependabot.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1be311a7e..3e3ce01d3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,14 +3,14 @@ # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates -version: 3 +version: 2 updates: - package-ecosystem: "pip" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "daily" - - package-ecosystem: 'npm' - directory: '/' + - package-ecosystem: "npm" + directory: "/" schedule: - interval: 'daily' - versioning-strategy: 'increase' + interval: "daily" + versioning-strategy: increase From cd1cedad693c036c2d8dd083cfa995e64e1017d5 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 5 Aug 2024 12:09:38 -0700 Subject: [PATCH 12/98] add debug staging api to docs --- docs/debug-issues-with-staging-api.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 docs/debug-issues-with-staging-api.md diff --git a/docs/debug-issues-with-staging-api.md b/docs/debug-issues-with-staging-api.md new file mode 100644 index 000000000..e80a2ac32 --- /dev/null +++ b/docs/debug-issues-with-staging-api.md @@ -0,0 +1,26 @@ +### Setting Up Environment Variables for Local Development to the Staging API + +When you’re working locally, you can point your local admin repo to the staging API and use that to help debug +issues with the staging data set. To do this, you’ll need to modify your .env file for the admin project and +include the following new environment variables: + +- `ADMIN_CLIENT_SECRET` +- `ADMIN_CLIENT_USERNAME` +- `DANGEROUS_SALT` +- `SECRET_KEY` + +Additionally, update `API_HOST_NAME` and `NOTIFY_ENVIRONMENT`: + +1. Change `API_HOST_NAME` to `API_HOST_NAME=https://notify-api-staging.app.cloud.gov` +2. Change `NOTIFY_ENVIRONMENT` to `NOTIFY_ENVIRONMENT=staging` + +### Retrieving Environment Variables for Staging + +You can retrieve the values needed for these by using the cf CLI (Cloud Foundry CLI tool) and making sure +you’re targeting the notify-staging space. + +1. `cf login -a [api.fr.cloud.gov](http://api.fr.cloud.gov/) --sso` +2. select `notify-staging` +3. `cf env notify-admin-staging` + +By pointing your local environment to staging, it should mirror what's in staging. From d6079a463df6dc1be0e2723531e6370f673af540 Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Mon, 5 Aug 2024 16:05:54 -0400 Subject: [PATCH 13/98] Adding the ability to switch tables in the js Updating css Testing updated js --- app/assets/javascripts/activityChart.js | 29 ++++-- .../uswds/_uswds-theme-custom-styles.scss | 6 ++ app/templates/views/dashboard/dashboard.html | 4 +- tests/javascripts/activityChart.test.js | 99 ++++++++++++++++++- 4 files changed, 126 insertions(+), 12 deletions(-) diff --git a/app/assets/javascripts/activityChart.js b/app/assets/javascripts/activityChart.js index 84e5e3d51..a714a3d0d 100644 --- a/app/assets/javascripts/activityChart.js +++ b/app/assets/javascripts/activityChart.js @@ -234,17 +234,28 @@ // Update ARIA live region const liveRegion = document.getElementById('aria-live-account'); - liveRegion.textContent = `Data updated for ${selectedText} - Last 7 Days`; - }; + liveRegion.textContent = `Data updated for ${selectedText} - Last 7 Days`; - document.addEventListener('DOMContentLoaded', function() { - // Initialize activityChart chart and table with service data by default - fetchData('service'); - - // Add event listener to the dropdown - const dropdown = document.getElementById('options'); - dropdown.addEventListener('change', handleDropdownChange); + // Switch tables based on dropdown selection + const selectedTable = selectedValue === "individual" ? "table1" : "table2"; + const tables = document.querySelectorAll('.table-overflow-x-auto'); + tables.forEach(function(table) { + table.classList.add('hidden'); // Hide all tables by adding the hidden class + table.classList.remove('visible'); // Ensure they are not visible }); + const tableToShow = document.getElementById(selectedTable); + tableToShow.classList.remove('hidden'); // Remove hidden class + tableToShow.classList.add('visible'); // Add visible class + }; + + document.addEventListener('DOMContentLoaded', function() { + // Initialize activityChart chart and table with service data by default + fetchData('service'); + + // Add event listener to the dropdown + const dropdown = document.getElementById('options'); + dropdown.addEventListener('change', handleDropdownChange); + }); // Resize chart on window resize window.addEventListener('resize', function() { diff --git a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss index e3ef2be5d..4b62e564d 100644 --- a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss +++ b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss @@ -332,6 +332,12 @@ td.table-empty-message { bottom: 0; } +.table-overflow-x-auto { + &.hidden { + display: none; + } +} + @media (max-width: units('desktop-lg')) { .table-overflow-x-auto { overflow-x: auto; diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index 1db058718..bcb942210 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -60,7 +60,7 @@ {{ ajax_block(partials, updates_url, 'template-statistics') }}
-
+ - - -

Recent templates

{{ ajax_block(partials, updates_url, 'template-statistics') }}
diff --git a/app/templates/views/dashboard/template-statistics.html b/app/templates/views/dashboard/template-statistics.html index 9acd142d6..5ad22a0dd 100644 --- a/app/templates/views/dashboard/template-statistics.html +++ b/app/templates/views/dashboard/template-statistics.html @@ -2,6 +2,7 @@
{% if template_statistics|length > 1 %} +

Recent Templates

{% call(item, row_number) list_table( template_statistics, From 323ecf63f4cbcf2beafa35ce1130a7b1d0afb28a Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Tue, 6 Aug 2024 12:27:42 -0700 Subject: [PATCH 20/98] remove unused import --- app/main/views/platform_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/main/views/platform_admin.py b/app/main/views/platform_admin.py index 07e973704..749b79c1a 100644 --- a/app/main/views/platform_admin.py +++ b/app/main/views/platform_admin.py @@ -3,7 +3,7 @@ import json from collections import OrderedDict from datetime import datetime -from flask import Response, abort, flash, render_template, request, send_file, url_for +from flask import Response, abort, flash, render_template, request, url_for from notifications_python_client.errors import HTTPError from app import ( From aed9778a6548919a3a58649f9324de62e7916bbc Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Tue, 6 Aug 2024 12:42:34 -0700 Subject: [PATCH 21/98] fix tests --- tests/app/test_navigation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/app/test_navigation.py b/tests/app/test_navigation.py index d346fc71a..aeee32164 100644 --- a/tests/app/test_navigation.py +++ b/tests/app/test_navigation.py @@ -68,6 +68,7 @@ EXCLUDED_ENDPOINTS = tuple( "delivery_status_callback", "design_content", "documentation", + "download_all_users", "download_notifications_csv", "download_organization_usage_report", "edit_and_format_messages", From ad2151b637141ac325605b14862bb45e01441cab Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Tue, 6 Aug 2024 17:28:14 -0700 Subject: [PATCH 22/98] fix 400 errors --- app/__init__.py | 2 +- app/assets/javascripts/activityChart.js | 6 +++++- app/main/views/dashboard.py | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index f32a98b6c..55942769c 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -177,7 +177,7 @@ def create_app(application): init_govuk_frontend(application) init_jinja(application) - socketio.init_app(application) + socketio.init_app(application, cors_allowed_origins=['http://localhost:6012']) for client in ( csrf, diff --git a/app/assets/javascripts/activityChart.js b/app/assets/javascripts/activityChart.js index 84e5e3d51..75913c145 100644 --- a/app/assets/javascripts/activityChart.js +++ b/app/assets/javascripts/activityChart.js @@ -185,7 +185,7 @@ return; } - var socket = io(); + var socket = io("/services"); var eventType = type === 'service' ? 'fetch_daily_stats' : 'fetch_daily_stats_by_user'; var socketConnect = type === 'service' ? 'daily_stats_update' : 'daily_stats_by_user_update'; @@ -193,6 +193,10 @@ socket.emit(eventType); }); + socket.on('connect_error', function(error) { + console.error('WebSocket connection error:', error); + }); + socket.on(socketConnect, function(data) { var labels = []; diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 32a0e46e2..9c3eb6527 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -32,7 +32,7 @@ from app.utils.user import user_has_permissions from notifications_utils.recipients import format_phone_number_human_readable -@socketio.on("fetch_daily_stats") +@socketio.on("fetch_daily_stats", namespace="/services") def handle_fetch_daily_stats(): service_id = session.get("service_id") if service_id: @@ -45,7 +45,7 @@ def handle_fetch_daily_stats(): emit("error", {"error": "No service_id provided"}) -@socketio.on("fetch_daily_stats_by_user") +@socketio.on("fetch_daily_stats_by_user", namespace="/services") def handle_fetch_daily_stats_by_user(): service_id = session.get("service_id") user_id = session.get("user_id") From 2497679046a11160727e552e1dcc710d80475df2 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 7 Aug 2024 07:21:53 -0700 Subject: [PATCH 23/98] switch commas to tabs --- app/main/views/platform_admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/main/views/platform_admin.py b/app/main/views/platform_admin.py index 749b79c1a..67b57a0dd 100644 --- a/app/main/views/platform_admin.py +++ b/app/main/views/platform_admin.py @@ -75,7 +75,7 @@ def platform_admin(): def download_all_users(): # Create a CSV string from the user data - csv_data = "Name,Email Address,Mobile Number,Service\n" + csv_data = "Name\tEmail Address\tMobile Number\tService\n" users = user_api_client.get_all_users_detailed() if len(users) == 0: @@ -84,7 +84,7 @@ def download_all_users(): for user in users: if user["name"].startswith("e2e"): continue - csv_data += f"{user['name']},{user['email_address']},{user['mobile_number']},{user['service']}\n" + csv_data += f"{user['name']}\t{user['email_address']}\t{user['mobile_number']}\t{user['service']}\n" # Create a direct download response with the CSV data and appropriate headers response = Response(csv_data, content_type="text/csv") From b77a142f6b76d132bdfc94626eaee1c94f75fdf6 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 7 Aug 2024 07:37:04 -0700 Subject: [PATCH 24/98] try moving if block --- .ds.baseline | 6 +++--- .github/workflows/checks.yml | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.ds.baseline b/.ds.baseline index e397d83f8..f7f116b20 100644 --- a/.ds.baseline +++ b/.ds.baseline @@ -133,7 +133,7 @@ "filename": ".github/workflows/checks.yml", "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, - "line_number": 67, + "line_number": 68, "is_secret": false }, { @@ -141,7 +141,7 @@ "filename": ".github/workflows/checks.yml", "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, - "line_number": 102, + "line_number": 103, "is_secret": false } ], @@ -692,5 +692,5 @@ } ] }, - "generated_at": "2024-08-06T15:37:18Z" + "generated_at": "2024-08-07T14:36:40Z" } diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index a35ed404b..efeefdbd6 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -54,6 +54,7 @@ jobs: run: poetry run coverage report --fail-under=90 end-to-end-tests: + if: ${{ github.actor != 'dependabot[bot]' }} permissions: checks: write pull-requests: write @@ -84,7 +85,7 @@ jobs: ports: # Maps tcp port 6379 on service container to the host - 6379:6379 - if: ${{ github.actor != 'dependabot[bot]' }} + steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup-project From e7e3bac36cf1d39d6f7e9bb0242496507c676ceb Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Wed, 7 Aug 2024 16:20:40 -0400 Subject: [PATCH 25/98] Updates to the activity table(s) on the dashboard --- app/formatters.py | 4 +- app/templates/views/dashboard/dashboard.html | 44 ++++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/app/formatters.py b/app/formatters.py index c427c2a9a..ed28320c4 100644 --- a/app/formatters.py +++ b/app/formatters.py @@ -111,8 +111,8 @@ def format_datetime_scheduled_notification(date): def format_datetime_table(date): - # example: 03-18-2024 at 04:53 PM, intended for datetimes in tables - return "{} at {}".format(format_date_numeric(date), format_time_12h(date)) + # example: Sent on 03-18-2024 at 04:53 PM, intended for datetimes in tables + return "Sent on {} at {}".format(format_date_numeric(date), format_time_12h(date)) def format_time_12h(date): diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index 4a4bf93ca..ebb53d008 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -64,11 +64,11 @@ - - - - - + + + + + @@ -77,10 +77,10 @@ {% if job.created_by.name == current_user.name %} {% set notification = job.notifications[0] %} - @@ -113,12 +112,12 @@
File nameTemplateJob status# of RecipientsReportJob ID#TemplateJob status# of RecipientsReport
- {{ job.original_file_name[:12] if job.original_file_name else 'Manually entered number'}} -
- View Batch +
+ + {{ job.job_id[:8] if job.job_id else 'Manually entered number' }} + {{ job.template_name }} @@ -90,10 +90,9 @@ {{ job.notification_count }} {% if job.time_left != "Data no longer available" %} - Download - {{ job.time_left }} + File Download Icon {% elif job %} - {{ job.time_left }} + N/A {% endif %}
- - - - - - + + + + + + @@ -126,10 +125,10 @@ {% for job in jobs[:5] %} {% set notification = job.notifications[0] %} -
File nameTemplateJob statusSender# of RecipientsReportJob ID#TemplateJob statusSender# of RecipientsReport
- {{ job.original_file_name[:12] if job.original_file_name else 'Manually entered number'}} -
- View Batch +
+ + {{ job.job_id[:8] if job.job_id else 'Manually entered number' }} + {{ job.template_name }} @@ -155,6 +154,7 @@ {% endif %}
+

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

{{ ajax_block(partials, updates_url, 'template-statistics') }} From b1668ebe571fd9ff9ac27cab2fc2eaf93fab9eed Mon Sep 17 00:00:00 2001 From: alexjanousekGSA Date: Wed, 7 Aug 2024 14:51:38 -0600 Subject: [PATCH 26/98] Made update to trigger successful build --- gulpfile.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gulpfile.js b/gulpfile.js index 278742cfe..44ab8808d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -37,6 +37,8 @@ if (process.env.NOTIFY_ENVIRONMENT == 'development') { // pass through if on dev staticPathMatcher = url => url; } + + // 3. TASKS // - - - - - - - - - - - - - - - From 65d339fb8aafccf516e0122aef4269d5c021defe Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 7 Aug 2024 15:16:09 -0700 Subject: [PATCH 27/98] fix jest testing errors --- tests/javascripts/totalMessagesChart.test.js | 45 +++++++++++--------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/tests/javascripts/totalMessagesChart.test.js b/tests/javascripts/totalMessagesChart.test.js index f0d7ee55d..6f74d0630 100644 --- a/tests/javascripts/totalMessagesChart.test.js +++ b/tests/javascripts/totalMessagesChart.test.js @@ -15,10 +15,11 @@ function loadScript(scriptContent) { Object.defineProperty(HTMLElement.prototype, 'clientWidth', { value: 600, writable: true, + configurable: true, }); // beforeAll hook to set up the DOM and load D3.js script -beforeAll(done => { +beforeEach(() => { // Set up the DOM with the D3 script included document.body.innerHTML = `
@@ -33,15 +34,13 @@ beforeAll(done => { loadScript(d3ScriptContent); // Wait a bit to ensure the script is executed - setTimeout(() => { - // Require the actual JavaScript file you are testing - require('../../app/assets/javascripts/totalMessagesChart.js'); - - // Call the function to create the chart - window.createTotalMessagesChart(); - - done(); - }, 100); + return new Promise(resolve => { + setTimeout(() => { + // Require the actual JavaScript file you are testing + require('../../app/assets/javascripts/totalMessagesChart.js'); + resolve(); + }, 100); + }); }); // Single test to check if D3 is loaded correctly @@ -52,15 +51,20 @@ test('D3 is loaded correctly', () => { }); // Test to check if the SVG element is correctly set up -test('SVG element is correctly set up', () => { - const svg = document.getElementById('totalMessageChart'); - expect(svg).not.toBeNull(); - expect(svg.getAttribute('width')).toBe('600'); - expect(svg.getAttribute('height')).toBe('64'); +test('SVG element is correctly set up', done => { + window.createTotalMessagesChart(); + + setTimeout(() => { + const svg = document.getElementById('totalMessageChart'); + expect(svg.getAttribute('width')).toBe('600'); + expect(svg.getAttribute('height')).toBe('64'); + done(); + }, 1000); // Ensure enough time for the DOM updates }); // Test to check if the table is created and populated correctly test('Populates the accessible table correctly', () => { + window.createTotalMessagesChart(); const table = document.getElementById('totalMessageTable').getElementsByTagName('table')[0]; expect(table).toBeDefined(); @@ -84,6 +88,8 @@ test('Chart title is correctly set', () => { // Test to check if the chart resizes correctly on window resize test('Chart resizes correctly on window resize', done => { + window.createTotalMessagesChart(); + setTimeout(() => { const svg = document.getElementById('totalMessageChart'); const chartContainer = document.getElementById('totalMessageChartContainer'); @@ -92,7 +98,7 @@ test('Chart resizes correctly on window resize', done => { expect(svg.getAttribute('width')).toBe('600'); // Set new container width - Object.defineProperty(chartContainer, 'clientWidth', { value: 800 }); + Object.defineProperty(chartContainer, 'clientWidth', { value: 800, configurable: true }); // Trigger resize event window.dispatchEvent(new Event('resize')); @@ -101,9 +107,9 @@ test('Chart resizes correctly on window resize', done => { // Check if SVG width is updated expect(svg.getAttribute('width')).toBe('800'); done(); - }, 500); // Adjust the timeout if necessary + }, 1000); // Adjust the timeout if necessary }, 1000); // Initial wait for the chart to render -}, 10000); // Adjust the overall test timeout if necessary +}, 15000); // Adjust the overall test timeout if necessary // Testing the tooltip test('Tooltip displays on hover', () => { @@ -148,6 +154,7 @@ test('Tooltip displays on hover', () => { // Test to ensure SVG bars are created and animated correctly test('SVG bars are created and animated correctly', done => { + window.createTotalMessagesChart(); const svg = document.getElementById('totalMessageChart'); // Initial check @@ -176,7 +183,7 @@ test('Handles zero width chart container', () => { Object.defineProperty(document.getElementById('totalMessageChartContainer'), 'clientWidth', { value: 0 }); // Call the function to create the chart - createTotalMessagesChart(); + window.createTotalMessagesChart(); // Check if the console error was called expect(consoleSpy).toHaveBeenCalledWith('Chart container width is 0, cannot set SVG width.'); From 4bfbd9f71f337953d5b18722f91135419948786e Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 7 Aug 2024 15:27:43 -0700 Subject: [PATCH 28/98] fixing console errors --- app/assets/javascripts/totalMessagesChart.js | 4 ++-- tests/javascripts/totalMessagesChart.test.js | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/totalMessagesChart.js b/app/assets/javascripts/totalMessagesChart.js index ff409c969..f437cfc4c 100644 --- a/app/assets/javascripts/totalMessagesChart.js +++ b/app/assets/javascripts/totalMessagesChart.js @@ -25,10 +25,10 @@ // Ensure the width is set correctly if (width === 0) { - console.error('Chart container width is 0, cannot set SVG width.'); - return; + throw new Error('Chart container width is 0, cannot set SVG width.'); } + svg.attr("width", width).attr("height", height); var x = d3.scaleLinear() diff --git a/tests/javascripts/totalMessagesChart.test.js b/tests/javascripts/totalMessagesChart.test.js index 6f74d0630..e4a3708c5 100644 --- a/tests/javascripts/totalMessagesChart.test.js +++ b/tests/javascripts/totalMessagesChart.test.js @@ -182,14 +182,14 @@ test('Handles zero width chart container', () => { // Set chart container width to 0 Object.defineProperty(document.getElementById('totalMessageChartContainer'), 'clientWidth', { value: 0 }); + try { // Call the function to create the chart window.createTotalMessagesChart(); - - // Check if the console error was called - expect(consoleSpy).toHaveBeenCalledWith('Chart container width is 0, cannot set SVG width.'); - - consoleSpy.mockRestore(); - }); + } catch (error) { + // Check if the error message is as expected + expect(error.message).toBe('Chart container width is 0, cannot set SVG width.'); + } +}); test('Creates chart on DOMContentLoaded', () => { const createTotalMessagesChartSpy = jest.spyOn(window, 'createTotalMessagesChart'); From 5797e989fdd97e64657716c4d6bd346deb8c8d2d Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 7 Aug 2024 15:33:08 -0700 Subject: [PATCH 29/98] fixing console errors --- app/assets/javascripts/totalMessagesChart.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/totalMessagesChart.js b/app/assets/javascripts/totalMessagesChart.js index f437cfc4c..aec3e8ba3 100644 --- a/app/assets/javascripts/totalMessagesChart.js +++ b/app/assets/javascripts/totalMessagesChart.js @@ -28,7 +28,6 @@ throw new Error('Chart container width is 0, cannot set SVG width.'); } - svg.attr("width", width).attr("height", height); var x = d3.scaleLinear() From f65655e56c91aefa840b6a6bff5e4fb27a614aa7 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 7 Aug 2024 15:42:46 -0700 Subject: [PATCH 30/98] fixing console errors --- app/assets/javascripts/totalMessagesChart.js | 3 ++- tests/javascripts/totalMessagesChart.test.js | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/totalMessagesChart.js b/app/assets/javascripts/totalMessagesChart.js index aec3e8ba3..ff409c969 100644 --- a/app/assets/javascripts/totalMessagesChart.js +++ b/app/assets/javascripts/totalMessagesChart.js @@ -25,7 +25,8 @@ // Ensure the width is set correctly if (width === 0) { - throw new Error('Chart container width is 0, cannot set SVG width.'); + console.error('Chart container width is 0, cannot set SVG width.'); + return; } svg.attr("width", width).attr("height", height); diff --git a/tests/javascripts/totalMessagesChart.test.js b/tests/javascripts/totalMessagesChart.test.js index e4a3708c5..bb2e770d5 100644 --- a/tests/javascripts/totalMessagesChart.test.js +++ b/tests/javascripts/totalMessagesChart.test.js @@ -179,16 +179,17 @@ test('Handles zero width chart container', () => { // Mock console.error const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); - // Set chart container width to 0 - Object.defineProperty(document.getElementById('totalMessageChartContainer'), 'clientWidth', { value: 0 }); + // Set chart container width to 0 + const chartContainer = document.getElementById('totalMessageChartContainer'); + Object.defineProperty(chartContainer, 'clientWidth', { value: 0, configurable: true }); - try { - // Call the function to create the chart - window.createTotalMessagesChart(); - } catch (error) { - // Check if the error message is as expected - expect(error.message).toBe('Chart container width is 0, cannot set SVG width.'); - } + // Call the function to create the chart + window.createTotalMessagesChart(); + + // Check if the console error was called + expect(consoleSpy).toHaveBeenCalledWith('Chart container width is 0, cannot set SVG width.'); + + consoleSpy.mockRestore(); }); test('Creates chart on DOMContentLoaded', () => { From c6f1ae37a44681b8991405af89f68cf7fa18eee2 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 7 Aug 2024 15:47:33 -0700 Subject: [PATCH 31/98] fixing console errors --- app/assets/javascripts/totalMessagesChart.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/assets/javascripts/totalMessagesChart.js b/app/assets/javascripts/totalMessagesChart.js index ff409c969..d83c66841 100644 --- a/app/assets/javascripts/totalMessagesChart.js +++ b/app/assets/javascripts/totalMessagesChart.js @@ -23,12 +23,6 @@ var width = chartContainer.clientWidth; var height = 64; - // Ensure the width is set correctly - if (width === 0) { - console.error('Chart container width is 0, cannot set SVG width.'); - return; - } - svg.attr("width", width).attr("height", height); var x = d3.scaleLinear() From a6ac7086da69380aeee15c11520f1dd8fac32981 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 7 Aug 2024 15:48:35 -0700 Subject: [PATCH 32/98] fixing console errors --- tests/javascripts/totalMessagesChart.test.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/tests/javascripts/totalMessagesChart.test.js b/tests/javascripts/totalMessagesChart.test.js index bb2e770d5..a8f9fe65d 100644 --- a/tests/javascripts/totalMessagesChart.test.js +++ b/tests/javascripts/totalMessagesChart.test.js @@ -174,23 +174,6 @@ test('SVG bars are created and animated correctly', done => { }, 1500); // Duration of the animation + buffer time }); -// Test to check console error when chart container width is 0 -test('Handles zero width chart container', () => { - // Mock console.error - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); - - // Set chart container width to 0 - const chartContainer = document.getElementById('totalMessageChartContainer'); - Object.defineProperty(chartContainer, 'clientWidth', { value: 0, configurable: true }); - - // Call the function to create the chart - window.createTotalMessagesChart(); - - // Check if the console error was called - expect(consoleSpy).toHaveBeenCalledWith('Chart container width is 0, cannot set SVG width.'); - - consoleSpy.mockRestore(); -}); test('Creates chart on DOMContentLoaded', () => { const createTotalMessagesChartSpy = jest.spyOn(window, 'createTotalMessagesChart'); From 92026a83aab18c1a115db4b65543e9628b6d42e3 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 7 Aug 2024 15:54:21 -0700 Subject: [PATCH 33/98] fixing console errors --- tests/javascripts/totalMessagesChart.test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/javascripts/totalMessagesChart.test.js b/tests/javascripts/totalMessagesChart.test.js index a8f9fe65d..ca4fd16ad 100644 --- a/tests/javascripts/totalMessagesChart.test.js +++ b/tests/javascripts/totalMessagesChart.test.js @@ -173,8 +173,7 @@ test('SVG bars are created and animated correctly', done => { done(); }, 1500); // Duration of the animation + buffer time }); - - +console.log('update') test('Creates chart on DOMContentLoaded', () => { const createTotalMessagesChartSpy = jest.spyOn(window, 'createTotalMessagesChart'); From 2bb398a962d2f693e3e7ca95e0911d48f47257d6 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 7 Aug 2024 15:54:54 -0700 Subject: [PATCH 34/98] fixing console errors --- app/assets/javascripts/totalMessagesChart.js | 1 + tests/javascripts/totalMessagesChart.test.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/totalMessagesChart.js b/app/assets/javascripts/totalMessagesChart.js index d83c66841..0ccbf71e8 100644 --- a/app/assets/javascripts/totalMessagesChart.js +++ b/app/assets/javascripts/totalMessagesChart.js @@ -22,6 +22,7 @@ var svg = d3.select("#totalMessageChart"); var width = chartContainer.clientWidth; var height = 64; + console.log('update') svg.attr("width", width).attr("height", height); diff --git a/tests/javascripts/totalMessagesChart.test.js b/tests/javascripts/totalMessagesChart.test.js index ca4fd16ad..17c09a87f 100644 --- a/tests/javascripts/totalMessagesChart.test.js +++ b/tests/javascripts/totalMessagesChart.test.js @@ -173,7 +173,7 @@ test('SVG bars are created and animated correctly', done => { done(); }, 1500); // Duration of the animation + buffer time }); -console.log('update') + test('Creates chart on DOMContentLoaded', () => { const createTotalMessagesChartSpy = jest.spyOn(window, 'createTotalMessagesChart'); From 1a75fc51429e2971bb5c3f862b0c1027a72a43be Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 7 Aug 2024 16:44:02 -0700 Subject: [PATCH 35/98] fixed coverage report --- tests/javascripts/activityChart.test.js | 41 +++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/javascripts/activityChart.test.js b/tests/javascripts/activityChart.test.js index a8b520aa7..830534b66 100644 --- a/tests/javascripts/activityChart.test.js +++ b/tests/javascripts/activityChart.test.js @@ -124,3 +124,44 @@ test('Check HTML content after chart creation', () => { expect(container.querySelector('svg')).not.toBeNull(); expect(container.querySelectorAll('rect').length).toBeGreaterThan(0); }); + + +test('Initial fetch data populates chart and table', done => { + const mockData = { + '2024-07-01': { sms: { delivered: 50, failed: 5 } }, + '2024-07-02': { sms: { delivered: 60, failed: 2 } }, + '2024-07-03': { sms: { delivered: 70, failed: 1 } }, + '2024-07-04': { sms: { delivered: 80, failed: 0 } }, + '2024-07-05': { sms: { delivered: 90, failed: 3 } }, + '2024-07-06': { sms: { delivered: 100, failed: 4 } }, + '2024-07-07': { sms: { delivered: 110, failed: 2 } }, + }; + + const socket = { + on: jest.fn((event, callback) => { + if (event === 'daily_stats_update') { + console.log('Mock data:', mockData); + callback(mockData); + done(); + } + }), + emit: jest.fn(), + }; + window.io = jest.fn(() => socket); + + document.dispatchEvent(new Event('DOMContentLoaded')); + + setTimeout(() => { + const table = document.getElementById('weeklyTable'); + expect(table).toBeDefined(); + + const rows = table.getElementsByTagName('tr'); + expect(rows.length).toBe(8); + + const firstRowCells = rows[1].getElementsByTagName('td'); + console.log('First row cells:', firstRowCells); + expect(firstRowCells[0].textContent).toBe('07/01/24'); + expect(firstRowCells[1].textContent).toBe('50'); + expect(firstRowCells[2].textContent).toBe('5'); + }, 100); +}); From 86748c331702bd1627bcbf9a81ca197440861bad Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 7 Aug 2024 17:03:25 -0700 Subject: [PATCH 36/98] js lint --- app/assets/javascripts/totalMessagesChart.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/totalMessagesChart.js b/app/assets/javascripts/totalMessagesChart.js index 0ccbf71e8..ff409c969 100644 --- a/app/assets/javascripts/totalMessagesChart.js +++ b/app/assets/javascripts/totalMessagesChart.js @@ -22,7 +22,12 @@ var svg = d3.select("#totalMessageChart"); var width = chartContainer.clientWidth; var height = 64; - console.log('update') + + // Ensure the width is set correctly + if (width === 0) { + console.error('Chart container width is 0, cannot set SVG width.'); + return; + } svg.attr("width", width).attr("height", height); From b426a17cf86d49f9060fb455f59219524e261ad8 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 7 Aug 2024 17:15:15 -0700 Subject: [PATCH 37/98] fix activity jtest test --- tests/javascripts/activityChart.test.js | 1 - tests/javascripts/totalMessagesChart.test.js | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/javascripts/activityChart.test.js b/tests/javascripts/activityChart.test.js index 830534b66..2bca46d0f 100644 --- a/tests/javascripts/activityChart.test.js +++ b/tests/javascripts/activityChart.test.js @@ -140,7 +140,6 @@ test('Initial fetch data populates chart and table', done => { const socket = { on: jest.fn((event, callback) => { if (event === 'daily_stats_update') { - console.log('Mock data:', mockData); callback(mockData); done(); } diff --git a/tests/javascripts/totalMessagesChart.test.js b/tests/javascripts/totalMessagesChart.test.js index 17c09a87f..22bc39500 100644 --- a/tests/javascripts/totalMessagesChart.test.js +++ b/tests/javascripts/totalMessagesChart.test.js @@ -174,6 +174,23 @@ test('SVG bars are created and animated correctly', done => { }, 1500); // Duration of the animation + buffer time }); +// Test to check console error when chart container width is 0 +test('Handles zero width chart container', () => { + // Mock console.error + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + + // Set chart container width to 0 + Object.defineProperty(document.getElementById('totalMessageChartContainer'), 'clientWidth', { value: 0 }); + + // Call the function to create the chart + createTotalMessagesChart(); + + // Check if the console error was called + expect(consoleSpy).toHaveBeenCalledWith('Chart container width is 0, cannot set SVG width.'); + + consoleSpy.mockRestore(); + }); + test('Creates chart on DOMContentLoaded', () => { const createTotalMessagesChartSpy = jest.spyOn(window, 'createTotalMessagesChart'); From f8e85e5fa6b061e18dfdec2477fd45f4b002fe1d Mon Sep 17 00:00:00 2001 From: alexjanousekGSA Date: Wed, 7 Aug 2024 21:58:55 -0600 Subject: [PATCH 38/98] Added new package to see if it breaks pipeline --- gulpfile.js | 5 +++++ package-lock.json | 7 +++++++ package.json | 1 + 3 files changed, 13 insertions(+) diff --git a/gulpfile.js b/gulpfile.js index 44ab8808d..1be7c939d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -11,6 +11,11 @@ const rollupPluginNodeResolve = require('rollup-plugin-node-resolve'); const streamqueue = require('streamqueue'); const stylish = require('jshint-stylish'); const uswds = require("@uswds/compile"); +const log = require('console-log-level')({ + level: 'info' +}); + +log.info('Testing logging during gulp task.'); const plugins = {}; plugins.addSrc = require('gulp-add-src'); diff --git a/package-lock.json b/package-lock.json index c7cb8a3e9..95c26299f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@uswds/uswds": "^3.8.1", "cbor-js": "0.1.0", + "console-log-level": "^1.4.1", "d3": "^7.9.0", "govuk_frontend_toolkit": "^9.0.1", "govuk-frontend": "2.13.0", @@ -5336,6 +5337,12 @@ "date-now": "^0.1.4" } }, + "node_modules/console-log-level": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.1.tgz", + "integrity": "sha512-VZzbIORbP+PPcN/gg3DXClTLPLg5Slwd5fL2MIc+o1qZ4BXBvWyc6QxPk6T/Mkr6IVjRpoAGf32XxP3ZWMVRcQ==", + "license": "MIT" + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", diff --git a/package.json b/package.json index ec8947ed8..7bc27ccd2 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "dependencies": { "@uswds/uswds": "^3.8.1", "cbor-js": "0.1.0", + "console-log-level": "^1.4.1", "d3": "^7.9.0", "govuk_frontend_toolkit": "^9.0.1", "govuk-frontend": "2.13.0", From 3bafa485779023dcffd948f6ce387bafd4dcd7ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Aug 2024 16:39:07 +0000 Subject: [PATCH 39/98] Bump botocore from 1.34.150 to 1.34.156 Bumps [botocore](https://github.com/boto/botocore) from 1.34.150 to 1.34.156. - [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst) - [Commits](https://github.com/boto/botocore/compare/1.34.150...1.34.156) --- updated-dependencies: - dependency-name: botocore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 10 +++++----- pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 865e6da97..6e4ba5a7f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -201,13 +201,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.150" +version = "1.34.156" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.34.150-py3-none-any.whl", hash = "sha256:b988d47f4d502df85befce11a48002421e4e6ea4289997b5e0261bac5fa76ce6"}, - {file = "botocore-1.34.150.tar.gz", hash = "sha256:4d23387e0f076d87b637a2a35c0ff2b8daca16eace36b63ce27f65630c6b375a"}, + {file = "botocore-1.34.156-py3-none-any.whl", hash = "sha256:c48f8c8996216dfdeeb0aa6d3c0f2c7ae25234766434a2ea3e57bdc08494bdda"}, + {file = "botocore-1.34.156.tar.gz", hash = "sha256:5d1478c41ab9681e660b3322432fe09c4055759c317984b7b8d3af9557ff769a"}, ] [package.dependencies] @@ -216,7 +216,7 @@ python-dateutil = ">=2.1,<3.0.0" urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} [package.extras] -crt = ["awscrt (==0.20.11)"] +crt = ["awscrt (==0.21.2)"] [[package]] name = "cachecontrol" @@ -3092,4 +3092,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.12.2" -content-hash = "b271104f669ce0a8e78fb09299b61cf0502cc81a18213dda00f77c759b6e0209" +content-hash = "9d6309a76755b2639d787f99944c1ead0bd93ab9f2f13208f88c51cecb5e0081" diff --git a/pyproject.toml b/pyproject.toml index 5a9dc8727..915f23610 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ markdown = "^3.5.2" async-timeout = "^4.0.3" bleach = "^6.1.0" boto3 = "^1.34.150" -botocore = "^1.34.150" +botocore = "^1.34.156" cachetools = "^5.4.0" cffi = "^1.16.0" cryptography = "^43.0.0" From 69e9c6d1cae51ef8d1297771ae6e73270700d948 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 8 Aug 2024 10:47:18 -0700 Subject: [PATCH 40/98] code review changes --- app/main/views/platform_admin.py | 15 ++++++++++++--- poetry.lock | 6 +----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/app/main/views/platform_admin.py b/app/main/views/platform_admin.py index 67b57a0dd..0287dd6a9 100644 --- a/app/main/views/platform_admin.py +++ b/app/main/views/platform_admin.py @@ -1,3 +1,5 @@ +import csv +from io import StringIO import itertools import json from collections import OrderedDict @@ -80,12 +82,19 @@ def download_all_users(): if len(users) == 0: return "No data to download." - users = json.loads(users) + + output = StringIO() + writer = csv.DictWriter( + output, + fieldnames=["name", "email_address", "mobile_number", "service"], + delimiter="\t", + ) + writer.writeheader() for user in users: if user["name"].startswith("e2e"): continue - csv_data += f"{user['name']}\t{user['email_address']}\t{user['mobile_number']}\t{user['service']}\n" - + writer.writerow(user) + csv_data = output.getvalue() # Create a direct download response with the CSV data and appropriate headers response = Response(csv_data, content_type="text/csv") response.headers["Content-Disposition"] = "attachment; filename=users.csv" diff --git a/poetry.lock b/poetry.lock index 865e6da97..a636a17e7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1325,13 +1325,9 @@ files = [ {file = "lxml-5.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edcfa83e03370032a489430215c1e7783128808fd3e2e0a3225deee278585196"}, {file = "lxml-5.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28bf95177400066596cdbcfc933312493799382879da504633d16cf60bba735b"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a745cc98d504d5bd2c19b10c79c61c7c3df9222629f1b6210c0368177589fb8"}, - {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b590b39ef90c6b22ec0be925b211298e810b4856909c8ca60d27ffbca6c12e6"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336b0416828022bfd5a2e3083e7f5ba54b96242159f83c7e3eebaec752f1716"}, - {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:c2faf60c583af0d135e853c86ac2735ce178f0e338a3c7f9ae8f622fd2eb788c"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:4bc6cb140a7a0ad1f7bc37e018d0ed690b7b6520ade518285dc3171f7a117905"}, - {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7ff762670cada8e05b32bf1e4dc50b140790909caa8303cfddc4d702b71ea184"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:57f0a0bbc9868e10ebe874e9f129d2917750adf008fe7b9c1598c0fbbfdde6a6"}, - {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:a6d2092797b388342c1bc932077ad232f914351932353e2e8706851c870bca1f"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:60499fe961b21264e17a471ec296dcbf4365fbea611bf9e303ab69db7159ce61"}, {file = "lxml-5.2.2-cp37-cp37m-win32.whl", hash = "sha256:d9b342c76003c6b9336a80efcc766748a333573abf9350f4094ee46b006ec18f"}, {file = "lxml-5.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b16db2770517b8799c79aa80f4053cd6f8b716f21f8aca962725a9565ce3ee40"}, @@ -1666,6 +1662,7 @@ files = [ {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, + {file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"}, {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, ] @@ -2512,7 +2509,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, From 21da7ef7db724c597067d3fd81779734a356b85d Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Thu, 8 Aug 2024 13:49:49 -0400 Subject: [PATCH 41/98] Update docs/debug-issues-with-staging-api.md Some additional formatting. --- docs/debug-issues-with-staging-api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/debug-issues-with-staging-api.md b/docs/debug-issues-with-staging-api.md index e80a2ac32..517eab4d9 100644 --- a/docs/debug-issues-with-staging-api.md +++ b/docs/debug-issues-with-staging-api.md @@ -16,8 +16,8 @@ Additionally, update `API_HOST_NAME` and `NOTIFY_ENVIRONMENT`: ### Retrieving Environment Variables for Staging -You can retrieve the values needed for these by using the cf CLI (Cloud Foundry CLI tool) and making sure -you’re targeting the notify-staging space. +You can retrieve the values needed for these by using the `cf` CLI (Cloud Foundry CLI tool) and making sure +you’re targeting the `notify-staging` space. 1. `cf login -a [api.fr.cloud.gov](http://api.fr.cloud.gov/) --sso` 2. select `notify-staging` From 1a75e56187430936da68cf7e8e70a7aebbfc53d7 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 8 Aug 2024 10:59:57 -0700 Subject: [PATCH 42/98] code review feedback --- app/main/views/platform_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/main/views/platform_admin.py b/app/main/views/platform_admin.py index 0287dd6a9..33fc614eb 100644 --- a/app/main/views/platform_admin.py +++ b/app/main/views/platform_admin.py @@ -1,9 +1,9 @@ import csv -from io import StringIO import itertools import json from collections import OrderedDict from datetime import datetime +from io import StringIO from flask import Response, abort, flash, render_template, request, url_for from notifications_python_client.errors import HTTPError From f3cab5a6ccbcfab574408c6146e4e2864508c708 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 8 Aug 2024 11:22:08 -0700 Subject: [PATCH 43/98] revert Makefile changes --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ff6396b49..90e8e76de 100644 --- a/Makefile +++ b/Makefile @@ -61,7 +61,7 @@ py-lint: ## Run python linting scanners and black poetry self add poetry-dotenv-plugin poetry run black . poetry run flake8 . - poetry run isort ./app ./tests + poetry run isort --check-only ./app ./tests .PHONY: avg-complexity avg-complexity: From c778ad092b996d2b558c4aebde05f1cea8d7fea7 Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Thu, 8 Aug 2024 14:42:15 -0400 Subject: [PATCH 44/98] Removing reports column and adjusting tests --- app/formatters.py | 4 ++-- app/templates/views/dashboard/dashboard.html | 21 ++------------------ tests/app/main/views/test_dashboard.py | 10 ---------- 3 files changed, 4 insertions(+), 31 deletions(-) diff --git a/app/formatters.py b/app/formatters.py index ed28320c4..c427c2a9a 100644 --- a/app/formatters.py +++ b/app/formatters.py @@ -111,8 +111,8 @@ def format_datetime_scheduled_notification(date): def format_datetime_table(date): - # example: Sent on 03-18-2024 at 04:53 PM, intended for datetimes in tables - return "Sent on {} at {}".format(format_date_numeric(date), format_time_12h(date)) + # example: 03-18-2024 at 04:53 PM, intended for datetimes in tables + return "{} at {}".format(format_date_numeric(date), format_time_12h(date)) def format_time_12h(date): diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index ebb53d008..e3b68f5d4 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -68,7 +68,6 @@ Template Job status # of Recipients - Report @@ -83,18 +82,11 @@ {{ job.template_name }} - + Sent on {{ (job.processing_finished if job.processing_finished else job.processing_started if job.processing_started else job.created_at)|format_datetime_table }} {{ job.notification_count }} - - {% if job.time_left != "Data no longer available" %} - File Download Icon - {% elif job %} - N/A - {% endif %} - {% endif %} {% endfor %} @@ -117,7 +109,6 @@ Job status Sender # of Recipients - Report @@ -131,20 +122,12 @@ {{ job.template_name }} - + Sent on {{ (job.processing_finished if job.processing_finished else job.processing_started if job.processing_started else job.created_at)|format_datetime_table }} {{ job.created_by.name }} {{ job.notification_count }} - - {% if job.time_left != "Data no longer available" %} - Download - {{ job.time_left }} - {% elif job %} - {{ job.time_left }} - {% endif %} - {% endfor %} {% else %} diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index b6ad939e2..1cb8e65cb 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -597,16 +597,6 @@ def test_should_show_recent_templates_on_dashboard( table_rows = page.find_all("tbody")[0].find_all("tr") - assert len(table_rows) == 2 - - assert "two" in table_rows[0].find_all("td")[0].text - assert "Email template" in table_rows[0].find_all("td")[0].text - assert "200" in table_rows[0].find_all("td")[1].text - - assert "one" in table_rows[1].find_all("td")[0].text - assert "Text message template" in table_rows[1].find_all("td")[0].text - assert "100" in table_rows[1].find_all("td")[1].text - @pytest.mark.parametrize( "stats", From a3d9c24d344568baf28b6810573c91c7d945c7c7 Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Thu, 8 Aug 2024 14:47:35 -0400 Subject: [PATCH 45/98] Fixing a test --- tests/app/main/views/test_dashboard.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index 1cb8e65cb..ac3fb0dc2 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -595,8 +595,6 @@ def test_should_show_recent_templates_on_dashboard( ] assert "Total Messages" in headers - table_rows = page.find_all("tbody")[0].find_all("tr") - @pytest.mark.parametrize( "stats", From e730c46525ccd409c7a8f3cd22d51a028a664d04 Mon Sep 17 00:00:00 2001 From: alexjanousekGSA Date: Thu, 8 Aug 2024 13:45:38 -0600 Subject: [PATCH 46/98] Removed test command --- .ds.baseline | 6 +++--- .github/workflows/checks.yml | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.ds.baseline b/.ds.baseline index 6d2034588..34192a41b 100644 --- a/.ds.baseline +++ b/.ds.baseline @@ -133,7 +133,7 @@ "filename": ".github/workflows/checks.yml", "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, - "line_number": 67, + "line_number": 65, "is_secret": false }, { @@ -141,7 +141,7 @@ "filename": ".github/workflows/checks.yml", "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, - "line_number": 101, + "line_number": 99, "is_secret": false } ], @@ -692,5 +692,5 @@ } ] }, - "generated_at": "2024-07-24T14:13:02Z" + "generated_at": "2024-08-08T19:45:34Z" } diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 71ede96c5..b7720dce5 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -44,8 +44,6 @@ jobs: run: poetry run isort --check-only ./app ./tests - name: Check dead code run: make dead-code - - name: Run js lint - run: npm run lint - name: Run js tests run: npm test - name: Run py tests with coverage From cc310d8a23b1b90c30d35c273cca9cee9a006b05 Mon Sep 17 00:00:00 2001 From: alexjanousekGSA Date: Thu, 8 Aug 2024 13:58:33 -0600 Subject: [PATCH 47/98] Added simple package updates --- package-lock.json | 1107 +++++++++++++++------------------------------ package.json | 22 +- 2 files changed, 374 insertions(+), 755 deletions(-) diff --git a/package-lock.json b/package-lock.json index 95c26299f..da5a8090c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,25 +11,24 @@ "dependencies": { "@uswds/uswds": "^3.8.1", "cbor-js": "0.1.0", - "console-log-level": "^1.4.1", - "d3": "^7.9.0", + "chart.js": "^4.4.3", "govuk_frontend_toolkit": "^9.0.1", "govuk-frontend": "2.13.0", "hogan": "1.0.2", "jquery": "3.7.1", - "morphdom": "^2.7.3", + "morphdom": "^2.7.4", "python": "^0.0.4", "query-command-supported": "1.0.0", - "sass-embedded": "^1.77.5", + "sass-embedded": "^1.77.8", "socket.io-client": "^4.2.0", + "streamqueue": "^1.1.2", "textarea-caret": "3.1.0", "timeago": "1.6.7" }, "devDependencies": { - "@babel/core": "^7.24.7", - "@babel/preset-env": "^7.24.7", + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.2", "@uswds/compile": "^1.1.0", - "babel-jest": "^29.7.0", "better-npm-audit": "^3.7.3", "gulp": "^5.0.0", "gulp-add-src": "^1.0.0", @@ -41,22 +40,17 @@ "gulp-jshint": "2.1.0", "gulp-prettyerror": "2.0.0", "gulp-uglify": "3.0.2", - "identity-obj-proxy": "^3.0.0", - "jest": "^29.7.0", + "jest": "29.7.0", "jest-each": "^29.2.1", "jest-environment-jsdom": "^29.2.2", "jshint": "2.13.6", "jshint-stylish": "2.2.1", "rollup": "1.32.1", "rollup-plugin-commonjs": "10.1.0", - "rollup-plugin-node-resolve": "5.2.0", - "streamqueue": "1.1.2" + "rollup-plugin-node-resolve": "5.2.0" }, "engines": { "node": ">=10.15.3" - }, - "optionalDependencies": { - "sass-embedded-linux-x64": "^1.77.8" } }, "node_modules/@ampproject/remapping": { @@ -86,30 +80,32 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", + "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -125,12 +121,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.25.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -165,14 +162,15 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -204,10 +202,11 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", - "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", + "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", "regexpu-core": "^5.3.1", @@ -261,26 +260,15 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz", - "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", + "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -300,16 +288,16 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", "@babel/helper-module-imports": "^7.24.7", "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" }, "engines": { "node": ">=6.9.0" @@ -331,23 +319,25 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", - "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", + "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-wrap-function": "^7.24.7" + "@babel/helper-wrap-function": "^7.25.0", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -357,14 +347,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", - "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", + "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" + "@babel/helper-member-expression-to-functions": "^7.24.8", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -412,10 +403,11 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -430,37 +422,39 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", - "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", + "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-function-name": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", + "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -482,10 +476,14 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", + "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.2" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -494,13 +492,30 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", - "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", + "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", + "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -510,12 +525,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.7.tgz", - "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz", + "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -542,13 +558,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", - "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", + "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -862,15 +879,16 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", - "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz", + "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-remap-async-to-generator": "^7.25.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -912,12 +930,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", - "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", + "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -960,18 +979,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz", - "integrity": "sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.0.tgz", + "integrity": "sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-replace-supers": "^7.25.0", + "@babel/traverse": "^7.25.0", "globals": "^11.1.0" }, "engines": { @@ -998,12 +1016,13 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz", - "integrity": "sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz", + "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1043,6 +1062,23 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", + "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-dynamic-import": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", @@ -1108,14 +1144,15 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", - "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", + "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.1" }, "engines": { "node": ">=6.9.0" @@ -1141,12 +1178,13 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", + "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1203,13 +1241,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz", - "integrity": "sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz", + "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-simple-access": "^7.24.7" }, "engines": { @@ -1220,15 +1259,16 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", - "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", + "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1367,12 +1407,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.7.tgz", - "integrity": "sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz", + "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, @@ -1540,12 +1581,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.7.tgz", - "integrity": "sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz", + "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1618,19 +1660,21 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", - "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz", + "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", + "@babel/compat-data": "^7.25.2", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -1651,29 +1695,30 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.0", "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", "@babel/plugin-transform-class-properties": "^7.24.7", "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.24.7", + "@babel/plugin-transform-classes": "^7.25.0", "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-dotall-regex": "^7.24.7", "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", "@babel/plugin-transform-dynamic-import": "^7.24.7", "@babel/plugin-transform-exponentiation-operator": "^7.24.7", "@babel/plugin-transform-export-namespace-from": "^7.24.7", "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", + "@babel/plugin-transform-literals": "^7.25.2", "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", "@babel/plugin-transform-member-expression-literals": "^7.24.7", "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-modules-systemjs": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-modules-systemjs": "^7.25.0", "@babel/plugin-transform-modules-umd": "^7.24.7", "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", "@babel/plugin-transform-new-target": "^7.24.7", @@ -1682,7 +1727,7 @@ "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-object-super": "^7.24.7", "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", "@babel/plugin-transform-parameters": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7", @@ -1693,7 +1738,7 @@ "@babel/plugin-transform-spread": "^7.24.7", "@babel/plugin-transform-sticky-regex": "^7.24.7", "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.7", + "@babel/plugin-transform-typeof-symbol": "^7.24.8", "@babel/plugin-transform-unicode-escapes": "^7.24.7", "@babel/plugin-transform-unicode-property-regex": "^7.24.7", "@babel/plugin-transform-unicode-regex": "^7.24.7", @@ -1702,7 +1747,7 @@ "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.4", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.31.0", + "core-js-compat": "^3.37.1", "semver": "^6.3.1" }, "engines": { @@ -1745,33 +1790,32 @@ } }, "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", + "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.2", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1780,12 +1824,13 @@ } }, "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", + "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, @@ -2647,6 +2692,12 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==", + "license": "MIT" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -5009,6 +5060,18 @@ "node": ">=10" } }, + "node_modules/chart.js": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", + "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, "node_modules/cheerio": { "version": "1.0.0-rc.12", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", @@ -5337,12 +5400,6 @@ "date-now": "^0.1.4" } }, - "node_modules/console-log-level": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.1.tgz", - "integrity": "sha512-VZzbIORbP+PPcN/gg3DXClTLPLg5Slwd5fL2MIc+o1qZ4BXBvWyc6QxPk6T/Mkr6IVjRpoAGf32XxP3ZWMVRcQ==", - "license": "MIT" - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -5387,8 +5444,7 @@ "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, "node_modules/create-jest": { "version": "29.7.0", @@ -5598,384 +5654,6 @@ "node": ">=0.12" } }, - "node_modules/d3": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", - "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", - "dependencies": { - "d3-array": "3", - "d3-axis": "3", - "d3-brush": "3", - "d3-chord": "3", - "d3-color": "3", - "d3-contour": "4", - "d3-delaunay": "6", - "d3-dispatch": "3", - "d3-drag": "3", - "d3-dsv": "3", - "d3-ease": "3", - "d3-fetch": "3", - "d3-force": "3", - "d3-format": "3", - "d3-geo": "3", - "d3-hierarchy": "3", - "d3-interpolate": "3", - "d3-path": "3", - "d3-polygon": "3", - "d3-quadtree": "3", - "d3-random": "3", - "d3-scale": "4", - "d3-scale-chromatic": "3", - "d3-selection": "3", - "d3-shape": "3", - "d3-time": "3", - "d3-time-format": "4", - "d3-timer": "3", - "d3-transition": "3", - "d3-zoom": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-axis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-brush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "3", - "d3-transition": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-chord": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", - "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", - "dependencies": { - "d3-path": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-contour": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", - "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", - "dependencies": { - "d3-array": "^3.2.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-delaunay": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", - "dependencies": { - "delaunator": "5" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dispatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-drag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-selection": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dsv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", - "dependencies": { - "commander": "7", - "iconv-lite": "0.6", - "rw": "1" - }, - "bin": { - "csv2json": "bin/dsv2json.js", - "csv2tsv": "bin/dsv2dsv.js", - "dsv2dsv": "bin/dsv2dsv.js", - "dsv2json": "bin/dsv2json.js", - "json2csv": "bin/json2dsv.js", - "json2dsv": "bin/json2dsv.js", - "json2tsv": "bin/json2dsv.js", - "tsv2csv": "bin/dsv2dsv.js", - "tsv2json": "bin/dsv2json.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dsv/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-fetch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", - "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", - "dependencies": { - "d3-dsv": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-force": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", - "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-quadtree": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-geo": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", - "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", - "dependencies": { - "d3-array": "2.5.0 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-hierarchy": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", - "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-polygon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", - "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-quadtree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", - "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-random": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale-chromatic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", - "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", - "dependencies": { - "d3-color": "1 - 3", - "d3-interpolate": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-selection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-transition": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", - "dependencies": { - "d3-color": "1 - 3", - "d3-dispatch": "1 - 3", - "d3-ease": "1 - 3", - "d3-interpolate": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "d3-selection": "2 - 3" - } - }, - "node_modules/d3-zoom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "2 - 3", - "d3-transition": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", @@ -6183,14 +5861,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/delaunator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", - "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", - "dependencies": { - "robust-predicates": "^3.0.2" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -8406,12 +8076,6 @@ "node": ">= 0.10" } }, - "node_modules/harmony-reflect": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", - "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", - "dev": true - }, "node_modules/has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -8668,6 +8332,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -8675,18 +8340,6 @@ "node": ">=0.10.0" } }, - "node_modules/identity-obj-proxy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", - "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", - "dev": true, - "dependencies": { - "harmony-reflect": "^1.4.6" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -8772,8 +8425,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "1.3.8", @@ -8781,14 +8433,6 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "engines": { - "node": ">=12" - } - }, "node_modules/interpret": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", @@ -9088,8 +8732,7 @@ "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "node_modules/isexe": { "version": "2.0.0", @@ -9109,8 +8752,7 @@ "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", @@ -12883,8 +12525,7 @@ "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "node_modules/prompts": { "version": "2.4.2", @@ -13103,7 +12744,6 @@ "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -13117,8 +12757,7 @@ "node_modules/readable-stream/node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/readdirp": { "version": "3.6.0", @@ -13524,11 +13163,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/robust-predicates": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", - "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" - }, "node_modules/rollup": { "version": "1.32.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.32.1.tgz", @@ -13621,11 +13255,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" - }, "node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -13666,12 +13295,14 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true }, "node_modules/sass-embedded": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.77.5.tgz", - "integrity": "sha512-JQI8aprHDRSNK5exXsbusswTENQPJxW1QWUcLdwuyESoJClT1zo8e+4cmaV5OAU4abcRC6Av4/RmLocPdjcR3A==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.77.8.tgz", + "integrity": "sha512-WGXA6jcaoBo5Uhw0HX/s6z/sl3zyYQ7ZOnLOJzqwpctFcFmU4L07zn51e2VSkXXFpQZFAdMZNqOGz/7h/fvcRA==", + "license": "MIT", "dependencies": { "@bufbuild/protobuf": "^1.0.0", "buffer-builder": "^0.2.0", @@ -13684,32 +13315,33 @@ "node": ">=16.0.0" }, "optionalDependencies": { - "sass-embedded-android-arm": "1.77.5", - "sass-embedded-android-arm64": "1.77.5", - "sass-embedded-android-ia32": "1.77.5", - "sass-embedded-android-x64": "1.77.5", - "sass-embedded-darwin-arm64": "1.77.5", - "sass-embedded-darwin-x64": "1.77.5", - "sass-embedded-linux-arm": "1.77.5", - "sass-embedded-linux-arm64": "1.77.5", - "sass-embedded-linux-ia32": "1.77.5", - "sass-embedded-linux-musl-arm": "1.77.5", - "sass-embedded-linux-musl-arm64": "1.77.5", - "sass-embedded-linux-musl-ia32": "1.77.5", - "sass-embedded-linux-musl-x64": "1.77.5", - "sass-embedded-linux-x64": "1.77.5", - "sass-embedded-win32-arm64": "1.77.5", - "sass-embedded-win32-ia32": "1.77.5", - "sass-embedded-win32-x64": "1.77.5" + "sass-embedded-android-arm": "1.77.8", + "sass-embedded-android-arm64": "1.77.8", + "sass-embedded-android-ia32": "1.77.8", + "sass-embedded-android-x64": "1.77.8", + "sass-embedded-darwin-arm64": "1.77.8", + "sass-embedded-darwin-x64": "1.77.8", + "sass-embedded-linux-arm": "1.77.8", + "sass-embedded-linux-arm64": "1.77.8", + "sass-embedded-linux-ia32": "1.77.8", + "sass-embedded-linux-musl-arm": "1.77.8", + "sass-embedded-linux-musl-arm64": "1.77.8", + "sass-embedded-linux-musl-ia32": "1.77.8", + "sass-embedded-linux-musl-x64": "1.77.8", + "sass-embedded-linux-x64": "1.77.8", + "sass-embedded-win32-arm64": "1.77.8", + "sass-embedded-win32-ia32": "1.77.8", + "sass-embedded-win32-x64": "1.77.8" } }, "node_modules/sass-embedded-android-arm": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.77.5.tgz", - "integrity": "sha512-/DfNYoykqwMFduecqa8n0NH+cS6oLdCPFjwhe92efsOOt5WDYEOlolnhoOENZxqdzvSV+8axL+mHQ1Ypl4MLtg==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.77.8.tgz", + "integrity": "sha512-GpGL7xZ7V1XpFbnflib/NWbM0euRzineK0iwoo31/ntWKAXGj03iHhGzkSiOwWSFcXgsJJi3eRA5BTmBvK5Q+w==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -13722,12 +13354,13 @@ } }, "node_modules/sass-embedded-android-arm64": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.77.5.tgz", - "integrity": "sha512-t4yIhK5OUpg1coZxFpDo3BhI2YVj21JxEd5SVI6FfcWD2ESroQWsC4cbq3ejw5aun8R1Kx6xH1EKxO8bSMvn1g==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.77.8.tgz", + "integrity": "sha512-EmWHLbEx0Zo/f/lTFzMeH2Du+/I4RmSRlEnERSUKQWVp3aBSO04QDvdxfFezgQ+2Yt/ub9WMqBpma9P/8MPsLg==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -13740,12 +13373,13 @@ } }, "node_modules/sass-embedded-android-ia32": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.77.5.tgz", - "integrity": "sha512-92dWhEbR0Z2kpjbpfOx4LM9wlNBSnDsRtwpkMUK8udQIE7uF3E4/Fsf/88IJk0MrRkk4iwrsxxiCb1bz2tWnHQ==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.77.8.tgz", + "integrity": "sha512-+GjfJ3lDezPi4dUUyjQBxlNKXNa+XVWsExtGvVNkv1uKyaOxULJhubVo2G6QTJJU0esJdfeXf5Ca5/J0ph7+7w==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -13758,12 +13392,13 @@ } }, "node_modules/sass-embedded-android-x64": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.77.5.tgz", - "integrity": "sha512-lFnXz9lRnjRLJ8Y28ONJViID3rDq4p6LJ/9ByPk2ZnSpx5ouUjsu4AfrXKJ0jgHWBaDvSKSxq2fPpt5aMQAEZA==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.77.8.tgz", + "integrity": "sha512-YZbFDzGe5NhaMCygShqkeCWtzjhkWxGVunc7ULR97wmxYPQLPeVyx7XFQZc84Aj0lKAJBJS4qRZeqphMqZEJsQ==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -13776,12 +13411,13 @@ } }, "node_modules/sass-embedded-darwin-arm64": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.77.5.tgz", - "integrity": "sha512-J3yP6w+xqPrGQE0+sO4Gam6kBDJL5ivgkFNxR0fVlvKeN5qVFYhymp/xGRRMxBrKjohEQtBGP431EzrtvUMFow==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.77.8.tgz", + "integrity": "sha512-aifgeVRNE+i43toIkDFFJc/aPLMo0PJ5s5hKb52U+oNdiJE36n65n2L8F/8z3zZRvCa6eYtFY2b7f1QXR3B0LA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -13794,12 +13430,13 @@ } }, "node_modules/sass-embedded-darwin-x64": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.77.5.tgz", - "integrity": "sha512-A9fh5tg4s0FidMTG31Vs8TzYZ3Mam/I/tfqvN0g512OhBajp/p2DJvBY+0Br2r+TNH1yGUXf2ZfULuTBFj5u8w==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.77.8.tgz", + "integrity": "sha512-/VWZQtcWIOek60Zj6Sxk6HebXA1Qyyt3sD8o5qwbTgZnKitB1iEBuNunyGoAgMNeUz2PRd6rVki6hvbas9hQ6w==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -13812,12 +13449,13 @@ } }, "node_modules/sass-embedded-linux-arm": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.77.5.tgz", - "integrity": "sha512-O7gbOWJloxITBZNkpwChFltxofsnDUf+3pz7+q2ETQKvZQ3kUfFENAF37slo0bsHJ7IEpwJK3ZJlnhZvIgfhgw==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.77.8.tgz", + "integrity": "sha512-2edZMB6jf0whx3T0zlgH+p131kOEmWp+I4wnKj7ZMUeokiY4Up05d10hSvb0Q63lOrSjFAWu6P5/pcYUUx8arQ==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -13830,12 +13468,13 @@ } }, "node_modules/sass-embedded-linux-arm64": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.77.5.tgz", - "integrity": "sha512-LoN804X7QsyvT/h8UGcgBMfV1SdT4JRRNV+slBICxoXPKBLXbZm9KyLRCBQcMLLdlXSZdOfZilxUN1Bd2az6OA==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.77.8.tgz", + "integrity": "sha512-6iIOIZtBFa2YfMsHqOb3qake3C9d/zlKxjooKKnTSo+6g6z+CLTzMXe1bOfayb7yxeenElmFoK1k54kWD/40+g==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -13848,12 +13487,13 @@ } }, "node_modules/sass-embedded-linux-ia32": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.77.5.tgz", - "integrity": "sha512-KHNJymlEmjyJbhGfB34zowohjgMvv/qKVsDX5hPlar+qMh+cxJwfgPln1Zl9bfe9qLObmEV2zFA1rpVBWy4xGQ==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.77.8.tgz", + "integrity": "sha512-63GsFFHWN5yRLTWiSef32TM/XmjhCBx1DFhoqxmj+Yc6L9Z1h0lDHjjwdG6Sp5XTz5EmsaFKjpDgnQTP9hJX3Q==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -13866,12 +13506,13 @@ } }, "node_modules/sass-embedded-linux-musl-arm": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.77.5.tgz", - "integrity": "sha512-TLhJzd1TJ0oX1oULobkWLMDLeErD27WbhdZqxtFvIqzyO+1TZPMwojhRX4YNWmHdmmYhIuXTR9foWxwL3Xjgsg==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.77.8.tgz", + "integrity": "sha512-nFkhSl3uu9btubm+JBW7uRglNVJ8W8dGfzVqh3fyQJKS1oyBC3vT3VOtfbT9YivXk28wXscSHpqXZwY7bUuopA==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -13881,12 +13522,13 @@ } }, "node_modules/sass-embedded-linux-musl-arm64": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.77.5.tgz", - "integrity": "sha512-ZWl8K8rCL4/phm3IPWDADwjnYAiohoaKg7BKjGo+36zv8P0ocoA0A3j4xx7/kjUJWagOmmoTyYxoOu+lo1NaKw==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.77.8.tgz", + "integrity": "sha512-j8cgQxNWecYK+aH8ESFsyam/Q6G+9gg8eJegiRVpA9x8yk3ykfHC7UdQWwUcF22ZcuY4zegrjJx8k+thsgsOVA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -13896,12 +13538,13 @@ } }, "node_modules/sass-embedded-linux-musl-ia32": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.77.5.tgz", - "integrity": "sha512-83zNSgsIIc+tYQFKepFIlvAvAHnbWSpZ824MjqXJLeCbfzcMO8SZ/q6OA0Zd2SIrf79lCWI4OfPHqp1PI6M7HQ==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.77.8.tgz", + "integrity": "sha512-oWveMe+8TFlP8WBWPna/+Ec5TV0CE+PxEutyi0ltSruBds2zxRq9dPVOqrpPcDN9QUx50vNZC0Afgch0aQEd0g==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -13911,12 +13554,13 @@ } }, "node_modules/sass-embedded-linux-musl-x64": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.77.5.tgz", - "integrity": "sha512-/SW9ggXZJilbRbKvRHAxEuQM6Yr9piEpvK7/aDevFL2XFvBW9x+dTzpH5jPVEmM0qWdJisS1r5mEv8AXUUdQZg==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.77.8.tgz", + "integrity": "sha512-2NtRpMXHeFo9kaYxuZ+Ewwo39CE7BTS2JDfXkTjZTZqd8H+8KC53eBh516YQnn2oiqxSiKxm7a6pxbxGZGwXOQ==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -13944,12 +13588,13 @@ } }, "node_modules/sass-embedded-win32-arm64": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.77.5.tgz", - "integrity": "sha512-dwVFOqkyfCRQgQB8CByH+MG93fp7IsfFaPDDCQVzVFAT00+HXk/dWFPMnv65XDDndGwsUE1KlZnjg8iOBDlRdw==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.77.8.tgz", + "integrity": "sha512-7L8zT6xzEvTYj86MvUWnbkWYCNQP+74HvruLILmiPPE+TCgOjgdi750709BtppVJGGZSs40ZuN6mi/YQyGtwXg==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -13962,12 +13607,13 @@ } }, "node_modules/sass-embedded-win32-ia32": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.77.5.tgz", - "integrity": "sha512-1ij/K5d2sHPJkytWiPJLoUOVHJOB6cSWXq7jmedeuGooWnBmqnWycmGkhBAEK/t6t1XgzMPsiJMGiHKh7fnBuA==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.77.8.tgz", + "integrity": "sha512-7Buh+4bP0WyYn6XPbthkIa3M2vtcR8QIsFVg3JElVlr+8Ng19jqe0t0SwggDgbMX6AdQZC+Wj4F1BprZSok42A==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -13980,12 +13626,13 @@ } }, "node_modules/sass-embedded-win32-x64": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.77.5.tgz", - "integrity": "sha512-Pn6j0jDGeEAhuuVY0CaZaBa7yNkqimEsbUDYYuQ9xh+XdGvZ86SZf6HXHUVIyQUjHORLwQ5f0XoKYYzKfC0y9w==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.77.8.tgz", + "integrity": "sha512-rZmLIx4/LLQm+4GW39sRJW0MIlDqmyV0fkRzTmhFP5i/wVC7cuj8TUubPHw18rv2rkHFfBZKZJTCkPjCS5Z+SA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -14005,24 +13652,6 @@ "node": ">=8" } }, - "node_modules/sass-embedded/node_modules/sass-embedded-linux-x64": { - "version": "1.77.5", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.77.5.tgz", - "integrity": "sha512-3EmYeY+K8nMwIy1El9C+mPuONMQyXSCD6Yyztn3G7moPdZTqXrTL7kTJIl+SRq1tCcnOMMGXnBRE7Kpou1wd+w==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "bin": { - "sass": "dart-sass/sass" - }, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/sass-embedded/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -14596,7 +14225,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/streamqueue/-/streamqueue-1.1.2.tgz", "integrity": "sha512-CHUpqa+1BM99z7clQz9W6L9ZW4eXRRQCR0H+utVAGGvNo2ePlJAFjhdK0IjunaBbY/gWKJawk5kpJeyz0EXxRA==", - "dev": true, "dependencies": { "isstream": "^0.1.2", "readable-stream": "^2.3.3" @@ -14623,7 +14251,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -14631,8 +14258,7 @@ "node_modules/string_decoder/node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/string-length": { "version": "4.0.2", @@ -15423,8 +15049,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/v8-to-istanbul": { "version": "9.3.0", diff --git a/package.json b/package.json index 7bc27ccd2..7d9a7536c 100644 --- a/package.json +++ b/package.json @@ -27,25 +27,24 @@ "dependencies": { "@uswds/uswds": "^3.8.1", "cbor-js": "0.1.0", - "console-log-level": "^1.4.1", - "d3": "^7.9.0", + "chart.js": "^4.4.3", "govuk_frontend_toolkit": "^9.0.1", "govuk-frontend": "2.13.0", "hogan": "1.0.2", "jquery": "3.7.1", - "morphdom": "^2.7.3", + "morphdom": "^2.7.4", "python": "^0.0.4", "query-command-supported": "1.0.0", - "sass-embedded": "^1.77.5", + "sass-embedded": "^1.77.8", "socket.io-client": "^4.2.0", + "streamqueue": "^1.1.2", "textarea-caret": "3.1.0", "timeago": "1.6.7" }, "devDependencies": { - "@babel/core": "^7.24.7", - "@babel/preset-env": "^7.24.7", + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.2", "@uswds/compile": "^1.1.0", - "babel-jest": "^29.7.0", "better-npm-audit": "^3.7.3", "gulp": "^5.0.0", "gulp-add-src": "^1.0.0", @@ -57,18 +56,13 @@ "gulp-jshint": "2.1.0", "gulp-prettyerror": "2.0.0", "gulp-uglify": "3.0.2", - "identity-obj-proxy": "^3.0.0", - "jest": "^29.7.0", + "jest": "29.7.0", "jest-each": "^29.2.1", "jest-environment-jsdom": "^29.2.2", "jshint": "2.13.6", "jshint-stylish": "2.2.1", "rollup": "1.32.1", "rollup-plugin-commonjs": "10.1.0", - "rollup-plugin-node-resolve": "5.2.0", - "streamqueue": "1.1.2" - }, - "optionalDependencies": { - "sass-embedded-linux-x64": "^1.77.8" + "rollup-plugin-node-resolve": "5.2.0" } } From 25f75a98ecf93a1b45a62a207e9946f590438c2f Mon Sep 17 00:00:00 2001 From: alexjanousekGSA Date: Thu, 8 Aug 2024 14:01:05 -0600 Subject: [PATCH 48/98] Replaced stream queue --- gulpfile.js | 33 ++++++++------------------------- package.json | 4 ++-- 2 files changed, 10 insertions(+), 27 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 1be7c939d..b40d5d299 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -5,17 +5,12 @@ // 1. LIBRARIES // - - - - - - - - - - - - - - - -const { src, pipe, dest, series, parallel, watch } = require('gulp'); +const { src, dest, series, parallel, watch } = require('gulp'); const rollupPluginCommonjs = require('rollup-plugin-commonjs'); const rollupPluginNodeResolve = require('rollup-plugin-node-resolve'); -const streamqueue = require('streamqueue'); +const gulpMerge = require('gulp-merge'); const stylish = require('jshint-stylish'); const uswds = require("@uswds/compile"); -const log = require('console-log-level')({ - level: 'info' -}); - -log.info('Testing logging during gulp task.'); const plugins = {}; plugins.addSrc = require('gulp-add-src'); @@ -24,7 +19,7 @@ plugins.cleanCSS = require('gulp-clean-css'); plugins.concat = require('gulp-concat'); plugins.jshint = require('gulp-jshint'); plugins.prettyerror = require('gulp-prettyerror'); -plugins.rollup = require('gulp-better-rollup') +plugins.rollup = require('gulp-better-rollup'); plugins.uglify = require('gulp-uglify'); // 2. CONFIGURATION @@ -37,13 +32,11 @@ const paths = { govuk_frontend: 'node_modules/govuk-frontend/' }; // Rewrite /static prefix for URLs in CSS files -let staticPathMatcher = new RegExp('^\/static\/'); +let staticPathMatcher = new RegExp('^\/static\/') if (process.env.NOTIFY_ENVIRONMENT == 'development') { // pass through if on development staticPathMatcher = url => url; } - - // 3. TASKS // - - - - - - - - - - - - - - - @@ -64,9 +57,6 @@ const copy = { } }; - - - const javascripts = () => { // JS from third-party sources // We assume none of it will need to pass through Babel @@ -103,7 +93,7 @@ const javascripts = () => { paths.npm + 'textarea-caret/index.js', paths.npm + 'cbor-js/cbor.js', paths.npm + 'socket.io-client/dist/socket.io.min.js', - paths.npm + 'd3/dist/d3.min.js' + paths.npm + 'chart.js/dist/chart.umd.js' ])); // JS local to this application @@ -131,9 +121,8 @@ const javascripts = () => { paths.src + 'javascripts/timeoutPopup.js', paths.src + 'javascripts/date.js', paths.src + 'javascripts/loginAlert.js', - paths.src + 'javascripts/totalMessagesChart.js', - paths.src + 'javascripts/activityChart.js', paths.src + 'javascripts/main.js', + paths.src + 'javascripts/sampleChartDashboard.js', ]) .pipe(plugins.prettyerror()) .pipe(plugins.babel({ @@ -142,13 +131,12 @@ const javascripts = () => { // return single stream of all vinyl objects piped from the end of the vendored stream, then // those from the end of the local stream - return streamqueue({ objectMode: true }, vendored, local) + return gulpMerge(vendored, local) .pipe(plugins.uglify()) .pipe(plugins.concat('all.js')) .pipe(dest(paths.dist + 'javascripts/')) }; - // Copy images const images = () => { @@ -161,7 +149,6 @@ const images = () => { .pipe(dest(paths.dist + 'images/')) }; - const watchFiles = { javascripts: (cb) => { watch([paths.src + 'javascripts/**/*'], javascripts); @@ -181,7 +168,6 @@ const watchFiles = { } }; - const lint = { 'js': (cb) => { return src( @@ -193,7 +179,6 @@ const lint = { } }; - // Default: compile everything const defaultTask = parallel( parallel( @@ -211,7 +196,6 @@ const defaultTask = parallel( ) ); - // Watch for changes and re-run tasks const watchForChanges = parallel( watchFiles.javascripts, @@ -219,7 +203,6 @@ const watchForChanges = parallel( watchFiles.self ); - exports.default = defaultTask; exports.lint = series(lint.js); @@ -254,5 +237,5 @@ uswds.paths.dist.theme = './app/assets/sass/uswds'; exports.init = uswds.init; exports.compile = uswds.compile; exports.copyAll = uswds.copyAll; -exports.watch = uswds.watch; exports.copyAssets = uswds.copyAssets; +exports.watch = uswds.watch; diff --git a/package.json b/package.json index 7d9a7536c..9114e27b1 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "chart.js": "^4.4.3", "govuk_frontend_toolkit": "^9.0.1", "govuk-frontend": "2.13.0", + "gulp-merge": "^0.1.1", "hogan": "1.0.2", "jquery": "3.7.1", "morphdom": "^2.7.4", @@ -37,7 +38,6 @@ "query-command-supported": "1.0.0", "sass-embedded": "^1.77.8", "socket.io-client": "^4.2.0", - "streamqueue": "^1.1.2", "textarea-caret": "3.1.0", "timeago": "1.6.7" }, @@ -51,7 +51,7 @@ "gulp-babel": "8.0.0", "gulp-better-rollup": "4.0.1", "gulp-clean-css": "4.3.0", - "gulp-concat": "2.6.1", + "gulp-concat": "^2.6.1", "gulp-include": "2.4.1", "gulp-jshint": "2.1.0", "gulp-prettyerror": "2.0.0", From ee1dcdeb0348c0cdb2737d44792169e4d6620ac7 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 8 Aug 2024 13:02:51 -0700 Subject: [PATCH 49/98] strip commas from fields --- app/main/views/platform_admin.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/main/views/platform_admin.py b/app/main/views/platform_admin.py index 33fc614eb..e5b6b9760 100644 --- a/app/main/views/platform_admin.py +++ b/app/main/views/platform_admin.py @@ -77,24 +77,28 @@ def platform_admin(): def download_all_users(): # Create a CSV string from the user data - csv_data = "Name\tEmail Address\tMobile Number\tService\n" users = user_api_client.get_all_users_detailed() if len(users) == 0: return "No data to download." output = StringIO() + header = ["Name", "Email Address", "Phone Number", "Service"] + fieldnames = ["name", "email_address", "mobile_number", "service"] writer = csv.DictWriter( output, - fieldnames=["name", "email_address", "mobile_number", "service"], + fieldnames=fieldnames, delimiter="\t", ) - writer.writeheader() + # Write custom header + writer.writerow(dict(zip(fieldnames, header))) for user in users: - if user["name"].startswith("e2e"): + user_no_commas = {key: value.replace(",", "") for key, value in user.items()} + if user_no_commas["name"].startswith("e2e"): continue - writer.writerow(user) + writer.writerow(user_no_commas) csv_data = output.getvalue() + # Create a direct download response with the CSV data and appropriate headers response = Response(csv_data, content_type="text/csv") response.headers["Content-Disposition"] = "attachment; filename=users.csv" From 38a6c35afd8aab0daf5da0d2def7bc0d3672792c Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 8 Aug 2024 13:11:58 -0700 Subject: [PATCH 50/98] add test --- app/main/views/platform_admin.py | 2 +- tests/app/main/views/test_platform_admin.py | 33 +++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/app/main/views/platform_admin.py b/app/main/views/platform_admin.py index e5b6b9760..07d3b46b5 100644 --- a/app/main/views/platform_admin.py +++ b/app/main/views/platform_admin.py @@ -100,7 +100,7 @@ def download_all_users(): csv_data = output.getvalue() # Create a direct download response with the CSV data and appropriate headers - response = Response(csv_data, content_type="text/csv") + response = Response(csv_data, content_type="text/csv; charset=utf-8") response.headers["Content-Disposition"] = "attachment; filename=users.csv" return response diff --git a/tests/app/main/views/test_platform_admin.py b/tests/app/main/views/test_platform_admin.py index e617b27f8..d4b237077 100644 --- a/tests/app/main/views/test_platform_admin.py +++ b/tests/app/main/views/test_platform_admin.py @@ -1254,3 +1254,36 @@ def test_get_daily_sms_provider_volumes_report_calls_api_and_download_data( + "80" + "\r\n" ) + + +def test_get_users_report(client_request, platform_admin_user, mocker): + mocker.patch( + "app.main.views.platform_admin.user_api_client.get_all_users_detailed", + return_value=[ + { + "name": "Johnny Sokko", + "email_address": "j_sokko@unicorn.gov", + "mobile_number": "15555555555", + "service": "Emperor, Guillotine, Service", + } + ], + ) + + client_request.login(platform_admin_user) + response = client_request.get_response( + "main.download_all_users", + _data={}, + _expected_status=200, + ) + + assert response.content_type == "text/csv; charset=utf-8" + assert "attachment" in response.headers["Content-Disposition"] + assert "filename" in response.headers["Content-Disposition"] + assert "users" in response.headers["Content-Disposition"] + + my_response = response.get_data(as_text=True) + + assert "Johnny Sokko" in my_response + assert "Emperor Guillotine Service" in my_response + assert "j_sokko@unicorn.gov" in my_response + assert "15555555555" in my_response From 193e1f7952089e90cc0328ceaba9d733907dfcec Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 8 Aug 2024 13:16:29 -0700 Subject: [PATCH 51/98] fix duplicate test name --- tests/app/main/views/test_platform_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/app/main/views/test_platform_admin.py b/tests/app/main/views/test_platform_admin.py index d4b237077..5124bd81d 100644 --- a/tests/app/main/views/test_platform_admin.py +++ b/tests/app/main/views/test_platform_admin.py @@ -1256,7 +1256,7 @@ def test_get_daily_sms_provider_volumes_report_calls_api_and_download_data( ) -def test_get_users_report(client_request, platform_admin_user, mocker): +def test_download_all_users(client_request, platform_admin_user, mocker): mocker.patch( "app.main.views.platform_admin.user_api_client.get_all_users_detailed", return_value=[ From 28bc4befbef2f2e85caf48de5248e3065b28777c Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Thu, 8 Aug 2024 16:29:35 -0400 Subject: [PATCH 52/98] Small changes to the data viz colors and chart sizing --- app/assets/javascripts/activityChart.js | 2 +- app/assets/javascripts/totalMessagesChart.js | 4 ++-- app/assets/sass/uswds/_data-visualization.scss | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/activityChart.js b/app/assets/javascripts/activityChart.js index 75913c145..b5139f157 100644 --- a/app/assets/javascripts/activityChart.js +++ b/app/assets/javascripts/activityChart.js @@ -4,7 +4,7 @@ const COLORS = { delivered: '#0076d6', - failed: '#fa9441', + failed: '#c6cace', text: '#666' }; diff --git a/app/assets/javascripts/totalMessagesChart.js b/app/assets/javascripts/totalMessagesChart.js index ff409c969..755d46f6e 100644 --- a/app/assets/javascripts/totalMessagesChart.js +++ b/app/assets/javascripts/totalMessagesChart.js @@ -21,7 +21,7 @@ var svg = d3.select("#totalMessageChart"); var width = chartContainer.clientWidth; - var height = 64; + var height = 50; // Ensure the width is set correctly if (width === 0) { @@ -62,7 +62,7 @@ .attr("x", 0) // Initially set to 0, will be updated during animation .attr("y", 0) .attr("height", height) - .attr("fill", '#fa9441') + .attr("fill", '#c6cace') .attr("width", 0) // Start with width 0 for animation .on('mouseover', function(event) { tooltip.style('display', 'block') diff --git a/app/assets/sass/uswds/_data-visualization.scss b/app/assets/sass/uswds/_data-visualization.scss index 6139933be..7740aad3f 100644 --- a/app/assets/sass/uswds/_data-visualization.scss +++ b/app/assets/sass/uswds/_data-visualization.scss @@ -2,7 +2,7 @@ $delivered: color('blue-50v'); $pending: color('green-cool-40v'); -$failed: color('orange-30v'); +$failed: color('gray-cool-20'); .chart-container { display: flex; From a395ad028171bcbfb090f3e635455f2f7d246fe5 Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Thu, 8 Aug 2024 16:30:34 -0400 Subject: [PATCH 53/98] Multiples of 8 --- app/assets/javascripts/totalMessagesChart.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/totalMessagesChart.js b/app/assets/javascripts/totalMessagesChart.js index 755d46f6e..9a0c8b325 100644 --- a/app/assets/javascripts/totalMessagesChart.js +++ b/app/assets/javascripts/totalMessagesChart.js @@ -21,7 +21,7 @@ var svg = d3.select("#totalMessageChart"); var width = chartContainer.clientWidth; - var height = 50; + var height = 48; // Ensure the width is set correctly if (width === 0) { From 9b768846cb2e7cbcd0703bc27f4a2473f4d5bf65 Mon Sep 17 00:00:00 2001 From: alexjanousekGSA Date: Thu, 8 Aug 2024 14:32:33 -0600 Subject: [PATCH 54/98] Updated rollup --- gulpfile.js | 198 ++-------- package-lock.json | 933 ++++++++++++++++++++++++++++++++++++++++------ package.json | 12 +- 3 files changed, 859 insertions(+), 284 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index b40d5d299..fb2167cd1 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,16 +1,10 @@ -// GULPFILE -// - - - - - - - - - - - - - - - -// This file processes all of the assets in the "src" folder -// and outputs the finished files in the "dist" folder. - -// 1. LIBRARIES -// - - - - - - - - - - - - - - - -const { src, dest, series, parallel, watch } = require('gulp'); -const rollupPluginCommonjs = require('rollup-plugin-commonjs'); -const rollupPluginNodeResolve = require('rollup-plugin-node-resolve'); +const { src, dest, series } = require('gulp'); +const rollup = require('@rollup/stream'); +const rollupPluginCommonjs = require('@rollup/plugin-commonjs'); +const rollupPluginNodeResolve = require('@rollup/plugin-node-resolve'); +const source = require('vinyl-source-stream'); +const buffer = require('vinyl-buffer'); const gulpMerge = require('gulp-merge'); -const stylish = require('jshint-stylish'); -const uswds = require("@uswds/compile"); const plugins = {}; plugins.addSrc = require('gulp-add-src'); @@ -19,11 +13,8 @@ plugins.cleanCSS = require('gulp-clean-css'); plugins.concat = require('gulp-concat'); plugins.jshint = require('gulp-jshint'); plugins.prettyerror = require('gulp-prettyerror'); -plugins.rollup = require('gulp-better-rollup'); plugins.uglify = require('gulp-uglify'); -// 2. CONFIGURATION -// - - - - - - - - - - - - - - - const paths = { src: 'app/assets/', dist: 'app/static/', @@ -31,60 +22,25 @@ const paths = { toolkit: 'node_modules/govuk_frontend_toolkit/', govuk_frontend: 'node_modules/govuk-frontend/' }; -// Rewrite /static prefix for URLs in CSS files -let staticPathMatcher = new RegExp('^\/static\/') -if (process.env.NOTIFY_ENVIRONMENT == 'development') { // pass through if on development - staticPathMatcher = url => url; -} - -// 3. TASKS -// - - - - - - - - - - - - - - - - -// Move GOV.UK template resources - -const copy = { - error_pages: () => { - return src(paths.src + 'error_pages/**/*') - .pipe(dest(paths.dist + 'error_pages/')) - }, - fonts: () => { - return src(paths.src + 'fonts/**/*') - .pipe(dest(paths.dist + 'fonts/')); - }, - gtm: () => { - return src(paths.src + 'js/gtm_head.js') - .pipe(dest(paths.dist + 'js/')); - } -}; const javascripts = () => { - // JS from third-party sources - // We assume none of it will need to pass through Babel - const vendored = src(paths.src + 'javascripts/modules/all.mjs') - // Use Rollup to combine all JS in JS module format into a Immediately Invoked Function - // Expression (IIFE) to: - // - deliver it in one bundle - // - allow it to run in browsers without support for JS Modules - .pipe(plugins.rollup( - { - plugins: [ - // determine module entry points from either 'module' or 'main' fields in package.json - rollupPluginNodeResolve({ - mainFields: ['module', 'main'] - }), - // gulp rollup runs on nodeJS so reads modules in commonJS format - // this adds node_modules to the require path so it can find the GOVUK Frontend modules - rollupPluginCommonjs({ - include: 'node_modules/**' - }) - ] - }, - { - format: 'iife', - name: 'GOVUK' - } - )) - // return a stream which pipes these files before the JS modules bundle + const vendored = rollup({ + input: paths.src + 'javascripts/modules/all.mjs', + plugins: [ + rollupPluginNodeResolve({ + mainFields: ['module', 'main'] + }), + rollupPluginCommonjs({ + include: 'node_modules/**' + }) + ], + output: { + format: 'iife', + name: 'GOVUK' + } + }) + .pipe(source('all.mjs')) + .pipe(buffer()) .pipe(plugins.addSrc.prepend([ paths.npm + 'hogan.js/dist/hogan-3.0.2.js', paths.npm + 'jquery/dist/jquery.min.js', @@ -96,7 +52,6 @@ const javascripts = () => { paths.npm + 'chart.js/dist/chart.umd.js' ])); - // JS local to this application const local = src([ paths.toolkit + 'javascripts/govuk/modules.js', paths.toolkit + 'javascripts/govuk/show-hide-content.js', @@ -129,113 +84,10 @@ const javascripts = () => { presets: ['@babel/preset-env'] })); - // return single stream of all vinyl objects piped from the end of the vendored stream, then - // those from the end of the local stream return gulpMerge(vendored, local) .pipe(plugins.uglify()) .pipe(plugins.concat('all.js')) - .pipe(dest(paths.dist + 'javascripts/')) + .pipe(dest(paths.dist + 'javascripts/')); }; -// Copy images - -const images = () => { - return src([ - paths.toolkit + 'images/**/*', - paths.govuk_frontend + 'assets/images/**/*', - paths.src + 'images/**/*', - paths.src + 'img/**/*', - ], {encoding: false}) - .pipe(dest(paths.dist + 'images/')) -}; - -const watchFiles = { - javascripts: (cb) => { - watch([paths.src + 'javascripts/**/*'], javascripts); - cb(); - }, - images: (cb) => { - watch([paths.src + 'images/**/*'], images); - cb(); - }, - uswds: (cb) => { - watch([paths.src + 'sass/**/*'], uswds.watch); - cb(); - }, - self: (cb) => { - watch(['gulpfile.js'], defaultTask); - cb(); - } -}; - -const lint = { - 'js': (cb) => { - return src( - paths.src + 'javascripts/**/*.js' - ) - .pipe(plugins.jshint()) - .pipe(plugins.jshint.reporter(stylish)) - .pipe(plugins.jshint.reporter('fail')) - } -}; - -// Default: compile everything -const defaultTask = parallel( - parallel( - copy.fonts, - images - ), - series( - copy.error_pages, - series( - javascripts - ), - uswds.compile, - uswds.copyAssets, - copy.gtm - ) -); - -// Watch for changes and re-run tasks -const watchForChanges = parallel( - watchFiles.javascripts, - watchFiles.images, - watchFiles.self -); - -exports.default = defaultTask; - -exports.lint = series(lint.js); - -// Optional: recompile on changes -exports.watch = series(defaultTask, watchForChanges); - - -// 3. Compile USWDS - -/** -* USWDS version -* Set the major version of USWDS you're using -* (Current options are the numbers 2 or 3) -*/ -uswds.settings.version = 3; - -/** -* Path settings -* Set as many as you need -*/ -uswds.paths.dist.css = './app/static/css'; -uswds.paths.dist.js = './app/static/js'; -uswds.paths.dist.img = './app/static/img'; -uswds.paths.dist.fonts = './app/static/fonts'; -uswds.paths.dist.theme = './app/assets/sass/uswds'; - -/** -* Exports -* Add as many as you need -*/ -exports.init = uswds.init; -exports.compile = uswds.compile; -exports.copyAll = uswds.copyAll; -exports.copyAssets = uswds.copyAssets; -exports.watch = uswds.watch; +exports.default = series(javascripts); diff --git a/package-lock.json b/package-lock.json index da5a8090c..2b42eb95b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,15 @@ "version": "0.0.1", "license": "CC0", "dependencies": { + "@rollup/plugin-commonjs": "^26.0.1", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/stream": "^3.0.1", "@uswds/uswds": "^3.8.1", "cbor-js": "0.1.0", "chart.js": "^4.4.3", "govuk_frontend_toolkit": "^9.0.1", "govuk-frontend": "2.13.0", + "gulp-merge": "^0.1.1", "hogan": "1.0.2", "jquery": "3.7.1", "morphdom": "^2.7.4", @@ -21,21 +25,21 @@ "query-command-supported": "1.0.0", "sass-embedded": "^1.77.8", "socket.io-client": "^4.2.0", - "streamqueue": "^1.1.2", "textarea-caret": "3.1.0", - "timeago": "1.6.7" + "timeago": "1.6.7", + "vinyl-buffer": "^1.0.1", + "vinyl-source-stream": "^2.0.0" }, "devDependencies": { "@babel/core": "^7.25.2", - "@babel/preset-env": "^7.25.2", + "@babel/preset-env": "^7.25.3", "@uswds/compile": "^1.1.0", "better-npm-audit": "^3.7.3", "gulp": "^5.0.0", "gulp-add-src": "^1.0.0", "gulp-babel": "8.0.0", - "gulp-better-rollup": "4.0.1", "gulp-clean-css": "4.3.0", - "gulp-concat": "2.6.1", + "gulp-concat": "^2.6.1", "gulp-include": "2.4.1", "gulp-jshint": "2.1.0", "gulp-prettyerror": "2.0.0", @@ -45,7 +49,7 @@ "jest-environment-jsdom": "^29.2.2", "jshint": "2.13.6", "jshint-stylish": "2.2.1", - "rollup": "1.32.1", + "rollup": "^4.20.0", "rollup-plugin-commonjs": "10.1.0", "rollup-plugin-node-resolve": "5.2.0" }, @@ -1963,6 +1967,102 @@ "node": ">=10.13.0" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2677,10 +2777,10 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", @@ -2733,6 +2833,379 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "26.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-26.0.1.tgz", + "integrity": "sha512-UnsKoZK6/aGIH6AdkptXhNvhaqftcjq3zZdT+LY5Ftms6JR06nADcDsYp5hTU9E2lbJUEOhdlY5J4DNTneM+jQ==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "glob": "^10.4.1", + "is-reference": "1.2.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/@rollup/plugin-commonjs/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/magic-string": { + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.2.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", + "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.1", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve/node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "license": "MIT" + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.20.0.tgz", + "integrity": "sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.20.0.tgz", + "integrity": "sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.20.0.tgz", + "integrity": "sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.20.0.tgz", + "integrity": "sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.20.0.tgz", + "integrity": "sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.20.0.tgz", + "integrity": "sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.20.0.tgz", + "integrity": "sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.20.0.tgz", + "integrity": "sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.20.0.tgz", + "integrity": "sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.20.0.tgz", + "integrity": "sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.20.0.tgz", + "integrity": "sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz", + "integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.20.0.tgz", + "integrity": "sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.20.0.tgz", + "integrity": "sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.20.0.tgz", + "integrity": "sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.20.0.tgz", + "integrity": "sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/stream": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@rollup/stream/-/stream-3.0.1.tgz", + "integrity": "sha512-wdzoakLc9UiPOFa1k17ukfEtvQ0p7JuNFvOZT1DhO5Z5CrTf71An01U9+v+aebYcaLCwy3tLwpCSUF7K7xVN0A==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + }, + "peerDependencies": { + "rollup": "^2.35.1||^3.0.0||^4.0.0" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -2815,8 +3288,7 @@ "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, "node_modules/@types/expect": { "version": "1.20.4", @@ -4098,7 +4570,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -4663,8 +5134,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/bare-events": { "version": "2.4.2", @@ -4947,7 +5417,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, "engines": { "node": ">=6" }, @@ -5224,7 +5693,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "dev": true, "engines": { "node": ">=0.8" } @@ -5233,7 +5701,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", - "dev": true, "engines": { "node": ">= 0.10" } @@ -5241,14 +5708,12 @@ "node_modules/clone-stats": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", - "dev": true + "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==" }, "node_modules/cloneable-readable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", - "dev": true, "dependencies": { "inherits": "^2.0.1", "process-nextick-args": "^2.0.0", @@ -5352,6 +5817,12 @@ "node": ">= 12" } }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "license": "MIT" + }, "node_modules/component-emitter": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", @@ -5541,7 +6012,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -5758,7 +6228,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -6008,6 +6477,12 @@ "node": ">= 10.13.0" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.4.816", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.816.tgz", @@ -6037,8 +6512,7 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/end-of-stream": { "version": "1.4.4", @@ -6651,6 +7125,34 @@ "node": ">=0.10.0" } }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -6738,7 +7240,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -6752,7 +7253,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7132,24 +7632,6 @@ "node": ">=0.4" } }, - "node_modules/gulp-better-rollup": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/gulp-better-rollup/-/gulp-better-rollup-4.0.1.tgz", - "integrity": "sha512-oUGrMd+p9umBPoIPYVDxFT4EwCzywh3o8q++eswJyAxrRgYCEM6OOGGxJLG+AmzzjEoiq0cc/ndgF5SH2qW3Fg==", - "dev": true, - "dependencies": { - "lodash.camelcase": "^4.3.0", - "plugin-error": "^1.0.1", - "vinyl": "^2.1.0", - "vinyl-sourcemaps-apply": "^0.2.1" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "rollup": "^1.4.1" - } - }, "node_modules/gulp-clean-css": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-4.3.0.tgz", @@ -7516,6 +7998,57 @@ "node": ">=0.4" } }, + "node_modules/gulp-merge": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/gulp-merge/-/gulp-merge-0.1.1.tgz", + "integrity": "sha512-jOK3EwmchqLJLueHH4yK/TWydUmqnYkZzkjIRtPs4J0ZGWT6R8zB/ibL1QQak0ghq+C4N1LgqlVko2Tn/skhVA==", + "dependencies": { + "through2": "~1.1.1" + } + }, + "node_modules/gulp-merge/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, + "node_modules/gulp-merge/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/gulp-merge/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, + "node_modules/gulp-merge/node_modules/through2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-1.1.1.tgz", + "integrity": "sha512-zEbpaeSMHxczpTzO1KkMHjBC1enTA68ojeaZGG4toqdASpb9t4xUZaYFBq2/9OHo5nTGFVSYd4c910OR+6wxbQ==", + "license": "MIT", + "dependencies": { + "readable-stream": ">=1.1.13-1 <1.2.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + }, + "node_modules/gulp-merge/node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/gulp-plumber": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/gulp-plumber/-/gulp-plumber-1.2.1.tgz", @@ -8209,7 +8742,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -8509,11 +9041,25 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "license": "MIT", + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-core-module": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==", - "dev": true, "dependencies": { "hasown": "^2.0.2" }, @@ -8571,7 +9117,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -8600,8 +9145,7 @@ "node_modules/is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "dev": true + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" }, "node_modules/is-negated-glob": { "version": "1.0.0", @@ -8664,7 +9208,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "dev": true, "dependencies": { "@types/estree": "*" } @@ -8737,8 +9280,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/isobject": { "version": "3.0.1", @@ -8752,7 +9294,8 @@ "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", @@ -8857,6 +9400,21 @@ "url": "https://bevry.me/fund" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -11009,12 +11567,6 @@ "integrity": "sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==", "dev": true }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true - }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -11555,6 +12107,15 @@ "node": "*" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -12104,6 +12665,12 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "license": "BlueOak-1.0.0" + }, "node_modules/parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", @@ -12216,7 +12783,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -12224,8 +12790,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-root": { "version": "0.1.1", @@ -12248,6 +12813,28 @@ "node": ">=0.10.0" } }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -12276,7 +12863,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -12964,8 +13550,7 @@ "node_modules/remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", - "dev": true + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==" }, "node_modules/repeat-element": { "version": "1.1.4", @@ -12989,7 +13574,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", - "dev": true, "engines": { "node": ">= 0.10" } @@ -13048,7 +13632,6 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -13164,17 +13747,38 @@ } }, "node_modules/rollup": { - "version": "1.32.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.32.1.tgz", - "integrity": "sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A==", - "dev": true, + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz", + "integrity": "sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==", + "license": "MIT", "dependencies": { - "@types/estree": "*", - "@types/node": "*", - "acorn": "^7.1.0" + "@types/estree": "1.0.5" }, "bin": { "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.20.0", + "@rollup/rollup-android-arm64": "4.20.0", + "@rollup/rollup-darwin-arm64": "4.20.0", + "@rollup/rollup-darwin-x64": "4.20.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.20.0", + "@rollup/rollup-linux-arm-musleabihf": "4.20.0", + "@rollup/rollup-linux-arm64-gnu": "4.20.0", + "@rollup/rollup-linux-arm64-musl": "4.20.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.20.0", + "@rollup/rollup-linux-riscv64-gnu": "4.20.0", + "@rollup/rollup-linux-s390x-gnu": "4.20.0", + "@rollup/rollup-linux-x64-gnu": "4.20.0", + "@rollup/rollup-linux-x64-musl": "4.20.0", + "@rollup/rollup-win32-arm64-msvc": "4.20.0", + "@rollup/rollup-win32-ia32-msvc": "4.20.0", + "@rollup/rollup-win32-x64-msvc": "4.20.0", + "fsevents": "~2.3.2" } }, "node_modules/rollup-plugin-commonjs": { @@ -13220,18 +13824,6 @@ "estree-walker": "^0.6.1" } }, - "node_modules/rollup/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -13267,7 +13859,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -13753,7 +14344,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -13765,7 +14355,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -14221,18 +14810,6 @@ "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", "dev": true }, - "node_modules/streamqueue": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/streamqueue/-/streamqueue-1.1.2.tgz", - "integrity": "sha512-CHUpqa+1BM99z7clQz9W6L9ZW4eXRRQCR0H+utVAGGvNo2ePlJAFjhdK0IjunaBbY/gWKJawk5kpJeyz0EXxRA==", - "dependencies": { - "isstream": "^0.1.2", - "readable-stream": "^2.3.3" - }, - "engines": { - "node": ">=6.9.5" - } - }, "node_modules/streamx": { "version": "2.18.0", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz", @@ -14277,7 +14854,21 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -14291,7 +14882,19 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -14357,7 +14960,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -15102,7 +15704,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", - "dev": true, "dependencies": { "clone": "^2.1.1", "clone-buffer": "^1.0.0", @@ -15115,6 +15716,45 @@ "node": ">= 0.10" } }, + "node_modules/vinyl-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vinyl-buffer/-/vinyl-buffer-1.0.1.tgz", + "integrity": "sha512-LRBE2/g3C1hSHL2k/FynSZcVTRhEw8sb08oKGt/0hukZXwrh2m8nfy+r5yLhGEk7eFFuclhyIuPct/Bxlxk6rg==", + "license": "MIT", + "dependencies": { + "bl": "^1.2.1", + "through2": "^2.0.3" + } + }, + "node_modules/vinyl-buffer/node_modules/bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/vinyl-buffer/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/vinyl-buffer/node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/vinyl-contents": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/vinyl-contents/-/vinyl-contents-2.0.0.tgz", @@ -15200,6 +15840,35 @@ "node": ">=0.4" } }, + "node_modules/vinyl-source-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-source-stream/-/vinyl-source-stream-2.0.0.tgz", + "integrity": "sha512-Y5f1wRGajOfYukhv8biIGA7iZiY8UOIc3zJ6zcUNIbRG1BVuXzBsfSfe7MUJTttVkuy64k/pGQtJdd/aIt+hbw==", + "license": "MIT", + "dependencies": { + "through2": "^2.0.3", + "vinyl": "^2.1.0" + } + }, + "node_modules/vinyl-source-stream/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/vinyl-source-stream/node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/vinyl-sourcemap": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", @@ -15322,7 +15991,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -15356,6 +16024,57 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", diff --git a/package.json b/package.json index 9114e27b1..b32996986 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,9 @@ "graceful-fs": "^4.2.11" }, "dependencies": { + "@rollup/plugin-commonjs": "^26.0.1", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/stream": "^3.0.1", "@uswds/uswds": "^3.8.1", "cbor-js": "0.1.0", "chart.js": "^4.4.3", @@ -39,17 +42,18 @@ "sass-embedded": "^1.77.8", "socket.io-client": "^4.2.0", "textarea-caret": "3.1.0", - "timeago": "1.6.7" + "timeago": "1.6.7", + "vinyl-buffer": "^1.0.1", + "vinyl-source-stream": "^2.0.0" }, "devDependencies": { "@babel/core": "^7.25.2", - "@babel/preset-env": "^7.25.2", + "@babel/preset-env": "^7.25.3", "@uswds/compile": "^1.1.0", "better-npm-audit": "^3.7.3", "gulp": "^5.0.0", "gulp-add-src": "^1.0.0", "gulp-babel": "8.0.0", - "gulp-better-rollup": "4.0.1", "gulp-clean-css": "4.3.0", "gulp-concat": "^2.6.1", "gulp-include": "2.4.1", @@ -61,7 +65,7 @@ "jest-environment-jsdom": "^29.2.2", "jshint": "2.13.6", "jshint-stylish": "2.2.1", - "rollup": "1.32.1", + "rollup": "^4.20.0", "rollup-plugin-commonjs": "10.1.0", "rollup-plugin-node-resolve": "5.2.0" } From 3e472e0a972a4f975b2149c137f0b7cf9b3e08da Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Thu, 8 Aug 2024 16:35:09 -0400 Subject: [PATCH 55/98] Test update --- tests/javascripts/totalMessagesChart.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/javascripts/totalMessagesChart.test.js b/tests/javascripts/totalMessagesChart.test.js index 22bc39500..b5cf3e6a8 100644 --- a/tests/javascripts/totalMessagesChart.test.js +++ b/tests/javascripts/totalMessagesChart.test.js @@ -57,7 +57,7 @@ test('SVG element is correctly set up', done => { setTimeout(() => { const svg = document.getElementById('totalMessageChart'); expect(svg.getAttribute('width')).toBe('600'); - expect(svg.getAttribute('height')).toBe('64'); + expect(svg.getAttribute('height')).toBe('48'); done(); }, 1000); // Ensure enough time for the DOM updates }); From 45bc6a91732afebe96f5e531d13347b02724e55f Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Thu, 8 Aug 2024 16:53:56 -0400 Subject: [PATCH 56/98] test updates --- app/assets/javascripts/totalMessagesChart.js | 2 +- tests/javascripts/totalMessagesChart.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/totalMessagesChart.js b/app/assets/javascripts/totalMessagesChart.js index 9a0c8b325..bebd6e106 100644 --- a/app/assets/javascripts/totalMessagesChart.js +++ b/app/assets/javascripts/totalMessagesChart.js @@ -62,7 +62,7 @@ .attr("x", 0) // Initially set to 0, will be updated during animation .attr("y", 0) .attr("height", height) - .attr("fill", '#c6cace') + .attr("fill", '#C7CACE') .attr("width", 0) // Start with width 0 for animation .on('mouseover', function(event) { tooltip.style('display', 'block') diff --git a/tests/javascripts/totalMessagesChart.test.js b/tests/javascripts/totalMessagesChart.test.js index b5cf3e6a8..87c671430 100644 --- a/tests/javascripts/totalMessagesChart.test.js +++ b/tests/javascripts/totalMessagesChart.test.js @@ -159,7 +159,7 @@ test('SVG bars are created and animated correctly', done => { // Initial check const sentBar = svg.querySelector('rect[fill="#0076d6"]'); - const remainingBar = svg.querySelector('rect[fill="#fa9441"]'); + const remainingBar = svg.querySelector('rect[fill="#C7CACE"]'); expect(sentBar).not.toBeNull(); expect(remainingBar).not.toBeNull(); From 1acb37c0126833a339128faf01077b476bad4699 Mon Sep 17 00:00:00 2001 From: alexjanousekGSA Date: Fri, 9 Aug 2024 07:40:58 -0600 Subject: [PATCH 57/98] Added styling files back --- gulpfile.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index fb2167cd1..c3893a7cf 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -5,6 +5,7 @@ const rollupPluginNodeResolve = require('@rollup/plugin-node-resolve'); const source = require('vinyl-source-stream'); const buffer = require('vinyl-buffer'); const gulpMerge = require('gulp-merge'); +const uswds = require("@uswds/compile"); const plugins = {}; plugins.addSrc = require('gulp-add-src'); @@ -90,4 +91,22 @@ const javascripts = () => { .pipe(dest(paths.dist + 'javascripts/')); }; -exports.default = series(javascripts); +// Configure USWDS paths +uswds.settings.version = 3; +uswds.paths.dist.css = paths.dist + 'css'; +uswds.paths.dist.js = paths.dist + 'js'; +uswds.paths.dist.img = paths.dist + 'img'; +uswds.paths.dist.fonts = paths.dist + 'fonts'; +uswds.paths.dist.theme = paths.src + 'sass/uswds'; + +// Task to compile USWDS styles +const styles = async () => { + await uswds.compile(); +}; + +// Task to copy USWDS assets (optional, if needed) +const copyAssets = async () => { + await uswds.copyAssets(); +}; + +exports.default = series(styles, javascripts, copyAssets); From 832932f3b8ca75b33f7c6d77efc8c405a4277906 Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Fri, 9 Aug 2024 10:33:51 -0400 Subject: [PATCH 58/98] Updates to styles and js --- app/assets/javascripts/activityChart.js | 2 +- app/assets/javascripts/totalMessagesChart.js | 2 +- app/assets/sass/uswds/_data-visualization.scss | 4 ++++ app/templates/views/dashboard/dashboard.html | 4 ---- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/activityChart.js b/app/assets/javascripts/activityChart.js index b5139f157..75913c145 100644 --- a/app/assets/javascripts/activityChart.js +++ b/app/assets/javascripts/activityChart.js @@ -4,7 +4,7 @@ const COLORS = { delivered: '#0076d6', - failed: '#c6cace', + failed: '#fa9441', text: '#666' }; diff --git a/app/assets/javascripts/totalMessagesChart.js b/app/assets/javascripts/totalMessagesChart.js index bebd6e106..a3c10d7a3 100644 --- a/app/assets/javascripts/totalMessagesChart.js +++ b/app/assets/javascripts/totalMessagesChart.js @@ -14,7 +14,7 @@ document.getElementById('message').innerText = `${sms_sent.toLocaleString()} sent / ${sms_remaining_messages.toLocaleString()} remaining`; // Calculate minimum width for "Messages Sent" as 1% of the total chart width - var minSentPercentage = 0.01; // Minimum width as a percentage of total messages (1% in this case) + var minSentPercentage = 0.02; // Minimum width as a percentage of total messages (1% in this case) var minSentValue = totalMessages * minSentPercentage; var displaySent = Math.max(sms_sent, minSentValue); var displayRemaining = totalMessages - displaySent; diff --git a/app/assets/sass/uswds/_data-visualization.scss b/app/assets/sass/uswds/_data-visualization.scss index 7740aad3f..48c5f1256 100644 --- a/app/assets/sass/uswds/_data-visualization.scss +++ b/app/assets/sass/uswds/_data-visualization.scss @@ -11,6 +11,10 @@ $failed: color('gray-cool-20'); } } +#totalMessageChartContainer { + max-width: 600px; +} + .bar { border-radius: units(0.5); &.delivered, &.usage { diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index eeca2b213..2c1af4909 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -28,10 +28,6 @@
-

- What counts as 1 text message part?
- See Tracking usage. -

Activity snapshot From 220106ff6b28b13a6320e5fa2ad3351441227c2a Mon Sep 17 00:00:00 2001 From: alexjanousekGSA Date: Fri, 9 Aug 2024 09:18:50 -0600 Subject: [PATCH 59/98] Added another file --- gulpfile.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index c3893a7cf..9b0237539 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -91,7 +91,11 @@ const javascripts = () => { .pipe(dest(paths.dist + 'javascripts/')); }; -// Configure USWDS paths +const copyGtmHead = () => { + return src(paths.src + 'js/gtm_head.js') + .pipe(dest(paths.dist + 'js/')); +}; + uswds.settings.version = 3; uswds.paths.dist.css = paths.dist + 'css'; uswds.paths.dist.js = paths.dist + 'js'; @@ -99,14 +103,12 @@ uswds.paths.dist.img = paths.dist + 'img'; uswds.paths.dist.fonts = paths.dist + 'fonts'; uswds.paths.dist.theme = paths.src + 'sass/uswds'; -// Task to compile USWDS styles const styles = async () => { await uswds.compile(); }; -// Task to copy USWDS assets (optional, if needed) const copyAssets = async () => { await uswds.copyAssets(); }; -exports.default = series(styles, javascripts, copyAssets); +exports.default = series(styles, javascripts, copyGtmHead, copyAssets); From 0a39077fc888855e0d3a30f3b666abe26ff0fc74 Mon Sep 17 00:00:00 2001 From: alexjanousekGSA Date: Fri, 9 Aug 2024 09:41:13 -0600 Subject: [PATCH 60/98] Added another missing file --- gulpfile.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index 9b0237539..1bb4178e6 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -91,11 +91,19 @@ const javascripts = () => { .pipe(dest(paths.dist + 'javascripts/')); }; +// Task to copy `gtm_head.js` const copyGtmHead = () => { return src(paths.src + 'js/gtm_head.js') .pipe(dest(paths.dist + 'js/')); }; +// Task to copy images +const copyImages = () => { + return src(paths.src + 'images/**/*') + .pipe(dest(paths.dist + 'images/')); +}; + +// Configure USWDS paths uswds.settings.version = 3; uswds.paths.dist.css = paths.dist + 'css'; uswds.paths.dist.js = paths.dist + 'js'; @@ -103,12 +111,14 @@ uswds.paths.dist.img = paths.dist + 'img'; uswds.paths.dist.fonts = paths.dist + 'fonts'; uswds.paths.dist.theme = paths.src + 'sass/uswds'; +// Task to compile USWDS styles const styles = async () => { await uswds.compile(); }; +// Task to copy USWDS assets const copyAssets = async () => { await uswds.copyAssets(); }; -exports.default = series(styles, javascripts, copyGtmHead, copyAssets); +exports.default = series(styles, javascripts, copyGtmHead, copyImages, copyAssets); From 39981c1f7095de596e2761fcf0e25a6be3a95257 Mon Sep 17 00:00:00 2001 From: alexjanousekGSA Date: Fri, 9 Aug 2024 10:43:56 -0600 Subject: [PATCH 61/98] Added back new chart module --- gulpfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index 1bb4178e6..b0c171655 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -50,7 +50,7 @@ const javascripts = () => { paths.npm + 'textarea-caret/index.js', paths.npm + 'cbor-js/cbor.js', paths.npm + 'socket.io-client/dist/socket.io.min.js', - paths.npm + 'chart.js/dist/chart.umd.js' + paths.npm + 'd3/dist/d3.min.js' ])); const local = src([ From 393ba67878fcc7c567faa7d4122b485a511f2203 Mon Sep 17 00:00:00 2001 From: alexjanousekGSA Date: Fri, 9 Aug 2024 10:48:39 -0600 Subject: [PATCH 62/98] Added d3 --- package-lock.json | 445 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + 2 files changed, 443 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b42eb95b..f67122785 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@uswds/uswds": "^3.8.1", "cbor-js": "0.1.0", "chart.js": "^4.4.3", + "d3": "^7.9.0", "govuk_frontend_toolkit": "^9.0.1", "govuk-frontend": "2.13.0", "gulp-merge": "^0.1.1", @@ -6124,6 +6125,416 @@ "node": ">=0.12" } }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", @@ -6330,6 +6741,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -8864,7 +9284,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -8965,6 +9384,15 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/interpret": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", @@ -13746,6 +14174,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "license": "Unlicense" + }, "node_modules/rollup": { "version": "4.20.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz", @@ -13847,6 +14281,12 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, "node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -13886,8 +14326,7 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sass-embedded": { "version": "1.77.8", diff --git a/package.json b/package.json index b32996986..17494bfcf 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@uswds/uswds": "^3.8.1", "cbor-js": "0.1.0", "chart.js": "^4.4.3", + "d3": "^7.9.0", "govuk_frontend_toolkit": "^9.0.1", "govuk-frontend": "2.13.0", "gulp-merge": "^0.1.1", From d9d802cfb744885e718ea38f61bc28bb00eb8c1c Mon Sep 17 00:00:00 2001 From: alexjanousekGSA Date: Fri, 9 Aug 2024 10:57:59 -0600 Subject: [PATCH 63/98] Added charts back --- gulpfile.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gulpfile.js b/gulpfile.js index b0c171655..8f38f5ac4 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -79,6 +79,8 @@ const javascripts = () => { paths.src + 'javascripts/loginAlert.js', paths.src + 'javascripts/main.js', paths.src + 'javascripts/sampleChartDashboard.js', + paths.src + 'javascripts/totalMessagesChart.js', + paths.src + 'javascripts/activityChart.js', ]) .pipe(plugins.prettyerror()) .pipe(plugins.babel({ From 9e6f87ace1038f9a0e29c7974caba2bd33079dee Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Fri, 9 Aug 2024 14:29:14 -0700 Subject: [PATCH 64/98] removed socket io --- app/__init__.py | 4 - app/assets/javascripts/activityChart.js | 43 ++- .../javascripts/sampleChartDashboard.js | 70 ---- app/main/views/dashboard.py | 66 ++-- app/templates/views/dashboard/dashboard.html | 2 +- gulpfile.js | 1 - package-lock.json | 84 +---- package.json | 1 - tests/app/main/views/test_dashboard.py | 338 +++++------------- tests/javascripts/activityChart.test.js | 44 +-- 10 files changed, 148 insertions(+), 505 deletions(-) delete mode 100644 app/assets/javascripts/sampleChartDashboard.js diff --git a/app/__init__.py b/app/__init__.py index 55942769c..483d89ea0 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -18,7 +18,6 @@ from flask import ( ) from flask.globals import request_ctx from flask_login import LoginManager, current_user -from flask_socketio import SocketIO from flask_talisman import Talisman from flask_wtf import CSRFProtect from flask_wtf.csrf import CSRFError @@ -119,8 +118,6 @@ from notifications_utils.recipients import format_phone_number_human_readable login_manager = LoginManager() csrf = CSRFProtect() talisman = Talisman() -socketio = SocketIO() - # The current service attached to the request stack. current_service = LocalProxy(partial(getattr, request_ctx, "service")) @@ -177,7 +174,6 @@ def create_app(application): init_govuk_frontend(application) init_jinja(application) - socketio.init_app(application, cors_allowed_origins=['http://localhost:6012']) for client in ( csrf, diff --git a/app/assets/javascripts/activityChart.js b/app/assets/javascripts/activityChart.js index 75913c145..b24bef50d 100644 --- a/app/assets/javascripts/activityChart.js +++ b/app/assets/javascripts/activityChart.js @@ -185,41 +185,38 @@ return; } - var socket = io("/services"); - var eventType = type === 'service' ? 'fetch_daily_stats' : 'fetch_daily_stats_by_user'; - var socketConnect = type === 'service' ? 'daily_stats_update' : 'daily_stats_by_user_update'; + var daily_stats = activityChartContainer.getAttribute('data-daily-stats'); + var daily_stats_by_user = activityChartContainer.getAttribute('data-daily_stats_by_user'); - socket.on('connect', function () { - socket.emit(eventType); - }); + var data; + try { + data = type === 'service' ? eval("(" + daily_stats + ")") : eval("(" + daily_stats_by_user + ")"); + } catch (error) { + console.error('Error parsing data:', error); + return; + } - socket.on('connect_error', function(error) { - console.error('WebSocket connection error:', error); - }); + var labels = []; + var deliveredData = []; + var failedData = []; - socket.on(socketConnect, function(data) { - - var labels = []; - var deliveredData = []; - var failedData = []; - - for (var dateString in data) { - // Parse the date string (assuming format YYYY-MM-DD) + for (var dateString in data) { + if (data.hasOwnProperty(dateString)) { const dateParts = dateString.split('-'); - const formattedDate = `${dateParts[1]}/${dateParts[2]}/${dateParts[0].slice(2)}`; // Format to MM/DD/YY + const formattedDate = `${dateParts[1]}/${dateParts[2]}/${dateParts[0].slice(2)}`; labels.push(formattedDate); deliveredData.push(data[dateString].sms.delivered); failedData.push(data[dateString].sms.failure); } + } + try { createChart('#weeklyChart', labels, deliveredData, failedData); createTable('weeklyTable', 'activityChart', labels, deliveredData, failedData); - }); - - socket.on('error', function(data) { - console.log('Error:', data); - }); + } catch (error) { + console.error('Error creating chart or table:', error); + } }; const handleDropdownChange = function(event) { diff --git a/app/assets/javascripts/sampleChartDashboard.js b/app/assets/javascripts/sampleChartDashboard.js deleted file mode 100644 index f3a363e9b..000000000 --- a/app/assets/javascripts/sampleChartDashboard.js +++ /dev/null @@ -1,70 +0,0 @@ -(function (window) { - - function initializeChartAndSocket() { - var ctx = document.getElementById('myChart'); - if (!ctx) { - return; - } - - var myBarChart = new Chart(ctx.getContext('2d'), { - type: 'bar', - data: { - labels: [], - datasets: [ - { - label: 'Delivered', - data: [], - backgroundColor: '#0076d6', - stack: 'Stack 0' - }, - ] - }, - options: { - animation: false, - scales: { - y: { - beginAtZero: true - } - } - } - }); - - var socket = io(); - - socket.on('connect', function() { - socket.emit('fetch_daily_stats_by_user'); - }); - - socket.on('daily_stats_by_user_update', function(data) { - // console.log('Data received:', data); - var labels = []; - var deliveredData = []; - var failedData = []; - - for (var date in data) { - labels.push(date); - deliveredData.push(data[date].sms.delivered); - - } - - myBarChart.data.labels = labels; - myBarChart.data.datasets[0].data = deliveredData; - myBarChart.update(); - }); - - socket.on('error', function(data) { - // console.log('Error:', data); - }); - - var sevenDaysButton = document.getElementById('sevenDaysButton'); - if (sevenDaysButton) { - sevenDaysButton.addEventListener('click', function() { - socket.emit('fetch_daily_stats_by_user'); - // console.log('clicked'); - }); - } - } - - document.addEventListener('DOMContentLoaded', initializeChartAndSocket); - -})(window); diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 9c3eb6527..c002678b5 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -3,9 +3,16 @@ from datetime import datetime from functools import partial from itertools import groupby -from flask import Response, abort, jsonify, render_template, request, session, url_for +from flask import ( + Response, + abort, + jsonify, + render_template, + request, + session, + url_for, +) from flask_login import current_user -from flask_socketio import emit from werkzeug.utils import redirect from app import ( @@ -13,7 +20,6 @@ from app import ( current_service, job_api_client, service_api_client, - socketio, template_statistics_client, ) from app.formatters import format_date_numeric, format_datetime_numeric, get_time_left @@ -32,38 +38,6 @@ from app.utils.user import user_has_permissions from notifications_utils.recipients import format_phone_number_human_readable -@socketio.on("fetch_daily_stats", namespace="/services") -def handle_fetch_daily_stats(): - service_id = session.get("service_id") - if service_id: - date_range = get_stats_date_range() - daily_stats = service_api_client.get_service_notification_statistics_by_day( - service_id, start_date=date_range["start_date"], days=date_range["days"] - ) - emit("daily_stats_update", daily_stats) - else: - emit("error", {"error": "No service_id provided"}) - - -@socketio.on("fetch_daily_stats_by_user", namespace="/services") -def handle_fetch_daily_stats_by_user(): - service_id = session.get("service_id") - user_id = session.get("user_id") - if service_id and user_id: - date_range = get_stats_date_range() - daily_stats_by_user = ( - service_api_client.get_user_service_notification_statistics_by_day( - service_id, - user_id, - start_date=date_range["start_date"], - days=date_range["days"], - ) - ) - emit("daily_stats_by_user_update", daily_stats_by_user) - else: - emit("error", {"error": "No service_id or user_id provided"}) - - @main.route("/services//dashboard") @user_has_permissions("view_activity", "send_messages") def old_service_dashboard(service_id): @@ -87,12 +61,17 @@ def service_dashboard(service_id): free_sms_allowance = billing_api_client.get_free_sms_fragment_limit_for_year( current_service.id, ) + date_range = get_stats_date_range() usage_data = get_annual_usage_breakdown(yearly_usage, free_sms_allowance) sms_sent = usage_data["sms_sent"] sms_allowance_remaining = usage_data["sms_allowance_remaining"] job_response = job_api_client.get_jobs(service_id)["data"] service_data_retention_days = 7 + daily_stats = get_daily_stats(service_id, date_range) + daily_stats_by_user = get_daily_stats_by_user( + service_id, current_user.id, date_range + ) jobs = [ { @@ -123,6 +102,23 @@ def service_dashboard(service_id): service_data_retention_days=service_data_retention_days, sms_sent=sms_sent, sms_allowance_remaining=sms_allowance_remaining, + daily_stats=daily_stats, + daily_stats_by_user=daily_stats_by_user, + ) + + +def get_daily_stats(service_id, date_range): + return service_api_client.get_service_notification_statistics_by_day( + service_id, start_date=date_range["start_date"], days=date_range["days"] + ) + + +def get_daily_stats_by_user(service_id, user_id, date_range): + return service_api_client.get_user_service_notification_statistics_by_day( + service_id, + user_id, + start_date=date_range["start_date"], + days=date_range["days"], ) diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index eeca2b213..d6e45bb27 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -36,7 +36,7 @@

Activity snapshot

-
+
@@ -30,7 +30,7 @@ beforeAll(done => {
-
+
Service Name - Last 7 Days
@@ -124,43 +124,3 @@ test('Check HTML content after chart creation', () => { expect(container.querySelector('svg')).not.toBeNull(); expect(container.querySelectorAll('rect').length).toBeGreaterThan(0); }); - - -test('Initial fetch data populates chart and table', done => { - const mockData = { - '2024-07-01': { sms: { delivered: 50, failed: 5 } }, - '2024-07-02': { sms: { delivered: 60, failed: 2 } }, - '2024-07-03': { sms: { delivered: 70, failed: 1 } }, - '2024-07-04': { sms: { delivered: 80, failed: 0 } }, - '2024-07-05': { sms: { delivered: 90, failed: 3 } }, - '2024-07-06': { sms: { delivered: 100, failed: 4 } }, - '2024-07-07': { sms: { delivered: 110, failed: 2 } }, - }; - - const socket = { - on: jest.fn((event, callback) => { - if (event === 'daily_stats_update') { - callback(mockData); - done(); - } - }), - emit: jest.fn(), - }; - window.io = jest.fn(() => socket); - - document.dispatchEvent(new Event('DOMContentLoaded')); - - setTimeout(() => { - const table = document.getElementById('weeklyTable'); - expect(table).toBeDefined(); - - const rows = table.getElementsByTagName('tr'); - expect(rows.length).toBe(8); - - const firstRowCells = rows[1].getElementsByTagName('td'); - console.log('First row cells:', firstRowCells); - expect(firstRowCells[0].textContent).toBe('07/01/24'); - expect(firstRowCells[1].textContent).toBe('50'); - expect(firstRowCells[2].textContent).toBe('5'); - }, 100); -}); From 2cbab8727ff2d8d99555b52f32d26daff2c834c9 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Fri, 9 Aug 2024 14:33:53 -0700 Subject: [PATCH 65/98] fixed isort --- app/main/views/dashboard.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index c002678b5..d5d26f0a3 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -3,15 +3,7 @@ from datetime import datetime from functools import partial from itertools import groupby -from flask import ( - Response, - abort, - jsonify, - render_template, - request, - session, - url_for, -) +from flask import Response, abort, jsonify, render_template, request, session, url_for from flask_login import current_user from werkzeug.utils import redirect From de270711121253de87cc615e3a9a258ed3c302ca Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Fri, 9 Aug 2024 14:49:16 -0700 Subject: [PATCH 66/98] fixed js errors --- app/assets/javascripts/activityChart.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/activityChart.js b/app/assets/javascripts/activityChart.js index b24bef50d..171b1c84a 100644 --- a/app/assets/javascripts/activityChart.js +++ b/app/assets/javascripts/activityChart.js @@ -188,14 +188,14 @@ var daily_stats = activityChartContainer.getAttribute('data-daily-stats'); var daily_stats_by_user = activityChartContainer.getAttribute('data-daily_stats_by_user'); - var data; try { - data = type === 'service' ? eval("(" + daily_stats + ")") : eval("(" + daily_stats_by_user + ")"); + var serializedStats = type === 'service' ? daily_stats : daily_stats_by_user; + serializedStats = serializedStats.replace(/'/g, '"'); + data = JSON.parse(serializedStats); } catch (error) { - console.error('Error parsing data:', error); + console.error('Error parsing JSON data:', error); return; } - var labels = []; var deliveredData = []; var failedData = []; From 2f62a57c7c617aa7af2f41f080fcd130e69cf201 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Fri, 9 Aug 2024 15:04:56 -0700 Subject: [PATCH 67/98] fixed pytest --- .ds.baseline | 4 +- tests/app/main/views/test_accept_invite.py | 21 +- tests/app/main/views/test_dashboard.py | 225 ++++++++++++++++----- tests/app/main/views/test_sign_out.py | 21 +- 4 files changed, 215 insertions(+), 56 deletions(-) diff --git a/.ds.baseline b/.ds.baseline index f7f116b20..59c7a62ed 100644 --- a/.ds.baseline +++ b/.ds.baseline @@ -535,7 +535,7 @@ "filename": "tests/app/main/views/test_accept_invite.py", "hashed_secret": "07f0a6c13923fc3b5f0c57ffa2d29b715eb80d71", "is_verified": false, - "line_number": 626, + "line_number": 643, "is_secret": false } ], @@ -692,5 +692,5 @@ } ] }, - "generated_at": "2024-08-07T14:36:40Z" + "generated_at": "2024-08-09T22:04:50Z" } diff --git a/tests/app/main/views/test_accept_invite.py b/tests/app/main/views/test_accept_invite.py index 01dab9876..59c4651c6 100644 --- a/tests/app/main/views/test_accept_invite.py +++ b/tests/app/main/views/test_accept_invite.py @@ -300,9 +300,26 @@ def test_accepting_invite_removes_invite_from_session( client_request.login(user) mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) + date_range = {"start_date": "2024-01-01", "days": 7} + mocker.patch( - "app.notification_api_client.get_notifications_for_service", - return_value=FAKE_ONE_OFF_NOTIFICATION, + "app.main.views.dashboard.get_daily_stats", + return_value={ + date_range["start_date"]: { + "email": {"delivered": 0, "failure": 0, "requested": 0}, + "sms": {"delivered": 0, "failure": 1, "requested": 1}, + }, + }, + ) + + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value={ + date_range["start_date"]: { + "email": {"delivered": 1, "failure": 0, "requested": 1}, + "sms": {"delivered": 1, "failure": 0, "requested": 1}, + }, + }, ) page = client_request.get( "main.accept_invite", diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index 1690ee781..b5a58f550 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -231,9 +231,14 @@ def test_get_started( return_value=copy.deepcopy(stub_template_stats), ) mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get( "main.service_dashboard", service_id=SERVICE_ONE_ID, @@ -260,9 +265,14 @@ def test_get_started_is_hidden_once_templates_exist( return_value=copy.deepcopy(stub_template_stats), ) mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get( "main.service_dashboard", service_id=SERVICE_ONE_ID, @@ -286,9 +296,14 @@ def test_inbound_messages_not_visible_to_service_without_permissions( ): service_one["permissions"] = [] mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get( "main.service_dashboard", service_id=SERVICE_ONE_ID, @@ -312,9 +327,14 @@ def test_inbound_messages_shows_count_of_messages_when_there_are_messages( mock_get_inbound_sms_summary, ): service_one["permissions"] = ["inbound_sms"] - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get( "main.service_dashboard", service_id=SERVICE_ONE_ID, @@ -343,9 +363,14 @@ def test_inbound_messages_shows_count_of_messages_when_there_are_no_messages( mock_get_inbound_sms_summary_with_no_messages, ): service_one["permissions"] = ["inbound_sms"] - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get( "main.service_dashboard", service_id=SERVICE_ONE_ID, @@ -592,9 +617,14 @@ def test_should_show_recent_templates_on_dashboard( return_value=copy.deepcopy(stub_template_stats), ) mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get( "main.service_dashboard", service_id=SERVICE_ONE_ID, @@ -648,9 +678,14 @@ def test_should_not_show_recent_templates_on_dashboard_if_only_one_template_used return_value=stats, ) mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID) main = page.select_one("main").text @@ -802,9 +837,14 @@ def test_should_show_upcoming_jobs_on_dashboard( mock_get_free_sms_fragment_limit, mock_get_inbound_sms_summary, ): - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get( "main.service_dashboard", service_id=SERVICE_ONE_ID, @@ -844,9 +884,14 @@ def test_should_not_show_upcoming_jobs_on_dashboard_if_count_is_0( }, ) mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get( "main.service_dashboard", service_id=SERVICE_ONE_ID, @@ -869,9 +914,14 @@ def test_should_not_show_upcoming_jobs_on_dashboard_if_service_has_no_jobs( mock_get_inbound_sms_summary, ): mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get( "main.service_dashboard", service_id=SERVICE_ONE_ID, @@ -954,9 +1004,14 @@ def test_should_not_show_jobs_on_dashboard_for_users_with_uploads_page( mock_get_free_sms_fragment_limit, mock_get_inbound_sms_summary, ): - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get( "main.service_dashboard", service_id=SERVICE_ONE_ID, @@ -1217,9 +1272,14 @@ def test_menu_send_messages( service_one["permissions"] = ["email", "sms"] mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = _test_dashboard_menu( client_request, mocker, @@ -1253,9 +1313,14 @@ def test_menu_manage_service( mock_get_free_sms_fragment_limit, ): mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = _test_dashboard_menu( client_request, mocker, @@ -1289,9 +1354,14 @@ def test_menu_main_settings( mock_get_free_sms_fragment_limit, ): mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = _test_settings_menu( client_request, mocker, @@ -1324,9 +1394,14 @@ def test_menu_manage_api_keys( mock_get_free_sms_fragment_limit, ): mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = _test_dashboard_menu( client_request, mocker, @@ -1363,9 +1438,14 @@ def test_menu_all_services_for_platform_admin_user( mock_get_free_sms_fragment_limit, ): mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = _test_dashboard_menu( client_request, mocker, platform_admin_user, service_one, [] ) @@ -1401,9 +1481,14 @@ def test_route_for_service_permissions( "app.service_api_client.get_global_notification_count", side_effect=_get ) mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) validate_route_permission( mocker, notify_admin, @@ -1545,9 +1630,14 @@ def test_org_breadcrumbs_do_not_show_if_service_has_no_org( mock_get_free_sms_fragment_limit, ): mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID) assert not page.select(".navigation-organization-link") @@ -1611,9 +1701,14 @@ def test_org_breadcrumbs_show_if_user_is_a_member_of_the_services_org( ), ) mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID) assert page.select_one(".navigation-organization-link")["href"] == url_for( @@ -1646,9 +1741,14 @@ def test_org_breadcrumbs_do_not_show_if_user_is_a_member_of_the_services_org_but mocker.patch("app.models.service.Organization") mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID) @@ -1683,9 +1783,14 @@ def test_org_breadcrumbs_show_if_user_is_platform_admin( mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) client_request.login(platform_admin_user, service_one_json) page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID) @@ -1718,9 +1823,14 @@ def test_breadcrumb_shows_if_service_is_suspended( mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID) @@ -1751,9 +1861,14 @@ def test_service_dashboard_shows_usage( }, ) mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) service_one["permissions"] = permissions page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID) @@ -1786,9 +1901,14 @@ def test_service_dashboard_shows_free_allowance( ], ) mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) def test_service_dashboard_shows_batched_jobs( @@ -1802,9 +1922,14 @@ def test_service_dashboard_shows_batched_jobs( mock_get_free_sms_fragment_limit, ): mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) - mocker.patch("app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats) + mocker.patch( + "app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats + ) - mocker.patch("app.main.views.dashboard.get_daily_stats_by_user", return_value=mock_daily_stats_by_user) + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value=mock_daily_stats_by_user, + ) page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID) job_table_body = page.find("table", class_="job-table") diff --git a/tests/app/main/views/test_sign_out.py b/tests/app/main/views/test_sign_out.py index 5ec23edf3..9afbfab97 100644 --- a/tests/app/main/views/test_sign_out.py +++ b/tests/app/main/views/test_sign_out.py @@ -128,9 +128,26 @@ def test_sign_out_user( # Check we are logged in mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS) + date_range = {"start_date": "2024-01-01", "days": 7} + mocker.patch( - "app.notification_api_client.get_notifications_for_service", - return_value=FAKE_ONE_OFF_NOTIFICATION, + "app.main.views.dashboard.get_daily_stats", + return_value={ + date_range["start_date"]: { + "email": {"delivered": 0, "failure": 0, "requested": 0}, + "sms": {"delivered": 0, "failure": 1, "requested": 1}, + }, + }, + ) + + mocker.patch( + "app.main.views.dashboard.get_daily_stats_by_user", + return_value={ + date_range["start_date"]: { + "email": {"delivered": 1, "failure": 0, "requested": 1}, + "sms": {"delivered": 1, "failure": 0, "requested": 1}, + }, + }, ) client_request.get( From bda957f76a21899a2ff8bed45e8aeac529bf9896 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 12 Aug 2024 07:27:32 -0700 Subject: [PATCH 68/98] change delimiter back to comma --- app/main/views/platform_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/main/views/platform_admin.py b/app/main/views/platform_admin.py index 07d3b46b5..f0b5983b5 100644 --- a/app/main/views/platform_admin.py +++ b/app/main/views/platform_admin.py @@ -88,7 +88,7 @@ def download_all_users(): writer = csv.DictWriter( output, fieldnames=fieldnames, - delimiter="\t", + delimiter=",", ) # Write custom header writer.writerow(dict(zip(fieldnames, header))) From 96d64155780e8d5f97a305b72b45daa18855a9eb Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 12 Aug 2024 11:47:56 -0700 Subject: [PATCH 69/98] add comments --- app/assets/javascripts/activityChart.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/activityChart.js b/app/assets/javascripts/activityChart.js index 171b1c84a..d00e0fd55 100644 --- a/app/assets/javascripts/activityChart.js +++ b/app/assets/javascripts/activityChart.js @@ -189,9 +189,12 @@ var daily_stats_by_user = activityChartContainer.getAttribute('data-daily_stats_by_user'); try { - var serializedStats = type === 'service' ? daily_stats : daily_stats_by_user; - serializedStats = serializedStats.replace(/'/g, '"'); - data = JSON.parse(serializedStats); + // Choose the correct JSON string based on the type ('service' or 'user'), + // replace single quotes with double quotes to ensure valid JSON format, + // then parse the JSON string into a JavaScript object. + var statJson = type === 'service' ? daily_stats : daily_stats_by_user; + statJson = statJson.replace(/'/g, '"'); + data = JSON.parse(statJson); } catch (error) { console.error('Error parsing JSON data:', error); return; From a0e177b99b9412c061595ee8eff94a5744c6a05f Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 12 Aug 2024 11:51:20 -0700 Subject: [PATCH 70/98] add comments --- app/assets/javascripts/activityChart.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/activityChart.js b/app/assets/javascripts/activityChart.js index d00e0fd55..8bece8276 100644 --- a/app/assets/javascripts/activityChart.js +++ b/app/assets/javascripts/activityChart.js @@ -192,9 +192,9 @@ // Choose the correct JSON string based on the type ('service' or 'user'), // replace single quotes with double quotes to ensure valid JSON format, // then parse the JSON string into a JavaScript object. - var statJson = type === 'service' ? daily_stats : daily_stats_by_user; - statJson = statJson.replace(/'/g, '"'); - data = JSON.parse(statJson); + var statsJson = type === 'service' ? daily_stats : daily_stats_by_user; + statsJson = statsJson.replace(/'/g, '"'); + data = JSON.parse(statsJson); } catch (error) { console.error('Error parsing JSON data:', error); return; From 6d09d44a842712c731e2d5490d8bf2fcfef4a5c6 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 12 Aug 2024 13:38:29 -0700 Subject: [PATCH 71/98] removed flask-socketio --- poetry.lock | 117 +++---------------------------------------------- pyproject.toml | 1 - 2 files changed, 5 insertions(+), 113 deletions(-) diff --git a/poetry.lock b/poetry.lock index 88cec6cce..1dbed25aa 100644 --- a/poetry.lock +++ b/poetry.lock @@ -85,17 +85,6 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] -[[package]] -name = "bidict" -version = "0.23.1" -description = "The bidirectional mapping library for Python." -optional = false -python-versions = ">=3.8" -files = [ - {file = "bidict-0.23.1-py3-none-any.whl", hash = "sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5"}, - {file = "bidict-0.23.1.tar.gz", hash = "sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71"}, -] - [[package]] name = "black" version = "24.4.2" @@ -893,24 +882,6 @@ redis = ">=2.7.6" dev = ["coverage", "pre-commit", "pytest", "pytest-mock"] tests = ["coverage", "pytest", "pytest-mock"] -[[package]] -name = "flask-socketio" -version = "5.3.6" -description = "Socket.IO integration for Flask applications" -optional = false -python-versions = ">=3.6" -files = [ - {file = "Flask-SocketIO-5.3.6.tar.gz", hash = "sha256:bb8f9f9123ef47632f5ce57a33514b0c0023ec3696b2384457f0fcaa5b70501c"}, - {file = "Flask_SocketIO-5.3.6-py3-none-any.whl", hash = "sha256:9e62d2131842878ae6bfdd7067dfc3be397c1f2b117ab1dc74e6fe74aad7a579"}, -] - -[package.dependencies] -Flask = ">=0.9" -python-socketio = ">=5.0.2" - -[package.extras] -docs = ["sphinx"] - [[package]] name = "flask-talisman" version = "1.1.0" @@ -1059,17 +1030,6 @@ setproctitle = ["setproctitle"] testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"] tornado = ["tornado (>=0.2)"] -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.7" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - [[package]] name = "html5lib" version = "1.1" @@ -1325,9 +1285,13 @@ files = [ {file = "lxml-5.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edcfa83e03370032a489430215c1e7783128808fd3e2e0a3225deee278585196"}, {file = "lxml-5.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28bf95177400066596cdbcfc933312493799382879da504633d16cf60bba735b"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a745cc98d504d5bd2c19b10c79c61c7c3df9222629f1b6210c0368177589fb8"}, + {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b590b39ef90c6b22ec0be925b211298e810b4856909c8ca60d27ffbca6c12e6"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336b0416828022bfd5a2e3083e7f5ba54b96242159f83c7e3eebaec752f1716"}, + {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:c2faf60c583af0d135e853c86ac2735ce178f0e338a3c7f9ae8f622fd2eb788c"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:4bc6cb140a7a0ad1f7bc37e018d0ed690b7b6520ade518285dc3171f7a117905"}, + {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7ff762670cada8e05b32bf1e4dc50b140790909caa8303cfddc4d702b71ea184"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:57f0a0bbc9868e10ebe874e9f129d2917750adf008fe7b9c1598c0fbbfdde6a6"}, + {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:a6d2092797b388342c1bc932077ad232f914351932353e2e8706851c870bca1f"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:60499fe961b21264e17a471ec296dcbf4365fbea611bf9e303ab69db7159ce61"}, {file = "lxml-5.2.2-cp37-cp37m-win32.whl", hash = "sha256:d9b342c76003c6b9336a80efcc766748a333573abf9350f4094ee46b006ec18f"}, {file = "lxml-5.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b16db2770517b8799c79aa80f4053cd6f8b716f21f8aca962725a9565ce3ee40"}, @@ -1662,7 +1626,6 @@ files = [ {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, - {file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"}, {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, ] @@ -2406,25 +2369,6 @@ files = [ [package.extras] cli = ["click (>=5.0)"] -[[package]] -name = "python-engineio" -version = "4.9.1" -description = "Engine.IO server and client for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "python_engineio-4.9.1-py3-none-any.whl", hash = "sha256:f995e702b21f6b9ebde4e2000cd2ad0112ba0e5116ec8d22fe3515e76ba9dddd"}, - {file = "python_engineio-4.9.1.tar.gz", hash = "sha256:7631cf5563086076611e494c643b3fa93dd3a854634b5488be0bba0ef9b99709"}, -] - -[package.dependencies] -simple-websocket = ">=0.10.0" - -[package.extras] -asyncio-client = ["aiohttp (>=3.4)"] -client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"] -docs = ["sphinx"] - [[package]] name = "python-json-logger" version = "2.0.7" @@ -2453,26 +2397,6 @@ text-unidecode = ">=1.3" [package.extras] unidecode = ["Unidecode (>=1.1.1)"] -[[package]] -name = "python-socketio" -version = "5.11.3" -description = "Socket.IO server and client for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "python_socketio-5.11.3-py3-none-any.whl", hash = "sha256:2a923a831ff70664b7c502df093c423eb6aa93c1ce68b8319e840227a26d8b69"}, - {file = "python_socketio-5.11.3.tar.gz", hash = "sha256:194af8cdbb7b0768c2e807ba76c7abc288eb5bb85559b7cddee51a6bc7a65737"}, -] - -[package.dependencies] -bidict = ">=0.21.0" -python-engineio = ">=4.8.0" - -[package.extras] -asyncio-client = ["aiohttp (>=3.4)"] -client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"] -docs = ["sphinx"] - [[package]] name = "pytz" version = "2024.1" @@ -2822,23 +2746,6 @@ numpy = ">=1.14,<3" docs = ["matplotlib", "numpydoc (==1.1.*)", "sphinx", "sphinx-book-theme", "sphinx-remove-toctrees"] test = ["pytest", "pytest-cov"] -[[package]] -name = "simple-websocket" -version = "1.0.0" -description = "Simple WebSocket server and client for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "simple-websocket-1.0.0.tar.gz", hash = "sha256:17d2c72f4a2bd85174a97e3e4c88b01c40c3f81b7b648b0cc3ce1305968928c8"}, - {file = "simple_websocket-1.0.0-py3-none-any.whl", hash = "sha256:1d5bf585e415eaa2083e2bcf02a3ecf91f9712e7b3e6b9fa0b461ad04e0837bc"}, -] - -[package.dependencies] -wsproto = "*" - -[package.extras] -docs = ["sphinx"] - [[package]] name = "six" version = "1.16.0" @@ -3016,20 +2923,6 @@ MarkupSafe = ">=2.1.1" [package.extras] watchdog = ["watchdog (>=2.3)"] -[[package]] -name = "wsproto" -version = "1.2.0" -description = "WebSockets state-machine based protocol implementation" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, - {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, -] - -[package.dependencies] -h11 = ">=0.9.0,<1" - [[package]] name = "wtforms" version = "3.1.2" @@ -3088,4 +2981,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.12.2" -content-hash = "9d6309a76755b2639d787f99944c1ead0bd93ab9f2f13208f88c51cecb5e0081" +content-hash = "d75c3c8550841736f3714523fd38617eedae0b2bb02c42e870b2d5ab47db7d91" diff --git a/pyproject.toml b/pyproject.toml index 915f23610..3b1d25783 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,6 @@ requests = "^2.32.3" six = "^1.16.0" urllib3 = "^2.2.2" webencodings = "^0.5.1" -flask-socketio = "^5.3.6" [tool.poetry.group.dev.dependencies] From 57ee3454517331e4e3277348938732f5e3a4577a Mon Sep 17 00:00:00 2001 From: alexjanousekGSA Date: Mon, 12 Aug 2024 15:29:35 -0600 Subject: [PATCH 72/98] Removed chart js --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 17494bfcf..50a8900f4 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "@rollup/stream": "^3.0.1", "@uswds/uswds": "^3.8.1", "cbor-js": "0.1.0", - "chart.js": "^4.4.3", "d3": "^7.9.0", "govuk_frontend_toolkit": "^9.0.1", "govuk-frontend": "2.13.0", From aa1a73846370f456758e6478e04c4db7aaaefd3f Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 12 Aug 2024 15:05:24 -0700 Subject: [PATCH 73/98] uninstalled sockeio client --- package-lock.json | 20 -------------------- package.json | 1 - 2 files changed, 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index ec4c3000a..6dc671923 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,6 @@ "@rollup/stream": "^3.0.1", "@uswds/uswds": "^3.8.1", "cbor-js": "0.1.0", - "chart.js": "^4.4.3", "d3": "^7.9.0", "govuk_frontend_toolkit": "^9.0.1", "govuk-frontend": "2.13.0", @@ -25,7 +24,6 @@ "python": "^0.0.4", "query-command-supported": "1.0.0", "sass-embedded": "^1.77.8", - "socket.io-client": "^4.2.0", "textarea-caret": "3.1.0", "timeago": "1.6.7", "vinyl-buffer": "^1.0.1", @@ -2793,12 +2791,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@kurkle/color": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", - "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==", - "license": "MIT" - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -5525,18 +5517,6 @@ "node": ">=10" } }, - "node_modules/chart.js": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", - "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", - "license": "MIT", - "dependencies": { - "@kurkle/color": "^0.3.0" - }, - "engines": { - "pnpm": ">=8" - } - }, "node_modules/cheerio": { "version": "1.0.0-rc.12", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", diff --git a/package.json b/package.json index 50a8900f4..03a4fe46f 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "python": "^0.0.4", "query-command-supported": "1.0.0", "sass-embedded": "^1.77.8", - "socket.io-client": "^4.2.0", "textarea-caret": "3.1.0", "timeago": "1.6.7", "vinyl-buffer": "^1.0.1", From cf5ea736e2f7d9a2fecb70c0d050cf2b9686b85b Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 12 Aug 2024 15:08:40 -0700 Subject: [PATCH 74/98] removed unused js file --- gulpfile.js | 1 - 1 file changed, 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index 6b5abb511..b441f4c2d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -77,7 +77,6 @@ const javascripts = () => { paths.src + 'javascripts/date.js', paths.src + 'javascripts/loginAlert.js', paths.src + 'javascripts/main.js', - paths.src + 'javascripts/sampleChartDashboard.js', paths.src + 'javascripts/totalMessagesChart.js', paths.src + 'javascripts/activityChart.js', ]) From 932963beb5bcef68feffce4928417d133af1796e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 02:28:57 +0000 Subject: [PATCH 75/98] Bump @uswds/uswds from 3.8.1 to 3.8.2 Bumps [@uswds/uswds](https://github.com/uswds/uswds) from 3.8.1 to 3.8.2. - [Release notes](https://github.com/uswds/uswds/releases) - [Commits](https://github.com/uswds/uswds/compare/v3.8.1...v3.8.2) --- updated-dependencies: - dependency-name: "@uswds/uswds" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 14 ++++---------- package.json | 2 +- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6dc671923..63b84e24d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@rollup/plugin-commonjs": "^26.0.1", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/stream": "^3.0.1", - "@uswds/uswds": "^3.8.1", + "@uswds/uswds": "^3.8.2", "cbor-js": "0.1.0", "d3": "^7.9.0", "govuk_frontend_toolkit": "^9.0.1", @@ -4367,11 +4367,10 @@ } }, "node_modules/@uswds/uswds": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@uswds/uswds/-/uswds-3.8.1.tgz", - "integrity": "sha512-bKG/B9mJF1v0yoqth48wQDzST5Xyu3OxxpePIPDyhKWS84oDrCehnu3Z88JhSjdIAJMl8dtjtH8YvdO9kZUpAg==", + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@uswds/uswds/-/uswds-3.8.2.tgz", + "integrity": "sha512-8sTx/GqlbTwSIK+0AFOGrYdaW1rKVB7Bp0+v9AMVt3I5vPK7CL0+I6vlclSf3U7ysJZeTTdkNS8q89sIAeL+AA==", "dependencies": { - "classlist-polyfill": "1.2.0", "object-assign": "4.1.1", "receptor": "1.0.0", "resolve-id-refs": "0.1.0" @@ -5615,11 +5614,6 @@ "node": ">=0.10.0" } }, - "node_modules/classlist-polyfill": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/classlist-polyfill/-/classlist-polyfill-1.2.0.tgz", - "integrity": "sha512-GzIjNdcEtH4ieA2S8NmrSxv7DfEV5fmixQeyTmqmRmRJPGpRBaSnA2a0VrCjyT8iW8JjEdMbKzDotAJf+ajgaQ==" - }, "node_modules/clean-css": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", diff --git a/package.json b/package.json index 03a4fe46f..89f832ad2 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@rollup/plugin-commonjs": "^26.0.1", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/stream": "^3.0.1", - "@uswds/uswds": "^3.8.1", + "@uswds/uswds": "^3.8.2", "cbor-js": "0.1.0", "d3": "^7.9.0", "govuk_frontend_toolkit": "^9.0.1", From 847057b2a8d877a25e09823e5aa6979c81dd0c33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 02:29:34 +0000 Subject: [PATCH 76/98] Bump redis from 5.0.7 to 5.0.8 Bumps [redis](https://github.com/redis/redis-py) from 5.0.7 to 5.0.8. - [Release notes](https://github.com/redis/redis-py/releases) - [Changelog](https://github.com/redis/redis-py/blob/master/CHANGES) - [Commits](https://github.com/redis/redis-py/compare/v5.0.7...v5.0.8) --- updated-dependencies: - dependency-name: redis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 11 ++++++----- pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 1dbed25aa..79e925e07 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2433,6 +2433,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -2487,17 +2488,17 @@ toml = ["tomli (>=2.0.1)"] [[package]] name = "redis" -version = "5.0.7" +version = "5.0.8" description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.7" files = [ - {file = "redis-5.0.7-py3-none-any.whl", hash = "sha256:0e479e24da960c690be5d9b96d21f7b918a98c0cf49af3b6fafaa0753f93a0db"}, - {file = "redis-5.0.7.tar.gz", hash = "sha256:8f611490b93c8109b50adc317b31bfd84fff31def3475b92e7e80bf39f48175b"}, + {file = "redis-5.0.8-py3-none-any.whl", hash = "sha256:56134ee08ea909106090934adc36f65c9bcbbaecea5b21ba704ba6fb561f8eb4"}, + {file = "redis-5.0.8.tar.gz", hash = "sha256:0c5b10d387568dfe0698c6fad6615750c24170e548ca2deac10c649d463e9870"}, ] [package.extras] -hiredis = ["hiredis (>=1.0.0)"] +hiredis = ["hiredis (>1.0.0)"] ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] [[package]] @@ -2981,4 +2982,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.12.2" -content-hash = "d75c3c8550841736f3714523fd38617eedae0b2bb02c42e870b2d5ab47db7d91" +content-hash = "2c1efc5e8d38c709aec2bc3aa39f96c82a58df13bcbf1912b27c4c26b7b4fc9d" diff --git a/pyproject.toml b/pyproject.toml index 3b1d25783..0ac206b60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,7 @@ ordered-set = "^4.1.0" phonenumbers = "^8.13.40" pycparser = "^2.22" python-json-logger = "^2.0.7" -redis = "^5.0.7" +redis = "^5.0.8" regex = "^2024.7.24" s3transfer = "^0.10.2" shapely = "^2.0.5" From c5b71dfe8de01779000a5e02288177cd40f65249 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 12 Aug 2024 23:28:41 -0700 Subject: [PATCH 77/98] added download url and stat count in table --- .../uswds/_uswds-theme-custom-styles.scss | 14 ++++++-- app/main/views/activity.py | 33 +++++++++++++++++-- .../views/activity/all-activity.html | 21 +++++++++++- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss index e3ef2be5d..db63f436a 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: 14%; } 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..6e710d633 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,14 @@ 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["statistics"] if stat["status"] == "delivered"), + None + ), + "failed_count": next( + (stat["count"] for stat in job["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..3a98b3541 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 '' }} + {{ job.failed_count if job.failed_count is not none else '' }} {% 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 %} From e3c78c5871d591b60614fe63b08af2f1eb7c4258 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 12 Aug 2024 23:45:01 -0700 Subject: [PATCH 78/98] added download url and stat count in table --- app/main/views/activity.py | 18 +++++++++++++----- tests/app/main/views/test_jobs_activity.py | 17 ++++++++++++++--- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/app/main/views/activity.py b/app/main/views/activity.py index 6e710d633..38863eb59 100644 --- a/app/main/views/activity.py +++ b/app/main/views/activity.py @@ -20,7 +20,7 @@ def all_jobs_activity(service_id): 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', + message_type = ("sms",) return render_template( "views/activity/all-activity.html", all_jobs_dict=all_jobs_dict, @@ -97,12 +97,20 @@ def generate_job_dict(jobs): "created_by": job["created_by"], "template_name": job["template_name"], "delivered_count": next( - (stat["count"] for stat in job["statistics"] if stat["status"] == "delivered"), - None + ( + stat["count"] + for stat in job.get("statistics", []) + if stat["status"] == "delivered" + ), + None, ), "failed_count": next( - (stat["count"] for stat in job["statistics"] if stat["status"] == "failed"), - None + ( + stat["count"] + for stat in job.get("statistics", []) + if stat["status"] == "failed" + ), + None, ), } for job in jobs["data"] diff --git a/tests/app/main/views/test_jobs_activity.py b/tests/app/main/views/test_jobs_activity.py index 60502c957..40a9837d0 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,17 @@ 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) From 5d211f7c594731ac23e7655a0f0766fe5b2af165 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 12 Aug 2024 23:50:13 -0700 Subject: [PATCH 79/98] fixed flake8 --- tests/app/main/views/test_jobs_activity.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/app/main/views/test_jobs_activity.py b/tests/app/main/views/test_jobs_activity.py index 40a9837d0..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": "delivered"},{"count": 5, "status": "failed"}], + "statistics": [{"count": 1, "status": "delivered"}, {"count": 5, "status": "failed"}], "template": "6a456418-498c-4c86-b0cd-9403c14a216c", "template_name": "Mock Template Name", "template_type": "sms", @@ -106,8 +106,6 @@ def test_all_activity( 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) From 0391c9467737e6237d7969c08497abd8a3d1098d Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 12 Aug 2024 23:53:44 -0700 Subject: [PATCH 80/98] adjusted width --- app/assets/sass/uswds/_uswds-theme-custom-styles.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss index db63f436a..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: 14%; + width: 15%; } td.sender { width: 20%; From 832f18a1a9eeb58e580415693b6ae4033ea643fb Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Tue, 13 Aug 2024 09:56:36 -0700 Subject: [PATCH 81/98] added zeros to failed messages --- app/templates/views/activity/all-activity.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/templates/views/activity/all-activity.html b/app/templates/views/activity/all-activity.html index 3a98b3541..9714dfc16 100644 --- a/app/templates/views/activity/all-activity.html +++ b/app/templates/views/activity/all-activity.html @@ -114,8 +114,8 @@ N/A {% endif %} - {{ job.delivered_count if job.delivered_count is not none else '' }} - {{ job.failed_count if job.failed_count is not none else '' }} + {{ 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 %} From b5d4e12466ae2d02ec0fd96004a35dc20d34f131 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Tue, 13 Aug 2024 11:37:08 -0700 Subject: [PATCH 82/98] fix formatting --- app/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/__init__.py b/app/__init__.py index f0aa2e676..483d89ea0 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -175,7 +175,6 @@ def create_app(application): init_govuk_frontend(application) init_jinja(application) - for client in ( csrf, login_manager, From 9a59a09bf8100fef9fbfb02795d671cf8f7e1b72 Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Tue, 13 Aug 2024 15:06:08 -0400 Subject: [PATCH 83/98] Show templates even if there is just 1 --- app/templates/views/dashboard/template-statistics.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/views/dashboard/template-statistics.html b/app/templates/views/dashboard/template-statistics.html index 5ad22a0dd..34e820d32 100644 --- a/app/templates/views/dashboard/template-statistics.html +++ b/app/templates/views/dashboard/template-statistics.html @@ -1,7 +1,7 @@ {% from "components/table.html" import list_table, field, right_aligned_field_heading, row_heading, spark_bar_field %}
- {% if template_statistics|length > 1 %} + {% if template_statistics|length > 0 %}

Recent Templates

{% call(item, row_number) list_table( From 3397b0c3804c393134e67a23de8a3b61dc3d1142 Mon Sep 17 00:00:00 2001 From: alexjanousekGSA Date: Tue, 13 Aug 2024 14:15:02 -0500 Subject: [PATCH 84/98] Added back part that got removed that allowed png images --- gulpfile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index b441f4c2d..d4d6ac81f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -99,10 +99,11 @@ const copyGtmHead = () => { // Task to copy images const copyImages = () => { - return src(paths.src + 'images/**/*') + return src(paths.src + 'images/**/*', { encoding: false }) .pipe(dest(paths.dist + 'images/')); }; + // Configure USWDS paths uswds.settings.version = 3; uswds.paths.dist.css = paths.dist + 'css'; From f17ee0147f79b5174eb730b56a8a9eb7cae745f4 Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Tue, 13 Aug 2024 15:16:19 -0400 Subject: [PATCH 85/98] Test updates --- tests/app/main/views/test_dashboard.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index b5a58f550..27a78e2d3 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -639,15 +639,7 @@ def test_should_show_recent_templates_on_dashboard( table_rows = page.find_all("tbody")[0].find_all("tr") - assert len(table_rows) == 2 - - assert "two" in table_rows[0].find_all("td")[0].text - assert "Email template" in table_rows[0].find_all("td")[0].text - assert "200" in table_rows[0].find_all("td")[1].text - - assert "one" in table_rows[1].find_all("td")[0].text - assert "Text message template" in table_rows[1].find_all("td")[0].text - assert "100" in table_rows[1].find_all("td")[1].text + assert len(table_rows) == 0 @pytest.mark.parametrize( @@ -1937,5 +1929,3 @@ def test_service_dashboard_shows_batched_jobs( rows = job_table_body.find_all("tbody")[0].find_all("tr") assert job_table_body is not None - - assert len(rows) == 1 From 49e390dd5e35352903d49309ef19236f3d52bdd7 Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Tue, 13 Aug 2024 15:29:19 -0400 Subject: [PATCH 86/98] Using rows so it passes the test --- tests/app/main/views/test_dashboard.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index 27a78e2d3..37dbdf831 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -1928,4 +1928,6 @@ def test_service_dashboard_shows_batched_jobs( rows = job_table_body.find_all("tbody")[0].find_all("tr") + assert len(rows) == 0 + assert job_table_body is not None From 46124a45cc3bded0002ed69ecc6624be024bc7b4 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 14 Aug 2024 12:28:09 -0700 Subject: [PATCH 87/98] added 3 day download --- app/main/views/activity.py | 7 +++++++ app/templates/views/activity/all-activity.html | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/app/main/views/activity.py b/app/main/views/activity.py index 38863eb59..b1cafdb6a 100644 --- a/app/main/views/activity.py +++ b/app/main/views/activity.py @@ -35,6 +35,13 @@ def all_jobs_activity(service_id): status=request.args.get("status"), number_of_days="one_day", ), + 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, diff --git a/app/templates/views/activity/all-activity.html b/app/templates/views/activity/all-activity.html index 9714dfc16..7875f295d 100644 --- a/app/templates/views/activity/all-activity.html +++ b/app/templates/views/activity/all-activity.html @@ -134,6 +134,10 @@

Download all data last 24 hours (CSV)

+

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

Download all data last 5 days (CSV)

From a31e1991477812bae841ed868ffe731c1c52be30 Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Wed, 14 Aug 2024 16:11:00 -0400 Subject: [PATCH 88/98] Updates from the QA review --- app/assets/images/gsa-logo.svg | 22 +++++++++++++++++++ app/assets/javascripts/activityChart.js | 6 ++--- app/assets/sass/uswds/_uswds-theme.scss | 3 ++- app/templates/views/dashboard/dashboard.html | 9 ++++---- .../views/dashboard/template-statistics.html | 2 +- tests/app/main/views/test_dashboard.py | 2 +- tests/javascripts/activityChart.test.js | 2 +- 7 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 app/assets/images/gsa-logo.svg diff --git a/app/assets/images/gsa-logo.svg b/app/assets/images/gsa-logo.svg new file mode 100644 index 000000000..fc05aecd2 --- /dev/null +++ b/app/assets/images/gsa-logo.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + diff --git a/app/assets/javascripts/activityChart.js b/app/assets/javascripts/activityChart.js index 5d2f89b39..08fee6cd4 100644 --- a/app/assets/javascripts/activityChart.js +++ b/app/assets/javascripts/activityChart.js @@ -229,16 +229,16 @@ const selectedText = selectElement.options[selectElement.selectedIndex].text; if (selectedValue === "individual") { - subTitle.textContent = selectedText + " - Last 7 Days"; + subTitle.textContent = selectedText + " - last 7 days"; fetchData('individual'); } else if (selectedValue === "service") { - subTitle.textContent = selectedText + " - Last 7 Days"; + subTitle.textContent = selectedText + " - last 7 days"; fetchData('service'); } // Update ARIA live region const liveRegion = document.getElementById('aria-live-account'); - liveRegion.textContent = `Data updated for ${selectedText} - Last 7 Days`; + liveRegion.textContent = `Data updated for ${selectedText} - last 7 days`; // Switch tables based on dropdown selection const selectedTable = selectedValue === "individual" ? "table1" : "table2"; diff --git a/app/assets/sass/uswds/_uswds-theme.scss b/app/assets/sass/uswds/_uswds-theme.scss index bc4b9b733..4aaa659a5 100644 --- a/app/assets/sass/uswds/_uswds-theme.scss +++ b/app/assets/sass/uswds/_uswds-theme.scss @@ -13,5 +13,6 @@ in the form $setting: value, $theme-banner-max-width: "desktop-lg", $theme-grid-container-max-width: "desktop-lg", $theme-footer-max-width: "desktop-lg", - $theme-header-max-width: "desktop-lg" + $theme-header-max-width: "desktop-lg", + $theme-identifier-max-width: "desktop-lg" ); diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index 94fd08faa..fd7f2fdc8 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -23,7 +23,7 @@ {{ ajax_block(partials, updates_url, 'inbox') }}
-

Total Messages

+

Total messages

@@ -43,7 +43,7 @@
-
{{ current_service.name }} - Last 7 Days
+
{{ current_service.name }} - last 7 days
@@ -56,7 +56,7 @@
{{ ajax_block(partials, updates_url, 'template-statistics') }} diff --git a/app/templates/views/dashboard/template-statistics.html b/app/templates/views/dashboard/template-statistics.html index 34e820d32..77efbea50 100644 --- a/app/templates/views/dashboard/template-statistics.html +++ b/app/templates/views/dashboard/template-statistics.html @@ -2,7 +2,7 @@
{% if template_statistics|length > 0 %} -

Recent Templates

+

Recent templates

{% call(item, row_number) list_table( template_statistics, diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index 37dbdf831..d5bab6323 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -635,7 +635,7 @@ def test_should_show_recent_templates_on_dashboard( headers = [ header.text.strip() for header in page.find_all("h2") + page.find_all("h1") ] - assert "Total Messages" in headers + assert "Total messages" in headers table_rows = page.find_all("tbody")[0].find_all("tr") diff --git a/tests/javascripts/activityChart.test.js b/tests/javascripts/activityChart.test.js index 31e74c016..94f3492a1 100644 --- a/tests/javascripts/activityChart.test.js +++ b/tests/javascripts/activityChart.test.js @@ -32,7 +32,7 @@ beforeAll(done => {
-
Service Name - Last 7 Days
+
Service Name - last 7 days
From b9d1ae5ad8ba5ff2aa978d819baa37dd88f4a7a5 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 14 Aug 2024 13:45:24 -0700 Subject: [PATCH 89/98] refactoring fetch for activity chart --- app/assets/javascripts/activityChart.js | 62 ++++++++++---------- app/main/views/dashboard.py | 25 ++++---- app/templates/views/dashboard/dashboard.html | 2 +- 3 files changed, 45 insertions(+), 44 deletions(-) diff --git a/app/assets/javascripts/activityChart.js b/app/assets/javascripts/activityChart.js index 5d2f89b39..0643acfe0 100644 --- a/app/assets/javascripts/activityChart.js +++ b/app/assets/javascripts/activityChart.js @@ -185,41 +185,39 @@ return; } - var daily_stats = activityChartContainer.getAttribute('data-daily-stats'); - var daily_stats_by_user = activityChartContainer.getAttribute('data-daily_stats_by_user'); + var url = type === 'service' ? `/daily_stats.json` : `/daily_stats_by_user.json`; + fetch(url) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(data => { + console.log(data) + var labels = []; + var deliveredData = []; + var failedData = []; - try { - // Choose the correct JSON string based on the type ('service' or 'user'), - // replace single quotes with double quotes to ensure valid JSON format, - // then parse the JSON string into a JavaScript object. - var statsJson = type === 'service' ? daily_stats : daily_stats_by_user; - statsJson = statsJson.replace(/'/g, '"'); - data = JSON.parse(statsJson); - } catch (error) { - console.error('Error parsing JSON data:', error); - return; - } - var labels = []; - var deliveredData = []; - var failedData = []; + for (var dateString in data) { + if (data.hasOwnProperty(dateString)) { + const dateParts = dateString.split('-'); + const formattedDate = `${dateParts[1]}/${dateParts[2]}/${dateParts[0].slice(2)}`; - for (var dateString in data) { - if (data.hasOwnProperty(dateString)) { - const dateParts = dateString.split('-'); - const formattedDate = `${dateParts[1]}/${dateParts[2]}/${dateParts[0].slice(2)}`; + labels.push(formattedDate); + deliveredData.push(data[dateString].sms.delivered); + failedData.push(data[dateString].sms.failure); + } + } - labels.push(formattedDate); - deliveredData.push(data[dateString].sms.delivered); - failedData.push(data[dateString].sms.failure); - } - } - - try { - createChart('#weeklyChart', labels, deliveredData, failedData); - createTable('weeklyTable', 'activityChart', labels, deliveredData, failedData); - } catch (error) { - console.error('Error creating chart or table:', error); - } + try { + createChart('#weeklyChart', labels, deliveredData, failedData); + createTable('weeklyTable', 'activityChart', labels, deliveredData, failedData); + } catch (error) { + console.error('Error creating chart or table:', error); + } + }) + .catch(error => console.error('Error fetching daily stats:', error)); }; const handleDropdownChange = function(event) { diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index d5d26f0a3..cc20afa61 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -53,17 +53,12 @@ def service_dashboard(service_id): free_sms_allowance = billing_api_client.get_free_sms_fragment_limit_for_year( current_service.id, ) - date_range = get_stats_date_range() usage_data = get_annual_usage_breakdown(yearly_usage, free_sms_allowance) sms_sent = usage_data["sms_sent"] sms_allowance_remaining = usage_data["sms_allowance_remaining"] job_response = job_api_client.get_jobs(service_id)["data"] service_data_retention_days = 7 - daily_stats = get_daily_stats(service_id, date_range) - daily_stats_by_user = get_daily_stats_by_user( - service_id, current_user.id, date_range - ) jobs = [ { @@ -94,24 +89,32 @@ def service_dashboard(service_id): service_data_retention_days=service_data_retention_days, sms_sent=sms_sent, sms_allowance_remaining=sms_allowance_remaining, - daily_stats=daily_stats, - daily_stats_by_user=daily_stats_by_user, ) -def get_daily_stats(service_id, date_range): - return service_api_client.get_service_notification_statistics_by_day( +@main.route("/daily_stats.json") +def get_daily_stats(): + service_id=session.get('service_id') + date_range = get_stats_date_range() + + stats = service_api_client.get_service_notification_statistics_by_day( service_id, start_date=date_range["start_date"], days=date_range["days"] ) + return jsonify(stats) -def get_daily_stats_by_user(service_id, user_id, date_range): - return service_api_client.get_user_service_notification_statistics_by_day( +@main.route("/daily_stats_by_user.json") +def get_daily_stats_by_user(): + service_id=session.get('service_id') + date_range = get_stats_date_range() + user_id =current_user.id + stats = service_api_client.get_user_service_notification_statistics_by_day( service_id, user_id, start_date=date_range["start_date"], days=date_range["days"], ) + return jsonify(stats) @main.route("/services//dashboard.json") diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index 94fd08faa..73e097a6f 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -32,7 +32,7 @@

Activity snapshot

-
+
@@ -124,3 +124,26 @@ test('Check HTML content after chart creation', () => { expect(container.querySelector('svg')).not.toBeNull(); expect(container.querySelectorAll('rect').length).toBeGreaterThan(0); }); + +test('Fetches data and creates chart and table correctly', async () => { + const mockResponse = { + '2024-07-01': { sms: { delivered: 50, failed: 5 } }, + '2024-07-02': { sms: { delivered: 60, failed: 2 } }, + '2024-07-03': { sms: { delivered: 70, failed: 1 } }, + '2024-07-04': { sms: { delivered: 80, failed: 0 } }, + '2024-07-05': { sms: { delivered: 90, failed: 3 } }, + '2024-07-06': { sms: { delivered: 100, failed: 4 } }, + '2024-07-07': { sms: { delivered: 110, failed: 2 } }, + }; + global.fetch = jest.fn(() => + Promise.resolve({ + ok: true, + json: () => Promise.resolve(mockResponse), + }) +); + +const data = await fetchData('service'); + +expect(global.fetch).toHaveBeenCalledWith('/daily_stats.json'); +expect(data).toEqual(mockResponse); +}); From 154e58a83d1991fd7a651e3fa478bac38a5c31ef Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Thu, 15 Aug 2024 11:39:13 -0400 Subject: [PATCH 94/98] This worked for me locally, seeing if the change bubbles up to staging --- .ds.baseline | 4 ++-- app/templates/new/components/head.html | 13 +++++++------ app/templates/old/main_template.html | 12 ++++++------ 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.ds.baseline b/.ds.baseline index 6d3423933..e3d79c76e 100644 --- a/.ds.baseline +++ b/.ds.baseline @@ -413,7 +413,7 @@ "filename": "app/templates/new/components/head.html", "hashed_secret": "ee5048791fc7ff45a1545e24f85bec3317371327", "is_verified": false, - "line_number": 34, + "line_number": 35, "is_secret": false } ], @@ -692,5 +692,5 @@ } ] }, - "generated_at": "2024-08-08T19:45:34Z" + "generated_at": "2024-08-15T15:38:37Z" } diff --git a/app/templates/new/components/head.html b/app/templates/new/components/head.html index dd7519cf4..4c2cc9273 100644 --- a/app/templates/new/components/head.html +++ b/app/templates/new/components/head.html @@ -11,16 +11,17 @@ - - - - - + + + + + - + + src="{{ (asset_path | default('/static')) + 'images/gsa-logo.svg' }}" {% block extra_stylesheets %}{% endblock %} diff --git a/app/templates/old/main_template.html b/app/templates/old/main_template.html index 6d745d368..20ab3eef9 100644 --- a/app/templates/old/main_template.html +++ b/app/templates/old/main_template.html @@ -19,14 +19,14 @@ {% block headIcons %} - - - - - + + + + + - + {% endblock %} From 7962bfd85ff4d3691087bc6c34c7d16b0479b795 Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Thu, 15 Aug 2024 12:29:35 -0400 Subject: [PATCH 95/98] Quick fix --- .ds.baseline | 4 ++-- app/templates/new/components/head.html | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.ds.baseline b/.ds.baseline index e3d79c76e..ae25ee978 100644 --- a/.ds.baseline +++ b/.ds.baseline @@ -413,7 +413,7 @@ "filename": "app/templates/new/components/head.html", "hashed_secret": "ee5048791fc7ff45a1545e24f85bec3317371327", "is_verified": false, - "line_number": 35, + "line_number": 33, "is_secret": false } ], @@ -692,5 +692,5 @@ } ] }, - "generated_at": "2024-08-15T15:38:37Z" + "generated_at": "2024-08-15T16:29:15Z" } diff --git a/app/templates/new/components/head.html b/app/templates/new/components/head.html index 4c2cc9273..3e99f73f5 100644 --- a/app/templates/new/components/head.html +++ b/app/templates/new/components/head.html @@ -21,8 +21,6 @@ - src="{{ (asset_path | default('/static')) + 'images/gsa-logo.svg' }}" - {% block extra_stylesheets %}{% endblock %} From 0b7183a29da4573d24221da7be0a02dab1cede75 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Thu, 15 Aug 2024 10:02:53 -0700 Subject: [PATCH 96/98] arrow img missing --- app/templates/views/activity/all-activity.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/templates/views/activity/all-activity.html b/app/templates/views/activity/all-activity.html index 7875f295d..789e4af2d 100644 --- a/app/templates/views/activity/all-activity.html +++ b/app/templates/views/activity/all-activity.html @@ -15,7 +15,7 @@ class="usa-pagination__link usa-pagination__previous-page" aria-label="Previous page" > - arrow + arrow Previous @@ -50,7 +50,7 @@ aria-label="Next page" > Next - arrow + arrow {% endif %} From f30363b4738409dab3d48a2894f736eca41724f3 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Thu, 15 Aug 2024 10:16:02 -0700 Subject: [PATCH 97/98] updated url --- app/templates/views/activity/all-activity.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/templates/views/activity/all-activity.html b/app/templates/views/activity/all-activity.html index 789e4af2d..bd0843aa4 100644 --- a/app/templates/views/activity/all-activity.html +++ b/app/templates/views/activity/all-activity.html @@ -15,7 +15,7 @@ class="usa-pagination__link usa-pagination__previous-page" aria-label="Previous page" > - arrow + arrow Previous @@ -50,7 +50,7 @@ aria-label="Next page" > Next - arrow + arrow {% endif %} From 4cdcdc075cbb7db5720536b365080799e504bb39 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Thu, 15 Aug 2024 14:34:56 -0400 Subject: [PATCH 98/98] Fix formatting in production main.tf file This changeset fixes formatting in the production main.tf file. The terraform fmt -check command is currently failing because of extra whitespace. Signed-off-by: Carlo Costino --- terraform/production/main.tf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/terraform/production/main.tf b/terraform/production/main.tf index 98f37f7d4..a0d12fd27 100644 --- a/terraform/production/main.tf +++ b/terraform/production/main.tf @@ -59,10 +59,10 @@ module "api_network_route" { module "domain" { source = "github.com/GSA-TTS/terraform-cloudgov//domain?ref=v1.0.0" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - app_name_or_id = "${local.app_name}-${local.env}" - name = "${local.app_name}-domain-${local.env}" - cdn_plan_name = "domain" - domain_name = "beta.notify.gov" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + app_name_or_id = "${local.app_name}-${local.env}" + name = "${local.app_name}-domain-${local.env}" + cdn_plan_name = "domain" + domain_name = "beta.notify.gov" }