From 700504c52fc79716a8b8125c84c10c309e2b0505 Mon Sep 17 00:00:00 2001 From: Chris Rossi Date: Tue, 13 May 2014 14:46:00 -0400 Subject: [PATCH] Fix a bug in Individual Due Date Extensions (IDDE). The IDDE implementation relied on a StudentModule being created for a particular block in order to set the extended due date on that block. Since StudentModules seem to be created on demand whenever data is written to an attribute with Scope.user_state, it meant that if a homework problem hadn't yet been touched by a student it was possible that the due date extension wouldn't take effect for that problem, even if the due date extension was successfully set for the parent unit. This patch fixes this problem by creating new StudentModules as necessary in order to make sure extended due dates propogate properly to all problems in a unit. --- lms/djangoapps/instructor/tests/test_tools.py | 11 ++++++-- lms/djangoapps/instructor/views/tools.py | 27 +++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_tools.py b/lms/djangoapps/instructor/tests/test_tools.py index b7c539eb0b..722b57a81c 100644 --- a/lms/djangoapps/instructor/tests/test_tools.py +++ b/lms/djangoapps/instructor/tests/test_tools.py @@ -199,11 +199,18 @@ class TestSetDueDateExtension(ModuleStoreTestCase): def test_set_due_date_extension(self): extended = datetime.datetime(2013, 12, 25, 0, 0, tzinfo=utc) - tools.set_due_date_extension(self.course, self.week1, self.user, - extended) + tools.set_due_date_extension(self.course, self.week1, self.user, extended) self.assertEqual(self.extended_due(self.week1), extended) self.assertEqual(self.extended_due(self.homework), extended) + def test_set_due_date_extension_create_studentmodule(self): + extended = datetime.datetime(2013, 12, 25, 0, 0, tzinfo=utc) + user = UserFactory.create() # No student modules for this user + tools.set_due_date_extension(self.course, self.week1, user, extended) + extended_due = functools.partial(get_extended_due, self.course, student=user) + self.assertEqual(extended_due(self.week1), extended) + self.assertEqual(extended_due(self.homework), extended) + def test_reset_due_date_extension(self): tools.set_due_date_extension(self.course, self.week1, self.user, None) self.assertEqual(self.extended_due(self.week1), None) diff --git a/lms/djangoapps/instructor/views/tools.py b/lms/djangoapps/instructor/views/tools.py index 4b6a621b97..d195db7a4c 100644 --- a/lms/djangoapps/instructor/views/tools.py +++ b/lms/djangoapps/instructor/views/tools.py @@ -173,13 +173,30 @@ def set_due_date_extension(course, unit, student, due_date): course_id=course.id, module_state_key=node.location ) - state = json.loads(student_module.state) - state['extended_due'] = DATE_FIELD.to_json(due_date) - student_module.state = json.dumps(state) - student_module.save() + except StudentModule.DoesNotExist: - pass + # Normally, a StudentModule is created as a side effect of assigning + # a value to a property in an XModule or XBlock which has a scope + # of 'Scope.user_state'. Here, we want to alter user state but + # can't use the standard XModule/XBlock machinery to do so, because + # it fails to take into account that the state being altered might + # belong to a student other than the one currently logged in. As a + # result, in our work around, we need to detect whether the + # StudentModule has been created for the given student on the given + # unit and create it if it is missing, so we can use it to store + # the extended due date. + student_module = StudentModule.objects.create( + student_id=student.id, + course_id=course.id, + module_state_key=node.location, + module_type=node.category + ) + state = {} + + state['extended_due'] = DATE_FIELD.to_json(due_date) + student_module.state = json.dumps(state) + student_module.save() for child in node.get_children(): set_due_date(child)