diff --git a/openedx/core/djangoapps/schedules/tests/test_signals.py b/openedx/core/djangoapps/schedules/tests/test_signals.py index d02bea94f0..e397a51413 100644 --- a/openedx/core/djangoapps/schedules/tests/test_signals.py +++ b/openedx/core/djangoapps/schedules/tests/test_signals.py @@ -242,29 +242,33 @@ class ResetScheduleTests(SharedModuleStoreTestCase): self.enrollment = CourseEnrollmentFactory( course_id=self.course.id, mode=CourseMode.AUDIT, + is_active=False, ) self.schedule = self.enrollment.schedule + self.user = self.enrollment.user def test_schedule_is_reset_after_enrollment_change(self): """ Test that an update in enrollment causes a schedule reset. """ original_start = self.schedule.start_date - CourseEnrollment.enroll(self.enrollment.user, self.course.id, mode=CourseMode.VERIFIED) + CourseEnrollment.enroll(self.user, self.course.id, mode=CourseMode.VERIFIED) self.schedule.refresh_from_db() self.assertGreater(self.schedule.start_date, original_start) # should have been reset to current time - def test_schedule_is_reset_to_availabilty_date(self): - """ Test that a switch to audit enrollment resets to the availabilty date, not current time. """ + def test_schedule_is_reset_to_availability_date(self): + """ Test that a switch to audit enrollment resets to the availability date, not current time. """ original_start = self.schedule.start_date # Switch to verified, confirm we change start date - CourseEnrollment.enroll(self.enrollment.user, self.course.id, mode=CourseMode.VERIFIED) + CourseEnrollment.enroll(self.user, self.course.id, mode=CourseMode.VERIFIED) self.schedule.refresh_from_db() self.assertNotEqual(self.schedule.start_date, original_start) + CourseEnrollment.unenroll(self.user, self.course.id) + # Switch back to audit, confirm we change back to original availability date - CourseEnrollment.enroll(self.enrollment.user, self.course.id, mode=CourseMode.AUDIT) + CourseEnrollment.enroll(self.user, self.course.id, mode=CourseMode.AUDIT) self.schedule.refresh_from_db() self.assertEqual(self.schedule.start_date, original_start) diff --git a/openedx/core/djangoapps/schedules/utils.py b/openedx/core/djangoapps/schedules/utils.py index 760448a4fa..3fd5f901ec 100644 --- a/openedx/core/djangoapps/schedules/utils.py +++ b/openedx/core/djangoapps/schedules/utils.py @@ -8,6 +8,7 @@ from django.db.models import F, Subquery from django.db.models.functions import Greatest from openedx.core.djangoapps.schedules.models import Schedule +from student.models import CourseEnrollment LOG = logging.getLogger(__name__) @@ -54,7 +55,18 @@ def reset_self_paced_schedule(user, course_key, use_availability_date=False): ) if use_availability_date: - schedule = schedule.annotate(start_of_access=Greatest(F('enrollment__created'), F('enrollment__course__start'))) - schedule.update(start_date=Subquery(schedule.values('start_of_access')[:1])) + # Query enrollments to find availability date -- very similar to query above, but we can't reuse that query + # object because mysql doesn't like a subquery of an update to reference the same table being updated. + # Be careful attempting to remove this logic because you can't reproduce a problem locally -- in my own testing, + # I could not reproduce in devstack, but it was happening on prod databases. So implementations vary. + # See https://dev.mysql.com/doc/refman/8.0/en/subquery-restrictions.html + enrollments = CourseEnrollment.objects.filter( + user=user, + course__id=course_key, + course__self_paced=True, + ).annotate( + availability=Greatest(F('created'), F('course__start')), + ) + schedule.update(start_date=Subquery(enrollments.values('availability')[:1])) else: schedule.update(start_date=datetime.datetime.now(pytz.utc))