Beispiel #1
0
    def setUp(self):
        """
        Initial data setup
        """
        super(TestHandleItemDeleted, self).setUp()

        self.course = CourseFactory.create()
        self.course.enable_subsection_gating = True
        self.course.save()
        self.chapter = ItemFactory.create(
            parent=self.course,
            category="chapter",
            display_name="Chapter"
        )
        self.open_seq = ItemFactory.create(
            parent=self.chapter,
            category='sequential',
            display_name="Open Sequential"
        )
        self.gated_seq = ItemFactory.create(
            parent=self.chapter,
            category='sequential',
            display_name="Gated Sequential"
        )
        gating_api.add_prerequisite(self.course.id, self.open_seq.location)
        gating_api.set_required_content(self.course.id, self.gated_seq.location, self.open_seq.location, 100)
Beispiel #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
            )
Beispiel #3
0
 def _setup_gating_milestone(self, min_score):
     """
     Setup a gating milestone for testing
     """
     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)
     self.prereq_milestone = gating_api.get_gating_milestone(self.course.id, self.seq1.location, 'fulfills')
Beispiel #4
0
    def test_get_gating_milestone_is_none(self):
        """ Test test_get_gating_milestone_is_none """

        gating_api.add_prerequisite(self.course.id, self.seq1.location)
        gating_api.set_required_content(self.course.id, self.seq2.location, self.seq1.location, 100)

        self.assertIsNone(gating_api.get_gating_milestone(self.course.id, self.seq1.location, 'requires'))
        self.assertIsNone(gating_api.get_gating_milestone(self.course.id, self.seq2.location, 'fulfills'))
 def setup_gating_milestone(self, min_score):
     """
     Setup a gating milestone for testing.
     Gating content: seq1 (must be fulfilled before access to seq2)
     Gated content: seq2 (requires completion of seq1 before access)
     """
     gating_api.add_prerequisite(self.course.id, str(self.seq1.location))
     gating_api.set_required_content(self.course.id, str(self.seq2.location), str(self.seq1.location), min_score)
     self.prereq_milestone = gating_api.get_gating_milestone(self.course.id, self.seq1.location, 'fulfills')
 def setup_gated_section(self, gated_block, gating_block):
     """
     Test helper to create a gating requirement.
     Args:
         gated_block: The block that should be inaccessible until gating_block is completed
         gating_block: The block that must be completed before access is granted
     """
     gating_api.add_prerequisite(self.course.id, unicode(gating_block.location))
     gating_api.set_required_content(self.course.id, gated_block.location, gating_block.location, 100)
    def setup_gated_section(self, gated_block, gating_block):
        """
        Test helper to create a gating requirement
        Args:
            gated_block: The block the that learner will not have access to until they complete the gating block
            gating_block: (The prerequisite) The block that must be completed to get access to the gated block
        """

        gating_api.add_prerequisite(self.course.id, unicode(gating_block.location))
        gating_api.set_required_content(self.course.id, gated_block.location, gating_block.location, 100)
    def setup_gated_section(self, gated_block, gating_block):
        """
        Test helper to create a gating requirement
        Args:
            gated_block: The block the that learner will not have access to until they complete the gating block
            gating_block: (The prerequisite) The block that must be completed to get access to the gated block
        """

        gating_api.add_prerequisite(self.course.id,
                                    unicode(gating_block.location))
        gating_api.set_required_content(self.course.id, gated_block.location,
                                        gating_block.location, 100)
Beispiel #9
0
    def test_find_gating_milestones(self):
        """ Test test_find_gating_milestones """

        gating_api.add_prerequisite(self.course.id, self.seq1.location)
        gating_api.set_required_content(self.course.id, self.seq2.location, self.seq1.location, 100)
        milestone = milestones_api.add_milestone(self.generic_milestone)
        milestones_api.add_course_content_milestone(self.course.id, self.seq1.location, 'fulfills', milestone)

        self.assertEqual(len(gating_api.find_gating_milestones(self.course.id, self.seq1.location, 'fulfills')), 1)
        self.assertEqual(len(gating_api.find_gating_milestones(self.course.id, self.seq1.location, 'requires')), 0)
        self.assertEqual(len(gating_api.find_gating_milestones(self.course.id, self.seq2.location, 'fulfills')), 0)
        self.assertEqual(len(gating_api.find_gating_milestones(self.course.id, self.seq2.location, 'requires')), 1)
