def test_get_course_content(self, mock_sailthru_client):
        """
        test routine which fetches data from Sailthru content api
        """
        mock_sailthru_client.api_get.return_value = SailthruResponse(
            JsonResponse({"title": "The title"}))
        response_json = _get_course_content(
            'course:123', mock_sailthru_client,
            EmailMarketingConfiguration.current())
        self.assertEquals(response_json, {"title": "The title"})
        mock_sailthru_client.api_get.assert_called_with(
            'content', {'id': 'course:123'})

        # test second call uses cache
        response_json = _get_course_content(
            'course:123', mock_sailthru_client,
            EmailMarketingConfiguration.current())
        self.assertEquals(response_json, {"title": "The title"})
        mock_sailthru_client.api_get.assert_not_called()

        # test error from Sailthru
        mock_sailthru_client.api_get.return_value = \
            SailthruResponse(JsonResponse({'error': 100, 'errormsg': 'Got an error'}))
        self.assertEquals(
            _get_course_content('course:124', mock_sailthru_client,
                                EmailMarketingConfiguration.current()), {})

        # test exception
        mock_sailthru_client.api_get.side_effect = SailthruClientError
        self.assertEquals(
            _get_course_content('course:125', mock_sailthru_client,
                                EmailMarketingConfiguration.current()), {})
예제 #2
0
    def test_get_course_content(self, mock_sailthru_client):
        """
        test routine which fetches data from Sailthru content api
        """
        mock_sailthru_client.api_get.return_value = SailthruResponse(JsonResponse({"title": "The title"}))
        response_json = _get_course_content("course:123", mock_sailthru_client, EmailMarketingConfiguration.current())
        self.assertEquals(response_json, {"title": "The title"})
        mock_sailthru_client.api_get.assert_called_with("content", {"id": "course:123"})

        # test second call uses cache
        response_json = _get_course_content("course:123", mock_sailthru_client, EmailMarketingConfiguration.current())
        self.assertEquals(response_json, {"title": "The title"})
        mock_sailthru_client.api_get.assert_not_called()

        # test error from Sailthru
        mock_sailthru_client.api_get.return_value = SailthruResponse(
            JsonResponse({"error": 100, "errormsg": "Got an error"})
        )
        self.assertEquals(
            _get_course_content("course:124", mock_sailthru_client, EmailMarketingConfiguration.current()), {}
        )

        # test exception
        mock_sailthru_client.api_get.side_effect = SailthruClientError
        self.assertEquals(
            _get_course_content("course:125", mock_sailthru_client, EmailMarketingConfiguration.current()), {}
        )
예제 #3
0
def email_marketing_user_field_changed(sender,
                                       user=None,
                                       table=None,
                                       setting=None,
                                       old_value=None,
                                       new_value=None,
                                       **kwargs):  # pylint: disable=unused-argument
    """
    Update a single user/profile field

    Args:
        sender: Not used
        user: The user object for the user being changed
        table: The name of the table being updated
        setting: The name of the setting being updated
        old_value: Prior value
        new_value: New value
        kwargs: Not used
    """

    # ignore anonymous users
    if user.is_anonymous():
        return

    # ignore anything but User, Profile or UserPreference tables
    if table not in {
            'auth_user', 'auth_userprofile', 'user_api_userpreference'
    }:
        return

    # ignore anything not in list of fields to handle
    if setting in CHANGED_FIELDNAMES:
        # skip if not enabled
        #  the check has to be here rather than at the start of the method to avoid
        #  accessing the config during migration 0001_date__add_ecommerce_service_user
        email_config = EmailMarketingConfiguration.current()
        if not email_config.enabled:
            return

        # set the activation flag when the user is marked as activated
        update_user.delay(_create_sailthru_user_vars(user, user.profile),
                          user.email,
                          site=_get_current_site(),
                          new_user=False,
                          activation=(setting == 'is_active')
                          and new_value is True)

    elif setting == 'email':
        # email update is special case
        email_config = EmailMarketingConfiguration.current()
        if not email_config.enabled:
            return
        update_user_email.delay(user.email, old_value)
예제 #4
0
def email_marketing_user_field_changed(sender,
                                       user=None,
                                       table=None,
                                       setting=None,
                                       old_value=None,
                                       new_value=None,
                                       **kwargs):  # pylint: disable=unused-argument
    """
    Update a single user/profile field

    Args:
        sender: Not used
        user: The user object for the user being changed
        table: The name of the table being updated
        setting: The name of the setting being updated
        old_value: Prior value
        new_value: New value
        kwargs: Not used
    """

    # ignore anonymous users
    if user.is_anonymous():
        return

    # ignore anything but User or Profile table
    if table != 'auth_user' and table != 'auth_userprofile':
        return

    # ignore anything not in list of fields to handle
    if setting in CHANGED_FIELDNAMES:
        # skip if not enabled
        #  the check has to be here rather than at the start of the method to avoid
        #  accessing the config during migration 0001_date__add_ecommerce_service_user
        email_config = EmailMarketingConfiguration.current()
        if not email_config.enabled:
            return
        # perform update asynchronously, flag if activation
        update_user.delay(user.username,
                          new_user=False,
                          activation=(setting == 'is_active')
                          and new_value is True)

    elif setting == 'email':
        # email update is special case
        email_config = EmailMarketingConfiguration.current()
        if not email_config.enabled:
            return
        update_user_email.delay(user.username, old_value)
