2024-08-08 10:47:18 -07:00
|
|
|
|
import csv
|
2016-08-23 17:38:27 +01:00
|
|
|
|
import itertools
|
2023-07-26 09:08:55 -07:00
|
|
|
|
import json
|
2019-02-19 11:35:34 +00:00
|
|
|
|
from collections import OrderedDict
|
2017-01-03 10:45:06 +00:00
|
|
|
|
from datetime import datetime
|
2024-08-08 10:59:57 -07:00
|
|
|
|
from io import StringIO
|
2018-02-20 11:22:17 +00:00
|
|
|
|
|
2024-09-02 07:51:57 -07:00
|
|
|
|
from flask import (
|
|
|
|
|
|
Response,
|
|
|
|
|
|
abort,
|
|
|
|
|
|
current_app,
|
|
|
|
|
|
flash,
|
|
|
|
|
|
render_template,
|
|
|
|
|
|
request,
|
|
|
|
|
|
session,
|
|
|
|
|
|
url_for,
|
|
|
|
|
|
)
|
2018-09-06 16:34:23 +01:00
|
|
|
|
from notifications_python_client.errors import HTTPError
|
2016-05-24 15:52:44 +01:00
|
|
|
|
|
2018-06-28 10:28:57 +01:00
|
|
|
|
from app import (
|
2019-08-23 17:14:04 +01:00
|
|
|
|
billing_api_client,
|
2018-06-28 10:28:57 +01:00
|
|
|
|
complaint_api_client,
|
2019-04-24 16:23:12 +01:00
|
|
|
|
format_date_numeric,
|
2019-07-22 13:09:03 +01:00
|
|
|
|
notification_api_client,
|
2018-06-28 10:28:57 +01:00
|
|
|
|
platform_stats_api_client,
|
|
|
|
|
|
service_api_client,
|
2023-07-26 09:08:55 -07:00
|
|
|
|
user_api_client,
|
2018-06-28 10:28:57 +01:00
|
|
|
|
)
|
2019-11-27 16:25:25 +00:00
|
|
|
|
from app.extensions import redis_client
|
2016-05-24 15:52:44 +01:00
|
|
|
|
from app.main import main
|
2019-02-15 11:13:53 +00:00
|
|
|
|
from app.main.forms import (
|
2022-03-15 10:50:18 +00:00
|
|
|
|
AdminClearCacheForm,
|
2022-01-28 15:43:58 +00:00
|
|
|
|
BillingReportDateFilterForm,
|
2019-02-15 11:13:53 +00:00
|
|
|
|
DateFilterForm,
|
2019-07-22 13:09:03 +01:00
|
|
|
|
RequiredDateFilterForm,
|
2019-02-15 11:13:53 +00:00
|
|
|
|
)
|
2024-09-02 08:13:05 -07:00
|
|
|
|
from app.main.views.send import _send_notification
|
2018-08-14 13:34:53 +01:00
|
|
|
|
from app.statistics_utils import (
|
|
|
|
|
|
get_formatted_percentage,
|
|
|
|
|
|
get_formatted_percentage_two_dp,
|
|
|
|
|
|
)
|
2021-06-14 11:00:05 +01:00
|
|
|
|
from app.utils.csv import Spreadsheet
|
|
|
|
|
|
from app.utils.pagination import (
|
2018-07-03 10:54:33 +01:00
|
|
|
|
generate_next_dict,
|
|
|
|
|
|
generate_previous_dict,
|
2018-07-04 10:38:46 +01:00
|
|
|
|
get_page_from_request,
|
2018-07-03 10:54:33 +01:00
|
|
|
|
)
|
2024-09-02 08:13:05 -07:00
|
|
|
|
from app.utils.user import user_is_platform_admin
|
2016-05-26 14:36:23 +01:00
|
|
|
|
|
2018-06-25 13:34:54 +01:00
|
|
|
|
COMPLAINT_THRESHOLD = 0.02
|
|
|
|
|
|
FAILURE_THRESHOLD = 3
|
|
|
|
|
|
ZERO_FAILURE_THRESHOLD = 0
|
|
|
|
|
|
|
2016-05-24 15:52:44 +01:00
|
|
|
|
|
2016-12-28 15:23:19 +00:00
|
|
|
|
@main.route("/platform-admin")
|
2018-02-27 16:45:20 +00:00
|
|
|
|
@user_is_platform_admin
|
2020-03-19 10:49:40 +00:00
|
|
|
|
def platform_admin_splash_page():
|
|
|
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"views/platform-admin/splash-page.html",
|
2020-03-19 10:49:40 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@main.route("/platform-admin/summary")
|
|
|
|
|
|
@user_is_platform_admin
|
2016-05-24 15:52:44 +01:00
|
|
|
|
def platform_admin():
|
2023-08-25 09:12:23 -07:00
|
|
|
|
form = DateFilterForm(request.args, meta={"csrf": False})
|
2018-06-25 13:34:54 +01:00
|
|
|
|
api_args = {}
|
|
|
|
|
|
|
2018-06-29 15:28:25 +01:00
|
|
|
|
form.validate()
|
|
|
|
|
|
|
2018-06-25 13:34:54 +01:00
|
|
|
|
if form.start_date.data:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
api_args["start_date"] = form.start_date.data
|
|
|
|
|
|
api_args["end_date"] = form.end_date.data or datetime.utcnow().date()
|
2018-06-25 13:34:54 +01:00
|
|
|
|
|
2018-06-28 10:28:57 +01:00
|
|
|
|
platform_stats = platform_stats_api_client.get_aggregate_platform_stats(api_args)
|
2018-06-25 13:34:54 +01:00
|
|
|
|
number_of_complaints = complaint_api_client.get_complaint_count(api_args)
|
|
|
|
|
|
|
|
|
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"views/platform-admin/index.html",
|
2018-06-25 13:34:54 +01:00
|
|
|
|
form=form,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
global_stats=make_columns(platform_stats, number_of_complaints),
|
2018-06-25 13:34:54 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-08-06 11:31:05 -07:00
|
|
|
|
@main.route("/platform-admin/download-all-users")
|
|
|
|
|
|
@user_is_platform_admin
|
|
|
|
|
|
def download_all_users():
|
|
|
|
|
|
|
|
|
|
|
|
# Create a CSV string from the user data
|
|
|
|
|
|
users = user_api_client.get_all_users_detailed()
|
|
|
|
|
|
|
|
|
|
|
|
if len(users) == 0:
|
|
|
|
|
|
return "No data to download."
|
2024-08-08 10:47:18 -07:00
|
|
|
|
|
|
|
|
|
|
output = StringIO()
|
2024-08-08 13:02:51 -07:00
|
|
|
|
header = ["Name", "Email Address", "Phone Number", "Service"]
|
|
|
|
|
|
fieldnames = ["name", "email_address", "mobile_number", "service"]
|
2024-08-08 10:47:18 -07:00
|
|
|
|
writer = csv.DictWriter(
|
|
|
|
|
|
output,
|
2024-08-08 13:02:51 -07:00
|
|
|
|
fieldnames=fieldnames,
|
2024-08-12 07:27:32 -07:00
|
|
|
|
delimiter=",",
|
2024-08-08 10:47:18 -07:00
|
|
|
|
)
|
2024-08-08 13:02:51 -07:00
|
|
|
|
# Write custom header
|
|
|
|
|
|
writer.writerow(dict(zip(fieldnames, header)))
|
2024-08-06 11:31:05 -07:00
|
|
|
|
for user in users:
|
2024-08-08 13:02:51 -07:00
|
|
|
|
user_no_commas = {key: value.replace(",", "") for key, value in user.items()}
|
|
|
|
|
|
if user_no_commas["name"].startswith("e2e"):
|
2024-08-06 11:31:05 -07:00
|
|
|
|
continue
|
2024-08-08 13:02:51 -07:00
|
|
|
|
writer.writerow(user_no_commas)
|
2024-08-08 10:47:18 -07:00
|
|
|
|
csv_data = output.getvalue()
|
2024-08-08 13:02:51 -07:00
|
|
|
|
|
2024-08-06 11:31:05 -07:00
|
|
|
|
# Create a direct download response with the CSV data and appropriate headers
|
2024-08-08 13:11:58 -07:00
|
|
|
|
response = Response(csv_data, content_type="text/csv; charset=utf-8")
|
2024-08-06 11:31:05 -07:00
|
|
|
|
response.headers["Content-Disposition"] = "attachment; filename=users.csv"
|
|
|
|
|
|
|
|
|
|
|
|
return response
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-06-25 13:34:54 +01:00
|
|
|
|
def is_over_threshold(number, total, threshold):
|
|
|
|
|
|
percentage = number / total * 100 if total else 0
|
|
|
|
|
|
return percentage > threshold
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_status_box_data(stats, key, label, threshold=FAILURE_THRESHOLD):
|
|
|
|
|
|
return {
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"number": "{:,}".format(stats["failures"][key]),
|
|
|
|
|
|
"label": label,
|
|
|
|
|
|
"failing": is_over_threshold(stats["failures"][key], stats["total"], threshold),
|
|
|
|
|
|
"percentage": get_formatted_percentage(stats["failures"][key], stats["total"]),
|
2018-06-25 13:34:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_tech_failure_status_box_data(stats):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
stats = get_status_box_data(
|
|
|
|
|
|
stats, "technical-failure", "technical failures", ZERO_FAILURE_THRESHOLD
|
|
|
|
|
|
)
|
|
|
|
|
|
stats.pop("percentage")
|
2018-06-25 13:34:54 +01:00
|
|
|
|
return stats
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def make_columns(global_stats, complaints_number):
|
|
|
|
|
|
return [
|
|
|
|
|
|
# email
|
|
|
|
|
|
{
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"black_box": {
|
|
|
|
|
|
"number": global_stats["email"]["total"],
|
|
|
|
|
|
"notification_type": "email",
|
2018-06-25 13:34:54 +01:00
|
|
|
|
},
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"other_data": [
|
|
|
|
|
|
get_tech_failure_status_box_data(global_stats["email"]),
|
|
|
|
|
|
get_status_box_data(
|
|
|
|
|
|
global_stats["email"], "permanent-failure", "permanent failures"
|
|
|
|
|
|
),
|
|
|
|
|
|
get_status_box_data(
|
|
|
|
|
|
global_stats["email"], "temporary-failure", "temporary failures"
|
|
|
|
|
|
),
|
2018-06-25 13:34:54 +01:00
|
|
|
|
{
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"number": complaints_number,
|
|
|
|
|
|
"label": "complaints",
|
|
|
|
|
|
"failing": is_over_threshold(
|
|
|
|
|
|
complaints_number,
|
|
|
|
|
|
global_stats["email"]["total"],
|
|
|
|
|
|
COMPLAINT_THRESHOLD,
|
|
|
|
|
|
),
|
|
|
|
|
|
"percentage": get_formatted_percentage_two_dp(
|
|
|
|
|
|
complaints_number, global_stats["email"]["total"]
|
|
|
|
|
|
),
|
|
|
|
|
|
"url": url_for("main.platform_admin_list_complaints"),
|
|
|
|
|
|
},
|
2018-06-25 13:34:54 +01:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"test_data": {
|
|
|
|
|
|
"number": global_stats["email"]["test-key"],
|
|
|
|
|
|
"label": "test emails",
|
|
|
|
|
|
},
|
2018-06-25 13:34:54 +01:00
|
|
|
|
},
|
|
|
|
|
|
# sms
|
|
|
|
|
|
{
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"black_box": {
|
|
|
|
|
|
"number": global_stats["sms"]["total"],
|
|
|
|
|
|
"notification_type": "sms",
|
2018-06-25 13:34:54 +01:00
|
|
|
|
},
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"other_data": [
|
|
|
|
|
|
get_tech_failure_status_box_data(global_stats["sms"]),
|
|
|
|
|
|
get_status_box_data(
|
|
|
|
|
|
global_stats["sms"], "permanent-failure", "permanent failures"
|
|
|
|
|
|
),
|
|
|
|
|
|
get_status_box_data(
|
|
|
|
|
|
global_stats["sms"], "temporary-failure", "temporary failures"
|
|
|
|
|
|
),
|
2018-06-25 13:34:54 +01:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"test_data": {
|
|
|
|
|
|
"number": global_stats["sms"]["test-key"],
|
|
|
|
|
|
"label": "test text messages",
|
|
|
|
|
|
},
|
2018-06-25 13:34:54 +01:00
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@main.route("/platform-admin/live-services", endpoint="live_services")
|
|
|
|
|
|
@main.route("/platform-admin/trial-services", endpoint="trial_services")
|
2018-02-27 16:45:20 +00:00
|
|
|
|
@user_is_platform_admin
|
2017-07-21 09:14:29 +01:00
|
|
|
|
def platform_admin_services():
|
|
|
|
|
|
form = DateFilterForm(request.args)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
if all(
|
|
|
|
|
|
(
|
|
|
|
|
|
request.args.get("include_from_test_key") is None,
|
|
|
|
|
|
request.args.get("start_date") is None,
|
|
|
|
|
|
request.args.get("end_date") is None,
|
|
|
|
|
|
)
|
|
|
|
|
|
):
|
2018-08-03 14:30:34 +01:00
|
|
|
|
# Default to True if the user hasn’t done any filtering,
|
|
|
|
|
|
# otherwise respect their choice
|
|
|
|
|
|
form.include_from_test_key.data = True
|
2020-04-09 16:31:19 +01:00
|
|
|
|
|
|
|
|
|
|
include_from_test_key = form.include_from_test_key.data
|
2023-08-25 09:12:23 -07:00
|
|
|
|
api_args = {
|
|
|
|
|
|
"detailed": True,
|
|
|
|
|
|
"only_active": False, # specifically DO get inactive services
|
|
|
|
|
|
"include_from_test_key": include_from_test_key,
|
|
|
|
|
|
}
|
2017-07-21 09:14:29 +01:00
|
|
|
|
|
|
|
|
|
|
if form.start_date.data:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
api_args["start_date"] = form.start_date.data
|
|
|
|
|
|
api_args["end_date"] = form.end_date.data or datetime.utcnow().date()
|
2017-07-21 09:14:29 +01:00
|
|
|
|
|
|
|
|
|
|
services = filter_and_sort_services(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service_api_client.get_services(api_args)["data"],
|
|
|
|
|
|
trial_mode_services=request.endpoint == "main.trial_services",
|
2017-07-21 09:14:29 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"views/platform-admin/services.html",
|
2020-04-09 16:31:19 +01:00
|
|
|
|
include_from_test_key=include_from_test_key,
|
2017-07-21 09:14:29 +01:00
|
|
|
|
form=form,
|
|
|
|
|
|
services=list(format_stats_by_service(services)),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
page_title="{} services".format(
|
|
|
|
|
|
"Trial mode" if request.endpoint == "main.trial_services" else "Live"
|
2017-07-21 09:14:29 +01:00
|
|
|
|
),
|
|
|
|
|
|
global_stats=create_global_stats(services),
|
|
|
|
|
|
)
|
2019-04-24 10:27:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@main.route("/platform-admin/reports")
|
|
|
|
|
|
@user_is_platform_admin
|
|
|
|
|
|
def platform_admin_reports():
|
2023-08-25 09:12:23 -07:00
|
|
|
|
return render_template("views/platform-admin/reports.html")
|
2019-04-24 16:23:12 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@main.route("/platform-admin/reports/live-services.csv")
|
|
|
|
|
|
@user_is_platform_admin
|
|
|
|
|
|
def live_services_csv():
|
2019-04-25 18:07:40 +01:00
|
|
|
|
results = service_api_client.get_live_services_data()["data"]
|
2019-05-31 12:13:34 +01:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
column_names = OrderedDict(
|
|
|
|
|
|
[
|
|
|
|
|
|
("service_id", "Service ID"),
|
|
|
|
|
|
("organization_name", "Organization"),
|
|
|
|
|
|
("organization_type", "Organization type"),
|
|
|
|
|
|
("service_name", "Service name"),
|
|
|
|
|
|
("consent_to_research", "Consent to research"),
|
|
|
|
|
|
("contact_name", "Main contact"),
|
|
|
|
|
|
("contact_email", "Contact email"),
|
|
|
|
|
|
("contact_mobile", "Contact mobile"),
|
|
|
|
|
|
("live_date", "Live date"),
|
|
|
|
|
|
("sms_volume_intent", "SMS volume intent"),
|
|
|
|
|
|
("email_volume_intent", "Email volume intent"),
|
|
|
|
|
|
("sms_totals", "SMS sent this year"),
|
|
|
|
|
|
("email_totals", "Emails sent this year"),
|
|
|
|
|
|
("free_sms_fragment_limit", "Free sms allowance"),
|
|
|
|
|
|
]
|
|
|
|
|
|
)
|
2019-05-31 12:13:34 +01:00
|
|
|
|
|
|
|
|
|
|
# initialise with header row
|
|
|
|
|
|
live_services_data = [[x for x in column_names.values()]]
|
|
|
|
|
|
|
2019-04-25 18:07:40 +01:00
|
|
|
|
for row in results:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
if row["live_date"]:
|
|
|
|
|
|
row["live_date"] = datetime.strptime(
|
|
|
|
|
|
row["live_date"], "%a, %d %b %Y %X %Z"
|
|
|
|
|
|
).strftime("%d-%m-%Y")
|
2019-05-31 12:13:34 +01:00
|
|
|
|
|
|
|
|
|
|
live_services_data.append([row[api_key] for api_key in column_names.keys()])
|
2019-04-30 17:44:59 +01:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
return (
|
|
|
|
|
|
Spreadsheet.from_rows(live_services_data).as_csv_data,
|
|
|
|
|
|
200,
|
|
|
|
|
|
{
|
|
|
|
|
|
"Content-Type": "text/csv; charset=utf-8",
|
|
|
|
|
|
"Content-Disposition": 'inline; filename="{} live services report.csv"'.format(
|
|
|
|
|
|
format_date_numeric(datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ")),
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
2019-04-30 17:44:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@main.route(
|
|
|
|
|
|
"/platform-admin/reports/notifications-sent-by-service", methods=["GET", "POST"]
|
|
|
|
|
|
)
|
2019-07-22 13:09:03 +01:00
|
|
|
|
@user_is_platform_admin
|
|
|
|
|
|
def notifications_sent_by_service():
|
|
|
|
|
|
form = RequiredDateFilterForm()
|
|
|
|
|
|
|
|
|
|
|
|
if form.validate_on_submit():
|
|
|
|
|
|
start_date = form.start_date.data
|
|
|
|
|
|
end_date = form.end_date.data
|
|
|
|
|
|
|
|
|
|
|
|
headers = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"date_created",
|
|
|
|
|
|
"service_id",
|
|
|
|
|
|
"service_name",
|
|
|
|
|
|
"notification_type",
|
|
|
|
|
|
"count_sending",
|
|
|
|
|
|
"count_delivered",
|
|
|
|
|
|
"count_technical_failure",
|
|
|
|
|
|
"count_temporary_failure",
|
|
|
|
|
|
"count_permanent_failure",
|
|
|
|
|
|
"count_sent",
|
2019-07-22 13:09:03 +01:00
|
|
|
|
]
|
2023-08-25 09:12:23 -07:00
|
|
|
|
result = notification_api_client.get_notification_status_by_service(
|
|
|
|
|
|
start_date, end_date
|
|
|
|
|
|
)
|
|
|
|
|
|
content_disposition = (
|
|
|
|
|
|
'attachment; filename="{} to {} notification status '
|
|
|
|
|
|
'per service report.csv"'.format(start_date, end_date)
|
|
|
|
|
|
)
|
|
|
|
|
|
return (
|
|
|
|
|
|
Spreadsheet.from_rows([headers] + result).as_csv_data,
|
|
|
|
|
|
200,
|
|
|
|
|
|
{
|
|
|
|
|
|
"Content-Type": "text/csv; charset=utf-8",
|
|
|
|
|
|
"Content-Disposition": content_disposition,
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
2019-07-22 13:09:03 +01:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
return render_template(
|
|
|
|
|
|
"views/platform-admin/notifications_by_service.html", form=form
|
|
|
|
|
|
)
|
2019-07-22 13:09:03 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@main.route("/platform-admin/reports/usage-for-all-services", methods=["GET", "POST"])
|
2019-08-23 17:14:04 +01:00
|
|
|
|
@user_is_platform_admin
|
2021-03-15 18:04:01 +00:00
|
|
|
|
def get_billing_report():
|
2022-01-28 15:43:58 +00:00
|
|
|
|
form = BillingReportDateFilterForm()
|
2019-08-23 17:14:04 +01:00
|
|
|
|
|
|
|
|
|
|
if form.validate_on_submit():
|
|
|
|
|
|
start_date = form.start_date.data
|
|
|
|
|
|
end_date = form.end_date.data
|
2021-03-02 13:13:41 +00:00
|
|
|
|
headers = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"organization_id",
|
|
|
|
|
|
"organization_name",
|
|
|
|
|
|
"service_id",
|
|
|
|
|
|
"service_name",
|
|
|
|
|
|
"sms_cost",
|
|
|
|
|
|
"sms_chargeable_units",
|
|
|
|
|
|
"purchase_order_number",
|
|
|
|
|
|
"contact_names",
|
|
|
|
|
|
"contact_email_addresses",
|
|
|
|
|
|
"billing_reference",
|
2021-03-02 13:13:41 +00:00
|
|
|
|
]
|
2021-03-15 18:28:05 +00:00
|
|
|
|
try:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
result = billing_api_client.get_data_for_billing_report(
|
|
|
|
|
|
start_date, end_date
|
|
|
|
|
|
)
|
2021-03-15 18:28:05 +00:00
|
|
|
|
except HTTPError as e:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
message = "Date must be in a single financial year."
|
2021-03-15 18:28:05 +00:00
|
|
|
|
if e.status_code == 400 and e.message == message:
|
|
|
|
|
|
flash(message)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
return render_template(
|
|
|
|
|
|
"views/platform-admin/get-billing-report.html", form=form
|
|
|
|
|
|
)
|
2021-03-15 18:28:05 +00:00
|
|
|
|
else:
|
|
|
|
|
|
raise e
|
2019-08-27 16:53:08 +01:00
|
|
|
|
rows = [
|
|
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
r["organization_id"],
|
|
|
|
|
|
r["organization_name"],
|
|
|
|
|
|
r["service_id"],
|
|
|
|
|
|
r["service_name"],
|
|
|
|
|
|
r["sms_cost"],
|
|
|
|
|
|
r["sms_chargeable_units"],
|
|
|
|
|
|
r.get("purchase_order_number"),
|
|
|
|
|
|
r.get("contact_names"),
|
|
|
|
|
|
r.get("contact_email_addresses"),
|
|
|
|
|
|
r.get("billing_reference"),
|
2019-08-27 16:53:08 +01:00
|
|
|
|
]
|
|
|
|
|
|
for r in result
|
|
|
|
|
|
]
|
2019-08-23 17:14:04 +01:00
|
|
|
|
if rows:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
return (
|
|
|
|
|
|
Spreadsheet.from_rows([headers] + rows).as_csv_data,
|
|
|
|
|
|
200,
|
|
|
|
|
|
{
|
|
|
|
|
|
"Content-Type": "text/csv; charset=utf-8",
|
|
|
|
|
|
"Content-Disposition": 'attachment; filename="Billing Report from {} to {}.csv"'.format(
|
|
|
|
|
|
start_date, end_date
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
2019-08-23 17:14:04 +01:00
|
|
|
|
else:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
flash("No results for dates")
|
|
|
|
|
|
return render_template("views/platform-admin/get-billing-report.html", form=form)
|
2019-08-23 17:14:04 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@main.route("/platform-admin/reports/get-users-report", methods=["GET", "POST"])
|
2023-07-26 09:08:55 -07:00
|
|
|
|
@user_is_platform_admin
|
|
|
|
|
|
def get_users_report():
|
|
|
|
|
|
headers = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"name",
|
|
|
|
|
|
"services",
|
|
|
|
|
|
"platform admin",
|
|
|
|
|
|
"permissions",
|
|
|
|
|
|
"password changed at",
|
|
|
|
|
|
"state",
|
2023-07-26 09:08:55 -07:00
|
|
|
|
]
|
|
|
|
|
|
try:
|
|
|
|
|
|
result = user_api_client.get_all_users()
|
|
|
|
|
|
|
|
|
|
|
|
except HTTPError as e:
|
|
|
|
|
|
raise e
|
|
|
|
|
|
|
|
|
|
|
|
rows = []
|
|
|
|
|
|
for r in result:
|
|
|
|
|
|
rows.append(_get_user_row(r))
|
|
|
|
|
|
if rows:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
return (
|
|
|
|
|
|
Spreadsheet.from_rows([headers] + rows).as_csv_data,
|
|
|
|
|
|
200,
|
|
|
|
|
|
{
|
|
|
|
|
|
"Content-Type": "text/csv; charset=utf-8",
|
|
|
|
|
|
"Content-Disposition": f'attachment; filename="User Report {datetime.utcnow()}.csv"',
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
2023-07-26 09:08:55 -07:00
|
|
|
|
else:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
flash("No results")
|
|
|
|
|
|
return render_template("views/platform-admin/get-users-report.html")
|
2023-07-26 09:08:55 -07:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@main.route("/platform-admin/reports/volumes-by-service", methods=["GET", "POST"])
|
2022-03-07 14:30:11 +00:00
|
|
|
|
@user_is_platform_admin
|
|
|
|
|
|
def get_volumes_by_service():
|
|
|
|
|
|
form = BillingReportDateFilterForm()
|
|
|
|
|
|
|
|
|
|
|
|
if form.validate_on_submit():
|
|
|
|
|
|
start_date = form.start_date.data
|
|
|
|
|
|
end_date = form.end_date.data
|
|
|
|
|
|
headers = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"organization id",
|
|
|
|
|
|
"organization name",
|
|
|
|
|
|
"service id",
|
|
|
|
|
|
"service name",
|
|
|
|
|
|
"free allowance",
|
|
|
|
|
|
"sms notifications",
|
|
|
|
|
|
"sms chargeable units",
|
|
|
|
|
|
"email totals",
|
2022-03-07 14:30:11 +00:00
|
|
|
|
]
|
2023-08-25 09:12:23 -07:00
|
|
|
|
result = billing_api_client.get_data_for_volumes_by_service_report(
|
|
|
|
|
|
start_date, end_date
|
|
|
|
|
|
)
|
2022-03-07 14:30:11 +00:00
|
|
|
|
|
|
|
|
|
|
rows = [
|
|
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
r["organization_id"],
|
|
|
|
|
|
r["organization_name"],
|
|
|
|
|
|
r["service_id"],
|
|
|
|
|
|
r["service_name"],
|
|
|
|
|
|
r["free_allowance"],
|
|
|
|
|
|
r["sms_notifications"],
|
|
|
|
|
|
r["sms_chargeable_units"],
|
|
|
|
|
|
r["email_totals"],
|
2022-03-07 14:30:11 +00:00
|
|
|
|
]
|
|
|
|
|
|
for r in result
|
|
|
|
|
|
]
|
|
|
|
|
|
if rows:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
return (
|
|
|
|
|
|
Spreadsheet.from_rows([headers] + rows).as_csv_data,
|
|
|
|
|
|
200,
|
|
|
|
|
|
{
|
|
|
|
|
|
"Content-Type": "text/csv; charset=utf-8",
|
|
|
|
|
|
"Content-Disposition": 'attachment; filename="Volumes by service report from {} to {}.csv"'.format(
|
|
|
|
|
|
start_date, end_date
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
2022-03-07 14:30:11 +00:00
|
|
|
|
else:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
flash("No results for dates")
|
|
|
|
|
|
return render_template(
|
|
|
|
|
|
"views/platform-admin/volumes-by-service-report.html", form=form
|
|
|
|
|
|
)
|
2022-03-07 14:30:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@main.route("/platform-admin/reports/daily-volumes-report", methods=["GET", "POST"])
|
2022-03-07 14:30:11 +00:00
|
|
|
|
@user_is_platform_admin
|
|
|
|
|
|
def get_daily_volumes():
|
|
|
|
|
|
form = BillingReportDateFilterForm()
|
|
|
|
|
|
|
|
|
|
|
|
if form.validate_on_submit():
|
|
|
|
|
|
start_date = form.start_date.data
|
|
|
|
|
|
end_date = form.end_date.data
|
|
|
|
|
|
headers = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"day",
|
|
|
|
|
|
"sms totals",
|
|
|
|
|
|
"sms fragment totals",
|
|
|
|
|
|
"sms chargeable units",
|
2022-12-05 15:33:44 -05:00
|
|
|
|
"email totals",
|
2022-03-07 14:30:11 +00:00
|
|
|
|
]
|
2023-08-25 09:12:23 -07:00
|
|
|
|
result = billing_api_client.get_data_for_daily_volumes_report(
|
|
|
|
|
|
start_date, end_date
|
|
|
|
|
|
)
|
2022-03-07 14:30:11 +00:00
|
|
|
|
|
|
|
|
|
|
rows = [
|
|
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
r["day"],
|
|
|
|
|
|
r["sms_totals"],
|
|
|
|
|
|
r["sms_fragment_totals"],
|
|
|
|
|
|
r["sms_chargeable_units"],
|
2022-12-05 15:33:44 -05:00
|
|
|
|
r["email_totals"],
|
2022-03-07 14:30:11 +00:00
|
|
|
|
]
|
|
|
|
|
|
for r in result
|
|
|
|
|
|
]
|
|
|
|
|
|
if rows:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
return (
|
|
|
|
|
|
Spreadsheet.from_rows([headers] + rows).as_csv_data,
|
|
|
|
|
|
200,
|
|
|
|
|
|
{
|
|
|
|
|
|
"Content-Type": "text/csv; charset=utf-8",
|
|
|
|
|
|
"Content-Disposition": 'attachment; filename="Daily volumes report from {} to {}.csv"'.format(
|
|
|
|
|
|
start_date, end_date
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
2022-03-07 14:30:11 +00:00
|
|
|
|
else:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
flash("No results for dates")
|
|
|
|
|
|
return render_template("views/platform-admin/daily-volumes-report.html", form=form)
|
2022-03-07 14:30:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@main.route(
|
|
|
|
|
|
"/platform-admin/reports/daily-sms-provider-volumes-report", methods=["GET", "POST"]
|
|
|
|
|
|
)
|
2022-04-08 16:05:43 +01:00
|
|
|
|
@user_is_platform_admin
|
|
|
|
|
|
def get_daily_sms_provider_volumes():
|
|
|
|
|
|
form = BillingReportDateFilterForm()
|
|
|
|
|
|
|
|
|
|
|
|
if form.validate_on_submit():
|
|
|
|
|
|
start_date = form.start_date.data
|
|
|
|
|
|
end_date = form.end_date.data
|
|
|
|
|
|
headers = [
|
|
|
|
|
|
"day",
|
|
|
|
|
|
"provider",
|
|
|
|
|
|
"sms totals",
|
|
|
|
|
|
"sms fragment totals",
|
|
|
|
|
|
"sms chargeable units",
|
|
|
|
|
|
"sms cost",
|
|
|
|
|
|
]
|
2023-08-25 09:12:23 -07:00
|
|
|
|
result = billing_api_client.get_data_for_daily_sms_provider_volumes_report(
|
|
|
|
|
|
start_date, end_date
|
|
|
|
|
|
)
|
2022-04-08 16:05:43 +01:00
|
|
|
|
|
|
|
|
|
|
rows = [
|
|
|
|
|
|
[
|
|
|
|
|
|
r["day"],
|
|
|
|
|
|
r["provider"],
|
|
|
|
|
|
r["sms_totals"],
|
|
|
|
|
|
r["sms_fragment_totals"],
|
|
|
|
|
|
r["sms_chargeable_units"],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
r["sms_cost"],
|
2022-04-08 16:05:43 +01:00
|
|
|
|
]
|
|
|
|
|
|
for r in result
|
|
|
|
|
|
]
|
2023-08-25 09:12:23 -07:00
|
|
|
|
content_disp = f'attachment; filename="Daily SMS provider volumes report from {start_date} to {end_date}.csv"'
|
2022-04-08 16:05:43 +01:00
|
|
|
|
if rows:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
return (
|
|
|
|
|
|
Spreadsheet.from_rows([headers] + rows).as_csv_data,
|
|
|
|
|
|
200,
|
|
|
|
|
|
{
|
|
|
|
|
|
"Content-Type": "text/csv; charset=utf-8",
|
|
|
|
|
|
"Content-Disposition": content_disp,
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
2022-04-08 16:05:43 +01:00
|
|
|
|
else:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
flash("No results for dates")
|
|
|
|
|
|
return render_template(
|
|
|
|
|
|
"views/platform-admin/daily-sms-provider-volumes-report.html", form=form
|
|
|
|
|
|
)
|
2022-04-08 16:05:43 +01:00
|
|
|
|
|
|
|
|
|
|
|
2018-06-06 15:22:48 +01:00
|
|
|
|
@main.route("/platform-admin/complaints")
|
|
|
|
|
|
@user_is_platform_admin
|
|
|
|
|
|
def platform_admin_list_complaints():
|
2018-07-04 10:38:46 +01:00
|
|
|
|
page = get_page_from_request()
|
|
|
|
|
|
if page is None:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
abort(404, "Invalid page argument ({}).".format(request.args.get("page")))
|
2018-07-04 10:38:46 +01:00
|
|
|
|
|
2018-07-03 10:54:33 +01:00
|
|
|
|
response = complaint_api_client.get_all_complaints(page=page)
|
|
|
|
|
|
|
|
|
|
|
|
prev_page = None
|
2023-08-25 09:12:23 -07:00
|
|
|
|
if response["links"].get("prev"):
|
|
|
|
|
|
prev_page = generate_previous_dict(
|
|
|
|
|
|
"main.platform_admin_list_complaints", None, page
|
|
|
|
|
|
)
|
2018-07-03 10:54:33 +01:00
|
|
|
|
next_page = None
|
2023-08-25 09:12:23 -07:00
|
|
|
|
if response["links"].get("next"):
|
|
|
|
|
|
next_page = generate_next_dict(
|
|
|
|
|
|
"main.platform_admin_list_complaints", None, page
|
|
|
|
|
|
)
|
2018-07-03 10:54:33 +01:00
|
|
|
|
|
2018-06-06 15:22:48 +01:00
|
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"views/platform-admin/complaints.html",
|
|
|
|
|
|
complaints=response["complaints"],
|
2018-07-03 10:54:33 +01:00
|
|
|
|
page=page,
|
|
|
|
|
|
prev_page=prev_page,
|
|
|
|
|
|
next_page=next_page,
|
2018-06-06 15:22:48 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@main.route("/platform-admin/clear-cache", methods=["GET", "POST"])
|
2019-02-15 10:27:38 +00:00
|
|
|
|
@user_is_platform_admin
|
|
|
|
|
|
def clear_cache():
|
|
|
|
|
|
# note: `service-{uuid}-templates` cache is cleared for both services and templates.
|
2023-08-25 09:12:23 -07:00
|
|
|
|
CACHE_KEYS = OrderedDict(
|
|
|
|
|
|
[
|
|
|
|
|
|
(
|
|
|
|
|
|
"user",
|
|
|
|
|
|
[
|
|
|
|
|
|
"user-????????-????-????-????-????????????",
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
"service",
|
|
|
|
|
|
[
|
|
|
|
|
|
"has_jobs-????????-????-????-????-????????????",
|
|
|
|
|
|
"service-????????-????-????-????-????????????",
|
|
|
|
|
|
"service-????????-????-????-????-????????????-templates",
|
|
|
|
|
|
"service-????????-????-????-????-????????????-data-retention",
|
|
|
|
|
|
"service-????????-????-????-????-????????????-template-folders",
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
"template",
|
|
|
|
|
|
[
|
|
|
|
|
|
"service-????????-????-????-????-????????????-templates",
|
|
|
|
|
|
"service-????????-????-????-????-????????????-template-????????-????-????-????-????????????-version-*", # noqa
|
|
|
|
|
|
"service-????????-????-????-????-????????????-template-????????-????-????-????-????????????-versions", # noqa
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
"organization",
|
|
|
|
|
|
[
|
|
|
|
|
|
"organizations",
|
|
|
|
|
|
"domains",
|
|
|
|
|
|
"live-service-and-organization-counts",
|
|
|
|
|
|
"organization-????????-????-????-????-????????????-name",
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
]
|
|
|
|
|
|
)
|
2019-02-19 11:35:34 +00:00
|
|
|
|
|
2022-03-15 10:50:18 +00:00
|
|
|
|
form = AdminClearCacheForm()
|
2022-02-21 11:28:15 +00:00
|
|
|
|
|
|
|
|
|
|
form.model_type.choices = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
(key, key.replace("_", " ").title()) for key in CACHE_KEYS
|
2022-02-21 11:28:15 +00:00
|
|
|
|
]
|
2019-02-15 10:27:38 +00:00
|
|
|
|
|
|
|
|
|
|
if form.validate_on_submit():
|
2022-02-21 11:28:15 +00:00
|
|
|
|
group_keys = form.model_type.data
|
|
|
|
|
|
groups = map(CACHE_KEYS.get, group_keys)
|
|
|
|
|
|
patterns = list(itertools.chain(*groups))
|
2019-02-15 10:27:38 +00:00
|
|
|
|
|
2022-09-08 09:07:54 -07:00
|
|
|
|
num_deleted = sum(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
redis_client.delete_by_pattern(pattern) for pattern in patterns
|
2022-09-08 09:07:54 -07:00
|
|
|
|
)
|
2022-02-21 11:28:15 +00:00
|
|
|
|
|
|
|
|
|
|
msg = (
|
2023-08-25 09:12:23 -07:00
|
|
|
|
f"Removed {num_deleted} objects "
|
|
|
|
|
|
f"across {len(patterns)} key formats "
|
2022-02-21 11:28:15 +00:00
|
|
|
|
f'for {", ".join(group_keys)}'
|
2019-02-15 10:27:38 +00:00
|
|
|
|
)
|
2022-02-21 10:37:51 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
flash(msg, category="default")
|
2019-02-15 10:27:38 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
return render_template("views/platform-admin/clear-cache.html", form=form)
|
2019-02-15 10:27:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
2017-04-25 12:54:58 +01:00
|
|
|
|
def sum_service_usage(service):
|
|
|
|
|
|
total = 0
|
2023-08-25 09:12:23 -07:00
|
|
|
|
for notification_type in service["statistics"].keys():
|
|
|
|
|
|
total += service["statistics"][notification_type]["requested"]
|
2017-04-25 12:54:58 +01:00
|
|
|
|
return total
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-21 09:14:29 +01:00
|
|
|
|
def filter_and_sort_services(services, trial_mode_services=False):
|
2017-07-31 11:29:04 +01:00
|
|
|
|
return [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service
|
|
|
|
|
|
for service in sorted(
|
2017-07-21 09:14:29 +01:00
|
|
|
|
services,
|
|
|
|
|
|
key=lambda service: (
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service["active"],
|
2017-07-21 09:14:29 +01:00
|
|
|
|
sum_service_usage(service),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service["created_at"],
|
2017-07-21 09:14:29 +01:00
|
|
|
|
),
|
|
|
|
|
|
reverse=True,
|
|
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
if service["restricted"] == trial_mode_services
|
2017-07-31 11:29:04 +01:00
|
|
|
|
]
|
2017-07-21 09:14:29 +01:00
|
|
|
|
|
|
|
|
|
|
|
2016-08-23 17:38:27 +01:00
|
|
|
|
def create_global_stats(services):
|
|
|
|
|
|
stats = {
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"email": {"delivered": 0, "failed": 0, "requested": 0},
|
|
|
|
|
|
"sms": {"delivered": 0, "failed": 0, "requested": 0},
|
2016-08-23 17:38:27 +01:00
|
|
|
|
}
|
2024-04-10 07:45:10 -07:00
|
|
|
|
# Issue #1323. The back end is now sending 'failure' instead of
|
|
|
|
|
|
# 'failed'. Adjust it here, but keep it flexible in case
|
|
|
|
|
|
# the backend reverts to 'failed'.
|
2016-08-23 17:38:27 +01:00
|
|
|
|
for service in services:
|
2024-04-10 07:45:10 -07:00
|
|
|
|
if service["statistics"]["sms"].get("failure") is not None:
|
|
|
|
|
|
service["statistics"]["sms"]["failed"] = service["statistics"]["sms"][
|
|
|
|
|
|
"failure"
|
|
|
|
|
|
]
|
|
|
|
|
|
if service["statistics"]["email"].get("failure") is not None:
|
|
|
|
|
|
service["statistics"]["email"]["failed"] = service["statistics"]["email"][
|
|
|
|
|
|
"failure"
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
for service in services:
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
for msg_type, status in itertools.product(
|
|
|
|
|
|
("sms", "email"), ("delivered", "failed", "requested")
|
|
|
|
|
|
):
|
|
|
|
|
|
stats[msg_type][status] += service["statistics"][msg_type][status]
|
2016-08-23 17:38:27 +01:00
|
|
|
|
|
|
|
|
|
|
for stat in stats.values():
|
2023-08-25 09:12:23 -07:00
|
|
|
|
stat["failure_rate"] = get_formatted_percentage(
|
|
|
|
|
|
stat["failed"], stat["requested"]
|
|
|
|
|
|
)
|
2016-08-23 17:38:27 +01:00
|
|
|
|
return stats
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def format_stats_by_service(services):
|
|
|
|
|
|
for service in services:
|
|
|
|
|
|
yield {
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"id": service["id"],
|
|
|
|
|
|
"name": service["name"],
|
|
|
|
|
|
"stats": service["statistics"],
|
|
|
|
|
|
"restricted": service["restricted"],
|
|
|
|
|
|
"created_at": service["created_at"],
|
|
|
|
|
|
"active": service["active"],
|
2016-05-31 11:08:22 +01:00
|
|
|
|
}
|
2023-07-26 09:08:55 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _get_user_row(r):
|
|
|
|
|
|
# [{
|
|
|
|
|
|
# 'name': 'Kenneth Kehl',
|
|
|
|
|
|
# 'organizations': [],
|
|
|
|
|
|
# 'password_changed_at': '2023-07-21 14:12:54.832850', 'permissions': {
|
|
|
|
|
|
# '672b8a66-e22e-40f6-b1e5-39cc1c6bf857': ['manage_users', 'manage_templates', 'manage_settings', 'send_texts',
|
|
|
|
|
|
# 'send_emails', 'manage_api_keys', 'view_activity']},
|
|
|
|
|
|
# 'platform_admin': True, 'services': ['672b8a66-e22e-40f6-b1e5-39cc1c6bf857'], 'state': 'active'}]
|
|
|
|
|
|
|
|
|
|
|
|
row = []
|
2023-08-25 09:12:23 -07:00
|
|
|
|
row.append(r["name"])
|
2023-07-26 09:08:55 -07:00
|
|
|
|
|
|
|
|
|
|
service_id_name_lookup = {}
|
|
|
|
|
|
services = []
|
2023-08-25 09:12:23 -07:00
|
|
|
|
for s in r["services"]:
|
2023-07-26 09:08:55 -07:00
|
|
|
|
my_service = service_api_client.get_service(s)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service_id_name_lookup[my_service["data"]["id"]] = my_service["data"]["name"]
|
|
|
|
|
|
services.append(my_service["data"]["name"])
|
2023-07-26 09:08:55 -07:00
|
|
|
|
services = str(services)
|
|
|
|
|
|
services = services.replace("[", "")
|
|
|
|
|
|
services = services.replace("]", "")
|
|
|
|
|
|
row.append(services)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
row.append(r["platform_admin"])
|
|
|
|
|
|
permissions = r["permissions"]
|
2023-07-26 09:08:55 -07:00
|
|
|
|
for k, v in service_id_name_lookup.items():
|
|
|
|
|
|
if permissions.get(k):
|
|
|
|
|
|
permissions[v] = permissions[k]
|
|
|
|
|
|
del permissions[k]
|
|
|
|
|
|
|
|
|
|
|
|
permissions = json.dumps(permissions, indent=4)
|
|
|
|
|
|
row.append(permissions)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
row.append(r["password_changed_at"])
|
|
|
|
|
|
row.append(r["state"])
|
2023-07-26 09:08:55 -07:00
|
|
|
|
return row
|
2024-08-30 09:50:01 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@main.route(
|
|
|
|
|
|
"/platform-admin/load-test",
|
|
|
|
|
|
methods=["POST", "GET"],
|
|
|
|
|
|
)
|
|
|
|
|
|
@user_is_platform_admin
|
|
|
|
|
|
def load_test():
|
2024-09-02 07:51:57 -07:00
|
|
|
|
"""
|
|
|
|
|
|
The load test assumes that a service called 'Test service' exists. It will make
|
|
|
|
|
|
the platform admin a member of this service if the platform is not already. All
|
|
|
|
|
|
messagese will be sent in this service.
|
|
|
|
|
|
"""
|
|
|
|
|
|
service = _find_load_test_service()
|
|
|
|
|
|
_prepare_load_test_service(service)
|
|
|
|
|
|
example_template = _find_example_template(service)
|
|
|
|
|
|
|
2024-09-03 10:37:05 -07:00
|
|
|
|
# Simulated success
|
|
|
|
|
|
for _ in range(0, 250):
|
|
|
|
|
|
session["recipient"] = current_app.config["SIMULATED_SMS_NUMBERS"][0]
|
2024-09-02 07:51:57 -07:00
|
|
|
|
session["placeholders"] = {
|
|
|
|
|
|
"day of week": "Monday",
|
|
|
|
|
|
"color": "blue",
|
2024-09-03 10:37:05 -07:00
|
|
|
|
"phone number": current_app.config["SIMULATED_SMS_NUMBERS"][0],
|
2024-09-02 07:51:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
_send_notification(service["id"], example_template["id"])
|
2024-09-03 10:37:05 -07:00
|
|
|
|
# Simulated failure
|
|
|
|
|
|
for _ in range(0, 250):
|
|
|
|
|
|
session["recipient"] = current_app.config["SIMULATED_SMS_NUMBERS"][1]
|
2024-09-02 07:51:57 -07:00
|
|
|
|
session["placeholders"] = {
|
2024-09-03 10:37:05 -07:00
|
|
|
|
"day of week": "Wednesday",
|
|
|
|
|
|
"color": "orange",
|
|
|
|
|
|
"phone number": current_app.config["SIMULATED_SMS_NUMBERS"][1],
|
2024-09-02 07:51:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
_send_notification(service["id"], example_template["id"])
|
2024-08-30 09:50:01 -07:00
|
|
|
|
|
2024-09-03 14:01:17 -07:00
|
|
|
|
# For now, just redirect to the splash page so we know it's done
|
|
|
|
|
|
return render_template(
|
|
|
|
|
|
"views/platform-admin/splash-page.html",
|
|
|
|
|
|
)
|
2024-09-02 07:51:57 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _find_example_template(service):
|
2024-08-30 09:50:01 -07:00
|
|
|
|
templates = service_api_client.get_service_templates(service["id"])
|
|
|
|
|
|
templates = templates["data"]
|
|
|
|
|
|
for template in templates:
|
|
|
|
|
|
# template = json.loads(template)
|
|
|
|
|
|
if template["name"] == "Example text message template":
|
2024-09-02 07:51:57 -07:00
|
|
|
|
return template
|
|
|
|
|
|
|
|
|
|
|
|
raise Exception("Could not find example template for load test")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _find_load_test_service():
|
|
|
|
|
|
services = service_api_client.find_services_by_name("Test service")
|
|
|
|
|
|
services = services["data"]
|
|
|
|
|
|
|
|
|
|
|
|
for service in services:
|
|
|
|
|
|
if service["name"] == "Test service":
|
|
|
|
|
|
return service
|
|
|
|
|
|
|
|
|
|
|
|
raise Exception("Could not find 'Test service' for load test")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _prepare_load_test_service(service):
|
|
|
|
|
|
users = user_api_client.get_all_users()
|
|
|
|
|
|
for user in users:
|
|
|
|
|
|
if user["platform_admin"] == "t":
|
|
|
|
|
|
try:
|
|
|
|
|
|
user_api_client.add_user_to_service(
|
|
|
|
|
|
service["id"], user["id"], ["send messages"]
|
|
|
|
|
|
)
|
2024-09-02 08:13:05 -07:00
|
|
|
|
except Exception:
|
|
|
|
|
|
current_app.logger.exception(
|
2024-09-02 08:22:03 -07:00
|
|
|
|
"Couldnt add user, may already be part of service"
|
2024-09-02 07:51:57 -07:00
|
|
|
|
)
|
|
|
|
|
|
pass
|