+ Was sent on {{ job.created_at|format_datetime_short_12h }} by {{ job.created_by.name }}
+
+
+
{% endif %}
{% if job.status == 'sending limits exceeded'%}
diff --git a/app/templates/views/notifications/notification.html b/app/templates/views/notifications/notification.html
index cf61250ed..9c921729d 100644
--- a/app/templates/views/notifications/notification.html
+++ b/app/templates/views/notifications/notification.html
@@ -24,18 +24,16 @@
{% if help %}
‘{{ template.name }}’
{% else %}
- ‘{{ template.name }}’
+
{% endif %}
- was sent
- {% if job and job.original_file_name != 'Report' %}
- {% set destination =
- {'email': 'an email address', 'sms': 'a phone number'} %}
- to {{ destination[template.template_type] }} from
- {{ job.original_file_name }}
- {% elif created_by %}
- by {{ created_by.name }}
- {% endif %}
- {{ created_at|format_datetime_human }}
+
+
+
{{ template.name }} - {{ current_service.name }}
+
+ Was sent on {{ created_at|format_datetime_short_12h }} by {{ created_by.name }}
+
+
+
diff --git a/tests/app/main/views/test_jobs.py b/tests/app/main/views/test_jobs.py
index 323a1fe00..74fd6371a 100644
--- a/tests/app/main/views/test_jobs.py
+++ b/tests/app/main/views/test_jobs.py
@@ -237,11 +237,11 @@ def test_should_show_job_with_sending_limit_exceeded_status(
job_id=fake_uuid,
)
- assert normalize_spaces(page.select("main p")[1].text) == (
+ assert normalize_spaces(page.select("main p")[2].text) == (
"Notify cannot send these messages because you have reached a limit. "
"You can only send 1,000 messages per day and 250,000 messages in total."
)
- assert normalize_spaces(page.select("main p")[2].text) == (
+ assert normalize_spaces(page.select("main p")[3].text) == (
"Upload this spreadsheet again tomorrow or contact the Notify.gov team to raise the limit."
)
@@ -350,7 +350,7 @@ def test_should_show_scheduled_job(
)
assert normalize_spaces(page.select("main div p")[1].text) == (
- "Sending Two week reminder today at 00:00 US/Eastern"
+ "Was scheduled on 2 January at 12:00 AM US/Eastern by Test User"
)
assert page.select("main p a")[0]["href"] == url_for(
@@ -425,7 +425,9 @@ def test_should_show_updates_for_one_job_as_json(
assert "Status" in content["notifications"]
assert "Delivered" in content["notifications"]
assert "00:00" in content["notifications"]
- assert "Sent by Test User on 1 January at 00:00" in content["status"]
+ assert (
+ "Was sent on 1 January at 12:00 AM US/Eastern by Test User" in content["status"]
+ )
@freeze_time("2016-01-01 05:00:00.000001")
@@ -467,7 +469,9 @@ def test_should_show_updates_for_scheduled_job_as_json(
assert "Status" in content["notifications"]
assert "Delivered" in content["notifications"]
assert "00:00" in content["notifications"]
- assert "Sent by Test User on 1 June at 16:00" in content["status"]
+ assert (
+ "Was sent on 1 January at 12:00 AM US/Eastern by Test User" in content["status"]
+ )
@pytest.mark.parametrize(
diff --git a/tests/app/main/views/test_notifications.py b/tests/app/main/views/test_notifications.py
index 7020cd3e0..61a7f495d 100644
--- a/tests/app/main/views/test_notifications.py
+++ b/tests/app/main/views/test_notifications.py
@@ -226,22 +226,30 @@ def test_notification_status_shows_expected_back_link(
[
(
"2012-01-01 06:01",
- ("‘sample template’ was sent by Test User today at 01:01 US/Eastern"),
+ (
+ "‘sample template’ sample template - service one Was sent on 1 January at 01:01 AM US/Eastern "
+ "by Test User"
+ ),
),
(
"2012-01-02 06:01",
- ("‘sample template’ was sent by Test User yesterday at 01:01 US/Eastern"),
+ (
+ "‘sample template’ sample template - service one Was sent on 1 January at 01:01 AM US/Eastern "
+ "by Test User"
+ ),
),
(
"2012-01-03 06:01",
(
- "‘sample template’ was sent by Test User on 1 January at 01:01 US/Eastern"
+ "‘sample template’ sample template - service one Was sent on 1 January at 01:01 AM US/Eastern "
+ "by Test User"
),
),
(
"2013-01-03 06:01",
(
- "‘sample template’ was sent by Test User on 1 January 2012 at 01:01 US/Eastern"
+ "‘sample template’ sample template - service one Was sent on 1 January at 01:01 AM US/Eastern "
+ "by Test User"
),
),
],
From 4eb886d8c026d780bc8dd241fe256b694bdbfe5d Mon Sep 17 00:00:00 2001
From: Beverly Nguyen
Date: Tue, 13 Feb 2024 18:18:13 -0800
Subject: [PATCH 02/55] update test
---
.../views/notifications/notification.html | 28 ++++++++-----------
tests/app/main/views/test_notifications.py | 18 ++++--------
2 files changed, 17 insertions(+), 29 deletions(-)
diff --git a/app/templates/views/notifications/notification.html b/app/templates/views/notifications/notification.html
index 9c921729d..5a03daf3d 100644
--- a/app/templates/views/notifications/notification.html
+++ b/app/templates/views/notifications/notification.html
@@ -20,26 +20,20 @@
{{ page_header(
1|message_count_label(template.template_type, suffix='') | capitalize
) }}
-
- {% if help %}
- ‘{{ template.name }}’
- {% else %}
-
- {% endif %}
-
-
-
{{ template.name }} - {{ current_service.name }}
-
- Was sent on {{ created_at|format_datetime_short_12h }} by {{ created_by.name }}
-
-
+ {% if help %}
+
‘{{ template.name }}’
+ {% endif %}
+
+
+
{{ template.name }} - {{ current_service.name }}
+
+ Was sent on {{ created_at|format_datetime_short_12h }} by {{ created_by.name }}
+
- Was scheduled on {{ job.scheduled_for|format_datetime_short_12h }} by {{ job.created_by.name }}
+ {{ job.template_name }} - {{ current_service.name }} was scheduled on {{ job.scheduled_for|format_datetime_short_12h }} by {{ job.created_by.name }}
- Was sent on {{ created_at|format_datetime_short_12h }} by {{ created_by.name }}
+ was sent
+ {% if job and job.original_file_name != 'Report' %}
+ {% set destination =
+ {'email': 'an email address', 'sms': 'a phone number'} %}
+ to {{ destination[template.template_type] }} from
+ {{ job.original_file_name }}
+ {% elif created_by %}
+ by {{ created_by.name }}
+ {% endif %}
+ {{ created_at|format_datetime_human }}
-
-
- {{ template|string }}
+
+ {{ template|string }}
+
{% if template.template_type == 'email' %}
diff --git a/tests/app/main/views/test_send.py b/tests/app/main/views/test_send.py
index 7448c44cf..c1714c668 100644
--- a/tests/app/main/views/test_send.py
+++ b/tests/app/main/views/test_send.py
@@ -2056,7 +2056,7 @@ def test_create_job_should_call_api(
_expected_status=200,
)
- assert original_file_name in page.text
+ assert "Message status" in page.text
mock_create_job.assert_called_with(
job_id,
From fd340ed1051e6c5e1500b54748489aac036419fd Mon Sep 17 00:00:00 2001
From: Beverly Nguyen
Date: Fri, 23 Feb 2024 14:59:25 -0800
Subject: [PATCH 08/55] update test
---
tests/app/main/views/test_notifications.py | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)
diff --git a/tests/app/main/views/test_notifications.py b/tests/app/main/views/test_notifications.py
index ce7bcab63..2d1ac37b4 100644
--- a/tests/app/main/views/test_notifications.py
+++ b/tests/app/main/views/test_notifications.py
@@ -226,26 +226,22 @@ def test_notification_status_shows_expected_back_link(
[
(
"2012-01-01 06:01",
- (
- "Was sent on 1 January at 01:01 AM US/Eastern by Test User"
- ),
+ ("‘sample template’ was sent by Test User today at 01:01 US/Eastern"),
),
(
"2012-01-02 06:01",
- (
- "Was sent on 1 January at 01:01 AM US/Eastern by Test User"
- ),
+ ("‘sample template’ was sent by Test User yesterday at 01:01 US/Eastern"),
),
(
"2012-01-03 06:01",
(
- "Was sent on 1 January at 01:01 AM US/Eastern by Test User"
+ "‘sample template’ was sent by Test User on 1 January at 01:01 US/Eastern"
),
),
(
"2013-01-03 06:01",
(
- "Was sent on 1 January at 01:01 AM US/Eastern by Test User"
+ "‘sample template’ was sent by Test User on 1 January 2012 at 01:01 US/Eastern"
),
),
],
@@ -271,8 +267,6 @@ def test_notification_page_doesnt_link_to_template_in_tour(
notification_id=fake_uuid,
help=3,
)
- p_text = normalize_spaces(page.select_one(".usa-alert__text").text)
- assert p_text == normalize_spaces(expected_message)
assert len(page.select("main p:nth-of-type(1) a")) == 0
From d08968a34815bb6ae5f8bbea4dbad02a93caae01 Mon Sep 17 00:00:00 2001
From: Beverly Nguyen
Date: Fri, 23 Feb 2024 15:02:18 -0800
Subject: [PATCH 09/55] update test
---
app/main/views/send.py | 28 ++++++++-----------
.../views/notifications/notification.html | 12 ++++----
2 files changed, 18 insertions(+), 22 deletions(-)
diff --git a/app/main/views/send.py b/app/main/views/send.py
index 10f74bb41..d45c1909f 100644
--- a/app/main/views/send.py
+++ b/app/main/views/send.py
@@ -52,14 +52,12 @@ def get_example_csv_fields(column_headers, use_example_as_example, submitted_fie
def get_example_csv_rows(template, use_example_as_example=True, submitted_fields=False):
return {
- "email": (
- ["test@example.com"]
- if use_example_as_example
- else [current_user.email_address]
- ),
- "sms": (
- ["12223334444"] if use_example_as_example else [current_user.mobile_number]
- ),
+ "email": ["test@example.com"]
+ if use_example_as_example
+ else [current_user.email_address],
+ "sms": ["12223334444"]
+ if use_example_as_example
+ else [current_user.mobile_number],
}[template.template_type] + get_example_csv_fields(
(
placeholder
@@ -513,14 +511,12 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
template=template,
max_initial_rows_shown=50,
max_errors_shown=50,
- guestlist=(
- itertools.chain.from_iterable(
- [user.name, user.mobile_number, user.email_address]
- for user in Users(service_id)
- )
- if current_service.trial_mode
- else None
- ),
+ guestlist=itertools.chain.from_iterable(
+ [user.name, user.mobile_number, user.email_address]
+ for user in Users(service_id)
+ )
+ if current_service.trial_mode
+ else None,
remaining_messages=remaining_messages,
allow_international_sms=current_service.has_permission("international_sms"),
)
diff --git a/app/templates/views/notifications/notification.html b/app/templates/views/notifications/notification.html
index e5a41d422..cf61250ed 100644
--- a/app/templates/views/notifications/notification.html
+++ b/app/templates/views/notifications/notification.html
@@ -20,13 +20,13 @@
{{ page_header(
1|message_count_label(template.template_type, suffix='') | capitalize
) }}
-
- {% if help %}
- ‘{{ template.name }}’
+
+ {% if help %}
+ ‘{{ template.name }}’
{% else %}
‘{{ template.name }}’
- {% endif %}
- was sent
+ {% endif %}
+ was sent
{% if job and job.original_file_name != 'Report' %}
{% set destination =
{'email': 'an email address', 'sms': 'a phone number'} %}
@@ -36,7 +36,7 @@
by {{ created_by.name }}
{% endif %}
{{ created_at|format_datetime_human }}
-
+
{{ template|string }}
From 039b7cdece8dbe078de3807b3b4a8aa4d42fadd8 Mon Sep 17 00:00:00 2001
From: Beverly Nguyen
Date: Fri, 23 Feb 2024 15:14:54 -0800
Subject: [PATCH 10/55] update test
---
tests/app/main/views/test_performance.py | 28 ++++++++++++------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/tests/app/main/views/test_performance.py b/tests/app/main/views/test_performance.py
index ef2ed06be..5ceb5288b 100644
--- a/tests/app/main/views/test_performance.py
+++ b/tests/app/main/views/test_performance.py
@@ -116,26 +116,26 @@ def test_should_render_performance_page(
""
"Messages sent since May 2023 "
"Date Emails Text messages "
- "27 February 2021 1 2 "
- "26 February 2021 1 2 "
- "25 February 2021 1 2 "
- "24 February 2021 1 2 "
- "23 February 2021 1 2 "
- "22 February 2021 1 2 "
- "21 February 2021 1,234,567 123,456 "
+ "February 27, 2021 1 2 "
+ "February 26, 2021 1 2 "
+ "February 25, 2021 1 2 "
+ "February 24, 2021 1 2 "
+ "February 23, 2021 1 2 "
+ "February 22, 2021 1 2 "
+ "February 21, 2021 1,234,567 123,456 "
"Only showing the last 7 days "
""
"Messages sent within 10 seconds "
"98.31% on average "
"Messages sent within 10 seconds "
"Date Percentage "
- "27 February 2021 98.60% "
- "26 February 2021 100.00% "
- "25 February 2021 99.99% "
- "24 February 2021 100.00% "
- "23 February 2021 95.00% "
- "22 February 2021 95.30% "
- "21 February 2021 99.25% "
+ "February 27, 2021 98.60% "
+ "February 26, 2021 100.00% "
+ "February 25, 2021 99.99% "
+ "February 24, 2021 100.00% "
+ "February 23, 2021 95.00% "
+ "February 22, 2021 95.30% "
+ "February 21, 2021 99.25% "
"Only showing the last 7 days "
""
"Organizations using Notify "
From 58e252f092b751a645d76c71f14381f2fe39c1a8 Mon Sep 17 00:00:00 2001
From: Beverly Nguyen
Date: Mon, 26 Feb 2024 12:25:53 -0800
Subject: [PATCH 11/55] updated Delivery Status title
---
app/templates/partials/jobs/status.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/templates/partials/jobs/status.html b/app/templates/partials/jobs/status.html
index e01e356a2..858a4ff10 100644
--- a/app/templates/partials/jobs/status.html
+++ b/app/templates/partials/jobs/status.html
@@ -16,7 +16,7 @@
{% endif %}
-
SMS Status
+
Delivery Status
{% endset %}
From ee5fe38001748f13768fcff16ce9687699852cc1 Mon Sep 17 00:00:00 2001
From: Beverly Nguyen
Date: Fri, 8 Mar 2024 11:30:49 -0800
Subject: [PATCH 12/55] update testing
---
app/main/views/send.py | 5 +++++
tests/app/main/views/test_jobs.py | 4 ++--
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/app/main/views/send.py b/app/main/views/send.py
index 10f74bb41..3ca38171b 100644
--- a/app/main/views/send.py
+++ b/app/main/views/send.py
@@ -912,6 +912,11 @@ def send_notification(service_id, template_id):
".view_job",
service_id=service_id,
job_id=upload_id,
+ from_job=upload_id,
+ notification_id=notifications["notifications"][0]["id"],
+ # used to show the final step of the tour (help=3) or not show
+ # a back link on a just sent one off notification (help=0)
+ help=request.args.get("help"),
)
)
diff --git a/tests/app/main/views/test_jobs.py b/tests/app/main/views/test_jobs.py
index c7afce860..9e04cdf21 100644
--- a/tests/app/main/views/test_jobs.py
+++ b/tests/app/main/views/test_jobs.py
@@ -424,7 +424,7 @@ def test_should_show_updates_for_one_job_as_json(
assert "2021234567" in content["notifications"]
assert "Status" in content["notifications"]
assert "Delivered" in content["notifications"]
- assert "00:00" in content["notifications"]
+ assert "01-01-2016 at 12:00 AM" in content["notifications"]
@freeze_time("2016-01-01 05:00:00.000001")
@@ -465,7 +465,7 @@ def test_should_show_updates_for_scheduled_job_as_json(
assert "2021234567" in content["notifications"]
assert "Status" in content["notifications"]
assert "Delivered" in content["notifications"]
- assert "00:00" in content["notifications"]
+ assert "01-01-2016 at 12:00 AM" in content["notifications"]
assert (
"Was sent on 1 January at 12:00 AM US/Eastern by Test User"
)
From b1b416db8c06fd7c1ee5466e49e4fb0523df5396 Mon Sep 17 00:00:00 2001
From: Beverly Nguyen
Date: Fri, 8 Mar 2024 11:43:54 -0800
Subject: [PATCH 13/55] update datetime naming
---
app/__init__.py | 4 ++--
app/formatters.py | 2 +-
app/templates/partials/jobs/status.html | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/app/__init__.py b/app/__init__.py
index 889a016a8..e605306ed 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -53,7 +53,7 @@ from app.formatters import (
format_datetime_normal,
format_datetime_relative,
format_datetime_short,
- format_datetime_short_12h,
+ format_datetime_text,
format_datetime_short_america,
format_day_of_week,
format_delta,
@@ -554,7 +554,7 @@ def add_template_filters(application):
format_datetime_24h,
format_datetime_normal,
format_datetime_short,
- format_datetime_short_12h,
+ format_datetime_text,
format_datetime_short_america,
valid_phone_number,
linkable_name,
diff --git a/app/formatters.py b/app/formatters.py
index db9f8dc04..f06d282dc 100644
--- a/app/formatters.py
+++ b/app/formatters.py
@@ -98,7 +98,7 @@ def format_datetime_short(date):
)
-def format_datetime_short_12h(date):
+def format_datetime_text(date):
# example: February 20, 2024 at 07:00 PM US/Eastern
return "{} at {} {}".format(
format_date_normal(date), format_time_12h(date), get_user_preferred_timezone()
diff --git a/app/templates/partials/jobs/status.html b/app/templates/partials/jobs/status.html
index 858a4ff10..d9b8ec505 100644
--- a/app/templates/partials/jobs/status.html
+++ b/app/templates/partials/jobs/status.html
@@ -27,7 +27,7 @@
Your text has been scheduled
- {{ job.template_name }} - {{ current_service.name }} was scheduled on {{ job.scheduled_for|format_datetime_short_12h }} by {{ job.created_by.name }}
+ {{ job.template_name }} - {{ current_service.name }} was scheduled on {{ job.scheduled_for|format_datetime_text }} by {{ job.created_by.name }}
@@ -37,7 +37,7 @@
Your text has been sent
- {{ job.template_name }} - {{ current_service.name }} was sent on {{ job.created_at|format_datetime_short_12h }} by {{ job.created_by.name }}
+ {{ job.template_name }} - {{ current_service.name }} was sent on {{ job.created_at|format_datetime_text }} by {{ job.created_by.name }}
{% if job.scheduled_for %}
{% if job.processing_started %}
- Sent by {{ job.created_by.name }} on {{ job.processing_started|format_datetime_short_america }}
+ Sent by {{ job.created_by.name }} on {{ job.processing_started|format_datetime_short }}
{% else %}
- Uploaded by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short_america }}
+ Uploaded by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short }}
{% endif %}
{% else %}
- Sent by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short_america }}
+ Sent by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short }}
{% endif %}
{% if job.scheduled_for %}
{% if job.processing_started %}
- Sent by {{ job.created_by.name }} on {{ job.processing_started|format_datetime_short }}
+ Sent by {{ job.created_by.name }} on {{ job.processing_started|format_datetime_normal }}
{% else %}
- Uploaded by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short }}
+ Uploaded by {{ job.created_by.name }} on {{ job.created_at|format_datetime_normal }}
{% endif %}
{% else %}
- Sent by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short }}
+ Sent by {{ job.created_by.name }} on {{ job.created_at|format_datetime_normal }}
{% endif %}
{{ notification.created_by.name }}
diff --git a/app/templates/views/platform-admin/complaints.html b/app/templates/views/platform-admin/complaints.html
index 10cf18fef..378d3b876 100644
--- a/app/templates/views/platform-admin/complaints.html
+++ b/app/templates/views/platform-admin/complaints.html
@@ -29,7 +29,7 @@
{{ text_field(item.complaint_type) }}
- {{ text_field(item.complaint_date|format_datetime_short if item.complaint_date else None) }}
+ {{ text_field(item.complaint_date|format_datetime_table if item.complaint_date else None) }}
{% endcall %}
diff --git a/tests/app/main/views/test_api_integration.py b/tests/app/main/views/test_api_integration.py
index c1ca57f22..51592b99e 100644
--- a/tests/app/main/views/test_api_integration.py
+++ b/tests/app/main/views/test_api_integration.py
@@ -6,7 +6,7 @@ from unittest.mock import call
import pytest
from flask import url_for
-from app.formatters import format_datetime_short
+from app.formatters import format_datetime_table
from tests import sample_uuid, validate_route_permission
from tests.conftest import SERVICE_ONE_ID, normalize_spaces
@@ -123,7 +123,7 @@ def test_should_show_api_keys_page(
assert rows[0] == "API keys Action"
assert (
rows[1]
- == f"another key name Revoked {format_datetime_short(date.fromtimestamp(0).isoformat())}"
+ == f"another key name Revoked {format_datetime_table(date.fromtimestamp(0).isoformat())}"
)
assert rows[2] == "some key name Revoke some key name"
diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py
index 37674e797..83b861ed4 100644
--- a/tests/app/main/views/test_dashboard.py
+++ b/tests/app/main/views/test_dashboard.py
@@ -511,18 +511,18 @@ def test_download_inbox(
)
assert response.headers["Content-Type"] == ("text/csv; " "charset=utf-8")
assert response.headers["Content-Disposition"] == (
- "inline; " 'filename="Received text messages 2016-07-01.csv"'
+ "inline; " 'filename="Received text messages 07-01-2016.csv"'
)
assert response.get_data(as_text=True) == (
"Phone number,Message,Received\r\n"
- "(202) 867-5300,message-1,2016-07-01 11:00 US/Eastern\r\n"
- "(202) 867-5300,message-2,2016-07-01 10:59 US/Eastern\r\n"
- "(202) 867-5300,message-3,2016-07-01 10:59 US/Eastern\r\n"
- "(202) 867-5302,message-4,2016-07-01 08:59 US/Eastern\r\n"
- "+33 1 12 34 56 78,message-5,2016-07-01 06:59 US/Eastern\r\n"
- "(202) 555-0104,message-6,2016-07-01 04:59 US/Eastern\r\n"
- "(202) 555-0104,message-7,2016-07-01 02:59 US/Eastern\r\n"
- "+682 12345,message-8,2016-07-01 02:59 US/Eastern\r\n"
+ "(202) 867-5300,message-1,07-01-2016 11:00 US/Eastern\r\n"
+ "(202) 867-5300,message-2,07-01-2016 10:59 US/Eastern\r\n"
+ "(202) 867-5300,message-3,07-01-2016 10:59 US/Eastern\r\n"
+ "(202) 867-5302,message-4,07-01-2016 08:59 US/Eastern\r\n"
+ "+33 1 12 34 56 78,message-5,07-01-2016 06:59 US/Eastern\r\n"
+ "(202) 555-0104,message-6,07-01-2016 04:59 US/Eastern\r\n"
+ "(202) 555-0104,message-7,07-01-2016 02:59 US/Eastern\r\n"
+ "+682 12345,message-8,07-01-2016 02:59 US/Eastern\r\n"
)
diff --git a/tests/app/main/views/test_jobs.py b/tests/app/main/views/test_jobs.py
index 3076fe88f..6c8fd6193 100644
--- a/tests/app/main/views/test_jobs.py
+++ b/tests/app/main/views/test_jobs.py
@@ -424,7 +424,10 @@ def test_should_show_updates_for_one_job_as_json(
assert "2021234567" in content["notifications"]
assert "Status" in content["notifications"]
assert "Delivered" in content["notifications"]
- assert "Sent by Test User on 01-01-2016 at 12:00 AM" in content["status"]
+ assert (
+ "Sent by Test User on January 01, 2016 at 12:00 AM US/Eastern"
+ in content["status"]
+ )
assert "12:00" in content["notifications"]
@@ -466,7 +469,9 @@ def test_should_show_updates_for_scheduled_job_as_json(
assert "2021234567" in content["notifications"]
assert "Status" in content["notifications"]
assert "Delivered" in content["notifications"]
- assert "Sent by Test User on 06-01-2016 at 04:00 PM" in content["status"]
+ assert (
+ "Sent by Test User on June 01, 2016 at 04:00 PM US/Eastern" in content["status"]
+ )
assert "12:00" in content["notifications"]
diff --git a/tests/app/main/views/test_performance.py b/tests/app/main/views/test_performance.py
index ef2ed06be..5ceb5288b 100644
--- a/tests/app/main/views/test_performance.py
+++ b/tests/app/main/views/test_performance.py
@@ -116,26 +116,26 @@ def test_should_render_performance_page(
""
"Messages sent since May 2023 "
"Date Emails Text messages "
- "27 February 2021 1 2 "
- "26 February 2021 1 2 "
- "25 February 2021 1 2 "
- "24 February 2021 1 2 "
- "23 February 2021 1 2 "
- "22 February 2021 1 2 "
- "21 February 2021 1,234,567 123,456 "
+ "February 27, 2021 1 2 "
+ "February 26, 2021 1 2 "
+ "February 25, 2021 1 2 "
+ "February 24, 2021 1 2 "
+ "February 23, 2021 1 2 "
+ "February 22, 2021 1 2 "
+ "February 21, 2021 1,234,567 123,456 "
"Only showing the last 7 days "
""
"Messages sent within 10 seconds "
"98.31% on average "
"Messages sent within 10 seconds "
"Date Percentage "
- "27 February 2021 98.60% "
- "26 February 2021 100.00% "
- "25 February 2021 99.99% "
- "24 February 2021 100.00% "
- "23 February 2021 95.00% "
- "22 February 2021 95.30% "
- "21 February 2021 99.25% "
+ "February 27, 2021 98.60% "
+ "February 26, 2021 100.00% "
+ "February 25, 2021 99.99% "
+ "February 24, 2021 100.00% "
+ "February 23, 2021 95.00% "
+ "February 22, 2021 95.30% "
+ "February 21, 2021 99.25% "
"Only showing the last 7 days "
""
"Organizations using Notify "
From de003a87223857da09e8ba7c27e9092a05de26ff Mon Sep 17 00:00:00 2001
From: Jonathan Bobel
Date: Tue, 19 Mar 2024 12:10:43 -0400
Subject: [PATCH 24/55] Removed "failure" area so the user can get to all the
statuses from the Dashboard Landing screen. A better way to view all statuses
will come with a Dashboard redesign.
---
app/templates/views/dashboard/_totals.html | 25 ++--------------------
tests/app/main/views/test_dashboard.py | 2 +-
2 files changed, 3 insertions(+), 24 deletions(-)
diff --git a/app/templates/views/dashboard/_totals.html b/app/templates/views/dashboard/_totals.html
index fab3b0435..87d6c22eb 100644
--- a/app/templates/views/dashboard/_totals.html
+++ b/app/templates/views/dashboard/_totals.html
@@ -17,30 +17,9 @@
Details
- {% if show_failures %}
-
- {% if statistics['sms']['failed'] %}
-
- {{ "{:,}".format(statistics['sms']['failed']) }}
- failed – {{ statistics['sms']['failed_percentage'] }}%
-
- {% else %}
- No failures
- {% endif %}
-
- {% endif %}
+ {# Removing the failures area for now, as the user can click on the above link to see all the details.
+ In the future state of the dashboard, the all statuses will be more apparent with data visualizations #}
-
diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py
index 37674e797..7d0ae9227 100644
--- a/tests/app/main/views/test_dashboard.py
+++ b/tests/app/main/views/test_dashboard.py
@@ -655,7 +655,7 @@ def test_should_not_show_recent_templates_on_dashboard_if_only_one_template_used
expected_count = stats[0]["count"]
assert expected_count == 50
assert normalize_spaces(page.select_one("#total-sms .big-number-smaller").text) == (
- "{} text messages sent".format(expected_count)
+ "{} text messages sent in the last seven days".format(expected_count)
)
From 4e6f143675fbe95276aca4ed38db223efbdfd8c9 Mon Sep 17 00:00:00 2001
From: Kenneth Kehl <@kkehl@flexion.us>
Date: Tue, 19 Mar 2024 09:30:20 -0700
Subject: [PATCH 25/55] fix register from join service
---
app/main/forms.py | 8 ++++++
app/main/views/index.py | 4 +++
app/main/views/register.py | 45 +++++++++++++++++++++++++++---
app/main/views/send.py | 4 ++-
app/main/views/sign_in.py | 23 +--------------
app/main/views/sign_out.py | 4 ---
app/templates/views/signedout.html | 2 +-
7 files changed, 58 insertions(+), 32 deletions(-)
diff --git a/app/main/forms.py b/app/main/forms.py
index 70dbfa37f..eb898142f 100644
--- a/app/main/forms.py
+++ b/app/main/forms.py
@@ -605,6 +605,14 @@ class RegisterUserForm(StripWhitespaceForm):
auth_type = HiddenField("auth_type", default="sms_auth")
+class SetupUserProfileForm(StripWhitespaceForm):
+ name = GovukTextInputField(
+ "Full name", validators=[DataRequired(message="Cannot be empty")]
+ )
+ mobile_number = international_phone_number()
+
+
+
class RegisterUserFromInviteForm(RegisterUserForm):
def __init__(self, invited_user):
super().__init__(
diff --git a/app/main/views/index.py b/app/main/views/index.py
index e6107ab10..d3af767ee 100644
--- a/app/main/views/index.py
+++ b/app/main/views/index.py
@@ -1,3 +1,4 @@
+import os
from flask import abort, redirect, render_template, request, url_for
from flask_login import current_user
@@ -8,6 +9,8 @@ from app.main.views.pricing import CURRENT_SMS_RATE
from app.main.views.sub_navigation_dictionaries import features_nav, using_notify_nav
from app.utils.user import user_is_logged_in
+login_dot_gov_url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
+
@main.route("/")
def index():
@@ -18,6 +21,7 @@ def index():
"views/signedout.html",
sms_rate=CURRENT_SMS_RATE,
counts=status_api_client.get_count_of_live_services_and_organizations(),
+ login_dot_gov_url=login_dot_gov_url,
)
diff --git a/app/main/views/register.py b/app/main/views/register.py
index 76c839251..fe0b04d66 100644
--- a/app/main/views/register.py
+++ b/app/main/views/register.py
@@ -1,6 +1,9 @@
from datetime import datetime, timedelta
+import uuid
-from flask import abort, redirect, render_template, session, url_for
+from flask import abort, current_app, redirect, render_template, request, session, url_for
+from app.main.views import sign_in
+from app import user_api_client
from flask_login import current_user
from app.main import main
@@ -8,6 +11,7 @@ from app.main.forms import (
RegisterUserForm,
RegisterUserFromInviteForm,
RegisterUserFromOrgInviteForm,
+ SetupUserProfileForm,
)
from app.main.views.verify import activate_user
from app.models.user import InvitedOrgUser, InvitedUser, User
@@ -120,13 +124,46 @@ def _do_registration(form, send_sms=True, send_email=True, organization_id=None)
def registration_continue():
if not session.get("user_details"):
return redirect(url_for(".show_accounts_or_dashboard"))
+ else:
+ raise Exception("Unexpected routing in registration_continue")
-@main.route("/set-up-your-profile")
+
+@main.route("/set-up-your-profile", methods=["GET", "POST"])
@hide_from_search_engines
def set_up_your_profile():
+ print("ENTER set_up_your_profile")
- form = RegisterUserForm()
+
+
+ form = SetupUserProfileForm()
if form.validate_on_submit():
- _do_registration(form, send_sms=False, send_email=False)
+ print("VALIDATING FORM")
+ # start login.gov
+ code = request.args.get("code")
+ state = request.args.get("state")
+ login_gov_error = request.args.get("error")
+ if code and state:
+ access_token = sign_in._get_access_token(code, state)
+ user_email, user_uuid = sign_in._get_user_email_and_uuid(access_token)
+ redirect_url = request.args.get("next")
+
+
+
+ elif login_gov_error:
+ current_app.logger.error(f"login.gov error: {login_gov_error}")
+ raise Exception(f"Could not login with login.gov {login_gov_error}")
+ # end login.gov
+
+ user = User.register(
+ name=form.name.data,
+ email_address=user_email,
+ mobile_number=form.mobile_number.data,
+ password=str(uuid.uuid4()),
+ auth_type="sms_auth",
+ )
+ # activate the user
+ user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
+ activate_user(user["id"])
+ return redirect(url_for("main.show_accounts_or_dashboard", next=redirect_url))
return render_template("views/set-up-your-profile.html", form=form)
diff --git a/app/main/views/send.py b/app/main/views/send.py
index 6ef2afc77..8853b7fc3 100644
--- a/app/main/views/send.py
+++ b/app/main/views/send.py
@@ -524,7 +524,9 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
for user in Users(service_id):
allow_list.extend([user.name, user.mobile_number, user.email_address])
# Failed sms number
- allow_list.extend(["simulated user (fail)", "+14254147167", "simulated@simulated.gov"])
+ allow_list.extend(
+ ["simulated user (fail)", "+14254147167", "simulated@simulated.gov"]
+ )
# Success sms number
allow_list.extend(
["simulated user (success)", "+14254147755", "simulatedtwo@simulated.gov"]
diff --git a/app/main/views/sign_in.py b/app/main/views/sign_in.py
index d39cb89af..034db6ae7 100644
--- a/app/main/views/sign_in.py
+++ b/app/main/views/sign_in.py
@@ -61,6 +61,7 @@ def _get_access_token(code, state):
url = f"{base_url}{cli_assert}&{cli_assert_type}&{code_param}&grant_type=authorization_code"
headers = {"Authorization": "Bearer %s" % token}
response = requests.post(url, headers=headers)
+ print(f"RESPONSE FROM LOGIN DOT GOV {response.json()}")
access_token = response.json()["access_token"]
return access_token
@@ -166,30 +167,8 @@ def sign_in():
other_device = current_user.logged_in_elsewhere()
notify_env = os.getenv("NOTIFY_ENVIRONMENT")
- current_app.logger.info("should render the sign in template")
- # TODO REMOVE THIS INFO ONCE STAGING WORKS WITH LOGIN DOT GOV
- current_app.logger.info(f"NOTIFY ENV = {notify_env}")
- current_app.logger.info(
- f"LOGIN_DOT_GOV_CLIENT_ID={os.getenv('LOGIN_DOT_GOV_CLIENT_ID')}"
- )
- current_app.logger.info(
- f"LOGIN_DOT_GOV_USER_INFO_URL={os.getenv('LOGIN_DOT_GOV_USER_INFO_URL')}"
- )
- current_app.logger.info(
- f"LOGIN_DOT_GOV_ACCESS_TOKEN_URL={os.getenv('LOGIN_DOT_GOV_ACCESS_TOKEN_URL')}"
- )
- current_app.logger.info(
- f"LOGIN_DOT_GOV_LOGOUT_URL={os.getenv('LOGIN_DOT_GOV_LOGOUT_URL')}"
- )
- current_app.logger.info(
- f"LOGIN_DOT_GOV_BASE_LOGOUT_URL={os.getenv('LOGIN_DOT_GOV_BASE_LOGOUT_URL')}"
- )
- current_app.logger.info(
- f"LOGIN_DOT_GOV_SIGNOUT_REDIRECT={os.getenv('LOGIN_DOT_GOV_SIGNOUT_REDIRECT')}"
- )
initial_signin_url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
- current_app.logger.info(f"LOGIN_DOT_GOV_INITIAL_SIGNIN_URL={initial_signin_url}")
return render_template(
"views/signin.html",
diff --git a/app/main/views/sign_out.py b/app/main/views/sign_out.py
index 6d03665da..5ec96e189 100644
--- a/app/main/views/sign_out.py
+++ b/app/main/views/sign_out.py
@@ -6,10 +6,6 @@ from flask_login import current_user
from app.main import main
-# ask login.gov if we really need manual logout and what's up with one hour sessions
-# ask login.gov how they recommend approaching dev environment
-# ask Tim Donaworth the same for #2
-
def _sign_out_at_login_dot_gov():
base_url = os.getenv("LOGIN_DOT_GOV_BASE_LOGOUT_URL")
diff --git a/app/templates/views/signedout.html b/app/templates/views/signedout.html
index 9c7699329..1ce65aefd 100644
--- a/app/templates/views/signedout.html
+++ b/app/templates/views/signedout.html
@@ -21,7 +21,7 @@ Notify.gov
Reach people where they are with government-powered text messages
Notify.gov is a text message service that helps federal, state, local, tribal and territorial governments more effectively communicate with the people they serve.
- Sign in
+ Sign in
if you are an existing pilot partner
Currently we are only working with select pilot partners. If you are interested in using Notify.gov in the future, please contact tts-benefits-studio@gsa.gov to learn more.
Reach people where they are with government-powered text messages
Notify.gov is a text message service that helps federal, state, local, tribal and territorial governments more effectively communicate with the people they serve.
- Sign in
+ Sign in
if you are an existing pilot partner
Currently we are only working with select pilot partners. If you are interested in using Notify.gov in the future, please contact tts-benefits-studio@gsa.gov to learn more.
From 197ef11075d01cfb8e6248336641006fa1d7882c Mon Sep 17 00:00:00 2001
From: Kenneth Kehl <@kkehl@flexion.us>
Date: Tue, 19 Mar 2024 11:32:36 -0700
Subject: [PATCH 27/55] fix tests
---
app/main/forms.py | 1 -
app/main/views/index.py | 1 +
app/main/views/register.py | 24 +++++++++++++-----------
app/main/views/sign_in.py | 1 -
tests/app/main/views/test_register.py | 17 +++++++++--------
tests/app/test_navigation.py | 1 +
6 files changed, 24 insertions(+), 21 deletions(-)
diff --git a/app/main/forms.py b/app/main/forms.py
index eb898142f..a2eeb1c63 100644
--- a/app/main/forms.py
+++ b/app/main/forms.py
@@ -612,7 +612,6 @@ class SetupUserProfileForm(StripWhitespaceForm):
mobile_number = international_phone_number()
-
class RegisterUserFromInviteForm(RegisterUserForm):
def __init__(self, invited_user):
super().__init__(
diff --git a/app/main/views/index.py b/app/main/views/index.py
index d3af767ee..c68605b2e 100644
--- a/app/main/views/index.py
+++ b/app/main/views/index.py
@@ -1,4 +1,5 @@
import os
+
from flask import abort, redirect, render_template, request, url_for
from flask_login import current_user
diff --git a/app/main/views/register.py b/app/main/views/register.py
index fe0b04d66..78f7ea2ae 100644
--- a/app/main/views/register.py
+++ b/app/main/views/register.py
@@ -1,11 +1,18 @@
-from datetime import datetime, timedelta
import uuid
+from datetime import datetime, timedelta
-from flask import abort, current_app, redirect, render_template, request, session, url_for
-from app.main.views import sign_in
-from app import user_api_client
+from flask import (
+ abort,
+ current_app,
+ redirect,
+ render_template,
+ request,
+ session,
+ url_for,
+)
from flask_login import current_user
+from app import user_api_client
from app.main import main
from app.main.forms import (
RegisterUserForm,
@@ -13,6 +20,7 @@ from app.main.forms import (
RegisterUserFromOrgInviteForm,
SetupUserProfileForm,
)
+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
@@ -128,16 +136,12 @@ def registration_continue():
raise Exception("Unexpected routing in registration_continue")
-@main.route("/set-up-your-profile", methods=["GET", "POST"])
+@main.route("/set-up-your-profile", methods=["GET", "POST"])
@hide_from_search_engines
def set_up_your_profile():
- print("ENTER set_up_your_profile")
-
-
form = SetupUserProfileForm()
if form.validate_on_submit():
- print("VALIDATING FORM")
# start login.gov
code = request.args.get("code")
@@ -148,8 +152,6 @@ def set_up_your_profile():
user_email, user_uuid = sign_in._get_user_email_and_uuid(access_token)
redirect_url = request.args.get("next")
-
-
elif login_gov_error:
current_app.logger.error(f"login.gov error: {login_gov_error}")
raise Exception(f"Could not login with login.gov {login_gov_error}")
diff --git a/app/main/views/sign_in.py b/app/main/views/sign_in.py
index 034db6ae7..6a805f62c 100644
--- a/app/main/views/sign_in.py
+++ b/app/main/views/sign_in.py
@@ -61,7 +61,6 @@ def _get_access_token(code, state):
url = f"{base_url}{cli_assert}&{cli_assert_type}&{code_param}&grant_type=authorization_code"
headers = {"Authorization": "Bearer %s" % token}
response = requests.post(url, headers=headers)
- print(f"RESPONSE FROM LOGIN DOT GOV {response.json()}")
access_token = response.json()["access_token"]
return access_token
diff --git a/tests/app/main/views/test_register.py b/tests/app/main/views/test_register.py
index e7bfc9327..d5fa744c6 100644
--- a/tests/app/main/views/test_register.py
+++ b/tests/app/main/views/test_register.py
@@ -30,6 +30,7 @@ def test_logged_in_user_redirects_to_account(
)
+@pytest.mark.skip("Deprecated due to change to login-dot-gov-only registration")
@pytest.mark.parametrize(
"phone_number_to_register_with",
[
@@ -75,14 +76,14 @@ def test_register_creates_new_user_and_redirects_to_continue_page(
== "An email has been sent to notfound@example.gsa.gov."
)
- mock_send_verify_email.assert_called_with(ANY, user_data["email_address"])
- mock_register_user.assert_called_with(
- user_data["name"],
- user_data["email_address"],
- user_data["mobile_number"],
- user_data["password"],
- user_data["auth_type"],
- )
+ # mock_send_verify_email.assert_called_with(ANY, user_data["email_address"])
+ # mock_register_user.assert_called_with(
+ # user_data["name"],
+ # user_data["email_address"],
+ # user_data["mobile_number"],
+ # user_data["password"],
+ # user_data["auth_type"],
+ # )
def test_register_continue_handles_missing_session_sensibly(
diff --git a/tests/app/test_navigation.py b/tests/app/test_navigation.py
index 5ee810f33..0732ab7e0 100644
--- a/tests/app/test_navigation.py
+++ b/tests/app/test_navigation.py
@@ -166,6 +166,7 @@ EXCLUDED_ENDPOINTS = tuple(
"send_one_off",
"send_one_off_step",
"send_one_off_to_myself",
+ "set_up_your_profile",
"service_add_email_reply_to",
"service_add_sms_sender",
"service_confirm_delete_email_reply_to",
From c053cc7402b56d2df5af61c999753852b5898cab Mon Sep 17 00:00:00 2001
From: Kenneth Kehl <@kkehl@flexion.us>
Date: Tue, 19 Mar 2024 13:30:50 -0700
Subject: [PATCH 28/55] change timezone to hidden field with default for now
---
app/main/forms.py | 2 ++
app/main/views/register.py | 10 ++--------
app/templates/views/set-up-your-profile.html | 4 ++--
3 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/app/main/forms.py b/app/main/forms.py
index a2eeb1c63..13a463a51 100644
--- a/app/main/forms.py
+++ b/app/main/forms.py
@@ -610,6 +610,8 @@ class SetupUserProfileForm(StripWhitespaceForm):
"Full name", validators=[DataRequired(message="Cannot be empty")]
)
mobile_number = international_phone_number()
+ # TODO This should be replaced with a select widget when one is available.
+ preferred_timezone = HiddenField("preferred_timezone", default="US/Eastern")
class RegisterUserFromInviteForm(RegisterUserForm):
diff --git a/app/main/views/register.py b/app/main/views/register.py
index 78f7ea2ae..8a0c884d3 100644
--- a/app/main/views/register.py
+++ b/app/main/views/register.py
@@ -141,8 +141,8 @@ def registration_continue():
def set_up_your_profile():
form = SetupUserProfileForm()
- if form.validate_on_submit():
+ if form.validate_on_submit():
# start login.gov
code = request.args.get("code")
state = request.args.get("state")
@@ -157,13 +157,7 @@ def set_up_your_profile():
raise Exception(f"Could not login with login.gov {login_gov_error}")
# end login.gov
- user = User.register(
- name=form.name.data,
- email_address=user_email,
- mobile_number=form.mobile_number.data,
- password=str(uuid.uuid4()),
- auth_type="sms_auth",
- )
+
# activate the user
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
activate_user(user["id"])
diff --git a/app/templates/views/set-up-your-profile.html b/app/templates/views/set-up-your-profile.html
index b6e696c1e..8f37d3617 100644
--- a/app/templates/views/set-up-your-profile.html
+++ b/app/templates/views/set-up-your-profile.html
@@ -19,7 +19,7 @@ Set up your profile
"hint": {"text": "We'll send you a security code by text message"},
}) }}
- {{ usaSelect({
+
{{form.auth_type}}
{{ page_footer("Save") }}
{% endcall %}
From b12e638592e824c5ccaf67a37b81961d8dd4c423 Mon Sep 17 00:00:00 2001
From: Kenneth Kehl <@kkehl@flexion.us>
Date: Tue, 19 Mar 2024 14:34:41 -0700
Subject: [PATCH 29/55] fix registration
---
app/main/views/register.py | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/app/main/views/register.py b/app/main/views/register.py
index 8a0c884d3..a060fc0eb 100644
--- a/app/main/views/register.py
+++ b/app/main/views/register.py
@@ -157,6 +157,17 @@ def set_up_your_profile():
raise Exception(f"Could not login with login.gov {login_gov_error}")
# end login.gov
+ # create the user
+ # TODO we have to provide something for password until that column goes away
+ # TODO ideally we would set the user's preferred timezone here as well
+ user = User.register(
+ name=form.name.data,
+ email_address=user_email,
+ mobile_number=form.mobile_number.data,
+ password=str(uuid.uuid4()),
+ auth_type="sms_auth",
+ )
+
# activate the user
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
From 04eaec6dd69843165446b06c01468fcfc07d0e50 Mon Sep 17 00:00:00 2001
From: Kenneth Kehl <@kkehl@flexion.us>
Date: Tue, 19 Mar 2024 14:36:43 -0700
Subject: [PATCH 30/55] fix register
---
app/main/views/register.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/app/main/views/register.py b/app/main/views/register.py
index a060fc0eb..7069270ea 100644
--- a/app/main/views/register.py
+++ b/app/main/views/register.py
@@ -168,7 +168,6 @@ def set_up_your_profile():
auth_type="sms_auth",
)
-
# activate the user
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
activate_user(user["id"])
From 24a0dd8c3bebfa48639a0343f6d712ac93e717e9 Mon Sep 17 00:00:00 2001
From: Jonathan Bobel
Date: Thu, 21 Mar 2024 11:00:44 -0400
Subject: [PATCH 31/55] Few style updates per PR feedback
---
.../sass/uswds/_uswds-theme-custom-styles.scss | 13 ++-----------
app/templates/views/dashboard/_totals.html | 2 +-
app/templates/views/dashboard/dashboard.html | 2 +-
3 files changed, 4 insertions(+), 13 deletions(-)
diff --git a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss
index 1193e00be..7f2422064 100644
--- a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss
+++ b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss
@@ -312,24 +312,15 @@ td.table-empty-message {
.dashboard {
.big-number-with-status {
- // a {
- // background: color("blue-60v");
- // padding: units(2);
- // margin-bottom: units(1);
- // text-decoration: none;
- // &:hover{
- // background: color("blue-warm-70v");
- // }
- // }
.big-number-smaller {
display: flex;
flex-direction: column;
.big-number-number {
font-size: units(5);
- line-height: units(6);
+ line-height: units(5);
}
.big-number-label {
- font-size: units(3);
+ font-size: units(2.5);
}
}
.big-number-status {
diff --git a/app/templates/views/dashboard/_totals.html b/app/templates/views/dashboard/_totals.html
index 87d6c22eb..9a297bf53 100644
--- a/app/templates/views/dashboard/_totals.html
+++ b/app/templates/views/dashboard/_totals.html
@@ -1,6 +1,6 @@
An official website of the United States government
+
Here’s how you know
+
+
+
+
+
+
+
+
+
+
+ Official websites use .gov
+
+ A .gov website belongs to an official government organization in the United States.
+
+
+
+
+
+
+
+ Secure .gov websites use HTTPS
+
+ A lock ( ) or https:// means you’ve safely connected to the .gov website. Share sensitive information only on official, secure websites.
+
+
diff --git a/app/templates/new/templates_glossary.md b/app/templates/new/templates_glossary.md
index 81325e26c..8ee501728 100644
--- a/app/templates/new/templates_glossary.md
+++ b/app/templates/new/templates_glossary.md
@@ -6,22 +6,22 @@ This document serves as a glossary for the templates directory structure of the
## Directory Structure
- `/templates`
- - `base.html`: The main base template from which all other templates inherit. This template is a combination of `main_template`, `admin_template`, `withoutnav_template` and `content_template`.
- - **/layouts**: Contains base templates and shared layouts used across the site. Simply put, it defines the overall structure or skeleton of the application (less frequently revised).
+ - `base.html`: The main base template from which all other templates inherit. This template is a combination of `main_template`, `admin_template`, `withoutnav_template`, `settings_templates`, and `content_template`.
+ - **/layouts**: Contains shared layouts used across the site. Simply put, it defines the overall structure or skeleton of the application (less frequently revised).
- `withnav_template.html`: A variation of the base layout that includes a sidebar.
- `org_template.html`: A variaton of the withnav_template
- - **/components**: Houses reusable UI components that can be included in multiple templates and can be tailored with different content or links depending on the context.(more frequently revised or customized)
+ - **/components**: Houses reusable UI components that can be included in multiple templates and can be tailored with different content or links depending on the context.(more frequently revised, used or customized)
- `header.html`: Template for the site's header, included in `base.html`.
- `footer.html`: Template for the site's footer, included in `base.html`.
- **/views** (or **/pages**): Individual page templates that use the base layouts, components, and partials to present content.
### Best Practices
-- Use **inheritance** (`{% extends %}`) to build on base layouts.
-- Employ **components** (`{% include %}`) for reusable UI elements to keep the code DRY and facilitate easier updates.
+- Use **inheritance** `{% extends %}` to build on base layouts.
+- Employ **components** `{% include %}` for reusable UI elements to keep the code DRY and facilitate easier updates.
### Observation Notes
-- The macro-options.json files in the header and footer component act as structural guides. They aren't directly used as data passed to the usaFooter function/macro. Instead, these files outline the expected properties and provide a description of their purpose. The `usaFooter` macro component is currently only invoked in the `admin_template`, which will eventually serve as the `base.html` template. This will simplify the approach when we change the footer macros to componenets by eliminating the need to dynamically pass this data from the base.html template.
+- The macro-options.json files in the header and footer component act as structural guides. They aren't directly used as data passed to the usaFooter function/macro. Instead, these files outline the expected properties and provide a description of their purpose. The `usaFooter` macro component is currently only invoked in the `admin_template`, which will eventually serve as the `base.html` template. This will simplify the approach when we change the footer macros to componenets by eliminating the need to dynamically pass this data from the base.html template. This is also true for `usaHeader` macro component.
From 8362c6b975cf989e3e43d4e257c55ca05861bc42 Mon Sep 17 00:00:00 2001
From: Kenneth Kehl <@kkehl@flexion.us>
Date: Fri, 22 Mar 2024 11:08:49 -0700
Subject: [PATCH 35/55] fix invites for service and organization
---
app/main/views/verify.py | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/app/main/views/verify.py b/app/main/views/verify.py
index 0201edfcc..f6bb47ab7 100644
--- a/app/main/views/verify.py
+++ b/app/main/views/verify.py
@@ -5,6 +5,7 @@ from itsdangerous import SignatureExpired
from notifications_utils.url_safe_token import check_token
from app import user_api_client
+from app.extensions import redis_client
from app.main import main
from app.main.forms import TwoFactorForm
from app.models.user import InvitedOrgUser, InvitedUser, User
@@ -64,20 +65,44 @@ def verify_email(token):
def activate_user(user_id):
user = User.from_id(user_id)
- # the user will have a new current_session_id set by the API - store it in the cookie for future requests
+
+ # This is the login.gov path
+ login_gov_invite_data = redis_client.get(f"service-invite-{user.email_address}")
+ if login_gov_invite_data:
+ login_gov_invite_data = json.loads(login_gov_invite_data.decode("utf8"))
+
+ # This is the deprecated path for organization invites where we get id from session
session["current_session_id"] = user.current_session_id
organization_id = session.get("organization_id")
+
activated_user = user.activate()
activated_user.login()
+ # TODO when login.gov is mandatory, get rid of the if clause, it is deprecated.
invited_user = InvitedUser.from_session()
if invited_user:
service_id = _add_invited_user_to_service(invited_user)
return redirect(url_for("main.service_dashboard", service_id=service_id))
+ elif login_gov_invite_data:
+ service_id = login_gov_invite_data["service_id"]
+ user.add_to_service(
+ service_id,
+ login_gov_invite_data["permissions"],
+ login_gov_invite_data["folder_permissions"],
+ login_gov_invite_data["from_user_id"],
+ )
+ return redirect(url_for("main.service_dashboard", service_id=service_id))
+
+ # TODO when login.gov is mandatory, git rid of the if clause, it is deprecated.
invited_org_user = InvitedOrgUser.from_session()
if invited_org_user:
user_api_client.add_user_to_organization(invited_org_user.organization, user_id)
+ elif redis_client.get(f"organization-invite-{user.email_address}"):
+ organization_id = redis_client.get(f"organization-invite-{user.email_address}")
+ user_api_client.add_user_to_organization(
+ organization_id.decode("utf8"), user_id
+ )
if organization_id:
return redirect(url_for("main.organization_dashboard", org_id=organization_id))
From 4428df7dd5bdfe90f1fe43c019a8fbf6fe562007 Mon Sep 17 00:00:00 2001
From: Mitchell Henke
Date: Tue, 26 Mar 2024 15:06:26 +0000
Subject: [PATCH 36/55] Reformat nested list
---
docs/login_dot_gov.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/docs/login_dot_gov.md b/docs/login_dot_gov.md
index 5f985faa4..29b65d989 100644
--- a/docs/login_dot_gov.md
+++ b/docs/login_dot_gov.md
@@ -4,11 +4,11 @@ How to integrate with the login.gov sandbox: https://dashboard.int.identitysand
1. Create a team and a user over in the login.gov sandbox.
2. Create a test app:
- a. you will need to create a unique client id that looks like: urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:test_notify_gov
- b. Select OpenIdConnect and private key JWT
- c. select authentication only
- d. select MFA required + remember device 30 days only (AAL1)
- e. set redirect urls like: http://localhost:6012/sign-in
+ - you will need to create a unique client id that looks like: urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:test_notify_gov
+ - Select OpenIdConnect and private key JWT
+ - select authentication only
+ - select MFA required + remember device 30 days only (AAL1)
+ - set redirect urls like: http://localhost:6012/sign-in
3. generate a cert: openssl req -nodes -x509 -days 365 -newkey rsa:2048 -keyout private.pem -out public.crt
4. Upload the public.crt to your app in the sandbox
5. put the private.pem contents and public.crt contents in github secrets (?)
From 89e7de0ab6cddf9a3e653ed134a015c5e40c2fda Mon Sep 17 00:00:00 2001
From: Beverly Nguyen
Date: Tue, 26 Mar 2024 12:37:06 -0700
Subject: [PATCH 37/55] fixed the testing
---
tests/app/main/views/test_jobs.py | 11 ++---------
1 file changed, 2 insertions(+), 9 deletions(-)
diff --git a/tests/app/main/views/test_jobs.py b/tests/app/main/views/test_jobs.py
index 23eb8fc34..ec63858bc 100644
--- a/tests/app/main/views/test_jobs.py
+++ b/tests/app/main/views/test_jobs.py
@@ -424,11 +424,7 @@ def test_should_show_updates_for_one_job_as_json(
assert "2021234567" in content["notifications"]
assert "Status" in content["notifications"]
assert "Delivered" in content["notifications"]
- assert (
- "Sent by Test User on January 01, 2016 at 12:00 AM US/Eastern"
- in content["status"]
- )
- assert "12:00" in content["notifications"]
+ assert "01-01-2016 at 12:00 AM" in content["notifications"]
@freeze_time("2016-01-01 05:00:00.000001")
@@ -469,10 +465,7 @@ def test_should_show_updates_for_scheduled_job_as_json(
assert "2021234567" in content["notifications"]
assert "Status" in content["notifications"]
assert "Delivered" in content["notifications"]
- assert (
- "Sent by Test User on June 01, 2016 at 04:00 PM US/Eastern" in content["status"]
- )
- assert "12:00" in content["notifications"]
+ assert "01-01-2016 at 12:00 AM" in content["notifications"]
@pytest.mark.parametrize(
From 6c1090ecb277c10a79f05bae099dd7bdfd1c272e Mon Sep 17 00:00:00 2001
From: Jonathan Bobel
Date: Tue, 26 Mar 2024 16:06:13 -0400
Subject: [PATCH 38/55] 1252 - add back blue boxes for pending and delivered
---
app/templates/partials/count.html | 48 ++-----------------------------
tests/app/main/views/test_jobs.py | 5 ++--
2 files changed, 5 insertions(+), 48 deletions(-)
diff --git a/app/templates/partials/count.html b/app/templates/partials/count.html
index 17321d67f..188884cdf 100644
--- a/app/templates/partials/count.html
+++ b/app/templates/partials/count.html
@@ -1,49 +1,7 @@
{% from "components/pill.html" import pill %}
- {% if notifications_deleted %}
-
- {% for label, query_param, url, count in counts %}
- {% if query_param == 'pending' %}
-
diff --git a/tests/app/main/views/test_jobs.py b/tests/app/main/views/test_jobs.py
index 3076fe88f..cbee48ccc 100644
--- a/tests/app/main/views/test_jobs.py
+++ b/tests/app/main/views/test_jobs.py
@@ -320,15 +320,14 @@ def test_should_show_old_job(
service_id=SERVICE_ONE_ID,
job_id=fake_uuid,
)
- assert not page.select(".pill")
assert not page.select("p.hint")
assert not page.select("a[download]")
assert page.select_one("tbody").text.strip() == expected_message
assert [
- normalize_spaces(column.text) for column in page.select("main .grid-col-3")
+ normalize_spaces(column.text) for column in page.select("main .pill .pill-item")
] == [
"1 total text messages",
- "1 pending",
+ "1 pending text message",
"0 delivered text messages",
"0 failed text messages",
]
From d8e9aaee0ff2167a51d19d887d42d018e10adfcb Mon Sep 17 00:00:00 2001
From: Beverly Nguyen
Date: Tue, 26 Mar 2024 18:00:01 -0700
Subject: [PATCH 39/55] updated header nav and seperated out usabanner
---
app/templates/new/base.html | 7 +-
app/templates/new/components/header.html | 157 +++----------------
app/templates/new/components/usa_banner.html | 44 ++++++
3 files changed, 66 insertions(+), 142 deletions(-)
create mode 100644 app/templates/new/components/usa_banner.html
diff --git a/app/templates/new/base.html b/app/templates/new/base.html
index f7aa58f68..4d5279342 100644
--- a/app/templates/new/base.html
+++ b/app/templates/new/base.html
@@ -1,6 +1,5 @@
{% from "../components/banner.html" import banner %}
{% from "../components/components/skip-link/macro.njk" import usaSkipLink -%}
-{% from "../components/components/header/macro.njk" import usaHeader -%}
{% from "../components/components/footer/macro.njk" import usaFooter -%}
@@ -25,13 +24,13 @@
}) }}
{% endblock %}
-
+ {# region header #}
{% block header %}
{% if current_user.is_authenticated %}
- {% include "new/components/header.html" %}
+ {% include 'new/components/usa_banner.html' %}
+ {% include 'new/components/header.html' %}
{% endif %}
{% endblock %}
-
{% block main %}
diff --git a/app/templates/new/components/header.html b/app/templates/new/components/header.html
index 1f559f447..e21963fed 100644
--- a/app/templates/new/components/header.html
+++ b/app/templates/new/components/header.html
@@ -1,145 +1,26 @@
+{# setting navigation and secondarynavigation #}
+{% set navigation = [
+ {"href": url_for("main.show_accounts_or_dashboard"), "text": "Current service", "active": header_navigation.is_selected('accounts-or-dashboard')},
+ {"href": url_for('main.get_started'), "text": "Using Notify", "active": header_navigation.is_selected('using_notify')},
+ {"href": url_for('main.features'), "text": "Features", "active": header_navigation.is_selected('features')},
+ {"href": url_for('main.support'), "text": "Contact us", "active": header_navigation.is_selected('support')}
+] %}
-
-
-
-
-
-
-
-
-
-
An official website of the United States government
-
Here’s how you know
-
-
-
-
-
-
-
-
-
-
- Official websites use .gov
-
- A .gov website belongs to an official government organization in the United States.
-
-
-
-
-
-
-
- Secure .gov websites use HTTPS
-
- A lock ( ) or https:// means you’ve safely connected to the .gov website. Share sensitive information only on official, secure websites.
-
diff --git a/app/templates/new/components/usa_banner.html b/app/templates/new/components/usa_banner.html
new file mode 100644
index 000000000..532a2937a
--- /dev/null
+++ b/app/templates/new/components/usa_banner.html
@@ -0,0 +1,44 @@
+
+{# usa banner #}
+
+
+
+
+
+
+
+
+
An official website of the United States government
+
Here’s how you know
+
+
+
+
+
+
+
+
+
+
+ Official websites use .gov
+
+ A .gov website belongs to an official government organization in the United States.
+
+
+
+
+
+
+
+ Secure .gov websites use HTTPS
+
+ A lock ( ) or https:// means you’ve safely connected to the .gov website. Share sensitive information only on official, secure websites.
+
+
+
+
+
+
+
From b29c2a3a623827355ad1533f1d30fd299f4c5da0 Mon Sep 17 00:00:00 2001
From: Kenneth Kehl <@kkehl@flexion.us>
Date: Thu, 28 Mar 2024 11:15:10 -0700
Subject: [PATCH 40/55] debug staging
---
app/main/views/verify.py | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/app/main/views/verify.py b/app/main/views/verify.py
index f6bb47ab7..85c138ea9 100644
--- a/app/main/views/verify.py
+++ b/app/main/views/verify.py
@@ -9,6 +9,7 @@ from app.extensions import redis_client
from app.main import main
from app.main.forms import TwoFactorForm
from app.models.user import InvitedOrgUser, InvitedUser, User
+from app.utils import hilite
from app.utils.login import redirect_to_sign_in
@@ -67,9 +68,10 @@ def activate_user(user_id):
user = User.from_id(user_id)
# This is the login.gov path
- login_gov_invite_data = redis_client.get(f"service-invite-{user.email_address}")
+ login_gov_invite_data = redis_client.raw_get(f"service-invite-{user.email_address}")
if login_gov_invite_data:
login_gov_invite_data = json.loads(login_gov_invite_data.decode("utf8"))
+ current_app.logger.info(hilite(f"LOGIN_GOV_INVITE_DATA {login_gov_invite_data}"))
# This is the deprecated path for organization invites where we get id from session
session["current_session_id"] = user.current_session_id
@@ -85,6 +87,7 @@ def activate_user(user_id):
return redirect(url_for("main.service_dashboard", service_id=service_id))
elif login_gov_invite_data:
service_id = login_gov_invite_data["service_id"]
+ current_app.logger.info(hilite(f"SERVICE_ID={service_id}"))
user.add_to_service(
service_id,
@@ -98,8 +101,11 @@ def activate_user(user_id):
invited_org_user = InvitedOrgUser.from_session()
if invited_org_user:
user_api_client.add_user_to_organization(invited_org_user.organization, user_id)
- elif redis_client.get(f"organization-invite-{user.email_address}"):
- organization_id = redis_client.get(f"organization-invite-{user.email_address}")
+ elif redis_client.raw_get(f"organization-invite-{user.email_address}"):
+ organization_id = redis_client.raw_get(
+ f"organization-invite-{user.email_address}"
+ )
+ current_app.logger.info(hilite(f"ORGANIZATION_ID FROM REDIS {organization_id}"))
user_api_client.add_user_to_organization(
organization_id.decode("utf8"), user_id
)
From 9b1c9b8ccf0b30655a1ede7fb15fd2cf0e7b9830 Mon Sep 17 00:00:00 2001
From: Kenneth Kehl <@kkehl@flexion.us>
Date: Thu, 28 Mar 2024 11:35:25 -0700
Subject: [PATCH 41/55] ugh
---
app/main/views/verify.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/main/views/verify.py b/app/main/views/verify.py
index 85c138ea9..1250db150 100644
--- a/app/main/views/verify.py
+++ b/app/main/views/verify.py
@@ -68,7 +68,7 @@ def activate_user(user_id):
user = User.from_id(user_id)
# This is the login.gov path
- login_gov_invite_data = redis_client.raw_get(f"service-invite-{user.email_address}")
+ login_gov_invite_data = redis_client.get(f"service-invite-{user.email_address}")
if login_gov_invite_data:
login_gov_invite_data = json.loads(login_gov_invite_data.decode("utf8"))
current_app.logger.info(hilite(f"LOGIN_GOV_INVITE_DATA {login_gov_invite_data}"))
@@ -101,7 +101,7 @@ def activate_user(user_id):
invited_org_user = InvitedOrgUser.from_session()
if invited_org_user:
user_api_client.add_user_to_organization(invited_org_user.organization, user_id)
- elif redis_client.raw_get(f"organization-invite-{user.email_address}"):
+ elif redis_client.get(f"organization-invite-{user.email_address}"):
organization_id = redis_client.raw_get(
f"organization-invite-{user.email_address}"
)
From 2c57d0d373ae56c5aace1eb446402ae70be1a2ff Mon Sep 17 00:00:00 2001
From: Jonathan Bobel
Date: Thu, 28 Mar 2024 15:58:02 -0400
Subject: [PATCH 42/55] 1213 - Removed the Usage page and updated the Dashboard
Landing page
---
app/templates/main_nav.html | 2 +-
app/templates/views/dashboard/_usage.html | 2 +-
app/templates/views/dashboard/dashboard.html | 34 ++++---------------
app/templates/views/usage.html | 14 ++------
tests/app/main/test_permissions.py | 1 -
.../views/accounts/test_choose_accounts.py | 2 +-
tests/app/main/views/test_activity.py | 2 +-
tests/app/main/views/test_dashboard.py | 22 ++----------
tests/app/test_navigation.py | 2 +-
9 files changed, 18 insertions(+), 63 deletions(-)
diff --git a/app/templates/main_nav.html b/app/templates/main_nav.html
index 2f7306f09..4aac354d9 100644
--- a/app/templates/main_nav.html
+++ b/app/templates/main_nav.html
@@ -14,7 +14,7 @@