Merge main

This commit is contained in:
Andrew Shumway
2024-06-10 10:44:09 -06:00
75 changed files with 1237 additions and 1256 deletions

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

@@ -26,6 +26,7 @@ from app.main.views import sign_in
from app.main.views.verify import activate_user
from app.models.user import InvitedOrgUser, InvitedUser, User
from app.utils import hide_from_search_engines, hilite
from app.utils.user import is_gov_user
@main.route("/register", methods=["GET", "POST"])
@@ -147,6 +148,11 @@ def check_invited_user_email_address_matches_expected(
flash("You cannot accept an invite for another person.")
abort(403)
if not is_gov_user(user_email):
debug_msg("invited user has a non-government email address.")
flash("You must use a government email address.")
abort(403)
@main.route("/set-up-your-profile", methods=["GET", "POST"])
@hide_from_search_engines
@@ -245,10 +251,21 @@ def get_invited_user_email_address(invited_user_id):
def invited_user_accept_invite(invited_user_id):
invited_user = InvitedUser.by_id(invited_user_id)
if invited_user.status == "expired":
current_app.logger.error("User invitation has expired")
flash("Your invitation has expired.")
flash(
"Your invitation has expired; please contact the person who invited you for additional help."
)
abort(401)
if invited_user.status == "cancelled":
current_app.logger.error("User invitation has been cancelled")
flash(
"Your invitation is no longer valid; please contact the person who invited you for additional help."
)
abort(401)
invited_user.accept_invite()

View File

@@ -3,7 +3,16 @@ import uuid
from string import ascii_uppercase
from zipfile import BadZipFile
from flask import abort, flash, redirect, render_template, request, session, url_for
from flask import (
abort,
current_app,
flash,
redirect,
render_template,
request,
session,
url_for,
)
from flask_login import current_user
from markupsafe import Markup
from notifications_python_client.errors import HTTPError
@@ -31,12 +40,18 @@ from app.s3_client.s3_csv_client import (
s3upload,
set_metadata_on_csv_upload,
)
from app.utils import PermanentRedirect, should_skip_template_page, unicode_truncate
from app.utils import (
PermanentRedirect,
hilite,
should_skip_template_page,
unicode_truncate,
)
from app.utils.csv import Spreadsheet, get_errors_for_csv
from app.utils.templates import get_template
from app.utils.user import user_has_permissions
from notifications_utils import SMS_CHAR_COUNT_LIMIT
from notifications_utils.insensitive_dict import InsensitiveDict
from notifications_utils.logging import scrub
from notifications_utils.recipients import RecipientCSV, first_column_headings
from notifications_utils.sanitise_text import SanitiseASCII
@@ -938,6 +953,9 @@ def send_notification(service_id, template_id):
)
)
current_app.logger.info(
hilite(scrub(f"Recipient for the one-off will be {recipient}"))
)
keys = []
values = []
for k, v in session["placeholders"].items():
@@ -948,7 +966,9 @@ def send_notification(service_id, template_id):
vals = ",".join(values)
data = f"{data}\r\n{vals}"
filename = f"one-off-{current_user.name}-{uuid.uuid4()}.csv"
filename = (
f"one-off-{uuid.uuid4()}.csv" # {current_user.name} removed from filename
)
my_data = {"filename": filename, "template_id": template_id, "data": data}
upload_id = s3upload(service_id, my_data)
form = CsvUploadForm()
@@ -969,6 +989,19 @@ def send_notification(service_id, template_id):
valid="True",
)
# Here we are attempting to cleverly link the job id to the one-off recipient
# If we know the partial phone number of the recipient, we can search
# on that initially and find this, which will give us the job_id
# And once we know the job_id, we can search on that and it might tell us something
# about report generation.
current_app.logger.info(
hilite(
scrub(
f"Created job to send one-off, recipient is {recipient}, job_id is {upload_id}"
)
)
)
session.pop("recipient")
session.pop("placeholders")

View File

@@ -4,7 +4,16 @@ import uuid
import jwt
import requests
from flask import Response, current_app, redirect, render_template, request, url_for
from flask import (
Response,
abort,
current_app,
flash,
redirect,
render_template,
request,
url_for,
)
from flask_login import current_user
from app import login_manager, user_api_client
@@ -15,6 +24,7 @@ from app.models.user import User
from app.utils import hide_from_search_engines
from app.utils.login import is_safe_redirect_url
from app.utils.time import is_less_than_days_ago
from app.utils.user import is_gov_user
from notifications_utils.url_safe_token import generate_token
@@ -88,6 +98,12 @@ def _do_login_dot_gov():
try:
access_token = _get_access_token(code, state)
user_email, user_uuid = _get_user_email_and_uuid(access_token)
if not is_gov_user(user_email):
current_app.logger.error(
"invited user has a non-government email address."
)
flash("You must use a government email address.")
abort(403)
redirect_url = request.args.get("next")
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)

View File

@@ -32,7 +32,7 @@ def using_notify_nav():
"link": "main.trial_mode_new",
},
{
"name": "Pricing",
"name": "Tracking usage",
"link": "main.pricing",
},
{