feat: add Waffle Flag to disable resetting self-paced deadlines by learners
This commit is contained in:
committed by
Piotr Surowiec
parent
e02555816f
commit
b91f858e70
@@ -50,6 +50,16 @@ COURSE_ENABLE_UNENROLLED_ACCESS_FLAG = CourseWaffleFlag( # lint-amnesty, pylint
|
||||
# .. toggle_tickets: https://openedx.atlassian.net/browse/AA-27
|
||||
RELATIVE_DATES_FLAG = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.relative_dates', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
|
||||
|
||||
# .. toggle_name: course_experience.relative_dates_disable_reset
|
||||
# .. toggle_implementation: CourseWaffleFlag
|
||||
# .. toggle_default: False
|
||||
# .. toggle_description: Waffle flag to disable resetting deadlines by learners in self-paced courses. The 'Dates' tab
|
||||
# will no longer show a banner about missed deadlines. The deadlines banner will also be hidden on unit pages.
|
||||
# .. toggle_use_cases: open_edx
|
||||
# .. toggle_creation_date: 2023-04-27
|
||||
# .. toggle_warning: For this toggle to have an effect, the RELATIVE_DATES_FLAG toggle must be enabled, too.
|
||||
RELATIVE_DATES_DISABLE_RESET_FLAG = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.relative_dates_disable_reset', __name__)
|
||||
|
||||
# .. toggle_name: course_experience.calendar_sync
|
||||
# .. toggle_implementation: CourseWaffleFlag
|
||||
# .. toggle_default: False
|
||||
|
||||
@@ -2,18 +2,20 @@
|
||||
Tests for reset deadlines endpoint.
|
||||
"""
|
||||
import datetime
|
||||
import ddt
|
||||
|
||||
import ddt
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from edx_toggles.toggles.testutils import override_waffle_flag
|
||||
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from common.djangoapps.util.testing import EventTestMixin
|
||||
from lms.djangoapps.courseware.tests.helpers import MasqueradeMixin
|
||||
from lms.djangoapps.course_home_api.tests.utils import BaseCourseHomeTests
|
||||
from lms.djangoapps.courseware.tests.helpers import MasqueradeMixin
|
||||
from openedx.core.djangoapps.schedules.models import Schedule
|
||||
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order
|
||||
from openedx.features.course_experience import RELATIVE_DATES_DISABLE_RESET_FLAG, RELATIVE_DATES_FLAG
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -25,17 +27,22 @@ class ResetCourseDeadlinesViewTests(EventTestMixin, BaseCourseHomeTests, Masquer
|
||||
# Need to supply tracker name for the EventTestMixin. Also, EventTestMixin needs to come
|
||||
# first in class inheritance so the setUp call here appropriately works
|
||||
super().setUp('openedx.features.course_experience.api.v1.views.tracker')
|
||||
self.course = CourseFactory.create(self_paced=True, start=timezone.now() - datetime.timedelta(days=1000))
|
||||
|
||||
def test_reset_deadlines(self):
|
||||
CourseEnrollment.enroll(self.user, self.course.id, CourseMode.VERIFIED)
|
||||
enrollment = CourseEnrollment.enroll(self.user, self.course.id, CourseMode.VERIFIED)
|
||||
enrollment.schedule.start_date = timezone.now() - datetime.timedelta(days=100)
|
||||
enrollment.schedule.save()
|
||||
# Test body with incorrect body param (course_key is required)
|
||||
response = self.client.post(reverse('course-experience-reset-course-deadlines'), {'course': self.course.id})
|
||||
assert response.status_code == 400
|
||||
assert enrollment.schedule == Schedule.objects.get(id=enrollment.schedule.id)
|
||||
self.assert_no_events_were_emitted()
|
||||
|
||||
# Test correct post body
|
||||
response = self.client.post(reverse('course-experience-reset-course-deadlines'), {'course_key': self.course.id})
|
||||
assert response.status_code == 200
|
||||
assert enrollment.schedule.start_date < Schedule.objects.get(id=enrollment.schedule.id).start_date
|
||||
self.assert_event_emitted(
|
||||
'edx.ui.lms.reset_deadlines.clicked',
|
||||
courserun_key=str(self.course.id),
|
||||
@@ -45,33 +52,44 @@ class ResetCourseDeadlinesViewTests(EventTestMixin, BaseCourseHomeTests, Masquer
|
||||
user_id=self.user.id,
|
||||
)
|
||||
|
||||
@override_waffle_flag(RELATIVE_DATES_FLAG, active=True)
|
||||
@override_waffle_flag(RELATIVE_DATES_DISABLE_RESET_FLAG, active=True)
|
||||
def test_reset_deadlines_disabled(self):
|
||||
enrollment = CourseEnrollment.enroll(self.user, self.course.id, CourseMode.VERIFIED)
|
||||
enrollment.schedule.start_date = timezone.now() - datetime.timedelta(days=100)
|
||||
enrollment.schedule.save()
|
||||
|
||||
response = self.client.post(reverse('course-experience-reset-course-deadlines'), {'course_key': self.course.id})
|
||||
assert response.status_code == 200
|
||||
assert enrollment.schedule == Schedule.objects.get(id=enrollment.schedule.id)
|
||||
self.assert_no_events_were_emitted()
|
||||
|
||||
def test_reset_deadlines_with_masquerade(self):
|
||||
""" Staff users should be able to masquerade as a learner and reset the learner's schedule """
|
||||
course = CourseFactory.create(self_paced=True, start=timezone.now() - datetime.timedelta(days=1))
|
||||
student_username = self.user.username
|
||||
student_user_id = self.user.id
|
||||
student_enrollment = CourseEnrollment.enroll(self.user, course.id)
|
||||
student_enrollment = CourseEnrollment.enroll(self.user, self.course.id)
|
||||
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 = CourseEnrollment.enroll(self.staff_user, self.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)
|
||||
self.update_masquerade(course=self.course, username=student_username)
|
||||
|
||||
self.client.post(reverse('course-experience-reset-course-deadlines'), {'course_key': course.id})
|
||||
self.client.post(reverse('course-experience-reset-course-deadlines'), {'course_key': self.course.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_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),
|
||||
courserun_key=str(self.course.id),
|
||||
is_masquerading=True,
|
||||
is_staff=False,
|
||||
org_key=course.org,
|
||||
org_key=self.course.org,
|
||||
user_id=student_user_id,
|
||||
)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from lms.djangoapps.course_api.blocks.api import get_blocks
|
||||
from lms.djangoapps.course_blocks.api import get_course_blocks
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.lib.cache_utils import request_cached
|
||||
from openedx.features.course_experience import RELATIVE_DATES_FLAG
|
||||
from openedx.features.course_experience import RELATIVE_DATES_DISABLE_RESET_FLAG, RELATIVE_DATES_FLAG
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
|
||||
|
||||
@@ -155,6 +155,14 @@ def dates_banner_should_display(course_key, user):
|
||||
if not RELATIVE_DATES_FLAG.is_enabled(course_key):
|
||||
return False, False
|
||||
|
||||
if RELATIVE_DATES_DISABLE_RESET_FLAG.is_enabled(course_key):
|
||||
# The `missed_deadlines` value is ignored by `reset_course_deadlines` views. Instead, they check the value of
|
||||
# `missed_gated_content` to determine if learners can reset the deadlines by themselves.
|
||||
# We could have added this logic directly to `reset_self_paced_schedule`, but this function is used in other
|
||||
# places (e.g., when an enrollment mode is changed). We want this flag to affect only the use case when
|
||||
# learners try to reset their deadlines.
|
||||
return False, True
|
||||
|
||||
course_overview = CourseOverview.objects.get(id=str(course_key))
|
||||
|
||||
# Only display the banner for self-paced courses
|
||||
|
||||
Reference in New Issue
Block a user