def handle_enroll_status_change(sender,
                                event=None,
                                user=None,
                                mode=None,
                                course_id=None,
                                cost=None,
                                currency=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

    request = crum.get_current_request()
    if not request:
        return

    # figure out course url
    course_url = _build_course_url(request, course_id.to_deprecated_string())

    # pass event to email_marketing.tasks
    update_course_enrollment.delay(
        user.email,
        course_url,
        event,
        mode,
        unit_cost=cost,
        course_id=course_id,
        currency=currency,
        message_id=request.COOKIES.get('sailthru_bid'))
예제 #6
0
def update_user(self, sailthru_vars, email, site=None, new_user=False, activation=False):
    """
    Adds/updates Sailthru profile information for a user.
     Args:
        sailthru_vars(dict): User profile information to pass as 'vars' to Sailthru
        email(str): User email address
        new_user(boolean): True if new registration
        activation(boolean): True if activation request
    Returns:
        None
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return

    sailthru_client = SailthruClient(email_config.sailthru_key, email_config.sailthru_secret)
    try:
        sailthru_response = sailthru_client.api_post("user",
                                                     _create_email_user_param(sailthru_vars, sailthru_client,
                                                                              email, new_user, email_config,
                                                                              site=site))

    except SailthruClientError as exc:
        log.error("Exception attempting to add/update user %s in Sailthru - %s", email, unicode(exc))
        raise self.retry(exc=exc,
                         countdown=email_config.sailthru_retry_interval,
                         max_retries=email_config.sailthru_max_retries)

    if not sailthru_response.is_ok():
        error = sailthru_response.get_error()
        log.error("Error attempting to add/update user in Sailthru: %s", error.get_message())
        if _retryable_sailthru_error(error):
            raise self.retry(countdown=email_config.sailthru_retry_interval,
                             max_retries=email_config.sailthru_max_retries)
        return

    # if activating user, send welcome email
    if activation and email_config.sailthru_activation_template:
        scheduled_datetime = datetime.utcnow() + timedelta(seconds=email_config.welcome_email_send_delay)
        try:
            sailthru_response = sailthru_client.api_post(
                "send",
                {
                    "email": email,
                    "template": email_config.sailthru_activation_template,
                    "schedule_time": scheduled_datetime.strftime('%Y-%m-%dT%H:%M:%SZ')
                }
            )
        except SailthruClientError as exc:
            log.error("Exception attempting to send welcome email to user %s in Sailthru - %s", email, unicode(exc))
            raise self.retry(exc=exc,
                             countdown=email_config.sailthru_retry_interval,
                             max_retries=email_config.sailthru_max_retries)

        if not sailthru_response.is_ok():
            error = sailthru_response.get_error()
            log.error("Error attempting to send welcome email to user in Sailthru: %s", error.get_message())
            if _retryable_sailthru_error(error):
                raise self.retry(countdown=email_config.sailthru_retry_interval,
                                 max_retries=email_config.sailthru_max_retries)
예제 #7
0
def add_email_marketing_cookies(sender, response=None, user=None,
                                **kwargs):  # pylint: disable=unused-argument
    """
    Signal function for adding any cookies needed for email marketing

    Args:
        response: http response object
        user: The user object for the user being changed

    Returns:
        response: http response object with cookie added
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return response

    post_parms = {
        'id': user.email,
        'fields': {'keys': 1},
        'vars': {'last_login_date': datetime.datetime.now().strftime("%Y-%m-%d")}
    }

    # get anonymous_interest cookie to capture usage before logon
    request = crum.get_current_request()
    if request:
        sailthru_content = request.COOKIES.get('anonymous_interest')
        if sailthru_content:
            post_parms['cookies'] = {'anonymous_interest': sailthru_content}

    try:
        sailthru_client = SailthruClient(email_config.sailthru_key, email_config.sailthru_secret)
        log.info(
            'Sending to Sailthru the user interest cookie [%s] for user [%s]',
            post_parms.get('cookies', ''),
            user.email
        )
        sailthru_response = \
            sailthru_client.api_post("user", post_parms)
    except SailthruClientError as exc:
        log.error("Exception attempting to obtain cookie from Sailthru: %s", unicode(exc))
        return response

    if sailthru_response.is_ok():
        if 'keys' in sailthru_response.json and 'cookie' in sailthru_response.json['keys']:
            cookie = sailthru_response.json['keys']['cookie']

            response.set_cookie(
                'sailthru_hid',
                cookie,
                max_age=365 * 24 * 60 * 60,  # set for 1 year
                domain=settings.SESSION_COOKIE_DOMAIN,
                path='/',
            )
        else:
            log.error("No cookie returned attempting to obtain cookie from Sailthru for %s", user.email)
    else:
        error = sailthru_response.get_error()
        # generally invalid email address
        log.info("Error attempting to obtain cookie from Sailthru: %s", error.get_message())
    return response
예제 #8
0
def update_user(self, sailthru_vars, email, site=None, new_user=False, activation=False):
    """
    Adds/updates Sailthru profile information for a user.
     Args:
        sailthru_vars(dict): User profile information to pass as 'vars' to Sailthru
        email(str): User email address
        new_user(boolean): True if new registration
        activation(boolean): True if activation request
    Returns:
        None
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return

    sailthru_client = SailthruClient(email_config.sailthru_key, email_config.sailthru_secret)
    try:
        sailthru_response = sailthru_client.api_post("user",
                                                     _create_email_user_param(sailthru_vars, sailthru_client,
                                                                              email, new_user, email_config,
                                                                              site=site))

    except SailthruClientError as exc:
        log.error("Exception attempting to add/update user %s in Sailthru - %s", email, unicode(exc))
        raise self.retry(exc=exc,
                         countdown=email_config.sailthru_retry_interval,
                         max_retries=email_config.sailthru_max_retries)

    if not sailthru_response.is_ok():
        error = sailthru_response.get_error()
        log.error("Error attempting to add/update user in Sailthru: %s", error.get_message())
        if _retryable_sailthru_error(error):
            raise self.retry(countdown=email_config.sailthru_retry_interval,
                             max_retries=email_config.sailthru_max_retries)
        return

    # if activating user, send welcome email
    if activation and email_config.sailthru_activation_template:
        scheduled_datetime = datetime.utcnow() + timedelta(seconds=email_config.welcome_email_send_delay)
        try:
            sailthru_response = sailthru_client.api_post(
                "send",
                {
                    "email": email,
                    "template": email_config.sailthru_activation_template,
                    "schedule_time": scheduled_datetime.strftime('%Y-%m-%dT%H:%M:%SZ')
                }
            )
        except SailthruClientError as exc:
            log.error("Exception attempting to send welcome email to user %s in Sailthru - %s", email, unicode(exc))
            raise self.retry(exc=exc,
                             countdown=email_config.sailthru_retry_interval,
                             max_retries=email_config.sailthru_max_retries)

        if not sailthru_response.is_ok():
            error = sailthru_response.get_error()
            log.error("Error attempting to send welcome email to user in Sailthru: %s", error.get_message())
            if _retryable_sailthru_error(error):
                raise self.retry(countdown=email_config.sailthru_retry_interval,
                                 max_retries=email_config.sailthru_max_retries)
예제 #9
0
def email_marketing_register_user(sender, user, registration, **kwargs):  # pylint: disable=unused-argument
    """
    Called after user created and saved

    Args:
        sender: Not used
        user: The user object for the user being changed
        registration: The user registration profile to activate user account
        kwargs: Not used
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return

    # ignore anonymous users
    if user.is_anonymous():
        return

    # perform update asynchronously
    update_user.delay(_create_sailthru_user_vars(user,
                                                 user.profile,
                                                 registration=registration),
                      user.email,
                      site=_get_current_site(),
                      new_user=True)
예제 #10
0
def update_user_email(self, new_email, old_email):
    """
    Adds/updates Sailthru when a user email address is changed
     Args:
        username(str): A string representation of user identifier
        old_email(str): Original email address
    Returns:
        None
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return

    # ignore if email not changed
    if new_email == old_email:
        return

    sailthru_parms = {"id": old_email, "key": "email", "keysconflict": "merge", "keys": {"email": new_email}}

    try:
        sailthru_client = SailthruClient(email_config.sailthru_key, email_config.sailthru_secret)
        sailthru_response = sailthru_client.api_post("user", sailthru_parms)
    except SailthruClientError as exc:
        log.error("Exception attempting to update email for %s in Sailthru - %s", old_email, unicode(exc))
        raise self.retry(exc=exc,
                         countdown=email_config.sailthru_retry_interval,
                         max_retries=email_config.sailthru_max_retries)

    if not sailthru_response.is_ok():
        error = sailthru_response.get_error()
        log.error("Error attempting to update user email address in Sailthru: %s", error.get_message())
        if _retryable_sailthru_error(error):
            raise self.retry(countdown=email_config.sailthru_retry_interval,
                             max_retries=email_config.sailthru_max_retries)
예제 #11
0
def update_course_enrollment(self, email, course_key, mode):
    """Adds/updates Sailthru when a user adds to cart/purchases/upgrades a course
         Args:
            user: current user
            course_key: course key of course
        Returns:
            None
    """
    course_url = build_course_url(course_key)
    config = EmailMarketingConfiguration.current()

    try:
        sailthru_client = SailthruClient(config.sailthru_key, config.sailthru_secret)
    except:
        return

    send_template = config.sailthru_enroll_template
    cost_in_cents = 0

    if not update_unenrolled_list(sailthru_client, email, course_url, False):
        schedule_retry(self, config)

    course_data = _get_course_content(course_key, course_url, sailthru_client, config)

    item = _build_purchase_item(course_key, course_url, cost_in_cents, mode, course_data)
    options = {}

    if send_template:
        options['send_template'] = send_template

    if not _record_purchase(sailthru_client, email, item, options):
        schedule_retry(self, config)
예제 #12
0
def update_course_enrollment(self, email, course_key, mode):
    """Adds/updates Sailthru when a user adds to cart/purchases/upgrades a course
         Args:
            user: current user
            course_key: course key of course
        Returns:
            None
    """
    course_url = build_course_url(course_key)
    config = EmailMarketingConfiguration.current()

    try:
        sailthru_client = SailthruClient(config.sailthru_key,
                                         config.sailthru_secret)
    except:
        return

    send_template = config.sailthru_enroll_template
    cost_in_cents = 0

    if not update_unenrolled_list(sailthru_client, email, course_url, False):
        schedule_retry(self, config)

    course_data = _get_course_content(course_key, course_url, sailthru_client,
                                      config)

    item = _build_purchase_item(course_key, course_url, cost_in_cents, mode,
                                course_data)
    options = {}

    if send_template:
        options['send_template'] = send_template

    if not _record_purchase(sailthru_client, email, item, options):
        schedule_retry(self, config)
예제 #13
0
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'))
예제 #14
0
def update_user_email(self, new_email, old_email):
    """
    Adds/updates Sailthru when a user email address is changed
     Args:
        username(str): A string representation of user identifier
        old_email(str): Original email address
    Returns:
        None
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return

    # ignore if email not changed
    if new_email == old_email:
        return

    sailthru_parms = {"id": old_email, "key": "email", "keysconflict": "merge", "keys": {"email": new_email}}

    try:
        sailthru_client = SailthruClient(email_config.sailthru_key, email_config.sailthru_secret)
        sailthru_response = sailthru_client.api_post("user", sailthru_parms)
    except SailthruClientError as exc:
        log.error("Exception attempting to update email for %s in Sailthru - %s", old_email, unicode(exc))
        raise self.retry(exc=exc,
                         countdown=email_config.sailthru_retry_interval,
                         max_retries=email_config.sailthru_max_retries)

    if not sailthru_response.is_ok():
        error = sailthru_response.get_error()
        log.error("Error attempting to update user email address in Sailthru: %s", error.get_message())
        if _retryable_sailthru_error(error):
            raise self.retry(countdown=email_config.sailthru_retry_interval,
                             max_retries=email_config.sailthru_max_retries)
예제 #15
0
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'))
예제 #16
0
def add_email_marketing_cookies(sender, response=None, user=None,
                                **kwargs):  # pylint: disable=unused-argument
    """
    Signal function for adding any cookies needed for email marketing

    Args:
        response: http response object
        user: The user object for the user being changed

    Returns:
        response: http response object with cookie added
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return response

    post_parms = {
        'id': user.email,
        'fields': {'keys': 1},
        'vars': {'last_login_date': datetime.datetime.now().strftime("%Y-%m-%d")}
    }

    # get anonymous_interest cookie to capture usage before logon
    request = crum.get_current_request()
    if request:
        sailthru_content = request.COOKIES.get('anonymous_interest')
        if sailthru_content:
            post_parms['cookies'] = {'anonymous_interest': sailthru_content}

    time_before_call = datetime.datetime.now()
    sailthru_response = get_email_cookies_via_sailthru.delay(user.email, post_parms)

    try:
        # synchronous call to get result of an asynchronous celery task, with timeout
        sailthru_response.get(timeout=email_config.user_registration_cookie_timeout_delay,
                              propagate=True)
        cookie = sailthru_response.result
        _log_sailthru_api_call_time(time_before_call)

    except TimeoutError as exc:
        log.error("Timeout error while attempting to obtain cookie from Sailthru: %s", unicode(exc))
        return response
    except SailthruClientError as exc:
        log.error("Exception attempting to obtain cookie from Sailthru: %s", unicode(exc))
        return response

    if not cookie:
        log.error("No cookie returned attempting to obtain cookie from Sailthru for %s", user.email)
        return response
    else:
        response.set_cookie(
            'sailthru_hid',
            cookie,
            max_age=365 * 24 * 60 * 60,  # set for 1 year
            domain=settings.SESSION_COOKIE_DOMAIN,
            path='/',
        )
        log.info("sailthru_hid cookie:%s successfully retrieved for user %s", cookie, user.email)

    return response
예제 #17
0
def add_email_marketing_cookies(sender, response=None, user=None,
                                **kwargs):  # pylint: disable=unused-argument
    """
    Signal function for adding any cookies needed for email marketing

    Args:
        response: http response object
        user: The user object for the user being changed

    Returns:
        response: http response object with cookie added
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return response

    post_parms = {
        'id': user.email,
        'fields': {'keys': 1},
        'vars': {'last_login_date': datetime.datetime.now().strftime("%Y-%m-%d")}
    }

    # get anonymous_interest cookie to capture usage before logon
    request = crum.get_current_request()
    if request:
        sailthru_content = request.COOKIES.get('anonymous_interest')
        if sailthru_content:
            post_parms['cookies'] = {'anonymous_interest': sailthru_content}

    time_before_call = datetime.datetime.now()
    sailthru_response = get_email_cookies_via_sailthru.delay(user.email, post_parms)

    try:
        # synchronous call to get result of an asynchronous celery task, with timeout
        sailthru_response.get(timeout=email_config.user_registration_cookie_timeout_delay,
                              propagate=True)
        cookie = sailthru_response.result

    except TimeoutError as exc:
        log.error("Timeout error while attempting to obtain cookie from Sailthru: %s", unicode(exc))
        return response
    except SailthruClientError as exc:
        log.error("Exception attempting to obtain cookie from Sailthru: %s", unicode(exc))
        return response

    if not cookie:
        log.error("No cookie returned attempting to obtain cookie from Sailthru for %s", user.email)
        return response
    else:
        response.set_cookie(
            'sailthru_hid',
            cookie,
            max_age=365 * 24 * 60 * 60,  # set for 1 year
            domain=settings.SESSION_COOKIE_DOMAIN,
            path='/',
        )
        _log_sailthru_api_call_time(time_before_call)

    return response
예제 #18
0
def handle_unenroll_done(sender, course_enrollment=None, skip_refund=False,
                         **kwargs):  # pylint: disable=unused-argument
    """
    Signal receiver for unenrollments
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return
예제 #19
0
def update_user(self, username, new_user=False, activation=False):
    """
    Adds/updates Sailthru profile information for a user.
     Args:
        username(str): A string representation of user identifier
    Returns:
        None
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return

    # get user
    user = User.objects.select_related('profile').get(username=username)
    if not user:
        log.error("User not found during Sailthru update %s", username)
        return

    # get profile
    profile = user.profile
    if not profile:
        log.error("User profile not found during Sailthru update %s", username)
        return

    sailthru_client = SailthruClient(email_config.sailthru_key, email_config.sailthru_secret)
    try:
        sailthru_response = sailthru_client.api_post("user",
                                                     _create_sailthru_user_parm(user, profile, new_user, email_config))
    except SailthruClientError as exc:
        log.error("Exception attempting to add/update user %s in Sailthru - %s", username, unicode(exc))
        raise self.retry(exc=exc,
                         countdown=email_config.sailthru_retry_interval,
                         max_retries=email_config.sailthru_max_retries)

    if not sailthru_response.is_ok():
        error = sailthru_response.get_error()
        # put out error and schedule retry
        log.error("Error attempting to add/update user in Sailthru: %s", error.get_message())
        raise self.retry(countdown=email_config.sailthru_retry_interval,
                         max_retries=email_config.sailthru_max_retries)

    # if activating user, send welcome email
    if activation and email_config.sailthru_activation_template:
        try:
            sailthru_response = sailthru_client.api_post("send",
                                                         {"email": user.email,
                                                          "template": email_config.sailthru_activation_template})
        except SailthruClientError as exc:
            log.error("Exception attempting to send welcome email to user %s in Sailthru - %s", username, unicode(exc))
            raise self.retry(exc=exc,
                             countdown=email_config.sailthru_retry_interval,
                             max_retries=email_config.sailthru_max_retries)

        if not sailthru_response.is_ok():
            error = sailthru_response.get_error()
            # probably an invalid template name, just put out error
            log.error("Error attempting to send welcome email to user in Sailthru: %s", error.get_message())
예제 #20
0
def force_unsubscribe_all(sender, **kwargs):  # pylint: disable=unused-argument
    """
    Synchronously(!) unsubscribes the given user from all Sailthru email lists.

    In the future this could be moved to a Celery task, however this is currently
    only used as part of user retirement, where we need a very reliable indication
    of success or failure.

    Args:
        email: Email address to unsubscribe
        new_email (optional): Email address to change 3rd party services to for this user (used in retirement to clear
                              personal information from the service)
    Returns:
        None
    """
    email = kwargs.get('email', None)
    new_email = kwargs.get('new_email', None)

    if not email:
        raise TypeError(
            'Expected an email address to unsubscribe, but received None.')

    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return

    sailthru_parms = {
        "id": email,
        "optout_email": "all",
        "fields": {
            "optout_email": 1
        }
    }

    # If we have a new email address to change to, do that as well
    if new_email:
        sailthru_parms["keys"] = {"email": new_email}
        sailthru_parms["fields"]["keys"] = 1
        sailthru_parms["keysconflict"] = "merge"

    try:
        sailthru_client = SailthruClient(email_config.sailthru_key,
                                         email_config.sailthru_secret)
        sailthru_response = sailthru_client.api_post("user", sailthru_parms)
    except SailthruClientError as exc:
        error_msg = "Exception attempting to opt-out user {} from Sailthru - {}".format(
            email, text_type(exc))
        log.error(error_msg)
        raise Exception(error_msg)

    if not sailthru_response.is_ok():
        error = sailthru_response.get_error()
        error_msg = "Error attempting to opt-out user {} from Sailthru - {}".format(
            email, error.get_message())
        log.error(error_msg)
        raise Exception(error_msg)
예제 #21
0
def handle_unenroll_done(sender,
                         course_enrollment=None,
                         skip_refund=False,
                         **kwargs):  # pylint: disable=unused-argument
    """
    Signal receiver for unenrollments
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return
예제 #22
0
def email_marketing_user_field_changed(sender, user=None, table=None, setting=None,
                                       old_value=None, new_value=None,
                                       **kwargs):  # pylint: disable=unused-argument
    """
    Update a single user/profile field

    Args:
        sender: Not used
        user: The user object for the user being changed
        table: The name of the table being updated
        setting: The name of the setting being updated
        old_value: Prior value
        new_value: New value
        kwargs: Not used
    """

    # ignore anonymous users
    if user.is_anonymous():
        return

    # ignore anything but User or Profile table
    if table != 'auth_user' and table != 'auth_userprofile':
        return

    # ignore anything not in list of fields to handle
    if setting in CHANGED_FIELDNAMES:
        # skip if not enabled
        #  the check has to be here rather than at the start of the method to avoid
        #  accessing the config during migration 0001_date__add_ecommerce_service_user
        email_config = EmailMarketingConfiguration.current()
        if not email_config.enabled:
            return

        # perform update asynchronously, flag if activation
        update_user.delay(_create_sailthru_user_vars(user, user.profile), user.email, site=_get_current_site(),
                          new_user=False, activation=(setting == 'is_active') and new_value is True)

    elif setting == 'email':
        # email update is special case
        email_config = EmailMarketingConfiguration.current()
        if not email_config.enabled:
            return
        update_user_email.delay(user.email, old_value)
예제 #23
0
def force_unsubscribe_all(sender, **kwargs):  # pylint: disable=unused-argument
    """
    Synchronously(!) unsubscribes the given user from all Sailthru email lists.

    In the future this could be moved to a Celery task, however this is currently
    only used as part of user retirement, where we need a very reliable indication
    of success or failure.

    Args:
        email: Email address to unsubscribe
        new_email (optional): Email address to change 3rd party services to for this user (used in retirement to clear
                              personal information from the service)
    Returns:
        None
    """
    email = kwargs.get('email', None)
    new_email = kwargs.get('new_email', None)

    if not email:
        raise TypeError('Expected an email address to unsubscribe, but received None.')

    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return

    sailthru_parms = {
        "id": email,
        "optout_email": "all",
        "fields": {"optout_email": 1}
    }

    # If we have a new email address to change to, do that as well
    if new_email:
        sailthru_parms["keys"] = {
            "email": new_email
        }
        sailthru_parms["fields"]["keys"] = 1
        sailthru_parms["keysconflict"] = "merge"

    try:
        sailthru_client = SailthruClient(email_config.sailthru_key, email_config.sailthru_secret)
        sailthru_response = sailthru_client.api_post("user", sailthru_parms)
    except SailthruClientError as exc:
        error_msg = "Exception attempting to opt-out user {} from Sailthru - {}".format(email, text_type(exc))
        log.error(error_msg)
        raise Exception(error_msg)

    if not sailthru_response.is_ok():
        error = sailthru_response.get_error()
        error_msg = "Error attempting to opt-out user {} from Sailthru - {}".format(email, error.get_message())
        log.error(error_msg)
        raise Exception(error_msg)
예제 #24
0
def add_email_marketing_cookies(sender, response=None, user=None,
                                **kwargs):  # pylint: disable=unused-argument
    """
    Signal function for adding any cookies needed for email marketing

    Args:
        response: http response object
        user: The user object for the user being changed

    Returns:
        response: http response object with cookie added
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return response

    post_parms = {
        'id': user.email,
        'fields': {'keys': 1},
        'vars': {'last_login_date': datetime.datetime.now().strftime("%Y-%m-%d")}
    }

    # get sailthru_content cookie to capture usage before logon
    request = crum.get_current_request()
    if request:
        sailthru_content = request.COOKIES.get('sailthru_content')
        if sailthru_content:
            post_parms['cookies'] = {'sailthru_content': sailthru_content}

    try:
        sailthru_client = SailthruClient(email_config.sailthru_key, email_config.sailthru_secret)
        sailthru_response = \
            sailthru_client.api_post("user", post_parms)
    except SailthruClientError as exc:
        log.error("Exception attempting to obtain cookie from Sailthru: %s", unicode(exc))
        return response

    if sailthru_response.is_ok():
        if 'keys' in sailthru_response.json and 'cookie' in sailthru_response.json['keys']:
            cookie = sailthru_response.json['keys']['cookie']

            response.set_cookie(
                'sailthru_hid',
                cookie,
                max_age=365 * 24 * 60 * 60  # set for 1 year
            )
        else:
            log.error("No cookie returned attempting to obtain cookie from Sailthru for %s", user.email)
    else:
        error = sailthru_response.get_error()
        log.error("Error attempting to obtain cookie from Sailthru: %s", error.get_message())
    return response
예제 #25
0
def update_user(self, sailthru_vars, email, new_user=False, activation=False):
    """
    Adds/updates Sailthru profile information for a user.
     Args:
        sailthru_vars(dict): User profile information to pass as 'vars' to Sailthru
        email(str): User email address
        new_user(boolean): True if new registration
        activation(boolean): True if activation request
    Returns:
        None
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return

    sailthru_client = SailthruClient(email_config.sailthru_key, email_config.sailthru_secret)
    try:
        sailthru_response = sailthru_client.api_post("user",
                                                     _create_sailthru_user_parm(sailthru_vars, email,
                                                                                new_user, email_config))

    except SailthruClientError as exc:
        log.error("Exception attempting to add/update user %s in Sailthru - %s", email, unicode(exc))
        raise self.retry(exc=exc,
                         countdown=email_config.sailthru_retry_interval,
                         max_retries=email_config.sailthru_max_retries)

    if not sailthru_response.is_ok():
        error = sailthru_response.get_error()
        # put out error and schedule retry
        log.error("Error attempting to add/update user in Sailthru: %s", error.get_message())
        raise self.retry(countdown=email_config.sailthru_retry_interval,
                         max_retries=email_config.sailthru_max_retries)

    # if activating user, send welcome email
    if activation and email_config.sailthru_activation_template:
        try:
            sailthru_response = sailthru_client.api_post("send",
                                                         {"email": email,
                                                          "template": email_config.sailthru_activation_template})
        except SailthruClientError as exc:
            log.error("Exception attempting to send welcome email to user %s in Sailthru - %s", email, unicode(exc))
            raise self.retry(exc=exc,
                             countdown=email_config.sailthru_retry_interval,
                             max_retries=email_config.sailthru_max_retries)

        if not sailthru_response.is_ok():
            error = sailthru_response.get_error()
            # probably a disabled template, just put out error message
            log.error("Error attempting to send welcome email to user in Sailthru: %s", error.get_message())
예제 #26
0
def handle_enroll_status_change(sender, event=None, user=None, mode=None, course_id=None, cost=None, currency=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

    request = crum.get_current_request()
    if not request:
        return

    # figure out course url
    course_url = _build_course_url(request, course_id.to_deprecated_string())

    # pass event to email_marketing.tasks
    update_course_enrollment.delay(user.email, course_url, event, mode,
                                   unit_cost=cost, course_id=course_id, currency=currency,
                                   message_id=request.COOKIES.get('sailthru_bid'))
def email_marketing_register_user(sender, user=None, profile=None, **kwargs):  # pylint: disable=unused-argument
    """
    Called after user created and saved

    Args:
        sender: Not used
        user: The user object for the user being changed
        profile: The user profile for the user being changed
        kwargs: Not used
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return

    # ignore anonymous users
    if user.is_anonymous():
        return

    # perform update asynchronously
    update_user.delay(user.username, new_user=True)
예제 #28
0
def get_email_cookies_via_sailthru(self, user_email, post_parms):
    """
    Adds/updates Sailthru cookie information for a new user.
     Args:
        post_parms(dict): User profile information to pass as 'vars' to Sailthru
    Returns:
        cookie(str): cookie fetched from Sailthru
    """

    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return None

    try:
        sailthru_client = SailthruClient(email_config.sailthru_key,
                                         email_config.sailthru_secret)
        log.info(
            u'Sending to Sailthru the user interest cookie [%s] for user [%s]',
            post_parms.get('cookies', ''), user_email)
        sailthru_response = sailthru_client.api_post("user", post_parms)
    except SailthruClientError as exc:
        log.error(u"Exception attempting to obtain cookie from Sailthru: %s",
                  six.text_type(exc))
        raise SailthruClientError

    if sailthru_response.is_ok():
        if 'keys' in sailthru_response.json and 'cookie' in sailthru_response.json[
                'keys']:
            cookie = sailthru_response.json['keys']['cookie']
            return cookie
        else:
            log.error(
                u"No cookie returned attempting to obtain cookie from Sailthru for %s",
                user_email)
    else:
        error = sailthru_response.get_error()
        # generally invalid email address
        log.info(u"Error attempting to obtain cookie from Sailthru: %s",
                 error.get_message())

    return None
예제 #29
0
def add_email_marketing_cookies(sender, response=None, user=None,
                                **kwargs):  # pylint: disable=unused-argument
    """
    Signal function for adding any cookies needed for email marketing

    Args:
        response: http response object
        user: The user object for the user being changed

    Returns:
        response: http response object with cookie added
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return response

    try:
        sailthru_client = SailthruClient(email_config.sailthru_key, email_config.sailthru_secret)
        sailthru_response = \
            sailthru_client.api_post("user", {'id': user.email, 'fields': {'keys': 1},
                                              'vars': {'last_login_date':
                                                       datetime.datetime.now().strftime("%Y-%m-%d")}})
    except SailthruClientError as exc:
        log.error("Exception attempting to obtain cookie from Sailthru: %s", unicode(exc))
        return response

    if sailthru_response.is_ok():
        if 'keys' in sailthru_response.json and 'cookie' in sailthru_response.json['keys']:
            cookie = sailthru_response.json['keys']['cookie']

            response.set_cookie(
                'sailthru_hid',
                cookie,
                max_age=365 * 24 * 60 * 60  # set for 1 year
            )
        else:
            log.error("No cookie returned attempting to obtain cookie from Sailthru for %s", user.email)
    else:
        error = sailthru_response.get_error()
        log.error("Error attempting to obtain cookie from Sailthru: %s", error.get_message())
    return response
예제 #30
0
def update_course_enrollment(self, email, course_key, mode, site=None):
    """Adds/updates Sailthru when a user adds to cart/purchases/upgrades a course
         Args:
            email: email address of enrolled user
            course_key: course key of course
            mode: mode user is enrolled in
            site: site where user enrolled
        Returns:
            None
    """
    # do not add user if registered at a white label site
    if not is_default_site(site):
        return

    course_url = build_course_url(course_key)
    config = EmailMarketingConfiguration.current()

    try:
        sailthru_client = SailthruClient(config.sailthru_key,
                                         config.sailthru_secret)
    except:
        return

    send_template = config.sailthru_enroll_template
    cost_in_cents = 0

    if not update_unenrolled_list(sailthru_client, email, course_url, False):
        schedule_retry(self, config)

    course_data = _get_course_content(course_key, course_url, sailthru_client,
                                      config)

    item = _build_purchase_item(course_key, course_url, cost_in_cents, mode,
                                course_data)
    options = {}

    if send_template:
        options['send_template'] = send_template

    if not _record_purchase(sailthru_client, email, item, options):
        schedule_retry(self, config)
예제 #31
0
def email_marketing_register_user(sender, user=None, profile=None,
                                  **kwargs):  # pylint: disable=unused-argument
    """
    Called after user created and saved

    Args:
        sender: Not used
        user: The user object for the user being changed
        profile: The user profile for the user being changed
        kwargs: Not used
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return

    # ignore anonymous users
    if user.is_anonymous():
        return

    # perform update asynchronously
    update_user.delay(_create_sailthru_user_vars(user, user.profile), user.email, new_user=True)
def force_unsubscribe_all(sender, **kwargs):  # pylint: disable=unused-argument
    """
    Synchronously(!) unsubscribes the given user from all Sailthru email lists.

    In the future this could be moved to a Celery task, however this is currently
    only used as part of user retirement, where we need a very reliable indication
    of success or failure.

    Args:
        user(User): Django model of type returned from get_user_model()
    Returns:
        None
    """
    user = kwargs.get('user', None)

    if not user:
        raise TypeError('Expected a User type, but received None.')

    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return

    sailthru_parms = {"id": user.email, "keys": {"optout_email": "all"}}

    try:
        sailthru_client = SailthruClient(email_config.sailthru_key,
                                         email_config.sailthru_secret)
        sailthru_response = sailthru_client.api_post("user", sailthru_parms)
    except SailthruClientError as exc:
        error_msg = "Exception attempting to opt-out user %s from Sailthru - %s" % (
            user.email, text_type(exc))
        log.error(error_msg)
        raise Exception(error_msg)

    if not sailthru_response.is_ok():
        error = sailthru_response.get_error()
        error_msg = "Error attempting to opt-out user %s from Sailthru - %s" % (
            user.email, error.get_message())
        log.error(error_msg)
        raise Exception(error_msg)
예제 #33
0
def email_marketing_register_user(sender, user, registration,
                                  **kwargs):  # pylint: disable=unused-argument
    """
    Called after user created and saved

    Args:
        sender: Not used
        user: The user object for the user being changed
        registration: The user registration profile to activate user account
        kwargs: Not used
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return

    # ignore anonymous users
    if user.is_anonymous:
        return

    # perform update asynchronously
    update_user.delay(_create_sailthru_user_vars(user, user.profile, registration=registration), user.email,
                      site=_get_current_site(), new_user=True)
예제 #34
0
def update_course_enrollment(self, email, course_key, mode, site=None):
    """Adds/updates Sailthru when a user adds to cart/purchases/upgrades a course
         Args:
            email: email address of enrolled user
            course_key: course key of course
            mode: mode user is enrolled in
            site: site where user enrolled
        Returns:
            None
    """
    # do not add user if registered at a white label site
    if not is_default_site(site):
        return

    course_url = build_course_url(course_key)
    config = EmailMarketingConfiguration.current()

    try:
        sailthru_client = SailthruClient(config.sailthru_key, config.sailthru_secret)
    except:
        return

    send_template = config.sailthru_enroll_template
    cost_in_cents = 0

    if not update_unenrolled_list(sailthru_client, email, course_url, False):
        schedule_retry(self, config)

    course_data = _get_course_content(course_key, course_url, sailthru_client, config)

    item = _build_purchase_item(course_key, course_url, cost_in_cents, mode, course_data)
    options = {}

    if send_template:
        options['send_template'] = send_template

    if not _record_purchase(sailthru_client, email, item, options):
        schedule_retry(self, config)
예제 #35
0
def get_email_cookies_via_sailthru(self, user_email, post_parms):
    """
    Adds/updates Sailthru cookie information for a new user.
     Args:
        post_parms(dict): User profile information to pass as 'vars' to Sailthru
    Returns:
        cookie(str): cookie fetched from Sailthru
    """

    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return None

    try:
        sailthru_client = SailthruClient(email_config.sailthru_key, email_config.sailthru_secret)
        log.info(
            'Sending to Sailthru the user interest cookie [%s] for user [%s]',
            post_parms.get('cookies', ''),
            user_email
        )
        sailthru_response = sailthru_client.api_post("user", post_parms)
    except SailthruClientError as exc:
        log.error("Exception attempting to obtain cookie from Sailthru: %s", unicode(exc))
        raise SailthruClientError

    if sailthru_response.is_ok():
        if 'keys' in sailthru_response.json and 'cookie' in sailthru_response.json['keys']:
            cookie = sailthru_response.json['keys']['cookie']
            return cookie
        else:
            log.error("No cookie returned attempting to obtain cookie from Sailthru for %s", user_email)
    else:
        error = sailthru_response.get_error()
        # generally invalid email address
        log.info("Error attempting to obtain cookie from Sailthru: %s", error.get_message())

    return None
예제 #36
0
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)
예제 #37
0
def update_course_enrollment(self, email, course_url, event, mode,
                             unit_cost=None, course_id=None,
                             currency=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(object): enroll mode (audit, verification, ...)
        unit_cost: cost if purchase event
        course_id(CourseKey): course 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)
        EnrollStatusChange.unenroll
            An unenroll
        EnrollStatusChange.upgrade_start
            A paid upgrade added to cart
        EnrollStatusChange.upgrade_complete
            A paid upgrade purchase complete
        EnrollStatusChange.paid_start
            A non-free course added to cart
        EnrollStatusChange.paid_complete
            A non-free course purchase complete
    """

    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return

    course_id_string = course_id.to_deprecated_string()

    # Use event type to figure out processing required
    new_enroll = unenroll = fetch_tags = False
    incomplete = send_template = None
    if unit_cost:
        cost_in_cents = unit_cost * 100

    if event == EnrollStatusChange.enroll:
        # new enroll for audit (no cost)
        new_enroll = True
        fetch_tags = True
        send_template = email_config.sailthru_enroll_template
        # set cost of $1 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

    elif event == EnrollStatusChange.upgrade_start:
        # add upgrade to cart
        incomplete = 1

    elif event == EnrollStatusChange.paid_start:
        # add course purchase (probably 'honor') to cart
        incomplete = 1

    elif event == EnrollStatusChange.upgrade_complete:
        # upgrade complete
        fetch_tags = True
        send_template = email_config.sailthru_upgrade_template

    elif event == EnrollStatusChange.paid_complete:
        # paid course purchase complete
        new_enroll = True
        fetch_tags = True
        send_template = email_config.sailthru_purchase_template

    sailthru_client = SailthruClient(email_config.sailthru_key, email_config.sailthru_secret)

    # update the "unenrolled" course array in the user record on Sailthru if new enroll or unenroll
    if new_enroll or unenroll:
        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
        if fetch_tags and email_config.sailthru_get_tags_from_sailthru:
            course_data = _get_course_content(course_url, sailthru_client, email_config)
        else:
            course_data = {}

        # build item description
        item = _build_purchase_item(course_id_string, course_url, cost_in_cents, mode, course_data, course_id)

        # build purchase api options list
        options = {}
        if incomplete and email_config.sailthru_abandoned_cart_template:
            options['reminder_template'] = email_config.sailthru_abandoned_cart_template
            options['reminder_time'] = "+{} minutes".format(email_config.sailthru_abandoned_cart_delay)

        # add appropriate send template
        if send_template:
            options['send_template'] = send_template

        if not _record_purchase(sailthru_client, email, item, incomplete, message_id, options):
            raise self.retry(countdown=email_config.sailthru_retry_interval,
                             max_retries=email_config.sailthru_max_retries)
예제 #38
0
def update_user(self, sailthru_vars, email, new_user=False, activation=False):
    """
    Adds/updates Sailthru profile information for a user.
     Args:
        sailthru_vars(dict): User profile information to pass as 'vars' to Sailthru
        email(str): User email address
        new_user(boolean): True if new registration
        activation(boolean): True if activation request
    Returns:
        None
    """
    email_config = EmailMarketingConfiguration.current()
    if not email_config.enabled:
        return

    sailthru_client = SailthruClient(email_config.sailthru_key,
                                     email_config.sailthru_secret)
    try:
        sailthru_response = sailthru_client.api_post(
            "user",
            _create_sailthru_user_parm(sailthru_vars, email, new_user,
                                       email_config))

    except SailthruClientError as exc:
        log.error(
            "Exception attempting to add/update user %s in Sailthru - %s",
            email, unicode(exc))
        raise self.retry(exc=exc,
                         countdown=email_config.sailthru_retry_interval,
                         max_retries=email_config.sailthru_max_retries)

    if not sailthru_response.is_ok():
        error = sailthru_response.get_error()
        # put out error and schedule retry
        log.error("Error attempting to add/update user in Sailthru: %s",
                  error.get_message())
        raise self.retry(countdown=email_config.sailthru_retry_interval,
                         max_retries=email_config.sailthru_max_retries)

    # if activating user, send welcome email
    if activation and email_config.sailthru_activation_template:
        try:
            sailthru_response = sailthru_client.api_post(
                "send", {
                    "email": email,
                    "template": email_config.sailthru_activation_template
                })
        except SailthruClientError as exc:
            log.error(
                "Exception attempting to send welcome email to user %s in Sailthru - %s",
                email, unicode(exc))
            raise self.retry(exc=exc,
                             countdown=email_config.sailthru_retry_interval,
                             max_retries=email_config.sailthru_max_retries)

        if not sailthru_response.is_ok():
            error = sailthru_response.get_error()
            # probably a disabled template, just put out error message
            log.error(
                "Error attempting to send welcome email to user in Sailthru: %s",
                error.get_message())
예제 #39
0
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)
예제 #40
0
def email_marketing_user_field_changed(sender, user=None, table=None, setting=None,
                                       old_value=None, new_value=None,
                                       **kwargs):  # pylint: disable=unused-argument
    """
    Update a single user/profile field

    Args:
        sender: Not used
        user: The user object for the user being changed
        table: The name of the table being updated
        setting: The name of the setting being updated
        old_value: Prior value
        new_value: New value
        kwargs: Not used
    """

    # ignore anonymous users
    if user.is_anonymous:
        return

    # ignore anything but User, Profile or UserPreference tables
    if table not in {'auth_user', 'auth_userprofile', 'user_api_userpreference'}:
        return

    # ignore anything not in list of fields to handle
    if setting in CHANGED_FIELDNAMES:
        # skip if not enabled
        #  the check has to be here rather than at the start of the method to avoid
        #  accessing the config during migration 0001_date__add_ecommerce_service_user
        email_config = EmailMarketingConfiguration.current()
        if not email_config.enabled:
            return

        # Is the status of the user account changing to active?
        is_activation = (setting == 'is_active') and new_value is True

        # Is this change in the context of an SSO-initiated registration?
        third_party_provider = None
        if third_party_auth.is_enabled():
            running_pipeline = third_party_auth.pipeline.get(crum.get_current_request())
            if running_pipeline:
                third_party_provider = third_party_auth.provider.Registry.get_from_pipeline(running_pipeline)

        # Send a welcome email if the user account is being activated
        # and we are not in a SSO registration flow whose associated
        # identity provider is configured to allow for the sending
        # of a welcome email.
        send_welcome_email = is_activation and (
            third_party_provider is None or third_party_provider.send_welcome_email
        )

        # set the activation flag when the user is marked as activated
        update_user.delay(_create_sailthru_user_vars(user, user.profile), user.email, site=_get_current_site(),
                          new_user=False, activation=send_welcome_email)

    elif setting == 'email':
        # email update is special case
        email_config = EmailMarketingConfiguration.current()
        if not email_config.enabled:
            return
        update_user_email.delay(user.email, old_value)
예제 #41
0
def email_marketing_user_field_changed(sender,
                                       user=None,
                                       table=None,
                                       setting=None,
                                       old_value=None,
                                       new_value=None,
                                       **kwargs):  # pylint: disable=unused-argument
    """
    Update a single user/profile field

    Args:
        sender: Not used
        user: The user object for the user being changed
        table: The name of the table being updated
        setting: The name of the setting being updated
        old_value: Prior value
        new_value: New value
        kwargs: Not used
    """

    # ignore anonymous users
    if user.is_anonymous:
        return

    # ignore anything but User, Profile or UserPreference tables
    if table not in {
            'auth_user', 'auth_userprofile', 'user_api_userpreference'
    }:
        return

    # ignore anything not in list of fields to handle
    if setting in CHANGED_FIELDNAMES:
        # skip if not enabled
        #  the check has to be here rather than at the start of the method to avoid
        #  accessing the config during migration 0001_date__add_ecommerce_service_user
        email_config = EmailMarketingConfiguration.current()
        if not email_config.enabled:
            return

        # Is the status of the user account changing to active?
        is_activation = (setting == 'is_active') and new_value is True

        # Is this change in the context of an SSO-initiated registration?
        third_party_provider = None
        if third_party_auth.is_enabled():
            running_pipeline = third_party_auth.pipeline.get(
                crum.get_current_request())
            if running_pipeline:
                third_party_provider = third_party_auth.provider.Registry.get_from_pipeline(
                    running_pipeline)

        # Send a welcome email if the user account is being activated
        # and we are not in a SSO registration flow whose associated
        # identity provider is configured to allow for the sending
        # of a welcome email.
        send_welcome_email = is_activation and (
            third_party_provider is None
            or third_party_provider.send_welcome_email)

        # set the activation flag when the user is marked as activated
        update_user.delay(_create_sailthru_user_vars(user, user.profile),
                          user.email,
                          site=_get_current_site(),
                          new_user=False,
                          activation=send_welcome_email)

    elif setting == 'email':
        # email update is special case
        email_config = EmailMarketingConfiguration.current()
        if not email_config.enabled:
            return
        update_user_email.delay(user.email, old_value)