Merge remote-tracking branch 'origin' into 1484-dashboard-visualizations

This commit is contained in:
Jonathan Bobel
2024-06-07 11:07:12 -04:00
14 changed files with 344 additions and 98 deletions

View File

@@ -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,

View File

@@ -1,24 +0,0 @@
(function (window) {
const ctx = document.getElementById('myChart');
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
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
})(window);

View File

@@ -0,0 +1,66 @@
(function (window) {
function initializeChartAndSocket() {
var ctx = document.getElementById('myChart');
if (!ctx) {
return;
}
var myBarChart = new Chart(ctx.getContext('2d'), {
type: 'bar',
data: {
labels: [],
datasets: [
{
label: 'Delivered',
data: [],
backgroundColor: '#0076d6',
stack: 'Stack 0'
},
]
},
options: {
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);

View File

@@ -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 emit
from werkzeug.utils import redirect
from app import (
@@ -14,6 +15,7 @@ from app import (
job_api_client,
notification_api_client,
service_api_client,
socketio,
template_statistics_client,
)
from app.formatters import format_date_numeric, format_datetime_numeric, get_time_left
@@ -32,6 +34,18 @@ from app.utils.user import user_has_permissions
from notifications_utils.recipients import format_phone_number_human_readable
@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"})
@main.route("/services/<uuid:service_id>/dashboard")
@user_has_permissions("view_activity", "send_messages")
def old_service_dashboard(service_id):
@@ -84,6 +98,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,
)
@@ -434,6 +449,24 @@ 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_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)]

View File

@@ -43,6 +43,16 @@ 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_services(self, params_dict=None):
"""
Retrieve a list of services.

View File

@@ -31,7 +31,6 @@
<meta property="og:image" content="/static/images/notify-og-image.png">
{# google #}
<script type="text/javascript" src="{{ asset_url('js/gtm_head.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/chart.umd.js') }}"></script>
<meta name="google-site-verification" content="niWnSqImOWz6mVQTYqNb5tFK8HaKSB4b3ED4Z9gtUQ0" />
{% if g.hide_from_search_engines %}
<meta name="robots" content="noindex" />

View File

@@ -80,6 +80,8 @@
Messages sent
</h2>
<!-- <button id="sevenDaysButton">7 Days</button>
<canvas id="myChart" data-service-id="{{ service_id }}"></canvas> -->
{{ ajax_block(partials, updates_url, 'inbox') }}
{{ ajax_block(partials, updates_url, 'totals') }}