only send upgrade reminders to learners who are eligible

parent e6abdf7e
import datetime import datetime
import itertools
from copy import deepcopy from copy import deepcopy
import logging
from unittest import skipUnless from unittest import skipUnless
import attr import attr
import ddt import ddt
import pytz import pytz
from django.conf import settings from django.conf import settings
from edx_ace import Message
from freezegun import freeze_time
from edx_ace.channel import ChannelType from edx_ace.channel import ChannelType
from edx_ace.test_utils import StubPolicy, patch_channels, patch_policies from edx_ace.test_utils import StubPolicy, patch_channels, patch_policies
from edx_ace.utils.date import serialize from edx_ace.utils.date import serialize
...@@ -17,42 +19,71 @@ from opaque_keys.edx.locator import CourseLocator ...@@ -17,42 +19,71 @@ from opaque_keys.edx.locator import CourseLocator
from course_modes.models import CourseMode from course_modes.models import CourseMode
from course_modes.tests.factories import CourseModeFactory from course_modes.tests.factories import CourseModeFactory
from courseware.models import DynamicUpgradeDeadlineConfiguration from courseware.models import DynamicUpgradeDeadlineConfiguration
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.schedules import resolvers, tasks from openedx.core.djangoapps.schedules import resolvers, tasks
from openedx.core.djangoapps.schedules.management.commands import send_upgrade_reminder as reminder from openedx.core.djangoapps.schedules.management.commands import send_upgrade_reminder as reminder
from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory
from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms, FilteredQueryCountMixin from openedx.core.djangolib.testing.utils import skip_unless_lms
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
# 1) Load the current django site SITE_QUERY = 1
# 2) Query the schedules to find all of the template context information SCHEDULES_QUERY = 1
NUM_QUERIES_NO_MATCHING_SCHEDULES = 2 COURSE_MODES_QUERY = 1
GLOBAL_DEADLINE_SWITCH_QUERY = 1
COMMERCE_CONFIG_QUERY = 1
# 3) Query all course modes for all courses in returned schedules NUM_QUERIES_NO_MATCHING_SCHEDULES = SITE_QUERY + SCHEDULES_QUERY
NUM_QUERIES_WITH_MATCHES = NUM_QUERIES_NO_MATCHING_SCHEDULES + 1
# 1) Global dynamic deadline switch NUM_QUERIES_WITH_MATCHES = (
# 2) Course dynamic deadline switch NUM_QUERIES_NO_MATCHING_SCHEDULES +
# 2) E-commerce configuration COURSE_MODES_QUERY
NUM_QUERIES_WITH_DEADLINE = 3 )
NUM_COURSE_MODES_QUERIES = 1 NUM_QUERIES_FIRST_MATCH = (
NUM_QUERIES_WITH_MATCHES
+ GLOBAL_DEADLINE_SWITCH_QUERY
+ COMMERCE_CONFIG_QUERY
)
LOG = logging.getLogger(__name__)
@ddt.ddt @ddt.ddt
@skip_unless_lms @skip_unless_lms
@skipUnless('openedx.core.djangoapps.schedules.apps.SchedulesConfig' in settings.INSTALLED_APPS, @skipUnless('openedx.core.djangoapps.schedules.apps.SchedulesConfig' in settings.INSTALLED_APPS,
"Can't test schedules if the app isn't installed") "Can't test schedules if the app isn't installed")
class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase): @freeze_time('2017-08-01 00:00:00', tz_offset=0, tick=True)
# pylint: disable=protected-access class TestUpgradeReminder(SharedModuleStoreTestCase):
ENABLED_CACHES = ['default'] ENABLED_CACHES = ['default']
@classmethod
def setUpClass(cls):
super(TestUpgradeReminder, cls).setUpClass()
cls.course = CourseFactory.create(
org='edX',
number='test',
display_name='Test Course',
self_paced=True,
start=datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=30),
)
cls.course_overview = CourseOverview.get_from_id(cls.course.id)
def setUp(self): def setUp(self):
super(TestUpgradeReminder, self).setUp() super(TestUpgradeReminder, self).setUp()
CourseModeFactory(
course_id=self.course.id,
mode_slug=CourseMode.VERIFIED,
expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30),
)
ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 1, 15, 44, 30, tzinfo=pytz.UTC)) ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 1, 15, 44, 30, tzinfo=pytz.UTC))
ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 1, 17, 34, 30, tzinfo=pytz.UTC)) ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 1, 17, 34, 30, tzinfo=pytz.UTC))
ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 2, 15, 34, 30, tzinfo=pytz.UTC)) ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 2, 15, 34, 30, tzinfo=pytz.UTC))
...@@ -96,22 +127,37 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase): ...@@ -96,22 +127,37 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
@patch.object(tasks, 'ace') @patch.object(tasks, 'ace')
@patch.object(tasks.ScheduleUpgradeReminder, 'async_send_task') @patch.object(tasks.ScheduleUpgradeReminder, 'async_send_task')
def test_schedule_bin(self, schedule_count, mock_schedule_send, mock_ace): def test_schedule_bin(self, schedule_count, mock_schedule_send, mock_ace):
upgrade_deadline = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=2)
schedules = [ schedules = [
ScheduleFactory.create( ScheduleFactory.create(
upgrade_deadline=datetime.datetime(2017, 8, 3, 18, 44, 30, tzinfo=pytz.UTC), upgrade_deadline=upgrade_deadline,
enrollment__course__id=CourseLocator('edX', 'toy', 'Bin') enrollment__course=self.course_overview,
) for i in range(schedule_count) ) for i in range(schedule_count)
] ]
bins_in_use = frozenset((s.enrollment.user.id % resolvers.UPGRADE_REMINDER_NUM_BINS) for s in schedules) bins_in_use = frozenset((s.enrollment.user.id % resolvers.UPGRADE_REMINDER_NUM_BINS) for s in schedules)
is_first_match = True
course_switch_queries = len(set(s.enrollment.course.id for s in schedules))
test_datetime = datetime.datetime(2017, 8, 3, 18, tzinfo=pytz.UTC) test_datetime = datetime.datetime(upgrade_deadline.year, upgrade_deadline.month, upgrade_deadline.day, 18,
tzinfo=pytz.UTC)
test_datetime_str = serialize(test_datetime) test_datetime_str = serialize(test_datetime)
for b in range(resolvers.UPGRADE_REMINDER_NUM_BINS): for b in range(resolvers.UPGRADE_REMINDER_NUM_BINS):
LOG.debug('Running bin %d', b)
expected_queries = NUM_QUERIES_NO_MATCHING_SCHEDULES expected_queries = NUM_QUERIES_NO_MATCHING_SCHEDULES
if b in bins_in_use: if b in bins_in_use:
# to fetch course modes for valid schedules if is_first_match:
expected_queries += NUM_COURSE_MODES_QUERIES expected_queries = (
# Since this is the first match, we need to cache all of the config models, so we run a query
# for each of those...
NUM_QUERIES_FIRST_MATCH
+ course_switch_queries
)
is_first_match = False
else:
expected_queries = NUM_QUERIES_WITH_MATCHES
with self.assertNumQueries(expected_queries, table_blacklist=WAFFLE_TABLES): with self.assertNumQueries(expected_queries, table_blacklist=WAFFLE_TABLES):
tasks.ScheduleUpgradeReminder.delay( tasks.ScheduleUpgradeReminder.delay(
...@@ -194,22 +240,27 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase): ...@@ -194,22 +240,27 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
ScheduleFactory.create( ScheduleFactory.create(
upgrade_deadline=datetime.datetime(2017, 8, 3, 17, 44, 30, tzinfo=pytz.UTC), upgrade_deadline=datetime.datetime(2017, 8, 3, 17, 44, 30, tzinfo=pytz.UTC),
enrollment__course__org=filtered_org, enrollment__course__org=filtered_org,
enrollment__course__self_paced=True,
enrollment__user=user1, enrollment__user=user1,
) )
ScheduleFactory.create( ScheduleFactory.create(
upgrade_deadline=datetime.datetime(2017, 8, 3, 17, 44, 30, tzinfo=pytz.UTC), upgrade_deadline=datetime.datetime(2017, 8, 3, 17, 44, 30, tzinfo=pytz.UTC),
enrollment__course__org=unfiltered_org, enrollment__course__org=unfiltered_org,
enrollment__course__self_paced=True,
enrollment__user=user1, enrollment__user=user1,
) )
ScheduleFactory.create( ScheduleFactory.create(
upgrade_deadline=datetime.datetime(2017, 8, 3, 17, 44, 30, tzinfo=pytz.UTC), upgrade_deadline=datetime.datetime(2017, 8, 3, 17, 44, 30, tzinfo=pytz.UTC),
enrollment__course__org=unfiltered_org, enrollment__course__org=unfiltered_org,
enrollment__course__self_paced=True,
enrollment__user=user2, enrollment__user=user2,
) )
test_datetime = datetime.datetime(2017, 8, 3, 17, tzinfo=pytz.UTC) test_datetime = datetime.datetime(2017, 8, 3, 17, tzinfo=pytz.UTC)
test_datetime_str = serialize(test_datetime) test_datetime_str = serialize(test_datetime)
with self.assertNumQueries(NUM_QUERIES_WITH_MATCHES, table_blacklist=WAFFLE_TABLES):
course_switch_queries = 1
with self.assertNumQueries(NUM_QUERIES_FIRST_MATCH + course_switch_queries, table_blacklist=WAFFLE_TABLES):
tasks.ScheduleUpgradeReminder.delay( tasks.ScheduleUpgradeReminder.delay(
limited_config.site.id, target_day_str=test_datetime_str, day_offset=2, bin_num=0, limited_config.site.id, target_day_str=test_datetime_str, day_offset=2, bin_num=0,
org_list=org_list, exclude_orgs=exclude_orgs, org_list=org_list, exclude_orgs=exclude_orgs,
...@@ -226,14 +277,18 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase): ...@@ -226,14 +277,18 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
ScheduleFactory.create( ScheduleFactory.create(
upgrade_deadline=datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC), upgrade_deadline=datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC),
enrollment__user=user, enrollment__user=user,
enrollment__course__self_paced=True,
enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(course_num)) enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(course_num))
) )
for course_num in (1, 2, 3) for course_num in (1, 2, 3)
] ]
num_courses = len(set(s.enrollment.course.id for s in schedules))
test_datetime = datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC) test_datetime = datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC)
test_datetime_str = serialize(test_datetime) test_datetime_str = serialize(test_datetime)
with self.assertNumQueries(NUM_QUERIES_WITH_MATCHES, table_blacklist=WAFFLE_TABLES):
with self.assertNumQueries(NUM_QUERIES_FIRST_MATCH + num_courses, table_blacklist=WAFFLE_TABLES):
tasks.ScheduleUpgradeReminder.delay( tasks.ScheduleUpgradeReminder.delay(
self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2, self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2,
bin_num=user.id % resolvers.UPGRADE_REMINDER_NUM_BINS, bin_num=user.id % resolvers.UPGRADE_REMINDER_NUM_BINS,
...@@ -242,9 +297,8 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase): ...@@ -242,9 +297,8 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
self.assertEqual(mock_schedule_send.apply_async.call_count, 1) self.assertEqual(mock_schedule_send.apply_async.call_count, 1)
self.assertFalse(mock_ace.send.called) self.assertFalse(mock_ace.send.called)
@ddt.data(*itertools.product((1, 10, 100), (2, 10))) @ddt.data(1, 10, 100)
@ddt.unpack def test_templates(self, message_count):
def test_templates(self, message_count, day):
now = datetime.datetime.now(pytz.UTC) now = datetime.datetime.now(pytz.UTC)
future_datetime = now + datetime.timedelta(days=21) future_datetime = now + datetime.timedelta(days=21)
...@@ -253,22 +307,22 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase): ...@@ -253,22 +307,22 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
ScheduleFactory.create( ScheduleFactory.create(
upgrade_deadline=future_datetime, upgrade_deadline=future_datetime,
enrollment__user=user, enrollment__user=user,
enrollment__course__self_paced=True,
enrollment__course__end=future_datetime + datetime.timedelta(days=30),
enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(course_num)) enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(course_num))
) )
for course_num in range(message_count) for course_num in range(message_count)
] ]
for schedule in schedules: for schedule in schedules:
schedule.enrollment.course.self_paced = True
schedule.enrollment.course.end = future_datetime + datetime.timedelta(days=30)
schedule.enrollment.course.save()
CourseModeFactory( CourseModeFactory(
course_id=schedule.enrollment.course.id, course_id=schedule.enrollment.course.id,
mode_slug=CourseMode.VERIFIED, mode_slug=CourseMode.VERIFIED,
expiration_datetime=future_datetime expiration_datetime=future_datetime
) )
num_courses = len(set(s.enrollment.course.id for s in schedules))
test_datetime = future_datetime test_datetime = future_datetime
test_datetime_str = serialize(test_datetime) test_datetime_str = serialize(test_datetime)
...@@ -285,12 +339,11 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase): ...@@ -285,12 +339,11 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
with patch.object(tasks.ScheduleUpgradeReminder, 'async_send_task') as mock_schedule_send: with patch.object(tasks.ScheduleUpgradeReminder, 'async_send_task') as mock_schedule_send:
mock_schedule_send.apply_async = lambda args, *_a, **_kw: sent_messages.append(args) mock_schedule_send.apply_async = lambda args, *_a, **_kw: sent_messages.append(args)
# we execute one query per course to see if it's opted out of dynamic upgrade deadlines, however, # we execute one query per course to see if it's opted out of dynamic upgrade deadlines
# since we create a new course for each schedule in this test, we expect there to be one per message num_expected_queries = NUM_QUERIES_FIRST_MATCH + num_courses
num_expected_queries = NUM_QUERIES_WITH_MATCHES + NUM_QUERIES_WITH_DEADLINE
with self.assertNumQueries(num_expected_queries, table_blacklist=WAFFLE_TABLES): with self.assertNumQueries(num_expected_queries, table_blacklist=WAFFLE_TABLES):
tasks.ScheduleUpgradeReminder.delay( tasks.ScheduleUpgradeReminder.delay(
self.site_config.site.id, target_day_str=test_datetime_str, day_offset=day, self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2,
bin_num=self._calculate_bin_for_user(user), bin_num=self._calculate_bin_for_user(user),
org_list=[schedules[0].enrollment.course.org], org_list=[schedules[0].enrollment.course.org],
) )
...@@ -316,4 +369,68 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase): ...@@ -316,4 +369,68 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
return templates_override return templates_override
def _calculate_bin_for_user(self, user): def _calculate_bin_for_user(self, user):
return user.id % resolvers.RECURRING_NUDGE_NUM_BINS return user.id % resolvers.UPGRADE_REMINDER_NUM_BINS
@patch.object(tasks, '_upgrade_reminder_schedule_send')
def test_dont_send_to_verified_learner(self, mock_schedule_send):
upgrade_deadline = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=2)
ScheduleFactory.create(
upgrade_deadline=upgrade_deadline,
enrollment__user=UserFactory.create(id=resolvers.UPGRADE_REMINDER_NUM_BINS),
enrollment__course=self.course_overview,
enrollment__mode=CourseMode.VERIFIED,
)
test_datetime_str = serialize(datetime.datetime.now(pytz.UTC))
tasks.ScheduleUpgradeReminder.delay(
self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2, bin_num=0,
org_list=[self.course.org],
)
self.assertFalse(mock_schedule_send.called)
self.assertFalse(mock_schedule_send.apply_async.called)
def test_filter_out_verified_schedules(self):
now = datetime.datetime.now(pytz.UTC)
future_datetime = now + datetime.timedelta(days=21)
user = UserFactory.create()
schedules = [
ScheduleFactory.create(
upgrade_deadline=future_datetime,
enrollment__user=user,
enrollment__course__self_paced=True,
enrollment__course__end=future_datetime + datetime.timedelta(days=30),
enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(i)),
enrollment__mode=CourseMode.VERIFIED if i in (0, 3) else CourseMode.AUDIT,
)
for i in range(5)
]
for schedule in schedules:
CourseModeFactory(
course_id=schedule.enrollment.course.id,
mode_slug=CourseMode.VERIFIED,
expiration_datetime=future_datetime
)
test_datetime = future_datetime
test_datetime_str = serialize(test_datetime)
bin_num = self._calculate_bin_for_user(user)
sent_messages = []
with patch.object(tasks.ScheduleUpgradeReminder, 'async_send_task') as mock_schedule_send:
mock_schedule_send.apply_async = lambda args, *_a, **_kw: sent_messages.append(args[1])
tasks.ScheduleUpgradeReminder.delay(
self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2, bin_num=bin_num,
org_list=[schedules[0].enrollment.course.org],
)
messages = [Message.from_string(m) for m in sent_messages]
self.assertEqual(len(messages), 1)
message = messages[0]
self.assertItemsEqual(
message.context['course_ids'],
[str(schedules[i].enrollment.course.id) for i in (1, 2, 4)]
)
...@@ -165,11 +165,10 @@ class BinnedSchedulesBaseResolver(PrefixedDebugLoggerMixin, RecipientResolver): ...@@ -165,11 +165,10 @@ class BinnedSchedulesBaseResolver(PrefixedDebugLoggerMixin, RecipientResolver):
template_context['course_ids'] = course_id_strs template_context['course_ids'] = course_id_strs
first_schedule = user_schedules[0] first_schedule = user_schedules[0]
template_context.update(self.get_template_context(user, user_schedules)) try:
template_context.update(self.get_template_context(user, user_schedules))
# Information for including upsell messaging in template. except InvalidContextError:
_add_upsell_button_information_to_template_context( continue
user, first_schedule, template_context)
yield (user, first_schedule.enrollment.course.language, template_context) yield (user, first_schedule.enrollment.course.language, template_context)
...@@ -188,10 +187,19 @@ class BinnedSchedulesBaseResolver(PrefixedDebugLoggerMixin, RecipientResolver): ...@@ -188,10 +187,19 @@ class BinnedSchedulesBaseResolver(PrefixedDebugLoggerMixin, RecipientResolver):
dict: This dict must be JSON serializable (no datetime objects!). When rendering the message templates it dict: This dict must be JSON serializable (no datetime objects!). When rendering the message templates it
it will be used as the template context. Note that it will also include several default values that it will be used as the template context. Note that it will also include several default values that
injected into all template contexts. See `get_base_template_context` for more information. injected into all template contexts. See `get_base_template_context` for more information.
Raises:
InvalidContextError: If this user and set of schedules are not valid for this type of message. Raising this
exception will prevent this user from receiving the message, but allow other messages to be sent to other
users.
""" """
return {} return {}
class InvalidContextError(Exception):
pass
class ScheduleStartResolver(BinnedSchedulesBaseResolver): class ScheduleStartResolver(BinnedSchedulesBaseResolver):
""" """
Send a message to all users whose schedule started at ``self.current_date`` + ``day_offset``. Send a message to all users whose schedule started at ``self.current_date`` + ``day_offset``.
...@@ -202,13 +210,18 @@ class ScheduleStartResolver(BinnedSchedulesBaseResolver): ...@@ -202,13 +210,18 @@ class ScheduleStartResolver(BinnedSchedulesBaseResolver):
def get_template_context(self, user, user_schedules): def get_template_context(self, user, user_schedules):
first_schedule = user_schedules[0] first_schedule = user_schedules[0]
return { context = {
'course_name': first_schedule.enrollment.course.display_name, 'course_name': first_schedule.enrollment.course.display_name,
'course_url': absolute_url( 'course_url': absolute_url(
self.site, reverse('course_root', args=[str(first_schedule.enrollment.course_id)]) self.site, reverse('course_root', args=[str(first_schedule.enrollment.course_id)])
), ),
} }
# Information for including upsell messaging in template.
context.update(_get_upsell_information_for_schedule(user, first_schedule))
return context
def _get_datetime_beginning_of_day(dt): def _get_datetime_beginning_of_day(dt):
""" """
...@@ -226,20 +239,41 @@ class UpgradeReminderResolver(BinnedSchedulesBaseResolver): ...@@ -226,20 +239,41 @@ class UpgradeReminderResolver(BinnedSchedulesBaseResolver):
num_bins = UPGRADE_REMINDER_NUM_BINS num_bins = UPGRADE_REMINDER_NUM_BINS
def get_template_context(self, user, user_schedules): def get_template_context(self, user, user_schedules):
first_schedule = user_schedules[0] course_id_strs = []
return { course_links = []
'course_links': [ first_valid_upsell_context = None
{ first_schedule = None
'url': absolute_url(self.site, reverse('course_root', args=[str(s.enrollment.course_id)])), for schedule in user_schedules:
'name': s.enrollment.course.display_name upsell_context = _get_upsell_information_for_schedule(user, schedule)
} for s in user_schedules if not upsell_context['show_upsell']:
], continue
if first_valid_upsell_context is None:
first_schedule = schedule
first_valid_upsell_context = upsell_context
course_id_str = str(schedule.enrollment.course_id)
course_id_strs.append(course_id_str)
course_links.append({
'url': absolute_url(self.site, reverse('course_root', args=[course_id_str])),
'name': schedule.enrollment.course.display_name
})
if first_schedule is None:
self.log_debug('No courses eligible for upgrade for user.')
raise InvalidContextError()
context = {
'course_links': course_links,
'first_course_name': first_schedule.enrollment.course.display_name, 'first_course_name': first_schedule.enrollment.course.display_name,
'cert_image': absolute_url(self.site, static('course_experience/images/verified-cert.png')), 'cert_image': absolute_url(self.site, static('course_experience/images/verified-cert.png')),
'course_ids': course_id_strs,
} }
context.update(first_valid_upsell_context)
return context
def _add_upsell_button_information_to_template_context(user, schedule, template_context): def _get_upsell_information_for_schedule(user, schedule):
template_context = {}
enrollment = schedule.enrollment enrollment = schedule.enrollment
course = enrollment.course course = enrollment.course
...@@ -258,6 +292,7 @@ def _add_upsell_button_information_to_template_context(user, schedule, template_ ...@@ -258,6 +292,7 @@ def _add_upsell_button_information_to_template_context(user, schedule, template_
) )
template_context['show_upsell'] = has_verified_upgrade_link template_context['show_upsell'] = has_verified_upgrade_link
return template_context
def _get_verified_upgrade_link(user, schedule): def _get_verified_upgrade_link(user, schedule):
...@@ -293,10 +328,9 @@ class CourseUpdateResolver(BinnedSchedulesBaseResolver): ...@@ -293,10 +328,9 @@ class CourseUpdateResolver(BinnedSchedulesBaseResolver):
course_id_str = str(enrollment.course_id) course_id_str = str(enrollment.course_id)
template_context.update({ template_context.update({
'student_name': user.profile.name,
'course_name': schedule.enrollment.course.display_name, 'course_name': schedule.enrollment.course.display_name,
'course_url': absolute_url( 'course_url': absolute_url(
self.site, reverse('course_root', args=[str(schedule.enrollment.course_id)]) self.site, reverse('course_root', args=[course_id_str])
), ),
'week_num': week_num, 'week_num': week_num,
'week_summary': week_summary, 'week_summary': week_summary,
...@@ -304,6 +338,7 @@ class CourseUpdateResolver(BinnedSchedulesBaseResolver): ...@@ -304,6 +338,7 @@ class CourseUpdateResolver(BinnedSchedulesBaseResolver):
# This is used by the bulk email optout policy # This is used by the bulk email optout policy
'course_ids': [course_id_str], 'course_ids': [course_id_str],
}) })
template_context.update(_get_upsell_information_for_schedule(user, schedule))
yield (user, schedule.enrollment.course.language, template_context) yield (user, schedule.enrollment.course.language, template_context)
......
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