Skip to content

Commit

Permalink
Merge pull request #1006 from GSA/main
Browse files Browse the repository at this point in the history
Production deploy 12/13/23
  • Loading branch information
stvnrlly authored Dec 13, 2023
2 parents ca7b339 + efb5ca3 commit c646fb2
Show file tree
Hide file tree
Showing 44 changed files with 400 additions and 1,273 deletions.
10 changes: 7 additions & 3 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@
performance_dashboard_api_client,
)
from app.notify_client.platform_stats_api_client import platform_stats_api_client
from app.notify_client.provider_client import provider_client
from app.notify_client.service_api_client import service_api_client
from app.notify_client.status_api_client import status_api_client
from app.notify_client.template_folder_api_client import template_folder_api_client
Expand Down Expand Up @@ -151,8 +150,14 @@ def _csp(config):
"https://js-agent.newrelic.com",
"https://gov-bam.nr-data.net",
"https://www.googletagmanager.com",
"https://www.google-analytics.com",
"https://dap.digitalgov.gov",
],
"connect-src": [
"'self'",
"https://gov-bam.nr-data.net",
"https://www.google-analytics.com",
],
"connect-src": ["'self'", "https://gov-bam.nr-data.net"],
"style-src": ["'self'", asset_domain],
"img-src": ["'self'", asset_domain, logo_domain],
}
Expand Down Expand Up @@ -191,7 +196,6 @@ def create_app(application):
organizations_client,
performance_dashboard_api_client,
platform_stats_api_client,
provider_client,
service_api_client,
status_api_client,
template_folder_api_client,
Expand Down
1 change: 0 additions & 1 deletion app/main/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
performance,
platform_admin,
pricing,
providers,
register,
security_policy,
send,
Expand Down
65 changes: 18 additions & 47 deletions app/main/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -1196,7 +1196,7 @@ class AdminServiceSMSAllowanceForm(StripWhitespaceForm):

class AdminServiceMessageLimitForm(StripWhitespaceForm):
message_limit = GovukIntegerField(
"Number of messages the service is allowed to send each day",
"Max number of messages the service has per send",
validators=[DataRequired(message="Cannot be empty")],
)

Expand Down Expand Up @@ -1279,6 +1279,23 @@ class ChangeNameForm(StripWhitespaceForm):
new_name = GovukTextInputField("Your name")


class ChangePreferredTimezoneForm(StripWhitespaceForm):
def __init__(self, *args, **kwargs):
super(ChangePreferredTimezoneForm, self).__init__(*args, **kwargs)
self.new_preferred_timezone.choices = [
("US/Eastern", "US/Eastern"),
("US/Central", "US/Central"),
("US/Mountain", "US/Mountain"),
("US/Pacific", "US/Pacific"),
("US/Hawaii", "US/Hawaii"),
]

new_preferred_timezone = GovukRadiosField(
"What timezone would you like to use?",
default="US/Eastern",
)


class ChangeEmailForm(StripWhitespaceForm):
def __init__(self, validate_email_func, *args, **kwargs):
self.validate_email_func = validate_email_func
Expand Down Expand Up @@ -1423,52 +1440,6 @@ def validate(self, *args, **kwargs):
return super().validate(*args, **kwargs)


class AdminProviderRatioForm(Form):
def __init__(self, providers):
self._providers = providers

# hack: https://github.com/wtforms/wtforms/issues/736
self._unbound_fields = [
(
provider["identifier"],
GovukIntegerField(
f"{provider['display_name']} (%)",
validators=[
validators.NumberRange(
min=0, max=100, message="Must be between 0 and 100"
)
],
param_extensions={
"classes": "width-8",
},
),
)
for provider in providers
]

super().__init__(
data={
provider["identifier"]: provider["priority"] for provider in providers
}
)

def validate(self, extra_validators=None):
if not super().validate(extra_validators):
return False

total = sum(
getattr(self, provider["identifier"]).data for provider in self._providers
)

if total == 100:
return True

for provider in self._providers:
getattr(self, provider["identifier"]).errors += ["Must add up to 100%"]

return False


class ServiceContactDetailsForm(StripWhitespaceForm):
contact_details_type = RadioField(
"Type of contact details",
Expand Down
4 changes: 2 additions & 2 deletions app/main/views/manage_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
SearchUsersForm,
)
from app.models.user import InvitedUser, User
from app.utils.user import is_gov_user, user_has_permissions, user_is_platform_admin
from app.utils.user import is_gov_user, user_has_permissions
from app.utils.user_permissions import permission_options


Expand All @@ -42,7 +42,7 @@ def manage_users(service_id):
@main.route(
"/services/<uuid:service_id>/users/invite/<uuid:user_id>", methods=["GET", "POST"]
)
@user_is_platform_admin
@user_has_permissions("manage_service")
def invite_user(service_id, user_id=None):
form_class = InviteUserForm
form = form_class(
Expand Down
79 changes: 0 additions & 79 deletions app/main/views/providers.py

This file was deleted.

27 changes: 24 additions & 3 deletions app/main/views/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from flask import abort, flash, jsonify, redirect, render_template, request, url_for
from flask_login import current_user
from markupsafe import Markup
from notifications_python_client.errors import HTTPError
from notifications_utils import SMS_CHAR_COUNT_LIMIT

Expand Down Expand Up @@ -679,8 +680,23 @@ def count_content_length(service_id, template_type):
)


