From 181ae4c60faafd852e13d6775dea5485ee43d44e Mon Sep 17 00:00:00 2001 From: jimmoffet Date: Mon, 29 Aug 2022 19:10:56 -0700 Subject: [PATCH] deukify service and template --- Makefile | 2 +- app/config.py | 2 +- app/us_template.py | 153 ++++++++++++++++++ .../versions/0374_fix_reg_template_history.py | 54 +++++++ migrations/versions/0375_fix_service_name.py | 39 +++++ sample.env | 2 +- 6 files changed, 249 insertions(+), 3 deletions(-) create mode 100644 app/us_template.py create mode 100644 migrations/versions/0374_fix_reg_template_history.py create mode 100644 migrations/versions/0375_fix_service_name.py diff --git a/Makefile b/Makefile index 7b13cced5..3116d54e6 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ run-flask: ## Run flask .PHONY: run-celery run-celery: ## Run celery, TODO remove purge for staging/prod - celery -A run_celery.notify_celery purge -f + # celery -A run_celery.notify_celery purge -f celery \ -A run_celery.notify_celery worker \ --pidfile="/tmp/celery.pid" \ diff --git a/app/config.py b/app/config.py index d06431ad4..238926c53 100644 --- a/app/config.py +++ b/app/config.py @@ -434,7 +434,7 @@ class Development(Config): NOTIFY_ENVIRONMENT = 'development' NOTIFY_LOG_PATH = 'application.log' - NOTIFY_EMAIL_DOMAIN = "dispostable.com" + NOTIFY_EMAIL_DOMAIN = "notify.sandbox.10x.gsa.gov" SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI', 'postgresql://postgres:chummy@db:5432/notification_api') REDIS_URL = os.environ.get('REDIS_URL') diff --git a/app/us_template.py b/app/us_template.py new file mode 100644 index 000000000..abc0b0aa5 --- /dev/null +++ b/app/us_template.py @@ -0,0 +1,153 @@ +from datetime import datetime, timedelta + +import pytz +from flask import url_for +from notifications_utils.template import ( + BroadcastMessageTemplate, + HTMLEmailTemplate, + LetterPrintTemplate, + SMSMessageTemplate, +) +from notifications_utils.timezones import convert_bst_to_utc +from sqlalchemy import func + +DATETIME_FORMAT_NO_TIMEZONE = "%Y-%m-%d %H:%M:%S.%f" +DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ" +DATE_FORMAT = "%Y-%m-%d" +local_timezone = pytz.timezone("Europe/London") + + +def pagination_links(pagination, endpoint, **kwargs): + if 'page' in kwargs: + kwargs.pop('page', None) + links = {} + if pagination.has_prev: + links['prev'] = url_for(endpoint, page=pagination.prev_num, **kwargs) + if pagination.has_next: + links['next'] = url_for(endpoint, page=pagination.next_num, **kwargs) + links['last'] = url_for(endpoint, page=pagination.pages, **kwargs) + return links + + +def get_prev_next_pagination_links(current_page, next_page_exists, endpoint, **kwargs): + if 'page' in kwargs: + kwargs.pop('page', None) + links = {} + if current_page > 1: + links['prev'] = url_for(endpoint, page=current_page - 1, **kwargs) + if next_page_exists: + links['next'] = url_for(endpoint, page=current_page + 1, **kwargs) + return links + + +def url_with_token(data, url, config, base_url=None): + from notifications_utils.url_safe_token import generate_token + token = generate_token(data, config['SECRET_KEY'], config['DANGEROUS_SALT']) + base_url = (base_url or config['ADMIN_BASE_URL']) + url + return base_url + token + + +def get_template_instance(template, values): + from app.models import BROADCAST_TYPE, EMAIL_TYPE, LETTER_TYPE, SMS_TYPE + return { + SMS_TYPE: SMSMessageTemplate, + EMAIL_TYPE: HTMLEmailTemplate, + LETTER_TYPE: LetterPrintTemplate, + BROADCAST_TYPE: BroadcastMessageTemplate, + }[template['template_type']](template, values) + + +def get_london_midnight_in_utc(date): + """ + This function converts date to midnight as BST (British Standard Time) to UTC, + the tzinfo is lastly removed from the datetime because the database stores the timestamps without timezone. + :param date: the day to calculate the London midnight in UTC for + :return: the datetime of London midnight in UTC, for example 2016-06-17 = 2016-06-16 23:00:00 + """ + return convert_bst_to_utc(datetime.combine(date, datetime.min.time())) + + +def get_midnight_for_day_before(date): + day_before = date - timedelta(1) + return get_london_midnight_in_utc(day_before) + + +def get_london_month_from_utc_column(column): + """ + Where queries need to count notifications by month it needs to be + the month in BST (British Summer Time). + The database stores all timestamps as UTC without the timezone. + - First set the timezone on created_at to UTC + - then convert the timezone to BST (or Europe/London) + - lastly truncate the datetime to month with which we can group + queries + """ + return func.date_trunc( + "month", + func.timezone("Europe/London", func.timezone("UTC", column)) + ) + + +def get_public_notify_type_text(notify_type, plural=False): + from app.models import ( + BROADCAST_TYPE, + PRECOMPILED_LETTER, + SMS_TYPE, + UPLOAD_DOCUMENT, + ) + notify_type_text = notify_type + if notify_type == SMS_TYPE: + notify_type_text = 'text message' + elif notify_type == UPLOAD_DOCUMENT: + notify_type_text = 'document' + elif notify_type == PRECOMPILED_LETTER: + notify_type_text = 'precompiled letter' + elif notify_type == BROADCAST_TYPE: + notify_type_text = 'broadcast message' + + return '{}{}'.format(notify_type_text, 's' if plural else '') + + +def midnight_n_days_ago(number_of_days): + """ + Returns midnight a number of days ago. Takes care of daylight savings etc. + """ + return get_london_midnight_in_utc(datetime.utcnow() - timedelta(days=number_of_days)) + + +def escape_special_characters(string): + for special_character in ('\\', '_', '%', '/'): + string = string.replace( + special_character, + r'\{}'.format(special_character) + ) + return string + + +def email_address_is_nhs(email_address): + return email_address.lower().endswith(( + '@nhs.uk', '@nhs.net', '.nhs.uk', '.nhs.net', + )) + + +def get_archived_db_column_value(column): + date = datetime.utcnow().strftime("%Y-%m-%d") + return f'_archived_{date}_{column}' + + +def get_dt_string_or_none(val): + return val.strftime(DATETIME_FORMAT) if val else None + + +def get_uuid_string_or_none(val): + return str(val) if val else None + + +def format_sequential_number(sequential_number): + return format(sequential_number, "x").zfill(8) + + +def get_reference_from_personalisation(personalisation): + if personalisation: + return personalisation.get("reference") + return None diff --git a/migrations/versions/0374_fix_reg_template_history.py b/migrations/versions/0374_fix_reg_template_history.py new file mode 100644 index 000000000..b8cfc3c60 --- /dev/null +++ b/migrations/versions/0374_fix_reg_template_history.py @@ -0,0 +1,54 @@ +"""empty message + +Revision ID: 0374_fix_reg_template_history +Revises: 0373_add_notifications_view +Create Date: 2022-08-22 11:04:15.888017 + +""" + +# revision identifiers, used by Alembic. +from datetime import datetime + +revision = '0374_fix_reg_template_history' +down_revision = '0373_add_notifications_view' + +from alembic import op +import sqlalchemy as sa + +service_id = 'd6aa2c68-a2d9-4437-ab19-3ae8eb202553' +user_id= '6af522d0-2915-4e52-83a3-3690455a5fe6' + +def upgrade(): + op.get_bind() + + # modify subject of verification email in templates + table_name = 'templates' + col = 'subject' + val = 'Confirm US Notify registration' + select_by_col = 'name' + select_by_val = 'Notify email verification code' + op.execute(f"update {table_name} set {col}='{val}' where {select_by_col} = '{select_by_val}'") + + # modify subject of verification email in templates_history + table_name = 'templates_history' + op.execute(f"update {table_name} set {col}='{val}' where {select_by_col} = '{select_by_val}'") + + # modify content of verification email in templates + table_name = 'templates' + col = 'content' + val = """Hi ((name)),\n\nTo complete your registration for US Notify please click the link below\n\n((url))""" + select_by_col = 'name' + select_by_val = 'Notify email verification code' + op.execute(f"update {table_name} set {col}='{val}' where {select_by_col} = '{select_by_val}'") + + # modify content of verification email in templates_history + table_name = 'templates_history' + op.execute(f"update {table_name} set {col}='{val}' where {select_by_col} = '{select_by_val}'") + + # TODO: modify other templates as necessary and re-run this migration + + +def downgrade(): + ### commands auto generated by Alembic - please adjust! ### + pass + ### end Alembic commands ### diff --git a/migrations/versions/0375_fix_service_name.py b/migrations/versions/0375_fix_service_name.py new file mode 100644 index 000000000..6ec34574f --- /dev/null +++ b/migrations/versions/0375_fix_service_name.py @@ -0,0 +1,39 @@ +"""empty message + +Revision ID: 0375_fix_service_name +Revises: 0374_fix_reg_template_history +Create Date: 2022-08-29 11:04:15.888017 + +""" + +# revision identifiers, used by Alembic. +from datetime import datetime + +revision = '0375_fix_service_name' +down_revision = '0374_fix_reg_template_history' + +from alembic import op +import sqlalchemy as sa + +service_id = 'd6aa2c68-a2d9-4437-ab19-3ae8eb202553' +user_id= '6af522d0-2915-4e52-83a3-3690455a5fe6' + +def upgrade(): + op.get_bind() + + # modify name of default service user in services + table_name = 'services' + col = 'name' + val = 'US Notify' + select_by_col = 'id' + select_by_val = service_id + op.execute(f"update {table_name} set {col}='{val}' where {select_by_col} = '{select_by_val}'") + + table_name = 'services_history' + op.execute(f"update {table_name} set {col}='{val}' where {select_by_col} = '{select_by_val}'") + + +def downgrade(): + ### commands auto generated by Alembic - please adjust! ### + pass + ### end Alembic commands ### diff --git a/sample.env b/sample.env index 38344bf2b..4d7727c45 100644 --- a/sample.env +++ b/sample.env @@ -4,7 +4,7 @@ DEBUG=True ANTIVIRUS_ENABLED=0 NOTIFY_ENVIRONMENT=development -NOTIFICATION_QUEUE_PREFIX=local_dev_10x +NOTIFICATION_QUEUE_PREFIX=local_dev_YOURNAME_ STATSD_HOST=localhost SES_STUB_URL=None NOTIFY_APP_NAME=api