-<% if (highlights_preview_only) { %>
-
- <%- gettext('This feature is currently in testing. Course teams can enter highlights, but learners will not receive email messages.') %>
-
-<% } %>
-
<%- gettext('Enter 3-5 highlights to include in the email message that learners receive for this section (250 character limit).') %>
diff --git a/common/djangoapps/entitlements/rest_api/v1/tests/test_views.py b/common/djangoapps/entitlements/rest_api/v1/tests/test_views.py
index fbb71df12c..c866d96d2d 100644
--- a/common/djangoapps/entitlements/rest_api/v1/tests/test_views.py
+++ b/common/djangoapps/entitlements/rest_api/v1/tests/test_views.py
@@ -20,7 +20,6 @@ from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from lms.djangoapps.courseware.models import DynamicUpgradeDeadlineConfiguration
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
-from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
from openedx.core.djangoapps.user_api.models import UserOrgTag
from common.djangoapps.student.models import CourseEnrollment
@@ -485,7 +484,7 @@ class EntitlementViewSetTest(ModuleStoreTestCase):
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
course = CourseFactory.create(self_paced=True)
course_uuid = uuid.uuid4()
- course_mode = CourseModeFactory(
+ CourseModeFactory(
course_id=course.id,
mode_slug=CourseMode.VERIFIED,
# This must be in the future to ensure it is returned by downstream code.
@@ -499,11 +498,9 @@ class EntitlementViewSetTest(ModuleStoreTestCase):
# Add an audit course enrollment for user.
enrollment = CourseEnrollment.enroll(self.user, course.id, mode=CourseMode.AUDIT)
- # Set an upgrade schedule so that dynamic upgrade deadlines are used
- ScheduleFactory.create(
- enrollment=enrollment,
- upgrade_deadline=course_mode.expiration_datetime + timedelta(days=-3)
- )
+ # Set an expired dynamic upgrade deadline
+ enrollment.schedule.upgrade_deadline = now() + timedelta(days=-2)
+ enrollment.schedule.save()
# The upgrade should complete and ignore the deadline
response = self.client.post(
diff --git a/common/djangoapps/student/tests/test_models.py b/common/djangoapps/student/tests/test_models.py
index 4b7afc7c27..fcc93e412d 100644
--- a/common/djangoapps/student/tests/test_models.py
+++ b/common/djangoapps/student/tests/test_models.py
@@ -27,7 +27,6 @@ from lms.djangoapps.courseware.toggles import (
)
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.schedules.models import Schedule
-from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
from openedx.core.djangolib.testing.utils import skip_unless_lms
from common.djangoapps.student.models import (
@@ -139,8 +138,6 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase): # lint-amnesty, pylint:
self.assertListEqual([self.user, self.user_2], all_enrolled_users)
@skip_unless_lms
- # NOTE: We mute the post_save signal to prevent Schedules from being created for new enrollments
- @factory.django.mute_signals(signals.post_save)
def test_upgrade_deadline(self):
""" The property should use either the CourseMode or related Schedule to determine the deadline. """
course = CourseFactory(self_paced=True)
@@ -151,12 +148,10 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase): # lint-amnesty, pylint:
expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=1)
)
enrollment = CourseEnrollmentFactory(course_id=course.id, mode=CourseMode.AUDIT)
- assert Schedule.objects.all().count() == 0
+ Schedule.objects.all().delete()
assert enrollment.upgrade_deadline == course_mode.expiration_datetime
@skip_unless_lms
- # NOTE: We mute the post_save signal to prevent Schedules from being created for new enrollments
- @factory.django.mute_signals(signals.post_save)
def test_upgrade_deadline_with_schedule(self):
""" The property should use either the CourseMode or related Schedule to determine the deadline. """
course = CourseFactory(self_paced=True)
@@ -167,16 +162,17 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase): # lint-amnesty, pylint:
expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30),
)
course_overview = CourseOverview.load_from_module_store(course.id)
- enrollment = CourseEnrollmentFactory(
+ CourseEnrollmentFactory(
course_id=course.id,
mode=CourseMode.AUDIT,
course=course_overview,
)
+ Schedule.objects.update(upgrade_deadline=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=5))
+ enrollment = CourseEnrollment.objects.first()
# The schedule's upgrade deadline should be used if a schedule exists
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
- schedule = ScheduleFactory(enrollment=enrollment)
- assert enrollment.upgrade_deadline == schedule.upgrade_deadline
+ assert enrollment.upgrade_deadline == enrollment.schedule.upgrade_deadline
@skip_unless_lms
@ddt.data(*(set(CourseMode.ALL_MODES) - set(CourseMode.AUDIT_MODES)))
@@ -197,7 +193,6 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase): # lint-amnesty, pylint:
)
enrollment = CourseEnrollmentFactory(course_id=course.id, mode=CourseMode.AUDIT)
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
- ScheduleFactory(enrollment=enrollment)
assert enrollment.schedule is not None
assert enrollment.upgrade_deadline == course_upgrade_deadline
@@ -215,13 +210,10 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase): # lint-amnesty, pylint:
)
enrollment = CourseEnrollmentFactory(course_id=course.id, mode=CourseMode.AUDIT)
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
- ScheduleFactory(enrollment=enrollment)
assert enrollment.schedule is not None
assert enrollment.upgrade_deadline is None
@skip_unless_lms
- # NOTE: We mute the post_save signal to prevent Schedules from being created for new enrollments
- @factory.django.mute_signals(signals.post_save)
def test_enrollments_not_deleted(self):
""" Recreating a CourseOverview with an outdated version should not delete the associated enrollment. """
course = CourseFactory(self_paced=True)
diff --git a/common/djangoapps/student/tests/test_views.py b/common/djangoapps/student/tests/test_views.py
index 7f26e201fc..937a25795f 100644
--- a/common/djangoapps/student/tests/test_views.py
+++ b/common/djangoapps/student/tests/test_views.py
@@ -17,7 +17,6 @@ from django.test import TestCase
from django.test.utils import override_settings
from django.urls import reverse
from django.utils.timezone import now
-from edx_toggles.toggles.testutils import override_waffle_flag
from milestones.tests.utils import MilestonesTestCaseMixin
from mock import patch
from opaque_keys import InvalidKeyError
@@ -31,8 +30,6 @@ from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFact
from openedx.core.djangoapps.catalog.tests.factories import ProgramFactory
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
-from openedx.core.djangoapps.schedules.config import COURSE_UPDATE_WAFFLE_FLAG
-from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
from openedx.core.djangoapps.site_configuration.tests.test_util import with_site_configuration_context
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
from openedx.features.course_experience.tests.views.helpers import add_course_mode
@@ -755,7 +752,6 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
assert resume_button_html in dashboard_html
assert view_button_html not in dashboard_html
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_content_gating_course_card_changes(self):
"""
When a course is expired, the links on the course card should be removed.
@@ -775,9 +771,6 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
enrollment.created = self.THREE_YEARS_AGO + timedelta(days=1)
enrollment.save()
- # pylint: disable=unused-variable
- schedule = ScheduleFactory(enrollment=enrollment)
-
response = self.client.get(reverse('dashboard'))
dashboard_html = self._remove_whitespace_from_response(response)
access_expired_substring = 'Accessexpired'
diff --git a/common/djangoapps/student/tests/tests.py b/common/djangoapps/student/tests/tests.py
index a71ff9946a..44848f460c 100644
--- a/common/djangoapps/student/tests/tests.py
+++ b/common/djangoapps/student/tests/tests.py
@@ -418,7 +418,7 @@ class DashboardTest(ModuleStoreTestCase, TestVerificationBase):
Note to future developers:
If you break this test so that the "check_mongo_calls(0)" fails,
- please do NOT change it to "check_mongo_calls(n>1)". Instead, change
+ please do NOT change it to "check_mongo_calls(n>=1)". Instead, change
your code to not load courses from the module store. This may
involve adding fields to CourseOverview so that loading a full
CourseDescriptor isn't necessary.
diff --git a/lms/djangoapps/courseware/tests/test_course_tools.py b/lms/djangoapps/courseware/tests/test_course_tools.py
index d726bcd7fb..4a012ee6f0 100644
--- a/lms/djangoapps/courseware/tests/test_course_tools.py
+++ b/lms/djangoapps/courseware/tests/test_course_tools.py
@@ -12,12 +12,9 @@ from mock import patch
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
-from edx_toggles.toggles.testutils import override_waffle_flag # lint-amnesty, pylint: disable=wrong-import-order
from lms.djangoapps.courseware.course_tools import FinancialAssistanceTool, VerifiedUpgradeTool
from lms.djangoapps.courseware.models import DynamicUpgradeDeadlineConfiguration
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
-from openedx.core.djangoapps.schedules.config import CREATE_SCHEDULE_WAFFLE_FLAG
-from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@@ -39,7 +36,6 @@ class VerifiedUpgradeToolTest(SharedModuleStoreTestCase): # lint-amnesty, pylin
)
cls.course_overview = CourseOverview.get_from_id(cls.course.id)
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
def setUp(self):
super(VerifiedUpgradeToolTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
@@ -49,11 +45,6 @@ class VerifiedUpgradeToolTest(SharedModuleStoreTestCase): # lint-amnesty, pylin
expiration_datetime=self.now + datetime.timedelta(days=30),
)
- patcher = patch('openedx.core.djangoapps.schedules.signals.get_current_site')
- mock_get_current_site = patcher.start()
- self.addCleanup(patcher.stop)
- mock_get_current_site.return_value = SiteFactory.create()
-
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
self.request = RequestFactory().request()
@@ -122,7 +113,6 @@ class FinancialAssistanceToolTest(SharedModuleStoreTestCase):
)
cls.course_overview = CourseOverview.get_from_id(cls.course.id)
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
def setUp(self):
super(FinancialAssistanceToolTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
diff --git a/lms/djangoapps/courseware/tests/test_date_summary.py b/lms/djangoapps/courseware/tests/test_date_summary.py
index 9601c6d68d..dd5c3aa2e5 100644
--- a/lms/djangoapps/courseware/tests/test_date_summary.py
+++ b/lms/djangoapps/courseware/tests/test_date_summary.py
@@ -40,7 +40,6 @@ from lms.djangoapps.verify_student.models import VerificationDeadline
from lms.djangoapps.verify_student.services import IDVerificationService
from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVerificationFactory
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
-from openedx.core.djangoapps.schedules.signals import CREATE_SCHEDULE_WAFFLE_FLAG
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
@@ -884,16 +883,6 @@ class TestDateAlerts(SharedModuleStoreTestCase):
class TestScheduleOverrides(SharedModuleStoreTestCase):
""" Tests for Schedule Overrides """
- def setUp(self):
- super(TestScheduleOverrides, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
-
- patcher = patch('openedx.core.djangoapps.schedules.signals.get_current_site')
- mock_get_current_site = patcher.start()
- self.addCleanup(patcher.stop)
-
- mock_get_current_site.return_value = SiteFactory.create()
-
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
def test_date_with_self_paced_with_enrollment_before_course_start(self):
""" Enrolling before a course begins should result in the upgrade deadline being set relative to the
course start date. """
@@ -914,7 +903,6 @@ class TestScheduleOverrides(SharedModuleStoreTestCase):
" certificate."
assert upgrade_date_summary.relative_datestring == u'by {date}'
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
def test_date_with_self_paced_with_enrollment_after_course_start(self):
""" Enrolling after a course begins should result in the upgrade deadline being set relative to the
enrollment date.
@@ -947,7 +935,6 @@ class TestScheduleOverrides(SharedModuleStoreTestCase):
expected = enrollment.created + timedelta(days=course_config.deadline_days)
assert block.date == expected
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
def test_date_with_self_paced_without_dynamic_upgrade_deadline(self):
""" Disabling the dynamic upgrade deadline functionality should result in the verified mode's
expiration date being returned. """
@@ -958,7 +945,6 @@ class TestScheduleOverrides(SharedModuleStoreTestCase):
block = VerifiedUpgradeDeadlineDate(course, enrollment.user)
assert block.date == expected
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
def test_date_with_existing_schedule(self):
""" If a schedule is created while deadlines are disabled, they shouldn't magically appear once the feature is
turned on. """
@@ -1018,7 +1004,6 @@ class TestScheduleOverrides(SharedModuleStoreTestCase):
(True, True, True, True, True, False),
)
@ddt.unpack
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
def test_date_with_org_and_course_config_overrides(self, enroll_first, org_config_enabled, org_config_opt_out,
course_config_enabled, course_config_opt_out,
expected_dynamic_deadline):
diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py
index bc8293171c..1a3d56f949 100644
--- a/lms/djangoapps/courseware/tests/test_views.py
+++ b/lms/djangoapps/courseware/tests/test_views.py
@@ -1408,7 +1408,7 @@ class ProgressPageTests(ProgressPageBaseTests):
self.assertContains(resp, u"Download Your Certificate")
@ddt.data(
- (True, 55),
+ (True, 54),
(False, 54),
)
@ddt.unpack
diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py
index fbf4c07636..5db9d5ccfc 100644
--- a/lms/djangoapps/instructor/tests/test_api.py
+++ b/lms/djangoapps/instructor/tests/test_api.py
@@ -22,8 +22,6 @@ from django.http import HttpRequest, HttpResponse
from django.test import RequestFactory, TestCase
from django.urls import reverse as django_reverse
from django.utils.translation import ugettext as _
-from edx_toggles.toggles.testutils import \
- override_waffle_flag # lint-amnesty, pylint: disable=unused-import, wrong-import-order
from edx_when.api import get_dates_for_course, get_overrides_for_user, set_date_for_block
from freezegun import freeze_time
from opaque_keys.edx.keys import CourseKey
@@ -48,28 +46,23 @@ from common.djangoapps.student.models import (
get_retired_email_by_email,
get_retired_username_by_username
)
-from common.djangoapps.student.roles import ( # lint-amnesty, pylint: disable=unused-import
+from common.djangoapps.student.roles import (
CourseBetaTesterRole,
CourseDataResearcherRole,
CourseFinanceAdminRole,
CourseInstructorRole,
- CourseSalesAdminRole
-)
-from common.djangoapps.student.tests.factories import ( # lint-amnesty, pylint: disable=unused-import
- AdminFactory,
- UserFactory
)
+from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from lms.djangoapps.bulk_email.models import BulkEmailFlag, CourseEmail, CourseEmailTemplate
from lms.djangoapps.certificates.api import generate_user_certificates
from lms.djangoapps.certificates.models import CertificateStatuses
from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory
from lms.djangoapps.courseware.models import StudentModule
-from lms.djangoapps.courseware.tests.factories import ( # lint-amnesty, pylint: disable=unused-import
+from lms.djangoapps.courseware.tests.factories import (
BetaTesterFactory,
GlobalStaffFactory,
InstructorFactory,
StaffFactory,
- UserProfileFactory
)
from lms.djangoapps.courseware.tests.helpers import LoginEnrollmentTestCase
from lms.djangoapps.experiments.testutils import override_experiment_waffle_flag
@@ -89,7 +82,6 @@ from openedx.core.djangoapps.course_date_signals.handlers import extract_dates
from openedx.core.djangoapps.course_groups.cohorts import set_course_cohorted
from openedx.core.djangoapps.django_comment_common.models import FORUM_ROLE_COMMUNITY_TA
from openedx.core.djangoapps.django_comment_common.utils import seed_permissions_roles
-from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
from openedx.core.lib.teams_config import TeamsConfig
@@ -3905,8 +3897,8 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
self.user1 = user1
self.user2 = user2
- ScheduleFactory.create(enrollment__user=self.user1, enrollment__course_id=self.course.id)
- ScheduleFactory.create(enrollment__user=self.user2, enrollment__course_id=self.course.id)
+ CourseEnrollmentFactory.create(user=self.user1, course_id=self.course.id)
+ CourseEnrollmentFactory.create(user=self.user2, course_id=self.course.id)
self.instructor = InstructorFactory(course_key=self.course.id)
self.client.login(username=self.instructor.username, password='test')
extract_dates(None, self.course.id)
@@ -4076,8 +4068,8 @@ class TestDueDateExtensionsDeletedDate(ModuleStoreTestCase, LoginEnrollmentTestC
self.user1 = user1
self.user2 = user2
- ScheduleFactory.create(enrollment__user=self.user1, enrollment__course_id=self.course.id)
- ScheduleFactory.create(enrollment__user=self.user2, enrollment__course_id=self.course.id)
+ CourseEnrollmentFactory.create(user=self.user1, course_id=self.course.id)
+ CourseEnrollmentFactory.create(user=self.user2, course_id=self.course.id)
self.instructor = InstructorFactory(course_key=self.course.id)
self.client.login(username=self.instructor.username, password='test')
extract_dates(None, self.course.id)
diff --git a/lms/djangoapps/instructor/tests/test_tools.py b/lms/djangoapps/instructor/tests/test_tools.py
index 2ad4579e00..0713bd1f56 100644
--- a/lms/djangoapps/instructor/tests/test_tools.py
+++ b/lms/djangoapps/instructor/tests/test_tools.py
@@ -17,9 +17,8 @@ from edx_when.field_data import DateLookupFieldData
from opaque_keys.edx.keys import CourseKey
from pytz import UTC
-from common.djangoapps.student.tests.factories import UserFactory
+from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from openedx.core.djangoapps.course_date_signals import handlers
-from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
from xmodule.fields import Date
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
@@ -219,7 +218,7 @@ class TestSetDueDateExtension(ModuleStoreTestCase):
self.week3 = week3
self.user = user
- ScheduleFactory.create(enrollment__user=self.user, enrollment__course_id=self.course.id)
+ CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id)
inject_field_data((course, week1, week2, week3, homework, assignment), course, user)
@@ -307,8 +306,8 @@ class TestDataDumps(ModuleStoreTestCase):
self.week2 = week2
self.user1 = user1
self.user2 = user2
- ScheduleFactory.create(enrollment__user=self.user1, enrollment__course_id=self.course.id)
- ScheduleFactory.create(enrollment__user=self.user2, enrollment__course_id=self.course.id)
+ CourseEnrollmentFactory.create(user=self.user1, course_id=self.course.id)
+ CourseEnrollmentFactory.create(user=self.user2, course_id=self.course.id)
handlers.extract_dates(None, course.id)
def test_dump_module_extensions(self):
diff --git a/lms/djangoapps/mobile_api/users/tests.py b/lms/djangoapps/mobile_api/users/tests.py
index dc73b85850..4f3a0d7f4f 100644
--- a/lms/djangoapps/mobile_api/users/tests.py
+++ b/lms/djangoapps/mobile_api/users/tests.py
@@ -34,7 +34,6 @@ from lms.djangoapps.mobile_api.testutils import (
MobileCourseAccessTestMixin
)
from lms.djangoapps.mobile_api.utils import API_V1, API_V05
-from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
from openedx.core.lib.courses import course_image_url
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
from openedx.features.course_experience.tests.views.helpers import add_course_mode
@@ -279,10 +278,6 @@ class TestUserEnrollmentApi(UrlResetMixin, MobileAPITestCase, MobileAuthUserTest
)
enrollment.created = self.THREE_YEARS_AGO + datetime.timedelta(days=1)
enrollment.save()
-
- ScheduleFactory(
- enrollment=enrollment
- )
else:
course = CourseFactory.create(start=self.LAST_WEEK, mobile_available=True)
self.enroll(course.id)
diff --git a/openedx/core/djangoapps/content/course_overviews/migrations/0024_overview_adds_has_highlights.py b/openedx/core/djangoapps/content/course_overviews/migrations/0024_overview_adds_has_highlights.py
new file mode 100644
index 0000000000..a63459c7a7
--- /dev/null
+++ b/openedx/core/djangoapps/content/course_overviews/migrations/0024_overview_adds_has_highlights.py
@@ -0,0 +1,23 @@
+# Generated by Django 2.2.19 on 2021-02-23 14:30
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('course_overviews', '0023_courseoverview_banner_image_url'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='courseoverview',
+ name='has_highlights',
+ field=models.NullBooleanField(default=None),
+ ),
+ migrations.AddField(
+ model_name='historicalcourseoverview',
+ name='has_highlights',
+ field=models.NullBooleanField(default=None),
+ ),
+ ]
diff --git a/openedx/core/djangoapps/content/course_overviews/models.py b/openedx/core/djangoapps/content/course_overviews/models.py
index bab37b1e9e..0c4649ae3a 100644
--- a/openedx/core/djangoapps/content/course_overviews/models.py
+++ b/openedx/core/djangoapps/content/course_overviews/models.py
@@ -12,7 +12,9 @@ from config_models.models import ConfigurationModel
from django.conf import settings
from django.db import models, transaction
from django.db.models import Q
-from django.db.models.fields import BooleanField, DateTimeField, DecimalField, FloatField, IntegerField, TextField
+from django.db.models.fields import (
+ BooleanField, DateTimeField, DecimalField, FloatField, IntegerField, NullBooleanField, TextField
+)
from django.db.models.signals import post_save, post_delete
from django.db.utils import IntegrityError
from django.template import defaultfilters
@@ -126,6 +128,9 @@ class CourseOverview(TimeStampedModel):
marketing_url = TextField(null=True)
eligible_for_financial_aid = BooleanField(default=True)
+ # Course highlight info, used to guide course update emails
+ has_highlights = NullBooleanField(default=None) # if None, you have to look up the answer yourself
+
language = TextField(null=True)
history = HistoricalRecords()
@@ -229,6 +234,8 @@ class CourseOverview(TimeStampedModel):
course_overview.course_video_url = CourseDetails.fetch_video_url(course.id)
course_overview.self_paced = course.self_paced
+ course_overview.has_highlights = cls._get_course_has_highlights(course)
+
if not CatalogIntegration.is_enabled():
course_overview.language = course.language
@@ -413,6 +420,12 @@ class CourseOverview(TimeStampedModel):
overviews[course_id] = None
return overviews
+ @classmethod
+ def _get_course_has_highlights(cls, course):
+ # Avoid circular import here
+ from openedx.core.djangoapps.schedules.content_highlights import course_has_highlights
+ return course_has_highlights(course)
+
def clean_id(self, padding_char='='):
"""
Returns a unique deterministic base32-encoded ID for the course.
diff --git a/openedx/core/djangoapps/schedules/admin.py b/openedx/core/djangoapps/schedules/admin.py
index 9e27d7c315..fa7e3184e4 100644
--- a/openedx/core/djangoapps/schedules/admin.py
+++ b/openedx/core/djangoapps/schedules/admin.py
@@ -166,22 +166,16 @@ class ScheduleAdmin(admin.ModelAdmin): # lint-amnesty, pylint: disable=missing-
class ScheduleConfigAdminForm(forms.ModelForm): # lint-amnesty, pylint: disable=missing-class-docstring
-
- def clean_hold_back_ratio(self):
- hold_back_ratio = self.cleaned_data["hold_back_ratio"]
- if hold_back_ratio < 0 or hold_back_ratio > 1:
- raise forms.ValidationError("Invalid hold back ratio, the value must be between 0 and 1.")
- return hold_back_ratio
+ pass
@admin.register(models.ScheduleConfig)
class ScheduleConfigAdmin(admin.ModelAdmin): # lint-amnesty, pylint: disable=missing-class-docstring
search_fields = ('site',)
list_display = (
- 'site', 'create_schedules',
+ 'site',
'enqueue_recurring_nudge', 'deliver_recurring_nudge',
'enqueue_upgrade_reminder', 'deliver_upgrade_reminder',
'enqueue_course_update', 'deliver_course_update',
- 'hold_back_ratio',
)
form = ScheduleConfigAdminForm
diff --git a/openedx/core/djangoapps/schedules/config.py b/openedx/core/djangoapps/schedules/config.py
index 768c9f0132..f02043c766 100644
--- a/openedx/core/djangoapps/schedules/config.py
+++ b/openedx/core/djangoapps/schedules/config.py
@@ -2,31 +2,10 @@
Contains configuration for schedules app
"""
+from edx_toggles.toggles import LegacyWaffleSwitch, LegacyWaffleSwitchNamespace, WaffleFlag
-from edx_toggles.toggles import (
- WaffleFlag,
- LegacyWaffleFlagNamespace,
- LegacyWaffleSwitch,
- LegacyWaffleSwitchNamespace
-)
-
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
-
-WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='schedules')
WAFFLE_SWITCH_NAMESPACE = LegacyWaffleSwitchNamespace(name='schedules')
-CREATE_SCHEDULE_WAFFLE_FLAG = CourseWaffleFlag(
- waffle_namespace=WAFFLE_FLAG_NAMESPACE,
- flag_name='create_schedules_for_course',
- module_name=__name__,
-)
-
-COURSE_UPDATE_WAFFLE_FLAG = CourseWaffleFlag(
- waffle_namespace=WAFFLE_FLAG_NAMESPACE,
- flag_name='send_updates_for_course',
- module_name=__name__,
-)
-
# .. toggle_name: schedules.enable_debugging
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
diff --git a/openedx/core/djangoapps/schedules/content_highlights.py b/openedx/core/djangoapps/schedules/content_highlights.py
index 282e4aff82..4d0eac8bcb 100644
--- a/openedx/core/djangoapps/schedules/content_highlights.py
+++ b/openedx/core/djangoapps/schedules/content_highlights.py
@@ -7,7 +7,6 @@ schedule experience built on the Schedules app.
import logging
from openedx.core.djangoapps.course_date_signals.utils import spaced_out_sections
-from openedx.core.djangoapps.schedules.config import COURSE_UPDATE_WAFFLE_FLAG
from openedx.core.djangoapps.schedules.exceptions import CourseUpdateDoesNotExist
from openedx.core.lib.request_utils import get_request_or_stub
from xmodule.modulestore.django import modulestore
@@ -15,16 +14,16 @@ from xmodule.modulestore.django import modulestore
log = logging.getLogger(__name__)
-def course_has_highlights(course_key):
+def course_has_highlights(course):
"""
Does the course have any highlights for any section/week in it?
This ignores access checks, since highlights may be lurking in currently
inaccessible content.
- """
- try:
- course = _get_course_with_highlights(course_key)
- except CourseUpdateDoesNotExist:
+ Arguments:
+ course (CourseDescriptor): course object to check
+ """
+ if not course.highlights_enabled_for_messaging:
return False
else:
@@ -36,12 +35,28 @@ def course_has_highlights(course_key):
if not highlights_are_available:
log.warning(
- 'Course team enabled highlights and provided no highlights in {}'.format(course_key)
+ 'Course team enabled highlights and provided no highlights in {}'.format(course.id)
)
return highlights_are_available
+def course_has_highlights_from_store(course_key):
+ """
+ Does the course have any highlights for any section/week in it?
+ This ignores access checks, since highlights may be lurking in currently
+ inaccessible content.
+
+ Arguments:
+ course_key (CourseKey): course to lookup from the modulestore
+ """
+ try:
+ course = _get_course_descriptor(course_key)
+ except CourseUpdateDoesNotExist:
+ return False
+ return course_has_highlights(course)
+
+
def get_week_highlights(user, course_key, week_num):
"""
Get highlights (list of unicode strings) for a given week.
@@ -76,11 +91,6 @@ def get_next_section_highlights(user, course_key, start_date, target_date):
def _get_course_with_highlights(course_key):
""" Gets Course descriptor iff highlights are enabled for the course """
- if not COURSE_UPDATE_WAFFLE_FLAG.is_enabled(course_key):
- raise CourseUpdateDoesNotExist(
- '{} Course Update Messages waffle flag is disabled.'.format(course_key)
- )
-
course_descriptor = _get_course_descriptor(course_key)
if not course_descriptor.highlights_enabled_for_messaging:
raise CourseUpdateDoesNotExist(
diff --git a/openedx/core/djangoapps/schedules/docs/README.rst b/openedx/core/djangoapps/schedules/docs/README.rst
index 853ca0af7b..9b662eb7bb 100644
--- a/openedx/core/djangoapps/schedules/docs/README.rst
+++ b/openedx/core/djangoapps/schedules/docs/README.rst
@@ -236,28 +236,6 @@ Configuration Flags
Configuring Schedule Creation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Make sure a Site has been created at ``/admin/sites/site``.
-
-ScheduleConfig
-^^^^^^^^^^^^^^
-
-In the Django admin panel at
-``/admin/schedules/scheduleconfig/`` create a ScheduleConfig
-and link it to the Site. Make sure to enable all of the settings:
-
-- ``create_schedules``: enables creating new Schedules when new Course
- Enrollments are created.
-- ``hold_back_ratio``: ratio of all new Course Enrollments that should
- NOT have a Schedule created.
-
-Roll-out Waffle Flag
-^^^^^^^^^^^^^^^^^^^^
-
-There is one roll-out related course waffle flag that we plan to delete
-called ``schedules.create_schedules_for_course``, which, if the
-``ScheduleConfig.create_schedules`` is disabled, will enable schedule
-creation on a per-course basis.
-
Self-paced Configuration
^^^^^^^^^^^^^^^^^^^^^^^^
@@ -324,25 +302,6 @@ configure enqueueing and delivering emails per message type:
- ``deliver_*``: allows delivering emails through ACE for this message
type.
-.. roll-out-waffle-flag-1:
-
-Roll-out Waffle Flag
-^^^^^^^^^^^^^^^^^^^^
-
-Another roll-out related course waffle flag that we plan to delete
-called ``schedules.send_updates_for_course`` will enable sending
-specifically the course updates email per-course.
-
-Configuring Highlights UI in Studio
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The button and modal on the course outline page that allows course
-authors to enter section highlights can be toggled globally by going to
-``/admin/waffle/switch/`` and adding an active switch called
-``dynamic_pacing.studio_course_update``.
-
-This is a roll-out related waffle switch that we will eventually delete.
-
Configuring a Learner’s Schedule
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -463,10 +422,10 @@ To begin using Litmus, follow these steps:
1. Make sure that ACE is configured to use Sailthru (see instructions above).
2. Go to the `Litmus checklist page `__ and start
a new checklist.
-3. The checklist will provide you with an email address to which you will send
+3. The checklist will provide you with an email address to which you will send
a test email.
-4. Send an email. Use one of the management commands with the
- `--override-recipient-email` flag. Use the Litmus email you got in step 3
+4. Send an email. Use one of the management commands with the
+ `--override-recipient-email` flag. Use the Litmus email you got in step 3
as the flag value.
::
diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_course_update.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_course_update.py
index bfb0797f9a..5332d7e56b 100644
--- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_course_update.py
+++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_course_update.py
@@ -12,9 +12,7 @@ from edx_ace.utils.date import serialize
from mock import patch
from six.moves import range
-from edx_toggles.toggles.testutils import override_waffle_flag
from openedx.core.djangoapps.schedules import resolvers, tasks
-from openedx.core.djangoapps.schedules.config import COURSE_UPDATE_WAFFLE_FLAG
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 (
ExperienceTest,
@@ -59,13 +57,12 @@ class TestSendCourseUpdate(ScheduleUpsellTestMixin, ScheduleSendEmailTestMixin,
mock_highlights.return_value = [u'Highlight {}'.format(num + 1) for num in range(3)]
self.addCleanup(self.stop_highlights_patcher)
- def prepare_course_data(self, mock_get_current_site, is_self_paced=True):
+ def prepare_course_data(self, is_self_paced=True):
"""
Prepare course data with highlights
"""
self.highlights_patcher.stop()
self.highlights_patcher = None
- mock_get_current_site.return_value = self.site_config.site
course = CourseFactory(highlights_enabled_for_messaging=True, self_paced=is_self_paced)
with self.store.bulk_operations(course.id):
@@ -96,10 +93,8 @@ class TestSendCourseUpdate(ScheduleUpsellTestMixin, ScheduleSendEmailTestMixin,
def test_schedule_in_different_experience(self, test_config):
self._check_if_email_sent_for_experience(test_config)
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
- @patch('openedx.core.djangoapps.schedules.signals.get_current_site')
- def test_with_course_data(self, mock_get_current_site):
- offset, target_day, enrollment = self.prepare_course_data(mock_get_current_site)
+ def test_with_course_data(self):
+ offset, target_day, enrollment = self.prepare_course_data()
with patch.object(tasks, 'ace') as mock_ace:
self.task().apply(kwargs=dict(
@@ -111,14 +106,12 @@ class TestSendCourseUpdate(ScheduleUpsellTestMixin, ScheduleSendEmailTestMixin,
assert mock_ace.send.called
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
- @patch('openedx.core.djangoapps.schedules.signals.get_current_site')
- def test_template_for_instructor_led_courses(self, mock_get_current_site):
+ def test_template_for_instructor_led_courses(self):
"""
Test that InstructorLedCourseUpdate template is picked for instructor led
courses
"""
- offset, target_day, enrollment = self.prepare_course_data(mock_get_current_site, is_self_paced=False)
+ offset, target_day, enrollment = self.prepare_course_data(is_self_paced=False)
self.task().apply(kwargs=dict(
site_id=self.site_config.site.id,
diff --git a/openedx/core/djangoapps/schedules/models.py b/openedx/core/djangoapps/schedules/models.py
index 00f857af5f..d747b77ccb 100644
--- a/openedx/core/djangoapps/schedules/models.py
+++ b/openedx/core/djangoapps/schedules/models.py
@@ -54,14 +54,14 @@ class ScheduleConfig(ConfigurationModel):
KEY_FIELDS = ('site',)
site = models.ForeignKey(Site, on_delete=models.CASCADE)
- create_schedules = models.BooleanField(default=False)
+ create_schedules = models.BooleanField(default=False) # deprecated, do not use
enqueue_recurring_nudge = models.BooleanField(default=False)
deliver_recurring_nudge = models.BooleanField(default=False)
enqueue_upgrade_reminder = models.BooleanField(default=False)
deliver_upgrade_reminder = models.BooleanField(default=False)
enqueue_course_update = models.BooleanField(default=False)
deliver_course_update = models.BooleanField(default=False)
- hold_back_ratio = models.FloatField(default=0)
+ hold_back_ratio = models.FloatField(default=0) # deprecated, do not use
class ScheduleExperience(models.Model):
diff --git a/openedx/core/djangoapps/schedules/signals.py b/openedx/core/djangoapps/schedules/signals.py
index b1454fd9b6..f84a6f5566 100644
--- a/openedx/core/djangoapps/schedules/signals.py
+++ b/openedx/core/djangoapps/schedules/signals.py
@@ -4,7 +4,6 @@ CourseEnrollment related signal handlers.
import datetime
import logging
-import random
import six
from django.db.models.signals import post_save
@@ -18,16 +17,13 @@ from lms.djangoapps.courseware.models import (
OrgDynamicUpgradeDeadlineConfiguration
)
from openedx.core.djangoapps.content.course_overviews.signals import COURSE_START_DATE_CHANGED
-from openedx.core.djangoapps.schedules.content_highlights import course_has_highlights
+from openedx.core.djangoapps.schedules.content_highlights import course_has_highlights_from_store
from openedx.core.djangoapps.schedules.models import ScheduleExperience
from openedx.core.djangoapps.schedules.utils import reset_self_paced_schedule
-from openedx.core.djangoapps.theming.helpers import get_current_site
from common.djangoapps.student.models import CourseEnrollment
-from common.djangoapps.student.signals import ENROLL_STATUS_CHANGE, ENROLLMENT_TRACK_UPDATED # lint-amnesty, pylint: disable=unused-import
-from common.djangoapps.track import segment
+from common.djangoapps.student.signals import ENROLLMENT_TRACK_UPDATED
-from .config import CREATE_SCHEDULE_WAFFLE_FLAG
-from .models import Schedule, ScheduleConfig
+from .models import Schedule
from .tasks import update_course_schedules
log = logging.getLogger(__name__)
@@ -139,39 +135,6 @@ def _get_upgrade_deadline_delta_setting(course_id): # lint-amnesty, pylint: dis
return delta
-def _should_randomly_suppress_schedule_creation( # lint-amnesty, pylint: disable=missing-function-docstring
- schedule_config,
- enrollment,
- upgrade_deadline,
- experience_type,
- content_availability_date,
-):
- # The hold back ratio is always between 0 and 1. A value of 0 indicates that schedules should be created for all
- # schedules. A value of 1 indicates that no schedules should be created for any enrollments. A value of 0.2 would
- # mean that 20% of enrollments should *not* be given schedules.
-
- # This allows us to measure the impact of the dynamic schedule experience by comparing this "control" group that
- # does not receive any of benefits of the feature against the group that does.
- if random.random() < schedule_config.hold_back_ratio:
- log.debug('Schedules: Enrollment held back from dynamic schedule experiences.')
- upgrade_deadline_str = None
- if upgrade_deadline:
- upgrade_deadline_str = upgrade_deadline.isoformat()
- segment.track(
- user_id=enrollment.user.id,
- event_name='edx.bi.schedule.suppressed',
- properties={
- 'course_id': six.text_type(enrollment.course_id),
- 'experience_type': experience_type,
- 'upgrade_deadline': upgrade_deadline_str,
- 'content_availability_date': content_availability_date.isoformat(),
- }
- )
- return True
-
- return False
-
-
def _create_schedule(enrollment, enrollment_created):
"""
Checks configuration and creates Schedule with ScheduleExperience for this enrollment.
@@ -180,34 +143,12 @@ def _create_schedule(enrollment, enrollment_created):
# only create schedules when enrollment records are created
return
- current_site = get_current_site()
- if current_site is None:
- log.debug('Schedules: No current site')
- return
-
- schedule_config = ScheduleConfig.current(current_site)
- if (
- not schedule_config.create_schedules and
- not CREATE_SCHEDULE_WAFFLE_FLAG.is_enabled(enrollment.course_id)
- ):
- log.debug('Schedules: Creation not enabled for this course or for this site')
- return
-
# This represents the first date at which the learner can access the content. This will be the latter of
# either the enrollment date or the course's start date.
content_availability_date = max(enrollment.created, enrollment.course_overview.start)
upgrade_deadline = _calculate_upgrade_deadline(enrollment.course_id, content_availability_date)
experience_type = _get_experience_type(enrollment)
- if _should_randomly_suppress_schedule_creation(
- schedule_config,
- enrollment,
- upgrade_deadline,
- experience_type,
- content_availability_date,
- ):
- return
-
schedule = Schedule.objects.create(
enrollment=enrollment,
start_date=content_availability_date,
@@ -229,7 +170,11 @@ def _get_experience_type(enrollment):
Schedules will receive the Course Updates experience if the course has any section highlights defined.
"""
- if course_has_highlights(enrollment.course_id):
+ has_highlights = enrollment.course_overview.has_highlights
+ if has_highlights is None: # old course that doesn't have this info cached in the overview
+ has_highlights = course_has_highlights_from_store(enrollment.course_id)
+
+ if has_highlights:
return ScheduleExperience.EXPERIENCES.course_updates
else:
return ScheduleExperience.EXPERIENCES.default
diff --git a/openedx/core/djangoapps/schedules/tests/factories.py b/openedx/core/djangoapps/schedules/tests/factories.py
index 4e4467e85b..580cf44018 100644
--- a/openedx/core/djangoapps/schedules/tests/factories.py
+++ b/openedx/core/djangoapps/schedules/tests/factories.py
@@ -33,11 +33,9 @@ class ScheduleConfigFactory(factory.DjangoModelFactory): # lint-amnesty, pylint
model = models.ScheduleConfig
site = factory.SubFactory(SiteFactory)
- create_schedules = True
enqueue_recurring_nudge = True
deliver_recurring_nudge = True
enqueue_upgrade_reminder = True
deliver_upgrade_reminder = True
enqueue_course_update = True
deliver_course_update = True
- hold_back_ratio = 0
diff --git a/openedx/core/djangoapps/schedules/tests/test_content_highlights.py b/openedx/core/djangoapps/schedules/tests/test_content_highlights.py
index edededafb0..adb3f63243 100644
--- a/openedx/core/djangoapps/schedules/tests/test_content_highlights.py
+++ b/openedx/core/djangoapps/schedules/tests/test_content_highlights.py
@@ -3,10 +3,8 @@
import datetime
from unittest.mock import patch
import pytest
-from edx_toggles.toggles.testutils import override_waffle_flag
-from openedx.core.djangoapps.schedules.config import COURSE_UPDATE_WAFFLE_FLAG
from openedx.core.djangoapps.schedules.content_highlights import (
- course_has_highlights,
+ course_has_highlights_from_store,
get_next_section_highlights,
get_week_highlights
)
@@ -44,35 +42,22 @@ class TestContentHighlights(ModuleStoreTestCase): # lint-amnesty, pylint: disab
**kwargs
)
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_non_existent_course_raises_exception(self):
nonexistent_course_key = self.course_key.replace(run='no_such_run')
with pytest.raises(CourseUpdateDoesNotExist):
get_week_highlights(self.user, nonexistent_course_key, week_num=1)
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_empty_course_raises_exception(self):
with pytest.raises(CourseUpdateDoesNotExist):
get_week_highlights(self.user, self.course_key, week_num=1)
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, False)
- def test_flag_disabled(self):
- with self.store.bulk_operations(self.course_key):
- self._create_chapter(highlights=['highlights'])
-
- assert not course_has_highlights(self.course_key)
- with pytest.raises(CourseUpdateDoesNotExist):
- get_week_highlights(self.user, self.course_key, week_num=1)
-
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
- def test_flag_enabled(self):
+ def test_happy_path(self):
highlights = ['highlights']
with self.store.bulk_operations(self.course_key):
self._create_chapter(highlights=highlights)
- assert course_has_highlights(self.course_key)
+ assert course_has_highlights_from_store(self.course_key)
assert get_week_highlights(self.user, self.course_key, week_num=1) == highlights
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_highlights_disabled_for_messaging(self):
highlights = ['A test highlight.']
with self.store.bulk_operations(self.course_key):
@@ -80,7 +65,7 @@ class TestContentHighlights(ModuleStoreTestCase): # lint-amnesty, pylint: disab
self.course.highlights_enabled_for_messaging = False
self.store.update_item(self.course, self.user.id)
- assert not course_has_highlights(self.course_key)
+ assert not course_has_highlights_from_store(self.course_key)
with pytest.raises(CourseUpdateDoesNotExist):
get_week_highlights(
@@ -89,7 +74,6 @@ class TestContentHighlights(ModuleStoreTestCase): # lint-amnesty, pylint: disab
week_num=1,
)
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_course_with_no_highlights(self):
with self.store.bulk_operations(self.course_key):
self._create_chapter(display_name=u"Week 1")
@@ -98,25 +82,23 @@ class TestContentHighlights(ModuleStoreTestCase): # lint-amnesty, pylint: disab
self.course = self.store.get_course(self.course_key) # lint-amnesty, pylint: disable=attribute-defined-outside-init
assert len(self.course.get_children()) == 2
- assert not course_has_highlights(self.course_key)
+ assert not course_has_highlights_from_store(self.course_key)
with pytest.raises(CourseUpdateDoesNotExist):
get_week_highlights(self.user, self.course_key, week_num=1)
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_course_with_highlights(self):
with self.store.bulk_operations(self.course_key):
self._create_chapter(highlights=['a', 'b', 'á'])
self._create_chapter(highlights=[])
self._create_chapter(highlights=['skipped a week'])
- assert course_has_highlights(self.course_key)
+ assert course_has_highlights_from_store(self.course_key)
assert get_week_highlights(self.user, self.course_key, week_num=1) == ['a', 'b', 'á']
assert get_week_highlights(self.user, self.course_key, week_num=2) == ['skipped a week']
with pytest.raises(CourseUpdateDoesNotExist):
get_week_highlights(self.user, self.course_key, week_num=3)
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_staff_only(self):
with self.store.bulk_operations(self.course_key):
self._create_chapter(
@@ -124,11 +106,10 @@ class TestContentHighlights(ModuleStoreTestCase): # lint-amnesty, pylint: disab
visible_to_staff_only=True,
)
- assert course_has_highlights(self.course_key)
+ assert course_has_highlights_from_store(self.course_key)
with pytest.raises(CourseUpdateDoesNotExist):
get_week_highlights(self.user, self.course_key, week_num=1)
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
@patch('openedx.core.djangoapps.course_date_signals.utils.get_expected_duration')
def test_get_next_section_highlights(self, mock_duration):
# All of the dates chosen here are to make things easy and clean to calculate with date offsets
@@ -170,7 +151,6 @@ class TestContentHighlights(ModuleStoreTestCase): # lint-amnesty, pylint: disab
with pytest.raises(CourseUpdateDoesNotExist):
get_next_section_highlights(self.user, self.course_key, two_days_ago, six_days.date())
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
@patch('lms.djangoapps.courseware.module_render.get_module_for_descriptor')
def test_get_highlights_without_module(self, mock_get_module):
mock_get_module.return_value = None
diff --git a/openedx/core/djangoapps/schedules/tests/test_resolvers.py b/openedx/core/djangoapps/schedules/tests/test_resolvers.py
index bc51100323..d0fb6776b1 100644
--- a/openedx/core/djangoapps/schedules/tests/test_resolvers.py
+++ b/openedx/core/djangoapps/schedules/tests/test_resolvers.py
@@ -4,7 +4,7 @@ Tests for schedules resolvers
import datetime
-from unittest.mock import Mock, patch
+from unittest.mock import Mock
import ddt
from django.test import TestCase
@@ -12,8 +12,6 @@ from django.test.utils import override_settings
from testfixtures import LogCapture
from waffle.testutils import override_switch
-from edx_toggles.toggles.testutils import override_waffle_flag
-from openedx.core.djangoapps.schedules.config import COURSE_UPDATE_WAFFLE_FLAG
from openedx.core.djangoapps.schedules.models import Schedule
from openedx.core.djangoapps.schedules.resolvers import (
LOG,
@@ -111,9 +109,7 @@ class TestCourseUpdateResolver(SchedulesResolverTestMixin, ModuleStoreTestCase):
"""
Creates a CourseUpdateResolver with an enrollment to schedule.
"""
- with patch('openedx.core.djangoapps.schedules.signals.get_current_site') as mock_get_current_site:
- mock_get_current_site.return_value = self.site_config.site
- enrollment = CourseEnrollmentFactory(course_id=self.course.id, user=self.user, mode='audit')
+ enrollment = CourseEnrollmentFactory(course_id=self.course.id, user=self.user, mode='audit')
return CourseUpdateResolver(
async_send_task=Mock(name='async_send_task'),
@@ -125,7 +121,6 @@ class TestCourseUpdateResolver(SchedulesResolverTestMixin, ModuleStoreTestCase):
@override_settings(CONTACT_MAILING_ADDRESS='123 Sesame Street')
@override_settings(LOGO_URL_PNG='https://www.logo.png')
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_schedule_context(self):
resolver = self.create_resolver()
schedules = list(resolver.schedules_for_bin())
@@ -149,14 +144,12 @@ class TestCourseUpdateResolver(SchedulesResolverTestMixin, ModuleStoreTestCase):
}
assert schedules == [(self.user, None, expected_context)]
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
@override_switch('schedules.course_update_show_unsubscribe', True)
def test_schedule_context_show_unsubscribe(self):
resolver = self.create_resolver()
schedules = list(resolver.schedules_for_bin())
assert 'optout' in schedules[0][2]['unsubscribe_url']
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_get_schedules_with_target_date_by_bin_and_orgs_filter_inactive_users(self):
"""Tests that schedules of inactive users are excluded"""
resolver = self.create_resolver()
@@ -194,9 +187,7 @@ class TestCourseNextSectionUpdateResolver(SchedulesResolverTestMixin, ModuleStor
"""
Creates a CourseNextSectionUpdateResolver with an enrollment to schedule.
"""
- with patch('openedx.core.djangoapps.schedules.signals.get_current_site') as mock_get_current_site:
- mock_get_current_site.return_value = self.site_config.site
- CourseEnrollmentFactory(course_id=self.course.id, user=self.user, mode='audit')
+ CourseEnrollmentFactory(course_id=self.course.id, user=self.user, mode='audit')
# Need to update the user's schedule so the due date for the chapter we want
# matches with the user's schedule and the target date. The numbers are based on the
@@ -214,7 +205,6 @@ class TestCourseNextSectionUpdateResolver(SchedulesResolverTestMixin, ModuleStor
@override_settings(CONTACT_MAILING_ADDRESS='123 Sesame Street')
@override_settings(LOGO_URL_PNG='https://www.logo.png')
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_schedule_context(self):
resolver = self.create_resolver()
# using this to make sure the select_related stays intact
@@ -242,14 +232,12 @@ class TestCourseNextSectionUpdateResolver(SchedulesResolverTestMixin, ModuleStor
}
assert schedules == [(self.user, None, expected_context)]
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
@override_switch('schedules.course_update_show_unsubscribe', True)
def test_schedule_context_show_unsubscribe(self):
resolver = self.create_resolver()
schedules = list(resolver.get_schedules())
assert 'optout' in schedules[0][2]['unsubscribe_url']
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_schedule_context_error(self):
resolver = self.create_resolver(user_start_date_offset=29)
with LogCapture(LOG.name) as log_capture:
@@ -258,7 +246,6 @@ class TestCourseNextSectionUpdateResolver(SchedulesResolverTestMixin, ModuleStor
'There are no more highlights for {}'.format(self.course.id))
log_capture.check_present((LOG.name, 'WARNING', log_message))
- @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_no_updates_if_course_ended(self):
self.course.end = self.yesterday
self.course = self.update_course(self.course, self.user.id)
diff --git a/openedx/core/djangoapps/schedules/tests/test_signals.py b/openedx/core/djangoapps/schedules/tests/test_signals.py
index bf50282a2a..0739a5e7d6 100644
--- a/openedx/core/djangoapps/schedules/tests/test_signals.py
+++ b/openedx/core/djangoapps/schedules/tests/test_signals.py
@@ -7,16 +7,14 @@ import datetime
import ddt
import pytest
-from edx_toggles.toggles.testutils import override_waffle_flag
from mock import patch
from pytz import utc
-from testfixtures import LogCapture
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from lms.djangoapps.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, log
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
from openedx.core.djangolib.testing.utils import skip_unless_lms
from common.djangoapps.student.models import CourseEnrollment
@@ -30,7 +28,6 @@ from ..tests.factories import ScheduleConfigFactory
@ddt.ddt
-@patch('openedx.core.djangoapps.schedules.signals.get_current_site')
@skip_unless_lms
class CreateScheduleTests(SharedModuleStoreTestCase): # lint-amnesty, pylint: disable=missing-class-docstring
@@ -57,115 +54,39 @@ class CreateScheduleTests(SharedModuleStoreTestCase): # lint-amnesty, pylint: d
with pytest.raises(Schedule.DoesNotExist):
enrollment.schedule # lint-amnesty, pylint: disable=pointless-statement
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
- def test_create_schedule(self, mock_get_current_site):
- site = SiteFactory.create()
- mock_get_current_site.return_value = site
- ScheduleConfigFactory.create(site=site)
+ def test_create_schedule(self):
self.assert_schedule_created()
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
- def test_no_current_site(self, mock_get_current_site):
- mock_get_current_site.return_value = None
- self.assert_schedule_not_created()
-
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
- def test_schedule_config_disabled_waffle_enabled(self, mock_get_current_site):
- site = SiteFactory.create()
- mock_get_current_site.return_value = site
- ScheduleConfigFactory.create(site=site, create_schedules=False)
- self.assert_schedule_created()
-
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, False)
- def test_schedule_config_enabled_waffle_disabled(self, mock_get_current_site):
- site = SiteFactory.create()
- mock_get_current_site.return_value = site
- ScheduleConfigFactory.create(site=site, create_schedules=True)
- self.assert_schedule_created()
-
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, False)
- def test_schedule_config_disabled_waffle_disabled(self, mock_get_current_site):
- site = SiteFactory.create()
- mock_get_current_site.return_value = site
- ScheduleConfigFactory.create(site=site, create_schedules=False)
- with LogCapture(log.name) as log_capture:
- self.assert_schedule_not_created()
- log_capture.check((log.name, 'DEBUG', 'Schedules: Creation not enabled for this course or for this site'))
-
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
- @patch('openedx.core.djangoapps.schedules.signals.course_has_highlights')
- def test_schedule_config_creation_enabled_instructor_paced(self, mock_course_has_highlights, mock_get_current_site):
- site = SiteFactory.create()
- mock_course_has_highlights.return_value = True
- mock_get_current_site.return_value = site
+ @patch.object(CourseOverview, '_get_course_has_highlights', return_value=True)
+ def test_schedule_config_creation_enabled_instructor_paced(self, _mock_highlights):
self.assert_schedule_created(is_self_paced=False, experience_type=ScheduleExperience.EXPERIENCES.course_updates)
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
- @patch('openedx.core.djangoapps.schedules.signals.course_has_highlights')
- def test_create_schedule_course_updates_experience(self, mock_course_has_highlights, mock_get_current_site):
- site = SiteFactory.create()
- mock_course_has_highlights.return_value = True
- mock_get_current_site.return_value = site
+ @patch.object(CourseOverview, '_get_course_has_highlights', return_value=True)
+ def test_create_schedule_course_updates_experience(self, _mock_highlights):
self.assert_schedule_created(experience_type=ScheduleExperience.EXPERIENCES.course_updates)
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
- @patch('openedx.core.djangoapps.schedules.signals.segment.track')
- @patch('openedx.core.djangoapps.schedules.signals.random.random', return_value=0.2)
- @ddt.data(
- (0, True),
- (0.1, True),
- (0.3, False),
- )
- @ddt.unpack
- def test_create_schedule_hold_backs(
- self,
- hold_back_ratio,
- expect_schedule_created,
- mock_random, # lint-amnesty, pylint: disable=unused-argument
- mock_track,
- mock_get_current_site
- ):
- schedule_config = ScheduleConfigFactory.create(enabled=True, hold_back_ratio=hold_back_ratio)
- mock_get_current_site.return_value = schedule_config.site
- if expect_schedule_created:
- self.assert_schedule_created()
- assert not mock_track.called
- else:
- self.assert_schedule_not_created()
- mock_track.assert_called_once()
- assert mock_track.call_args[1].get('event_name') == 'edx.bi.schedule.suppressed'
-
@patch('openedx.core.djangoapps.schedules.signals.log.exception')
@patch('openedx.core.djangoapps.schedules.signals.Schedule.objects.create')
- def test_create_schedule_error(self, mock_create_schedule, mock_log, mock_get_current_site):
- site = SiteFactory.create()
- mock_get_current_site.return_value = site
- ScheduleConfigFactory.create(site=site)
+ def test_create_schedule_error(self, mock_create_schedule, mock_log):
mock_create_schedule.side_effect = ValueError('Fake error')
self.assert_schedule_not_created()
mock_log.assert_called_once()
assert 'Encountered error in creating a Schedule for CourseEnrollment' in mock_log.call_args[0][0]
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
- def test_course_start_date_in_future(self, mock_get_current_site):
+ def test_course_start_date_in_future(self):
"""
Test that the schedule start date will be set to course's start date
if course starts after enrollment
"""
- site = SiteFactory.create()
- mock_get_current_site.return_value = site
course = _create_course_run(self_paced=True, start_day_offset=5) # course starts in future
enrollment = CourseEnrollmentFactory(course_id=course.id, mode=CourseMode.AUDIT)
assert _strip_secs(enrollment.schedule.start_date) == _strip_secs(course.start)
- @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
- def test_course_already_started(self, mock_get_current_site):
+ def test_course_already_started(self):
"""
Test that the schedule start date will be set to the date enrollment was
created if course has already started
"""
- site = SiteFactory.create()
- mock_get_current_site.return_value = site
course = _create_course_run(self_paced=True, start_day_offset=-5) # course already started
enrollment = CourseEnrollmentFactory(course_id=course.id, mode=CourseMode.AUDIT)
assert _strip_secs(enrollment.schedule.start_date) == _strip_secs(enrollment.created)
@@ -173,7 +94,6 @@ class CreateScheduleTests(SharedModuleStoreTestCase): # lint-amnesty, pylint: d
@ddt.ddt
@skip_unless_lms
-@patch('openedx.core.djangoapps.schedules.signals.get_current_site')
class UpdateScheduleTests(SharedModuleStoreTestCase): # lint-amnesty, pylint: disable=missing-class-docstring
ENABLED_SIGNALS = ['course_published']
VERIFICATION_DEADLINE_DAYS = 14
@@ -189,9 +109,7 @@ class UpdateScheduleTests(SharedModuleStoreTestCase): # lint-amnesty, pylint: d
deadline_delta = datetime.timedelta(days=self.VERIFICATION_DEADLINE_DAYS)
assert _strip_secs(schedule.upgrade_deadline) == _strip_secs(expected_start) + deadline_delta
- def test_updated_when_course_not_started(self, mock_get_current_site):
- mock_get_current_site.return_value = self.site
-
+ def test_updated_when_course_not_started(self):
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.start)
@@ -201,9 +119,7 @@ class UpdateScheduleTests(SharedModuleStoreTestCase): # lint-amnesty, pylint: d
enrollment = CourseEnrollment.objects.get(id=enrollment.id)
self.assert_schedule_dates(enrollment.schedule, course.start) # start set to new course start
- def test_updated_when_course_already_started(self, mock_get_current_site):
- mock_get_current_site.return_value = self.site
-
+ def test_updated_when_course_already_started(self):
course = _create_course_run(self_paced=True, start_day_offset=-5) # course starts in past
enrollment = CourseEnrollmentFactory(course_id=course.id, mode=CourseMode.AUDIT)
self.assert_schedule_dates(enrollment.schedule, enrollment.created)
@@ -213,9 +129,7 @@ class UpdateScheduleTests(SharedModuleStoreTestCase): # lint-amnesty, pylint: d
enrollment = CourseEnrollment.objects.get(id=enrollment.id)
self.assert_schedule_dates(enrollment.schedule, course.start) # start set to new course start
- def test_updated_when_new_start_in_past(self, mock_get_current_site):
- mock_get_current_site.return_value = self.site
-
+ def test_updated_when_new_start_in_past(self):
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.start
@@ -228,17 +142,11 @@ class UpdateScheduleTests(SharedModuleStoreTestCase): # lint-amnesty, pylint: d
@skip_unless_lms
-@override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
class ResetScheduleTests(SharedModuleStoreTestCase): # lint-amnesty, pylint: disable=missing-class-docstring
def setUp(self):
super().setUp()
- self.config = ScheduleConfigFactory(create_schedules=True)
-
- site_patch = patch('openedx.core.djangoapps.schedules.signals.get_current_site', return_value=self.config.site)
- self.addCleanup(site_patch.stop)
- site_patch.start()
-
+ self.config = ScheduleConfigFactory()
self.course = _create_course_run(self_paced=True)
self.enrollment = CourseEnrollmentFactory(
course_id=self.course.id,
diff --git a/openedx/core/djangoapps/schedules/tests/test_utils.py b/openedx/core/djangoapps/schedules/tests/test_utils.py
index 8e6a7d90a1..4bfb76df36 100644
--- a/openedx/core/djangoapps/schedules/tests/test_utils.py
+++ b/openedx/core/djangoapps/schedules/tests/test_utils.py
@@ -5,10 +5,9 @@ Tests for schedules utils
import datetime
import ddt
-from common.djangoapps.course_modes.models import CourseMode
-from mock import patch # lint-amnesty, pylint: disable=wrong-import-order
-from pytz import utc # lint-amnesty, pylint: disable=wrong-import-order
+from pytz import utc
+from common.djangoapps.course_modes.models import CourseMode
from openedx.core.djangoapps.schedules.models import Schedule
from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory
from openedx.core.djangoapps.schedules.utils import reset_self_paced_schedule
@@ -22,11 +21,7 @@ from xmodule.modulestore.tests.factories import CourseFactory
@skip_unless_lms
class ResetSelfPacedScheduleTests(SharedModuleStoreTestCase): # lint-amnesty, pylint: disable=missing-class-docstring
def create_schedule(self, offset=0): # lint-amnesty, pylint: disable=missing-function-docstring
- self.config = ScheduleConfigFactory(create_schedules=True) # lint-amnesty, pylint: disable=attribute-defined-outside-init
-
- site_patch = patch('openedx.core.djangoapps.schedules.signals.get_current_site', return_value=self.config.site)
- self.addCleanup(site_patch.stop)
- site_patch.start()
+ self.config = ScheduleConfigFactory() # lint-amnesty, pylint: disable=attribute-defined-outside-init
start = datetime.datetime.now(utc) - datetime.timedelta(days=100)
self.course = CourseFactory.create(start=start, self_paced=True) # lint-amnesty, pylint: disable=attribute-defined-outside-init
diff --git a/openedx/features/course_duration_limits/tests/test_access.py b/openedx/features/course_duration_limits/tests/test_access.py
index 8ce1e90b04..8b18d3a9bd 100644
--- a/openedx/features/course_duration_limits/tests/test_access.py
+++ b/openedx/features/course_duration_limits/tests/test_access.py
@@ -14,7 +14,7 @@ from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from common.djangoapps.student.tests.factories import UserFactory
from lms.djangoapps.courseware.models import DynamicUpgradeDeadlineConfiguration
-from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
+from openedx.core.djangoapps.schedules.models import Schedule
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
from openedx.features.course_duration_limits.access import (
@@ -113,10 +113,7 @@ class TestAccess(CacheIsolationTestCase):
course_id=enrollment.course.id,
mode_slug=CourseMode.AUDIT,
)
- ScheduleFactory.create(
- enrollment=enrollment,
- upgrade_deadline=schedule_upgrade_deadline,
- )
+ Schedule.objects.update(upgrade_deadline=schedule_upgrade_deadline)
duration_limit_upgrade_deadline = get_user_course_expiration_date(enrollment.user, enrollment.course)
assert duration_limit_upgrade_deadline is not None
@@ -155,10 +152,7 @@ class TestAccess(CacheIsolationTestCase):
course_id=enrollment.course.id,
mode_slug=CourseMode.AUDIT,
)
- ScheduleFactory.create(
- enrollment=enrollment,
- start_date=datetime(2017, 1, 1, tzinfo=UTC),
- )
+ Schedule.objects.update(start_date=datetime(2017, 1, 1, tzinfo=UTC))
content_availability_date = max(enrollment.created, enrollment.course.start)
access_duration = get_user_course_duration(enrollment.user, enrollment.course)
diff --git a/openedx/features/course_duration_limits/tests/test_course_expiration.py b/openedx/features/course_duration_limits/tests/test_course_expiration.py
index 9d6f084e6d..ee0edc4e18 100644
--- a/openedx/features/course_duration_limits/tests/test_course_expiration.py
+++ b/openedx/features/course_duration_limits/tests/test_course_expiration.py
@@ -30,7 +30,7 @@ from openedx.core.djangoapps.django_comment_common.models import (
FORUM_ROLE_GROUP_MODERATOR,
FORUM_ROLE_MODERATOR
)
-from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
+from openedx.core.djangoapps.schedules.models import Schedule
from openedx.features.content_type_gating.helpers import CONTENT_GATING_PARTITION_ID, CONTENT_TYPE_GATE_GROUP_IDS
from openedx.features.course_duration_limits.access import get_user_course_expiration_date
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
@@ -337,12 +337,12 @@ class CourseExpirationTestCase(ModuleStoreTestCase, MasqueradeMixin):
else:
expired_staff = role_factory.create(password=TEST_PASSWORD, course_key=self.course.id)
- ScheduleFactory(
- start_date=self.THREE_YEARS_AGO,
- enrollment__mode=CourseMode.AUDIT,
- enrollment__course_id=self.course.id,
- enrollment__user=expired_staff
+ CourseEnrollmentFactory.create(
+ mode=CourseMode.AUDIT,
+ course_id=self.course.id,
+ user=expired_staff,
)
+ Schedule.objects.update(start_date=self.THREE_YEARS_AGO)
CourseDurationLimitConfig.objects.create(
enabled=True,
course=CourseOverview.get_from_id(self.course.id),
@@ -385,12 +385,12 @@ class CourseExpirationTestCase(ModuleStoreTestCase, MasqueradeMixin):
role = RoleFactory(name=role_name, course_id=self.course.id)
role.users.add(expired_staff)
- ScheduleFactory(
- start_date=self.THREE_YEARS_AGO,
- enrollment__mode=CourseMode.AUDIT,
- enrollment__course_id=self.course.id,
- enrollment__user=expired_staff
+ CourseEnrollmentFactory.create(
+ mode=CourseMode.AUDIT,
+ course_id=self.course.id,
+ user=expired_staff,
)
+ Schedule.objects.update(start_date=self.THREE_YEARS_AGO)
CourseDurationLimitConfig.objects.create(
enabled=True,
diff --git a/openedx/features/course_experience/api/v1/tests/test_views.py b/openedx/features/course_experience/api/v1/tests/test_views.py
index 87cc854be3..c92cf32f7e 100644
--- a/openedx/features/course_experience/api/v1/tests/test_views.py
+++ b/openedx/features/course_experience/api/v1/tests/test_views.py
@@ -44,15 +44,12 @@ class ResetCourseDeadlinesViewTests(EventTestMixin, BaseCourseHomeTests, Masquer
student_username = self.user.username
student_user_id = self.user.id
student_enrollment = CourseEnrollment.enroll(self.user, course.id)
- student_schedule = ScheduleFactory.create(
- start_date=timezone.now() - datetime.timedelta(days=100),
- enrollment=student_enrollment
- )
- staff_schedule = ScheduleFactory(
- start_date=timezone.now() - datetime.timedelta(days=30),
- enrollment__course__id=course.id,
- enrollment__user=self.staff_user,
- )
+ student_enrollment.schedule.start_date = timezone.now() - datetime.timedelta(days=100)
+ student_enrollment.schedule.save()
+
+ staff_enrollment = CourseEnrollment.enroll(self.staff_user, course.id)
+ staff_enrollment.schedule.start_date = timezone.now() - datetime.timedelta(days=30)
+ staff_enrollment.schedule.save()
self.switch_to_staff()
self.update_masquerade(course=course, username=student_username)
@@ -60,10 +57,10 @@ class ResetCourseDeadlinesViewTests(EventTestMixin, BaseCourseHomeTests, Masquer
with patch('openedx.features.course_experience.api.v1.views.dates_banner_should_display',
return_value=(True, False)):
self.client.post(reverse('course-experience-reset-course-deadlines'), {'course_key': course.id})
- updated_schedule = Schedule.objects.get(id=student_schedule.id)
+ updated_schedule = Schedule.objects.get(id=student_enrollment.schedule.id)
assert updated_schedule.start_date.date() == datetime.datetime.today().date()
- updated_staff_schedule = Schedule.objects.get(id=staff_schedule.id)
- assert updated_staff_schedule.start_date == staff_schedule.start_date
+ updated_staff_schedule = Schedule.objects.get(id=staff_enrollment.schedule.id)
+ assert updated_staff_schedule.start_date == staff_enrollment.schedule.start_date
self.assert_event_emitted(
'edx.ui.lms.reset_deadlines.clicked',
courserun_key=str(course.id),
diff --git a/openedx/features/course_experience/tests/views/test_course_home.py b/openedx/features/course_experience/tests/views/test_course_home.py
index 874b9b71c5..24ff886d83 100644
--- a/openedx/features/course_experience/tests/views/test_course_home.py
+++ b/openedx/features/course_experience/tests/views/test_course_home.py
@@ -44,7 +44,7 @@ from openedx.core.djangoapps.django_comment_common.models import (
FORUM_ROLE_GROUP_MODERATOR,
FORUM_ROLE_MODERATOR
)
-from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
+from openedx.core.djangoapps.schedules.models import Schedule
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
from openedx.core.djangolib.markup import HTML
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
@@ -448,12 +448,8 @@ class TestCourseHomePageAccess(CourseHomePageTestCase):
url = course_home_url(course)
user = UserFactory.create(password=self.TEST_PASSWORD)
- ScheduleFactory(
- start_date=THREE_YEARS_AGO,
- enrollment__mode=CourseMode.VERIFIED,
- enrollment__course_id=course.id,
- enrollment__user=user
- )
+ CourseEnrollment.enroll(user, self.course.id, mode=CourseMode.VERIFIED)
+ Schedule.objects.update(start_date=THREE_YEARS_AGO)
# ensure that the user who has indefinite access
self.client.login(username=user.username, password=self.TEST_PASSWORD)
@@ -478,12 +474,8 @@ class TestCourseHomePageAccess(CourseHomePageTestCase):
url = course_home_url(course)
user = role_factory.create(password=self.TEST_PASSWORD, course_key=course.id)
- ScheduleFactory(
- start_date=THREE_YEARS_AGO,
- enrollment__mode=CourseMode.AUDIT,
- enrollment__course_id=course.id,
- enrollment__user=user
- )
+ CourseEnrollment.enroll(user, self.course.id, mode=CourseMode.AUDIT)
+ Schedule.objects.update(start_date=THREE_YEARS_AGO)
# ensure that the user has indefinite access
self.client.login(username=user.username, password=self.TEST_PASSWORD)
@@ -526,12 +518,8 @@ class TestCourseHomePageAccess(CourseHomePageTestCase):
url = course_home_url(course)
user = role_factory.create(password=self.TEST_PASSWORD)
- ScheduleFactory(
- start_date=THREE_YEARS_AGO,
- enrollment__mode=CourseMode.AUDIT,
- enrollment__course_id=course.id,
- enrollment__user=user
- )
+ CourseEnrollment.enroll(user, self.course.id, mode=CourseMode.AUDIT)
+ Schedule.objects.update(start_date=THREE_YEARS_AGO)
# ensure that the user who has indefinite access
self.client.login(username=user.username, password=self.TEST_PASSWORD)
@@ -557,7 +545,6 @@ class TestCourseHomePageAccess(CourseHomePageTestCase):
audit_enrollment = CourseEnrollment.enroll(audit_user, course.id, mode=CourseMode.AUDIT)
audit_enrollment.created = THREE_YEARS_AGO + timedelta(days=1)
audit_enrollment.save()
- ScheduleFactory(enrollment=audit_enrollment)
response = self.client.get(url)
@@ -627,7 +614,7 @@ class TestCourseHomePageAccess(CourseHomePageTestCase):
audit_user = UserFactory(password=self.TEST_PASSWORD)
self.client.login(username=audit_user.username, password=self.TEST_PASSWORD)
audit_enrollment = CourseEnrollment.enroll(audit_user, course.id, mode=CourseMode.AUDIT)
- ScheduleFactory(start_date=THREE_YEARS_AGO, enrollment=audit_enrollment)
+ Schedule.objects.update(start_date=THREE_YEARS_AGO)
FBEEnrollmentExclusion.objects.create(
enrollment=audit_enrollment
)
diff --git a/openedx/features/course_experience/tests/views/test_course_outline.py b/openedx/features/course_experience/tests/views/test_course_outline.py
index 825cdfca27..888c47a59e 100644
--- a/openedx/features/course_experience/tests/views/test_course_outline.py
+++ b/openedx/features/course_experience/tests/views/test_course_outline.py
@@ -128,11 +128,8 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase, MasqueradeMixin):
"""Set up and enroll our fake user in the course."""
cls.user = UserFactory(password=TEST_PASSWORD)
for course in cls.courses:
- enrollment = CourseEnrollment.enroll(cls.user, course.id)
- ScheduleFactory.create(
- start_date=timezone.now() - datetime.timedelta(days=1),
- enrollment=enrollment
- )
+ CourseEnrollment.enroll(cls.user, course.id)
+ Schedule.objects.update(start_date=timezone.now() - datetime.timedelta(days=1))
def setUp(self):
"""
@@ -238,53 +235,41 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase, MasqueradeMixin):
@override_experiment_waffle_flag(RELATIVE_DATES_FLAG, active=True)
def test_reset_course_deadlines(self):
course = self.courses[0]
- enrollment = CourseEnrollment.objects.get(course_id=course.id)
- enrollment.schedule.start_date = timezone.now() - datetime.timedelta(days=30)
- enrollment.schedule.save()
- student_schedule = CourseEnrollment.objects.get(course_id=course.id, user=self.user).schedule
- student_schedule.start_date = timezone.now() - datetime.timedelta(days=30)
- student_schedule.save()
staff = StaffFactory(course_key=course.id)
- staff_schedule = ScheduleFactory(
- start_date=timezone.now() - datetime.timedelta(days=30),
- enrollment__course__id=course.id,
- enrollment__user=staff,
- )
+ CourseEnrollment.enroll(staff, course.id)
+
+ start_date = timezone.now() - datetime.timedelta(days=30)
+ Schedule.objects.update(start_date=start_date)
self.client.login(username=staff.username, password=TEST_PASSWORD)
self.update_masquerade(course=course, username=self.user.username)
post_dict = {'course_id': str(course.id)}
self.client.post(reverse(RESET_COURSE_DEADLINES_NAME), post_dict)
- updated_schedule = Schedule.objects.get(id=student_schedule.id)
+ updated_schedule = Schedule.objects.get(enrollment__user=self.user, enrollment__course_id=course.id)
assert updated_schedule.start_date.date() == datetime.datetime.today().date()
- updated_staff_schedule = Schedule.objects.get(id=staff_schedule.id)
- assert updated_staff_schedule.start_date == staff_schedule.start_date
+ updated_staff_schedule = Schedule.objects.get(enrollment__user=staff, enrollment__course_id=course.id)
+ assert updated_staff_schedule.start_date == start_date
@override_experiment_waffle_flag(RELATIVE_DATES_FLAG, active=True)
def test_reset_course_deadlines_masquerade_generic_student(self):
course = self.courses[0]
- student_schedule = CourseEnrollment.objects.get(course_id=course.id, user=self.user).schedule
- student_schedule.start_date = timezone.now() - datetime.timedelta(days=30)
- student_schedule.save()
-
staff = StaffFactory(course_key=course.id)
- staff_schedule = ScheduleFactory(
- start_date=timezone.now() - datetime.timedelta(days=30),
- enrollment__course__id=course.id,
- enrollment__user=staff,
- )
+ CourseEnrollment.enroll(staff, course.id)
+
+ start_date = timezone.now() - datetime.timedelta(days=30)
+ Schedule.objects.update(start_date=start_date)
self.client.login(username=staff.username, password=TEST_PASSWORD)
self.update_masquerade(course=course)
post_dict = {'course_id': str(course.id)}
self.client.post(reverse(RESET_COURSE_DEADLINES_NAME), post_dict)
- updated_student_schedule = Schedule.objects.get(id=student_schedule.id)
- assert updated_student_schedule.start_date == student_schedule.start_date
- updated_staff_schedule = Schedule.objects.get(id=staff_schedule.id)
+ updated_student_schedule = Schedule.objects.get(enrollment__user=self.user, enrollment__course_id=course.id)
+ assert updated_student_schedule.start_date == start_date
+ updated_staff_schedule = Schedule.objects.get(enrollment__user=staff, enrollment__course_id=course.id)
assert updated_staff_schedule.start_date.date() == datetime.date.today()