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); -});