Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2205 - Comp and Pen Batches #2206

Merged
merged 17 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions app/celery/process_comp_and_pen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
from dataclasses import dataclass
from uuid import uuid4

from flask import current_app
from sqlalchemy.orm.exc import NoResultFound

from notifications_utils.statsd_decorators import statsd

from app import notify_celery
from app.dao.service_sms_sender_dao import dao_get_service_sms_sender_by_id
from app.models import (
Service,
Template,
)
from app.notifications.send_notifications import lookup_notification_sms_setup_data, send_notification_bypass_route
from app.va.identifier import IdentifierType


@dataclass
class DynamoRecord:
participant_id: str
payment_amount: str
vaprofile_id: str


@notify_celery.task(name='comp-and-pen-batch-process')
@statsd(namespace='tasks')
def comp_and_pen_batch_process(records: list[dict[str, str]]) -> None:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we want to leave a comment about this being triggered by a Glue Script?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't do that with other code. I don't believe that's necessary here. I do need to update some of our other docs though.

"""Process batches of Comp and Pen notification requests.

Args:
records (list[dict[str, str]]): The incoming records
"""
current_app.logger.debug(f'comp_and_pen_batch_process records: {records}')

# Grab all the necessary data
try:
service, template, sms_sender_id = lookup_notification_sms_setup_data(
current_app.config['COMP_AND_PEN_SERVICE_ID'],
current_app.config['COMP_AND_PEN_TEMPLATE_ID'],
current_app.config['COMP_AND_PEN_SMS_SENDER_ID'],
)
reply_to_text = dao_get_service_sms_sender_by_id(service.id, sms_sender_id).sms_sender
except (AttributeError, NoResultFound, ValueError):
current_app.logger.exception('Unable to send comp and pen notifications due to improper configuration')
raise

_send_comp_and_pen_sms(
service,
template,
sms_sender_id,
reply_to_text,
[DynamoRecord(**item) for item in records],

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitty and no fix required.

Double iteration - We're iterating over all the records to convert them in a DynamoRecord object. Then in _send_comp_and_pen_sms we are iterating over thru all the records again. Could we do both these things in the same iteration?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good callout. I had gone back and forth on creating the dataclass at each iteration vs in a single call. I opted to go this route for simplicity in the downstream method since the other methods acts as a "pre-processor".

current_app.config['COMP_AND_PEN_PERF_TO_NUMBER'],
)


def _send_comp_and_pen_sms(
service: Service,
template: Template,
sms_sender_id: str,
reply_to_text: str,
comp_and_pen_messages: list[DynamoRecord],
perf_to_number: str,
) -> None:
"""
Sends scheduled SMS notifications to recipients based on the provided parameters.

Args:
:param service (Service): The service used to send the SMS notifications.
:param template (Template): The template used for the SMS notifications.
:param sms_sender_id (str): The ID of the SMS sender.
:param reply_to_text (str): The text a Veteran can reply to.
:param comp_and_pen_messages (list[DynamoRecord]): A list of DynamoRecord from the dynamodb table containing
the details needed to send the messages.
:param perf_to_number (str): The recipient's phone number.

Raises:
Exception: If there is an error while sending the SMS notification.
"""

for item in comp_and_pen_messages:
current_app.logger.debug('sending - record from dynamodb: %s', item.participant_id)

# Use perf_to_number as the recipient if available. Otherwise, use vaprofile_id as recipient_item.
recipient_item = (
None
if perf_to_number is not None
else {
'id_type': IdentifierType.VA_PROFILE_ID.value,
'id_value': item.vaprofile_id,
}
)

try:
# call generic method to send messages
send_notification_bypass_route(
service=service,
template=template,
reply_to_text=reply_to_text,
personalisation={'amount': item.payment_amount},
sms_sender_id=sms_sender_id,
recipient=perf_to_number,
recipient_item=recipient_item,
notification_id=uuid4(),
)
except Exception:
current_app.logger.exception(
'Error attempting to send Comp and Pen notification with '
'send_comp_and_pen_sms | record from dynamodb: %s',
item.participant_id,
)
else:
if perf_to_number is not None:
current_app.logger.info(
'Notification sent using Perf simulated number %s instead of vaprofile_id', perf_to_number
)

current_app.logger.info('Notification sent to queue for record from dynamodb: %s', item.participant_id)
69 changes: 0 additions & 69 deletions app/celery/scheduled_tasks.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from datetime import datetime, timedelta
from time import monotonic

