diff --git a/.ds.baseline b/.ds.baseline index 999298014..f0d0e6d2c 100644 --- a/.ds.baseline +++ b/.ds.baseline @@ -527,7 +527,7 @@ "filename": "tests/app/main/views/test_accept_invite.py", "hashed_secret": "07f0a6c13923fc3b5f0c57ffa2d29b715eb80d71", "is_verified": false, - "line_number": 643, + "line_number": 631, "is_secret": false } ], @@ -684,5 +684,5 @@ } ] }, - "generated_at": "2025-02-03T17:01:06Z" + "generated_at": "2025-02-26T18:19:37Z" } diff --git a/app/assets/javascripts/totalMessagesChart.js b/app/assets/javascripts/totalMessagesChart.js index 68c9fd8f9..2c4003987 100644 --- a/app/assets/javascripts/totalMessagesChart.js +++ b/app/assets/javascripts/totalMessagesChart.js @@ -6,17 +6,17 @@ var chartTitle = document.getElementById('chartTitle').textContent; // Access data attributes from the HTML - var sms_sent = parseInt(chartContainer.getAttribute('data-sms-sent')); - var sms_remaining_messages = parseInt(chartContainer.getAttribute('data-sms-allowance-remaining')); - var totalMessages = sms_sent + sms_remaining_messages; + var messagesSent = parseInt(chartContainer.getAttribute('data-messages-sent')); + var messagesRemaining = parseInt(chartContainer.getAttribute('data-messages-remaining')); + var totalMessages = messagesSent + messagesRemaining; // Update the message below the chart - document.getElementById('message').innerText = `${sms_sent.toLocaleString()} sent / ${sms_remaining_messages.toLocaleString()} remaining`; + document.getElementById('message').innerText = `${messagesSent.toLocaleString()} sent / ${messagesRemaining.toLocaleString()} remaining`; // Calculate minimum width for "Messages Sent" as 1% of the total chart width - var minSentPercentage = (sms_sent === 0) ? 0 : 0.02; + var minSentPercentage = (messagesSent === 0) ? 0 : 0.02; var minSentValue = totalMessages * minSentPercentage; - var displaySent = Math.max(sms_sent, minSentValue); + var displaySent = Math.max(messagesSent, minSentValue); var displayRemaining = totalMessages - displaySent; var svg = d3.select("#totalMessageChart"); @@ -48,7 +48,7 @@ .attr("width", 0) // Start with width 0 for animation .on('mouseover', function(event) { tooltip.style('display', 'block') - .html(`Messages Sent: ${sms_sent.toLocaleString()}`); + .html(`Messages Sent: ${messagesSent.toLocaleString()}`); }) .on('mousemove', function(event) { tooltip.style('left', `${event.pageX + 10}px`) @@ -66,7 +66,7 @@ .attr("width", 0) // Start with width 0 for animation .on('mouseover', function(event) { tooltip.style('display', 'block') - .html(`Remaining: ${sms_remaining_messages.toLocaleString()}`); + .html(`Remaining: ${messagesRemaining.toLocaleString()}`); }) .on('mousemove', function(event) { tooltip.style('left', `${event.pageX + 10}px`) @@ -115,9 +115,9 @@ var tbodyRow = document.createElement('tr'); var tdMessagesSent = document.createElement('td'); - tdMessagesSent.textContent = sms_sent.toLocaleString(); // Value for Messages Sent + tdMessagesSent.textContent = messagesSent.toLocaleString(); // Value for Messages Sent var tdRemaining = document.createElement('td'); - tdRemaining.textContent = sms_remaining_messages.toLocaleString(); // Value for Remaining + tdRemaining.textContent = messagesRemaining.toLocaleString(); // Value for Remaining tbodyRow.appendChild(tdMessagesSent); tbodyRow.appendChild(tdRemaining); diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index e172b53ba..52a30b5de 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -49,17 +49,6 @@ def service_dashboard(service_id): if not current_user.has_permissions("view_activity"): return redirect(url_for("main.choose_template", service_id=service_id)) - yearly_usage = billing_api_client.get_annual_usage_for_service( - service_id, - get_current_financial_year(), - ) - free_sms_allowance = billing_api_client.get_free_sms_fragment_limit_for_year( - current_service.id, - ) - 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 @@ -70,14 +59,17 @@ def service_dashboard(service_id): for job_dict in sorted_jobs ] + total_messages = service_api_client.get_service_message_ratio(service_id) + messages_remaining = total_messages.get("messages_remaining", 0) + messages_sent = total_messages.get("messages_sent", 0) return render_template( "views/dashboard/dashboard.html", updates_url=url_for(".service_dashboard_updates", service_id=service_id), partials=get_dashboard_partials(service_id), jobs=job_lists, service_data_retention_days=service_data_retention_days, - sms_sent=sms_sent, - sms_allowance_remaining=sms_allowance_remaining, + messages_remaining=messages_remaining, + messages_sent=messages_sent, ) diff --git a/app/notify_client/service_api_client.py b/app/notify_client/service_api_client.py index 0229fee3d..6ccc2747e 100644 --- a/app/notify_client/service_api_client.py +++ b/app/notify_client/service_api_client.py @@ -537,6 +537,11 @@ class ServiceAPIClient(NotifyAdminAPIClient): """ return self.get("/service/invite/redis/{0}".format(redis_key)) + def get_service_message_ratio(self, service_id): + return self.get( + url="service/get-service-message-ratio?service_id={0}".format(service_id), + ) + service_api_client = ServiceAPIClient() diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index bd19585c3..15df829f3 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -26,7 +26,7 @@ {{ ajax_block(partials, updates_url, 'inbox') }} -
+

Total messages

