Skip to content

Commit

Permalink
chore: resolve conflicts and fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
navinkarkera committed May 7, 2024
1 parent 139b4be commit 5775a20
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 170 deletions.
91 changes: 18 additions & 73 deletions license_manager/apps/api/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from license_manager.apps.api_client.mailchimp import (
MailchimpTransactionalApiClient,
)
from license_manager.apps.subscriptions.constants import REMIND_EMAIL_ACTION_TYPE
from license_manager.apps.subscriptions.event_utils import (
get_license_tracking_properties,
)
Expand All @@ -30,78 +31,16 @@ def __init__(self) -> None:
else:
raise ValueError("Please set TRANSACTIONAL_MAIL_SERVICE setting to either 'braze' or 'mailchimp'.")

def send_assignment_email(self, pending_licenses, enterprise_customer, custom_template_text):
"""Helper function to send assignment email.
Args:
pending_licenses (list[License]): List of pending license objects
enterprise_customer (dict): enterprise customer information
custom_template_text (dict): Dictionary containing `greeting` and `closing` keys to be used for customizing
the email template.
Returns:
dict of pending license by email
"""
enterprise_slug = enterprise_customer.get('slug')
enterprise_name = enterprise_customer.get('name')
enterprise_sender_alias = get_enterprise_sender_alias(enterprise_customer)
enterprise_contact_email = enterprise_customer.get('contact_email')
pending_license_by_email = {}
# We need to send these emails individually, because each email's text must be
# generated for every single user/activation_key
for pending_license in pending_licenses:
user_email = pending_license.user_email
pending_license_by_email[user_email] = pending_license
license_activation_key = str(pending_license.activation_key)
if settings.TRANSACTIONAL_MAIL_SERVICE == 'braze':
template_context = {
'TEMPLATE_GREETING': custom_template_text['greeting'],
'TEMPLATE_CLOSING': custom_template_text['closing'],
'license_activation_key': license_activation_key,
'enterprise_customer_slug': enterprise_slug,
'enterprise_customer_name': enterprise_name,
'enterprise_sender_alias': enterprise_sender_alias,
'enterprise_contact_email': enterprise_contact_email,
}
self._braze_client.send_single_email(
template_context,
user_email,
braze_campaign_id=settings.BRAZE_ASSIGNMENT_EMAIL_CAMPAIGN,
err_message=(
'License manager activation email '
f'sending received an exception for enterprise: {enterprise_name}.'
),
)
elif settings.TRANSACTIONAL_MAIL_SERVICE == 'mailchimp':
template_context = [
{'name': 'TEMPLATE_GREETING', 'content': custom_template_text['greeting']},
{'name': 'TEMPLATE_CLOSING', 'content': custom_template_text['closing']},
{'name': 'license_activation_key', 'content': license_activation_key},
{'name': 'enterprise_customer_slug', 'content': enterprise_slug},
{'name': 'enterprise_customer_name', 'content': enterprise_name},
{'name': 'enterprise_sender_alias', 'content': enterprise_sender_alias},
{'name': 'enterprise_contact_email', 'content': enterprise_contact_email},
]
self._mailchimp_client.send_single_email(
template_context,
user_email,
template_slug=settings.MAILCHIMP_ASSIGNMENT_EMAIL_TEMPLATE,
subject=settings.MAILCHIMP_ASSIGNMENT_EMAIL_SUBJECT,
err_message=(
'License manager activation email '
f'sending received an exception for enterprise: {enterprise_name}.'
),
)
return pending_license_by_email