Beispiel #10
0
 def setup_gating_milestone(self, min_score):
     """
     Setup a gating milestone for testing.
     Gating content: seq1 (must be fulfilled before access to seq2)
     Gated content: seq2 (requires completion of seq1 before access)
     """
     gating_api.add_prerequisite(self.course.id, str(self.seq1.location))
     gating_api.set_required_content(self.course.id,
                                     str(self.seq2.location),
                                     str(self.seq1.location), min_score)
     self.prereq_milestone = gating_api.get_gating_milestone(
         self.course.id, self.seq1.location, 'fulfills')
Beispiel #11
0
    def test_find_gating_milestones(self):
        """ Test test_find_gating_milestones """

        gating_api.add_prerequisite(self.course.id, self.seq1.location)
        gating_api.set_required_content(self.course.id, self.seq2.location, self.seq1.location, 100)
        milestone = milestones_api.add_milestone(self.generic_milestone)
        milestones_api.add_course_content_milestone(self.course.id, self.seq1.location, 'fulfills', milestone)

        self.assertEqual(len(gating_api.find_gating_milestones(self.course.id, self.seq1.location, 'fulfills')), 1)
        self.assertEqual(len(gating_api.find_gating_milestones(self.course.id, self.seq1.location, 'requires')), 0)
        self.assertEqual(len(gating_api.find_gating_milestones(self.course.id, self.seq2.location, 'fulfills')), 0)
        self.assertEqual(len(gating_api.find_gating_milestones(self.course.id, self.seq2.location, 'requires')), 1)
Beispiel #12
0
    def test_get_gating_milestone_is_none(self):
        """ Test test_get_gating_milestone_is_none """

        gating_api.add_prerequisite(self.course.id, self.seq1.location)
        gating_api.set_required_content(self.course.id, self.seq2.location,
                                        self.seq1.location, 100)

        self.assertIsNone(
            gating_api.get_gating_milestone(self.course.id, self.seq1.location,
                                            'requires'))
        self.assertIsNone(
            gating_api.get_gating_milestone(self.course.id, self.seq2.location,
                                            'fulfills'))
Beispiel #13
0
    def test_required_content(self):
        """ Test test_required_content """

        gating_api.add_prerequisite(self.course.id, self.seq1.location)
        gating_api.set_required_content(self.course.id, self.seq2.location, self.seq1.location, 100)

        prereq_content_key, min_score = gating_api.get_required_content(self.course.id, self.seq2.location)
        self.assertEqual(prereq_content_key, unicode(self.seq1.location))
        self.assertEqual(min_score, 100)

        gating_api.set_required_content(self.course.id, self.seq2.location, None, None)

        prereq_content_key, min_score = gating_api.get_required_content(self.course.id, self.seq2.location)
        self.assertIsNone(prereq_content_key)
        self.assertIsNone(min_score)
Beispiel #14
0
    def test_get_gated_content(self):
        """ Test test_get_gated_content """

        mock_user = MagicMock()
        mock_user.id.return_value = 1

        self.assertEqual(gating_api.get_gated_content(self.course, mock_user), [])

        gating_api.add_prerequisite(self.course.id, self.seq1.location)
        gating_api.set_required_content(self.course.id, self.seq2.location, self.seq1.location, 100)
        milestone = milestones_api.get_course_content_milestones(self.course.id, self.seq2.location, 'requires')[0]

        self.assertEqual(gating_api.get_gated_content(self.course, mock_user), [unicode(self.seq2.location)])

        milestones_api.add_user_milestone({'id': mock_user.id}, milestone)

        self.assertEqual(gating_api.get_gated_content(self.course, mock_user), [])