diff --git a/tests/app/main/views/test_accept_invite.py b/tests/app/main/views/test_accept_invite.py index ef5e56fda..297dc7603 100644 --- a/tests/app/main/views/test_accept_invite.py +++ b/tests/app/main/views/test_accept_invite.py @@ -300,25 +300,13 @@ 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.main.views.dashboard.get_daily_stats", + "app.service_api_client.get_service_message_ratio", 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}, - }, + "messages_remaining": 71919, + "messages_sent": 28081, + "total_message_limit": 100000, }, ) page = client_request.get( diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index fbf2f6d5e..32d6b9bee 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -196,6 +196,12 @@ mock_daily_stats_by_user = { }, } +mock_service_message_ratio = { + "messages_remaining": 71919, + "messages_sent": 28081, + "total_message_limit": 100000, +} + @pytest.mark.parametrize( "user", @@ -255,12 +261,8 @@ def test_get_started( ) 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get( "main.service_dashboard", @@ -289,12 +291,8 @@ def test_get_started_is_hidden_once_templates_exist( ) 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get( "main.service_dashboard", @@ -320,12 +318,8 @@ 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get( "main.service_dashboard", @@ -351,12 +345,8 @@ def test_inbound_messages_shows_count_of_messages_when_there_are_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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get( "main.service_dashboard", @@ -387,12 +377,8 @@ def test_inbound_messages_shows_count_of_messages_when_there_are_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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get( "main.service_dashboard", @@ -641,12 +627,8 @@ def test_should_show_recent_templates_on_dashboard( ) 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get( "main.service_dashboard", @@ -693,12 +675,8 @@ def test_should_not_show_recent_templates_on_dashboard_if_only_one_template_used ) 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID) main = page.select_one("main").text @@ -852,12 +830,8 @@ def test_should_show_upcoming_jobs_on_dashboard( 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get( "main.service_dashboard", @@ -899,12 +873,8 @@ 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get( "main.service_dashboard", @@ -929,12 +899,8 @@ def test_should_not_show_upcoming_jobs_on_dashboard_if_service_has_no_jobs( ): 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get( "main.service_dashboard", @@ -1019,12 +985,8 @@ def test_should_not_show_jobs_on_dashboard_for_users_with_uploads_page( 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get( "main.service_dashboard", @@ -1287,12 +1249,8 @@ def test_menu_send_messages( 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = _test_dashboard_menu( client_request, @@ -1328,12 +1286,8 @@ def test_menu_manage_service( ): 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = _test_dashboard_menu( client_request, @@ -1369,12 +1323,8 @@ def test_menu_main_settings( ): 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = _test_settings_menu( client_request, @@ -1409,12 +1359,8 @@ def test_menu_manage_api_keys( ): 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = _test_dashboard_menu( client_request, @@ -1450,12 +1396,8 @@ def test_menu_all_services_for_platform_admin_user( ): 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = _test_dashboard_menu( client_request, mocker, platform_admin_user, service_one, [] @@ -1493,12 +1435,8 @@ def test_route_for_service_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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) validate_route_permission( mocker, @@ -1642,12 +1580,8 @@ def test_org_breadcrumbs_do_not_show_if_service_has_no_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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID) @@ -1713,12 +1647,8 @@ 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID) @@ -1753,12 +1683,8 @@ def test_org_breadcrumbs_do_not_show_if_user_is_a_member_of_the_services_org_but 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID) @@ -1795,12 +1721,8 @@ 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) client_request.login(platform_admin_user, service_one_json) @@ -1832,14 +1754,9 @@ 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID) @@ -1872,12 +1789,8 @@ 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) service_one["permissions"] = permissions @@ -1912,12 +1825,8 @@ 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) @@ -1933,12 +1842,8 @@ def test_service_dashboard_shows_batched_jobs( ): 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_by_user", - return_value=mock_daily_stats_by_user, + "app.service_api_client.get_service_message_ratio", + return_value=mock_service_message_ratio, ) page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID) diff --git a/tests/app/main/views/test_sign_out.py b/tests/app/main/views/test_sign_out.py index 9afbfab97..995050204 100644 --- a/tests/app/main/views/test_sign_out.py +++ b/tests/app/main/views/test_sign_out.py @@ -128,25 +128,12 @@ 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.main.views.dashboard.get_daily_stats", + "app.service_api_client.get_service_message_ratio", 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}, - }, + "messages_remaining": 71919, + "messages_sent": 28081, + "total_message_limit": 100000, }, ) diff --git a/tests/javascripts/totalMessagesChart.test.js b/tests/javascripts/totalMessagesChart.test.js index 87c671430..5138f9a43 100644 --- a/tests/javascripts/totalMessagesChart.test.js +++ b/tests/javascripts/totalMessagesChart.test.js @@ -22,7 +22,7 @@ Object.defineProperty(HTMLElement.prototype, 'clientWidth', { beforeEach(() => { // Set up the DOM with the D3 script included document.body.innerHTML = ` -
+

Total Messages

@@ -76,8 +76,8 @@ test('Populates the accessible table correctly', () => { expect(headers[1].textContent).toBe('Remaining'); const firstRowCells = rows[1].getElementsByTagName('td'); - expect(firstRowCells[0].textContent).toBe('100'); - expect(firstRowCells[1].textContent).toBe('249,900'); + expect(firstRowCells[0].textContent).toBe('28,081'); + expect(firstRowCells[1].textContent).toBe('71,919'); }); // Test to check if the chart title is correctly set @@ -114,7 +114,7 @@ test('Chart resizes correctly on window resize', done => { // Testing the tooltip test('Tooltip displays on hover', () => { document.body.innerHTML = ` -
+

Total Messages