def send_reminder_email(self, pending_licenses, enterprise_customer, custom_template_text):
"""Helper function to send reminder email.
def send_assignment_or_reminder_email(self, pending_licenses, enterprise_customer, custom_template_text, action_type):
"""Helper function to send a assignment notification or reminder email.
Args:
pending_licenses (list[License]): List of pending license objects
enterprise_customer (dict): enterprise customer information
custom_template_text (dict): Dictionary containing `greeting` and `closing` keys to be used for customizing
the email template.
action_type (str): A string used in logging messages to indicate which type of notification
is being sent and determine template for email.
Returns:
dict of pending license by email
"""
Expand Down Expand Up @@ -151,30 +90,36 @@ def send_reminder_email(self, pending_licenses, enterprise_customer, custom_temp
}
)
if settings.TRANSACTIONAL_MAIL_SERVICE == 'braze':
campaign_id = settings.BRAZE_REMIND_EMAIL_CAMPAIGN
campaign_id = settings.BRAZE_REMIND_EMAIL_CAMPAIGN if action_type == REMIND_EMAIL_ACTION_TYPE else settings.BRAZE_ASSIGNMENT_EMAIL_CAMPAIGN
self._braze_client.send_emails(
campaign_id,
recipients=messages,
user_emails=user_emails,
success_msg=(
f'{LICENSE_DEBUG_PREFIX} Sent license reminder emails '
f'{LICENSE_DEBUG_PREFIX} Sent license {action_type} emails '
f'braze campaign {campaign_id} to {user_emails}'
),
err_msg=(f'Error hitting Braze API reminder email to {campaign_id} for license failed.'),
err_msg=(f'Error hitting Braze API {action_type} email to {campaign_id} for license failed.'),
)
elif settings.TRANSACTIONAL_MAIL_SERVICE == 'mailchimp':
template_name = settings.MAILCHIMP_REMINDER_EMAIL_TEMPLATE
if action_type == REMIND_EMAIL_ACTION_TYPE:
template_name = settings.MAILCHIMP_REMINDER_EMAIL_TEMPLATE
subject = settings.MAILCHIMP_REMINDER_EMAIL_TEMPLATE
else:
template_name = settings.MAILCHIMP_ASSIGNMENT_EMAIL_TEMPLATE
subject = settings.MAILCHIMP_ASSIGNMENT_EMAIL_SUBJECT

self._mailchimp_client.send_emails(
template_name,
merge_vars=messages,
to_users=user_emails,
subject=settings.MAILCHIMP_ASSIGNMENT_EMAIL_SUBJECT,
subject=subject,
recipient_metadata=recipient_metadata,
success_msg=(
f'{LICENSE_DEBUG_PREFIX} Sent license assignment email '
f'{LICENSE_DEBUG_PREFIX} Sent license {action_type} emails '
f'mailchimp template {template_name} to {user_emails}'
),
err_msg=(f'Error hitting Mailchimp API reminder email to {template_name} for license failed.'),
err_msg=(f'Error hitting Mailchimp API {action_type} email to {template_name} for license failed.'),
)
return pending_license_by_email

Expand Down
97 changes: 10 additions & 87 deletions license_manager/apps/api/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
REMINDER_EMAIL_BATCH_SIZE,
REVOCABLE_LICENSE_STATUSES,
TRACK_LICENSE_CHANGES_BATCH_SIZE,
NOTIFY_EMAIL_ACTION_TYPE,
REMIND_EMAIL_ACTION_TYPE,
NotificationChoices,
)
from license_manager.apps.subscriptions.event_utils import track_license_changes
Expand All @@ -54,10 +56,6 @@

LICENSE_DEBUG_PREFIX = '[LICENSE DEBUGGING]'

# Magic strings for logging in notify/remind email tasks
NOTIFY_EMAIL_ACTION_TYPE = 'notify'
REMIND_EMAIL_ACTION_TYPE = 'remind'


class LoggedTaskWithRetry(LoggedTask): # pylint: disable=abstract-method
"""
Expand Down Expand Up @@ -106,38 +104,6 @@ def create_braze_aliases_task(user_emails):
raise exc


@shared_task(base=LoggedTaskWithRetry, soft_time_limit=SOFT_TIME_LIMIT, time_limit=MAX_TIME_LIMIT)
def send_assignment_email_task(custom_template_text, email_recipient_list, subscription_uuid):
"""
Sends license assignment email(s) asynchronously.
Arguments:
custom_template_text (dict): Dictionary containing `greeting` and `closing` keys to be used for customizing
the email template.
email_recipient_list (list of str): List of recipients to send the emails to.
subscription_uuid (str): UUID (string representation) of the subscription that the recipients are associated
with or will be associated with.
"""
subscription_plan = SubscriptionPlan.objects.get(uuid=subscription_uuid)
pending_licenses = subscription_plan.licenses.filter(user_email__in=email_recipient_list).order_by('uuid')
enterprise_api_client = EnterpriseApiClient()
enterprise_customer = enterprise_api_client.get_enterprise_customer_data(subscription_plan.enterprise_customer_uuid)

email_client = EmailClient()
pending_license_by_email = email_client.send_assignment_email(
pending_licenses,
enterprise_customer,
custom_template_text
)

emails_with_no_assignments = [
_email for _email in email_recipient_list
if _email not in pending_license_by_email
]
if emails_with_no_assignments:
logger.warning(f'{LICENSE_DEBUG_PREFIX} No assignment email sent for {emails_with_no_assignments}')


