Unverified Commit 588f3d9d by Nimisha Asthagiri Committed by GitHub

Merge pull request #16381 from edx/naa/course-update-tests

Schedules: Tests for Course Update messages
parents 4eb49087 da95676e
...@@ -23,15 +23,20 @@ from student.tests.factories import UserFactory ...@@ -23,15 +23,20 @@ from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
SITE_QUERY = 1 SITE_QUERY = 2 # django_site, site_configuration_siteconfiguration
ORG_DEADLINE_QUERY = 1
SCHEDULES_QUERY = 1
COURSE_MODES_QUERY = 1
GLOBAL_DEADLINE_SWITCH_QUERY = 1
COMMERCE_CONFIG_QUERY = 1
NUM_QUERIES_NO_ORG_LIST = 1
NUM_QUERIES_NO_MATCHING_SCHEDULES = SITE_QUERY + SCHEDULES_QUERY SCHEDULES_QUERY = 1 # schedules_schedule
COURSE_MODES_QUERY = 1 # course_modes_coursemode
GLOBAL_DEADLINE_QUERY = 1 # courseware_dynamicupgradedeadlineconfiguration
ORG_DEADLINE_QUERY = 1 # courseware_orgdynamicupgradedeadlineconfiguration
COURSE_DEADLINE_QUERY = 1 # courseware_coursedynamicupgradedeadlineconfiguration
COMMERCE_CONFIG_QUERY = 1 # commerce_commerceconfiguration
NUM_QUERIES_NO_MATCHING_SCHEDULES = (
SITE_QUERY +
SCHEDULES_QUERY
)
NUM_QUERIES_WITH_MATCHES = ( NUM_QUERIES_WITH_MATCHES = (
NUM_QUERIES_NO_MATCHING_SCHEDULES + NUM_QUERIES_NO_MATCHING_SCHEDULES +
...@@ -40,8 +45,9 @@ NUM_QUERIES_WITH_MATCHES = ( ...@@ -40,8 +45,9 @@ NUM_QUERIES_WITH_MATCHES = (
NUM_QUERIES_FIRST_MATCH = ( NUM_QUERIES_FIRST_MATCH = (
NUM_QUERIES_WITH_MATCHES NUM_QUERIES_WITH_MATCHES
+ GLOBAL_DEADLINE_SWITCH_QUERY + GLOBAL_DEADLINE_QUERY
+ ORG_DEADLINE_QUERY + ORG_DEADLINE_QUERY
+ COURSE_DEADLINE_QUERY
+ COMMERCE_CONFIG_QUERY + COMMERCE_CONFIG_QUERY
) )
...@@ -56,7 +62,8 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase): ...@@ -56,7 +62,8 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
ENABLED_CACHES = ['default'] ENABLED_CACHES = ['default']
has_course_queries = False queries_deadline_for_each_course = False
consolidates_emails_for_learner = False
def setUp(self): def setUp(self):
super(ScheduleSendEmailTestBase, self).setUp() super(ScheduleSendEmailTestBase, self).setUp()
...@@ -68,13 +75,17 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase): ...@@ -68,13 +75,17 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True) DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
def _calculate_bin_for_user(self, user): def _calculate_bin_for_user(self, user):
return user.id % self.tested_task.num_bins return user.id % self.task.num_bins
def _get_dates(self, offset=None): def _get_dates(self, offset=None):
current_day = _get_datetime_beginning_of_day(datetime.datetime.now(pytz.UTC)) current_day = _get_datetime_beginning_of_day(datetime.datetime.now(pytz.UTC))
offset = offset or self.expected_offsets[0] offset = offset or self.expected_offsets[0]
target_day = current_day + datetime.timedelta(days=offset) target_day = current_day + datetime.timedelta(days=offset)
return current_day, offset, target_day if self.resolver.schedule_date_field == 'upgrade_deadline':
upgrade_deadline = target_day
else:
upgrade_deadline = current_day + datetime.timedelta(days=7)
return current_day, offset, target_day, upgrade_deadline
def _get_template_overrides(self): def _get_template_overrides(self):
templates_override = deepcopy(settings.TEMPLATES) templates_override = deepcopy(settings.TEMPLATES)
...@@ -82,12 +93,12 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase): ...@@ -82,12 +93,12 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
return templates_override return templates_override
def test_command_task_binding(self): def test_command_task_binding(self):
self.assertEqual(self.tested_command.async_send_task, self.tested_task) self.assertEqual(self.command.async_send_task, self.task)
def test_handle(self): def test_handle(self):
with patch.object(self.tested_command, 'async_send_task') as mock_send: with patch.object(self.command, 'async_send_task') as mock_send:
test_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) test_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
self.tested_command().handle(date='2017-08-01', site_domain_name=self.site_config.site.domain) self.command().handle(date='2017-08-01', site_domain_name=self.site_config.site.domain)
for offset in self.expected_offsets: for offset in self.expected_offsets:
mock_send.enqueue.assert_any_call( mock_send.enqueue.assert_any_call(
...@@ -99,15 +110,15 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase): ...@@ -99,15 +110,15 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
@patch.object(tasks, 'ace') @patch.object(tasks, 'ace')
def test_resolver_send(self, mock_ace): def test_resolver_send(self, mock_ace):
current_day, offset, target_day = self._get_dates() current_day, offset, target_day, _ = self._get_dates()
with patch.object(self.tested_task, 'apply_async') as mock_apply_async: with patch.object(self.task, 'apply_async') as mock_apply_async:
self.tested_task.enqueue(self.site_config.site, current_day, offset) self.task.enqueue(self.site_config.site, current_day, offset)
mock_apply_async.assert_any_call( mock_apply_async.assert_any_call(
(self.site_config.site.id, serialize(target_day), offset, 0, None), (self.site_config.site.id, serialize(target_day), offset, 0, None),
retry=False, retry=False,
) )
mock_apply_async.assert_any_call( mock_apply_async.assert_any_call(
(self.site_config.site.id, serialize(target_day), offset, self.tested_task.num_bins - 1, None), (self.site_config.site.id, serialize(target_day), offset, self.task.num_bins - 1, None),
retry=False, retry=False,
) )
self.assertFalse(mock_ace.send.called) self.assertFalse(mock_ace.send.called)
...@@ -116,22 +127,21 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase): ...@@ -116,22 +127,21 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
@patch.object(tasks, 'ace') @patch.object(tasks, 'ace')
@patch.object(resolvers, 'set_custom_metric') @patch.object(resolvers, 'set_custom_metric')
def test_schedule_bin(self, schedule_count, mock_metric, mock_ace): def test_schedule_bin(self, schedule_count, mock_metric, mock_ace):
with patch.object(self.tested_task, 'async_send_task') as mock_schedule_send: with patch.object(self.task, 'async_send_task') as mock_schedule_send:
current_day, offset, target_day = self._get_dates() current_day, offset, target_day, upgrade_deadline = self._get_dates()
schedules = [ schedules = [
ScheduleFactory.create( ScheduleFactory.create(
start=target_day, start=target_day,
upgrade_deadline=target_day, upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True, enrollment__course__self_paced=True,
) for _ in range(schedule_count) ) for _ in range(schedule_count)
] ]
bins_in_use = frozenset((self._calculate_bin_for_user(s.enrollment.user)) for s in schedules) bins_in_use = frozenset((self._calculate_bin_for_user(s.enrollment.user)) for s in schedules)
is_first_match = True is_first_match = True
course_queries = len(set(s.enrollment.course.id for s in schedules)) if self.has_course_queries else 0
target_day_str = serialize(target_day) target_day_str = serialize(target_day)
for b in range(self.tested_task.num_bins): for b in range(self.task.num_bins):
LOG.debug('Running bin %d', b) 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:
...@@ -139,16 +149,14 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase): ...@@ -139,16 +149,14 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
expected_queries = ( expected_queries = (
# Since this is the first match, we need to cache all of the config models, so we run a # Since this is the first match, we need to cache all of the config models, so we run a
# query for each of those... # query for each of those...
NUM_QUERIES_FIRST_MATCH + course_queries NUM_QUERIES_FIRST_MATCH
) )
is_first_match = False is_first_match = False
else: else:
expected_queries = NUM_QUERIES_WITH_MATCHES expected_queries = NUM_QUERIES_WITH_MATCHES
expected_queries += NUM_QUERIES_NO_ORG_LIST
with self.assertNumQueries(expected_queries, table_blacklist=WAFFLE_TABLES): with self.assertNumQueries(expected_queries, table_blacklist=WAFFLE_TABLES):
self.tested_task.apply(kwargs=dict( self.task.apply(kwargs=dict(
site_id=self.site_config.site.id, target_day_str=target_day_str, day_offset=offset, bin_num=b, site_id=self.site_config.site.id, target_day_str=target_day_str, day_offset=offset, bin_num=b,
)) ))
...@@ -162,18 +170,18 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase): ...@@ -162,18 +170,18 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
self.assertFalse(mock_ace.send.called) self.assertFalse(mock_ace.send.called)
def test_no_course_overview(self): def test_no_course_overview(self):
current_day, offset, target_day = self._get_dates() current_day, offset, target_day, upgrade_deadline = self._get_dates()
schedule = ScheduleFactory.create( schedule = ScheduleFactory.create(
start=target_day, start=target_day,
upgrade_deadline=target_day, upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True, enrollment__course__self_paced=True,
) )
schedule.enrollment.course_id = CourseKey.from_string('edX/toy/Not_2012_Fall') schedule.enrollment.course_id = CourseKey.from_string('edX/toy/Not_2012_Fall')
schedule.enrollment.save() schedule.enrollment.save()
with patch.object(self.tested_task, 'async_send_task') as mock_schedule_send: with patch.object(self.task, 'async_send_task') as mock_schedule_send:
for b in range(self.tested_task.num_bins): for b in range(self.task.num_bins):
self.tested_task.apply(kwargs=dict( self.task.apply(kwargs=dict(
site_id=self.site_config.site.id, site_id=self.site_config.site.id,
target_day_str=serialize(target_day), target_day_str=serialize(target_day),
day_offset=offset, day_offset=offset,
...@@ -214,8 +222,8 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase): ...@@ -214,8 +222,8 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
ScheduleConfigFactory.create(**schedule_config_kwargs) ScheduleConfigFactory.create(**schedule_config_kwargs)
current_datetime = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) current_datetime = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
with patch.object(self.tested_task, 'apply_async') as mock_apply_async: with patch.object(self.task, 'apply_async') as mock_apply_async:
self.tested_task.enqueue(self.site_config.site, current_datetime, 3) self.task.enqueue(self.site_config.site, current_datetime, 3)
if is_enabled: if is_enabled:
self.assertTrue(mock_apply_async.called) self.assertTrue(mock_apply_async.called)
...@@ -237,34 +245,34 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase): ...@@ -237,34 +245,34 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
for config in (this_config, other_config): for config in (this_config, other_config):
ScheduleConfigFactory.create(site=config.site) ScheduleConfigFactory.create(site=config.site)
user1 = UserFactory.create(id=self.tested_task.num_bins) user1 = UserFactory.create(id=self.task.num_bins)
user2 = UserFactory.create(id=self.tested_task.num_bins * 2) user2 = UserFactory.create(id=self.task.num_bins * 2)
current_day, offset, target_day = self._get_dates() current_day, offset, target_day, upgrade_deadline = self._get_dates()
ScheduleFactory.create( ScheduleFactory.create(
upgrade_deadline=target_day, upgrade_deadline=upgrade_deadline,
start=target_day, start=target_day,
enrollment__course__org=filtered_org, enrollment__course__org=filtered_org,
enrollment__course__self_paced=True, enrollment__course__self_paced=True,
enrollment__user=user1, enrollment__user=user1,
) )
ScheduleFactory.create( ScheduleFactory.create(
upgrade_deadline=target_day, upgrade_deadline=upgrade_deadline,
start=target_day, start=target_day,
enrollment__course__org=unfiltered_org, enrollment__course__org=unfiltered_org,
enrollment__course__self_paced=True, enrollment__course__self_paced=True,
enrollment__user=user1, enrollment__user=user1,
) )
ScheduleFactory.create( ScheduleFactory.create(
upgrade_deadline=target_day, upgrade_deadline=upgrade_deadline,
start=target_day, start=target_day,
enrollment__course__org=unfiltered_org, enrollment__course__org=unfiltered_org,
enrollment__course__self_paced=True, enrollment__course__self_paced=True,
enrollment__user=user2, enrollment__user=user2,
) )
with patch.object(self.tested_task, 'async_send_task') as mock_schedule_send: with patch.object(self.task, 'async_send_task') as mock_schedule_send:
self.tested_task.apply(kwargs=dict( self.task.apply(kwargs=dict(
site_id=this_config.site.id, target_day_str=serialize(target_day), day_offset=offset, bin_num=0 site_id=this_config.site.id, target_day_str=serialize(target_day), day_offset=offset, bin_num=0
)) ))
...@@ -273,12 +281,12 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase): ...@@ -273,12 +281,12 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
@ddt.data(True, False) @ddt.data(True, False)
def test_course_end(self, has_course_ended): def test_course_end(self, has_course_ended):
user1 = UserFactory.create(id=self.tested_task.num_bins) user1 = UserFactory.create(id=self.task.num_bins)
current_day, offset, target_day = self._get_dates() current_day, offset, target_day, upgrade_deadline = self._get_dates()
schedule = ScheduleFactory.create( schedule = ScheduleFactory.create(
start=target_day, start=target_day,
upgrade_deadline=target_day, upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True, enrollment__course__self_paced=True,
enrollment__user=user1, enrollment__user=user1,
) )
...@@ -288,8 +296,8 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase): ...@@ -288,8 +296,8 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
schedule.enrollment.course.end = current_day + datetime.timedelta(days=end_date_offset) schedule.enrollment.course.end = current_day + datetime.timedelta(days=end_date_offset)
schedule.enrollment.course.save() schedule.enrollment.course.save()
with patch.object(self.tested_task, 'async_send_task') as mock_schedule_send: with patch.object(self.task, 'async_send_task') as mock_schedule_send:
self.tested_task.apply(kwargs=dict( self.task.apply(kwargs=dict(
site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset, bin_num=0, site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset, bin_num=0,
)) ))
...@@ -299,29 +307,31 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase): ...@@ -299,29 +307,31 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
self.assertTrue(mock_schedule_send.apply_async.called) self.assertTrue(mock_schedule_send.apply_async.called)
@patch.object(tasks, 'ace') @patch.object(tasks, 'ace')
def test_multiple_enrollments(self, mock_ace): def test_multiple_target_schedules(self, mock_ace):
user = UserFactory.create() user = UserFactory.create()
current_day, offset, target_day = self._get_dates() current_day, offset, target_day, upgrade_deadline = self._get_dates()
num_courses = 3 num_courses = 3
for course_index in range(num_courses): for course_index in range(num_courses):
ScheduleFactory.create( ScheduleFactory.create(
start=target_day, start=target_day,
upgrade_deadline=target_day, upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True, enrollment__course__self_paced=True,
enrollment__user=user, enrollment__user=user,
enrollment__course__id=CourseKey.from_string('edX/toy/course{}'.format(course_index)) enrollment__course__id=CourseKey.from_string('edX/toy/course{}'.format(course_index))
) )
course_queries = num_courses if self.has_course_queries else 0 additional_course_queries = num_courses - 1 if self.queries_deadline_for_each_course else 0
expected_query_count = NUM_QUERIES_FIRST_MATCH + course_queries + NUM_QUERIES_NO_ORG_LIST expected_query_count = NUM_QUERIES_FIRST_MATCH + additional_course_queries
with self.assertNumQueries(expected_query_count, table_blacklist=WAFFLE_TABLES): with self.assertNumQueries(expected_query_count, table_blacklist=WAFFLE_TABLES):
with patch.object(self.tested_task, 'async_send_task') as mock_schedule_send: with patch.object(self.task, 'async_send_task') as mock_schedule_send:
self.tested_task.apply(kwargs=dict( self.task.apply(kwargs=dict(
site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset, site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset,
bin_num=self._calculate_bin_for_user(user), bin_num=self._calculate_bin_for_user(user),
)) ))
self.assertEqual(mock_schedule_send.apply_async.call_count, 1)
self.assertFalse(mock_ace.send.called) expected_call_count = 1 if self.consolidates_emails_for_learner else num_courses
self.assertEqual(mock_schedule_send.apply_async.call_count, expected_call_count)
self.assertFalse(mock_ace.send.called)
@ddt.data(1, 10, 100) @ddt.data(1, 10, 100)
def test_templates(self, message_count): def test_templates(self, message_count):
...@@ -330,13 +340,13 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase): ...@@ -330,13 +340,13 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
self.clear_caches() self.clear_caches()
def _assert_template_for_offset(self, offset, message_count): def _assert_template_for_offset(self, offset, message_count):
current_day, offset, target_day = self._get_dates(offset) current_day, offset, target_day, upgrade_deadline = self._get_dates(offset)
user = UserFactory.create() user = UserFactory.create()
for course_index in range(message_count): for course_index in range(message_count):
ScheduleFactory.create( ScheduleFactory.create(
start=target_day, start=target_day,
upgrade_deadline=target_day, upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True, enrollment__course__self_paced=True,
enrollment__user=user, enrollment__user=user,
enrollment__course__id=CourseKey.from_string('edX/toy/course{}'.format(course_index)) enrollment__course__id=CourseKey.from_string('edX/toy/course{}'.format(course_index))
...@@ -351,23 +361,23 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase): ...@@ -351,23 +361,23 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
sent_messages = [] sent_messages = []
with self.settings(TEMPLATES=self._get_template_overrides()): with self.settings(TEMPLATES=self._get_template_overrides()):
with patch.object(self.tested_task, 'async_send_task') as mock_schedule_send: with patch.object(self.task, '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)
num_expected_queries = NUM_QUERIES_NO_ORG_LIST + NUM_QUERIES_FIRST_MATCH num_expected_queries = NUM_QUERIES_FIRST_MATCH
if self.has_course_queries: if self.queries_deadline_for_each_course:
num_expected_queries += message_count num_expected_queries += (message_count - 1)
with self.assertNumQueries(num_expected_queries, table_blacklist=WAFFLE_TABLES): with self.assertNumQueries(num_expected_queries, table_blacklist=WAFFLE_TABLES):
self.tested_task.apply(kwargs=dict( self.task.apply(kwargs=dict(
site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset, site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset,
bin_num=self._calculate_bin_for_user(user), bin_num=self._calculate_bin_for_user(user),
)) ))
self.assertEqual(len(sent_messages), 1) num_expected_messages = 1 if self.consolidates_emails_for_learner else message_count
self.assertEqual(len(sent_messages), num_expected_messages)
with self.assertNumQueries(2): with self.assertNumQueries(2):
for args in sent_messages: self.deliver_task(*sent_messages[0])
self.deliver_task(*args)
self.assertEqual(mock_channel.deliver.call_count, 1) self.assertEqual(mock_channel.deliver.call_count, 1)
for (_name, (_msg, email), _kwargs) in mock_channel.deliver.mock_calls: for (_name, (_msg, email), _kwargs) in mock_channel.deliver.mock_calls:
......
from mock import patch
from unittest import skipUnless
from django.conf import settings
from openedx.core.djangoapps.schedules import resolvers, tasks
from openedx.core.djangoapps.schedules.management.commands import send_course_update as nudge
from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ScheduleSendEmailTestBase
from openedx.core.djangoapps.schedules.management.commands.tests.upsell_base import ScheduleUpsellTestMixin
from openedx.core.djangolib.testing.utils import skip_unless_lms
@skip_unless_lms
@skipUnless(
'openedx.core.djangoapps.schedules.apps.SchedulesConfig' in settings.INSTALLED_APPS,
"Can't test schedules if the app isn't installed",
)
class TestSendCourseUpdate(ScheduleUpsellTestMixin, ScheduleSendEmailTestBase):
__test__ = True
# pylint: disable=protected-access
resolver = resolvers.CourseUpdateResolver
task = tasks.ScheduleCourseUpdate
deliver_task = tasks._course_update_schedule_send
command = nudge.Command
deliver_config = 'deliver_course_update'
enqueue_config = 'enqueue_course_update'
expected_offsets = xrange(-7, -77, -7)
queries_deadline_for_each_course = True
def setUp(self):
super(TestSendCourseUpdate, self).setUp()
patcher = patch('openedx.core.djangoapps.schedules.resolvers.get_week_highlights')
mock_highlights = patcher.start()
mock_highlights.return_value = ['Highlight {}'.format(num + 1) for num in range(3)]
self.addCleanup(patcher.stop)
...@@ -2,7 +2,7 @@ from unittest import skipUnless ...@@ -2,7 +2,7 @@ from unittest import skipUnless
from django.conf import settings from django.conf import settings
from openedx.core.djangoapps.schedules import tasks from openedx.core.djangoapps.schedules import resolvers, tasks
from openedx.core.djangoapps.schedules.management.commands import send_recurring_nudge as nudge from openedx.core.djangoapps.schedules.management.commands import send_recurring_nudge as nudge
from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ScheduleSendEmailTestBase from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ScheduleSendEmailTestBase
from openedx.core.djangoapps.schedules.management.commands.tests.upsell_base import ScheduleUpsellTestMixin from openedx.core.djangoapps.schedules.management.commands.tests.upsell_base import ScheduleUpsellTestMixin
...@@ -18,9 +18,12 @@ class TestSendRecurringNudge(ScheduleUpsellTestMixin, ScheduleSendEmailTestBase) ...@@ -18,9 +18,12 @@ class TestSendRecurringNudge(ScheduleUpsellTestMixin, ScheduleSendEmailTestBase)
__test__ = True __test__ = True
# pylint: disable=protected-access # pylint: disable=protected-access
tested_task = tasks.ScheduleRecurringNudge resolver = resolvers.RecurringNudgeResolver
task = tasks.ScheduleRecurringNudge
deliver_task = tasks._recurring_nudge_schedule_send deliver_task = tasks._recurring_nudge_schedule_send
tested_command = nudge.Command command = nudge.Command
deliver_config = 'deliver_recurring_nudge' deliver_config = 'deliver_recurring_nudge'
enqueue_config = 'enqueue_recurring_nudge' enqueue_config = 'enqueue_recurring_nudge'
expected_offsets = (-3, -10) expected_offsets = (-3, -10)
consolidates_emails_for_learner = True
...@@ -5,11 +5,11 @@ import ddt ...@@ -5,11 +5,11 @@ import ddt
from django.conf import settings from django.conf import settings
from edx_ace import Message from edx_ace import Message
from edx_ace.utils.date import serialize from edx_ace.utils.date import serialize
from mock import Mock, patch from mock import patch
from opaque_keys.edx.locator import CourseLocator from opaque_keys.edx.locator import CourseLocator
from course_modes.models import CourseMode from course_modes.models import CourseMode
from openedx.core.djangoapps.schedules import 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.management.commands.tests.send_email_base import ScheduleSendEmailTestBase from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ScheduleSendEmailTestBase
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
...@@ -27,28 +27,30 @@ LOG = logging.getLogger(__name__) ...@@ -27,28 +27,30 @@ LOG = logging.getLogger(__name__)
class TestUpgradeReminder(ScheduleSendEmailTestBase): class TestUpgradeReminder(ScheduleSendEmailTestBase):
__test__ = True __test__ = True
tested_task = tasks.ScheduleUpgradeReminder resolver = resolvers.UpgradeReminderResolver
task = tasks.ScheduleUpgradeReminder
deliver_task = tasks._upgrade_reminder_schedule_send deliver_task = tasks._upgrade_reminder_schedule_send
tested_command = reminder.Command command = reminder.Command
deliver_config = 'deliver_upgrade_reminder' deliver_config = 'deliver_upgrade_reminder'
enqueue_config = 'enqueue_upgrade_reminder' enqueue_config = 'enqueue_upgrade_reminder'
expected_offsets = (2,) expected_offsets = (2,)
has_course_queries = True queries_deadline_for_each_course = True
consolidates_emails_for_learner = True
@ddt.data(True, False) @ddt.data(True, False)
@patch.object(tasks, 'ace') @patch.object(tasks, 'ace')
def test_verified_learner(self, is_verified, mock_ace): def test_verified_learner(self, is_verified, mock_ace):
user = UserFactory.create(id=self.tested_task.num_bins) user = UserFactory.create(id=self.task.num_bins)
current_day, offset, target_day = self._get_dates() current_day, offset, target_day, upgrade_deadline = self._get_dates()
ScheduleFactory.create( ScheduleFactory.create(
upgrade_deadline=target_day, upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True, enrollment__course__self_paced=True,
enrollment__user=user, enrollment__user=user,
enrollment__mode=CourseMode.VERIFIED if is_verified else CourseMode.AUDIT, enrollment__mode=CourseMode.VERIFIED if is_verified else CourseMode.AUDIT,
) )
self.tested_task.apply(kwargs=dict( self.task.apply(kwargs=dict(
site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset, site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset,
bin_num=self._calculate_bin_for_user(user), bin_num=self._calculate_bin_for_user(user),
)) ))
...@@ -56,12 +58,12 @@ class TestUpgradeReminder(ScheduleSendEmailTestBase): ...@@ -56,12 +58,12 @@ class TestUpgradeReminder(ScheduleSendEmailTestBase):
self.assertEqual(mock_ace.send.called, not is_verified) self.assertEqual(mock_ace.send.called, not is_verified)
def test_filter_out_verified_schedules(self): def test_filter_out_verified_schedules(self):
current_day, offset, target_day = self._get_dates() current_day, offset, target_day, upgrade_deadline = self._get_dates()
user = UserFactory.create() user = UserFactory.create()
schedules = [ schedules = [
ScheduleFactory.create( ScheduleFactory.create(
upgrade_deadline=target_day, upgrade_deadline=upgrade_deadline,
enrollment__user=user, enrollment__user=user,
enrollment__course__self_paced=True, enrollment__course__self_paced=True,
enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(i)), enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(i)),
...@@ -71,10 +73,10 @@ class TestUpgradeReminder(ScheduleSendEmailTestBase): ...@@ -71,10 +73,10 @@ class TestUpgradeReminder(ScheduleSendEmailTestBase):
] ]
sent_messages = [] sent_messages = []
with patch.object(self.tested_task, 'async_send_task') as mock_schedule_send: with patch.object(self.task, 'async_send_task') as mock_schedule_send:
mock_schedule_send.apply_async = lambda args, *_a, **_kw: sent_messages.append(args[1]) mock_schedule_send.apply_async = lambda args, *_a, **_kw: sent_messages.append(args[1])
self.tested_task.apply(kwargs=dict( self.task.apply(kwargs=dict(
site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset, site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset,
bin_num=self._calculate_bin_for_user(user), bin_num=self._calculate_bin_for_user(user),
)) ))
......
...@@ -29,7 +29,7 @@ class ScheduleUpsellTestMixin(object): ...@@ -29,7 +29,7 @@ class ScheduleUpsellTestMixin(object):
def test_upsell(self, enable_config, testcase): def test_upsell(self, enable_config, testcase):
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=enable_config) DynamicUpgradeDeadlineConfiguration.objects.create(enabled=enable_config)
current_day, offset, target_day = self._get_dates() current_day, offset, target_day, _ = self._get_dates()
upgrade_deadline = None upgrade_deadline = None
if testcase.set_deadline: if testcase.set_deadline:
upgrade_deadline = current_day + datetime.timedelta(days=testcase.deadline_offset) upgrade_deadline = current_day + datetime.timedelta(days=testcase.deadline_offset)
...@@ -41,9 +41,9 @@ class ScheduleUpsellTestMixin(object): ...@@ -41,9 +41,9 @@ class ScheduleUpsellTestMixin(object):
) )
sent_messages = [] sent_messages = []
with patch.object(self.tested_task, 'async_send_task') as mock_schedule_send: with patch.object(self.task, 'async_send_task') as mock_schedule_send:
mock_schedule_send.apply_async = lambda args, *_a, **_kw: sent_messages.append(args[1]) mock_schedule_send.apply_async = lambda args, *_a, **_kw: sent_messages.append(args[1])
self.tested_task.apply(kwargs=dict( self.task.apply(kwargs=dict(
site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset, site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset,
bin_num=self._calculate_bin_for_user(schedule.enrollment.user), bin_num=self._calculate_bin_for_user(schedule.enrollment.user),
)) ))
......
...@@ -224,11 +224,11 @@ class InvalidContextError(Exception): ...@@ -224,11 +224,11 @@ class InvalidContextError(Exception):
pass pass
class ScheduleStartResolver(BinnedSchedulesBaseResolver): class RecurringNudgeResolver(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``.
""" """
log_prefix = 'Scheduled Nudge' log_prefix = 'Recurring Nudge'
schedule_date_field = 'start' schedule_date_field = 'start'
num_bins = RECURRING_NUDGE_NUM_BINS num_bins = RECURRING_NUDGE_NUM_BINS
......
...@@ -147,7 +147,7 @@ class ScheduleRecurringNudge(ScheduleMessageBaseTask): ...@@ -147,7 +147,7 @@ class ScheduleRecurringNudge(ScheduleMessageBaseTask):
num_bins = resolvers.RECURRING_NUDGE_NUM_BINS num_bins = resolvers.RECURRING_NUDGE_NUM_BINS
enqueue_config_var = 'enqueue_recurring_nudge' enqueue_config_var = 'enqueue_recurring_nudge'
log_prefix = RECURRING_NUDGE_LOG_PREFIX log_prefix = RECURRING_NUDGE_LOG_PREFIX
resolver = resolvers.ScheduleStartResolver resolver = resolvers.RecurringNudgeResolver
async_send_task = _recurring_nudge_schedule_send async_send_task = _recurring_nudge_schedule_send
def make_message_type(self, day_offset): def make_message_type(self, day_offset):
......
...@@ -4,8 +4,11 @@ ...@@ -4,8 +4,11 @@
Welcome to week {{ week_num }} of our {{ course_name }} course! Welcome to week {{ week_num }} of our {{ course_name }} course!
Here is what you can look forward to learning this week: Here is what you can look forward to learning this week:
{{ week_summary }}
{% endblocktrans %} {% endblocktrans %}
{% for highlight in week_highlights %}
* {{ highlight }}
{% endfor %}
{% include "schedules/edx_ace/common/upsell_cta.txt"%} {% include "schedules/edx_ace/common/upsell_cta.txt"%}
...@@ -25,3 +25,5 @@ class ScheduleConfigFactory(factory.DjangoModelFactory): ...@@ -25,3 +25,5 @@ class ScheduleConfigFactory(factory.DjangoModelFactory):
deliver_recurring_nudge = True deliver_recurring_nudge = True
enqueue_upgrade_reminder = True enqueue_upgrade_reminder = True
deliver_upgrade_reminder = True deliver_upgrade_reminder = True
enqueue_course_update = True
deliver_course_update = True
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