243 lines
8.9 KiB
Python
243 lines
8.9 KiB
Python
""" Tests calling the grades api directly """
|
|
|
|
|
|
from unittest.mock import patch
|
|
|
|
import ddt
|
|
|
|
from common.djangoapps.student.tests.factories import UserFactory
|
|
from lms.djangoapps.grades import api
|
|
from lms.djangoapps.grades.models import (
|
|
PersistentSubsectionGrade,
|
|
PersistentSubsectionGradeOverride,
|
|
PersistentCourseGrade
|
|
)
|
|
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order
|
|
from xmodule.modulestore.tests.factories import CourseFactory, BlockFactory # lint-amnesty, pylint: disable=wrong-import-order
|
|
|
|
|
|
@ddt.ddt
|
|
class OverrideSubsectionGradeTests(ModuleStoreTestCase):
|
|
"""
|
|
Tests for the override subsection grades api call
|
|
"""
|
|
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
super().setUpTestData()
|
|
cls.user = UserFactory()
|
|
cls.overriding_user = UserFactory()
|
|
cls.signal_patcher = patch('lms.djangoapps.grades.signals.signals.SUBSECTION_OVERRIDE_CHANGED.send')
|
|
cls.signal_patcher.start()
|
|
cls.id_patcher = patch('lms.djangoapps.grades.api.create_new_event_transaction_id')
|
|
cls.mock_create_id = cls.id_patcher.start()
|
|
cls.mock_create_id.return_value = 1
|
|
cls.type_patcher = patch('lms.djangoapps.grades.api.set_event_transaction_type')
|
|
cls.type_patcher.start()
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
super().tearDownClass()
|
|
cls.signal_patcher.stop()
|
|
cls.id_patcher.stop()
|
|
cls.type_patcher.stop()
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.course = CourseFactory.create()
|
|
self.subsection = BlockFactory.create(parent=self.course, category="sequential", display_name="Subsection")
|
|
self.grade = PersistentSubsectionGrade.update_or_create_grade(
|
|
user_id=self.user.id,
|
|
course_id=self.course.id,
|
|
usage_key=self.subsection.location,
|
|
first_attempted=None,
|
|
visible_blocks=[],
|
|
earned_all=6.0,
|
|
possible_all=6.0,
|
|
earned_graded=5.0,
|
|
possible_graded=5.0
|
|
)
|
|
|
|
def tearDown(self):
|
|
super().tearDown()
|
|
PersistentSubsectionGradeOverride.objects.all().delete()
|
|
|
|
@ddt.data(0.0, None, 3.0)
|
|
def test_override_subsection_grade(self, earned_graded):
|
|
api.override_subsection_grade(
|
|
self.user.id,
|
|
self.course.id,
|
|
self.subsection.location,
|
|
overrider=self.overriding_user,
|
|
earned_graded=earned_graded,
|
|
comment='Test Override Comment',
|
|
)
|
|
override_obj = api.get_subsection_grade_override(
|
|
self.user.id,
|
|
self.course.id,
|
|
self.subsection.location
|
|
)
|
|
assert override_obj is not None
|
|
assert override_obj.earned_graded_override == earned_graded
|
|
assert override_obj.override_reason == 'Test Override Comment'
|
|
|
|
for i in range(3):
|
|
override_obj.override_reason = 'this field purposefully left blank'
|
|
override_obj.earned_graded_override = i
|
|
override_obj.save()
|
|
|
|
api.override_subsection_grade(
|
|
self.user.id,
|
|
self.course.id,
|
|
self.subsection.location,
|
|
overrider=self.overriding_user,
|
|
earned_graded=earned_graded,
|
|
comment='Test Override Comment 2',
|
|
)
|
|
override_obj = api.get_subsection_grade_override(
|
|
self.user.id,
|
|
self.course.id,
|
|
self.subsection.location
|
|
)
|
|
|
|
assert override_obj is not None
|
|
assert override_obj.earned_graded_override == earned_graded
|
|
assert override_obj.override_reason == 'Test Override Comment 2'
|
|
|
|
assert 5 == len(override_obj.history.all())
|
|
for history_entry in override_obj.history.all():
|
|
if history_entry.override_reason.startswith('Test Override Comment'):
|
|
assert self.overriding_user == history_entry.history_user
|
|
assert self.overriding_user.id == history_entry.history_user_id
|
|
else:
|
|
assert history_entry.history_user is None
|
|
assert history_entry.history_user_id is None
|
|
|
|
|
|
class ClearGradeTests(ModuleStoreTestCase):
|
|
"""
|
|
Tests for the clearing grades api call
|
|
"""
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
super().setUpTestData()
|
|
cls.user = UserFactory()
|
|
cls.overriding_user = UserFactory()
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
super().tearDownClass()
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.course = CourseFactory.create()
|
|
self.subsection = BlockFactory.create(parent=self.course)
|
|
self.grade = PersistentSubsectionGrade.update_or_create_grade(
|
|
user_id=self.user.id,
|
|
course_id=self.course.id,
|
|
usage_key=self.subsection.location,
|
|
first_attempted=None,
|
|
visible_blocks=[],
|
|
earned_all=6.0,
|
|
possible_all=6.0,
|
|
earned_graded=5.0,
|
|
possible_graded=5.0
|
|
)
|
|
self.params = {
|
|
"user_id": self.user.id,
|
|
"course_id": self.course.id,
|
|
"course_version": "JoeMcEwing",
|
|
"percent_grade": 77.7,
|
|
"letter_grade": "Great job",
|
|
"passed": True,
|
|
}
|
|
PersistentCourseGrade.update_or_create(**self.params)
|
|
|
|
def tearDown(self):
|
|
super().tearDown()
|
|
PersistentSubsectionGradeOverride.objects.all().delete() # clear out all previous overrides
|
|
|
|
def test_clear_user_course_grades(self):
|
|
api.override_subsection_grade(
|
|
self.user.id,
|
|
self.course.id,
|
|
self.subsection.location,
|
|
overrider=self.overriding_user,
|
|
earned_graded=0.0,
|
|
comment='Test Override Comment',
|
|
)
|
|
override_obj = api.get_subsection_grade_override(
|
|
self.user.id,
|
|
self.course.id,
|
|
self.subsection.location
|
|
)
|
|
course_grade = PersistentCourseGrade.read(self.user.id, self.course.id)
|
|
self.assertIsNotNone(course_grade)
|
|
self.assertIsNotNone(override_obj)
|
|
|
|
api.clear_user_course_grades(self.user.id, self.course.id)
|
|
|
|
with self.assertRaises(PersistentCourseGrade.DoesNotExist):
|
|
PersistentCourseGrade.read(self.user.id, self.course.id)
|
|
|
|
with self.assertRaises(PersistentSubsectionGrade.DoesNotExist):
|
|
api.get_subsection_grade_override(
|
|
self.user.id,
|
|
self.course.id,
|
|
self.subsection.location
|
|
)
|
|
|
|
def _create_and_get_user_grades(self, user_id):
|
|
""" Creates grades for a user and override object """
|
|
api.override_subsection_grade(
|
|
user_id,
|
|
self.course.id,
|
|
self.subsection.location,
|
|
overrider=self.overriding_user,
|
|
earned_graded=0.0,
|
|
comment='Test Override Comment',
|
|
)
|
|
return api.get_subsection_grade_override(
|
|
user_id,
|
|
self.course.id,
|
|
self.subsection.location
|
|
)
|
|
|
|
def test_clear_other_user_course_grades(self):
|
|
"""
|
|
Make sure it deletes grades for other_user and not self.user
|
|
"""
|
|
# Create grades for 2 users
|
|
other_user = UserFactory()
|
|
user_override_obj = self._create_and_get_user_grades(self.user.id)
|
|
other_user_override_obj = self._create_and_get_user_grades(other_user.id)
|
|
|
|
# fetch and assert grades are available for both users
|
|
user_course_grade = PersistentCourseGrade.read(self.user.id, self.course.id)
|
|
other_user_course_grade = PersistentCourseGrade.read(self.user.id, self.course.id)
|
|
self.assertIsNotNone(user_course_grade)
|
|
self.assertIsNotNone(user_override_obj)
|
|
self.assertIsNotNone(other_user_override_obj)
|
|
self.assertIsNotNone(other_user_course_grade)
|
|
|
|
api.clear_user_course_grades(other_user.id, self.course.id)
|
|
|
|
# assert grades after deletion for other_user
|
|
after_clear_override_obj = api.get_subsection_grade_override(
|
|
self.user.id,
|
|
self.course.id,
|
|
self.subsection.location
|
|
)
|
|
after_clear_user_course_grade = PersistentCourseGrade.read(self.user.id, self.course.id)
|
|
with self.assertRaises(PersistentCourseGrade.DoesNotExist):
|
|
PersistentCourseGrade.read(other_user.id, self.course.id)
|
|
self.assertIsNotNone(after_clear_override_obj)
|
|
self.assertIsNotNone(after_clear_user_course_grade)
|
|
|
|
@patch('lms.djangoapps.grades.models_api._PersistentSubsectionGrade')
|
|
@patch('lms.djangoapps.grades.models_api._PersistentCourseGrade')
|
|
def test_assert_clear_grade_methods_called(self, mock_course_grade, mock_subsection_grade):
|
|
api.clear_user_course_grades(self.user.id, self.course.id)
|
|
mock_course_grade.delete_course_grade_for_learner.assert_called_with(self.course.id, self.user.id)
|
|
mock_subsection_grade.delete_subsection_grades_for_learner.assert_called_with(self.user.id, self.course.id)
|