Merge pull request #16209 from edx/thallada/dont-send-emails-after-course-end
Don't send RET emails after course end
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import json
|
||||
|
||||
import factory
|
||||
from django.utils.timezone import get_current_timezone
|
||||
from factory.django import DjangoModelFactory
|
||||
|
||||
from ..models import CourseOverview
|
||||
@@ -14,7 +15,8 @@ class CourseOverviewFactory(DjangoModelFactory):
|
||||
|
||||
version = CourseOverview.VERSION
|
||||
pre_requisite_courses = []
|
||||
start = factory.Faker('past_datetime')
|
||||
start = factory.Faker('past_datetime', tzinfo=get_current_timezone())
|
||||
end = factory.Faker('future_datetime', tzinfo=get_current_timezone())
|
||||
org = 'edX'
|
||||
|
||||
@factory.lazy_attribute
|
||||
|
||||
@@ -18,6 +18,7 @@ from opaque_keys.edx.locator import CourseLocator
|
||||
from course_modes.models import CourseMode
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
from courseware.models import DynamicUpgradeDeadlineConfiguration
|
||||
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
|
||||
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.tests.factories import ScheduleConfigFactory, ScheduleFactory
|
||||
@@ -61,9 +62,9 @@ class TestSendRecurringNudge(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
|
||||
@patch.object(nudge.Command, 'resolver_class')
|
||||
def test_handle(self, mock_resolver):
|
||||
test_time = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
|
||||
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_time)
|
||||
mock_resolver.assert_called_with(self.site_config.site, test_day)
|
||||
|
||||
for day in (-3, -10):
|
||||
mock_resolver().send.assert_any_call(day, None)
|
||||
@@ -71,16 +72,16 @@ class TestSendRecurringNudge(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
@patch.object(tasks, 'ace')
|
||||
@patch.object(resolvers.ScheduleStartResolver, 'async_send_task')
|
||||
def test_resolver_send(self, mock_schedule_bin, mock_ace):
|
||||
current_time = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
|
||||
nudge.ScheduleStartResolver(self.site_config.site, current_time).send(-3)
|
||||
test_time = current_time + datetime.timedelta(days=-3)
|
||||
current_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
|
||||
nudge.ScheduleStartResolver(self.site_config.site, current_day).send(-3)
|
||||
test_day = current_day + datetime.timedelta(days=-3)
|
||||
self.assertFalse(mock_schedule_bin.called)
|
||||
mock_schedule_bin.apply_async.assert_any_call(
|
||||
(self.site_config.site.id, serialize(test_time), -3, 0, [], True, None),
|
||||
(self.site_config.site.id, serialize(test_day), -3, 0, [], True, None),
|
||||
retry=False,
|
||||
)
|
||||
mock_schedule_bin.apply_async.assert_any_call(
|
||||
(self.site_config.site.id, serialize(test_time), -3, tasks.RECURRING_NUDGE_NUM_BINS - 1, [], True, None),
|
||||
(self.site_config.site.id, serialize(test_day), -3, tasks.RECURRING_NUDGE_NUM_BINS - 1, [], True, None),
|
||||
retry=False,
|
||||
)
|
||||
self.assertFalse(mock_ace.send.called)
|
||||
@@ -98,8 +99,8 @@ class TestSendRecurringNudge(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
|
||||
bins_in_use = frozenset((s.enrollment.user.id % tasks.RECURRING_NUDGE_NUM_BINS) for s in schedules)
|
||||
|
||||
test_time = datetime.datetime(2017, 8, 3, 18, tzinfo=pytz.UTC)
|
||||
test_time_str = serialize(test_time)
|
||||
test_datetime = datetime.datetime(2017, 8, 3, 18, tzinfo=pytz.UTC)
|
||||
test_datetime_str = serialize(test_datetime)
|
||||
for b in range(tasks.RECURRING_NUDGE_NUM_BINS):
|
||||
expected_queries = NUM_QUERIES_NO_MATCHING_SCHEDULES
|
||||
if b in bins_in_use:
|
||||
@@ -108,7 +109,7 @@ class TestSendRecurringNudge(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
|
||||
with self.assertNumQueries(expected_queries, table_blacklist=WAFFLE_TABLES):
|
||||
tasks.recurring_nudge_schedule_bin(
|
||||
self.site_config.site.id, target_day_str=test_time_str, day_offset=-3, bin_num=b,
|
||||
self.site_config.site.id, target_day_str=test_datetime_str, day_offset=-3, bin_num=b,
|
||||
org_list=[schedules[0].enrollment.course.org],
|
||||
)
|
||||
self.assertEqual(mock_schedule_send.apply_async.call_count, schedule_count)
|
||||
@@ -123,12 +124,12 @@ class TestSendRecurringNudge(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
schedule.enrollment.course_id = CourseKey.from_string('edX/toy/Not_2012_Fall')
|
||||
schedule.enrollment.save()
|
||||
|
||||
test_time = datetime.datetime(2017, 8, 3, 20, tzinfo=pytz.UTC)
|
||||
test_time_str = serialize(test_time)
|
||||
test_datetime = datetime.datetime(2017, 8, 3, 20, tzinfo=pytz.UTC)
|
||||
test_datetime_str = serialize(test_datetime)
|
||||
for b in range(tasks.RECURRING_NUDGE_NUM_BINS):
|
||||
with self.assertNumQueries(NUM_QUERIES_NO_MATCHING_SCHEDULES, table_blacklist=WAFFLE_TABLES):
|
||||
tasks.recurring_nudge_schedule_bin(
|
||||
self.site_config.site.id, target_day_str=test_time_str, day_offset=-3, bin_num=b,
|
||||
self.site_config.site.id, target_day_str=test_datetime_str, day_offset=-3, bin_num=b,
|
||||
org_list=[schedule.enrollment.course.org],
|
||||
)
|
||||
|
||||
@@ -140,6 +141,27 @@ class TestSendRecurringNudge(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
# is null.
|
||||
self.assertEqual(mock_schedule_send.apply_async.call_count, 0)
|
||||
|
||||
@patch.object(tasks, '_recurring_nudge_schedule_send')
|
||||
def test_send_after_course_end(self, mock_schedule_send):
|
||||
user1 = UserFactory.create(id=tasks.RECURRING_NUDGE_NUM_BINS)
|
||||
|
||||
schedule = ScheduleFactory.create(
|
||||
start=datetime.datetime(2017, 8, 3, 20, 34, 30, tzinfo=pytz.UTC),
|
||||
enrollment__user=user1,
|
||||
)
|
||||
schedule.enrollment.course = CourseOverviewFactory()
|
||||
schedule.enrollment.course.end = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=1)
|
||||
|
||||
test_datetime = datetime.datetime(2017, 8, 3, 20, tzinfo=pytz.UTC)
|
||||
test_datetime_str = serialize(test_datetime)
|
||||
|
||||
tasks.recurring_nudge_schedule_bin.apply_async(
|
||||
self.site_config.site.id, target_day_str=test_datetime_str, day_offset=-3, bin_num=0,
|
||||
org_list=[schedule.enrollment.course.org],
|
||||
)
|
||||
|
||||
self.assertFalse(mock_schedule_send.apply_async.called)
|
||||
|
||||
@patch.object(tasks, 'ace')
|
||||
def test_delivery_disabled(self, mock_ace):
|
||||
ScheduleConfigFactory.create(site=self.site_config.site, deliver_recurring_nudge=False)
|
||||
@@ -153,8 +175,8 @@ class TestSendRecurringNudge(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
def test_enqueue_disabled(self, mock_schedule_bin, mock_ace):
|
||||
ScheduleConfigFactory.create(site=self.site_config.site, enqueue_recurring_nudge=False)
|
||||
|
||||
current_time = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
|
||||
nudge.ScheduleStartResolver(self.site_config.site, current_time).send(3)
|
||||
current_datetime = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
|
||||
nudge.ScheduleStartResolver(self.site_config.site, current_datetime).send(3)
|
||||
self.assertFalse(mock_schedule_bin.called)
|
||||
self.assertFalse(mock_schedule_bin.apply_async.called)
|
||||
self.assertFalse(mock_ace.send.called)
|
||||
@@ -196,11 +218,11 @@ class TestSendRecurringNudge(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
enrollment__user=user2,
|
||||
)
|
||||
|
||||
test_time = datetime.datetime(2017, 8, 3, 17, tzinfo=pytz.UTC)
|
||||
test_time_str = serialize(test_time)
|
||||
test_datetime = datetime.datetime(2017, 8, 3, 17, tzinfo=pytz.UTC)
|
||||
test_datetime_str = serialize(test_datetime)
|
||||
with self.assertNumQueries(NUM_QUERIES_WITH_MATCHES, table_blacklist=WAFFLE_TABLES):
|
||||
tasks.recurring_nudge_schedule_bin(
|
||||
limited_config.site.id, target_day_str=test_time_str, day_offset=-3, bin_num=0,
|
||||
limited_config.site.id, target_day_str=test_datetime_str, day_offset=-3, bin_num=0,
|
||||
org_list=org_list, exclude_orgs=exclude_orgs,
|
||||
)
|
||||
|
||||
@@ -220,11 +242,11 @@ class TestSendRecurringNudge(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
for course_num in (1, 2, 3)
|
||||
]
|
||||
|
||||
test_time = datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC)
|
||||
test_time_str = serialize(test_time)
|
||||
test_datetime = datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC)
|
||||
test_datetime_str = serialize(test_datetime)
|
||||
with self.assertNumQueries(NUM_QUERIES_WITH_MATCHES, table_blacklist=WAFFLE_TABLES):
|
||||
tasks.recurring_nudge_schedule_bin(
|
||||
self.site_config.site.id, target_day_str=test_time_str, day_offset=-3,
|
||||
self.site_config.site.id, target_day_str=test_datetime_str, day_offset=-3,
|
||||
bin_num=user.id % tasks.RECURRING_NUDGE_NUM_BINS,
|
||||
org_list=[schedules[0].enrollment.course.org],
|
||||
)
|
||||
@@ -245,8 +267,8 @@ class TestSendRecurringNudge(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
for course_num in range(message_count)
|
||||
]
|
||||
|
||||
test_time = datetime.datetime(2017, 8, 3, 19, tzinfo=pytz.UTC)
|
||||
test_time_str = serialize(test_time)
|
||||
test_datetime = datetime.datetime(2017, 8, 3, 19, tzinfo=pytz.UTC)
|
||||
test_datetime_str = serialize(test_datetime)
|
||||
|
||||
patch_policies(self, [StubPolicy([ChannelType.PUSH])])
|
||||
mock_channel = Mock(
|
||||
@@ -263,7 +285,7 @@ class TestSendRecurringNudge(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
|
||||
with self.assertNumQueries(NUM_QUERIES_WITH_MATCHES, table_blacklist=WAFFLE_TABLES):
|
||||
tasks.recurring_nudge_schedule_bin(
|
||||
self.site_config.site.id, target_day_str=test_time_str, day_offset=day,
|
||||
self.site_config.site.id, target_day_str=test_datetime_str, day_offset=day,
|
||||
bin_num=self._calculate_bin_for_user(user), org_list=[schedules[0].enrollment.course.org],
|
||||
)
|
||||
|
||||
|
||||
@@ -60,29 +60,31 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
self.site_config = SiteConfigurationFactory.create(site=site)
|
||||
ScheduleConfigFactory.create(site=self.site_config.site)
|
||||
|
||||
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
|
||||
|
||||
@patch.object(reminder.Command, 'resolver_class')
|
||||
def test_handle(self, mock_resolver):
|
||||
test_time = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
|
||||
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_time)
|
||||
mock_resolver.assert_called_with(self.site_config.site, test_day)
|
||||
|
||||
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):
|
||||
current_time = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
|
||||
test_time = current_time + datetime.timedelta(days=2)
|
||||
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))
|
||||
|
||||
reminder.UpgradeReminderResolver(self.site_config.site, current_time).send(2)
|
||||
reminder.UpgradeReminderResolver(self.site_config.site, current_day).send(2)
|
||||
self.assertFalse(mock_schedule_bin.called)
|
||||
mock_schedule_bin.apply_async.assert_any_call(
|
||||
(self.site_config.site.id, serialize(test_time), 2, 0, [], True, None),
|
||||
(self.site_config.site.id, serialize(test_day), 2, 0, [], True, None),
|
||||
retry=False,
|
||||
)
|
||||
mock_schedule_bin.apply_async.assert_any_call(
|
||||
(self.site_config.site.id, serialize(test_time), 2, tasks.UPGRADE_REMINDER_NUM_BINS - 1, [], True, None),
|
||||
(self.site_config.site.id, serialize(test_day), 2, tasks.UPGRADE_REMINDER_NUM_BINS - 1, [], True, None),
|
||||
retry=False,
|
||||
)
|
||||
self.assertFalse(mock_ace.send.called)
|
||||
@@ -100,8 +102,8 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
|
||||
bins_in_use = frozenset((s.enrollment.user.id % tasks.UPGRADE_REMINDER_NUM_BINS) for s in schedules)
|
||||
|
||||
test_time = datetime.datetime(2017, 8, 3, 18, tzinfo=pytz.UTC)
|
||||
test_time_str = serialize(test_time)
|
||||
test_datetime = datetime.datetime(2017, 8, 3, 18, tzinfo=pytz.UTC)
|
||||
test_datetime_str = serialize(test_datetime)
|
||||
for b in range(tasks.UPGRADE_REMINDER_NUM_BINS):
|
||||
expected_queries = NUM_QUERIES_NO_MATCHING_SCHEDULES
|
||||
if b in bins_in_use:
|
||||
@@ -110,7 +112,7 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
|
||||
with self.assertNumQueries(expected_queries, table_blacklist=WAFFLE_TABLES):
|
||||
tasks.upgrade_reminder_schedule_bin(
|
||||
self.site_config.site.id, target_day_str=test_time_str, day_offset=2, bin_num=b,
|
||||
self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2, bin_num=b,
|
||||
org_list=[schedules[0].enrollment.course.org],
|
||||
)
|
||||
|
||||
@@ -126,12 +128,12 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
schedule.enrollment.course_id = CourseKey.from_string('edX/toy/Not_2012_Fall')
|
||||
schedule.enrollment.save()
|
||||
|
||||
test_time = datetime.datetime(2017, 8, 3, 20, tzinfo=pytz.UTC)
|
||||
test_time_str = serialize(test_time)
|
||||
test_datetime = datetime.datetime(2017, 8, 3, 20, tzinfo=pytz.UTC)
|
||||
test_datetime_str = serialize(test_datetime)
|
||||
for b in range(tasks.UPGRADE_REMINDER_NUM_BINS):
|
||||
with self.assertNumQueries(NUM_QUERIES_NO_MATCHING_SCHEDULES, table_blacklist=WAFFLE_TABLES):
|
||||
tasks.upgrade_reminder_schedule_bin(
|
||||
self.site_config.site.id, target_day_str=test_time_str, day_offset=2, bin_num=b,
|
||||
self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2, bin_num=b,
|
||||
org_list=[schedule.enrollment.course.org],
|
||||
)
|
||||
|
||||
@@ -156,8 +158,8 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
def test_enqueue_disabled(self, mock_schedule_bin, mock_ace):
|
||||
ScheduleConfigFactory.create(site=self.site_config.site, enqueue_upgrade_reminder=False)
|
||||
|
||||
current_time = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
|
||||
reminder.UpgradeReminderResolver(self.site_config.site, current_time).send(3)
|
||||
current_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
|
||||
reminder.UpgradeReminderResolver(self.site_config.site, current_day).send(3)
|
||||
self.assertFalse(mock_schedule_bin.called)
|
||||
self.assertFalse(mock_schedule_bin.apply_async.called)
|
||||
self.assertFalse(mock_ace.send.called)
|
||||
@@ -199,11 +201,11 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
enrollment__user=user2,
|
||||
)
|
||||
|
||||
test_time = datetime.datetime(2017, 8, 3, 17, tzinfo=pytz.UTC)
|
||||
test_time_str = serialize(test_time)
|
||||
test_datetime = datetime.datetime(2017, 8, 3, 17, tzinfo=pytz.UTC)
|
||||
test_datetime_str = serialize(test_datetime)
|
||||
with self.assertNumQueries(NUM_QUERIES_WITH_MATCHES, table_blacklist=WAFFLE_TABLES):
|
||||
tasks.upgrade_reminder_schedule_bin(
|
||||
limited_config.site.id, target_day_str=test_time_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,
|
||||
)
|
||||
|
||||
@@ -223,11 +225,11 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
for course_num in (1, 2, 3)
|
||||
]
|
||||
|
||||
test_time = datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC)
|
||||
test_time_str = serialize(test_time)
|
||||
test_datetime = datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC)
|
||||
test_datetime_str = serialize(test_datetime)
|
||||
with self.assertNumQueries(NUM_QUERIES_WITH_MATCHES, table_blacklist=WAFFLE_TABLES):
|
||||
tasks.upgrade_reminder_schedule_bin(
|
||||
self.site_config.site.id, target_day_str=test_time_str, day_offset=2,
|
||||
self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2,
|
||||
bin_num=user.id % tasks.UPGRADE_REMINDER_NUM_BINS,
|
||||
org_list=[schedules[0].enrollment.course.org],
|
||||
)
|
||||
@@ -237,14 +239,13 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
@ddt.data(*itertools.product((1, 10, 100), (2, 10)))
|
||||
@ddt.unpack
|
||||
def test_templates(self, message_count, day):
|
||||
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
|
||||
now = datetime.datetime.now(pytz.UTC)
|
||||
future_date = now + datetime.timedelta(days=21)
|
||||
future_datetime = now + datetime.timedelta(days=21)
|
||||
|
||||
user = UserFactory.create()
|
||||
schedules = [
|
||||
ScheduleFactory.create(
|
||||
upgrade_deadline=future_date,
|
||||
upgrade_deadline=future_datetime,
|
||||
enrollment__user=user,
|
||||
enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(course_num))
|
||||
)
|
||||
@@ -253,16 +254,17 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
|
||||
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(
|
||||
course_id=schedule.enrollment.course.id,
|
||||
mode_slug=CourseMode.VERIFIED,
|
||||
expiration_datetime=future_date
|
||||
expiration_datetime=future_datetime
|
||||
)
|
||||
|
||||
test_time = future_date
|
||||
test_time_str = serialize(test_time)
|
||||
test_datetime = future_datetime
|
||||
test_datetime_str = serialize(test_datetime)
|
||||
|
||||
patch_policies(self, [StubPolicy([ChannelType.PUSH])])
|
||||
mock_channel = Mock(
|
||||
@@ -273,9 +275,7 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
|
||||
sent_messages = []
|
||||
|
||||
templates_override = deepcopy(settings.TEMPLATES)
|
||||
templates_override[0]['OPTIONS']['string_if_invalid'] = "TEMPLATE WARNING - MISSING VARIABLE [%s]"
|
||||
with self.settings(TEMPLATES=templates_override):
|
||||
with self.settings(TEMPLATES=self._get_template_overrides()):
|
||||
with patch.object(tasks, '_upgrade_reminder_schedule_send') as mock_schedule_send:
|
||||
mock_schedule_send.apply_async = lambda args, *_a, **_kw: sent_messages.append(args)
|
||||
|
||||
@@ -284,17 +284,28 @@ class TestUpgradeReminder(FilteredQueryCountMixin, CacheIsolationTestCase):
|
||||
num_expected_queries = NUM_QUERIES_WITH_MATCHES + NUM_QUERIES_WITH_DEADLINE + message_count
|
||||
with self.assertNumQueries(num_expected_queries, table_blacklist=WAFFLE_TABLES):
|
||||
tasks.upgrade_reminder_schedule_bin(
|
||||
self.site_config.site.id, target_day_str=test_time_str, day_offset=day,
|
||||
bin_num=user.id % tasks.UPGRADE_REMINDER_NUM_BINS,
|
||||
self.site_config.site.id, target_day_str=test_datetime_str, day_offset=day,
|
||||
bin_num=self._calculate_bin_for_user(user),
|
||||
org_list=[schedules[0].enrollment.course.org],
|
||||
)
|
||||
|
||||
self.assertEqual(len(sent_messages), message_count)
|
||||
|
||||
for args in sent_messages:
|
||||
tasks._upgrade_reminder_schedule_send(*args)
|
||||
# Load the site (which we query per message sent)
|
||||
# Check the schedule config
|
||||
with self.assertNumQueries(1 + message_count):
|
||||
for args in sent_messages:
|
||||
tasks._upgrade_reminder_schedule_send(*args)
|
||||
|
||||
self.assertEqual(mock_channel.deliver.call_count, message_count)
|
||||
for (_name, (_msg, email), _kwargs) in mock_channel.deliver.mock_calls:
|
||||
for template in attr.astuple(email):
|
||||
self.assertNotIn("TEMPLATE WARNING", template)
|
||||
|
||||
def _get_template_overrides(self):
|
||||
templates_override = deepcopy(settings.TEMPLATES)
|
||||
templates_override[0]['OPTIONS']['string_if_invalid'] = "TEMPLATE WARNING - MISSING VARIABLE [%s]"
|
||||
return templates_override
|
||||
|
||||
def _calculate_bin_for_user(self, user):
|
||||
return user.id % tasks.RECURRING_NUDGE_NUM_BINS
|
||||
|
||||
@@ -9,7 +9,7 @@ from django.contrib.sites.models import Site
|
||||
from django.contrib.staticfiles.templatetags.staticfiles import static
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db.models import F, Min
|
||||
from django.db.models import F, Min, Q
|
||||
from django.db.utils import DatabaseError
|
||||
from django.utils.formats import dateformat, get_format
|
||||
import pytz
|
||||
@@ -90,12 +90,15 @@ def _recurring_nudge_schedule_send(site_id, msg_str):
|
||||
def recurring_nudge_schedule_bin(
|
||||
site_id, target_day_str, day_offset, bin_num, org_list, exclude_orgs=False, override_recipient_email=None,
|
||||
):
|
||||
target_day = deserialize(target_day_str)
|
||||
target_datetime = deserialize(target_day_str)
|
||||
# TODO: in the next refactor of this task, pass in current_datetime instead of reproducing it here
|
||||
current_datetime = target_datetime - datetime.timedelta(days=day_offset)
|
||||
msg_type = RecurringNudge(abs(day_offset))
|
||||
|
||||
for (user, language, context) in _recurring_nudge_schedules_for_bin(
|
||||
Site.objects.get(id=site_id),
|
||||
target_day,
|
||||
current_datetime,
|
||||
target_datetime,
|
||||
bin_num,
|
||||
org_list,
|
||||
exclude_orgs
|
||||
@@ -111,11 +114,11 @@ def recurring_nudge_schedule_bin(
|
||||
_recurring_nudge_schedule_send.apply_async((site_id, str(msg)), retry=False)
|
||||
|
||||
|
||||
def _recurring_nudge_schedules_for_bin(site, target_day, bin_num, org_list, exclude_orgs=False):
|
||||
beginning_of_day = target_day.replace(hour=0, minute=0, second=0)
|
||||
def _recurring_nudge_schedules_for_bin(site, current_datetime, target_datetime, bin_num, org_list, exclude_orgs=False):
|
||||
schedules = get_schedules_with_target_date_by_bin_and_orgs(
|
||||
schedule_date_field='start',
|
||||
target_date=beginning_of_day,
|
||||
current_datetime=current_datetime,
|
||||
target_datetime=target_datetime,
|
||||
bin_num=bin_num,
|
||||
num_bins=RECURRING_NUDGE_NUM_BINS,
|
||||
org_list=org_list,
|
||||
@@ -154,12 +157,15 @@ class UpgradeReminder(ScheduleMessageType):
|
||||
def upgrade_reminder_schedule_bin(
|
||||
site_id, target_day_str, day_offset, bin_num, org_list, exclude_orgs=False, override_recipient_email=None,
|
||||
):
|
||||
target_day = deserialize(target_day_str)
|
||||
target_datetime = deserialize(target_day_str)
|
||||
# TODO: in the next refactor of this task, pass in current_datetime instead of reproducing it here
|
||||
current_datetime = target_datetime - datetime.timedelta(days=day_offset)
|
||||
msg_type = UpgradeReminder()
|
||||
|
||||
for (user, language, context) in _upgrade_reminder_schedules_for_bin(
|
||||
Site.objects.get(id=site_id),
|
||||
target_day,
|
||||
current_datetime,
|
||||
target_datetime,
|
||||
bin_num,
|
||||
org_list,
|
||||
exclude_orgs
|
||||
@@ -185,12 +191,11 @@ def _upgrade_reminder_schedule_send(site_id, msg_str):
|
||||
ace.send(msg)
|
||||
|
||||
|
||||
def _upgrade_reminder_schedules_for_bin(site, target_day, bin_num, org_list, exclude_orgs=False):
|
||||
beginning_of_day = target_day.replace(hour=0, minute=0, second=0)
|
||||
|
||||
def _upgrade_reminder_schedules_for_bin(site, current_datetime, target_datetime, bin_num, org_list, exclude_orgs=False):
|
||||
schedules = get_schedules_with_target_date_by_bin_and_orgs(
|
||||
schedule_date_field='upgrade_deadline',
|
||||
target_date=beginning_of_day,
|
||||
current_datetime=current_datetime,
|
||||
target_datetime=target_datetime,
|
||||
bin_num=bin_num,
|
||||
num_bins=RECURRING_NUDGE_NUM_BINS,
|
||||
org_list=org_list,
|
||||
@@ -235,12 +240,15 @@ class CourseUpdate(ScheduleMessageType):
|
||||
def course_update_schedule_bin(
|
||||
site_id, target_day_str, day_offset, bin_num, org_list, exclude_orgs=False, override_recipient_email=None,
|
||||
):
|
||||
target_day = deserialize(target_day_str)
|
||||
target_datetime = deserialize(target_day_str)
|
||||
# TODO: in the next refactor of this task, pass in current_datetime instead of reproducing it here
|
||||
current_datetime = target_datetime - datetime.timedelta(days=day_offset)
|
||||
msg_type = CourseUpdate()
|
||||
|
||||
for (user, language, context) in _course_update_schedules_for_bin(
|
||||
Site.objects.get(id=site_id),
|
||||
target_day,
|
||||
current_datetime,
|
||||
target_datetime,
|
||||
day_offset,
|
||||
bin_num,
|
||||
org_list,
|
||||
@@ -267,12 +275,13 @@ def _course_update_schedule_send(site_id, msg_str):
|
||||
ace.send(msg)
|
||||
|
||||
|
||||
def _course_update_schedules_for_bin(site, target_day, day_offset, bin_num, org_list, exclude_orgs=False):
|
||||
def _course_update_schedules_for_bin(site, current_datetime, target_datetime, day_offset, bin_num, org_list,
|
||||
exclude_orgs=False):
|
||||
week_num = abs(day_offset) / 7
|
||||
beginning_of_day = target_day.replace(hour=0, minute=0, second=0)
|
||||
schedules = get_schedules_with_target_date_by_bin_and_orgs(
|
||||
schedule_date_field='start',
|
||||
target_date=beginning_of_day,
|
||||
current_datetime=current_datetime,
|
||||
target_datetime=target_datetime,
|
||||
bin_num=bin_num,
|
||||
num_bins=COURSE_UPDATE_NUM_BINS,
|
||||
org_list=org_list,
|
||||
@@ -317,37 +326,40 @@ def get_course_week_summary(course_id, week_num):
|
||||
raise CourseUpdateDoesNotExist()
|
||||
|
||||
|
||||
def get_schedules_with_target_date_by_bin_and_orgs(schedule_date_field, target_date, bin_num, num_bins=DEFAULT_NUM_BINS,
|
||||
org_list=None, exclude_orgs=False, order_by='enrollment__user__id'):
|
||||
def get_schedules_with_target_date_by_bin_and_orgs(schedule_date_field, current_datetime, target_datetime, bin_num,
|
||||
num_bins=DEFAULT_NUM_BINS, org_list=None, exclude_orgs=False,
|
||||
order_by='enrollment__user__id'):
|
||||
"""
|
||||
Returns Schedules with the target_date, related to Users whose id matches the bin_num, and filtered by org_list.
|
||||
|
||||
Arguments:
|
||||
schedule_date_field -- string field name to query on the User's Schedule model
|
||||
target_date -- datetime day (with zeroed-out time) that the User's Schedule's schedule_date_field value should fall
|
||||
under
|
||||
current_datetime -- datetime that will be used as "right now" in the query
|
||||
target_datetime -- datetime that the User's Schedule's schedule_date_field value should fall under
|
||||
bin_num -- int for selecting the bin of Users whose id % num_bins == bin_num
|
||||
num_bin -- int specifying the number of bins to separate the Users into (default: DEFAULT_NUM_BINS)
|
||||
org_list -- list of course_org names (strings) that the returned Schedules must or must not be in (default: None)
|
||||
exclude_orgs -- boolean indicating whether the returned Schedules should exclude (True) the course_orgs in org_list
|
||||
or strictly include (False) them (default: False)
|
||||
order_by -- string for field to sort the resulting Schedules by
|
||||
"""
|
||||
schedule_date_equals_target_date_filter = {
|
||||
'courseenrollment__schedule__{}__gte'.format(schedule_date_field): target_date,
|
||||
'courseenrollment__schedule__{}__lt'.format(schedule_date_field): target_date + datetime.timedelta(days=1),
|
||||
target_day = _get_datetime_beginning_of_day(target_datetime)
|
||||
schedule_day_equals_target_day_filter = {
|
||||
'courseenrollment__schedule__{}__gte'.format(schedule_date_field): target_day,
|
||||
'courseenrollment__schedule__{}__lt'.format(schedule_date_field): target_day + datetime.timedelta(days=1),
|
||||
}
|
||||
users = User.objects.filter(
|
||||
courseenrollment__is_active=True,
|
||||
**schedule_date_equals_target_date_filter
|
||||
**schedule_day_equals_target_day_filter
|
||||
).annotate(
|
||||
id_mod=F('id') % num_bins
|
||||
).filter(
|
||||
id_mod=bin_num
|
||||
)
|
||||
|
||||
schedule_date_equals_target_date_filter = {
|
||||
'{}__gte'.format(schedule_date_field): target_date,
|
||||
'{}__lt'.format(schedule_date_field): target_date + datetime.timedelta(days=1),
|
||||
schedule_day_equals_target_day_filter = {
|
||||
'{}__gte'.format(schedule_date_field): target_day,
|
||||
'{}__lt'.format(schedule_date_field): target_day + datetime.timedelta(days=1),
|
||||
}
|
||||
schedules = Schedule.objects.select_related(
|
||||
'enrollment__user__profile',
|
||||
@@ -355,9 +367,10 @@ def get_schedules_with_target_date_by_bin_and_orgs(schedule_date_field, target_d
|
||||
).prefetch_related(
|
||||
'enrollment__course__modes'
|
||||
).filter(
|
||||
Q(enrollment__course__end__isnull=True) | Q(enrollment__course__end__gte=current_datetime),
|
||||
enrollment__user__in=users,
|
||||
enrollment__is_active=True,
|
||||
**schedule_date_equals_target_date_filter
|
||||
**schedule_day_equals_target_day_filter
|
||||
).order_by(order_by)
|
||||
|
||||
if org_list is not None:
|
||||
@@ -399,3 +412,10 @@ def _get_link_to_purchase_verified_certificate(a_user, a_schedule):
|
||||
return None
|
||||
|
||||
return verified_upgrade_deadline_link(a_user, enrollment.course)
|
||||
|
||||
|
||||
def _get_datetime_beginning_of_day(dt):
|
||||
"""
|
||||
Truncates hours, minutes, seconds, and microseconds to zero on given datetime.
|
||||
"""
|
||||
return dt.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
Reference in New Issue
Block a user