From d1d8d31bbae7e27994957b7295d7ff7c1f4b35d9 Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Tue, 20 Dec 2016 11:40:43 -0500 Subject: [PATCH] Test retrieving subsection grade after losing access to block in subsection --- lms/djangoapps/grades/new/subsection_grade.py | 28 +++-- .../grades/tests/integration/test_access.py | 108 ++++++++++++++++++ .../grades/tests/integration/test_events.py | 2 +- 3 files changed, 126 insertions(+), 12 deletions(-) create mode 100644 lms/djangoapps/grades/tests/integration/test_access.py diff --git a/lms/djangoapps/grades/new/subsection_grade.py b/lms/djangoapps/grades/new/subsection_grade.py index ebd5867ff6..ef5e4597fe 100644 --- a/lms/djangoapps/grades/new/subsection_grade.py +++ b/lms/djangoapps/grades/new/subsection_grade.py @@ -130,17 +130,23 @@ class SubsectionGrade(object): Compute score for the given block. If persisted_values is provided, it is used for possible and weight. """ - block = course_structure[block_key] - - if getattr(block, 'has_score', False): - problem_score = get_score( - submissions_scores, - csm_scores, - persisted_block, - block, - ) - if problem_score: - self.locations_to_scores[block_key] = problem_score + try: + block = course_structure[block_key] + except KeyError: + # It's possible that the user's access to that + # block has changed since the subsection grade + # was last persisted. + pass + else: + if getattr(block, 'has_score', False): + problem_score = get_score( + submissions_scores, + csm_scores, + persisted_block, + block, + ) + if problem_score: + self.locations_to_scores[block_key] = problem_score def _persisted_model_params(self, student): """ diff --git a/lms/djangoapps/grades/tests/integration/test_access.py b/lms/djangoapps/grades/tests/integration/test_access.py new file mode 100644 index 0000000000..cd7c8e2cbb --- /dev/null +++ b/lms/djangoapps/grades/tests/integration/test_access.py @@ -0,0 +1,108 @@ +""" +Test grading with access changes. +""" +# pylint: disable=protected-access + +from capa.tests.response_xml_factory import MultipleChoiceResponseXMLFactory +from lms.djangoapps.course_blocks.api import get_course_blocks +from openedx.core.djangolib.testing.utils import get_mock_request +from xmodule.modulestore.django import modulestore +from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase +from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory + +from courseware.tests.test_submitting_problems import ProblemSubmissionTestMixin +from student.models import CourseEnrollment +from student.tests.factories import UserFactory +from xmodule.modulestore import ModuleStoreEnum + +from ...new.subsection_grade import SubsectionGradeFactory + + +class GradesAccessIntegrationTest(ProblemSubmissionTestMixin, SharedModuleStoreTestCase): + """ + Tests integration between grading and block access. + """ + @classmethod + def setUpClass(cls): + super(GradesAccessIntegrationTest, cls).setUpClass() + cls.store = modulestore() + cls.course = CourseFactory.create() + cls.chapter = ItemFactory.create( + parent=cls.course, + category="chapter", + display_name="Test Chapter" + ) + cls.sequence = ItemFactory.create( + parent=cls.chapter, + category='sequential', + display_name="Test Sequential 1", + graded=True, + format="Homework" + ) + cls.vertical = ItemFactory.create( + parent=cls.sequence, + category='vertical', + display_name='Test Vertical 1' + ) + problem_xml = MultipleChoiceResponseXMLFactory().build_xml( + question_text='The correct answer is Choice 2', + choices=[False, False, True, False], + choice_names=['choice_0', 'choice_1', 'choice_2', 'choice_3'] + ) + cls.problem = ItemFactory.create( + parent=cls.vertical, + category="problem", + display_name="p1", + data=problem_xml, + metadata={'weight': 2} + ) + + cls.problem_2 = ItemFactory.create( + parent=cls.vertical, + category="problem", + display_name="p2", + data=problem_xml, + metadata={'weight': 2} + ) + + def setUp(self): + super(GradesAccessIntegrationTest, self).setUp() + self.request = get_mock_request(UserFactory()) + self.student = self.request.user + self.client.login(username=self.student.username, password="test") + CourseEnrollment.enroll(self.student, self.course.id) + self.instructor = UserFactory.create(is_staff=True, username=u'test_instructor', password=u'test') + self.refresh_course() + + def test_subsection_access_changed(self): + """ + Tests retrieving a subsection grade before and after losing access + to a block in the subsection. + """ + # submit answers + self.submit_question_answer('p1', {'2_1': 'choice_choice_2'}) + self.submit_question_answer('p2', {'2_1': 'choice_choice_2'}) + + # check initial subsection grade + course_structure = get_course_blocks(self.request.user, self.course.location) + subsection_grade_factory = SubsectionGradeFactory(self.request.user, self.course, course_structure) + grade = subsection_grade_factory.create(self.sequence, read_only=True) + self.assertEqual(grade.graded_total.earned, 4.0) + self.assertEqual(grade.graded_total.possible, 4.0) + + # set a block in the subsection to be visible to staff only + with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred): + problem_2 = self.store.get_item(self.problem_2.location) + problem_2.visible_to_staff_only = True + self.store.update_item(problem_2, self.instructor.id) + self.store.publish(self.course.location, self.instructor.id) + course_structure = get_course_blocks(self.student, self.course.location) + + # ensure that problem_2 is not accessible for the student + self.assertNotIn(problem_2.location, course_structure) + + # make sure we can still get the subsection grade + subsection_grade_factory = SubsectionGradeFactory(self.student, self.course, course_structure) + grade = subsection_grade_factory.create(self.sequence, read_only=True) + self.assertEqual(grade.graded_total.earned, 4.0) + self.assertEqual(grade.graded_total.possible, 4.0) diff --git a/lms/djangoapps/grades/tests/integration/test_events.py b/lms/djangoapps/grades/tests/integration/test_events.py index 3c579f3fb9..0d51760e0f 100644 --- a/lms/djangoapps/grades/tests/integration/test_events.py +++ b/lms/djangoapps/grades/tests/integration/test_events.py @@ -1,5 +1,5 @@ """ -Test grading event across apps. +Test grading events across apps. """ # pylint: disable=protected-access