def _is_latin1(s):
return bool(s.encode(encoding="latin-1", errors="strict"))


def _get_content_count_error_and_message_for_template(template):
url = "https://en.wikipedia.org/wiki/ISO/IEC_8859-1"
if template.template_type == "sms":
s1 = f"<html><body>Use of characters outside the <a href='{url}'>IEC_8859-1</a> character set may increase "
s2 = "the message fragment count, resulting in additional charges, and these IEC_8859-1 "
s3 = "characters may not display properly on some older mobile devices.</body></html>"

warning = ""
try:
_is_latin1(template.content)
except UnicodeEncodeError:
warning = f"{s1}{s2}{s3}"

if template.is_message_too_long():
return True, (
f"You have "
Expand All @@ -689,11 +705,16 @@ def _get_content_count_error_and_message_for_template(template):
)
if template.placeholders:
return False, (
f"Will be charged as {message_count(template.fragment_count, template.template_type)} "
f"(not including personalization)"
Markup(
f"Will be charged as {message_count(template.fragment_count, template.template_type)} "
f"(not including personalization). {warning}"
)
)
return False, (
f"Will be charged as {message_count(template.fragment_count, template.template_type)} "
# Markup marks html contents safe so that they render properly. Don't use it if there is user input.
Markup(
f"Will be charged as {message_count(template.fragment_count, template.template_type)}. {warning} "
)
)


Expand Down
17 changes: 17 additions & 0 deletions app/main/views/user_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
ChangeMobileNumberForm,
ChangeNameForm,
ChangePasswordForm,
ChangePreferredTimezoneForm,
ConfirmPasswordForm,
ServiceOnOffSettingForm,
TwoFactorForm,
Expand Down Expand Up @@ -61,6 +62,22 @@ def user_profile_name():
)


@main.route("/user-profile/preferred_timezone", methods=["GET", "POST"])
@user_is_logged_in
def user_profile_preferred_timezone():
form = ChangePreferredTimezoneForm(new_name=current_user.preferred_timezone)

if form.validate_on_submit():
current_user.update(preferred_timezone=form.new_preferred_timezone.data)
current_user.preferred_timezone = form.new_preferred_timezone.data
return redirect(url_for(".user_profile"))
return render_template(
"views/user-profile/change.html",
thing="preferred timezone",
form_field=form.new_preferred_timezone,
)


@main.route("/user-profile/email", methods=["GET", "POST"])
@user_is_logged_in
@user_is_gov_user
Expand Down
2 changes: 1 addition & 1 deletion app/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ def __init__(self, _dict):
super().__init__(_dict)
self.permissions = _dict.get("permissions", {})
self._platform_admin = _dict["platform_admin"]
self.preferred_timezone = "US/Eastern"

@classmethod
def from_id(cls, user_id):
Expand Down Expand Up @@ -366,6 +365,7 @@ def serialize(self):
"permissions": [x for x in self._permissions],
"organizations": self.organization_ids,
"current_session_id": self.current_session_id,
"preferred_timezone": self.preferred_timezone,
}
if hasattr(self, "_password"):
dct["password"] = self._password
Expand Down
4 changes: 1 addition & 3 deletions app/navigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,14 @@ class HeaderNavigation(Navigation):
"user_profile_mobile_number_delete",
"user_profile_name",
"user_profile_password",
"user_profile_preferred_timezone",
"user_profile_disable_platform_admin_view",
},
"platform-admin": {
"archive_user",
"change_user_auth",
"clear_cache",
"create_email_branding",
"edit_sms_provider_ratio",
"email_branding",
"find_services_by_name",
"find_users_by_email",
Expand All @@ -185,8 +185,6 @@ class HeaderNavigation(Navigation):
"trial_services",
"update_email_branding",
"user_information",
"view_provider",
"view_providers",
},
"sign-in": {
"revalidate_email_sent",
Expand Down
20 changes: 20 additions & 0 deletions app/notify_client/job_api_client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import datetime
from zoneinfo import ZoneInfo

from app.extensions import redis_client
from app.notify_client import NotifyAdminAPIClient, _attach_current_user, cache
from app.utils.csv import get_user_preferred_timezone


class JobApiClient(NotifyAdminAPIClient):
Expand Down Expand Up @@ -86,10 +90,26 @@ def get_scheduled_job_stats(self, service_id):
def has_jobs(self, service_id):
return bool(self.get_jobs(service_id)["data"])

@classmethod
def convert_user_time_to_utc(cls, scheduled_for):
user_preferred_tz = get_user_preferred_timezone()

user_date = datetime.datetime.fromisoformat(scheduled_for)
scheduled_for = (
user_date.replace(tzinfo=ZoneInfo(user_preferred_tz))
.astimezone(ZoneInfo("UTC"))
.strftime("%Y-%m-%dT%H:%M:%S")
)

return scheduled_for

def create_job(self, job_id, service_id, scheduled_for=None):
data = {"id": job_id}

# make a datetime object in the user's preferred timezone

if scheduled_for:
scheduled_for = JobApiClient.convert_user_time_to_utc(scheduled_for)
data.update({"scheduled_for": scheduled_for})

data = _attach_current_user(data)
Expand Down
Loading

0 comments on commit c646fb2

Please sign in to comment.