EDUCATOR-3668 | The CreateSubsectionGrade class should reflect possibly overridden values from the PersistentSubsectionGrade class.

This commit is contained in:
Alex Dusenbery
2018-12-17 16:25:24 -05:00
committed by Alex Dusenbery
parent 19b7adae5c
commit 7b04832606
2 changed files with 60 additions and 2 deletions

View File

@@ -253,7 +253,29 @@ class CreateSubsectionGrade(NonZeroSubsectionGrade):
Saves or updates the subsection grade in a persisted model.
"""
if self._should_persist_per_attempted(score_deleted, force_update_subsections):
return PersistentSubsectionGrade.update_or_create_grade(**self._persisted_model_params(student))
model = PersistentSubsectionGrade.update_or_create_grade(**self._persisted_model_params(student))
self._update_aggregated_scores_from_model(model)
return model
def _update_aggregated_scores_from_model(self, model):
"""
Updates this grade's `all_total` and `graded_total` attributes
to reflect the values from the related persisted model.
This is important, because PersistentSubsectionGradeOverrides
are only taken into account when reading/writing at the persistence layer,
so after we update a PersistentSubsectionGrade model (which could involve
writing values that are overridden by the PersistentSubsectionGradeOverride model)
we also need to update this grade object's attributes to reflect the
now (possibly) overriden values.
TODO: https://openedx.atlassian.net/browse/EDUCATOR-3835
"""
self.all_total.earned = model.earned_all
self.all_total.possible = model.possible_all
self.all_total.first_attempted = model.first_attempted
self.graded_total.earned = model.earned_graded
self.graded_total.possible = model.possible_graded
self.graded_total.first_attempted = model.first_attempted
@classmethod
def bulk_create_models(cls, student, subsection_grades, course_key):

View File

@@ -1,10 +1,13 @@
"""
Tests for the SubsectionGradeFactory class.
"""
import ddt
from courseware.tests.test_submitting_problems import ProblemSubmissionTestMixin
from django.conf import settings
from lms.djangoapps.grades.config.tests.utils import persistent_grades_feature_flags
from mock import patch
from ..models import PersistentSubsectionGrade
from ..models import PersistentSubsectionGrade, PersistentSubsectionGradeOverride
from ..subsection_grade_factory import ZeroSubsectionGrade
from .base import GradeTestBase
from .utils import mock_get_score
@@ -29,6 +32,10 @@ class TestSubsectionGradeFactory(ProblemSubmissionTestMixin, GradeTestBase):
(grade.all_total.earned, grade.all_total.possible),
(expected_earned, expected_possible),
)
self.assertEqual(
(grade.graded_total.earned, grade.graded_total.possible),
(expected_earned, expected_possible),
)
def test_create_zero(self):
"""
@@ -100,3 +107,32 @@ class TestSubsectionGradeFactory(ProblemSubmissionTestMixin, GradeTestBase):
):
self.subsection_grade_factory.create(self.sequence)
self.assertEqual(mock_read_saved_grade.called, feature_flag and course_setting)
def test_update_with_override(self):
"""
Tests that when a PersistentSubsectionGradeOverride exists, the update()
method returns a CreateSubsectionGrade with scores that account
for the override.
"""
# first, do an update to create a persistent grade
with mock_get_score(2, 3):
grade = self.subsection_grade_factory.update(self.sequence)
self.assert_grade(grade, 2, 3)
# there should only be one persistent grade
persistent_grade = PersistentSubsectionGrade.objects.first()
self.assertEqual(2, persistent_grade.earned_graded)
self.assertEqual(3, persistent_grade.possible_graded)
# Now create the override
PersistentSubsectionGradeOverride.objects.create(
grade=persistent_grade,
earned_graded_override=0,
earned_all_override=0,
)
# Now, even if the problem scores interface gives us a 2/3,
# the subsection grade returned should be 0/3 due to the override.
with mock_get_score(2, 3):
grade = self.subsection_grade_factory.update(self.sequence)
self.assert_grade(grade, 0, 3)