Beispiel #15
0
    def test_compute_is_prereq_met(self):
        """
        Test if prereq has been met and force recompute
        """
        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, 100, 0)

        # 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_grade_percentage') as mock_grade:
            mock_grade.return_value = 75
            # don't force recompute
            prereq_met, prereq_meta_info = gating_api.compute_is_prereq_met(
                self.seq2.location, student.id, False)
            self.assertFalse(prereq_met)
            self.assertIsNone(prereq_meta_info['url'])
            self.assertIsNone(prereq_meta_info['display_name'])

            # force recompute
            prereq_met, prereq_meta_info = gating_api.compute_is_prereq_met(
                self.seq2.location, student.id, True)
            self.assertFalse(prereq_met)
            self.assertIsNotNone(prereq_meta_info['url'])
            self.assertIsNotNone(prereq_meta_info['display_name'])

            # change to passing grade
            mock_grade.return_value = 100

            # don't force recompute
            prereq_met, prereq_meta_info = gating_api.compute_is_prereq_met(
                self.seq2.location, student.id, False)
            self.assertFalse(prereq_met)
            self.assertIsNone(prereq_meta_info['url'])
            self.assertIsNone(prereq_meta_info['display_name'])

            # force recompute
            prereq_met, prereq_meta_info = gating_api.compute_is_prereq_met(
                self.seq2.location, student.id, True)
            self.assertTrue(prereq_met)
            self.assertIsNotNone(prereq_meta_info['url'])
            self.assertIsNotNone(prereq_meta_info['display_name'])
Beispiel #16
0
    def test_get_gated_content(self):
        """
        Verify staff bypasses gated content and student gets list of unfulfilled prerequisites.
        """

        staff = UserFactory(is_staff=True)
        student = UserFactory(is_staff=False)

        self.assertEqual(gating_api.get_gated_content(self.course, staff), [])
        self.assertEqual(gating_api.get_gated_content(self.course, student), [])

        gating_api.add_prerequisite(self.course.id, self.seq1.location)
        gating_api.set_required_content(self.course.id, self.seq2.location, self.seq1.location, 100)
        milestone = milestones_api.get_course_content_milestones(self.course.id, self.seq2.location, 'requires')[0]

        self.assertEqual(gating_api.get_gated_content(self.course, staff), [])
        self.assertEqual(gating_api.get_gated_content(self.course, student), [unicode(self.seq2.location)])

        milestones_api.add_user_milestone({'id': student.id}, milestone)  # pylint: disable=no-member

        self.assertEqual(gating_api.get_gated_content(self.course, student), [])
Beispiel #17
0
    def test_required_content(self):
        """ Test test_required_content """

        gating_api.add_prerequisite(self.course.id, self.seq1.location)
        gating_api.set_required_content(self.course.id, self.seq2.location,
                                        self.seq1.location, 100, 100)

        prereq_content_key, min_score, min_completion = gating_api.get_required_content(
            self.course.id, self.seq2.location)
        assert prereq_content_key == str(self.seq1.location)
        assert min_score == 100
        assert min_completion == 100

        gating_api.set_required_content(self.course.id, self.seq2.location,
                                        None, None, None)

        prereq_content_key, min_score, min_completion = gating_api.get_required_content(
            self.course.id, self.seq2.location)
        assert prereq_content_key is None
        assert min_score is None
        assert min_completion is None
