Example #1
0
def task_evaluate_subsection_completion_milestones(course_id, block_id,
                                                   user_id):
    """
    Updates users' milestones related to completion of a subsection.
     Args:
        course_id(str): Course id which triggered a completion event
        block_id(str): Id of the completed block
        user_id(int): Id of the user who completed a block
    """
    store = modulestore()
    course_key = CourseKey.from_string(course_id)
    with store.bulk_operations(course_key):
        course = store.get_course(course_key)
        if not course or not course.enable_subsection_gating:
            log.debug(
                "Gating: ignoring evaluation of completion milestone because it disabled for course [%s]",
                course_id)
        else:
            try:
                user = User.objects.get(id=user_id)
                course_structure = get_course_blocks(
                    user, store.make_course_usage_key(course_key))
                completed_block_usage_key = UsageKey.from_string(
                    block_id).map_into_course(course.id)
                subsection_block = _get_subsection_of_block(
                    completed_block_usage_key, course_structure)
                subsection = course_structure[subsection_block]
                log.debug(
                    "Gating: Evaluating completion milestone for subsection [%s] and user [%s]",
                    str(subsection.location), user.id)
                gating_api.evaluate_prerequisite(course, subsection, user)
            except KeyError:
                log.error(
                    "Gating: Given prerequisite subsection [%s] not found in course structure",
                    block_id)
Example #2
0
    def test_is_gate_fulfilled(self, min_score, min_completion, learner_score,
                               learner_completion, is_gate_fulfilled):
        """
        Test if prereq section has any unfulfilled milestones
        """
        student = UserFactory(is_staff=False)
        gating_api.add_prerequisite(self.course.id, self.seq1.location)
        gating_api.set_required_content(self.course.id, self.seq2.location,
                                        self.seq1.location, min_score,
                                        min_completion)
        milestone = milestones_api.add_milestone(self.generic_milestone)
        milestones_api.add_course_content_milestone(self.course.id,
                                                    self.seq1.location,
                                                    'fulfills', milestone)

        self.assertFalse(
            gating_api.is_gate_fulfilled(self.course.id, self.seq1.location,
                                         student.id))

        # complete the prerequisite to unlock the gated content
        # this call triggers reevaluation of prerequisites fulfilled by the gating block.
        with patch.object(
                gating_api,
                'get_subsection_completion_percentage') as mock_grade:
            mock_grade.return_value = learner_completion
            lms_gating_api.evaluate_prerequisite(
                self.course,
                Mock(location=self.seq1.location,
                     percent_graded=learner_score / 100.0),
                student,
            )
            self.assertEqual(
                gating_api.is_gate_fulfilled(self.course.id,
                                             self.seq1.location, student.id),
                is_gate_fulfilled)
Example #3
0
    def test_is_gate_fulfilled(self, min_score, min_completion, learner_score, learner_completion, is_gate_fulfilled):
        """
        Test if prereq section has any unfulfilled milestones
        """
        student = UserFactory(is_staff=False)
        gating_api.add_prerequisite(self.course.id, self.seq1.location)
        gating_api.set_required_content(
            self.course.id, self.seq2.location, self.seq1.location, min_score, min_completion
        )
        milestone = milestones_api.add_milestone(self.generic_milestone)
        milestones_api.add_course_content_milestone(self.course.id, self.seq1.location, 'fulfills', milestone)

        self.assertFalse(gating_api.is_gate_fulfilled(self.course.id, self.seq1.location, student.id))

        # complete the prerequisite to unlock the gated content
        # this call triggers reevaluation of prerequisites fulfilled by the gating block.
        with patch.object(gating_api, 'get_subsection_completion_percentage') as mock_grade:
            mock_grade.return_value = learner_completion
            lms_gating_api.evaluate_prerequisite(
                self.course,
                Mock(location=self.seq1.location, percent_graded=learner_score / 100.0),
                student,
            )
            self.assertEqual(
                gating_api.is_gate_fulfilled(self.course.id, self.seq1.location, student.id), is_gate_fulfilled
            )
Example #4
0
    def test_min_score_achieved(self, min_score, min_completion, module_score,
                                module_completion, result, mock_completion):
        self._setup_gating_milestone(min_score, min_completion)
        mock_completion.return_value = module_completion
        self.subsection_grade.percent_graded = module_score / 100.0

        evaluate_prerequisite(self.course, self.subsection_grade, self.user)
        assert milestones_api.user_has_milestone(
            self.user_dict, self.prereq_milestone) == result
Example #5
0
    def test_invalid_min_score(self, module_score, module_completion, result,
                               mock_min_score, mock_completion):
        self._setup_gating_milestone(None, None)
        mock_completion.return_value = module_completion
        self.subsection_grade.percent_graded = module_score / 100.0
        mock_min_score.return_value = 100, 100

        evaluate_prerequisite(self.course, self.subsection_grade, self.user)
        self.assertEqual(
            milestones_api.user_has_milestone(self.user_dict,
                                              self.prereq_milestone), result)
