Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@

from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.sites.models import Site
from django.core.management import BaseCommand
from django.utils import timezone
from edx_django_utils.plugins import pluggable_override
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import CourseLocator

Expand All @@ -23,13 +25,36 @@
from openedx.core.constants import COURSE_PUBLISHED
from openedx.core.djangoapps.catalog.utils import get_programs
from openedx.core.djangoapps.programs.utils import ProgramProgressMeter
from openedx.features.enterprise_support.api import get_enterprise_learner_data_from_db

User = get_user_model()

LOGGER = logging.getLogger(__name__)


@pluggable_override('OVERRIDE_PROGRAM_NUDGE_SUGGESTED_COURSE_URL')
def get_suggested_course_url(
user: AbstractBaseUser,
suggested_course: dict,
suggested_course_run: dict,
) -> str:
"""
Return the URL that the nudge email's suggested course link should point at.

Arguments:
user: The learner the nudge email is being sent to.
suggested_course: The catalog course being suggested.
suggested_course_run: The catalog course run being suggested.

Returns:
str: The absolute URL of the suggested course's about page.

This function can be overridden by an installed plugin via the
OVERRIDE_PROGRAM_NUDGE_SUGGESTED_COURSE_URL setting, for deployments that
route some learners to a different course landing page.
"""
return urljoin(settings.MKTG_URLS.get('ROOT'), suggested_course_run['marketing_url'])


class Command(BaseCommand):
"""
Django management command for sending nudge emails to learners
Expand Down Expand Up @@ -185,16 +210,11 @@ def emit_event(self, user, program, suggested_course_run, suggested_course, comp
"""
Emit the Segment event which will be used by Braze to send the email
"""
learner_data = get_enterprise_learner_data_from_db(user)
enterprise_customer = learner_data[0]['enterprise_customer'] if learner_data else None
if enterprise_customer and enterprise_customer['enable_learner_portal']:
# If user is an enterprise learner then we want to redirect to B2B course landing page on learner portal.
recommended_course_url = urljoin(
settings.ENTERPRISE_LEARNER_PORTAL_BASE_URL,
'/'.join([enterprise_customer['slug'], 'course', suggested_course['key']]),
)
else:
recommended_course_url = urljoin(settings.MKTG_URLS.get('ROOT'), suggested_course_run['marketing_url'])
recommended_course_url = get_suggested_course_url(
user=user,
suggested_course=suggested_course,
suggested_course_run=suggested_course_run,
)

event_properties = {
'COURSE_ONE_NAME': completed_course_run['title'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
"""
from datetime import timedelta
from unittest.mock import patch
from urllib.parse import urljoin

import ddt
from django.conf import settings
from django.core.management import call_command
from django.test.utils import override_settings
from django.utils import timezone
Expand All @@ -17,11 +19,18 @@
from lms.djangoapps.grades.models import PersistentCourseGrade
from openedx.core.djangoapps.catalog.tests.factories import CourseFactory as CatalogCourseFactory
from openedx.core.djangoapps.catalog.tests.factories import CourseRunFactory, ProgramFactory
from openedx.features.enterprise_support.tests.factories import EnterpriseCustomerUserFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory

LOG_PATH = 'lms.djangoapps.program_enrollments.management.commands.send_program_course_nudge_email'
TEST_MODULE_PATH = __name__


def replacement_suggested_course_url(prev_fn, user, suggested_course, suggested_course_run):
"""
Test override that builds its own URL instead of delegating to ``prev_fn``.
"""
return f'https://example.com/{user.username}/{suggested_course["key"]}'


@ddt.ddt
Expand All @@ -40,10 +49,6 @@ def setUp(self):
self.user_1 = UserFactory()
self.user_2 = UserFactory()

self.enterprise_customer_user = EnterpriseCustomerUserFactory.create(
user_id=self.user_1.id, enterprise_customer__enable_learner_portal=True
)

completed_course = CourseFactory.create()
self.completed_course_run = CourseRunFactory(key=str(completed_course.id))
user_1_in_progress_course = CourseFactory.create()
Expand Down Expand Up @@ -108,7 +113,6 @@ def enroll_user(self, user, course, create_grade=False):
@patch('common.djangoapps.student.models.course_enrollment.segment.track')
@patch('lms.djangoapps.program_enrollments.management.commands.send_program_course_nudge_email.get_programs')
@patch('lms.djangoapps.certificates.api.certificates_viewable_for_course', return_value=True)
@override_settings(FEATURES=dict(ENABLE_ENTERPRISE_INTEGRATION=True))
def test_email_send(self, add_no_commit, __, get_programs_mock, mock_track):
"""
Test Segment fired as expected.
Expand Down Expand Up @@ -145,7 +149,6 @@ def test_email_send(self, add_no_commit, __, get_programs_mock, mock_track):
)
@patch('common.djangoapps.student.models.course_enrollment.segment.track')
@patch('lms.djangoapps.program_enrollments.management.commands.send_program_course_nudge_email.get_programs')
@override_settings(FEATURES=dict(ENABLE_ENTERPRISE_INTEGRATION=True))
def test_email_no_course_recommendation(self, add_no_commit, get_programs_mock, mock_track):
"""
Test Segment fired as expected.
Expand All @@ -164,3 +167,37 @@ def test_email_no_course_recommendation(self, add_no_commit, get_programs_mock,
)
)
assert mock_track.call_count == 0

@ddt.data(
{
'override_setting': (),
'expected_url_template': '{default_url}',
},
{
'override_setting': f'{TEST_MODULE_PATH}.replacement_suggested_course_url',
'expected_url_template': 'https://example.com/{username}/{course_key}',
},
)
@ddt.unpack
@patch('common.djangoapps.student.models.course_enrollment.segment.track')
@patch('lms.djangoapps.program_enrollments.management.commands.send_program_course_nudge_email.get_programs')
@patch('lms.djangoapps.certificates.api.certificates_viewable_for_course', return_value=True)
def test_suggested_course_url(self, __, get_programs_mock, mock_track, override_setting, expected_url_template):
"""
Test the suggested course link, with no override configured and with one that replaces it.
"""
get_programs_mock.return_value = [self.partially_completed_program_1]
with override_settings(OVERRIDE_PROGRAM_NUDGE_SUGGESTED_COURSE_URL=override_setting):
call_command(self.command)

default_url = urljoin(settings.MKTG_URLS.get('ROOT'), self.not_started_course_run_1['marketing_url'])
course_key = self.catalog_not_started_course_1['key']
tracked_urls = {call.args[0]: call.args[2]['COURSE_TWO_LINK'] for call in mock_track.call_args_list}
assert tracked_urls == {
user.id: expected_url_template.format(
default_url=default_url,
username=user.username,
course_key=course_key,
)
for user in (self.user_1, self.user_2)
}
Loading