mirror of
https://github.com/GSA/notifications-api.git
synced 2025-12-20 15:31:15 -05:00
Merge pull request #659 from GSA/ADMIN-97_Resend_Expired_Invites
Admin 97 resend expired invites
This commit is contained in:
@@ -117,9 +117,7 @@ def purge_functional_test_data(user_email_prefix):
|
||||
current_app.logger.error("Can only be run in development")
|
||||
return
|
||||
|
||||
users = User.query.filter(
|
||||
User.email_address.like("{}%".format(user_email_prefix))
|
||||
).all()
|
||||
users = User.query.filter(User.email_address.like(f"{user_email_prefix}%")).all()
|
||||
for usr in users:
|
||||
# Make sure the full email includes a uuid in it
|
||||
# Just in case someone decides to use a similar email address.
|
||||
@@ -127,9 +125,7 @@ def purge_functional_test_data(user_email_prefix):
|
||||
uuid.UUID(usr.email_address.split("@")[0].split("+")[1])
|
||||
except ValueError:
|
||||
print(
|
||||
"Skipping {} as the user email doesn't contain a UUID.".format(
|
||||
usr.email_address
|
||||
)
|
||||
f"Skipping {usr.email_address} as the user email doesn't contain a UUID."
|
||||
)
|
||||
else:
|
||||
services = dao_fetch_all_services_by_user(usr.id)
|
||||
@@ -164,7 +160,7 @@ def purge_functional_test_data(user_email_prefix):
|
||||
def insert_inbound_numbers_from_file(file_name):
|
||||
# TODO maintainability what is the purpose of this command? Who would use it and why?
|
||||
|
||||
print("Inserting inbound numbers from {}".format(file_name))
|
||||
print(f"Inserting inbound numbers from {file_name}")
|
||||
with open(file_name) as file:
|
||||
sql = "insert into inbound_numbers values('{}', '{}', 'sns', null, True, now(), null);"
|
||||
|
||||
@@ -199,9 +195,7 @@ def rebuild_ft_billing_for_day(service_id, day):
|
||||
def rebuild_ft_data(process_day, service):
|
||||
deleted_rows = delete_billing_data_for_service_for_day(process_day, service)
|
||||
current_app.logger.info(
|
||||
"deleted {} existing billing rows for {} on {}".format(
|
||||
deleted_rows, service, process_day
|
||||
)
|
||||
f"deleted {deleted_rows} existing billing rows for {service} on {process_day}"
|
||||
)
|
||||
transit_data = fetch_billing_data_for_day(
|
||||
process_day=process_day, service_id=service
|
||||
@@ -211,9 +205,7 @@ def rebuild_ft_billing_for_day(service_id, day):
|
||||
# upsert existing rows
|
||||
update_fact_billing(data, process_day)
|
||||
current_app.logger.info(
|
||||
"added/updated {} billing rows for {} on {}".format(
|
||||
len(transit_data), service, process_day
|
||||
)
|
||||
f"added/updated {len(transit_data)} billing rows for {service} on {process_day}"
|
||||
)
|
||||
|
||||
if service_id:
|
||||
@@ -276,7 +268,7 @@ def bulk_invite_user_to_service(file_name, service_id, user_id, auth_type, permi
|
||||
}
|
||||
current_app.logger.info(f"DATA = {data}")
|
||||
with current_app.test_request_context(
|
||||
path="/service/{}/invite/".format(service_id),
|
||||
path=f"/service/{service_id}/invite/",
|
||||
method="POST",
|
||||
data=json.dumps(data),
|
||||
headers={"Content-Type": "application/json"},
|
||||
@@ -286,16 +278,12 @@ def bulk_invite_user_to_service(file_name, service_id, user_id, auth_type, permi
|
||||
current_app.logger.info(f"RESPONSE {response[1]}")
|
||||
if response[1] != 201:
|
||||
print(
|
||||
"*** ERROR occurred for email address: {}".format(
|
||||
email_address.strip()
|
||||
)
|
||||
f"*** ERROR occurred for email address: {email_address.strip()}"
|
||||
)
|
||||
print(response[0].get_data(as_text=True))
|
||||
except Exception as e:
|
||||
print(
|
||||
"*** ERROR occurred for email address: {}. \n{}".format(
|
||||
email_address.strip(), e
|
||||
)
|
||||
f"*** ERROR occurred for email address: {email_address.strip()}. \n{e}"
|
||||
)
|
||||
|
||||
file.close()
|
||||
@@ -318,7 +306,7 @@ def bulk_invite_user_to_service(file_name, service_id, user_id, auth_type, permi
|
||||
)
|
||||
def update_jobs_archived_flag(start_date, end_date):
|
||||
current_app.logger.info(
|
||||
"Archiving jobs created between {} to {}".format(start_date, end_date)
|
||||
f"Archiving jobs created between {start_date} to {end_date}"
|
||||
)
|
||||
|
||||
process_date = start_date
|
||||
@@ -331,21 +319,20 @@ def update_jobs_archived_flag(start_date, end_date):
|
||||
where
|
||||
created_at >= (date :start + time '00:00:00')
|
||||
and created_at < (date :end + time '00:00:00')
|
||||
"""
|
||||
"""
|
||||
result = db.session.execute(
|
||||
sql, {"start": process_date, "end": process_date + timedelta(days=1)}
|
||||
)
|
||||
db.session.commit()
|
||||
current_app.logger.info(
|
||||
"jobs: --- Completed took {}ms. Archived {} jobs for {}".format(
|
||||
datetime.now() - start_time, result.rowcount, process_date
|
||||
)
|
||||
f"jobs: --- Completed took {datetime.now() - start_time}ms. Archived "
|
||||
f"{result.rowcount} jobs for {process_date}"
|
||||
)
|
||||
|
||||
process_date += timedelta(days=1)
|
||||
|
||||
total_updated += result.rowcount
|
||||
current_app.logger.info("Total archived jobs = {}".format(total_updated))
|
||||
current_app.logger.info(f"Total archived jobs = {total_updated}")
|
||||
|
||||
|
||||
@notify_command(name="populate-organizations-from-file")
|
||||
@@ -551,9 +538,7 @@ def fix_billable_units():
|
||||
show_prefix=notification.service.prefix_sms,
|
||||
)
|
||||
print(
|
||||
"Updating notification: {} with {} billable_units".format(
|
||||
notification.id, template.fragment_count
|
||||
)
|
||||
f"Updating notification: {notification.id} with {template.fragment_count} billable_units"
|
||||
)
|
||||
|
||||
Notification.query.filter(Notification.id == notification.id).update(
|
||||
@@ -586,9 +571,7 @@ def process_row_from_job(job_id, job_row_number):
|
||||
if row.index == job_row_number:
|
||||
notification_id = process_row(row, template, job, job.service)
|
||||
current_app.logger.info(
|
||||
"Process row {} for job {} created notification_id: {}".format(
|
||||
job_row_number, job_id, notification_id
|
||||
)
|
||||
f"Process row {job_row_number} for job {job_id} created notification_id: {notification_id}"
|
||||
)
|
||||
|
||||
|
||||
@@ -623,9 +606,7 @@ def populate_annual_billing_with_the_previous_years_allowance(year):
|
||||
latest_annual_billing, {"service_id": row.id}
|
||||
)
|
||||
free_allowance = [x[0] for x in free_allowance_rows]
|
||||
print(
|
||||
"create free limit of {} for service: {}".format(free_allowance[0], row.id)
|
||||
)
|
||||
print(f"create free limit of {free_allowance[0]} for service: {row.id}")
|
||||
dao_create_or_update_annual_billing_for_year(
|
||||
service_id=row.id,
|
||||
free_sms_fragment_limit=free_allowance[0],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from app import db
|
||||
from app.models import INVITE_EXPIRED, InvitedUser
|
||||
from app.models import INVITE_EXPIRED, INVITE_PENDING, InvitedUser
|
||||
|
||||
|
||||
def save_invited_user(invited_user):
|
||||
@@ -13,32 +13,36 @@ def get_invited_user_by_service_and_id(service_id, invited_user_id):
|
||||
return InvitedUser.query.filter(
|
||||
InvitedUser.service_id == service_id,
|
||||
InvitedUser.id == invited_user_id,
|
||||
InvitedUser.status != INVITE_EXPIRED,
|
||||
).one()
|
||||
|
||||
|
||||
def get_expired_invite_by_service_and_id(service_id, invited_user_id):
|
||||
return InvitedUser.query.filter(
|
||||
InvitedUser.service_id == service_id,
|
||||
InvitedUser.id == invited_user_id,
|
||||
InvitedUser.status == INVITE_EXPIRED,
|
||||
).one()
|
||||
|
||||
|
||||
def get_invited_user_by_id(invited_user_id):
|
||||
return InvitedUser.query.filter(
|
||||
InvitedUser.id == invited_user_id, InvitedUser.status != INVITE_EXPIRED
|
||||
).one()
|
||||
return InvitedUser.query.filter(InvitedUser.id == invited_user_id).one()
|
||||
|
||||
|
||||
def get_expired_invited_users_for_service(service_id):
|
||||
return InvitedUser.query.filter(
|
||||
InvitedUser.service_id == service_id, InvitedUser.status == INVITE_EXPIRED
|
||||
).all()
|
||||
return InvitedUser.query.filter(InvitedUser.service_id == service_id).all()
|
||||
|
||||
|
||||
def get_invited_users_for_service(service_id):
|
||||
return InvitedUser.query.filter(
|
||||
InvitedUser.service_id == service_id, InvitedUser.status != INVITE_EXPIRED
|
||||
).all()
|
||||
return InvitedUser.query.filter(InvitedUser.service_id == service_id).all()
|
||||
|
||||
|
||||
def expire_invitations_created_more_than_two_days_ago():
|
||||
expired = (
|
||||
db.session.query(InvitedUser)
|
||||
.filter(InvitedUser.created_at <= datetime.utcnow() - timedelta(days=2))
|
||||
.filter(
|
||||
InvitedUser.created_at <= datetime.utcnow() - timedelta(days=2),
|
||||
InvitedUser.status.in_((INVITE_PENDING,)),
|
||||
)
|
||||
.update({InvitedUser.status: INVITE_EXPIRED})
|
||||
)
|
||||
db.session.commit()
|
||||
|
||||
@@ -87,9 +87,7 @@ def persist_notification(
|
||||
if not notification_id:
|
||||
notification_id = uuid.uuid4()
|
||||
|
||||
current_app.logger.info(
|
||||
"Persisting notification with id {}".format(notification_id)
|
||||
)
|
||||
current_app.logger.info(f"Persisting notification with id {notification_id}")
|
||||
|
||||
notification = Notification(
|
||||
id=notification_id,
|
||||
@@ -124,9 +122,7 @@ def persist_notification(
|
||||
notification.phone_prefix = recipient_info.country_prefix
|
||||
notification.rate_multiplier = recipient_info.billable_units
|
||||
elif notification_type == EMAIL_TYPE:
|
||||
current_app.logger.info(
|
||||
"Persisting notification with type: {}".format(EMAIL_TYPE)
|
||||
)
|
||||
current_app.logger.info(f"Persisting notification with type: {EMAIL_TYPE}")
|
||||
notification.normalised_to = format_email_address(notification.to)
|
||||
|
||||
# if simulated create a Notification model to return but do not persist the Notification to the dB
|
||||
@@ -141,9 +137,7 @@ def persist_notification(
|
||||
)
|
||||
|
||||
current_app.logger.info(
|
||||
"{} {} created at {}".format(
|
||||
notification_type, notification_id, notification_created_at
|
||||
)
|
||||
f"{notification_type} {notification_id} created at {notification_created_at}"
|
||||
)
|
||||
return notification
|
||||
|
||||
@@ -170,15 +164,16 @@ def send_notification_to_queue_detached(
|
||||
raise
|
||||
|
||||
current_app.logger.debug(
|
||||
"{} {} sent to the {} queue for delivery".format(
|
||||
notification_type, notification_id, queue
|
||||
)
|
||||
f"{notification_type} {notification_id} sent to the {queue} queue for delivery"
|
||||
)
|
||||
|
||||
|
||||
def send_notification_to_queue(notification, queue=None):
|
||||
send_notification_to_queue_detached(
|
||||
notification.key_type, notification.notification_type, notification.id, queue
|
||||
notification.key_type,
|
||||
notification.notification_type,
|
||||
notification.id,
|
||||
queue,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
from datetime import datetime
|
||||
|
||||
from flask import Blueprint, current_app, jsonify, request
|
||||
from itsdangerous import BadData, SignatureExpired
|
||||
from notifications_utils.url_safe_token import check_token, generate_token
|
||||
|
||||
from app.config import QueueNames
|
||||
from app.dao.invited_user_dao import (
|
||||
get_expired_invite_by_service_and_id,
|
||||
get_expired_invited_users_for_service,
|
||||
get_invited_user_by_id,
|
||||
get_invited_user_by_service_and_id,
|
||||
@@ -12,7 +15,7 @@ from app.dao.invited_user_dao import (
|
||||
)
|
||||
from app.dao.templates_dao import dao_get_template_by_id
|
||||
from app.errors import InvalidRequest, register_errors
|
||||
from app.models import EMAIL_TYPE, KEY_TYPE_NORMAL, Service
|
||||
from app.models import EMAIL_TYPE, INVITE_PENDING, KEY_TYPE_NORMAL, Service
|
||||
from app.notifications.process_notifications import (
|
||||
persist_notification,
|
||||
send_notification_to_queue,
|
||||
@@ -24,15 +27,11 @@ service_invite = Blueprint("service_invite", __name__)
|
||||
register_errors(service_invite)
|
||||
|
||||
|
||||
@service_invite.route("/service/<service_id>/invite", methods=["POST"])
|
||||
def create_invited_user(service_id):
|
||||
request_json = request.get_json()
|
||||
invited_user = invited_user_schema.load(request_json)
|
||||
save_invited_user(invited_user)
|
||||
|
||||
def _create_service_invite(invited_user, invite_link_host):
|
||||
template_id = current_app.config["INVITATION_EMAIL_TEMPLATE_ID"]
|
||||
|
||||
template = dao_get_template_by_id(template_id)
|
||||
|
||||
service = Service.query.get(current_app.config["NOTIFY_SERVICE_ID"])
|
||||
|
||||
saved_notification = persist_notification(
|
||||
@@ -43,10 +42,7 @@ def create_invited_user(service_id):
|
||||
personalisation={
|
||||
"user_name": invited_user.from_user.name,
|
||||
"service_name": invited_user.service.name,
|
||||
"url": invited_user_url(
|
||||
invited_user.id,
|
||||
request_json.get("invite_link_host"),
|
||||
),
|
||||
"url": invited_user_url(invited_user.id, invite_link_host),
|
||||
},
|
||||
notification_type=EMAIL_TYPE,
|
||||
api_key_id=None,
|
||||
@@ -56,6 +52,15 @@ def create_invited_user(service_id):
|
||||
|
||||
send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY)
|
||||
|
||||
|
||||
@service_invite.route("/service/<service_id>/invite", methods=["POST"])
|
||||
def create_invited_user(service_id):
|
||||
request_json = request.get_json()
|
||||
invited_user = invited_user_schema.load(request_json)
|
||||
save_invited_user(invited_user)
|
||||
|
||||
_create_service_invite(invited_user, request_json.get("invite_link_host"))
|
||||
|
||||
return jsonify(data=invited_user_schema.dump(invited_user)), 201
|
||||
|
||||
|
||||
@@ -92,6 +97,36 @@ def update_invited_user(service_id, invited_user_id):
|
||||
return jsonify(data=invited_user_schema.dump(fetched)), 200
|
||||
|
||||
|
||||
@service_invite.route(
|
||||
"/service/<service_id>/invite/<invited_user_id>/resend", methods=["POST"]
|
||||
)
|
||||
def resend_service_invite(service_id, invited_user_id):
|
||||
"""Resend an expired invite.
|
||||
|
||||
This resets the invited user's created date and status to make it a new invite, and
|
||||
sends the new invite out to the user.
|
||||
|
||||
Note:
|
||||
This ignores the POST data entirely.
|
||||
"""
|
||||
fetched = get_expired_invite_by_service_and_id(
|
||||
service_id=service_id,
|
||||
invited_user_id=invited_user_id,
|
||||
)
|
||||
|
||||
fetched.created_at = datetime.utcnow()
|
||||
fetched.status = INVITE_PENDING
|
||||
|
||||
current_data = {k: v for k, v in invited_user_schema.dump(fetched).items()}
|
||||
update_dict = invited_user_schema.load(current_data)
|
||||
|
||||
save_invited_user(update_dict)
|
||||
|
||||
_create_service_invite(fetched, current_app.config["ADMIN_BASE_URL"])
|
||||
|
||||
return jsonify(data=invited_user_schema.dump(fetched)), 200
|
||||
|
||||
|
||||
def invited_user_url(invited_user_id, invite_link_host=None):
|
||||
token = generate_token(
|
||||
str(invited_user_id),
|
||||
|
||||
Reference in New Issue
Block a user