from botocore.exceptions import ClientError
from flask import current_app
from notifications_utils.statsd_decorators import statsd
from sqlalchemy import and_, select
Expand All @@ -28,11 +26,8 @@
dao_old_letters_with_created_status,
)
from app.dao.users_dao import delete_codes_older_created_more_than_a_day_ago
from app.feature_flags import is_feature_enabled, FeatureFlag
from app.integrations.comp_and_pen.scheduled_message_helpers import CompPenMsgHelper
from app.models import Job
from app.notifications.process_notifications import send_notification_to_queue
from app.notifications.send_notifications import lookup_notification_sms_setup_data
from app.v2.errors import JobIncompleteError


Expand Down Expand Up @@ -196,67 +191,3 @@ def check_templated_letter_state():
message=msg,
ticket_type=zendesk_client.TYPE_INCIDENT,
)


@notify_celery.task(name='send-scheduled-comp-and-pen-sms')
@statsd(namespace='tasks')
def send_scheduled_comp_and_pen_sms() -> None:
start_time = monotonic()
# this is the agreed upon message per 1 minute limit
messages_per_min = 90

# get config info
dynamodb_table_name = current_app.config['COMP_AND_PEN_DYNAMODB_TABLE_NAME']
service_id = current_app.config['COMP_AND_PEN_SERVICE_ID']
template_id = current_app.config['COMP_AND_PEN_TEMPLATE_ID']
sms_sender_id = current_app.config['COMP_AND_PEN_SMS_SENDER_ID']
# Perf uses the AWS simulated delivered number
perf_to_number = current_app.config['COMP_AND_PEN_PERF_TO_NUMBER']

comp_pen_helper = CompPenMsgHelper(dynamodb_table_name=dynamodb_table_name)

current_app.logger.debug('send_scheduled_comp_and_pen_sms connecting to dynamodb...')
try:
comp_pen_helper._connect_to_dynamodb()
except ClientError as e:
current_app.logger.critical(
'Unable to connect to dynamodb table with name %s - exception: %s', dynamodb_table_name, e
)
raise

current_app.logger.debug('... connected to dynamodb in send_scheduled_comp_and_pen_sms')
current_app.logger.info('dynamo connection took: %s seconds', monotonic() - start_time)

# get messages to send
try:
comp_and_pen_messages: list = comp_pen_helper.get_dynamodb_comp_pen_messages(messages_per_min)
except Exception as e:
current_app.logger.critical(
'Exception trying to scan dynamodb table for send_scheduled_comp_and_pen_sms exception_type: %s - '
'exception_message: %s',
type(e),
e,
)
raise

current_app.logger.debug('send_scheduled_comp_and_pen_sms list of items from dynamodb: %s', comp_and_pen_messages)

# only continue if there are messages to update and send
if comp_and_pen_messages:
comp_pen_helper.remove_dynamo_item_is_processed(comp_and_pen_messages)

if is_feature_enabled(FeatureFlag.COMP_AND_PEN_MESSAGES_ENABLED):
# get the data necessary to send the notifications
service, template, sms_sender_id = lookup_notification_sms_setup_data(
service_id, template_id, sms_sender_id
)

comp_pen_helper.send_comp_and_pen_sms(
service=service,
template=template,
sms_sender_id=sms_sender_id,
comp_and_pen_messages=comp_and_pen_messages,
perf_to_number=perf_to_number,
)
else:
current_app.logger.info('Comp and Pen Notifications not sent to queue (feature flag disabled)')
7 changes: 1 addition & 6 deletions app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ class Config(object):
'task_serializer': 'json',
'imports': (
'app.celery.tasks',
'app.celery.process_comp_and_pen',
'app.celery.scheduled_tasks',
'app.celery.reporting_tasks',
'app.celery.nightly_tasks',
Expand Down Expand Up @@ -316,12 +317,6 @@ class Config(object):
'schedule': crontab(hour=4, minute=0),
'options': {'queue': QueueNames.PERIODIC},
},
'send-scheduled-comp-and-pen-sms': {
'task': 'send-scheduled-comp-and-pen-sms',
# Every 1 minute past every hour from 13 through 21 on every day-of-month from 22 through end-of-month
'schedule': crontab(hour='13-21', day_of_month='23-31', minute='*/1'),
'options': {'queue': QueueNames.PERIODIC},
},
'update-twilio-status': {
'task': 'update-twilio-status',
'schedule': crontab(hour='*', minute='*/5'),
Expand Down
1 change: 0 additions & 1 deletion app/feature_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ class FeatureFlag(Enum):
PLATFORM_STATS_ENABLED = 'PLATFORM_STATS_ENABLED'
VA_SSO_ENABLED = 'VA_SSO_ENABLED'
V3_ENABLED = 'V3_ENABLED'
COMP_AND_PEN_MESSAGES_ENABLED = 'COMP_AND_PEN_MESSAGES_ENABLED'
VA_PROFILE_SMS_STATUS_ENABLED = 'VA_PROFILE_SMS_STATUS_ENABLED'


Expand Down
Loading
Loading