Commit a80d4c1c by Tyler Hallada

Check waffle flag before grade overriding

parent ff6329fe
...@@ -894,53 +894,55 @@ def update_attempt_status(exam_id, user_id, to_status, ...@@ -894,53 +894,55 @@ def update_attempt_status(exam_id, user_id, to_status,
if ProctoredExamStudentAttemptStatus.needs_grade_override(to_status): if ProctoredExamStudentAttemptStatus.needs_grade_override(to_status):
grades_service = get_runtime_service('grades') grades_service = get_runtime_service('grades')
log_msg = ( if grades_service.should_override_grade_on_rejected_exam(exam['course_id']):
'Overriding exam subsection grade for ' log_msg = (
'user_id {user_id} on {course_id} for ' 'Overriding exam subsection grade for '
'content_id {content_id}. Override ' 'user_id {user_id} on {course_id} for '
'earned_all: {earned_all}, ' 'content_id {content_id}. Override '
'earned_graded: {earned_graded}.'.format( 'earned_all: {earned_all}, '
'earned_graded: {earned_graded}.'.format(
user_id=exam_attempt_obj.user_id,
course_id=exam['course_id'],
content_id=exam_attempt_obj.proctored_exam.content_id,
earned_all=REJECTED_GRADE_OVERRIDE_EARNED,
earned_graded=REJECTED_GRADE_OVERRIDE_EARNED
)
)
log.info(log_msg)
grades_service.override_subsection_grade(
user_id=exam_attempt_obj.user_id, user_id=exam_attempt_obj.user_id,
course_id=exam['course_id'], course_key_or_id=exam['course_id'],
content_id=exam_attempt_obj.proctored_exam.content_id, usage_key_or_id=exam_attempt_obj.proctored_exam.content_id,
earned_all=REJECTED_GRADE_OVERRIDE_EARNED, earned_all=REJECTED_GRADE_OVERRIDE_EARNED,
earned_graded=REJECTED_GRADE_OVERRIDE_EARNED earned_graded=REJECTED_GRADE_OVERRIDE_EARNED
) )
)
log.info(log_msg)
grades_service.override_subsection_grade(
user_id=exam_attempt_obj.user_id,
course_key_or_id=exam['course_id'],
usage_key_or_id=exam_attempt_obj.proctored_exam.content_id,
earned_all=REJECTED_GRADE_OVERRIDE_EARNED,
earned_graded=REJECTED_GRADE_OVERRIDE_EARNED
)
if (to_status == ProctoredExamStudentAttemptStatus.verified and if (to_status == ProctoredExamStudentAttemptStatus.verified and
ProctoredExamStudentAttemptStatus.needs_grade_override(from_status)): ProctoredExamStudentAttemptStatus.needs_grade_override(from_status)):
grades_service = get_runtime_service('grades') grades_service = get_runtime_service('grades')
log_msg = ( if grades_service.should_override_grade_on_rejected_exam(exam['course_id']):
'Deleting override of exam subsection grade for ' log_msg = (
'user_id {user_id} on {course_id} for ' 'Deleting override of exam subsection grade for '
'content_id {content_id}. Override ' 'user_id {user_id} on {course_id} for '
'earned_all: {earned_all}, ' 'content_id {content_id}. Override '
'earned_graded: {earned_graded}.'.format( 'earned_all: {earned_all}, '
user_id=exam_attempt_obj.user_id, 'earned_graded: {earned_graded}.'.format(
course_id=exam['course_id'], user_id=exam_attempt_obj.user_id,
content_id=exam_attempt_obj.proctored_exam.content_id, course_id=exam['course_id'],
earned_all=REJECTED_GRADE_OVERRIDE_EARNED, content_id=exam_attempt_obj.proctored_exam.content_id,
earned_graded=REJECTED_GRADE_OVERRIDE_EARNED earned_all=REJECTED_GRADE_OVERRIDE_EARNED,
earned_graded=REJECTED_GRADE_OVERRIDE_EARNED
)
) )
) log.info(log_msg)
log.info(log_msg)
grades_service.undo_override_subsection_grade( grades_service.undo_override_subsection_grade(
user_id=exam_attempt_obj.user_id, user_id=exam_attempt_obj.user_id,
course_key_or_id=exam['course_id'], course_key_or_id=exam['course_id'],
usage_key_or_id=exam_attempt_obj.proctored_exam.content_id, usage_key_or_id=exam_attempt_obj.proctored_exam.content_id,
) )
# call service to get course name. # call service to get course name.
credit_service = get_runtime_service('credit') credit_service = get_runtime_service('credit')
......
...@@ -1001,6 +1001,82 @@ class ProctoredExamApiTests(ProctoredExamTestCase): ...@@ -1001,6 +1001,82 @@ class ProctoredExamApiTests(ProctoredExamTestCase):
'earned_graded': 5.0 'earned_graded': 5.0
}) })
def test_disabled_grade_override(self):
"""
Verify that when the REJECTED_EXAM_OVERRIDES_GRADE flag is disabled for a course,
the learner's subsection grade for the exam will not be overriden.
"""
set_runtime_service('grades', MockGradesService(rejected_exam_overrides_grade=False))
grades_service = get_runtime_service('grades')
exam_attempt = self._create_started_exam_attempt()
# Pretend learner answered 5 graded questions in the exam correctly
grades_service.init_grade(
user_id=self.user.id,
course_key_or_id=exam_attempt.proctored_exam.course_id,
usage_key_or_id=exam_attempt.proctored_exam.content_id,
earned_all=5.0,
earned_graded=5.0
)
update_attempt_status(
exam_attempt.proctored_exam_id,
self.user.id,
ProctoredExamStudentAttemptStatus.rejected
)
# Rejected exam attempt should override learner's grade to 0
override = grades_service.get_subsection_grade_override(
user_id=self.user.id,
course_key_or_id=exam_attempt.proctored_exam.course_id,
usage_key_or_id=exam_attempt.proctored_exam.content_id
)
self.assertIsNone(override)
grade = grades_service.get_subsection_grade(
user_id=self.user.id,
course_key_or_id=exam_attempt.proctored_exam.course_id,
usage_key_or_id=exam_attempt.proctored_exam.content_id
)
# Grade is not overriden
self.assertDictEqual({
'earned_all': grade.earned_all,
'earned_graded': grade.earned_graded
}, {
'earned_all': 5.0,
'earned_graded': 5.0
})
update_attempt_status(
exam_attempt.proctored_exam_id,
self.user.id,
ProctoredExamStudentAttemptStatus.verified
)
override = grades_service.get_subsection_grade_override(
user_id=self.user.id,
course_key_or_id=exam_attempt.proctored_exam.course_id,
usage_key_or_id=exam_attempt.proctored_exam.content_id
)
self.assertIsNone(override)
grade = grades_service.get_subsection_grade(
user_id=self.user.id,
course_key_or_id=exam_attempt.proctored_exam.course_id,
usage_key_or_id=exam_attempt.proctored_exam.content_id
)
# Grade has still the original score
self.assertDictEqual({
'earned_all': grade.earned_all,
'earned_graded': grade.earned_graded
}, {
'earned_all': 5.0,
'earned_graded': 5.0
})
@ddt.data( @ddt.data(
(ProctoredExamStudentAttemptStatus.declined, ProctoredExamStudentAttemptStatus.eligible), (ProctoredExamStudentAttemptStatus.declined, ProctoredExamStudentAttemptStatus.eligible),
(ProctoredExamStudentAttemptStatus.timed_out, ProctoredExamStudentAttemptStatus.created), (ProctoredExamStudentAttemptStatus.timed_out, ProctoredExamStudentAttemptStatus.created),
......
...@@ -193,10 +193,11 @@ class MockGradesService(object): ...@@ -193,10 +193,11 @@ class MockGradesService(object):
""" """
Simple mock of the Grades Service Simple mock of the Grades Service
""" """
def __init__(self): def __init__(self, rejected_exam_overrides_grade=True):
"""Initialize empty data stores for grades and overrides (just dicts)""" """Initialize empty data stores for grades and overrides (just dicts)"""
self.grades = {} self.grades = {}
self.overrides = {} self.overrides = {}
self.rejected_exam_overrides_grade = rejected_exam_overrides_grade
def init_grade(self, user_id, course_key_or_id, usage_key_or_id, earned_all, earned_graded): def init_grade(self, user_id, course_key_or_id, usage_key_or_id, earned_all, earned_graded):
"""Initialize a grade in MockGradesService for testing. Actual GradesService does not have this method.""" """Initialize a grade in MockGradesService for testing. Actual GradesService does not have this method."""
...@@ -234,3 +235,7 @@ class MockGradesService(object): ...@@ -234,3 +235,7 @@ class MockGradesService(object):
key = str(user_id) + str(course_key_or_id) + str(usage_key_or_id) key = str(user_id) + str(course_key_or_id) + str(usage_key_or_id)
if key in self.overrides: if key in self.overrides:
del self.overrides[key] del self.overrides[key]
def should_override_grade_on_rejected_exam(self, course_key):
"""Mock will always return instance variable: rejected_exam_overrides_grade"""
return self.rejected_exam_overrides_grade
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