notify-api-412 use black to enforce python coding style

This commit is contained in:
Kenneth Kehl
2023-08-25 09:12:23 -07:00
parent c6eb007386
commit 8c9721d8e2
201 changed files with 31660 additions and 28105 deletions

View File

@@ -8,17 +8,21 @@ from orderedset._orderedset import OrderedSet
from werkzeug.datastructures import MultiDict
from werkzeug.routing import RequestRedirect
SENDING_STATUSES = ['created', 'pending', 'sending']
DELIVERED_STATUSES = ['delivered', 'sent']
FAILURE_STATUSES = ['failed', 'temporary-failure', 'permanent-failure',
'technical-failure', 'validation-failed']
SENDING_STATUSES = ["created", "pending", "sending"]
DELIVERED_STATUSES = ["delivered", "sent"]
FAILURE_STATUSES = [
"failed",
"temporary-failure",
"permanent-failure",
"technical-failure",
"validation-failed",
]
REQUESTED_STATUSES = SENDING_STATUSES + DELIVERED_STATUSES + FAILURE_STATUSES
NOTIFICATION_TYPES = ["sms", "email"]
def service_has_permission(permission):
from app import current_service
def wrap(func):
@@ -27,12 +31,18 @@ def service_has_permission(permission):
if not current_service or not current_service.has_permission(permission):
abort(403)
return func(*args, **kwargs)
return wrap_func
return wrap
def get_help_argument():
return request.args.get('help') if request.args.get('help') in ('1', '2', '3') else None
return (
request.args.get("help")
if request.args.get("help") in ("1", "2", "3")
else None
)
def parse_filter_args(filter_dict):
@@ -40,43 +50,50 @@ def parse_filter_args(filter_dict):
filter_dict = MultiDict(filter_dict)
return MultiDict(
(
key,
(','.join(filter_dict.getlist(key))).split(',')
)
(key, (",".join(filter_dict.getlist(key))).split(","))
for key in filter_dict.keys()
if ''.join(filter_dict.getlist(key))
if "".join(filter_dict.getlist(key))
)
def set_status_filters(filter_args):
status_filters = filter_args.get('status', [])
return list(OrderedSet(chain(
(status_filters or REQUESTED_STATUSES),
DELIVERED_STATUSES if 'delivered' in status_filters else [],
SENDING_STATUSES if 'sending' in status_filters else [],
FAILURE_STATUSES if 'failed' in status_filters else []
)))
status_filters = filter_args.get("status", [])
return list(
OrderedSet(
chain(
(status_filters or REQUESTED_STATUSES),
DELIVERED_STATUSES if "delivered" in status_filters else [],
SENDING_STATUSES if "sending" in status_filters else [],
FAILURE_STATUSES if "failed" in status_filters else [],
)
)
)
def unicode_truncate(s, length):
encoded = s.encode('utf-8')[:length]
return encoded.decode('utf-8', 'ignore')
encoded = s.encode("utf-8")[:length]
return encoded.decode("utf-8", "ignore")
def should_skip_template_page(db_template):
return (
current_user.has_permissions('send_messages')
and not current_user.has_permissions('manage_templates', 'manage_api_keys')
and not db_template['archived']
current_user.has_permissions("send_messages")
and not current_user.has_permissions("manage_templates", "manage_api_keys")
and not db_template["archived"]
)
def get_default_sms_sender(sms_senders):
return str(next((
Field(x['sms_sender'], html='escape')
for x in sms_senders if x['is_default']
), "None"))
return str(
next(
(
Field(x["sms_sender"], html="escape")
for x in sms_senders
if x["is_default"]
),
"None",
)
)
class PermanentRedirect(RequestRedirect):
@@ -85,6 +102,7 @@ class PermanentRedirect(RequestRedirect):
308 status codes are not supported when Internet Explorer is used with Windows 7
and Windows 8.1, so this class keeps the original status code of 301.
"""
code = 301
@@ -93,8 +111,9 @@ def hide_from_search_engines(f):
def decorated_function(*args, **kwargs):
g.hide_from_search_engines = True
response = make_response(f(*args, **kwargs))
response.headers['X-Robots-Tag'] = 'noindex'
response.headers["X-Robots-Tag"] = "noindex"
return response
return decorated_function

View File

@@ -2,19 +2,22 @@ from app.models.organization import Organization
def get_email_choices(service):
organization_branding_id = service.organization.email_branding_id if service.organization else None
organization_branding_id = (
service.organization.email_branding_id if service.organization else None
)
if (
service.organization_type == Organization.TYPE_FEDERAL
and service.email_branding_id is not None # GOV.UK is not current branding
and organization_branding_id is None # no default to supersede it (GOV.UK)
):
yield ('govuk', 'GOV.UK')
yield ("govuk", "GOV.UK")
if (
service.organization_type == Organization.TYPE_FEDERAL
and service.organization
and organization_branding_id is None # don't offer both if org has default
and service.email_branding_name.lower() != f'GOV.UK and {service.organization.name}'.lower()
and service.email_branding_name.lower()
!= f"GOV.UK and {service.organization.name}".lower()
):
yield ('govuk_and_org', f'GOV.UK and {service.organization.name}')
yield ("govuk_and_org", f"GOV.UK and {service.organization.name}")

View File

@@ -5,17 +5,16 @@ from app.utils.templates import get_sample_template
def get_errors_for_csv(recipients, template_type):
errors = []
if any(recipients.rows_with_bad_recipients):
number_of_bad_recipients = len(list(recipients.rows_with_bad_recipients))
if 'sms' == template_type:
if "sms" == template_type:
if 1 == number_of_bad_recipients:
errors.append("fix 1 phone number")
else:
errors.append("fix {} phone numbers".format(number_of_bad_recipients))
elif 'email' == template_type:
elif "email" == template_type:
if 1 == number_of_bad_recipients:
errors.append("fix 1 email address")
else:
@@ -26,23 +25,35 @@ def get_errors_for_csv(recipients, template_type):
if 1 == number_of_rows_with_missing_data:
errors.append("enter missing data in 1 row")
else:
errors.append("enter missing data in {} rows".format(number_of_rows_with_missing_data))
errors.append(
"enter missing data in {} rows".format(number_of_rows_with_missing_data)
)
if any(recipients.rows_with_message_too_long):
number_of_rows_with_message_too_long = len(list(recipients.rows_with_message_too_long))
number_of_rows_with_message_too_long = len(
list(recipients.rows_with_message_too_long)
)
if 1 == number_of_rows_with_message_too_long:
errors.append("shorten the message in 1 row")
else:
errors.append("shorten the messages in {} rows".format(number_of_rows_with_message_too_long))
errors.append(
"shorten the messages in {} rows".format(
number_of_rows_with_message_too_long
)
)
if any(recipients.rows_with_empty_message):
number_of_rows_with_empty_message = len(list(recipients.rows_with_empty_message))
number_of_rows_with_empty_message = len(
list(recipients.rows_with_empty_message)
)
if 1 == number_of_rows_with_empty_message:
errors.append("check you have content for the empty message in 1 row")
else:
errors.append("check you have content for the empty messages in {} rows".format(
number_of_rows_with_empty_message
))
errors.append(
"check you have content for the empty messages in {} rows".format(
number_of_rows_with_empty_message
)
)
return errors
@@ -50,52 +61,71 @@ def get_errors_for_csv(recipients, template_type):
def generate_notifications_csv(**kwargs):
from app import notification_api_client
from app.s3_client.s3_csv_client import s3download
if 'page' not in kwargs:
kwargs['page'] = 1
if kwargs.get('job_id'):
original_file_contents = s3download(kwargs['service_id'], kwargs['job_id'])
if "page" not in kwargs:
kwargs["page"] = 1
if kwargs.get("job_id"):
original_file_contents = s3download(kwargs["service_id"], kwargs["job_id"])
original_upload = RecipientCSV(
original_file_contents,
template=get_sample_template(kwargs['template_type']),
template=get_sample_template(kwargs["template_type"]),
)
original_column_headers = original_upload.column_headers
fieldnames = ['Row number'] + original_column_headers + ['Template', 'Type', 'Job', 'Status', 'Time']
fieldnames = (
["Row number"]
+ original_column_headers
+ ["Template", "Type", "Job", "Status", "Time"]
)
else:
fieldnames = ['Recipient', 'Template', 'Type', 'Sent by', 'Job', 'Status', 'Time']
fieldnames = [
"Recipient",
"Template",
"Type",
"Sent by",
"Job",
"Status",
"Time",
]
yield ','.join(fieldnames) + '\n'
yield ",".join(fieldnames) + "\n"
while kwargs['page']:
notifications_resp = notification_api_client.get_notifications_for_service(**kwargs)
for notification in notifications_resp['notifications']:
if kwargs.get('job_id'):
values = [
notification['row_number'],
] + [
original_upload[notification['row_number'] - 1].get(header).data
for header in original_column_headers
] + [
notification['template_name'],
notification['template_type'],
notification['job_name'],
notification['status'],
notification['created_at'],
]
while kwargs["page"]:
notifications_resp = notification_api_client.get_notifications_for_service(
**kwargs
)
for notification in notifications_resp["notifications"]:
if kwargs.get("job_id"):
values = (
[
notification["row_number"],
]
+ [
original_upload[notification["row_number"] - 1].get(header).data
for header in original_column_headers
]
+ [
notification["template_name"],
notification["template_type"],
notification["job_name"],
notification["status"],
notification["created_at"],
]
)
else:
values = [
notification['recipient'],
notification['template_name'],
notification['template_type'],
notification['created_by_name'] or '',
notification['job_name'] or '',
notification['status'],
notification['created_at']
notification["recipient"],
notification["template_name"],
notification["template_type"],
notification["created_by_name"] or "",
notification["job_name"] or "",
notification["status"],
notification["created_at"],
]
yield Spreadsheet.from_rows([map(str, values)]).as_csv_data
if notifications_resp['links'].get('next'):
kwargs['page'] += 1
if notifications_resp["links"].get("next"):
kwargs["page"] += 1
else:
return
raise Exception("Should never reach here")

View File

@@ -9,10 +9,11 @@ from app.utils.time import is_less_than_days_ago
def redirect_to_sign_in(f):
@wraps(f)
def wrapped(*args, **kwargs):
if 'user_details' not in session:
return redirect(url_for('main.sign_in'))
if "user_details" not in session:
return redirect(url_for("main.sign_in"))
else:
return f(*args, **kwargs)
return wrapped
@@ -20,10 +21,10 @@ def log_in_user(user_id):
try:
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
session['current_session_id'] = user.current_session_id
session["current_session_id"] = user.current_session_id
# Check if coming from new password page
if 'password' in session.get('user_details', {}):
user.update_password(session['user_details']['password'])
if "password" in session.get("user_details", {}):
user.update_password(session["user_details"]["password"])
user.activate()
user.login()
finally:
@@ -35,11 +36,11 @@ def log_in_user(user_id):
def redirect_when_logged_in(platform_admin):
next_url = request.args.get('next')
next_url = request.args.get("next")
if next_url and is_safe_redirect_url(next_url):
return redirect(next_url)
return redirect(url_for('main.show_accounts_or_dashboard'))
return redirect(url_for("main.show_accounts_or_dashboard"))
def email_needs_revalidating(user):
@@ -49,7 +50,10 @@ def email_needs_revalidating(user):
# see https://stackoverflow.com/questions/60532973/how-do-i-get-a-is-safe-url-function-to-use-with-flask-and-how-does-it-work # noqa
def is_safe_redirect_url(target):
from urllib.parse import urljoin, urlparse
host_url = urlparse(request.host_url)
redirect_url = urlparse(urljoin(request.host_url, target))
return redirect_url.scheme in ('http', 'https') and \
host_url.netloc == redirect_url.netloc
return (
redirect_url.scheme in ("http", "https")
and host_url.netloc == redirect_url.netloc
)

View File

@@ -2,9 +2,9 @@ from flask import request, url_for
def get_page_from_request():
if 'page' in request.args:
if "page" in request.args:
try:
return int(request.args['page'])
return int(request.args["page"])
except ValueError:
return None
else:
@@ -12,16 +12,20 @@ def get_page_from_request():
def generate_previous_dict(view, service_id, page, url_args=None):
return generate_previous_next_dict(view, service_id, page - 1, 'Previous page', url_args or {})
return generate_previous_next_dict(
view, service_id, page - 1, "Previous page", url_args or {}
)
def generate_next_dict(view, service_id, page, url_args=None):
return generate_previous_next_dict(view, service_id, page + 1, 'Next page', url_args or {})
return generate_previous_next_dict(
view, service_id, page + 1, "Next page", url_args or {}
)
def generate_previous_next_dict(view, service_id, page, title, url_args):
return {
'url': url_for(view, service_id=service_id, page=page, **url_args),
'title': title,
'label': 'page {}'.format(page)
"url": url_for(view, service_id=service_id, page=page, **url_args),
"title": title,
"label": "page {}".format(page),
}

View File

@@ -2,10 +2,12 @@ from notifications_utils.template import EmailPreviewTemplate, SMSPreviewTemplat
def get_sample_template(template_type):
if template_type == 'email':
return EmailPreviewTemplate({'content': 'any', 'subject': '', 'template_type': 'email'})
if template_type == 'sms':
return SMSPreviewTemplate({'content': 'any', 'template_type': 'sms'})
if template_type == "email":
return EmailPreviewTemplate(
{"content": "any", "subject": "", "template_type": "email"}
)
if template_type == "sms":
return SMSPreviewTemplate({"content": "any", "template_type": "sms"})
def get_template(
@@ -16,16 +18,16 @@ def get_template(
email_reply_to=None,
sms_sender=None,
):
if 'email' == template['template_type']:
if "email" == template["template_type"]:
return EmailPreviewTemplate(
template,
from_name=service.name,
from_address='{}@notifications.service.gov.uk'.format(service.email_from),
from_address="{}@notifications.service.gov.uk".format(service.email_from),
show_recipient=show_recipient,
redact_missing_personalisation=redact_missing_personalisation,
reply_to=email_reply_to,
)
if 'sms' == template['template_type']:
if "sms" == template["template_type"]:
return SMSPreviewTemplate(
template,
prefix=service.name,

View File

@@ -6,8 +6,8 @@ from dateutil import parser
def get_current_financial_year():
now = datetime.now(pytz.utc)
current_month = int(now.strftime('%-m'))
current_year = int(now.strftime('%Y'))
current_month = int(now.strftime("%-m"))
current_year = int(now.strftime("%Y"))
return current_year if current_month > 9 else current_year - 1

View File

@@ -18,7 +18,9 @@ def user_has_permissions(*permissions, **permission_kwargs):
if not current_user.has_permissions(*permissions, **permission_kwargs):
abort(403)
return func(*args, **kwargs)
return wrap_func
return wrap
@@ -30,6 +32,7 @@ def user_is_gov_user(f):
if not current_user.is_gov_user:
abort(403)
return f(*args, **kwargs)
return wrapped
@@ -41,23 +44,24 @@ def user_is_platform_admin(f):
if not current_user.platform_admin:
abort(403)
return f(*args, **kwargs)
return wrapped
def is_gov_user(email_address):
return _email_address_ends_with(
email_address, config.Config.GOVERNMENT_EMAIL_DOMAIN_NAMES
) or _email_address_ends_with(
email_address, organizations_client.get_domains()
)
) or _email_address_ends_with(email_address, organizations_client.get_domains())
def _email_address_ends_with(email_address, known_domains):
return any(
email_address.lower().endswith((
"@{}".format(known),
".{}".format(known),
))
email_address.lower().endswith(
(
"@{}".format(known),
".{}".format(known),
)
)
for known in known_domains
)

View File

@@ -2,22 +2,22 @@ from itertools import chain
permission_mappings = {
# TODO: consider turning off email-sending permissions during SMS pilot
'send_messages': ['send_texts', 'send_emails'],
'manage_templates': ['manage_templates'],
'manage_service': ['manage_users', 'manage_settings'],
'manage_api_keys': ['manage_api_keys'],
'view_activity': ['view_activity'],
"send_messages": ["send_texts", "send_emails"],
"manage_templates": ["manage_templates"],
"manage_service": ["manage_users", "manage_settings"],
"manage_api_keys": ["manage_api_keys"],
"view_activity": ["view_activity"],
}
all_ui_permissions = set(permission_mappings.keys())
all_db_permissions = set(chain(*permission_mappings.values()))
permission_options = (
('view_activity', 'See dashboard'),
('send_messages', 'Send messages'),
('manage_templates', 'Add and edit templates'),
('manage_service', 'Manage settings, team and usage'),
('manage_api_keys', 'Manage API integration'),
("view_activity", "See dashboard"),
("send_messages", "Send messages"),
("manage_templates", "Add and edit templates"),
("manage_service", "Manage settings, team and usage"),
("manage_api_keys", "Manage API integration"),
)
@@ -31,7 +31,8 @@ def translate_permissions_from_db_to_ui(db_permissions):
unknown_database_permissions = set(db_permissions) - all_db_permissions
return {
ui_permission for ui_permission, db_permissions_for_ui_permission in permission_mappings.items()
ui_permission
for ui_permission, db_permissions_for_ui_permission in permission_mappings.items()
if set(db_permissions_for_ui_permission) <= set(db_permissions)
} | unknown_database_permissions
@@ -42,6 +43,9 @@ def translate_permissions_from_ui_to_db(ui_permissions):
Looks them up in the mapping, falling back to just passing through if they're not recognised.
"""
return set(chain.from_iterable(
permission_mappings.get(ui_permission, [ui_permission]) for ui_permission in ui_permissions
))
return set(
chain.from_iterable(
permission_mappings.get(ui_permission, [ui_permission])
for ui_permission in ui_permissions
)
)