Merge pull request #13881 from edx/efischer/tnl-5314

Dashboard Final Grade Updates
This commit is contained in:
sanfordstudent
2016-11-07 16:08:06 -05:00
committed by GitHub
4 changed files with 120 additions and 26 deletions

View File

@@ -240,6 +240,25 @@ class CourseGrade(object):
return course_grade
@classmethod
def get_persisted_grade(cls, user, course):
"""
Gets the persisted grade in the database, without checking
whether it is up-to-date with the course's grading policy.
For read use only.
"""
try:
persistent_grade = PersistentCourseGrade.read_course_grade(user.id, course.id)
except PersistentCourseGrade.DoesNotExist:
return None
else:
course_grade = CourseGrade(user, course, None) # no course structure needed
course_grade._percent = persistent_grade.percent_grade # pylint: disable=protected-access
course_grade._letter_grade = persistent_grade.letter_grade # pylint: disable=protected-access
course_grade.course_version = persistent_grade.course_version
course_grade.course_edited_timestamp = persistent_grade.course_edited_timestamp
return course_grade
@staticmethod
def _calc_percent(grade_value):
"""
@@ -328,17 +347,17 @@ class CourseGradeFactory(object):
course_structure = get_course_blocks(self.student, course.location)
self._compute_and_update_grade(course, course_structure)
def _compute_and_update_grade(self, course, course_structure, read_only=False):
def get_persisted(self, course):
"""
Freshly computes and updates the grade for the student and course.
If read_only is True, doesn't save any updates to the grades.
Returns the saved grade for the given course and student,
irrespective of whether the saved grade is up-to-date.
"""
course_grade = CourseGrade(self.student, course, course_structure)
course_grade.compute_and_update(read_only)
return course_grade
if not PersistentGradesEnabledFlag.feature_enabled(course.id):
return None
def _get_saved_grade(self, course, course_structure): # pylint: disable=unused-argument
return CourseGrade.get_persisted_grade(self.student, course)
def _get_saved_grade(self, course, course_structure):
"""
Returns the saved grade for the given course and student.
"""
@@ -351,6 +370,16 @@ class CourseGradeFactory(object):
course_structure
)
def _compute_and_update_grade(self, course, course_structure, read_only=False):
"""
Freshly computes and updates the grade for the student and course.
If read_only is True, doesn't save any updates to the grades.
"""
course_grade = CourseGrade(self.student, course, course_structure)
course_grade.compute_and_update(read_only)
return course_grade
def _user_has_access_to_course(self, course_structure):
"""
Given a course structure, returns whether the user

View File

@@ -82,6 +82,23 @@ class TestCourseGradeFactory(GradeTestBase):
"""
Test that CourseGrades are calculated properly
"""
def setUp(self):
super(TestCourseGradeFactory, self).setUp()
grading_policy = {
"GRADER": [
{
"type": "Homework",
"min_count": 1,
"drop_count": 0,
"short_label": "HW",
"weight": 1.0,
},
],
"GRADE_CUTOFFS": {
"Pass": 0.5,
},
}
self.course.set_grading_policy(grading_policy)
@patch.dict(settings.FEATURES, {'PERSISTENT_GRADES_ENABLED_FOR_ALL_TESTS': False})
@ddt.data(
@@ -106,7 +123,26 @@ class TestCourseGradeFactory(GradeTestBase):
self.assertEqual(mock_save_grades.called, feature_flag and course_setting)
def test_course_grade_creation(self):
grading_policy = {
grade_factory = CourseGradeFactory(self.request.user)
with mock_get_score(1, 2):
course_grade = grade_factory.create(self.course)
self.assertEqual(course_grade.letter_grade, u'Pass')
self.assertEqual(course_grade.percent, 0.5)
def test_get_persisted(self):
grade_factory = CourseGradeFactory(self.request.user)
# first, create a grade in the database
with mock_get_score(1, 2):
grade_factory.create(self.course, read_only=False)
# retrieve the grade, ensuring it is as expected and take just one query
with self.assertNumQueries(1):
course_grade = grade_factory.get_persisted(self.course)
self.assertEqual(course_grade.letter_grade, u'Pass')
self.assertEqual(course_grade.percent, 0.5)
# update the grading policy
new_grading_policy = {
"GRADER": [
{
"type": "Homework",
@@ -117,13 +153,15 @@ class TestCourseGradeFactory(GradeTestBase):
},
],
"GRADE_CUTOFFS": {
"Pass": 0.5,
"Pass": 0.9,
},
}
self.course.set_grading_policy(grading_policy)
grade_factory = CourseGradeFactory(self.request.user)
with mock_get_score(1, 2):
course_grade = grade_factory.create(self.course)
self.course.set_grading_policy(new_grading_policy)
# ensure the grade can still be retrieved via get_persisted
# despite its outdated grading policy
with self.assertNumQueries(1):
course_grade = grade_factory.get_persisted(self.course)
self.assertEqual(course_grade.letter_grade, u'Pass')
self.assertEqual(course_grade.percent, 0.5)
@@ -168,18 +206,18 @@ class TestSubsectionGradeFactory(ProblemSubmissionTestMixin, GradeTestBase):
with patch(
'lms.djangoapps.grades.new.subsection_grade.SubsectionGradeFactory._get_bulk_cached_grade',
wraps=self.subsection_grade_factory._get_bulk_cached_grade
) as mock_get_saved_grade:
) as mock_get_bulk_cached_grade:
with self.assertNumQueries(14):
grade_a = self.subsection_grade_factory.create(self.sequence)
self.assertTrue(mock_get_saved_grade.called)
self.assertTrue(mock_get_bulk_cached_grade.called)
self.assertTrue(mock_create_grade.called)
mock_get_saved_grade.reset_mock()
mock_get_bulk_cached_grade.reset_mock()
mock_create_grade.reset_mock()
with self.assertNumQueries(0):
grade_b = self.subsection_grade_factory.create(self.sequence)
self.assertTrue(mock_get_saved_grade.called)
self.assertTrue(mock_get_bulk_cached_grade.called)
self.assertFalse(mock_create_grade.called)
self.assertEqual(grade_a.url_name, grade_b.url_name)