Commit 20876b7a by Simon Chen Committed by GitHub

Merge pull request #14263 from edx/schen/ECOM-6581

Remove Sailthru enrollment related signal handlers 
parents a637121d 3bda3c4a
......@@ -6,16 +6,14 @@ import datetime
import logging
from django.conf import settings
from django.core.urlresolvers import reverse
from django.dispatch import receiver
from student.models import ENROLL_STATUS_CHANGE
from student.cookies import CREATE_LOGON_COOKIE
from student.views import REGISTER_USER
from email_marketing.models import EmailMarketingConfiguration
from util.model_utils import USER_FIELD_CHANGED
from lms.djangoapps.email_marketing.tasks import (
update_user, update_user_email, update_course_enrollment
update_user, update_user_email
)
from sailthru.sailthru_client import SailthruClient
......@@ -29,51 +27,6 @@ CHANGED_FIELDNAMES = ['username', 'is_active', 'name', 'gender', 'education',
'country']
@receiver(ENROLL_STATUS_CHANGE)
def handle_enroll_status_change(sender, event=None, user=None, mode=None, course_id=None,
**kwargs): # pylint: disable=unused-argument
"""
Signal receiver for enroll/unenroll/purchase events
"""
email_config = EmailMarketingConfiguration.current()
if not email_config.enabled or not event or not user or not mode or not course_id:
return
# skip tracking (un)enrolls if simulated cost=0
if email_config.sailthru_enroll_cost == 0:
return
request = crum.get_current_request()
if not request:
return
# get string course_id serializable to send through celery
course_id_string = unicode(course_id)
# figure out course url
course_url = _build_course_url(request, course_id_string, email_config)
# pass event to email_marketing.tasks
update_course_enrollment.delay(user.email, course_url, event, mode,
course_id=course_id_string,
message_id=request.COOKIES.get('sailthru_bid'))
def _build_course_url(request, course_id, email_config):
"""
Build a course url from a course id and the host from the current request
or use override in config
:param request:
:param course_id:
:return:
"""
path = reverse('info', kwargs={'course_id': course_id})
if email_config.sailthru_lms_url_override:
return '{}{}'.format(email_config.sailthru_lms_url_override, path)
else:
return '{}://{}{}'.format(request.scheme, request.get_host(), path)
@receiver(CREATE_LOGON_COOKIE)
def add_email_marketing_cookies(sender, response=None, user=None,
**kwargs): # pylint: disable=unused-argument
......
......@@ -9,8 +9,6 @@ from django.core.cache import cache
from django.conf import settings
from email_marketing.models import EmailMarketingConfiguration
from student.models import EnrollStatusChange
from sailthru.sailthru_client import SailthruClient
from sailthru.sailthru_error import SailthruClientError
......@@ -131,175 +129,6 @@ def _create_email_user_param(sailthru_vars, sailthru_client, email, new_user, em
return sailthru_user
# pylint: disable=not-callable
@task(bind=True, default_retry_delay=3600, max_retries=24)
def update_course_enrollment(self, email, course_url, event, mode,
course_id=None, message_id=None): # pylint: disable=unused-argument
"""
Adds/updates Sailthru when a user enrolls/unenrolls/adds to cart/purchases/upgrades a course
Args:
email(str): The user's email address
course_url(str): Course home page url
event(str): event type
mode(str): enroll mode (audit, verification, ...)
unit_cost: cost if purchase event
course_id(str): course run id
currency(str): currency if purchase event - currently ignored since Sailthru only supports USD
Returns:
None
The event can be one of the following:
EnrollStatusChange.enroll
A free enroll (mode=audit or honor)
EnrollStatusChange.unenroll
An unenroll
EnrollStatusChange.upgrade_start
A paid upgrade added to cart - ignored
EnrollStatusChange.upgrade_complete
A paid upgrade purchase complete - ignored
EnrollStatusChange.paid_start
A non-free course added to cart - ignored
EnrollStatusChange.paid_complete
A non-free course purchase complete - ignored
"""
email_config = EmailMarketingConfiguration.current()
if not email_config.enabled:
return
# Use event type to figure out processing required
unenroll = False
send_template = None
cost_in_cents = 0
if event == EnrollStatusChange.enroll:
send_template = email_config.sailthru_enroll_template
# set cost so that Sailthru recognizes the event
cost_in_cents = email_config.sailthru_enroll_cost
elif event == EnrollStatusChange.unenroll:
# unenroll - need to update list of unenrolled courses for user in Sailthru
unenroll = True
else:
# All purchase events should be handled by ecommerce, so ignore
return
sailthru_client = SailthruClient(email_config.sailthru_key, email_config.sailthru_secret)
# update the "unenrolled" course array in the user record on Sailthru
if not _update_unenrolled_list(sailthru_client, email, course_url, unenroll):
raise self.retry(countdown=email_config.sailthru_retry_interval,
max_retries=email_config.sailthru_max_retries)
# if there is a cost, call Sailthru purchase api to record
if cost_in_cents:
# get course information if configured and appropriate event
course_data = {}
if email_config.sailthru_get_tags_from_sailthru:
course_data = _get_course_content(course_url, sailthru_client, email_config)
# build item description
item = _build_purchase_item(course_id, course_url, cost_in_cents, mode, course_data)
# build purchase api options list
options = {}
# add appropriate send template
if send_template:
options['send_template'] = send_template
if not _record_purchase(sailthru_client, email, item, message_id, options):
raise self.retry(countdown=email_config.sailthru_retry_interval,
max_retries=email_config.sailthru_max_retries)
def _build_purchase_item(course_id_string, course_url, cost_in_cents, mode, course_data):
"""
Build Sailthru purchase item object
:return: item
"""
# build item description
item = {
'id': "{}-{}".format(course_id_string, mode),
'url': course_url,
'price': cost_in_cents,
'qty': 1,
}
# make up title if we don't already have it from Sailthru
if 'title' in course_data:
item['title'] = course_data['title']
else:
item['title'] = 'Course {} mode: {}'.format(course_id_string, mode)
if 'tags' in course_data:
item['tags'] = course_data['tags']
# add vars to item
item['vars'] = dict(course_data.get('vars', {}), mode=mode, course_run_id=course_id_string)
return item
def _record_purchase(sailthru_client, email, item, message_id, options):
"""
Record a purchase in Sailthru
:param sailthru_client:
:param email:
:param item:
:param incomplete:
:param message_id:
:param options:
:return: False it retryable error
"""
try:
sailthru_response = sailthru_client.purchase(email, [item],
message_id=message_id,
options=options)
if not sailthru_response.is_ok():
error = sailthru_response.get_error()
log.error("Error attempting to record purchase in Sailthru: %s", error.get_message())
return not _retryable_sailthru_error(error)
except SailthruClientError as exc:
log.error("Exception attempting to record purchase for %s in Sailthru - %s", email, unicode(exc))
return False
return True
def _get_course_content(course_url, sailthru_client, email_config):
"""
Get course information using the Sailthru content api.
If there is an error, just return with an empty response.
:param course_url:
:param sailthru_client:
:return: dict with course information
"""
# check cache first
response = cache.get(course_url)
if not response:
try:
sailthru_response = sailthru_client.api_get("content", {"id": course_url})
if not sailthru_response.is_ok():
return {}
response = sailthru_response.json
cache.set(course_url, response, email_config.sailthru_content_cache_age)
except SailthruClientError:
response = {}
return response
def _get_or_create_user_list_for_site(sailthru_client, site=None, default_list_name=None):
"""
Get the user list name from cache if exists else create one and return the name,
......@@ -393,61 +222,6 @@ def _create_user_list(sailthru_client, list_name):
return True
def _update_unenrolled_list(sailthru_client, email, course_url, unenroll):
"""
Maintain a list of courses the user has unenrolled from in the Sailthru user record
:param sailthru_client:
:param email:
:param email_config:
:param course_url:
:param unenroll:
:return: False if retryable error, else True
"""
try:
# get the user 'vars' values from sailthru
sailthru_response = sailthru_client.api_get("user", {"id": email, "fields": {"vars": 1}})
if not sailthru_response.is_ok():
error = sailthru_response.get_error()
log.info("Error attempting to read user record from Sailthru: %s", error.get_message())
return not _retryable_sailthru_error(error)
response_json = sailthru_response.json
unenroll_list = []
if response_json and "vars" in response_json and response_json["vars"] \
and "unenrolled" in response_json["vars"]:
unenroll_list = response_json["vars"]["unenrolled"]
changed = False
# if unenrolling, add course to unenroll list
if unenroll:
if course_url not in unenroll_list:
unenroll_list.append(course_url)
changed = True
# if enrolling, remove course from unenroll list
elif course_url in unenroll_list:
unenroll_list.remove(course_url)
changed = True
if changed:
# write user record back
sailthru_response = sailthru_client.api_post(
"user", {'id': email, 'key': 'email', "vars": {"unenrolled": unenroll_list}})
if not sailthru_response.is_ok():
error = sailthru_response.get_error()
log.info("Error attempting to update user record in Sailthru: %s", error.get_message())
return not _retryable_sailthru_error(error)
# everything worked
return True
except SailthruClientError as exc:
log.error("Exception attempting to update user record for %s in Sailthru - %s", email, unicode(exc))
return False
def _retryable_sailthru_error(error):
""" Return True if error should be retried.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment