From 93d7ca405254e53492d4cc8947344c5cf46fb355 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 6 May 2024 17:23:38 -0700 Subject: [PATCH 01/25] added pending and requested status to monthly stats dict --- app/main/views/dashboard.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 8453ef369..89d05d6a6 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -24,6 +24,7 @@ from app.utils import ( DELIVERED_STATUSES, FAILURE_STATUSES, REQUESTED_STATUSES, + SENDING_STATUSES, service_has_permission, ) from app.utils.csv import Spreadsheet @@ -341,6 +342,9 @@ def get_dashboard_partials(service_id): service_id, free_sms_fragment_limit=free_sms_allowance ) + monthly_stats = format_monthly_stats_to_list( + service_api_client.get_monthly_notification_stats(service_id, get_current_financial_year())["data"] + ) yearly_usage = billing_api_client.get_annual_usage_for_service( service_id, get_current_financial_year(), @@ -366,6 +370,7 @@ def get_dashboard_partials(service_id): ), "usage": render_template( "views/dashboard/_usage.html", + monthly_stats=monthly_stats, **get_annual_usage_breakdown(yearly_usage, free_sms_allowance), ), } @@ -424,6 +429,8 @@ def aggregate_status_types(counts_dict): "{}_counts".format(message_type): { "failed": sum(stats.get(status, 0) for status in FAILURE_STATUSES), "requested": sum(stats.get(status, 0) for status in REQUESTED_STATUSES), + "delivered": sum(stats.get(status, 0) for status in DELIVERED_STATUSES), + "pending": sum(stats.get(status, 0) for status in SENDING_STATUSES), } for message_type, stats in counts_dict.items() } From ad68c264503ddc9b8b5b6cdc1a9ed5b59aa6a388 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 20 May 2024 16:21:38 -0700 Subject: [PATCH 02/25] installing socketIO and testing the WebSocket connection --- app/__init__.py | 3 +++ app/assets/javascripts/socket.js | 23 ++++++++++++++++++++ app/main/views/dashboard.py | 12 ++++++++++ app/templates/new/components/head.html | 1 + app/templates/views/dashboard/dashboard.html | 3 +++ gulpfile.js | 1 + 6 files changed, 43 insertions(+) create mode 100644 app/assets/javascripts/socket.js diff --git a/app/__init__.py b/app/__init__.py index 5c9283c79..7ecc5dd3b 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -18,6 +18,7 @@ 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 @@ -118,6 +119,7 @@ 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. @@ -175,6 +177,7 @@ def create_app(application): init_govuk_frontend(application) init_jinja(application) + socketio.init_app(application) for client in ( csrf, diff --git a/app/assets/javascripts/socket.js b/app/assets/javascripts/socket.js new file mode 100644 index 000000000..608fa0bde --- /dev/null +++ b/app/assets/javascripts/socket.js @@ -0,0 +1,23 @@ + +(function (window) { + document.addEventListener('DOMContentLoaded', (event) => { + var socket = io(); + + socket.on('connect', function() { + console.log('Connected to the server'); + }); + + socket.on('message', function(msg) { + var li = document.createElement("li"); + li.appendChild(document.createTextNode(msg)); + document.getElementById("messages").appendChild(li); + }); + + document.getElementById('sendButton').addEventListener('click', function() { + var message = document.getElementById("message").value; + socket.send(message); + document.getElementById("message").value = ''; + }); + }); + +})(window); diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 3bbf432b3..042cbe94e 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -6,6 +6,7 @@ from itertools import groupby from flask import Response, abort, jsonify, render_template, request, session, url_for from flask_login import current_user +from flask_socketio import send, emit from werkzeug.utils import redirect from app import ( @@ -15,6 +16,7 @@ from app import ( notification_api_client, service_api_client, template_statistics_client, + socketio ) from app.formatters import format_date_numeric, format_datetime_numeric, get_time_left from app.main import main @@ -32,6 +34,16 @@ from app.utils.user import user_has_permissions from notifications_utils.recipients import format_phone_number_human_readable +@socketio.on('message') +def handle_message(msg): + print('''Message: + + + + ''' + msg) + emit('message', msg, broadcast=True) + + @main.route("/services//dashboard") @user_has_permissions("view_activity", "send_messages") def old_service_dashboard(service_id): diff --git a/app/templates/new/components/head.html b/app/templates/new/components/head.html index 51f3c4da3..f7c7153e2 100644 --- a/app/templates/new/components/head.html +++ b/app/templates/new/components/head.html @@ -32,6 +32,7 @@ {# google #} + {% if g.hide_from_search_engines %} diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index 0a558ad8f..4a2751eb2 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -22,6 +22,9 @@ Messages sent + +
    + {{ ajax_block(partials, updates_url, 'inbox') }} {{ ajax_block(partials, updates_url, 'totals') }} diff --git a/gulpfile.js b/gulpfile.js index 98afbbacf..3b7d765a7 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -126,6 +126,7 @@ const javascripts = () => { paths.src + 'javascripts/loginAlert.js', paths.src + 'javascripts/main.js', paths.src + 'javascripts/chartDashboard.js', + paths.src + 'javascripts/socket.js', ]) .pipe(plugins.prettyerror()) .pipe(plugins.babel({ From 758c1cd8a0f59c337a60106536c7981898d8cc57 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 29 May 2024 12:13:09 -0700 Subject: [PATCH 03/25] added new api for month, by year, previous 7 day stats --- app/main/views/dashboard.py | 34 ++++++++++++++++++++----- app/notify_client/service_api_client.py | 18 +++++++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 89d05d6a6..33e98e104 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -19,6 +19,7 @@ from app import ( ) from app.formatters import format_date_numeric, format_datetime_numeric, get_time_left from app.main import main +from app.models.user import User from app.statistics_utils import get_formatted_percentage from app.utils import ( DELIVERED_STATUSES, @@ -324,6 +325,11 @@ def aggregate_notifications_stats(template_statistics): def get_dashboard_partials(service_id): + current_financial_year = get_current_financial_year() + current_month = get_current_month_for_financial_year(current_financial_year) + start_date = datetime.now().strftime('%Y-%m-%d') + days=7 + all_statistics = template_statistics_client.get_template_statistics_for_service( service_id, limit_days=7 ) @@ -336,19 +342,30 @@ def get_dashboard_partials(service_id): ) # These 2 calls will update the dashboard sms allowance count while in trial mode. billing_api_client.get_monthly_usage_for_service( - service_id, get_current_financial_year() + service_id, current_financial_year ) billing_api_client.create_or_update_free_sms_fragment_limit( service_id, free_sms_fragment_limit=free_sms_allowance ) - - monthly_stats = format_monthly_stats_to_list( - service_api_client.get_monthly_notification_stats(service_id, get_current_financial_year())["data"] - ) yearly_usage = billing_api_client.get_annual_usage_for_service( service_id, - get_current_financial_year(), + current_financial_year, ) + + #Previous 7 day stats + daily_stats = service_api_client.get_service_notification_statistics_by_day(service_id, start_date=start_date, days=days) + + #Single month stats + single_month_notification_stats = service_api_client.get_single_month_notification_stats(service_id, year=current_financial_year, month=current_month) + + #monthly stats by year + monthly_stats = format_monthly_stats_to_list( + service_api_client.get_monthly_notification_stats(service_id, current_financial_year)["data"] + ) + + # user=User.from_id(user_id), + # single_month_notification_stats = service_api_client.get_single_month_notification_stats_by_user(service_id, user, year=current_financial_year, month=current_month) + return { "upcoming": render_template( "views/dashboard/_upcoming.html", @@ -441,6 +458,11 @@ def get_months_for_financial_year(year, time_format="%B"): return [month.strftime(time_format) for month in (get_months_for_year(1, 13, year))] +def get_current_month_for_financial_year(year): + current_month = datetime.now().month + return current_month + + def get_months_for_year(start, end, year): return [datetime(year, month, 1) for month in range(start, end)] diff --git a/app/notify_client/service_api_client.py b/app/notify_client/service_api_client.py index d34516b8b..f2cc2f934 100644 --- a/app/notify_client/service_api_client.py +++ b/app/notify_client/service_api_client.py @@ -43,6 +43,24 @@ class ServiceAPIClient(NotifyAdminAPIClient): params={"limit_days": limit_days}, )["data"] + def get_service_notification_statistics_by_day(self, service_id, start_date=None, days=None): + if start_date is None: + start_date = datetime.now().strftime('%Y-%m-%d') + + return self.get( + "/service/{0}/statistics/{1}/{2}".format(service_id, start_date, days), + )["data"] + + def get_single_month_notification_stats(self, service_id, year, month): + return self.get( + "/service/{0}/notifications/month?year={1}&month={2}".format(service_id, year, month), + ) + + # def get_single_month_notification_stats(self, service_id, user_id, year, month): + # return self.get( + # "/service/{0}/notifications//month?year={1}&month={2}".format(service_id, user_id, year, month), + # ) + def get_services(self, params_dict=None): """ Retrieve a list of services. From 108e889ac564dae4478c61a0a21e6e02c233c190 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Thu, 30 May 2024 14:48:25 -0700 Subject: [PATCH 04/25] added example of fetch --- app/assets/javascripts/socket.js | 14 ++++++-------- app/main/views/dashboard.py | 19 ++++++++++++------- app/templates/views/dashboard/dashboard.html | 4 ++-- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/app/assets/javascripts/socket.js b/app/assets/javascripts/socket.js index 608fa0bde..c4a54198d 100644 --- a/app/assets/javascripts/socket.js +++ b/app/assets/javascripts/socket.js @@ -7,16 +7,14 @@ console.log('Connected to the server'); }); - socket.on('message', function(msg) { - var li = document.createElement("li"); - li.appendChild(document.createTextNode(msg)); - document.getElementById("messages").appendChild(li); + // Listen for job updates from the server + socket.on('job_update', function(data) { + console.log('Received job update:', data); }); - document.getElementById('sendButton').addEventListener('click', function() { - var message = document.getElementById("message").value; - socket.send(message); - document.getElementById("message").value = ''; + document.getElementById('fetchJobsButton').addEventListener('click', function() { + const serviceId = 'b1226555-1f1a-472c-9086-043b0a69f4ec'; // Example service ID + socket.emit('fetch_jobs', serviceId); }); }); diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 042cbe94e..60c44ee13 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -6,7 +6,7 @@ from itertools import groupby from flask import Response, abort, jsonify, render_template, request, session, url_for from flask_login import current_user -from flask_socketio import send, emit +from flask_socketio import SocketIO, emit from werkzeug.utils import redirect from app import ( @@ -34,14 +34,20 @@ from app.utils.user import user_has_permissions from notifications_utils.recipients import format_phone_number_human_readable -@socketio.on('message') -def handle_message(msg): - print('''Message: +# @socketio.on('connect') +# def handle_connect(): +# print('Client connected') +# @socketio.on('disconnect') +# def handle_disconnect(): +# print('Client disconnected') - ''' + msg) - emit('message', msg, broadcast=True) + +@socketio.on('fetch_jobs') +def handle_fetch_jobs(service_id): + job_response = job_api_client.get_jobs(service_id)["data"] + emit('job_update', job_response) @main.route("/services//dashboard") @@ -71,7 +77,6 @@ def service_dashboard(service_id): job_id = notification.get("job", {}).get("id", None) if job_id: aggregate_notifications_by_job[job_id].append(notification) - job_and_notifications = [ { "job_id": job["id"], diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index 4a2751eb2..1361576e6 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -22,8 +22,8 @@ Messages sent - -
      +

      Job Dashboard

      + {{ ajax_block(partials, updates_url, 'inbox') }} From 01321aeddc3926b0454837e2464d495830ea65e9 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Thu, 30 May 2024 21:17:10 -0700 Subject: [PATCH 05/25] connecting api data to socket and setting up chartjs to use the data --- app/assets/javascripts/chartDashboard.js | 78 +++++++++++++++++--- app/main/views/dashboard.py | 58 ++++++++------- app/templates/views/dashboard/dashboard.html | 5 +- 3 files changed, 101 insertions(+), 40 deletions(-) diff --git a/app/assets/javascripts/chartDashboard.js b/app/assets/javascripts/chartDashboard.js index fc40936f8..adc9ea255 100644 --- a/app/assets/javascripts/chartDashboard.js +++ b/app/assets/javascripts/chartDashboard.js @@ -1,24 +1,78 @@ (function (window) { - const ctx = document.getElementById('myChart'); + var socket = io(); + var serviceId = chart.getAttribute('data-service-id'); - new Chart(ctx, { + socket.on('connect', function() { + console.log('Connected to the server'); // Debug log, i'll delete later + socket.emit('fetch_daily_stats', serviceId); + socket.emit('fetch_single_month_notification_stats', serviceId); + socket.emit('fetch_monthly_stats_by_year', serviceId); + }); + + //this is for previous 7 days + socket.on('daily_stats_update', function(data) { + console.log('Received daily_stats_update:', data); + // Process the data + var labels = []; + var deliveredData = []; + // var failureData = []; + // var requestedData = []; + + for (var date in data) { + labels.push(date); + deliveredData.push(data[date].sms.delivered); + // failureData.push(data[date].sms.failure); + // requestedData.push(data[date].sms.requested); + } + + // Update Chart.js + myBarChart.data.labels = labels; + myBarChart.data.datasets[0].data = deliveredData; + myBarChart.update(); + }); + //this is for a single month + socket.on('single_month_notification_stats_update', function(data) { + console.log('Received single_month_notification_stats_update:', data); + // Update Chart.js with new data here + }); + //this is for monthly stats by year + socket.on('monthly_stats_by_year_update', function(data) { + console.log('Received monthly_stats_by_year_update:', data); + // Update Chart.js with new data here + }); + + socket.on('error', function(data) { + console.log('Error:', data); + }); + + sevenDaysButton.addEventListener('click', function() { + socket.emit('fetch_monthly_stats_by_year', serviceId); + console.log('button click'); // Debug log, i'll delete later + }); + + // Initialize Chart.js bar chart + var ctx = document.getElementById('myChart').getContext('2d'); + var myBarChart = new Chart(ctx, { type: 'bar', data: { - labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'], - datasets: [{ - label: '# of Votes', - data: [12, 19, 3, 5, 2, 3], - borderWidth: 1 - }] + labels: [], // Initialize with empty data + datasets: [ + { + label: 'Delivered', + data: [], + backgroundColor: '#0076d6', + stack: 'Stack 0' + }, + ] }, options: { - scales: { - y: { - beginAtZero: true + scales: { + y: { + beginAtZero: true + } } } - } }); })(window); diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 9d857dbab..59add85f3 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -20,7 +20,6 @@ from app import ( ) from app.formatters import format_date_numeric, format_datetime_numeric, get_time_left from app.main import main -from app.models.user import User from app.statistics_utils import get_formatted_percentage from app.utils import ( DELIVERED_STATUSES, @@ -36,20 +35,30 @@ from app.utils.user import user_has_permissions from notifications_utils.recipients import format_phone_number_human_readable -# @socketio.on('connect') -# def handle_connect(): -# print('Client connected') +@socketio.on('fetch_daily_stats') +def handle_fetch_daily_stats(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('disconnect') -# def handle_disconnect(): -# print('Client disconnected') +@socketio.on('fetch_single_month_notification_stats') +def handle_fetch_single_month_notification_stats(service_id): + date_range = get_stats_date_range() + single_month_notification_stats = service_api_client.get_single_month_notification_stats(service_id, year=date_range['current_financial_year'], month=date_range['current_month']) + emit('single_month_notification_stats_update', single_month_notification_stats) -@socketio.on('fetch_jobs') -def handle_fetch_jobs(service_id): - job_response = job_api_client.get_jobs(service_id)["data"] - emit('job_update', job_response) +@socketio.on('fetch_monthly_stats_by_year') +def handle_fetch_monthly_stats(service_id): + date_range = get_stats_date_range() + monthly_stats_by_year_stats = format_monthly_stats_to_list( + service_api_client.get_monthly_notification_stats(service_id, year=date_range['current_financial_year'])["data"] + ) + emit('monthly_stats_by_year_update', monthly_stats_by_year_stats) @main.route("/services//dashboard") @@ -103,6 +112,7 @@ def service_dashboard(service_id): partials=get_dashboard_partials(service_id), job_and_notifications=job_and_notifications, service_data_retention_days=service_data_retention_days, + service_id=service_id ) @@ -343,10 +353,6 @@ def aggregate_notifications_stats(template_statistics): def get_dashboard_partials(service_id): current_financial_year = get_current_financial_year() - current_month = get_current_month_for_financial_year(current_financial_year) - start_date = datetime.now().strftime('%Y-%m-%d') - days=7 - all_statistics = template_statistics_client.get_template_statistics_for_service( service_id, limit_days=7 ) @@ -368,21 +374,10 @@ def get_dashboard_partials(service_id): service_id, current_financial_year, ) - - #Previous 7 day stats - daily_stats = service_api_client.get_service_notification_statistics_by_day(service_id, start_date=start_date, days=days) - - #Single month stats - single_month_notification_stats = service_api_client.get_single_month_notification_stats(service_id, year=current_financial_year, month=current_month) - - #monthly stats by year monthly_stats = format_monthly_stats_to_list( service_api_client.get_monthly_notification_stats(service_id, current_financial_year)["data"] ) - # user=User.from_id(user_id), - # single_month_notification_stats = service_api_client.get_single_month_notification_stats_by_user(service_id, user, year=current_financial_year, month=current_month) - return { "upcoming": render_template( "views/dashboard/_upcoming.html", @@ -479,6 +474,17 @@ def get_current_month_for_financial_year(year): current_month = datetime.now().month return current_month +def get_stats_date_range(): + current_financial_year = get_current_financial_year() + current_month = get_current_month_for_financial_year(current_financial_year) + start_date = datetime.now().strftime('%Y-%m-%d') + days = 7 + return { + "current_financial_year": current_financial_year, + "current_month": current_month, + "start_date": start_date, + "days": days, + } def get_months_for_year(start, end, year): return [datetime(year, month, 1) for month in range(start, end)] diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index 1361576e6..d73e6bd9a 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -23,8 +23,9 @@

      Job Dashboard

      - - + + +
      {{ ajax_block(partials, updates_url, 'inbox') }} {{ ajax_block(partials, updates_url, 'totals') }} From 6c44982a5e29802415953e0c9974753d6b0594f2 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Thu, 30 May 2024 21:20:09 -0700 Subject: [PATCH 06/25] removed socket.js --- app/assets/javascripts/socket.js | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 app/assets/javascripts/socket.js diff --git a/app/assets/javascripts/socket.js b/app/assets/javascripts/socket.js deleted file mode 100644 index c4a54198d..000000000 --- a/app/assets/javascripts/socket.js +++ /dev/null @@ -1,21 +0,0 @@ - -(function (window) { - document.addEventListener('DOMContentLoaded', (event) => { - var socket = io(); - - socket.on('connect', function() { - console.log('Connected to the server'); - }); - - // Listen for job updates from the server - socket.on('job_update', function(data) { - console.log('Received job update:', data); - }); - - document.getElementById('fetchJobsButton').addEventListener('click', function() { - const serviceId = 'b1226555-1f1a-472c-9086-043b0a69f4ec'; // Example service ID - socket.emit('fetch_jobs', serviceId); - }); - }); - -})(window); From 4ebd5734d6ddd84d77552d0e32a4aa12e8bf0cbc Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 3 Jun 2024 11:32:56 -0700 Subject: [PATCH 07/25] removed socket.js --- app/templates/new/components/head.html | 1 - gulpfile.js | 1 - 2 files changed, 2 deletions(-) diff --git a/app/templates/new/components/head.html b/app/templates/new/components/head.html index f7c7153e2..51f3c4da3 100644 --- a/app/templates/new/components/head.html +++ b/app/templates/new/components/head.html @@ -32,7 +32,6 @@ {# google #} - {% if g.hide_from_search_engines %} diff --git a/gulpfile.js b/gulpfile.js index 3b7d765a7..98afbbacf 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -126,7 +126,6 @@ const javascripts = () => { paths.src + 'javascripts/loginAlert.js', paths.src + 'javascripts/main.js', paths.src + 'javascripts/chartDashboard.js', - paths.src + 'javascripts/socket.js', ]) .pipe(plugins.prettyerror()) .pipe(plugins.babel({ From 72e82c818b9a21a4166bd2f66179adc0fdd9ca3b Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 3 Jun 2024 11:39:02 -0700 Subject: [PATCH 08/25] added socket.io.min.js --- .ds.baseline | 4 ++-- app/templates/new/components/head.html | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.ds.baseline b/.ds.baseline index 859f30b4d..13a4125fe 100644 --- a/.ds.baseline +++ b/.ds.baseline @@ -423,7 +423,7 @@ "filename": "app/templates/new/components/head.html", "hashed_secret": "ee5048791fc7ff45a1545e24f85bec3317371327", "is_verified": false, - "line_number": 35, + "line_number": 36, "is_secret": false } ], @@ -710,5 +710,5 @@ } ] }, - "generated_at": "2024-05-29T21:18:03Z" + "generated_at": "2024-06-03T18:37:18Z" } diff --git a/app/templates/new/components/head.html b/app/templates/new/components/head.html index 51f3c4da3..f7c7153e2 100644 --- a/app/templates/new/components/head.html +++ b/app/templates/new/components/head.html @@ -32,6 +32,7 @@ {# google #} + {% if g.hide_from_search_engines %} From 2e41b752f5ef082c42812d5efde42da9f94e6184 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 3 Jun 2024 12:50:05 -0700 Subject: [PATCH 09/25] Remove .ds.baseline from repository --- .ds.baseline | 714 --------------------------------------------------- 1 file changed, 714 deletions(-) delete mode 100644 .ds.baseline diff --git a/.ds.baseline b/.ds.baseline deleted file mode 100644 index 13a4125fe..000000000 --- a/.ds.baseline +++ /dev/null @@ -1,714 +0,0 @@ -{ - "version": "1.5.0", - "plugins_used": [ - { - "name": "ArtifactoryDetector" - }, - { - "name": "AWSKeyDetector" - }, - { - "name": "AzureStorageKeyDetector" - }, - { - "name": "Base64HighEntropyString", - "limit": 4.5 - }, - { - "name": "BasicAuthDetector" - }, - { - "name": "CloudantDetector" - }, - { - "name": "DiscordBotTokenDetector" - }, - { - "name": "GitHubTokenDetector" - }, - { - "name": "GitLabTokenDetector" - }, - { - "name": "HexHighEntropyString", - "limit": 3.0 - }, - { - "name": "IbmCloudIamDetector" - }, - { - "name": "IbmCosHmacDetector" - }, - { - "name": "IPPublicDetector" - }, - { - "name": "JwtTokenDetector" - }, - { - "name": "KeywordDetector", - "keyword_exclude": "" - }, - { - "name": "MailchimpDetector" - }, - { - "name": "NpmDetector" - }, - { - "name": "OpenAIDetector" - }, - { - "name": "PrivateKeyDetector" - }, - { - "name": "PypiTokenDetector" - }, - { - "name": "SendGridDetector" - }, - { - "name": "SlackDetector" - }, - { - "name": "SoftlayerDetector" - }, - { - "name": "SquareOAuthDetector" - }, - { - "name": "StripeDetector" - }, - { - "name": "TelegramBotTokenDetector" - }, - { - "name": "TwilioKeyDetector" - } - ], - "filters_used": [ - { - "path": "detect_secrets.filters.allowlist.is_line_allowlisted" - }, - { - "path": "detect_secrets.filters.common.is_baseline_file", - "filename": ".ds.baseline" - }, - { - "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", - "min_level": 2 - }, - { - "path": "detect_secrets.filters.heuristic.is_indirect_reference" - }, - { - "path": "detect_secrets.filters.heuristic.is_likely_id_string" - }, - { - "path": "detect_secrets.filters.heuristic.is_lock_file" - }, - { - "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" - }, - { - "path": "detect_secrets.filters.heuristic.is_potential_uuid" - }, - { - "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" - }, - { - "path": "detect_secrets.filters.heuristic.is_sequential_string" - }, - { - "path": "detect_secrets.filters.heuristic.is_swagger_file" - }, - { - "path": "detect_secrets.filters.heuristic.is_templated_secret" - } - ], - "results": { - ".github/workflows/checks.yml": [ - { - "type": "Secret Keyword", - "filename": ".github/workflows/checks.yml", - "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", - "is_verified": false, - "line_number": 61, - "is_secret": false - }, - { - "type": "Basic Auth Credentials", - "filename": ".github/workflows/checks.yml", - "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", - "is_verified": false, - "line_number": 95, - "is_secret": false - } - ], - "app/assets/js/uswds.min.js": [ - { - "type": "Secret Keyword", - "filename": "app/assets/js/uswds.min.js", - "hashed_secret": "372ea08cab33e71c02c651dbc83a474d32c676ea", - "is_verified": false, - "line_number": 85, - "is_secret": false - }, - { - "type": "Secret Keyword", - "filename": "app/assets/js/uswds.min.js", - "hashed_secret": "53e07a32bf191d6917ee6fd863f0b52632a86798", - "is_verified": false, - "line_number": 85, - "is_secret": false - } - ], - "app/config.py": [ - { - "type": "Secret Keyword", - "filename": "app/config.py", - "hashed_secret": "577a4c667e4af8682ca431857214b3a920883efc", - "is_verified": false, - "line_number": 111, - "is_secret": false - } - ], - "app/main/_commonly_used_passwords.py": [ - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "82e19fa12aab7cfc718a002fc82c0f074bf070e7", - "is_verified": false, - "line_number": 123, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "a172ffc990129fe6f68b50f6037c54a1894ee3fd", - "is_verified": false, - "line_number": 240, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "4de69ee6b12b7fc91070873b71ba6e2929b90619", - "is_verified": false, - "line_number": 244, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "370194ff6e0f93a7432e16cc9badd9427e8b4e13", - "is_verified": false, - "line_number": 284, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "3dd635a808ddb6dd4b6731f7c409d53dd4b14df2", - "is_verified": false, - "line_number": 356, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "67a74306b06d0c01624fe0d0249a570f4d093747", - "is_verified": false, - "line_number": 374, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "61d6504733ca7757e259c644acd085c4dd471019", - "is_verified": false, - "line_number": 910, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "4ea872dfd7eefbde0036da7f0780826353dc7477", - "is_verified": false, - "line_number": 940, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "b214f706bb602c1cc2adc5c6165e73622305f4bb", - "is_verified": false, - "line_number": 1010, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "5cbabd43e49a1fedbbc3b86311aa6c8fe446abf9", - "is_verified": false, - "line_number": 1195, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "18ad10fd4a67f21fc07b1aa5046b410f6b2bedf1", - "is_verified": false, - "line_number": 1213, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "10470c3b4b1fed12c3baac014be15fac67c6e815", - "is_verified": false, - "line_number": 1263, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "65e1946c8f102eca8ba0af291f7c5e807516d94c", - "is_verified": false, - "line_number": 1346, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "0075df0a74c07ee295c98238c018401c9a80183b", - "is_verified": false, - "line_number": 1397, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "ca0023d7b345802fbc227b902cb9c57a3e02195f", - "is_verified": false, - "line_number": 1442, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "c8c6ca2e11c2dfd2a40914585b5944bffea15c8c", - "is_verified": false, - "line_number": 1555, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "b85b97a99eab8c809570c61d6404c1e49bdefbb4", - "is_verified": false, - "line_number": 1596, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "dec7dd342a499dfd4d283d872ccf598d8a7b6039", - "is_verified": false, - "line_number": 1789, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "2dc5053699a351121bf839c446bd4a878dda5735", - "is_verified": false, - "line_number": 1939, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "e5d54f0ac13abbdaa94b696c2469148b96dd11ab", - "is_verified": false, - "line_number": 2242, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "6059f42e2bbae78141e8a9e6286755ee691d5ce0", - "is_verified": false, - "line_number": 2305, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "fe703d258c7ef5f50b71e06565a65aa07194907f", - "is_verified": false, - "line_number": 2348, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "c229b68e1c3ffd9874838b5cb5354a0ee1367ddc", - "is_verified": false, - "line_number": 2349, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "756de479126e911b6f3400ae686d663d9d26b509", - "is_verified": false, - "line_number": 2920, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "6b174322afcdb440ee9cc3cc11eb16f9a00dec04", - "is_verified": false, - "line_number": 2975, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "9860783bfb510cbb2bf34471ec0b84a7ea587695", - "is_verified": false, - "line_number": 3359, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "b227cbd22eaa96019ebfc4aff35ad2add2a47439", - "is_verified": false, - "line_number": 3590, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "381d48209aecab8834eb495c5b5406100da07882", - "is_verified": false, - "line_number": 3811, - "is_secret": false - }, - { - "type": "Hex High Entropy String", - "filename": "app/main/_commonly_used_passwords.py", - "hashed_secret": "508b38590a90d32990aadd7350d160b795c3ab41", - "is_verified": false, - "line_number": 3850, - "is_secret": false - } - ], - "app/main/views/sign_in.py": [ - { - "type": "Private Key", - "filename": "app/main/views/sign_in.py", - "hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9", - "is_verified": false, - "line_number": 27, - "is_secret": false - } - ], - "app/templates/new/components/head.html": [ - { - "type": "Base64 High Entropy String", - "filename": "app/templates/new/components/head.html", - "hashed_secret": "ee5048791fc7ff45a1545e24f85bec3317371327", - "is_verified": false, - "line_number": 36, - "is_secret": false - } - ], - "app/templates/old/admin_template.html": [ - { - "type": "Base64 High Entropy String", - "filename": "app/templates/old/admin_template.html", - "hashed_secret": "ee5048791fc7ff45a1545e24f85bec3317371327", - "is_verified": false, - "line_number": 18, - "is_secret": false - } - ], - "deploy-config/sandbox.yml": [ - { - "type": "Secret Keyword", - "filename": "deploy-config/sandbox.yml", - "hashed_secret": "113151dd10316fcb0d5507b6215d78e2f3fe9e54", - "is_verified": false, - "line_number": 8, - "is_secret": false - } - ], - "pytest.ini": [ - { - "type": "Secret Keyword", - "filename": "pytest.ini", - "hashed_secret": "577a4c667e4af8682ca431857214b3a920883efc", - "is_verified": false, - "line_number": 7, - "is_secret": false - }, - { - "type": "Base64 High Entropy String", - "filename": "pytest.ini", - "hashed_secret": "d347784b1ab6074a65cda7bc42f1561bed85493f", - "is_verified": false, - "line_number": 7, - "is_secret": false - }, - { - "type": "Base64 High Entropy String", - "filename": "pytest.ini", - "hashed_secret": "ed1754d5cc82c8fd83205ebfb8c43fe4e88415a4", - "is_verified": false, - "line_number": 9, - "is_secret": false - }, - { - "type": "Secret Keyword", - "filename": "pytest.ini", - "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", - "is_verified": false, - "line_number": 11, - "is_secret": false - } - ], - "tests/__init__.py": [ - { - "type": "Secret Keyword", - "filename": "tests/__init__.py", - "hashed_secret": "f8377c90fcfd699f0ddbdcb30c2c9183d2d933ea", - "is_verified": false, - "line_number": 388, - "is_secret": false - } - ], - "tests/app/main/forms/test_register_user_form.py": [ - { - "type": "Secret Keyword", - "filename": "tests/app/main/forms/test_register_user_form.py", - "hashed_secret": "8c6c978dc8e08771c7dea1ea2370fdf2446e5ba5", - "is_verified": false, - "line_number": 38, - "is_secret": false - } - ], - "tests/app/main/test_errorhandlers.py": [ - { - "type": "Base64 High Entropy String", - "filename": "tests/app/main/test_errorhandlers.py", - "hashed_secret": "005fa73b3f2be8f0d71d361c1f0a9d787cd09b4e", - "is_verified": false, - "line_number": 33, - "is_secret": false - } - ], - "tests/app/main/test_request_header.py": [ - { - "type": "Secret Keyword", - "filename": "tests/app/main/test_request_header.py", - "hashed_secret": "6866ef97a972ba3a2c6ff8bb2812981054770162", - "is_verified": false, - "line_number": 21, - "is_secret": false - } - ], - "tests/app/main/views/organizations/test_organization_invites.py": [ - { - "type": "Secret Keyword", - "filename": "tests/app/main/views/organizations/test_organization_invites.py", - "hashed_secret": "bdbb156d25d02fd7792865824201dda1c60f4473", - "is_verified": false, - "line_number": 265, - "is_secret": false - }, - { - "type": "Secret Keyword", - "filename": "tests/app/main/views/organizations/test_organization_invites.py", - "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", - "is_verified": false, - "line_number": 273, - "is_secret": false - } - ], - "tests/app/main/views/test_accept_invite.py": [ - { - "type": "Secret Keyword", - "filename": "tests/app/main/views/test_accept_invite.py", - "hashed_secret": "07f0a6c13923fc3b5f0c57ffa2d29b715eb80d71", - "is_verified": false, - "line_number": 607, - "is_secret": false - } - ], - "tests/app/main/views/test_new_password.py": [ - { - "type": "Secret Keyword", - "filename": "tests/app/main/views/test_new_password.py", - "hashed_secret": "a41d5c3bbcd0b39c627b9cbf4897c6d25efa694f", - "is_verified": false, - "line_number": 89, - "is_secret": false - } - ], - "tests/app/main/views/test_register.py": [ - { - "type": "Secret Keyword", - "filename": "tests/app/main/views/test_register.py", - "hashed_secret": "bdbb156d25d02fd7792865824201dda1c60f4473", - "is_verified": false, - "line_number": 116, - "is_secret": false - }, - { - "type": "Secret Keyword", - "filename": "tests/app/main/views/test_register.py", - "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", - "is_verified": false, - "line_number": 192, - "is_secret": false - }, - { - "type": "Secret Keyword", - "filename": "tests/app/main/views/test_register.py", - "hashed_secret": "bb5b7caa27d005d38039e3797c3ddb9bcd22c3c8", - "is_verified": false, - "line_number": 260, - "is_secret": false - } - ], - "tests/app/main/views/test_sign_in.py": [ - { - "type": "Private Key", - "filename": "tests/app/main/views/test_sign_in.py", - "hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9", - "is_verified": false, - "line_number": 31, - "is_secret": false - }, - { - "type": "Secret Keyword", - "filename": "tests/app/main/views/test_sign_in.py", - "hashed_secret": "8b8b69116ee882b5e987e330f55db81aba0636f9", - "is_verified": false, - "line_number": 104, - "is_secret": false - } - ], - "tests/app/main/views/test_two_factor.py": [ - { - "type": "Secret Keyword", - "filename": "tests/app/main/views/test_two_factor.py", - "hashed_secret": "dc66ad927c29e31c6c374231f57a4684b0687bfe", - "is_verified": false, - "line_number": 267, - "is_secret": false - } - ], - "tests/app/main/views/test_user_profile.py": [ - { - "type": "Secret Keyword", - "filename": "tests/app/main/views/test_user_profile.py", - "hashed_secret": "8072d7aad32964ec43fbcb699c75dc38890792f7", - "is_verified": false, - "line_number": 350, - "is_secret": false - }, - { - "type": "Secret Keyword", - "filename": "tests/app/main/views/test_user_profile.py", - "hashed_secret": "4c9dbb972da179e4f66f023eaa5fb9451d835030", - "is_verified": false, - "line_number": 351, - "is_secret": false - } - ], - "tests/app/main/views/test_verify.py": [ - { - "type": "Secret Keyword", - "filename": "tests/app/main/views/test_verify.py", - "hashed_secret": "faafcfa63e128929409bf310b7ea5a415f2331ce", - "is_verified": false, - "line_number": 160, - "is_secret": false - } - ], - "tests/app/notify_client/test_user_client.py": [ - { - "type": "Secret Keyword", - "filename": "tests/app/notify_client/test_user_client.py", - "hashed_secret": "f2c57870308dc87f432e5912d4de6f8e322721ba", - "is_verified": false, - "line_number": 55, - "is_secret": false - } - ], - "tests/app/test_cloudfoundry_config.py": [ - { - "type": "Secret Keyword", - "filename": "tests/app/test_cloudfoundry_config.py", - "hashed_secret": "5e44dae2de8b6e57c797b968035265c9f2cd2b3e", - "is_verified": false, - "line_number": 12, - "is_secret": false - }, - { - "type": "Secret Keyword", - "filename": "tests/app/test_cloudfoundry_config.py", - "hashed_secret": "e5e178db7317356946d13e5d2da037d39ac61c71", - "is_verified": false, - "line_number": 27, - "is_secret": false - } - ], - "tests/conftest.py": [ - { - "type": "Secret Keyword", - "filename": "tests/conftest.py", - "hashed_secret": "f8377c90fcfd699f0ddbdcb30c2c9183d2d933ea", - "is_verified": false, - "line_number": 3266, - "is_secret": false - } - ], - "tests/notifications_utils/clients/antivirus/test_antivirus_client.py": [ - { - "type": "Secret Keyword", - "filename": "tests/notifications_utils/clients/antivirus/test_antivirus_client.py", - "hashed_secret": "932b25270abe1301c22c709a19082dff07d469ff", - "is_verified": false, - "line_number": 16, - "is_secret": false - } - ], - "tests/notifications_utils/clients/encryption/test_encryption_client.py": [ - { - "type": "Secret Keyword", - "filename": "tests/notifications_utils/clients/encryption/test_encryption_client.py", - "hashed_secret": "f1e923a9667de11be6a210849a8651c1bfd81605", - "is_verified": false, - "line_number": 13, - "is_secret": false - } - ], - "tests/notifications_utils/clients/zendesk/test_zendesk_client.py": [ - { - "type": "Secret Keyword", - "filename": "tests/notifications_utils/clients/zendesk/test_zendesk_client.py", - "hashed_secret": "913a73b565c8e2c8ed94497580f619397709b8b6", - "is_verified": false, - "line_number": 16, - "is_secret": false - } - ] - }, - "generated_at": "2024-06-03T18:37:18Z" -} From d12b798bba97e4b0b9115888c9678f2340a4ba62 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 3 Jun 2024 13:17:31 -0700 Subject: [PATCH 10/25] removed sending status --- .ds.baseline | 714 ++++++++++++++++++++++++++++++++++++ app/main/views/dashboard.py | 5 +- 2 files changed, 715 insertions(+), 4 deletions(-) create mode 100644 .ds.baseline diff --git a/.ds.baseline b/.ds.baseline new file mode 100644 index 000000000..859f30b4d --- /dev/null +++ b/.ds.baseline @@ -0,0 +1,714 @@ +{ + "version": "1.5.0", + "plugins_used": [ + { + "name": "ArtifactoryDetector" + }, + { + "name": "AWSKeyDetector" + }, + { + "name": "AzureStorageKeyDetector" + }, + { + "name": "Base64HighEntropyString", + "limit": 4.5 + }, + { + "name": "BasicAuthDetector" + }, + { + "name": "CloudantDetector" + }, + { + "name": "DiscordBotTokenDetector" + }, + { + "name": "GitHubTokenDetector" + }, + { + "name": "GitLabTokenDetector" + }, + { + "name": "HexHighEntropyString", + "limit": 3.0 + }, + { + "name": "IbmCloudIamDetector" + }, + { + "name": "IbmCosHmacDetector" + }, + { + "name": "IPPublicDetector" + }, + { + "name": "JwtTokenDetector" + }, + { + "name": "KeywordDetector", + "keyword_exclude": "" + }, + { + "name": "MailchimpDetector" + }, + { + "name": "NpmDetector" + }, + { + "name": "OpenAIDetector" + }, + { + "name": "PrivateKeyDetector" + }, + { + "name": "PypiTokenDetector" + }, + { + "name": "SendGridDetector" + }, + { + "name": "SlackDetector" + }, + { + "name": "SoftlayerDetector" + }, + { + "name": "SquareOAuthDetector" + }, + { + "name": "StripeDetector" + }, + { + "name": "TelegramBotTokenDetector" + }, + { + "name": "TwilioKeyDetector" + } + ], + "filters_used": [ + { + "path": "detect_secrets.filters.allowlist.is_line_allowlisted" + }, + { + "path": "detect_secrets.filters.common.is_baseline_file", + "filename": ".ds.baseline" + }, + { + "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", + "min_level": 2 + }, + { + "path": "detect_secrets.filters.heuristic.is_indirect_reference" + }, + { + "path": "detect_secrets.filters.heuristic.is_likely_id_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_lock_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_potential_uuid" + }, + { + "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" + }, + { + "path": "detect_secrets.filters.heuristic.is_sequential_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_swagger_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_templated_secret" + } + ], + "results": { + ".github/workflows/checks.yml": [ + { + "type": "Secret Keyword", + "filename": ".github/workflows/checks.yml", + "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", + "is_verified": false, + "line_number": 61, + "is_secret": false + }, + { + "type": "Basic Auth Credentials", + "filename": ".github/workflows/checks.yml", + "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", + "is_verified": false, + "line_number": 95, + "is_secret": false + } + ], + "app/assets/js/uswds.min.js": [ + { + "type": "Secret Keyword", + "filename": "app/assets/js/uswds.min.js", + "hashed_secret": "372ea08cab33e71c02c651dbc83a474d32c676ea", + "is_verified": false, + "line_number": 85, + "is_secret": false + }, + { + "type": "Secret Keyword", + "filename": "app/assets/js/uswds.min.js", + "hashed_secret": "53e07a32bf191d6917ee6fd863f0b52632a86798", + "is_verified": false, + "line_number": 85, + "is_secret": false + } + ], + "app/config.py": [ + { + "type": "Secret Keyword", + "filename": "app/config.py", + "hashed_secret": "577a4c667e4af8682ca431857214b3a920883efc", + "is_verified": false, + "line_number": 111, + "is_secret": false + } + ], + "app/main/_commonly_used_passwords.py": [ + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "82e19fa12aab7cfc718a002fc82c0f074bf070e7", + "is_verified": false, + "line_number": 123, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "a172ffc990129fe6f68b50f6037c54a1894ee3fd", + "is_verified": false, + "line_number": 240, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "4de69ee6b12b7fc91070873b71ba6e2929b90619", + "is_verified": false, + "line_number": 244, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "370194ff6e0f93a7432e16cc9badd9427e8b4e13", + "is_verified": false, + "line_number": 284, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "3dd635a808ddb6dd4b6731f7c409d53dd4b14df2", + "is_verified": false, + "line_number": 356, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "67a74306b06d0c01624fe0d0249a570f4d093747", + "is_verified": false, + "line_number": 374, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "61d6504733ca7757e259c644acd085c4dd471019", + "is_verified": false, + "line_number": 910, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "4ea872dfd7eefbde0036da7f0780826353dc7477", + "is_verified": false, + "line_number": 940, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "b214f706bb602c1cc2adc5c6165e73622305f4bb", + "is_verified": false, + "line_number": 1010, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "5cbabd43e49a1fedbbc3b86311aa6c8fe446abf9", + "is_verified": false, + "line_number": 1195, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "18ad10fd4a67f21fc07b1aa5046b410f6b2bedf1", + "is_verified": false, + "line_number": 1213, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "10470c3b4b1fed12c3baac014be15fac67c6e815", + "is_verified": false, + "line_number": 1263, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "65e1946c8f102eca8ba0af291f7c5e807516d94c", + "is_verified": false, + "line_number": 1346, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "0075df0a74c07ee295c98238c018401c9a80183b", + "is_verified": false, + "line_number": 1397, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "ca0023d7b345802fbc227b902cb9c57a3e02195f", + "is_verified": false, + "line_number": 1442, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "c8c6ca2e11c2dfd2a40914585b5944bffea15c8c", + "is_verified": false, + "line_number": 1555, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "b85b97a99eab8c809570c61d6404c1e49bdefbb4", + "is_verified": false, + "line_number": 1596, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "dec7dd342a499dfd4d283d872ccf598d8a7b6039", + "is_verified": false, + "line_number": 1789, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "2dc5053699a351121bf839c446bd4a878dda5735", + "is_verified": false, + "line_number": 1939, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "e5d54f0ac13abbdaa94b696c2469148b96dd11ab", + "is_verified": false, + "line_number": 2242, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "6059f42e2bbae78141e8a9e6286755ee691d5ce0", + "is_verified": false, + "line_number": 2305, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "fe703d258c7ef5f50b71e06565a65aa07194907f", + "is_verified": false, + "line_number": 2348, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "c229b68e1c3ffd9874838b5cb5354a0ee1367ddc", + "is_verified": false, + "line_number": 2349, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "756de479126e911b6f3400ae686d663d9d26b509", + "is_verified": false, + "line_number": 2920, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "6b174322afcdb440ee9cc3cc11eb16f9a00dec04", + "is_verified": false, + "line_number": 2975, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "9860783bfb510cbb2bf34471ec0b84a7ea587695", + "is_verified": false, + "line_number": 3359, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "b227cbd22eaa96019ebfc4aff35ad2add2a47439", + "is_verified": false, + "line_number": 3590, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "381d48209aecab8834eb495c5b5406100da07882", + "is_verified": false, + "line_number": 3811, + "is_secret": false + }, + { + "type": "Hex High Entropy String", + "filename": "app/main/_commonly_used_passwords.py", + "hashed_secret": "508b38590a90d32990aadd7350d160b795c3ab41", + "is_verified": false, + "line_number": 3850, + "is_secret": false + } + ], + "app/main/views/sign_in.py": [ + { + "type": "Private Key", + "filename": "app/main/views/sign_in.py", + "hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9", + "is_verified": false, + "line_number": 27, + "is_secret": false + } + ], + "app/templates/new/components/head.html": [ + { + "type": "Base64 High Entropy String", + "filename": "app/templates/new/components/head.html", + "hashed_secret": "ee5048791fc7ff45a1545e24f85bec3317371327", + "is_verified": false, + "line_number": 35, + "is_secret": false + } + ], + "app/templates/old/admin_template.html": [ + { + "type": "Base64 High Entropy String", + "filename": "app/templates/old/admin_template.html", + "hashed_secret": "ee5048791fc7ff45a1545e24f85bec3317371327", + "is_verified": false, + "line_number": 18, + "is_secret": false + } + ], + "deploy-config/sandbox.yml": [ + { + "type": "Secret Keyword", + "filename": "deploy-config/sandbox.yml", + "hashed_secret": "113151dd10316fcb0d5507b6215d78e2f3fe9e54", + "is_verified": false, + "line_number": 8, + "is_secret": false + } + ], + "pytest.ini": [ + { + "type": "Secret Keyword", + "filename": "pytest.ini", + "hashed_secret": "577a4c667e4af8682ca431857214b3a920883efc", + "is_verified": false, + "line_number": 7, + "is_secret": false + }, + { + "type": "Base64 High Entropy String", + "filename": "pytest.ini", + "hashed_secret": "d347784b1ab6074a65cda7bc42f1561bed85493f", + "is_verified": false, + "line_number": 7, + "is_secret": false + }, + { + "type": "Base64 High Entropy String", + "filename": "pytest.ini", + "hashed_secret": "ed1754d5cc82c8fd83205ebfb8c43fe4e88415a4", + "is_verified": false, + "line_number": 9, + "is_secret": false + }, + { + "type": "Secret Keyword", + "filename": "pytest.ini", + "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", + "is_verified": false, + "line_number": 11, + "is_secret": false + } + ], + "tests/__init__.py": [ + { + "type": "Secret Keyword", + "filename": "tests/__init__.py", + "hashed_secret": "f8377c90fcfd699f0ddbdcb30c2c9183d2d933ea", + "is_verified": false, + "line_number": 388, + "is_secret": false + } + ], + "tests/app/main/forms/test_register_user_form.py": [ + { + "type": "Secret Keyword", + "filename": "tests/app/main/forms/test_register_user_form.py", + "hashed_secret": "8c6c978dc8e08771c7dea1ea2370fdf2446e5ba5", + "is_verified": false, + "line_number": 38, + "is_secret": false + } + ], + "tests/app/main/test_errorhandlers.py": [ + { + "type": "Base64 High Entropy String", + "filename": "tests/app/main/test_errorhandlers.py", + "hashed_secret": "005fa73b3f2be8f0d71d361c1f0a9d787cd09b4e", + "is_verified": false, + "line_number": 33, + "is_secret": false + } + ], + "tests/app/main/test_request_header.py": [ + { + "type": "Secret Keyword", + "filename": "tests/app/main/test_request_header.py", + "hashed_secret": "6866ef97a972ba3a2c6ff8bb2812981054770162", + "is_verified": false, + "line_number": 21, + "is_secret": false + } + ], + "tests/app/main/views/organizations/test_organization_invites.py": [ + { + "type": "Secret Keyword", + "filename": "tests/app/main/views/organizations/test_organization_invites.py", + "hashed_secret": "bdbb156d25d02fd7792865824201dda1c60f4473", + "is_verified": false, + "line_number": 265, + "is_secret": false + }, + { + "type": "Secret Keyword", + "filename": "tests/app/main/views/organizations/test_organization_invites.py", + "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", + "is_verified": false, + "line_number": 273, + "is_secret": false + } + ], + "tests/app/main/views/test_accept_invite.py": [ + { + "type": "Secret Keyword", + "filename": "tests/app/main/views/test_accept_invite.py", + "hashed_secret": "07f0a6c13923fc3b5f0c57ffa2d29b715eb80d71", + "is_verified": false, + "line_number": 607, + "is_secret": false + } + ], + "tests/app/main/views/test_new_password.py": [ + { + "type": "Secret Keyword", + "filename": "tests/app/main/views/test_new_password.py", + "hashed_secret": "a41d5c3bbcd0b39c627b9cbf4897c6d25efa694f", + "is_verified": false, + "line_number": 89, + "is_secret": false + } + ], + "tests/app/main/views/test_register.py": [ + { + "type": "Secret Keyword", + "filename": "tests/app/main/views/test_register.py", + "hashed_secret": "bdbb156d25d02fd7792865824201dda1c60f4473", + "is_verified": false, + "line_number": 116, + "is_secret": false + }, + { + "type": "Secret Keyword", + "filename": "tests/app/main/views/test_register.py", + "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", + "is_verified": false, + "line_number": 192, + "is_secret": false + }, + { + "type": "Secret Keyword", + "filename": "tests/app/main/views/test_register.py", + "hashed_secret": "bb5b7caa27d005d38039e3797c3ddb9bcd22c3c8", + "is_verified": false, + "line_number": 260, + "is_secret": false + } + ], + "tests/app/main/views/test_sign_in.py": [ + { + "type": "Private Key", + "filename": "tests/app/main/views/test_sign_in.py", + "hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9", + "is_verified": false, + "line_number": 31, + "is_secret": false + }, + { + "type": "Secret Keyword", + "filename": "tests/app/main/views/test_sign_in.py", + "hashed_secret": "8b8b69116ee882b5e987e330f55db81aba0636f9", + "is_verified": false, + "line_number": 104, + "is_secret": false + } + ], + "tests/app/main/views/test_two_factor.py": [ + { + "type": "Secret Keyword", + "filename": "tests/app/main/views/test_two_factor.py", + "hashed_secret": "dc66ad927c29e31c6c374231f57a4684b0687bfe", + "is_verified": false, + "line_number": 267, + "is_secret": false + } + ], + "tests/app/main/views/test_user_profile.py": [ + { + "type": "Secret Keyword", + "filename": "tests/app/main/views/test_user_profile.py", + "hashed_secret": "8072d7aad32964ec43fbcb699c75dc38890792f7", + "is_verified": false, + "line_number": 350, + "is_secret": false + }, + { + "type": "Secret Keyword", + "filename": "tests/app/main/views/test_user_profile.py", + "hashed_secret": "4c9dbb972da179e4f66f023eaa5fb9451d835030", + "is_verified": false, + "line_number": 351, + "is_secret": false + } + ], + "tests/app/main/views/test_verify.py": [ + { + "type": "Secret Keyword", + "filename": "tests/app/main/views/test_verify.py", + "hashed_secret": "faafcfa63e128929409bf310b7ea5a415f2331ce", + "is_verified": false, + "line_number": 160, + "is_secret": false + } + ], + "tests/app/notify_client/test_user_client.py": [ + { + "type": "Secret Keyword", + "filename": "tests/app/notify_client/test_user_client.py", + "hashed_secret": "f2c57870308dc87f432e5912d4de6f8e322721ba", + "is_verified": false, + "line_number": 55, + "is_secret": false + } + ], + "tests/app/test_cloudfoundry_config.py": [ + { + "type": "Secret Keyword", + "filename": "tests/app/test_cloudfoundry_config.py", + "hashed_secret": "5e44dae2de8b6e57c797b968035265c9f2cd2b3e", + "is_verified": false, + "line_number": 12, + "is_secret": false + }, + { + "type": "Secret Keyword", + "filename": "tests/app/test_cloudfoundry_config.py", + "hashed_secret": "e5e178db7317356946d13e5d2da037d39ac61c71", + "is_verified": false, + "line_number": 27, + "is_secret": false + } + ], + "tests/conftest.py": [ + { + "type": "Secret Keyword", + "filename": "tests/conftest.py", + "hashed_secret": "f8377c90fcfd699f0ddbdcb30c2c9183d2d933ea", + "is_verified": false, + "line_number": 3266, + "is_secret": false + } + ], + "tests/notifications_utils/clients/antivirus/test_antivirus_client.py": [ + { + "type": "Secret Keyword", + "filename": "tests/notifications_utils/clients/antivirus/test_antivirus_client.py", + "hashed_secret": "932b25270abe1301c22c709a19082dff07d469ff", + "is_verified": false, + "line_number": 16, + "is_secret": false + } + ], + "tests/notifications_utils/clients/encryption/test_encryption_client.py": [ + { + "type": "Secret Keyword", + "filename": "tests/notifications_utils/clients/encryption/test_encryption_client.py", + "hashed_secret": "f1e923a9667de11be6a210849a8651c1bfd81605", + "is_verified": false, + "line_number": 13, + "is_secret": false + } + ], + "tests/notifications_utils/clients/zendesk/test_zendesk_client.py": [ + { + "type": "Secret Keyword", + "filename": "tests/notifications_utils/clients/zendesk/test_zendesk_client.py", + "hashed_secret": "913a73b565c8e2c8ed94497580f619397709b8b6", + "is_verified": false, + "line_number": 16, + "is_secret": false + } + ] + }, + "generated_at": "2024-05-29T21:18:03Z" +} diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 59add85f3..780bb8218 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -25,7 +25,6 @@ from app.utils import ( DELIVERED_STATUSES, FAILURE_STATUSES, REQUESTED_STATUSES, - SENDING_STATUSES, service_has_permission, ) from app.utils.csv import Spreadsheet @@ -88,6 +87,7 @@ def service_dashboard(service_id): job_id = notification.get("job", {}).get("id", None) if job_id: aggregate_notifications_by_job[job_id].append(notification) + job_and_notifications = [ { "job_id": job["id"], @@ -377,7 +377,6 @@ def get_dashboard_partials(service_id): monthly_stats = format_monthly_stats_to_list( service_api_client.get_monthly_notification_stats(service_id, current_financial_year)["data"] ) - return { "upcoming": render_template( "views/dashboard/_upcoming.html", @@ -458,8 +457,6 @@ def aggregate_status_types(counts_dict): "{}_counts".format(message_type): { "failed": sum(stats.get(status, 0) for status in FAILURE_STATUSES), "requested": sum(stats.get(status, 0) for status in REQUESTED_STATUSES), - "delivered": sum(stats.get(status, 0) for status in DELIVERED_STATUSES), - "pending": sum(stats.get(status, 0) for status in SENDING_STATUSES), } for message_type, stats in counts_dict.items() } From 89f19bb63de83f4d722d91a3cd64ee0627f4cfd5 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 3 Jun 2024 13:35:30 -0700 Subject: [PATCH 11/25] cleared up style checks --- app/main/views/dashboard.py | 18 +++++++++++++++--- app/notify_client/service_api_client.py | 5 ----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 780bb8218..64ef2965c 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -38,7 +38,11 @@ from notifications_utils.recipients import format_phone_number_human_readable def handle_fetch_daily_stats(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']) + 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'}) @@ -47,7 +51,11 @@ def handle_fetch_daily_stats(service_id): @socketio.on('fetch_single_month_notification_stats') def handle_fetch_single_month_notification_stats(service_id): date_range = get_stats_date_range() - single_month_notification_stats = service_api_client.get_single_month_notification_stats(service_id, year=date_range['current_financial_year'], month=date_range['current_month']) + single_month_notification_stats = service_api_client.get_single_month_notification_stats( + service_id, + year=date_range['current_financial_year'], + month=date_range['current_month'] + ) emit('single_month_notification_stats_update', single_month_notification_stats) @@ -55,7 +63,9 @@ def handle_fetch_single_month_notification_stats(service_id): def handle_fetch_monthly_stats(service_id): date_range = get_stats_date_range() monthly_stats_by_year_stats = format_monthly_stats_to_list( - service_api_client.get_monthly_notification_stats(service_id, year=date_range['current_financial_year'])["data"] + service_api_client.get_monthly_notification_stats( + service_id, + year=date_range['current_financial_year'])["data"] ) emit('monthly_stats_by_year_update', monthly_stats_by_year_stats) @@ -471,6 +481,7 @@ def get_current_month_for_financial_year(year): current_month = datetime.now().month return current_month + def get_stats_date_range(): current_financial_year = get_current_financial_year() current_month = get_current_month_for_financial_year(current_financial_year) @@ -483,6 +494,7 @@ def get_stats_date_range(): "days": days, } + def get_months_for_year(start, end, year): return [datetime(year, month, 1) for month in range(start, end)] diff --git a/app/notify_client/service_api_client.py b/app/notify_client/service_api_client.py index f2cc2f934..6b9e1d43c 100644 --- a/app/notify_client/service_api_client.py +++ b/app/notify_client/service_api_client.py @@ -56,11 +56,6 @@ class ServiceAPIClient(NotifyAdminAPIClient): "/service/{0}/notifications/month?year={1}&month={2}".format(service_id, year, month), ) - # def get_single_month_notification_stats(self, service_id, user_id, year, month): - # return self.get( - # "/service/{0}/notifications//month?year={1}&month={2}".format(service_id, user_id, year, month), - # ) - def get_services(self, params_dict=None): """ Retrieve a list of services. From d3bee617426e12a578e4f456d665f2c373131ba5 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 3 Jun 2024 13:45:36 -0700 Subject: [PATCH 12/25] cleared up style checks --- app/main/views/dashboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 64ef2965c..d1f68448c 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -6,7 +6,7 @@ from itertools import groupby from flask import Response, abort, jsonify, render_template, request, session, url_for from flask_login import current_user -from flask_socketio import SocketIO, emit +from flask_socketio import emit from werkzeug.utils import redirect from app import ( From 8554df58f8a3fce3c563e5296aebc3033fed4f0d Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 3 Jun 2024 13:51:59 -0700 Subject: [PATCH 13/25] cleared up style checks --- app/main/views/dashboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index d1f68448c..48a36dbb4 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -15,8 +15,8 @@ from app import ( job_api_client, notification_api_client, service_api_client, + socketio, template_statistics_client, - socketio ) from app.formatters import format_date_numeric, format_datetime_numeric, get_time_left from app.main import main From 7a9d2607173bdf7407e1f7f8c1be7be2371456bf Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 3 Jun 2024 15:34:43 -0700 Subject: [PATCH 14/25] fix testing --- app/main/views/dashboard.py | 46 +++++++++----------- app/notify_client/service_api_client.py | 10 +++-- app/templates/views/dashboard/dashboard.html | 1 - 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 48a36dbb4..7ad899a53 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -34,40 +34,40 @@ 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") def handle_fetch_daily_stats(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'] + service_id, start_date=date_range["start_date"], days=date_range["days"] ) - emit('daily_stats_update', daily_stats) + emit("daily_stats_update", daily_stats) else: - emit('error', {'error': 'No service_id provided'}) + emit("error", {"error": "No service_id provided"}) -@socketio.on('fetch_single_month_notification_stats') +@socketio.on("fetch_single_month_notification_stats") def handle_fetch_single_month_notification_stats(service_id): date_range = get_stats_date_range() - single_month_notification_stats = service_api_client.get_single_month_notification_stats( - service_id, - year=date_range['current_financial_year'], - month=date_range['current_month'] + single_month_notification_stats = ( + service_api_client.get_single_month_notification_stats( + service_id, + year=date_range["current_financial_year"], + month=date_range["current_month"], + ) ) - emit('single_month_notification_stats_update', single_month_notification_stats) + emit("single_month_notification_stats_update", single_month_notification_stats) -@socketio.on('fetch_monthly_stats_by_year') +@socketio.on("fetch_monthly_stats_by_year") def handle_fetch_monthly_stats(service_id): date_range = get_stats_date_range() monthly_stats_by_year_stats = format_monthly_stats_to_list( service_api_client.get_monthly_notification_stats( - service_id, - year=date_range['current_financial_year'])["data"] + service_id, year=date_range["current_financial_year"] + )["data"] ) - emit('monthly_stats_by_year_update', monthly_stats_by_year_stats) + emit("monthly_stats_by_year_update", monthly_stats_by_year_stats) @main.route("/services//dashboard") @@ -122,7 +122,7 @@ def service_dashboard(service_id): partials=get_dashboard_partials(service_id), job_and_notifications=job_and_notifications, service_data_retention_days=service_data_retention_days, - service_id=service_id + service_id=service_id, ) @@ -362,7 +362,6 @@ def aggregate_notifications_stats(template_statistics): def get_dashboard_partials(service_id): - current_financial_year = get_current_financial_year() all_statistics = template_statistics_client.get_template_statistics_for_service( service_id, limit_days=7 ) @@ -375,17 +374,15 @@ def get_dashboard_partials(service_id): ) # These 2 calls will update the dashboard sms allowance count while in trial mode. billing_api_client.get_monthly_usage_for_service( - service_id, current_financial_year + service_id, get_current_financial_year() ) billing_api_client.create_or_update_free_sms_fragment_limit( service_id, free_sms_fragment_limit=free_sms_allowance ) + yearly_usage = billing_api_client.get_annual_usage_for_service( service_id, - current_financial_year, - ) - monthly_stats = format_monthly_stats_to_list( - service_api_client.get_monthly_notification_stats(service_id, current_financial_year)["data"] + get_current_financial_year(), ) return { "upcoming": render_template( @@ -408,7 +405,6 @@ def get_dashboard_partials(service_id): ), "usage": render_template( "views/dashboard/_usage.html", - monthly_stats=monthly_stats, **get_annual_usage_breakdown(yearly_usage, free_sms_allowance), ), } @@ -485,7 +481,7 @@ def get_current_month_for_financial_year(year): def get_stats_date_range(): current_financial_year = get_current_financial_year() current_month = get_current_month_for_financial_year(current_financial_year) - start_date = datetime.now().strftime('%Y-%m-%d') + start_date = datetime.now().strftime("%Y-%m-%d") days = 7 return { "current_financial_year": current_financial_year, diff --git a/app/notify_client/service_api_client.py b/app/notify_client/service_api_client.py index 6b9e1d43c..627bd0ccd 100644 --- a/app/notify_client/service_api_client.py +++ b/app/notify_client/service_api_client.py @@ -43,9 +43,11 @@ class ServiceAPIClient(NotifyAdminAPIClient): params={"limit_days": limit_days}, )["data"] - def get_service_notification_statistics_by_day(self, service_id, start_date=None, days=None): + def get_service_notification_statistics_by_day( + self, service_id, start_date=None, days=None + ): if start_date is None: - start_date = datetime.now().strftime('%Y-%m-%d') + start_date = datetime.now().strftime("%Y-%m-%d") return self.get( "/service/{0}/statistics/{1}/{2}".format(service_id, start_date, days), @@ -53,7 +55,9 @@ class ServiceAPIClient(NotifyAdminAPIClient): def get_single_month_notification_stats(self, service_id, year, month): return self.get( - "/service/{0}/notifications/month?year={1}&month={2}".format(service_id, year, month), + "/service/{0}/notifications/month?year={1}&month={2}".format( + service_id, year, month + ), ) def get_services(self, params_dict=None): diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index cb00254dc..a776ce83c 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -22,7 +22,6 @@ Messages sent -

      Job Dashboard

      From fec6c2ff72366dbf60367d36b3e9f973ce615edd Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Tue, 4 Jun 2024 16:27:35 -0700 Subject: [PATCH 15/25] removed comments --- app/assets/javascripts/chartDashboard.js | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/chartDashboard.js b/app/assets/javascripts/chartDashboard.js index adc9ea255..98378137a 100644 --- a/app/assets/javascripts/chartDashboard.js +++ b/app/assets/javascripts/chartDashboard.js @@ -6,14 +6,12 @@ socket.on('connect', function() { console.log('Connected to the server'); // Debug log, i'll delete later socket.emit('fetch_daily_stats', serviceId); - socket.emit('fetch_single_month_notification_stats', serviceId); - socket.emit('fetch_monthly_stats_by_year', serviceId); }); //this is for previous 7 days socket.on('daily_stats_update', function(data) { console.log('Received daily_stats_update:', data); - // Process the data + var labels = []; var deliveredData = []; // var failureData = []; @@ -31,23 +29,13 @@ myBarChart.data.datasets[0].data = deliveredData; myBarChart.update(); }); - //this is for a single month - socket.on('single_month_notification_stats_update', function(data) { - console.log('Received single_month_notification_stats_update:', data); - // Update Chart.js with new data here - }); - //this is for monthly stats by year - socket.on('monthly_stats_by_year_update', function(data) { - console.log('Received monthly_stats_by_year_update:', data); - // Update Chart.js with new data here - }); socket.on('error', function(data) { console.log('Error:', data); }); sevenDaysButton.addEventListener('click', function() { - socket.emit('fetch_monthly_stats_by_year', serviceId); + socket.emit('fetch_daily_stats', serviceId); console.log('button click'); // Debug log, i'll delete later }); @@ -56,7 +44,7 @@ var myBarChart = new Chart(ctx, { type: 'bar', data: { - labels: [], // Initialize with empty data + labels: [], datasets: [ { label: 'Delivered', From 0a4cf290ac1c8f2e41e247599114ac4c20dbca0f Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Tue, 4 Jun 2024 16:33:55 -0700 Subject: [PATCH 16/25] added flask-socketio --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 9762f7140..d7f3e5c3d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,6 +69,7 @@ requests = "^2.32.3" six = "^1.16.0" urllib3 = "^2.2.1" webencodings = "^0.5.1" +flask-socketio = "^5.3.6" [tool.poetry.group.dev.dependencies] From fed2aff43c684f59f5e3545a1cd310ef42d61049 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Tue, 4 Jun 2024 16:52:32 -0700 Subject: [PATCH 17/25] cleaned up code --- app/assets/javascripts/chartDashboard.js | 12 +----------- app/main/views/dashboard.py | 24 ------------------------ app/notify_client/service_api_client.py | 7 ------- 3 files changed, 1 insertion(+), 42 deletions(-) diff --git a/app/assets/javascripts/chartDashboard.js b/app/assets/javascripts/chartDashboard.js index 98378137a..e73f655f9 100644 --- a/app/assets/javascripts/chartDashboard.js +++ b/app/assets/javascripts/chartDashboard.js @@ -1,30 +1,21 @@ (function (window) { - + // Initialize flask-socketio var socket = io(); var serviceId = chart.getAttribute('data-service-id'); socket.on('connect', function() { - console.log('Connected to the server'); // Debug log, i'll delete later socket.emit('fetch_daily_stats', serviceId); }); - //this is for previous 7 days socket.on('daily_stats_update', function(data) { - console.log('Received daily_stats_update:', data); - var labels = []; var deliveredData = []; - // var failureData = []; - // var requestedData = []; for (var date in data) { labels.push(date); deliveredData.push(data[date].sms.delivered); - // failureData.push(data[date].sms.failure); - // requestedData.push(data[date].sms.requested); } - // Update Chart.js myBarChart.data.labels = labels; myBarChart.data.datasets[0].data = deliveredData; myBarChart.update(); @@ -36,7 +27,6 @@ sevenDaysButton.addEventListener('click', function() { socket.emit('fetch_daily_stats', serviceId); - console.log('button click'); // Debug log, i'll delete later }); // Initialize Chart.js bar chart diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 7ad899a53..a64444dc2 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -46,30 +46,6 @@ def handle_fetch_daily_stats(service_id): emit("error", {"error": "No service_id provided"}) -@socketio.on("fetch_single_month_notification_stats") -def handle_fetch_single_month_notification_stats(service_id): - date_range = get_stats_date_range() - single_month_notification_stats = ( - service_api_client.get_single_month_notification_stats( - service_id, - year=date_range["current_financial_year"], - month=date_range["current_month"], - ) - ) - emit("single_month_notification_stats_update", single_month_notification_stats) - - -@socketio.on("fetch_monthly_stats_by_year") -def handle_fetch_monthly_stats(service_id): - date_range = get_stats_date_range() - monthly_stats_by_year_stats = format_monthly_stats_to_list( - service_api_client.get_monthly_notification_stats( - service_id, year=date_range["current_financial_year"] - )["data"] - ) - emit("monthly_stats_by_year_update", monthly_stats_by_year_stats) - - @main.route("/services//dashboard") @user_has_permissions("view_activity", "send_messages") def old_service_dashboard(service_id): diff --git a/app/notify_client/service_api_client.py b/app/notify_client/service_api_client.py index 627bd0ccd..42f54572f 100644 --- a/app/notify_client/service_api_client.py +++ b/app/notify_client/service_api_client.py @@ -53,13 +53,6 @@ class ServiceAPIClient(NotifyAdminAPIClient): "/service/{0}/statistics/{1}/{2}".format(service_id, start_date, days), )["data"] - def get_single_month_notification_stats(self, service_id, year, month): - return self.get( - "/service/{0}/notifications/month?year={1}&month={2}".format( - service_id, year, month - ), - ) - def get_services(self, params_dict=None): """ Retrieve a list of services. From e5a9eed626f17352ec92b54ff348a8b20f5aa98d Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 5 Jun 2024 14:56:22 -0700 Subject: [PATCH 18/25] installing socketio --- app/templates/new/components/head.html | 1 - gulpfile.js | 3 +- package-lock.json | 84 +++++++++++++++++++++++++- package.json | 1 + 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/app/templates/new/components/head.html b/app/templates/new/components/head.html index f7c7153e2..51f3c4da3 100644 --- a/app/templates/new/components/head.html +++ b/app/templates/new/components/head.html @@ -32,7 +32,6 @@ {# google #} - {% if g.hide_from_search_engines %} diff --git a/gulpfile.js b/gulpfile.js index 98afbbacf..836596165 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -96,7 +96,8 @@ const javascripts = () => { paths.npm + 'query-command-supported/dist/queryCommandSupported.min.js', paths.npm + 'timeago/jquery.timeago.js', paths.npm + 'textarea-caret/index.js', - paths.npm + 'cbor-js/cbor.js' + paths.npm + 'cbor-js/cbor.js', + paths.npm + 'socket.io-client/dist/socket.io.min.js' ])); // JS local to this application diff --git a/package-lock.json b/package-lock.json index c96f68f8a..30688bfde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "python": "^0.0.4", "query-command-supported": "1.0.0", "sass-embedded": "^1.69.5", + "socket.io-client": "^4.7.5", "textarea-caret": "3.1.0", "timeago": "1.6.7" }, @@ -2592,6 +2593,11 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -4644,7 +4650,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -5039,6 +5044,46 @@ "once": "^1.4.0" } }, + "node_modules/engine.io-client": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", + "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -10779,8 +10824,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/multipipe": { "version": "0.1.2", @@ -13127,6 +13171,32 @@ "urix": "^0.1.0" } }, + "node_modules/socket.io-client": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -14511,6 +14581,14 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/xtend": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", diff --git a/package.json b/package.json index b60893b57..38cc83797 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "python": "^0.0.4", "query-command-supported": "1.0.0", "sass-embedded": "^1.69.5", + "socket.io-client": "^4.7.5", "textarea-caret": "3.1.0", "timeago": "1.6.7" }, From 9d3dc047ea20cbd92fadf256d8601c301b6f0a62 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Wed, 5 Jun 2024 15:03:19 -0700 Subject: [PATCH 19/25] added chartjs to path in gulp --- .ds.baseline | 4 ++-- app/templates/new/components/head.html | 1 - gulpfile.js | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.ds.baseline b/.ds.baseline index 859f30b4d..ec87d9c30 100644 --- a/.ds.baseline +++ b/.ds.baseline @@ -423,7 +423,7 @@ "filename": "app/templates/new/components/head.html", "hashed_secret": "ee5048791fc7ff45a1545e24f85bec3317371327", "is_verified": false, - "line_number": 35, + "line_number": 34, "is_secret": false } ], @@ -710,5 +710,5 @@ } ] }, - "generated_at": "2024-05-29T21:18:03Z" + "generated_at": "2024-06-05T22:01:56Z" } diff --git a/app/templates/new/components/head.html b/app/templates/new/components/head.html index 51f3c4da3..dd7519cf4 100644 --- a/app/templates/new/components/head.html +++ b/app/templates/new/components/head.html @@ -31,7 +31,6 @@ {# google #} - {% if g.hide_from_search_engines %} diff --git a/gulpfile.js b/gulpfile.js index 836596165..b68da089f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -97,7 +97,8 @@ const javascripts = () => { paths.npm + 'timeago/jquery.timeago.js', paths.npm + 'textarea-caret/index.js', paths.npm + 'cbor-js/cbor.js', - paths.npm + 'socket.io-client/dist/socket.io.min.js' + paths.npm + 'socket.io-client/dist/socket.io.min.js', + paths.npm + 'chart.js/dist/chart.umd.js' ])); // JS local to this application From 61253963f746ed24469532c142b139ec58a2fa55 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Thu, 6 Jun 2024 00:48:08 -0700 Subject: [PATCH 20/25] added socketiotestclent --- tests/app/main/views/test_dashboard.py | 68 +++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index 926163dbd..f92efbb19 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -3,15 +3,21 @@ import json from datetime import datetime import pytest -from flask import url_for +from flask import Flask, url_for +from flask_socketio import SocketIOTestClient from freezegun import freeze_time +from app import ( + create_app, + socketio +) from app.main.views.dashboard import ( aggregate_notifications_stats, aggregate_status_types, aggregate_template_usage, format_monthly_stats_to_list, get_dashboard_totals, + get_stats_date_range, get_tuples_of_financial_years, ) from tests import ( @@ -23,6 +29,7 @@ from tests import ( from tests.conftest import ( ORGANISATION_ID, SERVICE_ONE_ID, + SERVICE_TWO_ID, create_active_caseworking_user, create_active_user_view_permissions, normalize_spaces, @@ -1875,3 +1882,62 @@ def test_service_dashboard_shows_batched_jobs( assert job_table_body is not None assert len(rows) == 1 + + +@pytest.fixture +def app_with_socketio(): + app = Flask("app") + create_app(app) + return app, socketio + + +@pytest.mark.parametrize( + "service_id, date_range, expected_call_args", + [ + (SERVICE_ONE_ID, {"start_date": "2024-01-01", "days": 7}, {"service_id": SERVICE_ONE_ID, "start_date": "2024-01-01", "days": 7}), + (SERVICE_TWO_ID, {"start_date": "2023-06-01", "days": 7}, {"service_id": SERVICE_TWO_ID, "start_date": "2023-06-01", "days": 7}), + ] +) +def test_fetch_daily_stats( + app_with_socketio, mocker, + service_id, + date_range, + expected_call_args +): + app, socketio = app_with_socketio + + mocker.patch( + "app.main.views.dashboard.get_stats_date_range", + return_value=date_range + ) + + mock_service_api = mocker.patch( + "app.service_api_client.get_service_notification_statistics_by_day", + return_value={ + date_range["start_date"]: {"email": {"delivered": 0, "failure": 0, "requested": 0}, "sms": {"delivered": 0, "failure": 1, "requested": 1}}, + } + ) + + client = SocketIOTestClient(app, socketio) + try: + connected = client.is_connected() + assert connected, "Client should be connected" + + client.emit('fetch_daily_stats', service_id) + + received = client.get_received() + assert received, "Should receive a response message" + assert received[0]['name'] == 'daily_stats_update' + assert received[0]['args'][0] == { + date_range["start_date"]: {"email": {"delivered": 0, "failure": 0, "requested": 0}, "sms": {"delivered": 0, "failure": 1, "requested": 1}}, + } + + mock_service_api.assert_called_once_with( + service_id, + start_date=expected_call_args["start_date"], + days=expected_call_args["days"] + ) + finally: + client.disconnect() + disconnected = not client.is_connected() + assert disconnected, "Client should be disconnected" From ae7a14f3f2570d90ef85e36c4fdd914eae8fa89c Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Thu, 6 Jun 2024 01:10:09 -0700 Subject: [PATCH 21/25] fixed js chart referenced --- app/assets/javascripts/chartDashboard.js | 104 ++++++++++--------- app/templates/views/dashboard/dashboard.html | 3 +- 2 files changed, 58 insertions(+), 49 deletions(-) diff --git a/app/assets/javascripts/chartDashboard.js b/app/assets/javascripts/chartDashboard.js index e73f655f9..d191abef1 100644 --- a/app/assets/javascripts/chartDashboard.js +++ b/app/assets/javascripts/chartDashboard.js @@ -1,56 +1,66 @@ (function (window) { - // Initialize flask-socketio - var socket = io(); - var serviceId = chart.getAttribute('data-service-id'); - socket.on('connect', function() { - socket.emit('fetch_daily_stats', serviceId); - }); - - socket.on('daily_stats_update', function(data) { - var labels = []; - var deliveredData = []; - - for (var date in data) { - labels.push(date); - deliveredData.push(data[date].sms.delivered); + function initializeChartAndSocket() { + var ctx = document.getElementById('myChart'); + if (!ctx) { + return; } - myBarChart.data.labels = labels; - myBarChart.data.datasets[0].data = deliveredData; - myBarChart.update(); - }); - - socket.on('error', function(data) { - console.log('Error:', data); - }); - - sevenDaysButton.addEventListener('click', function() { - socket.emit('fetch_daily_stats', serviceId); - }); - - // Initialize Chart.js bar chart - var ctx = document.getElementById('myChart').getContext('2d'); - var myBarChart = new Chart(ctx, { - type: 'bar', - data: { - labels: [], - datasets: [ - { - label: 'Delivered', - data: [], - backgroundColor: '#0076d6', - stack: 'Stack 0' - }, - ] - }, - options: { - scales: { - y: { - beginAtZero: true + var myBarChart = new Chart(ctx.getContext('2d'), { + type: 'bar', + data: { + labels: [], + datasets: [ + { + label: 'Delivered', + data: [], + backgroundColor: '#0076d6', + stack: 'Stack 0' + }, + ] + }, + options: { + scales: { + y: { + beginAtZero: true + } } } + }); + + var socket = io(); + var serviceId = ctx.getAttribute('data-service-id'); + + socket.on('connect', function() { + socket.emit('fetch_daily_stats', serviceId); + }); + + socket.on('daily_stats_update', function(data) { + var labels = []; + var deliveredData = []; + + 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', serviceId); + }); } - }); + } + + document.addEventListener('DOMContentLoaded', initializeChartAndSocket); })(window); diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index a776ce83c..7b04f9baf 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -23,8 +23,7 @@ - -
      + {{ ajax_block(partials, updates_url, 'inbox') }} {{ ajax_block(partials, updates_url, 'totals') }} From f98cff8d40546509d6c5c8c5e5a2fd34ea7d638a Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Thu, 6 Jun 2024 10:01:58 -0700 Subject: [PATCH 22/25] fixed linting issues --- tests/app/main/views/test_dashboard.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index f92efbb19..d46235668 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -1884,7 +1884,7 @@ def test_service_dashboard_shows_batched_jobs( assert len(rows) == 1 -@pytest.fixture +@pytest.fixture() def app_with_socketio(): app = Flask("app") create_app(app) @@ -1892,10 +1892,18 @@ def app_with_socketio(): @pytest.mark.parametrize( - "service_id, date_range, expected_call_args", + ("service_id", "date_range", "expected_call_args"), [ - (SERVICE_ONE_ID, {"start_date": "2024-01-01", "days": 7}, {"service_id": SERVICE_ONE_ID, "start_date": "2024-01-01", "days": 7}), - (SERVICE_TWO_ID, {"start_date": "2023-06-01", "days": 7}, {"service_id": SERVICE_TWO_ID, "start_date": "2023-06-01", "days": 7}), + ( + SERVICE_ONE_ID, + {"start_date": "2024-01-01", "days": 7}, + {"service_id": SERVICE_ONE_ID, "start_date": "2024-01-01", "days": 7} + ), + ( + SERVICE_TWO_ID, + {"start_date": "2023-06-01", "days": 7}, + {"service_id": SERVICE_TWO_ID, "start_date": "2023-06-01", "days": 7} + ), ] ) def test_fetch_daily_stats( @@ -1914,7 +1922,10 @@ def test_fetch_daily_stats( mock_service_api = mocker.patch( "app.service_api_client.get_service_notification_statistics_by_day", return_value={ - date_range["start_date"]: {"email": {"delivered": 0, "failure": 0, "requested": 0}, "sms": {"delivered": 0, "failure": 1, "requested": 1}}, + date_range["start_date"]: { + "email": {"delivered": 0, "failure": 0, "requested": 0}, + "sms": {"delivered": 0, "failure": 1, "requested": 1} + }, } ) @@ -1929,7 +1940,10 @@ def test_fetch_daily_stats( assert received, "Should receive a response message" assert received[0]['name'] == 'daily_stats_update' assert received[0]['args'][0] == { - date_range["start_date"]: {"email": {"delivered": 0, "failure": 0, "requested": 0}, "sms": {"delivered": 0, "failure": 1, "requested": 1}}, + date_range["start_date"]: { + "email": {"delivered": 0, "failure": 0, "requested": 0}, + "sms": {"delivered": 0, "failure": 1, "requested": 1} + }, } mock_service_api.assert_called_once_with( From e413f8c640c96c625edec8469a6cd57b53ee7d3d Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Thu, 6 Jun 2024 10:05:36 -0700 Subject: [PATCH 23/25] added sampleChartDashboard.js --- .../{chartDashboard.js => sampleChartDashboard.js} | 0 app/templates/views/dashboard/dashboard.html | 4 ++-- gulpfile.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename app/assets/javascripts/{chartDashboard.js => sampleChartDashboard.js} (100%) diff --git a/app/assets/javascripts/chartDashboard.js b/app/assets/javascripts/sampleChartDashboard.js similarity index 100% rename from app/assets/javascripts/chartDashboard.js rename to app/assets/javascripts/sampleChartDashboard.js diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index 7b04f9baf..c1ba46caf 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -22,8 +22,8 @@ Messages sent - - + {{ ajax_block(partials, updates_url, 'inbox') }} {{ ajax_block(partials, updates_url, 'totals') }} diff --git a/gulpfile.js b/gulpfile.js index b68da089f..0e2c1eaac 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -127,7 +127,7 @@ const javascripts = () => { paths.src + 'javascripts/date.js', paths.src + 'javascripts/loginAlert.js', paths.src + 'javascripts/main.js', - paths.src + 'javascripts/chartDashboard.js', + paths.src + 'javascripts/sampleChartDashboard.js', ]) .pipe(plugins.prettyerror()) .pipe(plugins.babel({ From c0b2c42473cb3d1ab15bd63d029d7d9ea5e6f424 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Thu, 6 Jun 2024 10:42:49 -0700 Subject: [PATCH 24/25] removed imported unused style issue --- tests/app/main/views/test_dashboard.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index d46235668..5e4e73f62 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -17,7 +17,6 @@ from app.main.views.dashboard import ( aggregate_template_usage, format_monthly_stats_to_list, get_dashboard_totals, - get_stats_date_range, get_tuples_of_financial_years, ) from tests import ( From 33f8f4c0000161ce0415c2e8fd6a87c4e5ce24a9 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Thu, 6 Jun 2024 10:50:24 -0700 Subject: [PATCH 25/25] fix import sort --- tests/app/main/views/test_dashboard.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index 5e4e73f62..285444b1b 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -7,10 +7,7 @@ from flask import Flask, url_for from flask_socketio import SocketIOTestClient from freezegun import freeze_time -from app import ( - create_app, - socketio -) +from app import create_app, socketio from app.main.views.dashboard import ( aggregate_notifications_stats, aggregate_status_types,