Skip to content

Commit

Permalink
feat: send an action_required_by prop for assignment braze tasks
Browse files Browse the repository at this point in the history
ENT-8146 | Send an `action_required_by` property for assignment
braze notification and reminder campaigns.  The value is the min
of the enrollment deadline, subsidy expiration, and auto-cancellation date.
  • Loading branch information
iloveagent57 committed Jan 12, 2024
1 parent 571b02e commit 90fa068
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
'%Y-%m-%d %H:%M:%SZ',
'%Y-%m-%d %H:%M:%S.%fZ',
]
DEFAULT_STRFTIME_PATTERN = '%b %d, %Y'


def get_content_metadata_for_assignments(enterprise_catalog_uuid, assignments):
Expand Down Expand Up @@ -48,7 +49,7 @@ def get_card_image_url(content_metadata):
return None


def get_human_readable_date(datetime_string, output_pattern='%b %d, %Y'):
def get_human_readable_date(datetime_string, output_pattern=DEFAULT_STRFTIME_PATTERN):
"""
Given a datetime string value from some content metadata record,
convert it to the provided pattern.
Expand Down Expand Up @@ -79,6 +80,10 @@ def parse_datetime_string(datetime_string):
return None


def format_datetime_obj(datetime_obj, output_pattern=DEFAULT_STRFTIME_PATTERN):
return datetime_obj.strftime(output_pattern)


def get_course_partners(course_metadata):
"""
Returns a list of course partner data for subsidy requests given a course dictionary.
Expand Down
58 changes: 53 additions & 5 deletions enterprise_access/apps/content_assignments/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@
"""

import logging
from datetime import datetime

from braze.exceptions import BrazeBadRequestError
from celery import shared_task
from django.apps import apps
from django.conf import settings
from pytz import UTC

from enterprise_access.apps.api_client.braze_client import ENTERPRISE_BRAZE_ALIAS_LABEL, BrazeApiClient
from enterprise_access.apps.api_client.lms_client import LmsApiClient
from enterprise_access.apps.content_assignments.content_metadata_api import (
format_datetime_obj,
get_card_image_url,
get_content_metadata_for_assignments,
get_course_partners,
get_human_readable_date
get_human_readable_date,
parse_datetime_string
)
from enterprise_access.tasks import LoggedTaskWithRetry

Expand Down Expand Up @@ -55,7 +59,8 @@ class BrazeCampaignSender:
'start_date',
'course_partner',
'course_card_image',
'learner_portal_link'
'learner_portal_link',
'action_required_by',
}

def __init__(self, assignment):
Expand Down Expand Up @@ -154,6 +159,15 @@ def course_metadata(self):
raise Exception(msg)
return self._course_metadata

@property
def subsidy_record(self):
"""
Returns a cached subsidy record for the policy related to this assignment.
"""
# send an extra cache arg so that cache keys are scoped
# to the context of braze campaign-sending.
return self.policy.subsidy_record_from_tiered_cache('braze_campaign_sender')

def get_properties(self, *property_names):
"""
Looks for instance methods on ``self`` that match "get_{property_name}"
Expand All @@ -180,16 +194,48 @@ def get_organization(self):
def get_course_title(self):
return self.assignment.content_title

def _enrollment_deadline_raw(self):
return self.course_metadata.get('normalized_metadata', {}).get('enroll_by_date')

def get_enrollment_deadline(self):
return get_human_readable_date(
self.course_metadata.get('normalized_metadata', {}).get('enroll_by_date')
)
return get_human_readable_date(self._enrollment_deadline_raw())

def get_start_date(self):
return get_human_readable_date(
self.course_metadata.get('normalized_metadata', {}).get('start_date')
)

def get_action_required_by(self):
"""
Returns the minimum of this assignment's auto-cancellation date,
the content's enrollment deadline, and the related policy's expiration datetime.
"""
if subsidy_record := self.subsidy_record:
subsidy_expiration = parse_datetime_string(subsidy_record.get('expiration_datetime')) or datetime.max
subsidy_expiration = subsidy_expiration.replace(tzinfo=UTC)
else:
subsidy_expiration = datetime.max.replace(tzinfo=UTC)

enrollment_deadline = parse_datetime_string(self._enrollment_deadline_raw()) or datetime.max
enrollment_deadline = enrollment_deadline.replace(tzinfo=UTC)

auto_cancellation_date = self.assignment.get_auto_expiration_date() or datetime.max
auto_cancellation_date = auto_cancellation_date.replace(tzinfo=UTC)

message = (
'action_required_by assignment=%s: subsidy_expiration=%s, enrollment_deadline=%s, '
'auto_cancellation_date=%s'
)
logger.info(
message,
self.assignment.uuid,
subsidy_expiration,
enrollment_deadline,
auto_cancellation_date,
)
action_required_by = min(subsidy_expiration, enrollment_deadline, auto_cancellation_date)
return format_datetime_obj(action_required_by)

def get_course_partner(self):
return get_course_partners(self.course_metadata)

Expand Down Expand Up @@ -348,6 +394,7 @@ def send_reminder_email_for_pending_assignment(assignment_uuid):
'course_partner',
'course_card_image',
'learner_portal_link',
'action_required_by',
)
campaign_uuid = settings.BRAZE_ASSIGNMENT_REMINDER_NOTIFICATION_CAMPAIGN
if assignment.lms_user_id is not None:
Expand Down Expand Up @@ -390,6 +437,7 @@ def send_email_for_new_assignment(new_assignment_uuid):
'course_partner',
'course_card_image',
'learner_portal_link',
'action_required_by',
)
campaign_uuid = settings.BRAZE_ASSIGNMENT_NOTIFICATION_CAMPAIGN
campaign_sender.send_campaign_message(
Expand Down
Loading

0 comments on commit 90fa068

Please sign in to comment.