mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-05 15:08:25 -04:00
Merge branch 'main' of https://github.com/GSA/notifications-admin into 1674-match-recent-batches-to-wireframe
This commit is contained in:
@@ -1,9 +1,11 @@
|
|||||||
|
import csv
|
||||||
import itertools
|
import itertools
|
||||||
import json
|
import json
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
from flask import abort, flash, render_template, request, url_for
|
from flask import Response, abort, flash, render_template, request, url_for
|
||||||
from notifications_python_client.errors import HTTPError
|
from notifications_python_client.errors import HTTPError
|
||||||
|
|
||||||
from app import (
|
from app import (
|
||||||
@@ -70,6 +72,40 @@ def platform_admin():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@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."
|
||||||
|
|
||||||
|
output = StringIO()
|
||||||
|
header = ["Name", "Email Address", "Phone Number", "Service"]
|
||||||
|
fieldnames = ["name", "email_address", "mobile_number", "service"]
|
||||||
|
writer = csv.DictWriter(
|
||||||
|
output,
|
||||||
|
fieldnames=fieldnames,
|
||||||
|
delimiter=",",
|
||||||
|
)
|
||||||
|
# Write custom header
|
||||||
|
writer.writerow(dict(zip(fieldnames, header)))
|
||||||
|
for user in users:
|
||||||
|
user_no_commas = {key: value.replace(",", "") for key, value in user.items()}
|
||||||
|
if user_no_commas["name"].startswith("e2e"):
|
||||||
|
continue
|
||||||
|
writer.writerow(user_no_commas)
|
||||||
|
csv_data = output.getvalue()
|
||||||
|
|
||||||
|
# Create a direct download response with the CSV data and appropriate headers
|
||||||
|
response = Response(csv_data, content_type="text/csv; charset=utf-8")
|
||||||
|
response.headers["Content-Disposition"] = "attachment; filename=users.csv"
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
def is_over_threshold(number, total, threshold):
|
def is_over_threshold(number, total, threshold):
|
||||||
percentage = number / total * 100 if total else 0
|
percentage = number / total * 100 if total else 0
|
||||||
return percentage > threshold
|
return percentage > threshold
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ class HeaderNavigation(Navigation):
|
|||||||
"get_billing_report",
|
"get_billing_report",
|
||||||
"get_users_report",
|
"get_users_report",
|
||||||
"get_daily_volumes",
|
"get_daily_volumes",
|
||||||
|
"download_all_users",
|
||||||
"get_daily_sms_provider_volumes",
|
"get_daily_sms_provider_volumes",
|
||||||
"get_volumes_by_service",
|
"get_volumes_by_service",
|
||||||
"organizations",
|
"organizations",
|
||||||
|
|||||||
@@ -157,6 +157,10 @@ class UserApiClient(NotifyAdminAPIClient):
|
|||||||
endpoint = "/user"
|
endpoint = "/user"
|
||||||
return self.get(endpoint)["data"]
|
return self.get(endpoint)["data"]
|
||||||
|
|
||||||
|
def get_all_users_detailed(self):
|
||||||
|
endpoint = "/user/report-all-users"
|
||||||
|
return self.get(endpoint)["data"]
|
||||||
|
|
||||||
@cache.delete("service-{service_id}")
|
@cache.delete("service-{service_id}")
|
||||||
@cache.delete("service-{service_id}-template-folders")
|
@cache.delete("service-{service_id}-template-folders")
|
||||||
@cache.delete("user-{user_id}")
|
@cache.delete("user-{user_id}")
|
||||||
|
|||||||
@@ -31,4 +31,8 @@
|
|||||||
<p>
|
<p>
|
||||||
<a class="usa-link" href="{{ url_for('main.get_users_report') }}">Users Report</a>
|
<a class="usa-link" href="{{ url_for('main.get_users_report') }}">Users Report</a>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<a class="usa-link" href="{{ url_for('main.download_all_users') }}">Download All Users</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -99,10 +99,11 @@ const copyGtmHead = () => {
|
|||||||
|
|
||||||
// Task to copy images
|
// Task to copy images
|
||||||
const copyImages = () => {
|
const copyImages = () => {
|
||||||
return src(paths.src + 'images/**/*')
|
return src(paths.src + 'images/**/*', { encoding: false })
|
||||||
.pipe(dest(paths.dist + 'images/'));
|
.pipe(dest(paths.dist + 'images/'));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Configure USWDS paths
|
// Configure USWDS paths
|
||||||
uswds.settings.version = 3;
|
uswds.settings.version = 3;
|
||||||
uswds.paths.dist.css = paths.dist + 'css';
|
uswds.paths.dist.css = paths.dist + 'css';
|
||||||
|
|||||||
@@ -1254,3 +1254,36 @@ def test_get_daily_sms_provider_volumes_report_calls_api_and_download_data(
|
|||||||
+ "80"
|
+ "80"
|
||||||
+ "\r\n"
|
+ "\r\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_download_all_users(client_request, platform_admin_user, mocker):
|
||||||
|
mocker.patch(
|
||||||
|
"app.main.views.platform_admin.user_api_client.get_all_users_detailed",
|
||||||
|
return_value=[
|
||||||
|
{
|
||||||
|
"name": "Johnny Sokko",
|
||||||
|
"email_address": "j_sokko@unicorn.gov",
|
||||||
|
"mobile_number": "15555555555",
|
||||||
|
"service": "Emperor, Guillotine, Service",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
client_request.login(platform_admin_user)
|
||||||
|
response = client_request.get_response(
|
||||||
|
"main.download_all_users",
|
||||||
|
_data={},
|
||||||
|
_expected_status=200,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.content_type == "text/csv; charset=utf-8"
|
||||||
|
assert "attachment" in response.headers["Content-Disposition"]
|
||||||
|
assert "filename" in response.headers["Content-Disposition"]
|
||||||
|
assert "users" in response.headers["Content-Disposition"]
|
||||||
|
|
||||||
|
my_response = response.get_data(as_text=True)
|
||||||
|
|
||||||
|
assert "Johnny Sokko" in my_response
|
||||||
|
assert "Emperor Guillotine Service" in my_response
|
||||||
|
assert "j_sokko@unicorn.gov" in my_response
|
||||||
|
assert "15555555555" in my_response
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ EXCLUDED_ENDPOINTS = tuple(
|
|||||||
"delivery_status_callback",
|
"delivery_status_callback",
|
||||||
"design_content",
|
"design_content",
|
||||||
"documentation",
|
"documentation",
|
||||||
|
"download_all_users",
|
||||||
"download_notifications_csv",
|
"download_notifications_csv",
|
||||||
"download_organization_usage_report",
|
"download_organization_usage_report",
|
||||||
"edit_and_format_messages",
|
"edit_and_format_messages",
|
||||||
|
|||||||
Reference in New Issue
Block a user