@shared_task(base=LoggedTaskWithRetry, soft_time_limit=SOFT_TIME_LIMIT, time_limit=MAX_TIME_LIMIT)
def link_learners_to_enterprise_task(learner_emails, enterprise_customer_uuid):
"""
Expand All @@ -161,7 +127,6 @@ def _batch_notify_or_remind_assigned_emails(
email_recipient_list,
subscription_uuid,
allowed_batch_size,
campaign_uuid,
action_type,
):
"""
Expand All @@ -176,9 +141,8 @@ def _batch_notify_or_remind_assigned_emails(
recipients are (or will be) associated with.
allowed_batch_size (int): Maximum number of recipients (really their associated assigned licenses)
which can be processed in a single call to this method.
campaign_uuid (str): The identifier of the Braze email campaign via which users are notified.
action_type (str): A string used in logging messages to indicate which type of notification
is being sent.
is being sent and determine template for email.
"""
subscription_plan = SubscriptionPlan.objects.get(uuid=subscription_uuid)
pending_licenses = subscription_plan.assigned_licenses.filter(
Expand All @@ -192,53 +156,14 @@ def _batch_notify_or_remind_assigned_emails(
enterprise_customer = enterprise_api_client.get_enterprise_customer_data(
subscription_plan.enterprise_customer_uuid,
)
enterprise_slug = enterprise_customer.get('slug')
enterprise_name = enterprise_customer.get('name')
enterprise_sender_alias = get_enterprise_sender_alias(enterprise_customer)
enterprise_contact_email = enterprise_customer.get('contact_email')

pending_license_by_email = {}
emails_for_aliasing = []
recipients = []

for pending_license in pending_licenses:
user_email = pending_license.user_email
emails_for_aliasing.append(user_email)
pending_license_by_email[user_email] = pending_license
license_activation_key = str(pending_license.activation_key)
trigger_properties = {
'TEMPLATE_GREETING': custom_template_text['greeting'],
'TEMPLATE_CLOSING': custom_template_text['closing'],
'license_activation_key': license_activation_key,
'enterprise_customer_slug': enterprise_slug,
'enterprise_customer_name': enterprise_name,
'enterprise_sender_alias': enterprise_sender_alias,
'enterprise_contact_email': enterprise_contact_email,
}
recipient = _aliased_recipient_object_from_email(user_email)
recipient['attributes'].update(get_license_tracking_properties(pending_license))
recipient['trigger_properties'] = trigger_properties
recipients.append(recipient)

# Batch at the Braze API layer
try:
braze_client_instance = BrazeApiClient()
braze_client_instance.create_braze_alias(
emails_for_aliasing,
ENTERPRISE_BRAZE_ALIAS_LABEL,
)
braze_client_instance.send_campaign_message(campaign_uuid, recipients=recipients)
logger.info(
f'{LICENSE_DEBUG_PREFIX} Sent license {action_type} emails '
f'braze campaign {campaign_uuid} to {email_recipient_list}'
)
except BrazeClientError as exc:
message = (
'Error hitting Braze API. '
f'{action_type} email to campaign {campaign_uuid} for license failed.'
)
logger.exception(message)
raise exc
email_client = EmailClient()
pending_license_by_email = email_client.send_assignment_or_reminder_email(
pending_licenses,
enterprise_customer,
custom_template_text,
action_type,
)

emails_with_no_assignments = [
_email for _email in email_recipient_list
Expand Down Expand Up @@ -267,7 +192,6 @@ def send_assignment_email_task(custom_template_text, email_recipient_list, subsc
email_recipient_list,
subscription_uuid,
ASSIGNMENT_EMAIL_BATCH_SIZE,
settings.BRAZE_ASSIGNMENT_EMAIL_CAMPAIGN,
NOTIFY_EMAIL_ACTION_TYPE,
)

Expand All @@ -292,7 +216,6 @@ def send_reminder_email_task(custom_template_text, email_recipient_list, subscri
email_recipient_list,
subscription_uuid,
REMINDER_EMAIL_BATCH_SIZE,
settings.BRAZE_REMIND_EMAIL_CAMPAIGN,
REMIND_EMAIL_ACTION_TYPE,
)
License.set_date_fields_to_now(pending_licenses, ['last_remind_date'])
Expand Down
20 changes: 10 additions & 10 deletions license_manager/apps/api/tests/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,26 +169,26 @@ def sort_key(x):

# pylint: disable=unused-argument
@mock.patch('license_manager.apps.api_client.braze.logger', return_value=mock.MagicMock())
@mock.patch('license_manager.apps.api_client.braze.BrazeClient.send_campaign_message', side_effect=BrazeClientError)
@mock.patch('license_manager.apps.api_client.braze.BrazeClient.create_braze_alias', side_effect=BrazeClientError)
@mock.patch('license_manager.apps.api.tasks.EnterpriseApiClient', return_value=mock.MagicMock())
def test_activation_task_send_email_failure_logged(
def test_assignment_task_send_email_failure_logged(
self,
mock_enterprise_client,
mock_send_campaign_message,
mock_create_alias,
mock_logger
):
"""
Tests that when sending the assignment email fails, an error gets logged
"""

with self.assertRaises(BrazeClientError):
mock_enterprise_client().get_enterprise_customer_data.return_value = {
'slug': self.enterprise_slug,
'name': self.enterprise_name,
'sender_alias': self.enterprise_sender_alias,
'contact_email': self.contact_email,
}
mock_enterprise_client().get_enterprise_customer_data.return_value = {
'slug': self.enterprise_slug,
'name': self.enterprise_name,
'sender_alias': self.enterprise_sender_alias,
'contact_email': self.contact_email,
}

with self.assertRaises(BrazeClientError):
tasks.send_assignment_email_task(
self.custom_template_text,
self.email_recipient_list,
Expand Down
4 changes: 4 additions & 0 deletions license_manager/apps/subscriptions/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,7 @@ class SegmentEvents:
}

ENTERPRISE_BRAZE_ALIAS_LABEL = 'Enterprise' # Do Not change this, this is consistent with other uses across edX repos.

# Magic strings for logging in notify/remind email tasks
NOTIFY_EMAIL_ACTION_TYPE = 'notify'
REMIND_EMAIL_ACTION_TYPE = 'remind'

0 comments on commit 5775a20

Please sign in to comment.