Skip to content
This repository has been archived by the owner on Nov 4, 2024. It is now read-only.

Commit

Permalink
feat: use a braze api-triggered campaign for ent offer usage
Browse files Browse the repository at this point in the history
  • Loading branch information
iloveagent57 committed Jun 23, 2022
1 parent b77c44d commit be3da20
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
"""
import logging
from datetime import datetime
from urllib.parse import urljoin

from django.conf import settings
from django.contrib.sites.models import Site
from django.core.management import BaseCommand
from django.db.models import Sum
from ecommerce_worker.email.v1.api import send_offer_usage_email

from ecommerce.extensions.fulfillment.status import ORDER
from ecommerce.core.models import User
from ecommerce.programs.custom import get_model

ConditionalOffer = get_model('offer', 'ConditionalOffer')
Expand All @@ -18,13 +20,6 @@
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

# pylint: disable=line-too-long
EMAIL_BODY = """
You have used {percentage_usage}% of the {offer_type} Limit associated with the entitlement offer called "{offer_name}"
{offer_type}s Redeemed: {current_usage}
{offer_type}s Limit: {total_limit}
Please reach out to customersuccess@edx.org, or to your Account Manager or Customer Success representative, if you have any questions.
"""
EMAIL_SUBJECT = 'Offer Usage Notification'


Expand Down Expand Up @@ -62,35 +57,46 @@ def get_enrollment_limits(offer):
return int(offer.max_global_applications), percentage_usage, int(offer.num_orders)

@staticmethod
def get_booking_limits(offer):
def get_booking_limits(site, offer):
"""
Return the total discount limit, percentage usage and current usage of booking limit.
"""
total_used_discount_amount = OrderDiscount.objects.filter(
offer_id=offer.id,
order__status=ORDER.COMPLETE
).aggregate(Sum('amount'))['amount__sum']
total_used_discount_amount = total_used_discount_amount if total_used_discount_amount else 0

percentage_usage = int((total_used_discount_amount / offer.max_discount) * 100)
return int(offer.max_discount), percentage_usage, int(total_used_discount_amount)
api_client = site.siteconfiguration.oauth_api_client
enterprise_customer_uuid = offer.condition.enterprise_customer_uuid
offer_analytics_url = urljoin(
settings.ENTERPRISE_ANALYTICS_API_URL,
f'/enterprise/api/v1/enterprise/{enterprise_customer_uuid}/offers/{offer.id}/',
)
response = api_client.get(offer_analytics_url)
response.raise_for_status()
offer_analytics = response.json()
if not offer_analytics:
raise Exception(f'No analytics for offer: {offer.id}')

return (
offer_analytics['max_discount'],
offer_analytics['percent_of_offer_spent'],
offer_analytics['amount_of_offer_spent'],
)

def get_email_content(self, offer):
def get_email_content(self, site, offer):
"""
Return the appropriate email body and subject of given offer.
"""
is_enrollment_limit_offer = bool(offer.max_global_applications)
total_limit, percentage_usage, current_usage = self.get_enrollment_limits(offer) if is_enrollment_limit_offer \
else self.get_booking_limits(offer)

email_body = EMAIL_BODY.format(
percentage_usage=percentage_usage,
total_limit=total_limit if is_enrollment_limit_offer else "{}$".format(total_limit),
offer_type='Enrollment' if is_enrollment_limit_offer else 'Booking',
offer_name=offer.name,
current_usage=current_usage if is_enrollment_limit_offer else "{}$".format(current_usage),
total_limit, percentage_usage, current_usage = (
self.get_enrollment_limits(offer)
if is_enrollment_limit_offer
else self.get_booking_limits(site, offer)
)
return email_body, EMAIL_SUBJECT

return {
'percent_usage': percentage_usage,
'total_limit': total_limit if is_enrollment_limit_offer else "${}".format(total_limit),
'offer_type': 'Enrollment' if is_enrollment_limit_offer else 'Booking',
'offer_name': offer.name,
'current_usage': current_usage if is_enrollment_limit_offer else "${}".format(current_usage),
}

@staticmethod
def _get_enterprise_offers():
Expand All @@ -103,7 +109,7 @@ def _get_enterprise_offers():
).exclude(emails_for_usage_alert='')

def handle(self, *args, **options):
send_enterprise_offer_count = 0
successful_send_count = 0
enterprise_offers = self._get_enterprise_offers()
total_enterprise_offers_count = enterprise_offers.count()
logger.info('[Offer Usage Alert] Total count of enterprise offers is %s.', total_enterprise_offers_count)
Expand All @@ -114,16 +120,32 @@ def handle(self, *args, **options):
enterprise_offer.name,
enterprise_offer.id
)
send_enterprise_offer_count += 1
email_body, email_subject = self.get_email_content(enterprise_offer)
OfferUsageEmail.create_record(enterprise_offer, meta_data={
'email_body': email_body,
'email_subject': email_subject,
'email_addresses': enterprise_offer.emails_for_usage_alert
})
send_offer_usage_email.delay(enterprise_offer.emails_for_usage_alert, email_subject, email_body)
site = Site.objects.get_current()
email_body = self.get_email_content(site, enterprise_offer)

lms_user_ids_by_email = {
user_email: User.get_lms_user_attribute_using_email(site, user_email, attribute='id')
for user_email in enterprise_offer.emails_for_usage_alert.strip().split(',')
}

task_result = send_offer_usage_email.delay(
lms_user_ids_by_email,
EMAIL_SUBJECT,
email_body,
)
# Block until the task is done, since we're inside a management command
# and likely running from a job scheduler (ex. Jenkins).
# propagate=False means we won't re-raise (and exit this method) if any one task fails.
task_result.get(propagate=False)
if task_result.successful():
successful_send_count += 1
OfferUsageEmail.create_record(enterprise_offer, meta_data={
'email_body': email_body,
'email_subject': EMAIL_SUBJECT,
'email_addresses': enterprise_offer.emails_for_usage_alert
})
logger.info(
'[Offer Usage Alert] %s of %s added to the email sending queue.',
'[Offer Usage Alert] %s of %s offers with usage alerts configured had an email sent.',
successful_send_count,
total_enterprise_offers_count,
send_enterprise_offer_count
)
2 changes: 2 additions & 0 deletions ecommerce/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,8 @@

ENTERPRISE_CATALOG_SERVICE_URL = 'http://localhost:18160/'

ENTERPRISE_ANALYTICS_API_URL = 'http://localhost:19001'

ENTERPRISE_LEARNER_PORTAL_HOSTNAME = os.environ.get('ENTERPRISE_LEARNER_PORTAL_HOSTNAME', 'localhost:8734')

# Name for waffle switch to use for enabling enterprise features on runtime.
Expand Down
2 changes: 2 additions & 0 deletions ecommerce/settings/devstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@

ENTERPRISE_CATALOG_API_URL = urljoin(f"{ENTERPRISE_CATALOG_SERVICE_URL}/", 'api/v1/')

ENTERPRISE_ANALYTICS_API_URL = 'http://edx.devstack.analyticsapi:19001'

# PAYMENT PROCESSING
PAYMENT_PROCESSOR_CONFIG = {
'edx': {
Expand Down

0 comments on commit be3da20

Please sign in to comment.