Beispiel #18
0
    def test_get_gated_content(self):
        """
        Verify staff bypasses gated content and student gets list of unfulfilled prerequisites.
        """

        staff = UserFactory(is_staff=True)
        student = UserFactory(is_staff=False)

        self.assertEqual(gating_api.get_gated_content(self.course, staff), [])
        self.assertEqual(gating_api.get_gated_content(self.course, student), [])

        gating_api.add_prerequisite(self.course.id, self.seq1.location)
        gating_api.set_required_content(self.course.id, self.seq2.location, self.seq1.location, 100)
        milestone = milestones_api.get_course_content_milestones(self.course.id, self.seq2.location, 'requires')[0]

        self.assertEqual(gating_api.get_gated_content(self.course, staff), [])
        self.assertEqual(gating_api.get_gated_content(self.course, student), [unicode(self.seq2.location)])

        milestones_api.add_user_milestone({'id': student.id}, milestone)

        self.assertEqual(gating_api.get_gated_content(self.course, student), [])
    def test_is_gate_fulfilled(self):
        """
        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, 100)
        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_percentage') as mock_grade:
            mock_grade.return_value = 75
            lms_gating_api.evaluate_prerequisite(
                self.course,
                Mock(location=self.seq1.location),
                student,
            )
            self.assertFalse(
                gating_api.is_gate_fulfilled(self.course.id,
                                             self.seq1.location, student.id))

            mock_grade.return_value = 100
            lms_gating_api.evaluate_prerequisite(
                self.course,
                Mock(location=self.seq1.location),
                student,
            )
            self.assertTrue(
                gating_api.is_gate_fulfilled(self.course.id,
                                             self.seq1.location, student.id))
    def setUp(self):
        """
        Initial data setup
        """
        super(TestHandleItemDeleted, self).setUp()

        self.course = CourseFactory.create()
        self.course.enable_subsection_gating = True
        self.course.save()
        self.chapter = ItemFactory.create(parent=self.course,
                                          category="chapter",
                                          display_name="Chapter")
        self.open_seq = ItemFactory.create(parent=self.chapter,
                                           category='sequential',
                                           display_name="Open Sequential")
        self.gated_seq = ItemFactory.create(parent=self.chapter,
                                            category='sequential',
                                            display_name="Gated Sequential")
        gating_api.add_prerequisite(self.course.id, self.open_seq.location)
        gating_api.set_required_content(self.course.id,
                                        self.gated_seq.location,
                                        self.open_seq.location, 100, 100)
Beispiel #21
0
    def test_compute_is_prereq_met(self):
        """
        Test if prereq has been met and force recompute
        """
        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, 100, 0)

        # 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_grade_percentage') as mock_grade:
            mock_grade.return_value = 75
            # don't force recompute
            prereq_met, prereq_meta_info = gating_api.compute_is_prereq_met(self.seq2.location, student.id, False)
            self.assertFalse(prereq_met)
            self.assertIsNone(prereq_meta_info['url'])
            self.assertIsNone(prereq_meta_info['display_name'])

            # force recompute
            prereq_met, prereq_meta_info = gating_api.compute_is_prereq_met(self.seq2.location, student.id, True)
            self.assertFalse(prereq_met)
            self.assertIsNotNone(prereq_meta_info['url'])
            self.assertIsNotNone(prereq_meta_info['display_name'])

            # change to passing grade
            mock_grade.return_value = 100

            # don't force recompute
            prereq_met, prereq_meta_info = gating_api.compute_is_prereq_met(self.seq2.location, student.id, False)
            self.assertFalse(prereq_met)
            self.assertIsNone(prereq_meta_info['url'])
            self.assertIsNone(prereq_meta_info['display_name'])

            # force recompute
            prereq_met, prereq_meta_info = gating_api.compute_is_prereq_met(self.seq2.location, student.id, True)
            self.assertTrue(prereq_met)
            self.assertIsNotNone(prereq_meta_info['url'])
            self.assertIsNotNone(prereq_meta_info['display_name'])
Beispiel #22
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)

        assert not 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,
            )
            assert gating_api.is_gate_fulfilled(self.course.id, self.seq1.location, student.id) == is_gate_fulfilled
Beispiel #23
0
def handle_item_deleted(**kwargs):
    """
    Receives the item_deleted signal sent by Studio when an XBlock is removed from
    the course structure and removes any gating milestone data associated with it or
    its descendants.

    Arguments:
        kwargs (dict): Contains the content usage key of the item deleted

    Returns:
        None
    """

    usage_key = kwargs.get('usage_key')
    if usage_key:
        # Strip branch info
        usage_key = usage_key.for_branch(None)
        course_key = usage_key.course_key
        deleted_module = modulestore().get_item(usage_key)
        for module in yield_dynamic_descriptor_descendants(deleted_module, kwargs.get('user_id')):
            # Remove prerequisite milestone data
            gating_api.remove_prerequisite(module.location)
            # Remove any 'requires' course content milestone relationships
            gating_api.set_required_content(course_key, module.location, None, None, None)
Beispiel #24
0
def handle_item_deleted(**kwargs):
    """
    Receives the item_deleted signal sent by Studio when an XBlock is removed from
    the course structure and removes any gating milestone data associated with it or
    its descendants.

    Arguments:
        kwargs (dict): Contains the content usage key of the item deleted

    Returns:
        None
    """

    usage_key = kwargs.get('usage_key')
    if usage_key:
        # Strip branch info
        usage_key = usage_key.for_branch(None)
        course_key = usage_key.course_key
        deleted_module = modulestore().get_item(usage_key)
        for module in yield_dynamic_descriptor_descendants(deleted_module, kwargs.get('user_id')):
            # Remove prerequisite milestone data
            gating_api.remove_prerequisite(module.location)
            # Remove any 'requires' course content milestone relationships
            gating_api.set_required_content(course_key, module.location, None, None)