Merge pull request #16943 from edx/sofiya/ora

Educator-1280 Force grade persist on changed enrollment track
This commit is contained in:
Sofiya Semenova
2017-12-27 12:07:18 -05:00
committed by GitHub
6 changed files with 38 additions and 16 deletions

View File

@@ -214,12 +214,12 @@ class CourseGradeBase(object):
Returns a list of subsection grades for the given chapter.
"""
return [
self._get_subsection_grade(course_structure[subsection_key])
self._get_subsection_grade(course_structure[subsection_key], self.force_update_subsections)
for subsection_key in _uniqueify_and_keep_order(course_structure.get_children(chapter_key))
]
@abstractmethod
def _get_subsection_grade(self, subsection):
def _get_subsection_grade(self, subsection, force_update_subsections=False):
"""
Abstract method to be implemented by subclasses for returning
the grade of the given subsection.
@@ -232,7 +232,7 @@ class ZeroCourseGrade(CourseGradeBase):
Course Grade class for Zero-value grades when no problems were
attempted in the course.
"""
def _get_subsection_grade(self, subsection):
def _get_subsection_grade(self, subsection, force_update_subsections=False):
return ZeroSubsectionGrade(subsection, self.course_data)
@@ -276,9 +276,9 @@ class CourseGrade(CourseGradeBase):
return True
return False
def _get_subsection_grade(self, subsection):
def _get_subsection_grade(self, subsection, force_update_subsections=False):
if self.force_update_subsections:
return self._subsection_grade_factory.update(subsection)
return self._subsection_grade_factory.update(subsection, force_update_subsections)
else:
# Pass read_only here so the subsection grades can be persisted in bulk at the end.
return self._subsection_grade_factory.create(subsection, read_only=True)

View File

@@ -66,7 +66,11 @@ class CourseGradeFactory(object):
or course_key should be provided.
"""
course_data = CourseData(user, course, collected_block_structure, course_structure, course_key)
return self._update(user, course_data, force_update_subsections=force_update_subsections)
return self._update(
user,
course_data,
force_update_subsections=force_update_subsections
)
def iter(
self,
@@ -164,7 +168,11 @@ class CourseGradeFactory(object):
if should_persist and force_update_subsections:
prefetch(user, course_data.course_key)
course_grade = CourseGrade(user, course_data, force_update_subsections=force_update_subsections)
course_grade = CourseGrade(
user,
course_data,
force_update_subsections=force_update_subsections
)
course_grade = course_grade.update()
should_persist = should_persist and course_grade.attempted

View File

@@ -248,4 +248,8 @@ def recalculate_course_and_subsection_grades(sender, user, course_key, **kwargs)
"""
previous_course_grade = CourseGradeFactory().read(user, course_key=course_key)
if previous_course_grade and previous_course_grade.attempted:
CourseGradeFactory().update(user=user, course_key=course_key, force_update_subsections=True)
CourseGradeFactory().update(
user=user,
course_key=course_key,
force_update_subsections=True
)

View File

@@ -230,11 +230,11 @@ class CreateSubsectionGrade(NonZeroSubsectionGrade):
super(CreateSubsectionGrade, self).__init__(subsection, all_total, graded_total)
def update_or_create_model(self, student, score_deleted=False):
def update_or_create_model(self, student, score_deleted=False, force_update_subsections=False):
"""
Saves or updates the subsection grade in a persisted model.
"""
if self._should_persist_per_attempted(score_deleted):
if self._should_persist_per_attempted(score_deleted, force_update_subsections):
return PersistentSubsectionGrade.update_or_create_grade(**self._persisted_model_params(student))
@classmethod
@@ -250,17 +250,23 @@ class CreateSubsectionGrade(NonZeroSubsectionGrade):
]
return PersistentSubsectionGrade.bulk_create_grades(params, student.id, course_key)
def _should_persist_per_attempted(self, score_deleted=False):
def _should_persist_per_attempted(self, score_deleted=False, force_update_subsections=False):
"""
Returns whether the SubsectionGrade's model should be
persisted based on settings and attempted status.
If the learner's score was just deleted, they will have
no attempts but the grade should still be persisted.
If the learner's enrollment track has changed, and the
subsection *only* contains track-specific problems that the
user has attempted, a re-grade will not occur. Should force
a re-grade in this case. See EDUCATOR-1280.
"""
return (
self.all_total.first_attempted is not None or
score_deleted
score_deleted or
force_update_subsections
)
def _persisted_model_params(self, student):

View File

@@ -63,7 +63,7 @@ class SubsectionGradeFactory(object):
)
self._unsaved_subsection_grades.clear()
def update(self, subsection, only_if_higher=None, score_deleted=False):
def update(self, subsection, only_if_higher=None, score_deleted=False, force_update_subsections=False):
"""
Updates the SubsectionGrade object for the student and subsection.
"""
@@ -89,7 +89,11 @@ class SubsectionGradeFactory(object):
):
return orig_subsection_grade
grade_model = calculated_grade.update_or_create_model(self.student, score_deleted)
grade_model = calculated_grade.update_or_create_model(
self.student,
score_deleted,
force_update_subsections
)
self._update_saved_subsection_grade(subsection.location, grade_model)
return calculated_grade

View File

@@ -94,7 +94,7 @@ class TestCourseGradeFactory(GradeTestBase):
with self.assertNumQueries(2), mock_get_score(1, 2):
_assert_read(expected_pass=False, expected_percent=0) # start off with grade of 0
with self.assertNumQueries(29), mock_get_score(1, 2):
with self.assertNumQueries(32), mock_get_score(1, 2):
grade_factory.update(self.request.user, self.course, force_update_subsections=True)
with self.assertNumQueries(2):
@@ -106,7 +106,7 @@ class TestCourseGradeFactory(GradeTestBase):
with self.assertNumQueries(2):
_assert_read(expected_pass=True, expected_percent=0.5) # NOT updated to grade of .25
with self.assertNumQueries(12), mock_get_score(2, 2):
with self.assertNumQueries(15), mock_get_score(2, 2):
grade_factory.update(self.request.user, self.course, force_update_subsections=True)
with self.assertNumQueries(2):