Example #6
0
def evaluate_subsection_gated_milestones(**kwargs):
    """
    Receives the SUBSECTION_SCORE_CHANGED signal and triggers the
    evaluation of any milestone relationships which are attached
    to the subsection.

    Arguments:
        kwargs (dict): Contains user, course, course_structure, subsection_grade
    Returns:
        None
    """
    subsection_grade = kwargs['subsection_grade']
    gating_api.evaluate_prerequisite(kwargs['course'], subsection_grade,
                                     kwargs.get('user'))
Example #7
0
    def test_content_unlocked(self):
        """
        Test that a sequential/subsection with met prereqs correctly indicated that its content is unlocked
        """
        course = self.course
        self.setup_gated_section(self.course_blocks['gated_content'],
                                 self.course_blocks['prerequisite'])

        # complete the prerequisite to unlock the gated content
        # this call triggers reevaluation of prerequisites fulfilled by the gating block.
        with patch(
                'openedx.core.lib.gating.api.get_subsection_completion_percentage',
                Mock(return_value=100)):
            lms_gating_api.evaluate_prerequisite(
                self.course,
                Mock(location=self.course_blocks['prerequisite'].location,
                     percent_graded=1.0),
                self.user,
            )

        response = self.client.get(course_home_url(course))
        self.assertEqual(response.status_code, 200)

        response_content = pq(response.content)

        # check unlock icon is not present
        unlock_icon = response_content('.fa-unlock')
        self.assertFalse(unlock_icon,
                         "unlock icon is present, yet shouldn't be.")

        gated_subsection_title = self.course_blocks[
            'gated_content'].display_name
        every_subsection_on_outline = response_content('.subsection-title')

        subsection_has_gated_text = False
        says_prerequisite_required = False

        for subsection_contents in every_subsection_on_outline.contents():
            subsection_has_gated_text = gated_subsection_title in subsection_contents
            says_prerequisite_required = "Prerequisite:" in subsection_contents

        # check that subsection-title-name is the display name of gated content section
        self.assertTrue(subsection_has_gated_text)
        self.assertFalse(says_prerequisite_required)
Example #8
0
    def test_special_exams(self):
        """
        When the block structure transformers are set to allow users to view special exams,
        ensure that we can see the special exams and not any of the otherwise gated blocks.
        """
        self.transformers = BlockStructureTransformers(
            [self.TRANSFORMER_CLASS_TO_TEST(True)])
        self.course.enable_subsection_gating = True
        self.setup_gated_section(self.blocks['H'], self.blocks['A'])
        expected_blocks = ('course', 'A', 'B', 'C', 'ProctoredExam', 'D', 'E',
                           'PracticeExam', 'F', 'G', 'TimedExam', 'J', 'K',
                           'H', 'I')
        self.get_blocks_and_check_against_expected(self.user, expected_blocks)
        # clear the request cache to simulate a new request
        self.clear_caches()

        # this call triggers reevaluation of prerequisites fulfilled by the gating block.
        lms_gating_api.evaluate_prerequisite(
            self.course,
            Mock(location=self.blocks['A'].location, percent_graded=1.0),
            self.user,
        )
        self.get_blocks_and_check_against_expected(self.user, self.ALL_BLOCKS)
Example #9
0
    def test_gated(self, gated_block_ref, gating_block_ref,
                   expected_blocks_before_completion):
        """
        Students should be able to see gated content blocks before and after they have completed the
        prerequisite for it.

        First, checks that a student can see the gated block when it is gated by the gating block and no
        attempt has been made to complete the gating block.
        Then, checks that the student can see the gated block after the gating block has been completed.

        expected_blocks_before_completion is the set of blocks we expect to be visible to the student
        before the student has completed the gating block.

        The test data includes one special exam and one non-special block as the gating blocks.
        """
        self.course.enable_subsection_gating = True
        self.setup_gated_section(self.blocks[gated_block_ref],
                                 self.blocks[gating_block_ref])

        with self.assertNumQueries(6):
            self.get_blocks_and_check_against_expected(
                self.user, expected_blocks_before_completion)

        # clear the request cache to simulate a new request
        self.clear_caches()

        # this call triggers reevaluation of prerequisites fulfilled by the gating block.
        lms_gating_api.evaluate_prerequisite(
            self.course,
            Mock(location=self.blocks[gating_block_ref].location,
                 percent_graded=1.0),
            self.user,
        )

        with self.assertNumQueries(6):
            self.get_blocks_and_check_against_expected(
                self.user, self.ALL_BLOCKS_EXCEPT_SPECIAL)
Example #10
0
    def test_no_gated_content(self, mock_score):
        gating_api.add_prerequisite(self.course.id, self.seq1.location)

        evaluate_prerequisite(self.course, self.subsection_grade, self.user)
        assert not mock_score.called
Example #11
0
 def test_no_prerequisites(self, mock_score):
     evaluate_prerequisite(self.course, self.subsection_grade, self.user)
     assert not mock_score.called