Override due dates in the LMS for self-paced courses.
This commit is contained in:
@@ -9,6 +9,7 @@ from nose.plugins.attrib import attr
|
||||
|
||||
from courseware.field_overrides import OverrideFieldData # pylint: disable=import-error
|
||||
from django.test.utils import override_settings
|
||||
from lms.djangoapps.courseware.tests.test_field_overrides import inject_field_overrides
|
||||
from request_cache.middleware import RequestCache
|
||||
from student.tests.factories import AdminFactory # pylint: disable=import-error
|
||||
from xmodule.modulestore.tests.django_utils import (
|
||||
@@ -69,13 +70,7 @@ class TestFieldOverrides(ModuleStoreTestCase):
|
||||
|
||||
self.addCleanup(RequestCache.clear_request_cache)
|
||||
|
||||
# Apparently the test harness doesn't use LmsFieldStorage, and I'm not
|
||||
# sure if there's a way to poke the test harness to do so. So, we'll
|
||||
# just inject the override field storage in this brute force manner.
|
||||
OverrideFieldData.provider_classes = None
|
||||
for block in iter_blocks(ccx.course):
|
||||
block._field_data = OverrideFieldData.wrap( # pylint: disable=protected-access
|
||||
AdminFactory.create(), course, block._field_data) # pylint: disable=protected-access
|
||||
inject_field_overrides(iter_blocks(ccx.course), course, AdminFactory.create())
|
||||
|
||||
def cleanup_provider_classes():
|
||||
"""
|
||||
|
||||
@@ -24,7 +24,7 @@ from xblock.field_data import FieldData
|
||||
from xmodule.modulestore.inheritance import InheritanceMixin
|
||||
|
||||
NOTSET = object()
|
||||
ENABLED_OVERRIDE_PROVIDERS_KEY = "courseware.field_overrides.enabled_providers"
|
||||
ENABLED_OVERRIDE_PROVIDERS_KEY = "courseware.field_overrides.enabled_providers.{course_id}"
|
||||
|
||||
|
||||
def resolve_dotted(name):
|
||||
@@ -77,7 +77,6 @@ class OverrideFieldData(FieldData):
|
||||
settings.FIELD_OVERRIDE_PROVIDERS))
|
||||
|
||||
enabled_providers = cls._providers_for_course(course)
|
||||
|
||||
if enabled_providers:
|
||||
# TODO: we might not actually want to return here. Might be better
|
||||
# to check for instance.providers after the instance is built. This
|
||||
@@ -98,14 +97,16 @@ class OverrideFieldData(FieldData):
|
||||
course: The course XBlock
|
||||
"""
|
||||
request_cache = RequestCache.get_request_cache()
|
||||
enabled_providers = request_cache.data.get(
|
||||
ENABLED_OVERRIDE_PROVIDERS_KEY, NOTSET
|
||||
)
|
||||
if course is None:
|
||||
cache_key = ENABLED_OVERRIDE_PROVIDERS_KEY.format(course_id='None')
|
||||
else:
|
||||
cache_key = ENABLED_OVERRIDE_PROVIDERS_KEY.format(course_id=unicode(course.id))
|
||||
enabled_providers = request_cache.data.get(cache_key, NOTSET)
|
||||
if enabled_providers == NOTSET:
|
||||
enabled_providers = tuple(
|
||||
(provider_class for provider_class in cls.provider_classes if provider_class.enabled_for(course))
|
||||
)
|
||||
request_cache.data[ENABLED_OVERRIDE_PROVIDERS_KEY] = enabled_providers
|
||||
request_cache.data[cache_key] = enabled_providers
|
||||
|
||||
return enabled_providers
|
||||
|
||||
|
||||
23
lms/djangoapps/courseware/self_paced_overrides.py
Normal file
23
lms/djangoapps/courseware/self_paced_overrides.py
Normal file
@@ -0,0 +1,23 @@
|
||||
"""
|
||||
Field overrides for self-paced courses. This allows overriding due
|
||||
dates for each block in the course.
|
||||
"""
|
||||
|
||||
from .field_overrides import FieldOverrideProvider
|
||||
|
||||
|
||||
class SelfPacedDateOverrideProvider(FieldOverrideProvider):
|
||||
"""
|
||||
A concrete implementation of
|
||||
:class:`~courseware.field_overrides.FieldOverrideProvider` which allows for
|
||||
due dates to be overridden for self-paced courses.
|
||||
"""
|
||||
def get(self, block, name, default):
|
||||
if name == 'due':
|
||||
return None
|
||||
return default
|
||||
|
||||
@classmethod
|
||||
def enabled_for(cls, course):
|
||||
"""This provider is enabled for self-paced courses only."""
|
||||
return course.self_paced
|
||||
@@ -132,3 +132,16 @@ class TestOverrideProvider(FieldOverrideProvider):
|
||||
@classmethod
|
||||
def enabled_for(cls, course):
|
||||
return True
|
||||
|
||||
|
||||
def inject_field_overrides(blocks, course, user):
|
||||
"""
|
||||
Apparently the test harness doesn't use LmsFieldStorage, and I'm
|
||||
not sure if there's a way to poke the test harness to do so. So,
|
||||
we'll just inject the override field storage in this brute force
|
||||
manner.
|
||||
"""
|
||||
OverrideFieldData.provider_classes = None
|
||||
for block in blocks:
|
||||
block._field_data = OverrideFieldData.wrap( # pylint: disable=protected-access
|
||||
user, course, block._field_data) # pylint: disable=protected-access
|
||||
|
||||
49
lms/djangoapps/courseware/tests/test_self_paced_overrides.py
Normal file
49
lms/djangoapps/courseware/tests/test_self_paced_overrides.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""
|
||||
Tests for self-paced course due date overrides.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from dateutil.tz import tzutc
|
||||
from django.test.utils import override_settings
|
||||
|
||||
from student.tests.factories import UserFactory
|
||||
from lms.djangoapps.ccx.tests.test_overrides import inject_field_overrides
|
||||
from lms.djangoapps.courseware.field_overrides import OverrideFieldData
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
|
||||
|
||||
@override_settings(
|
||||
FIELD_OVERRIDE_PROVIDERS=('courseware.self_paced_overrides.SelfPacedDateOverrideProvider',)
|
||||
)
|
||||
class SelfPacedDateOverrideTest(ModuleStoreTestCase):
|
||||
"""
|
||||
Tests for self-paced due date overrides.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(SelfPacedDateOverrideTest, self).setUp()
|
||||
self.due_date = datetime(2015, 5, 26, 8, 30, 00).replace(tzinfo=tzutc())
|
||||
self.instructor_led_course, self.il_section = self.setup_course("Instructor Led Course", False)
|
||||
self.self_paced_course, self.sp_section = self.setup_course("Self-Paced Course", True)
|
||||
|
||||
def tearDown(self):
|
||||
super(SelfPacedDateOverrideTest, self).tearDown()
|
||||
OverrideFieldData.provider_classes = None
|
||||
|
||||
def setup_course(self, display_name, self_paced):
|
||||
"""Set up a course with `display_name` and `self_paced` attributes.
|
||||
|
||||
Creates a child block with a due date, and ensures that field
|
||||
overrides are correctly applied for both blocks.
|
||||
"""
|
||||
course = CourseFactory.create(display_name=display_name, self_paced=self_paced)
|
||||
section = ItemFactory.create(parent=course, due=self.due_date)
|
||||
inject_field_overrides((course, section), course, UserFactory.create())
|
||||
return (course, section)
|
||||
|
||||
def test_instructor_led(self):
|
||||
self.assertEqual(self.due_date, self.il_section.due)
|
||||
|
||||
def test_self_paced(self):
|
||||
self.assertIsNone(self.sp_section.due)
|
||||
@@ -12,6 +12,7 @@ from django.test.utils import override_settings
|
||||
from nose.plugins.attrib import attr
|
||||
|
||||
from courseware.field_overrides import OverrideFieldData # pylint: disable=import-error
|
||||
from lms.djangoapps.ccx.tests.test_overrides import inject_field_overrides
|
||||
from student.tests.factories import UserFactory # pylint: disable=import-error
|
||||
from xmodule.fields import Date
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
|
||||
@@ -196,7 +197,6 @@ class TestSetDueDateExtension(ModuleStoreTestCase):
|
||||
Fixtures.
|
||||
"""
|
||||
super(TestSetDueDateExtension, self).setUp()
|
||||
OverrideFieldData.provider_classes = None
|
||||
|
||||
self.due = due = datetime.datetime(2010, 5, 12, 2, 42, tzinfo=utc)
|
||||
course = CourseFactory.create()
|
||||
@@ -216,12 +216,7 @@ class TestSetDueDateExtension(ModuleStoreTestCase):
|
||||
self.week3 = week3
|
||||
self.user = user
|
||||
|
||||
# Apparently the test harness doesn't use LmsFieldStorage, and I'm not
|
||||
# sure if there's a way to poke the test harness to do so. So, we'll
|
||||
# just inject the override field storage in this brute force manner.
|
||||
for block in (course, week1, week2, week3, homework, assignment):
|
||||
block._field_data = OverrideFieldData.wrap( # pylint: disable=protected-access
|
||||
user, course, block._field_data) # pylint: disable=protected-access
|
||||
inject_field_overrides((course, week1, week2, week3, homework, assignment), course, user)
|
||||
|
||||
def tearDown(self):
|
||||
super(TestSetDueDateExtension, self).tearDown()
|
||||
|
||||
@@ -676,6 +676,12 @@ if FEATURES.get('INDIVIDUAL_DUE_DATES'):
|
||||
'courseware.student_field_overrides.IndividualStudentOverrideProvider',
|
||||
)
|
||||
|
||||
##### Self-Paced Course Due Dates #####
|
||||
if FEATURES.get('ENABLE_SELF_PACED_COURSES'):
|
||||
FIELD_OVERRIDE_PROVIDERS += (
|
||||
'courseware.self_paced_overrides.SelfPacedDateOverrideProvider',
|
||||
)
|
||||
|
||||
# PROFILE IMAGE CONFIG
|
||||
PROFILE_IMAGE_BACKEND = ENV_TOKENS.get('PROFILE_IMAGE_BACKEND', PROFILE_IMAGE_BACKEND)
|
||||
PROFILE_IMAGE_SECRET_KEY = AUTH_TOKENS.get('PROFILE_IMAGE_SECRET_KEY', PROFILE_IMAGE_SECRET_KEY)
|
||||
|
||||
Reference in New Issue
Block a user