Commit 4805946a by Peter Fogg

Override due dates in the LMS for self-paced courses.

parent 7f673604
...@@ -9,6 +9,7 @@ from nose.plugins.attrib import attr ...@@ -9,6 +9,7 @@ from nose.plugins.attrib import attr
from courseware.field_overrides import OverrideFieldData # pylint: disable=import-error from courseware.field_overrides import OverrideFieldData # pylint: disable=import-error
from django.test.utils import override_settings 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 request_cache.middleware import RequestCache
from student.tests.factories import AdminFactory # pylint: disable=import-error from student.tests.factories import AdminFactory # pylint: disable=import-error
from xmodule.modulestore.tests.django_utils import ( from xmodule.modulestore.tests.django_utils import (
...@@ -69,13 +70,7 @@ class TestFieldOverrides(ModuleStoreTestCase): ...@@ -69,13 +70,7 @@ class TestFieldOverrides(ModuleStoreTestCase):
self.addCleanup(RequestCache.clear_request_cache) self.addCleanup(RequestCache.clear_request_cache)
# Apparently the test harness doesn't use LmsFieldStorage, and I'm not inject_field_overrides(iter_blocks(ccx.course), course, AdminFactory.create())
# 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
def cleanup_provider_classes(): def cleanup_provider_classes():
""" """
......
...@@ -24,7 +24,7 @@ from xblock.field_data import FieldData ...@@ -24,7 +24,7 @@ from xblock.field_data import FieldData
from xmodule.modulestore.inheritance import InheritanceMixin from xmodule.modulestore.inheritance import InheritanceMixin
NOTSET = object() 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): def resolve_dotted(name):
...@@ -77,7 +77,6 @@ class OverrideFieldData(FieldData): ...@@ -77,7 +77,6 @@ class OverrideFieldData(FieldData):
settings.FIELD_OVERRIDE_PROVIDERS)) settings.FIELD_OVERRIDE_PROVIDERS))
enabled_providers = cls._providers_for_course(course) enabled_providers = cls._providers_for_course(course)
if enabled_providers: if enabled_providers:
# TODO: we might not actually want to return here. Might be better # TODO: we might not actually want to return here. Might be better
# to check for instance.providers after the instance is built. This # to check for instance.providers after the instance is built. This
...@@ -98,14 +97,16 @@ class OverrideFieldData(FieldData): ...@@ -98,14 +97,16 @@ class OverrideFieldData(FieldData):
course: The course XBlock course: The course XBlock
""" """
request_cache = RequestCache.get_request_cache() request_cache = RequestCache.get_request_cache()
enabled_providers = request_cache.data.get( if course is None:
ENABLED_OVERRIDE_PROVIDERS_KEY, NOTSET 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: if enabled_providers == NOTSET:
enabled_providers = tuple( enabled_providers = tuple(
(provider_class for provider_class in cls.provider_classes if provider_class.enabled_for(course)) (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 return enabled_providers
......
"""
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): ...@@ -132,3 +132,16 @@ class TestOverrideProvider(FieldOverrideProvider):
@classmethod @classmethod
def enabled_for(cls, course): def enabled_for(cls, course):
return True 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
"""
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 ...@@ -12,6 +12,7 @@ from django.test.utils import override_settings
from nose.plugins.attrib import attr from nose.plugins.attrib import attr
from courseware.field_overrides import OverrideFieldData # pylint: disable=import-error 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 student.tests.factories import UserFactory # pylint: disable=import-error
from xmodule.fields import Date from xmodule.fields import Date
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
...@@ -196,7 +197,6 @@ class TestSetDueDateExtension(ModuleStoreTestCase): ...@@ -196,7 +197,6 @@ class TestSetDueDateExtension(ModuleStoreTestCase):
Fixtures. Fixtures.
""" """
super(TestSetDueDateExtension, self).setUp() super(TestSetDueDateExtension, self).setUp()
OverrideFieldData.provider_classes = None
self.due = due = datetime.datetime(2010, 5, 12, 2, 42, tzinfo=utc) self.due = due = datetime.datetime(2010, 5, 12, 2, 42, tzinfo=utc)
course = CourseFactory.create() course = CourseFactory.create()
...@@ -216,12 +216,7 @@ class TestSetDueDateExtension(ModuleStoreTestCase): ...@@ -216,12 +216,7 @@ class TestSetDueDateExtension(ModuleStoreTestCase):
self.week3 = week3 self.week3 = week3
self.user = user self.user = user
# Apparently the test harness doesn't use LmsFieldStorage, and I'm not inject_field_overrides((course, week1, week2, week3, homework, assignment), course, user)
# 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
def tearDown(self): def tearDown(self):
super(TestSetDueDateExtension, self).tearDown() super(TestSetDueDateExtension, self).tearDown()
......
...@@ -676,6 +676,12 @@ if FEATURES.get('INDIVIDUAL_DUE_DATES'): ...@@ -676,6 +676,12 @@ if FEATURES.get('INDIVIDUAL_DUE_DATES'):
'courseware.student_field_overrides.IndividualStudentOverrideProvider', '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 CONFIG
PROFILE_IMAGE_BACKEND = ENV_TOKENS.get('PROFILE_IMAGE_BACKEND', PROFILE_IMAGE_BACKEND) 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) PROFILE_IMAGE_SECRET_KEY = AUTH_TOKENS.get('PROFILE_IMAGE_SECRET_KEY', PROFILE_IMAGE_SECRET_KEY)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment