diff --git a/openedx/core/djangoapps/schedules/management/commands/__init__.py b/openedx/core/djangoapps/schedules/management/commands/__init__.py index 3be3b9f1a9..258fdc9b35 100644 --- a/openedx/core/djangoapps/schedules/management/commands/__init__.py +++ b/openedx/core/djangoapps/schedules/management/commands/__init__.py @@ -9,6 +9,7 @@ from openedx.core.djangoapps.schedules.utils import PrefixedDebugLoggerMixin class SendEmailBaseCommand(PrefixedDebugLoggerMixin, BaseCommand): resolver_class = None # define in subclass + async_send_task = None # define in subclass def add_arguments(self, parser): parser.add_argument( @@ -36,7 +37,7 @@ class SendEmailBaseCommand(PrefixedDebugLoggerMixin, BaseCommand): site = Site.objects.get(domain__iexact=options['site_domain_name']) self.log_debug('Running for site %s', site.domain) - return self.resolver_class(site, current_date) + return self.resolver_class(site, current_date, async_send_task=self.async_send_task) def send_emails(self, resolver, *args, **options): pass # define in subclass diff --git a/openedx/core/djangoapps/schedules/management/commands/send_course_update.py b/openedx/core/djangoapps/schedules/management/commands/send_course_update.py index 7846fc5ea5..a74339f779 100644 --- a/openedx/core/djangoapps/schedules/management/commands/send_course_update.py +++ b/openedx/core/djangoapps/schedules/management/commands/send_course_update.py @@ -1,9 +1,11 @@ from openedx.core.djangoapps.schedules.management.commands import SendEmailBaseCommand from openedx.core.djangoapps.schedules.resolvers import CourseUpdateResolver +from openedx.core.djangoapps.schedules.tasks import course_update_schedule_bin class Command(SendEmailBaseCommand): resolver_class = CourseUpdateResolver + async_send_task = course_update_schedule_bin def __init__(self, *args, **kwargs): super(Command, self).__init__(*args, **kwargs) diff --git a/openedx/core/djangoapps/schedules/management/commands/send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/send_recurring_nudge.py index 5f3b316e60..1de9c799aa 100644 --- a/openedx/core/djangoapps/schedules/management/commands/send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/send_recurring_nudge.py @@ -1,9 +1,11 @@ from openedx.core.djangoapps.schedules.management.commands import SendEmailBaseCommand from openedx.core.djangoapps.schedules.resolvers import ScheduleStartResolver +from openedx.core.djangoapps.schedules.tasks import recurring_nudge_schedule_bin class Command(SendEmailBaseCommand): resolver_class = ScheduleStartResolver + async_send_task = recurring_nudge_schedule_bin def __init__(self, *args, **kwargs): super(Command, self).__init__(*args, **kwargs) diff --git a/openedx/core/djangoapps/schedules/management/commands/send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/send_upgrade_reminder.py index c251c85275..c53f122e7e 100644 --- a/openedx/core/djangoapps/schedules/management/commands/send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/send_upgrade_reminder.py @@ -1,9 +1,11 @@ from openedx.core.djangoapps.schedules.management.commands import SendEmailBaseCommand from openedx.core.djangoapps.schedules.resolvers import UpgradeReminderResolver +from openedx.core.djangoapps.schedules.tasks import upgrade_reminder_schedule_bin class Command(SendEmailBaseCommand): resolver_class = UpgradeReminderResolver + async_send_task = upgrade_reminder_schedule_bin def __init__(self, *args, **kwargs): super(Command, self).__init__(*args, **kwargs) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_base.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_base.py index ce75828775..c69e89aa3e 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_base.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_base.py @@ -28,7 +28,8 @@ class TestSendEmailBaseCommand(CacheIsolationTestCase): self.command.make_resolver(site_domain_name='example.com', date='2017-09-29') resolver_class.assert_called_once_with( example_site, - datetime.datetime(2017, 9, 29, tzinfo=pytz.UTC) + datetime.datetime(2017, 9, 29, tzinfo=pytz.UTC), + async_send_task=None, ) def test_handle(self): diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 761ec19816..8b549b3ecb 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -64,16 +64,20 @@ class TestSendRecurringNudge(FilteredQueryCountMixin, CacheIsolationTestCase): def test_handle(self, mock_resolver): test_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) nudge.Command().handle(date='2017-08-01', site_domain_name=self.site_config.site.domain) - mock_resolver.assert_called_with(self.site_config.site, test_day) + mock_resolver.assert_called_with( + self.site_config.site, + test_day, + async_send_task=nudge.Command.async_send_task, + ) for day in (-3, -10): mock_resolver().send.assert_any_call(day, None) @patch.object(tasks, 'ace') - @patch.object(resolvers.ScheduleStartResolver, 'async_send_task') - def test_resolver_send(self, mock_schedule_bin, mock_ace): + def test_resolver_send(self, mock_ace): current_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) - nudge.ScheduleStartResolver(self.site_config.site, current_day).send(-3) + mock_schedule_bin = Mock() + nudge.ScheduleStartResolver(self.site_config.site, current_day, mock_schedule_bin).send(-3) test_day = current_day + datetime.timedelta(days=-3) self.assertFalse(mock_schedule_bin.called) mock_schedule_bin.apply_async.assert_any_call( @@ -171,12 +175,16 @@ class TestSendRecurringNudge(FilteredQueryCountMixin, CacheIsolationTestCase): self.assertFalse(mock_ace.send.called) @patch.object(tasks, 'ace') - @patch.object(resolvers.ScheduleStartResolver, 'async_send_task') - def test_enqueue_disabled(self, mock_schedule_bin, mock_ace): + def test_enqueue_disabled(self, mock_ace): ScheduleConfigFactory.create(site=self.site_config.site, enqueue_recurring_nudge=False) + mock_schedule_bin = Mock() current_datetime = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) - nudge.ScheduleStartResolver(self.site_config.site, current_datetime).send(3) + nudge.ScheduleStartResolver( + self.site_config.site, + current_datetime, + mock_schedule_bin, + ).send(3) self.assertFalse(mock_schedule_bin.called) self.assertFalse(mock_schedule_bin.apply_async.called) self.assertFalse(mock_ace.send.called) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index 1134bfbb06..5ceaff397e 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -67,18 +67,22 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase): def test_handle(self, mock_resolver): test_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) reminder.Command().handle(date='2017-08-01', site_domain_name=self.site_config.site.domain) - mock_resolver.assert_called_with(self.site_config.site, test_day) + mock_resolver.assert_called_with( + self.site_config.site, + test_day, + async_send_task=reminder.Command.async_send_task, + ) mock_resolver().send.assert_any_call(2, None) @patch.object(tasks, 'ace') - @patch.object(resolvers.UpgradeReminderResolver, 'async_send_task') - def test_resolver_send(self, mock_schedule_bin, mock_ace): + def test_resolver_send(self, mock_ace): current_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) test_day = current_day + datetime.timedelta(days=2) ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 3, 15, 34, 30, tzinfo=pytz.UTC)) + mock_schedule_bin = Mock() - reminder.UpgradeReminderResolver(self.site_config.site, current_day).send(2) + reminder.UpgradeReminderResolver(self.site_config.site, current_day, mock_schedule_bin).send(2) self.assertFalse(mock_schedule_bin.called) mock_schedule_bin.apply_async.assert_any_call( (self.site_config.site.id, serialize(test_day), 2, 0, [], True, None), @@ -155,12 +159,16 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase): self.assertFalse(mock_ace.send.called) @patch.object(tasks, 'ace') - @patch.object(resolvers.UpgradeReminderResolver, 'async_send_task') - def test_enqueue_disabled(self, mock_schedule_bin, mock_ace): + def test_enqueue_disabled(self, mock_ace): ScheduleConfigFactory.create(site=self.site_config.site, enqueue_upgrade_reminder=False) + mock_schedule_bin = Mock() current_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) - reminder.UpgradeReminderResolver(self.site_config.site, current_day).send(3) + reminder.UpgradeReminderResolver( + self.site_config.site, + current_day, + async_send_task=mock_schedule_bin, + ).send(3) self.assertFalse(mock_schedule_bin.called) self.assertFalse(mock_schedule_bin.apply_async.called) self.assertFalse(mock_ace.send.called) diff --git a/openedx/core/djangoapps/schedules/resolvers.py b/openedx/core/djangoapps/schedules/resolvers.py index 7fe74d6e87..35b16f884d 100644 --- a/openedx/core/djangoapps/schedules/resolvers.py +++ b/openedx/core/djangoapps/schedules/resolvers.py @@ -14,14 +14,10 @@ from edx_ace.recipient_resolver import RecipientResolver from edx_ace.utils.date import serialize from courseware.date_summary import verified_upgrade_deadline_link, verified_upgrade_link_is_valid +from openedx.core.djangoapps.monitoring_utils import set_custom_metric, function_trace from openedx.core.djangoapps.schedules.config import COURSE_UPDATE_WAFFLE_FLAG from openedx.core.djangoapps.schedules.exceptions import CourseUpdateDoesNotExist from openedx.core.djangoapps.schedules.models import Schedule, ScheduleConfig -from openedx.core.djangoapps.schedules.tasks import ( - recurring_nudge_schedule_bin, - upgrade_reminder_schedule_bin, - course_update_schedule_bin, -) from openedx.core.djangoapps.schedules.utils import PrefixedDebugLoggerMixin from openedx.core.djangoapps.schedules.template_context import ( absolute_url, @@ -49,20 +45,20 @@ class BinnedSchedulesBaseResolver(PrefixedDebugLoggerMixin, RecipientResolver): Arguments: site -- Site object that filtered Schedules will be a part of current_date -- datetime that will be used (with time zeroed-out) as the current date in the queries + async_send_task -- celery task function which this resolver will call out to Static attributes: - async_send_task -- celery task function which this resolver will call out to num_bins -- the int number of bins to split the users into enqueue_config_var -- the string field name of the config variable on ScheduleConfig to check before enqueuing """ - async_send_task = None # define in subclass num_bins = DEFAULT_NUM_BINS enqueue_config_var = None # define in subclass - def __init__(self, site, current_date, *args, **kwargs): + def __init__(self, site, current_date, async_send_task, *args, **kwargs): super(BinnedSchedulesBaseResolver, self).__init__(*args, **kwargs) self.site = site self.current_date = current_date.replace(hour=0, minute=0, second=0) + self.async_send_task = async_send_task def send(self, day_offset, override_recipient_email=None): if not self.is_enqueue_enabled(): @@ -125,7 +121,6 @@ class ScheduleStartResolver(BinnedSchedulesBaseResolver): """ Send a message to all users whose schedule started at ``self.current_date`` + ``day_offset``. """ - async_send_task = recurring_nudge_schedule_bin num_bins = RECURRING_NUDGE_NUM_BINS enqueue_config_var = 'enqueue_recurring_nudge' @@ -250,7 +245,6 @@ class UpgradeReminderResolver(BinnedSchedulesBaseResolver): """ Send a message to all users whose verified upgrade deadline is at ``self.current_date`` + ``day_offset``. """ - async_send_task = upgrade_reminder_schedule_bin num_bins = UPGRADE_REMINDER_NUM_BINS enqueue_config_var = 'enqueue_upgrade_reminder' @@ -275,17 +269,17 @@ def _upgrade_reminder_schedules_for_bin(site, current_datetime, target_datetime, course_id_strs = [str(schedule.enrollment.course_id) for schedule in user_schedules] first_schedule = user_schedules[0] - template_context = get_base_template_context(self.site) + template_context = get_base_template_context(site) template_context.update({ 'student_name': user.profile.name, 'course_links': [ { - 'url': absolute_url(self.site, reverse('course_root', args=[str(s.enrollment.course_id)])), + 'url': absolute_url(site, reverse('course_root', args=[str(s.enrollment.course_id)])), 'name': s.enrollment.course.display_name } for s in user_schedules ], '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(site, static('course_experience/images/verified-cert.png')), # This is used by the bulk email optout policy 'course_ids': course_id_strs, @@ -331,7 +325,6 @@ class CourseUpdateResolver(BinnedSchedulesBaseResolver): Send a message to all users whose schedule started at ``self.current_date`` + ``day_offset`` and the course has updates. """ - async_send_task = course_update_schedule_bin num_bins = COURSE_UPDATE_NUM_BINS enqueue_config_var = 'enqueue_course_update' diff --git a/openedx/core/djangoapps/schedules/tests/test_resolvers.py b/openedx/core/djangoapps/schedules/tests/test_resolvers.py index 1638f2158a..3c65ea5dcf 100644 --- a/openedx/core/djangoapps/schedules/tests/test_resolvers.py +++ b/openedx/core/djangoapps/schedules/tests/test_resolvers.py @@ -24,12 +24,12 @@ class TestBinnedSchedulesBaseResolver(CacheIsolationTestCase): self.site_config = SiteConfigurationFactory.create(site=self.site) self.schedule_config = ScheduleConfigFactory.create(site=self.site) - def setup_resolver(self, site=None, current_date=None): + def setup_resolver(self, site=None, current_date=None, async_send_task=None): if site is None: site = self.site if current_date is None: current_date = datetime.datetime.now() - resolver = BinnedSchedulesBaseResolver(self.site, current_date) + resolver = BinnedSchedulesBaseResolver(self.site, current_date, async_send_task) return resolver def test_init_site(self):