Commit 16114f58 by Nimisha Asthagiri

Miscellaneous grades code cleanup - pending from previous PRs

parent b30d9f15
"""
Custom exceptions raised by grades.
"""
class DatabaseNotReadyError(IOError):
"""
Subclass of IOError to indicate the database has not yet committed
the data we're trying to find.
"""
pass
...@@ -88,7 +88,7 @@ class TestResetGrades(TestCase): ...@@ -88,7 +88,7 @@ class TestResetGrades(TestCase):
for user_id in self.user_ids: for user_id in self.user_ids:
course_grade_params['user_id'] = user_id course_grade_params['user_id'] = user_id
course_grade_params['course_id'] = course_key course_grade_params['course_id'] = course_key
PersistentCourseGrade.update_or_create_course_grade(**course_grade_params) PersistentCourseGrade.update_or_create(**course_grade_params)
for subsection_key in self.subsection_keys_by_course[course_key]: for subsection_key in self.subsection_keys_by_course[course_key]:
subsection_grade_params['user_id'] = user_id subsection_grade_params['user_id'] = user_id
subsection_grade_params['usage_key'] = subsection_key subsection_grade_params['usage_key'] = subsection_key
...@@ -100,7 +100,7 @@ class TestResetGrades(TestCase): ...@@ -100,7 +100,7 @@ class TestResetGrades(TestCase):
""" """
for course_key in course_keys: for course_key in course_keys:
if db_table == "course" or db_table is None: if db_table == "course" or db_table is None:
self.assertIsNotNone(PersistentCourseGrade.read_course_grade(self.user_ids[0], course_key)) self.assertIsNotNone(PersistentCourseGrade.read(self.user_ids[0], course_key))
if db_table == "subsection" or db_table is None: if db_table == "subsection" or db_table is None:
for subsection_key in self.subsection_keys_by_course[course_key]: for subsection_key in self.subsection_keys_by_course[course_key]:
self.assertIsNotNone(PersistentSubsectionGrade.read_grade(self.user_ids[0], subsection_key)) self.assertIsNotNone(PersistentSubsectionGrade.read_grade(self.user_ids[0], subsection_key))
...@@ -112,7 +112,7 @@ class TestResetGrades(TestCase): ...@@ -112,7 +112,7 @@ class TestResetGrades(TestCase):
for course_key in course_keys: for course_key in course_keys:
if db_table == "course" or db_table is None: if db_table == "course" or db_table is None:
with self.assertRaises(PersistentCourseGrade.DoesNotExist): with self.assertRaises(PersistentCourseGrade.DoesNotExist):
PersistentCourseGrade.read_course_grade(self.user_ids[0], course_key) PersistentCourseGrade.read(self.user_ids[0], course_key)
if db_table == "subsection" or db_table is None: if db_table == "subsection" or db_table is None:
for subsection_key in self.subsection_keys_by_course[course_key]: for subsection_key in self.subsection_keys_by_course[course_key]:
......
...@@ -533,7 +533,7 @@ class PersistentCourseGrade(DeleteGradesMixin, TimeStampedModel): ...@@ -533,7 +533,7 @@ class PersistentCourseGrade(DeleteGradesMixin, TimeStampedModel):
]) ])
@classmethod @classmethod
def read_course_grade(cls, user_id, course_id): def read(cls, user_id, course_id):
""" """
Reads a grade from database Reads a grade from database
...@@ -546,7 +546,7 @@ class PersistentCourseGrade(DeleteGradesMixin, TimeStampedModel): ...@@ -546,7 +546,7 @@ class PersistentCourseGrade(DeleteGradesMixin, TimeStampedModel):
return cls.objects.get(user_id=user_id, course_id=course_id) return cls.objects.get(user_id=user_id, course_id=course_id)
@classmethod @classmethod
def update_or_create_course_grade(cls, user_id, course_id, **kwargs): def update_or_create(cls, user_id, course_id, **kwargs):
""" """
Creates a course grade in the database. Creates a course grade in the database.
Returns a PersistedCourseGrade object. Returns a PersistedCourseGrade object.
......
...@@ -105,8 +105,7 @@ class CourseGradeFactory(object): ...@@ -105,8 +105,7 @@ class CourseGradeFactory(object):
# Keep marching on even if this student couldn't be graded for # Keep marching on even if this student couldn't be graded for
# some reason, but log it for future reference. # some reason, but log it for future reference.
log.exception( log.exception(
'Cannot grade student %s (%s) in course %s because of exception: %s', 'Cannot grade student %s in course %s because of exception: %s',
student.username,
student.id, student.id,
course.id, course.id,
exc.message exc.message
...@@ -130,7 +129,7 @@ class CourseGradeFactory(object): ...@@ -130,7 +129,7 @@ class CourseGradeFactory(object):
if not should_persist_grades(course_data.course_key): if not should_persist_grades(course_data.course_key):
raise PersistentCourseGrade.DoesNotExist raise PersistentCourseGrade.DoesNotExist
persistent_grade = PersistentCourseGrade.read_course_grade(user.id, course_data.course_key) persistent_grade = PersistentCourseGrade.read(user.id, course_data.course_key)
course_grade = CourseGrade( course_grade = CourseGrade(
user, user,
course_data, course_data,
...@@ -159,7 +158,7 @@ class CourseGradeFactory(object): ...@@ -159,7 +158,7 @@ class CourseGradeFactory(object):
) )
if should_persist: if should_persist:
course_grade._subsection_grade_factory.bulk_create_unsaved() course_grade._subsection_grade_factory.bulk_create_unsaved()
PersistentCourseGrade.update_or_create_course_grade( PersistentCourseGrade.update_or_create(
user_id=user.id, user_id=user.id,
course_id=course_data.course_key, course_id=course_data.course_key,
course_version=course_data.version, course_version=course_data.version,
......
...@@ -78,3 +78,8 @@ SUBSECTION_SCORE_CHANGED = Signal( ...@@ -78,3 +78,8 @@ SUBSECTION_SCORE_CHANGED = Signal(
'subsection_grade', # SubsectionGrade object 'subsection_grade', # SubsectionGrade object
] ]
) )
# Signal that indicates that a user's grade for a course has been updated.
# This is a downstream signal of SUBSECTION_SCORE_CHANGED.
from openedx.core.djangoapps.signals.signals import COURSE_GRADE_CHANGED
...@@ -31,24 +31,17 @@ from util.date_utils import from_timestamp ...@@ -31,24 +31,17 @@ from util.date_utils import from_timestamp
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from .constants import ScoreDatabaseTableEnum from .constants import ScoreDatabaseTableEnum
from .exceptions import DatabaseNotReadyError
from .new.subsection_grade_factory import SubsectionGradeFactory from .new.subsection_grade_factory import SubsectionGradeFactory
from .new.course_grade_factory import CourseGradeFactory from .new.course_grade_factory import CourseGradeFactory
from .signals.signals import SUBSECTION_SCORE_CHANGED from .signals.signals import SUBSECTION_SCORE_CHANGED
from .transformer import GradesTransformer from .transformer import GradesTransformer
class DatabaseNotReadyError(IOError):
"""
Subclass of IOError to indicate the database has not yet committed
the data we're trying to find.
"""
pass
KNOWN_RETRY_ERRORS = ( # Errors we expect occasionally, should be resolved on retry KNOWN_RETRY_ERRORS = ( # Errors we expect occasionally, should be resolved on retry
DatabaseError, DatabaseError,
ValidationError, ValidationError,
DatabaseNotReadyError DatabaseNotReadyError,
) )
RECALCULATE_GRADE_DELAY = 2 # in seconds, to prevent excessive _has_db_updated failures. See TNL-6424. RECALCULATE_GRADE_DELAY = 2 # in seconds, to prevent excessive _has_db_updated failures. See TNL-6424.
...@@ -189,12 +182,7 @@ def _has_db_updated_with_new_score(self, scored_block_usage_key, **kwargs): ...@@ -189,12 +182,7 @@ def _has_db_updated_with_new_score(self, scored_block_usage_key, **kwargs):
return db_is_updated return db_is_updated
def _update_subsection_grades( def _update_subsection_grades(course_key, scored_block_usage_key, only_if_higher, user_id):
course_key,
scored_block_usage_key,
only_if_higher,
user_id,
):
""" """
A helper function to update subsection grades in the database A helper function to update subsection grades in the database
for each subsection containing the given block, and to signal for each subsection containing the given block, and to signal
......
...@@ -349,10 +349,10 @@ class PersistentCourseGradesTest(GradesModelTestCase): ...@@ -349,10 +349,10 @@ class PersistentCourseGradesTest(GradesModelTestCase):
} }
def test_update(self): def test_update(self):
created_grade = PersistentCourseGrade.update_or_create_course_grade(**self.params) created_grade = PersistentCourseGrade.update_or_create(**self.params)
self.params["percent_grade"] = 88.8 self.params["percent_grade"] = 88.8
self.params["letter_grade"] = "Better job" self.params["letter_grade"] = "Better job"
updated_grade = PersistentCourseGrade.update_or_create_course_grade(**self.params) updated_grade = PersistentCourseGrade.update_or_create(**self.params)
self.assertEqual(updated_grade.percent_grade, 88.8) self.assertEqual(updated_grade.percent_grade, 88.8)
self.assertEqual(updated_grade.letter_grade, "Better job") self.assertEqual(updated_grade.letter_grade, "Better job")
self.assertEqual(created_grade.id, updated_grade.id) self.assertEqual(created_grade.id, updated_grade.id)
...@@ -364,7 +364,7 @@ class PersistentCourseGradesTest(GradesModelTestCase): ...@@ -364,7 +364,7 @@ class PersistentCourseGradesTest(GradesModelTestCase):
u'letter_grade': u'', u'letter_grade': u'',
u'passed': False, u'passed': False,
}) })
grade = PersistentCourseGrade.update_or_create_course_grade(**self.params) grade = PersistentCourseGrade.update_or_create(**self.params)
self.assertIsNone(grade.passed_timestamp) self.assertIsNone(grade.passed_timestamp)
# After the user earns a passing grade, the passed_timestamp is set # After the user earns a passing grade, the passed_timestamp is set
...@@ -373,7 +373,7 @@ class PersistentCourseGradesTest(GradesModelTestCase): ...@@ -373,7 +373,7 @@ class PersistentCourseGradesTest(GradesModelTestCase):
u'letter_grade': u'C', u'letter_grade': u'C',
u'passed': True, u'passed': True,
}) })
grade = PersistentCourseGrade.update_or_create_course_grade(**self.params) grade = PersistentCourseGrade.update_or_create(**self.params)
passed_timestamp = grade.passed_timestamp passed_timestamp = grade.passed_timestamp
self.assertEqual(grade.letter_grade, u'C') self.assertEqual(grade.letter_grade, u'C')
self.assertIsInstance(passed_timestamp, datetime) self.assertIsInstance(passed_timestamp, datetime)
...@@ -385,7 +385,7 @@ class PersistentCourseGradesTest(GradesModelTestCase): ...@@ -385,7 +385,7 @@ class PersistentCourseGradesTest(GradesModelTestCase):
u'letter_grade': u'A', u'letter_grade': u'A',
u'passed': True, u'passed': True,
}) })
grade = PersistentCourseGrade.update_or_create_course_grade(**self.params) grade = PersistentCourseGrade.update_or_create(**self.params)
self.assertEqual(grade.letter_grade, u'A') self.assertEqual(grade.letter_grade, u'A')
self.assertEqual(grade.passed_timestamp, passed_timestamp) self.assertEqual(grade.passed_timestamp, passed_timestamp)
...@@ -395,18 +395,18 @@ class PersistentCourseGradesTest(GradesModelTestCase): ...@@ -395,18 +395,18 @@ class PersistentCourseGradesTest(GradesModelTestCase):
u'letter_grade': u'', u'letter_grade': u'',
u'passed': False, u'passed': False,
}) })
grade = PersistentCourseGrade.update_or_create_course_grade(**self.params) grade = PersistentCourseGrade.update_or_create(**self.params)
self.assertEqual(grade.letter_grade, u'') self.assertEqual(grade.letter_grade, u'')
self.assertEqual(grade.passed_timestamp, passed_timestamp) self.assertEqual(grade.passed_timestamp, passed_timestamp)
@freeze_time(now()) @freeze_time(now())
def test_passed_timestamp_is_now(self): def test_passed_timestamp_is_now(self):
grade = PersistentCourseGrade.update_or_create_course_grade(**self.params) grade = PersistentCourseGrade.update_or_create(**self.params)
self.assertEqual(now(), grade.passed_timestamp) self.assertEqual(now(), grade.passed_timestamp)
def test_create_and_read_grade(self): def test_create_and_read_grade(self):
created_grade = PersistentCourseGrade.update_or_create_course_grade(**self.params) created_grade = PersistentCourseGrade.update_or_create(**self.params)
read_grade = PersistentCourseGrade.read_course_grade(self.params["user_id"], self.params["course_id"]) read_grade = PersistentCourseGrade.read(self.params["user_id"], self.params["course_id"])
for param in self.params: for param in self.params:
if param == u'passed': if param == u'passed':
continue # passed/passed_timestamp takes special handling, and is tested separately continue # passed/passed_timestamp takes special handling, and is tested separately
...@@ -417,7 +417,7 @@ class PersistentCourseGradesTest(GradesModelTestCase): ...@@ -417,7 +417,7 @@ class PersistentCourseGradesTest(GradesModelTestCase):
@ddt.data('course_version', 'course_edited_timestamp') @ddt.data('course_version', 'course_edited_timestamp')
def test_optional_fields(self, field): def test_optional_fields(self, field):
del self.params[field] del self.params[field]
grade = PersistentCourseGrade.update_or_create_course_grade(**self.params) grade = PersistentCourseGrade.update_or_create(**self.params)
self.assertFalse(getattr(grade, field)) self.assertFalse(getattr(grade, field))
@ddt.data( @ddt.data(
...@@ -432,15 +432,15 @@ class PersistentCourseGradesTest(GradesModelTestCase): ...@@ -432,15 +432,15 @@ class PersistentCourseGradesTest(GradesModelTestCase):
def test_update_or_create_with_bad_params(self, param, val, error): def test_update_or_create_with_bad_params(self, param, val, error):
self.params[param] = val self.params[param] = val
with self.assertRaises(error): with self.assertRaises(error):
PersistentCourseGrade.update_or_create_course_grade(**self.params) PersistentCourseGrade.update_or_create(**self.params)
def test_grade_does_not_exist(self): def test_grade_does_not_exist(self):
with self.assertRaises(PersistentCourseGrade.DoesNotExist): with self.assertRaises(PersistentCourseGrade.DoesNotExist):
PersistentCourseGrade.read_course_grade(self.params["user_id"], self.params["course_id"]) PersistentCourseGrade.read(self.params["user_id"], self.params["course_id"])
def test_update_or_create_event(self): def test_update_or_create_event(self):
with patch('lms.djangoapps.grades.models.tracker') as tracker_mock: with patch('lms.djangoapps.grades.models.tracker') as tracker_mock:
grade = PersistentCourseGrade.update_or_create_course_grade(**self.params) grade = PersistentCourseGrade.update_or_create(**self.params)
self._assert_tracker_emitted_event(tracker_mock, grade) self._assert_tracker_emitted_event(tracker_mock, grade)
def _assert_tracker_emitted_event(self, tracker_mock, grade): def _assert_tracker_emitted_event(self, tracker_mock, grade):
......
...@@ -163,7 +163,7 @@ class TestCourseGradeFactory(GradeTestBase): ...@@ -163,7 +163,7 @@ class TestCourseGradeFactory(GradeTestBase):
course_id=self.course.id, course_id=self.course.id,
enabled_for_course=course_setting enabled_for_course=course_setting
): ):
with patch('lms.djangoapps.grades.models.PersistentCourseGrade.read_course_grade') as mock_read_grade: with patch('lms.djangoapps.grades.models.PersistentCourseGrade.read') as mock_read_grade:
grade_factory.create(self.request.user, self.course) grade_factory.create(self.request.user, self.course)
self.assertEqual(mock_read_grade.called, feature_flag and course_setting) self.assertEqual(mock_read_grade.called, feature_flag and course_setting)
......
...@@ -226,7 +226,7 @@ class RecalculateSubsectionGradeTest(HasCourseWithProblemsMixin, ModuleStoreTest ...@@ -226,7 +226,7 @@ class RecalculateSubsectionGradeTest(HasCourseWithProblemsMixin, ModuleStoreTest
with self.assertNumQueries(num_sql_queries): with self.assertNumQueries(num_sql_queries):
self._apply_recalculate_subsection_grade() self._apply_recalculate_subsection_grade()
with self.assertRaises(PersistentCourseGrade.DoesNotExist): with self.assertRaises(PersistentCourseGrade.DoesNotExist):
PersistentCourseGrade.read_course_grade(self.user.id, self.course.id) PersistentCourseGrade.read(self.user.id, self.course.id)
self.assertEqual(len(PersistentSubsectionGrade.bulk_read_grades(self.user.id, self.course.id)), 0) self.assertEqual(len(PersistentSubsectionGrade.bulk_read_grades(self.user.id, self.course.id)), 0)
@ddt.data( @ddt.data(
...@@ -240,7 +240,7 @@ class RecalculateSubsectionGradeTest(HasCourseWithProblemsMixin, ModuleStoreTest ...@@ -240,7 +240,7 @@ class RecalculateSubsectionGradeTest(HasCourseWithProblemsMixin, ModuleStoreTest
with check_mongo_calls(num_mongo_queries): with check_mongo_calls(num_mongo_queries):
with self.assertNumQueries(num_sql_queries): with self.assertNumQueries(num_sql_queries):
self._apply_recalculate_subsection_grade() self._apply_recalculate_subsection_grade()
self.assertIsNotNone(PersistentCourseGrade.read_course_grade(self.user.id, self.course.id)) self.assertIsNotNone(PersistentCourseGrade.read(self.user.id, self.course.id))
self.assertGreater(len(PersistentSubsectionGrade.bulk_read_grades(self.user.id, self.course.id)), 0) self.assertGreater(len(PersistentSubsectionGrade.bulk_read_grades(self.user.id, self.course.id)), 0)
@patch('lms.djangoapps.grades.signals.signals.SUBSECTION_SCORE_CHANGED.send') @patch('lms.djangoapps.grades.signals.signals.SUBSECTION_SCORE_CHANGED.send')
......
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