diff --git a/lms/djangoapps/courseware/management/commands/regrade_partial.py b/lms/djangoapps/courseware/management/commands/regrade_partial.py index 7bfcb16913..b997cff674 100644 --- a/lms/djangoapps/courseware/management/commands/regrade_partial.py +++ b/lms/djangoapps/courseware/management/commands/regrade_partial.py @@ -16,8 +16,7 @@ from capa.correctmap import CorrectMap # To narrow down the set of problems that might need fixing, the StudentModule # objects to be checked is filtered down to those: # -# grade=0.0 (the grade must have been zeroed out) -# created < '2013-03-08 05:19:00' (the problem must have been answered before the fix was installed) +# created < '2013-03-08 15:45:00' (the problem must have been answered before the fix was installed, on Prod and Edge) # modified > '2013-03-07 20:18:00' (the problem must have been visited after the bug was introduced) # state like '%"npoints": 0.%' (the problem must have some form of partial credit). # @@ -39,15 +38,16 @@ class Command(BaseCommand): def fix_studentmodules(self, save_changes): modules = StudentModule.objects.filter(# module_type='problem', modified__gt='2013-03-07 20:18:00', - created__lt='2013-03-08 05:19:00', - state__contains='"npoints": 0.', - grade=0.0) + created__lt='2013-03-08 15:45:00', + state__contains='"npoints": 0.') + for module in modules: - self.fix_studentmodule(module, save_changes) + self.fix_studentmodule_grade(module, save_changes) - def fix_studentmodule(self, module, save_changes): + def fix_studentmodule_grade(self, module, save_changes): module_state = module.state if module_state is None: + # not likely, since we filter on it. But in general... log.info("No state found for {type} module {id} for student {student} in course {course_id}".format( **{'type':module.module_type, 'id':module.module_state_key, 'student':module.student.username, 'course_id':module.course_id})) return @@ -55,32 +55,50 @@ class Command(BaseCommand): state_dict = json.loads(module_state) self.num_visited += 1 + # LoncapaProblem.get_score() checks student_answers -- if there are none, we will return a grade of 0 + # Check that this is the case, but do so sooner, before we do any of the other grading work. + student_answers = state_dict['student_answers'] + if (not student_answers) or len(student_answers) == 0: + # we should not have a grade here: + if module.grade != 0: + log.error("No answer found but grade {grade} exists for {type} module {id} for student {student} in course {course_id}".format(grade=module.grade, + type=module.module_type, id=module.module_state_key, student=module.student.username, course_id=module.course_id)) + else: + log.debug("No answer and no grade found for {type} module {id} for student {student} in course {course_id}".format(grade=module.grade, + type=module.module_type, id=module.module_state_key, student=module.student.username, course_id=module.course_id)) + return + + # load into a CorrectMap, as done in LoncapaProblem.__init__(): correct_map = CorrectMap() if 'correct_map' in state_dict: correct_map.set_dict(state_dict['correct_map']) + # calculate score the way LoncapaProblem.get_score() works, by deferring to CorrectMap's get_npoints implementation. correct = 0 for key in correct_map: correct += correct_map.get_npoints(key) if module.grade == correct: - log.info("Grade matches for {type} module {id} for student {student} in course {course_id}".format( - **{'type':module.module_type, 'id':module.module_state_key, 'student':module.student.username, 'course_id':module.course_id})) + # nothing to change + log.debug("Grade matches for {type} module {id} for student {student} in course {course_id}".format( + type=module.module_type, id=module.module_state_key, student=module.student.username, course_id=module.course_id)) elif save_changes: + # make the change log.info("Grade changing from {0} to {1} for {type} module {id} for student {student} in course {course_id}".format( module.grade, correct, - **{'type':module.module_type, 'id':module.module_state_key, 'student':module.student.username, 'course_id':module.course_id})) + type=module.module_type, id=module.module_state_key, student=module.student.username, course_id=module.course_id)) module.grade = correct module.save() self.num_changed += 1 else: + # don't make the change, but log that the change would be made log.info("Grade would change from {0} to {1} for {type} module {id} for student {student} in course {course_id}".format( module.grade, correct, - **{'type':module.module_type, 'id':module.module_state_key, 'student':module.student.username, 'course_id':module.course_id})) + type=module.module_type, id=module.module_state_key, student=module.student.username, course_id=module.course_id)) self.num_changed += 1 def handle(self, **options): - save_changes = 'save_changes' in options and options['save_changes'] + save_changes = options['save_changes'] log.info("Starting run: save_changes = {0}".format(save_changes))