Add tests for experience types, ensure courses have a verified mode

This commit is contained in:
Gabe Mulley
2017-10-30 16:16:46 -04:00
parent 57bf89c8e2
commit 7fd643faa4
19 changed files with 246 additions and 181 deletions

View File

@@ -1,3 +1,4 @@
from collections import namedtuple, defaultdict
from copy import deepcopy
import datetime
import ddt
@@ -9,6 +10,9 @@ from freezegun import freeze_time
from mock import Mock, patch
import pytz
from commerce.models import CommerceConfiguration
from course_modes.models import CourseMode
from course_modes.tests.factories import CourseModeFactory
from courseware.models import DynamicUpgradeDeadlineConfiguration
from edx_ace.channel import ChannelType
from edx_ace.utils.date import serialize
@@ -20,10 +24,12 @@ from openedx.core.djangoapps.schedules.resolvers import _get_datetime_beginning_
from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
from openedx.core.djangolib.testing.utils import FilteredQueryCountMixin, CacheIsolationTestCase
from student.models import CourseEnrollment
from student.tests.factories import UserFactory
SITE_QUERY = 2 # django_site, site_configuration_siteconfiguration
SITE_QUERY = 1 # django_site
SITE_CONFIG_QUERY = 1 # site_configuration_siteconfiguration
SCHEDULES_QUERY = 1 # schedules_schedule
COURSE_MODES_QUERY = 1 # course_modes_coursemode
@@ -33,18 +39,14 @@ ORG_DEADLINE_QUERY = 1 # courseware_orgdynamicupgradedeadlineconfiguration
COURSE_DEADLINE_QUERY = 1 # courseware_coursedynamicupgradedeadlineconfiguration
COMMERCE_CONFIG_QUERY = 1 # commerce_commerceconfiguration
NUM_QUERIES_NO_MATCHING_SCHEDULES = (
NUM_QUERIES_SITE_SCHEDULES = (
SITE_QUERY +
SITE_CONFIG_QUERY +
SCHEDULES_QUERY
)
NUM_QUERIES_WITH_MATCHES = (
NUM_QUERIES_NO_MATCHING_SCHEDULES +
COURSE_MODES_QUERY
)
NUM_QUERIES_FIRST_MATCH = (
NUM_QUERIES_WITH_MATCHES
NUM_QUERIES_SITE_SCHEDULES
+ GLOBAL_DEADLINE_QUERY
+ ORG_DEADLINE_QUERY
+ COURSE_DEADLINE_QUERY
@@ -54,6 +56,9 @@ NUM_QUERIES_FIRST_MATCH = (
LOG = logging.getLogger(__name__)
ExperienceTest = namedtuple('ExperienceTest', 'experience offset email_sent')
@ddt.ddt
@freeze_time('2017-08-01 00:00:00', tz_offset=0, tick=True)
class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase):
@@ -73,6 +78,9 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase)
ScheduleConfigFactory.create(site=self.site_config.site)
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
CommerceConfiguration.objects.create(checkout_on_ecommerce_service=True)
self._courses_with_verified_modes = set()
def _calculate_bin_for_user(self, user):
return user.id % self.task.num_bins
@@ -92,6 +100,24 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase)
templates_override[0]['OPTIONS']['string_if_invalid'] = "TEMPLATE WARNING - MISSING VARIABLE [%s]"
return templates_override
def _schedule_factory(self, offset=None, **factory_kwargs):
_, _, target_day, upgrade_deadline = self._get_dates(offset=offset)
factory_kwargs.setdefault('start', target_day)
factory_kwargs.setdefault('upgrade_deadline', upgrade_deadline)
factory_kwargs.setdefault('enrollment__course__self_paced', True)
if hasattr(self, 'experience_type'):
factory_kwargs.setdefault('experience__experience_type', self.experience_type)
schedule = ScheduleFactory(**factory_kwargs)
course_id = schedule.enrollment.course_id
if course_id not in self._courses_with_verified_modes:
CourseModeFactory(
course_id=course_id,
mode_slug=CourseMode.VERIFIED,
expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30),
)
self._courses_with_verified_modes.add(course_id)
return schedule
def test_command_task_binding(self):
self.assertEqual(self.command.async_send_task, self.task)
@@ -130,11 +156,7 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase)
with patch.object(self.task, 'async_send_task') as mock_schedule_send:
current_day, offset, target_day, upgrade_deadline = self._get_dates()
schedules = [
ScheduleFactory.create(
start=target_day,
upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True,
) for _ in range(schedule_count)
self._schedule_factory() for _ in range(schedule_count)
]
bins_in_use = frozenset((self._calculate_bin_for_user(s.enrollment.user)) for s in schedules)
@@ -142,18 +164,17 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase)
target_day_str = serialize(target_day)
for b in range(self.task.num_bins):
LOG.debug('Running bin %d', b)
expected_queries = NUM_QUERIES_NO_MATCHING_SCHEDULES
LOG.debug('Checking bin %d', b)
expected_queries = NUM_QUERIES_SITE_SCHEDULES
if b in bins_in_use:
if is_first_match:
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_MODES_QUERY # to cache the course modes for this course
)
is_first_match = False
else:
expected_queries = NUM_QUERIES_WITH_MATCHES
with self.assertNumQueries(expected_queries, table_blacklist=WAFFLE_TABLES):
self.task.apply(kwargs=dict(
@@ -171,13 +192,12 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase)
def test_no_course_overview(self):
current_day, offset, target_day, upgrade_deadline = self._get_dates()
schedule = ScheduleFactory.create(
start=target_day,
upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True,
# Don't use CourseEnrollmentFactory since it creates a course overview
enrollment = CourseEnrollment.objects.create(
course_id=CourseKey.from_string('edX/toy/Not_2012_Fall'),
user=UserFactory.create(),
)
schedule.enrollment.course_id = CourseKey.from_string('edX/toy/Not_2012_Fall')
schedule.enrollment.save()
schedule = self._schedule_factory(enrollment=enrollment)
with patch.object(self.task, 'async_send_task') as mock_schedule_send:
for b in range(self.task.num_bins):
@@ -249,25 +269,16 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase)
user2 = UserFactory.create(id=self.task.num_bins * 2)
current_day, offset, target_day, upgrade_deadline = self._get_dates()
ScheduleFactory.create(
upgrade_deadline=upgrade_deadline,
start=target_day,
self._schedule_factory(
enrollment__course__org=filtered_org,
enrollment__course__self_paced=True,
enrollment__user=user1,
)
ScheduleFactory.create(
upgrade_deadline=upgrade_deadline,
start=target_day,
self._schedule_factory(
enrollment__course__org=unfiltered_org,
enrollment__course__self_paced=True,
enrollment__user=user1,
)
ScheduleFactory.create(
upgrade_deadline=upgrade_deadline,
start=target_day,
self._schedule_factory(
enrollment__course__org=unfiltered_org,
enrollment__course__self_paced=True,
enrollment__user=user2,
)
@@ -284,17 +295,12 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase)
user1 = UserFactory.create(id=self.task.num_bins)
current_day, offset, target_day, upgrade_deadline = self._get_dates()
schedule = ScheduleFactory.create(
start=target_day,
upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True,
enrollment__user=user1,
)
schedule.enrollment.course.start = current_day - datetime.timedelta(days=30)
end_date_offset = -2 if has_course_ended else 2
schedule.enrollment.course.end = current_day + datetime.timedelta(days=end_date_offset)
schedule.enrollment.course.save()
self._schedule_factory(
enrollment__user=user1,
enrollment__course__start=current_day - datetime.timedelta(days=30),
enrollment__course__end=current_day + datetime.timedelta(days=end_date_offset)
)
with patch.object(self.task, 'async_send_task') as mock_schedule_send:
self.task.apply(kwargs=dict(
@@ -312,15 +318,14 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase)
current_day, offset, target_day, upgrade_deadline = self._get_dates()
num_courses = 3
for course_index in range(num_courses):
ScheduleFactory.create(
start=target_day,
upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True,
self._schedule_factory(
enrollment__user=user,
enrollment__course__id=CourseKey.from_string('edX/toy/course{}'.format(course_index))
)
additional_course_queries = num_courses - 1 if self.queries_deadline_for_each_course else 0
# 2 queries per course, one for the course opt out and one for the course modes
# one query for course modes for the first schedule if we aren't checking the deadline for each course
additional_course_queries = (num_courses * 2) - 1 if self.queries_deadline_for_each_course else 1
expected_query_count = NUM_QUERIES_FIRST_MATCH + additional_course_queries
with self.assertNumQueries(expected_query_count, table_blacklist=WAFFLE_TABLES):
with patch.object(self.task, 'async_send_task') as mock_schedule_send:
@@ -333,7 +338,9 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase)
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
)
def test_templates(self, message_count):
for offset in self.expected_offsets:
self._assert_template_for_offset(offset, message_count)
@@ -344,10 +351,8 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase)
user = UserFactory.create()
for course_index in range(message_count):
ScheduleFactory.create(
start=target_day,
upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True,
self._schedule_factory(
offset=offset,
enrollment__user=user,
enrollment__course__id=CourseKey.from_string('edX/toy/course{}'.format(course_index))
)
@@ -366,7 +371,10 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase)
num_expected_queries = NUM_QUERIES_FIRST_MATCH
if self.queries_deadline_for_each_course:
num_expected_queries += (message_count - 1)
# one query per course for opt-out and one for course modes
num_expected_queries += (message_count * 2) - 1
else:
num_expected_queries += 1
with self.assertNumQueries(num_expected_queries, table_blacklist=WAFFLE_TABLES):
self.task.apply(kwargs=dict(
@@ -385,3 +393,23 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase)
self.assertNotIn("TEMPLATE WARNING", template)
self.assertNotIn("{{", template)
self.assertNotIn("}}", template)
def _check_if_email_sent_for_experience(self, test_config):
current_day, offset, target_day, _ = self._get_dates(offset=test_config.offset)
kwargs = {
'offset': offset
}
if test_config.experience is None:
kwargs['experience'] = None
else:
kwargs['experience__experience_type'] = test_config.experience
schedule = self._schedule_factory(**kwargs)
with patch.object(tasks, 'ace') as mock_ace:
self.task.apply(kwargs=dict(
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),
))
self.assertEqual(mock_ace.send.called, test_config.email_sent)

View File

@@ -1,59 +0,0 @@
from collections import namedtuple
import datetime
import ddt
import pytz
from edx_ace.utils.date import serialize
from freezegun import freeze_time
from mock import patch
from courseware.models import DynamicUpgradeDeadlineConfiguration
from openedx.core.djangoapps.schedules import tasks
from openedx.core.djangoapps.schedules.models import ScheduleExperience
from openedx.core.djangoapps.schedules.resolvers import _get_datetime_beginning_of_day
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory, ScheduleConfigFactory
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory, SiteConfigurationFactory
from openedx.core.djangolib.testing.utils import skip_unless_lms, FilteredQueryCountMixin, CacheIsolationTestCase
@ddt.ddt
@skip_unless_lms
@freeze_time('2017-08-01 00:00:00', tz_offset=0, tick=True)
class TestExperiences(FilteredQueryCountMixin, CacheIsolationTestCase):
ENABLED_CACHES = ['default']
ExperienceTest = namedtuple('ExperienceTest', 'experience offset email_sent')
def setUp(self):
super(TestExperiences, self).setUp()
site = SiteFactory.create()
self.site_config = SiteConfigurationFactory.create(site=site)
ScheduleConfigFactory.create(site=self.site_config.site)
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
@ddt.data(
ExperienceTest(experience=ScheduleExperience.DEFAULT, offset=-3, email_sent=True),
ExperienceTest(experience=ScheduleExperience.DEFAULT, offset=-10, email_sent=True),
ExperienceTest(experience=ScheduleExperience.COURSE_UPDATES, offset=-3, email_sent=True),
ExperienceTest(experience=ScheduleExperience.COURSE_UPDATES, offset=-10, email_sent=False),
)
@patch.object(tasks, 'ace')
def test_experience_type_exclusion(self, test_config, mock_ace):
current_day = _get_datetime_beginning_of_day(datetime.datetime.now(pytz.UTC))
target_day = current_day + datetime.timedelta(days=test_config.offset)
schedule = ScheduleFactory.create(
start=target_day,
enrollment__course__self_paced=True,
experience__experience_type=test_config.experience,
)
tasks.ScheduleRecurringNudge.apply(kwargs=dict(
site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=test_config.offset,
bin_num=(schedule.enrollment.user.id % tasks.ScheduleRecurringNudge.num_bins),
))
self.assertEqual(mock_ace.send.called, test_config.email_sent)

View File

@@ -1,3 +1,4 @@
import ddt
from mock import patch
from unittest import skipUnless
@@ -5,11 +6,16 @@ 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.send_email_base import (
ScheduleSendEmailTestBase,
ExperienceTest
)
from openedx.core.djangoapps.schedules.management.commands.tests.upsell_base import ScheduleUpsellTestMixin
from openedx.core.djangoapps.schedules.models import ScheduleExperience
from openedx.core.djangolib.testing.utils import skip_unless_lms
@ddt.ddt
@skip_unless_lms
@skipUnless(
'openedx.core.djangoapps.schedules.apps.SchedulesConfig' in settings.INSTALLED_APPS,
@@ -25,7 +31,8 @@ class TestSendCourseUpdate(ScheduleUpsellTestMixin, ScheduleSendEmailTestBase):
command = nudge.Command
deliver_config = 'deliver_course_update'
enqueue_config = 'enqueue_course_update'
expected_offsets = xrange(-7, -77, -7)
expected_offsets = range(-7, -77, -7)
experience_type = ScheduleExperience.EXPERIENCES.course_updates
queries_deadline_for_each_course = True
@@ -35,3 +42,11 @@ class TestSendCourseUpdate(ScheduleUpsellTestMixin, ScheduleSendEmailTestBase):
mock_highlights = patcher.start()
mock_highlights.return_value = ['Highlight {}'.format(num + 1) for num in range(3)]
self.addCleanup(patcher.stop)
@ddt.data(
ExperienceTest(experience=ScheduleExperience.EXPERIENCES.default, offset=expected_offsets[0], email_sent=False),
ExperienceTest(experience=ScheduleExperience.EXPERIENCES.course_updates, offset=expected_offsets[0], email_sent=True),
ExperienceTest(experience=None, offset=expected_offsets[0], email_sent=False),
)
def test_schedule_in_different_experience(self, test_config):
self._check_if_email_sent_for_experience(test_config)

View File

@@ -1,14 +1,18 @@
from unittest import skipUnless
import ddt
from django.conf import settings
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.tests.send_email_base import ScheduleSendEmailTestBase
from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ScheduleSendEmailTestBase, \
ExperienceTest
from openedx.core.djangoapps.schedules.management.commands.tests.upsell_base import ScheduleUpsellTestMixin
from openedx.core.djangoapps.schedules.models import ScheduleExperience
from openedx.core.djangolib.testing.utils import skip_unless_lms
@ddt.ddt
@skip_unless_lms
@skipUnless(
'openedx.core.djangoapps.schedules.apps.SchedulesConfig' in settings.INSTALLED_APPS,
@@ -27,3 +31,14 @@ class TestSendRecurringNudge(ScheduleUpsellTestMixin, ScheduleSendEmailTestBase)
expected_offsets = (-3, -10)
consolidates_emails_for_learner = True
@ddt.data(
ExperienceTest(experience=ScheduleExperience.EXPERIENCES.default, offset=-3, email_sent=True),
ExperienceTest(experience=ScheduleExperience.EXPERIENCES.default, offset=-10, email_sent=True),
ExperienceTest(experience=ScheduleExperience.EXPERIENCES.course_updates, offset=-3, email_sent=True),
ExperienceTest(experience=ScheduleExperience.EXPERIENCES.course_updates, offset=-10, email_sent=False),
ExperienceTest(experience=None, offset=-3, email_sent=True),
ExperienceTest(experience=None, offset=-10, email_sent=True),
)
def test_nudge_experience(self, test_config):
self._check_if_email_sent_for_experience(test_config)

View File

@@ -11,8 +11,9 @@ from opaque_keys.edx.locator import CourseLocator
from course_modes.models import CourseMode
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.tests.send_email_base import ScheduleSendEmailTestBase
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ScheduleSendEmailTestBase, \
ExperienceTest
from openedx.core.djangoapps.schedules.models import ScheduleExperience
from openedx.core.djangolib.testing.utils import skip_unless_lms
from student.tests.factories import UserFactory
@@ -41,18 +42,14 @@ class TestUpgradeReminder(ScheduleSendEmailTestBase):
@ddt.data(True, False)
@patch.object(tasks, 'ace')
def test_verified_learner(self, is_verified, mock_ace):
user = UserFactory.create(id=self.task.num_bins)
current_day, offset, target_day, upgrade_deadline = self._get_dates()
ScheduleFactory.create(
upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True,
enrollment__user=user,
schedule = self._schedule_factory(
enrollment__mode=CourseMode.VERIFIED if is_verified else CourseMode.AUDIT,
)
self.task.apply(kwargs=dict(
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(schedule.enrollment.user),
))
self.assertEqual(mock_ace.send.called, not is_verified)
@@ -62,10 +59,8 @@ class TestUpgradeReminder(ScheduleSendEmailTestBase):
user = UserFactory.create()
schedules = [
ScheduleFactory.create(
upgrade_deadline=upgrade_deadline,
self._schedule_factory(
enrollment__user=user,
enrollment__course__self_paced=True,
enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(i)),
enrollment__mode=CourseMode.VERIFIED if i in (0, 3) else CourseMode.AUDIT,
)
@@ -88,3 +83,23 @@ class TestUpgradeReminder(ScheduleSendEmailTestBase):
message.context['course_ids'],
[str(schedules[i].enrollment.course.id) for i in (1, 2, 4)]
)
@patch.object(tasks, 'ace')
def test_course_without_verified_mode(self, mock_ace):
current_day, offset, target_day, upgrade_deadline = self._get_dates()
schedule = self._schedule_factory()
schedule.enrollment.course.modes.filter(mode_slug=CourseMode.VERIFIED).delete()
self.task.apply(kwargs=dict(
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),
))
self.assertEqual(mock_ace.send.called, False)
@ddt.data(
ExperienceTest(experience=ScheduleExperience.EXPERIENCES.default, offset=expected_offsets[0], email_sent=True),
ExperienceTest(experience=ScheduleExperience.EXPERIENCES.course_updates, offset=expected_offsets[0], email_sent=False),
ExperienceTest(experience=None, offset=expected_offsets[0], email_sent=True),
)
def test_upgrade_reminder_experience(self, test_config):
self._check_if_email_sent_for_experience(test_config)

View File

@@ -8,7 +8,6 @@ from edx_ace.utils.date import serialize
from edx_ace.message import Message
from courseware.models import DynamicUpgradeDeadlineConfiguration
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
@ddt.ddt
@@ -34,10 +33,8 @@ class ScheduleUpsellTestMixin(object):
if testcase.set_deadline:
upgrade_deadline = current_day + datetime.timedelta(days=testcase.deadline_offset)
schedule = ScheduleFactory.create(
start=target_day,
upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True,
schedule = self._schedule_factory(
upgrade_deadline=upgrade_deadline
)
sent_messages = []

View File

@@ -1,6 +1,7 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.contrib.sites.models import Site
from model_utils import Choices
from model_utils.models import TimeStampedModel
from config_models.models import ConfigurationModel
@@ -27,7 +28,7 @@ class Schedule(TimeStampedModel):
try:
return self.experience.experience_type
except ScheduleExperience.DoesNotExist:
return ScheduleExperience.DEFAULT
return ScheduleExperience.EXPERIENCES.default
class Meta(object):
verbose_name = _('Schedule')
@@ -48,12 +49,10 @@ class ScheduleConfig(ConfigurationModel):
class ScheduleExperience(models.Model):
DEFAULT = 0
COURSE_UPDATES = 1
EXPERIENCES = (
(DEFAULT, 'Recurring Nudge and Upgrade Reminder'),
(COURSE_UPDATES, 'Course Updates')
EXPERIENCES = Choices(
(0, 'default', 'Recurring Nudge and Upgrade Reminder'),
(1, 'course_updates', 'Course Updates')
)
schedule = models.OneToOneField(Schedule, related_name='experience')
experience_type = models.PositiveSmallIntegerField(choices=EXPERIENCES, default=DEFAULT)
experience_type = models.PositiveSmallIntegerField(choices=EXPERIENCES, default=EXPERIENCES.default)

View File

@@ -77,7 +77,8 @@ class BinnedSchedulesBaseResolver(PrefixedDebugLoggerMixin, RecipientResolver):
schedule_date_field = None
num_bins = DEFAULT_NUM_BINS
experience_filter = Q(experience__experience_type=ScheduleExperience.DEFAULT) | Q(experience__isnull=True)
experience_filter = (Q(experience__experience_type=ScheduleExperience.EXPERIENCES.default)
| Q(experience__isnull=True))
def __attrs_post_init__(self):
# TODO: in the next refactor of this task, pass in current_datetime instead of reproducing it here
@@ -126,8 +127,6 @@ class BinnedSchedulesBaseResolver(PrefixedDebugLoggerMixin, RecipientResolver):
schedules = Schedule.objects.select_related(
'enrollment__user__profile',
'enrollment__course',
).prefetch_related(
'enrollment__course__modes',
).filter(
Q(enrollment__course__end__isnull=True) | Q(
enrollment__course__end__gte=self.current_datetime),
@@ -148,6 +147,8 @@ class BinnedSchedulesBaseResolver(PrefixedDebugLoggerMixin, RecipientResolver):
# This will run the query and cache all of the results in memory.
num_schedules = len(schedules)
LOG.debug('Number of schedules = %d', num_schedules)
# This should give us a sense of the volume of data being processed by each task.
set_custom_metric('num_schedules', num_schedules)
@@ -240,10 +241,10 @@ class RecurringNudgeResolver(BinnedSchedulesBaseResolver):
@property
def experience_filter(self):
if self.day_offset == -3:
experiences = [ScheduleExperience.DEFAULT, ScheduleExperience.COURSE_UPDATES]
experiences = [ScheduleExperience.EXPERIENCES.default, ScheduleExperience.EXPERIENCES.course_updates]
return Q(experience__experience_type__in=experiences) | Q(experience__isnull=True)
else:
return Q(experience__experience_type=ScheduleExperience.DEFAULT) | Q(experience__isnull=True)
return Q(experience__experience_type=ScheduleExperience.EXPERIENCES.default) | Q(experience__isnull=True)
def get_template_context(self, user, user_schedules):
first_schedule = user_schedules[0]
@@ -346,7 +347,7 @@ class CourseUpdateResolver(BinnedSchedulesBaseResolver):
log_prefix = 'Course Update'
schedule_date_field = 'start'
num_bins = COURSE_UPDATE_NUM_BINS
experience_filter = Q(experience__experience_type=ScheduleExperience.COURSE_UPDATES)
experience_filter = Q(experience__experience_type=ScheduleExperience.EXPERIENCES.course_updates)
def schedules_for_bin(self):
week_num = abs(self.day_offset) / 7

View File

@@ -64,14 +64,14 @@ def create_schedule(sender, **kwargs):
try:
get_week_highlights(enrollment.course_id, 1)
experience_type = ScheduleExperience.COURSE_UPDATES
experience_type = ScheduleExperience.EXPERIENCES.course_updates
except CourseUpdateDoesNotExist:
experience_type = ScheduleExperience.DEFAULT
experience_type = ScheduleExperience.EXPERIENCES.default
ScheduleExperience(schedule=schedule, experience_type=experience_type).save()
log.debug('Schedules: created a new schedule starting at %s with an upgrade deadline of %s and experience type: %s',
content_availability_date, upgrade_deadline, ScheduleExperience.EXPERIENCES[experience_type][1])
content_availability_date, upgrade_deadline, ScheduleExperience.EXPERIENCES[experience_type])
@receiver(COURSE_START_DATE_CHANGED, dispatch_uid="update_schedules_on_course_start_changed")

View File

@@ -10,7 +10,7 @@ class ScheduleExperienceFactory(factory.DjangoModelFactory):
class Meta(object):
model = models.ScheduleExperience
experience_type = models.ScheduleExperience.DEFAULT
experience_type = models.ScheduleExperience.EXPERIENCES.default
class ScheduleFactory(factory.DjangoModelFactory):

View File

@@ -6,6 +6,7 @@ from pytz import utc
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.models import CourseOverview
from openedx.core.djangoapps.schedules.models import ScheduleExperience
from openedx.core.djangoapps.schedules.signals import CREATE_SCHEDULE_WAFFLE_FLAG
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
@@ -24,16 +25,22 @@ from ..tests.factories import ScheduleConfigFactory
@skip_unless_lms
class CreateScheduleTests(SharedModuleStoreTestCase):
def assert_schedule_created(self, experience_type=ScheduleExperience.DEFAULT):
def assert_schedule_created(self, experience_type=ScheduleExperience.EXPERIENCES.default):
course = _create_course_run(self_paced=True)
enrollment = CourseEnrollmentFactory(course_id=course.id, mode=CourseMode.AUDIT)
enrollment = CourseEnrollmentFactory(
course_id=course.id,
mode=CourseMode.AUDIT,
)
self.assertIsNotNone(enrollment.schedule)
self.assertIsNone(enrollment.schedule.upgrade_deadline)
self.assertEquals(enrollment.schedule.experience.experience_type, experience_type)
def assert_schedule_not_created(self):
course = _create_course_run(self_paced=True)
enrollment = CourseEnrollmentFactory(course_id=course.id, mode=CourseMode.AUDIT)
enrollment = CourseEnrollmentFactory(
course_id=course.id,
mode=CourseMode.AUDIT,
)
with self.assertRaises(Schedule.DoesNotExist):
enrollment.schedule
@@ -86,7 +93,7 @@ class CreateScheduleTests(SharedModuleStoreTestCase):
site = SiteFactory.create()
mock_get_week_highlights.return_value = True
mock_get_current_site.return_value = site
self.assert_schedule_created(experience_type=ScheduleExperience.COURSE_UPDATES)
self.assert_schedule_created(experience_type=ScheduleExperience.EXPERIENCES.course_updates)
@ddt.ddt
@@ -114,7 +121,7 @@ class UpdateScheduleTests(SharedModuleStoreTestCase):
course = _create_course_run(self_paced=True, start_day_offset=5) # course starts in future
enrollment = CourseEnrollmentFactory(course_id=course.id, mode=CourseMode.AUDIT)
self.assert_schedule_dates(enrollment.schedule, enrollment.course_overview.start)
self.assert_schedule_dates(enrollment.schedule, enrollment.course.start)
course.start = course.start + datetime.timedelta(days=3) # new course start changes to another future date
self.store.update_item(course, ModuleStoreEnum.UserID.test)
@@ -138,7 +145,7 @@ class UpdateScheduleTests(SharedModuleStoreTestCase):
course = _create_course_run(self_paced=True, start_day_offset=5) # course starts in future
enrollment = CourseEnrollmentFactory(course_id=course.id, mode=CourseMode.AUDIT)
previous_start = enrollment.course_overview.start
previous_start = enrollment.course.start
self.assert_schedule_dates(enrollment.schedule, previous_start)
course.start = course.start + datetime.timedelta(days=-10) # new course start changes to a past date