mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-03 13:18:57 -04:00
Fixed disable button, formated time to include time zone (#2850)
This commit is contained in:
@@ -1726,8 +1726,12 @@ class TemplateFolderForm(StripWhitespaceForm):
|
||||
def __init__(self, all_service_users=None, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if all_service_users is not None:
|
||||
regular_users = [user for user in all_service_users if not user.platform_admin]
|
||||
platform_admins = [user for user in all_service_users if user.platform_admin]
|
||||
regular_users = [
|
||||
user for user in all_service_users if not user.platform_admin
|
||||
]
|
||||
platform_admins = [
|
||||
user for user in all_service_users if user.platform_admin
|
||||
]
|
||||
|
||||
self.users_with_permission.all_service_users = regular_users
|
||||
self.users_with_permission.choices = [
|
||||
|
||||
@@ -34,14 +34,16 @@ def get_report_info(service_id, report_name, s3_config):
|
||||
# check_s3_file_exists already called obj.load(), so metadata should be populated
|
||||
size_bytes = obj.content_length
|
||||
|
||||
if size_bytes < 1024:
|
||||
size_str = f"{size_bytes} B"
|
||||
elif size_bytes < 1024 * 1024:
|
||||
size_str = f"{size_bytes / 1024:.1f} KB"
|
||||
else:
|
||||
size_str = f"{size_bytes / (1024 * 1024):.1f} MB"
|
||||
# Only show as available if file has any content (not empty)
|
||||
if size_bytes > 0:
|
||||
if size_bytes < 1024:
|
||||
size_str = f"{size_bytes} B"
|
||||
elif size_bytes < 1024 * 1024:
|
||||
size_str = f"{size_bytes / 1024:.1f} KB"
|
||||
else:
|
||||
size_str = f"{size_bytes / (1024 * 1024):.1f} MB"
|
||||
|
||||
return {"available": True, "size": size_str}
|
||||
return {"available": True, "size": size_str}
|
||||
except Exception: # nosec B110
|
||||
pass
|
||||
|
||||
|
||||
@@ -174,7 +174,11 @@ def download_notifications_csv(service_id):
|
||||
)
|
||||
s3_file_content = s3download(service_id, s3_report_id)
|
||||
return Response(
|
||||
stream_with_context(convert_s3_csv_timestamps(s3_file_content)),
|
||||
stream_with_context(
|
||||
convert_s3_csv_timestamps(
|
||||
s3_file_content, user_timezone=user_tz_name
|
||||
)
|
||||
),
|
||||
mimetype="text/csv",
|
||||
headers={
|
||||
"Content-Disposition": 'inline; filename="{} - {} - {} report.csv"'.format(
|
||||
@@ -185,7 +189,6 @@ def download_notifications_csv(service_id):
|
||||
},
|
||||
)
|
||||
except S3ObjectNotFound:
|
||||
# Edge case: File was deleted between page load and download attempt
|
||||
current_app.logger.warning(
|
||||
f"File {s3_report_id} was expected but not found for service {service_id}. "
|
||||
"It may have been deleted after page load."
|
||||
|
||||
@@ -183,7 +183,7 @@ def process_folder_management_form(form, current_folder_id):
|
||||
current_service.id,
|
||||
name=form.get_folder_name(),
|
||||
parent_id=current_folder_id,
|
||||
created_by_id=str(current_user.id)
|
||||
created_by_id=str(current_user.id),
|
||||
)
|
||||
|
||||
if form.is_move_op:
|
||||
|
||||
@@ -4,7 +4,9 @@ from app.notify_client import NotifyAdminAPIClient, cache
|
||||
|
||||
class TemplateFolderAPIClient(NotifyAdminAPIClient):
|
||||
@cache.delete("service-{service_id}-template-folders")
|
||||
def create_template_folder(self, service_id, name, parent_id=None, created_by_id=None):
|
||||
def create_template_folder(
|
||||
self, service_id, name, parent_id=None, created_by_id=None
|
||||
):
|
||||
data = {"name": name, "parent_id": parent_id}
|
||||
if created_by_id:
|
||||
data["created_by_id"] = created_by_id
|
||||
|
||||
@@ -203,7 +203,7 @@
|
||||
<button class="usa-button width-full"
|
||||
disabled
|
||||
aria-label="Yesterday's report not available - no messages were sent">
|
||||
Yesterday - No messages sent
|
||||
Yesterday - No messages sent
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -179,36 +179,66 @@ def generate_notifications_csv(**kwargs):
|
||||
raise Exception("Should never reach here")
|
||||
|
||||
|
||||
def convert_report_date_to_preferred_timezone(db_date_str_in_utc):
|
||||
def convert_report_date_to_preferred_timezone(db_date_str_in_utc, target_timezone=None):
|
||||
"""
|
||||
Report dates in the db are in UTC. We need to convert them to the user's default timezone,
|
||||
which defaults to "US/Eastern"
|
||||
Format: "2025-05-14 07:22:18 AM America/Los_Angeles"
|
||||
|
||||
Args:
|
||||
db_date_str_in_utc: UTC datetime string in format "YYYY-MM-DD HH:MM:SS"
|
||||
target_timezone: Optional timezone name to use (e.g. "US/Eastern", "America/Los_Angeles")
|
||||
If not provided, will use current user's preference or default
|
||||
"""
|
||||
date_arr = db_date_str_in_utc.split(" ")
|
||||
db_date_str_in_utc = f"{date_arr[0]}T{date_arr[1]}+00:00"
|
||||
utc_date_obj = datetime.datetime.fromisoformat(db_date_str_in_utc)
|
||||
try:
|
||||
date_arr = db_date_str_in_utc.split(" ")
|
||||
db_date_str_in_utc = f"{date_arr[0]}T{date_arr[1]}+00:00"
|
||||
utc_date_obj = datetime.datetime.fromisoformat(db_date_str_in_utc)
|
||||
|
||||
utc_date_obj = utc_date_obj.replace(tzinfo=ZoneInfo("UTC"))
|
||||
preferred_timezone = get_user_preferred_timezone_obj()
|
||||
preferred_date_obj = utc_date_obj.astimezone(preferred_timezone)
|
||||
preferred_tz_created_at = preferred_date_obj.strftime("%Y-%m-%d %H:%M:%S")
|
||||
utc_date_obj = utc_date_obj.replace(tzinfo=ZoneInfo("UTC"))
|
||||
|
||||
return preferred_tz_created_at
|
||||
if not target_timezone:
|
||||
target_timezone = get_user_preferred_timezone()
|
||||
|
||||
preferred_timezone = ZoneInfo(target_timezone)
|
||||
preferred_date_obj = utc_date_obj.astimezone(preferred_timezone)
|
||||
|
||||
formatted_date = preferred_date_obj.strftime("%Y-%m-%d %I:%M:%S %p")
|
||||
preferred_tz_created_at = f"{formatted_date} {target_timezone}"
|
||||
return preferred_tz_created_at
|
||||
|
||||
except Exception as e:
|
||||
try:
|
||||
current_app.logger.error(
|
||||
f"Error converting timezone for {db_date_str_in_utc}: {e}"
|
||||
)
|
||||
except RuntimeError:
|
||||
pass
|
||||
return f"{db_date_str_in_utc} UTC"
|
||||
|
||||
|
||||
_timezone_cache = {}
|
||||
|
||||
|
||||
def get_user_preferred_timezone():
|
||||
if current_user and hasattr(current_user, "preferred_timezone"):
|
||||
tz = current_user.preferred_timezone
|
||||
# Validate timezone using ZoneInfo - it will raise if invalid
|
||||
try:
|
||||
ZoneInfo(tz)
|
||||
return tz
|
||||
except ZoneInfoNotFoundError:
|
||||
# Invalid timezone, fall back to default
|
||||
pass
|
||||
try:
|
||||
if (
|
||||
current_user
|
||||
and current_user.is_authenticated
|
||||
and hasattr(current_user, "preferred_timezone")
|
||||
):
|
||||
tz = current_user.preferred_timezone
|
||||
if tz:
|
||||
try:
|
||||
ZoneInfo(tz)
|
||||
return tz
|
||||
except ZoneInfoNotFoundError:
|
||||
pass
|
||||
except (AttributeError, RuntimeError):
|
||||
# AttributeError: current_user might not have expected attributes
|
||||
# RuntimeError: working outside of request context
|
||||
pass
|
||||
|
||||
return "US/Eastern"
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
import csv
|
||||
import io
|
||||
|
||||
from app.utils.csv import convert_report_date_to_preferred_timezone
|
||||
from app.utils.csv import (
|
||||
convert_report_date_to_preferred_timezone,
|
||||
get_user_preferred_timezone,
|
||||
)
|
||||
|
||||
|
||||
def convert_s3_csv_timestamps(csv_content):
|
||||
def convert_s3_csv_timestamps(csv_content, user_timezone=None):
|
||||
"""
|
||||
Convert UTC timestamps in CSV to user's preferred timezone.
|
||||
|
||||
Args:
|
||||
csv_content: The CSV content as string or bytes
|
||||
user_timezone: Optional pre-captured timezone (to avoid context issues in streaming)
|
||||
"""
|
||||
if isinstance(csv_content, bytes):
|
||||
csv_content = csv_content.decode("utf-8")
|
||||
|
||||
if user_timezone is None:
|
||||
user_timezone = get_user_preferred_timezone()
|
||||
|
||||
reader = csv.reader(io.StringIO(csv_content))
|
||||
|
||||
time_column_index = None
|
||||
@@ -39,7 +52,7 @@ def convert_s3_csv_timestamps(csv_content):
|
||||
if len(row) > time_column_index and row[time_column_index]:
|
||||
try:
|
||||
row[time_column_index] = convert_report_date_to_preferred_timezone(
|
||||
row[time_column_index]
|
||||
row[time_column_index], target_timezone=user_timezone
|
||||
)
|
||||
except Exception: # nosec B110
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user