Example #1
0
 def test_fetch_course_content_milestones_invalid_milestone_relationship_type(self):
     """ Unit Test: test_fetch_course_content_milestones_invalid_milestone_relationship_type"""
     milestone1 = api.add_milestone({
         'display_name': 'Test Milestone',
         'name': 'test_milestone',
         'namespace': unicode(self.test_course_key),
         'description': 'Test Milestone Description',
     })
     api.add_course_content_milestone(
         self.test_course_key,
         self.test_content_key,
         self.relationship_types['REQUIRES'],
         milestone1
     )
     milestone2 = api.add_milestone({
         'display_name': 'Test Milestone 2',
         'name': 'test_milestone2',
         'namespace': unicode(self.test_course_key),
         'description': 'Test Milestone Description 2',
     })
     api.add_course_content_milestone(
         self.test_course_key,
         self.test_content_key,
         self.relationship_types['FULFILLS'],
         milestone2
     )
     with self.assertRaises(exceptions.InvalidMilestoneRelationshipTypeException):
         data.fetch_course_content_milestones(
             self.test_course_key,
             self.test_content_key,
             'invalid_relationshipppp'
         )
Example #2
0
 def test_fetch_courses_milestones_invalid_milestone_relationship_type(
         self):
     """ Unit Test: test_fetch_courses_milestones_invalid_milestone_relationship_type"""
     milestone1 = api.add_milestone({
         'display_name':
         'Test Milestone',
         'name':
         'test_milestone',
         'namespace':
         six.text_type(self.test_course_key),
         'description':
         'Test Milestone Description',
     })
     api.add_course_milestone(self.test_course_key,
                              self.relationship_types['REQUIRES'],
                              milestone1)
     milestone2 = api.add_milestone({
         'display_name':
         'Test Milestone 2',
         'name':
         'test_milestone_2',
         'namespace':
         six.text_type(self.test_course_key),
         'description':
         'Test Milestone Description 2',
     })
     api.add_course_milestone(self.test_course_key,
                              self.relationship_types['FULFILLS'],
                              milestone2)
     with self.assertRaises(
             exceptions.InvalidMilestoneRelationshipTypeException):
         data.fetch_courses_milestones([
             self.test_course_key,
         ], 'invalid_relationshipppp', milestone1)
Example #3
0
    def test_add_milestone_inactive_milestone_with_relationships_propagate_false(self):
        """ Unit Test: test_add_milestone_inactive_milestone_with_relationships_propagate_false"""
        milestone_data = {
            'name': 'local_milestone',
            'display_name': 'Local Milestone',
            'namespace': six.text_type(self.test_course_key),
            'description': 'Local Milestone Description'
        }
        milestone = api.add_milestone(milestone_data)
        api.add_course_milestone(
            self.test_course_key,
            self.relationship_types['REQUIRES'],
            milestone
        )
        api.add_course_content_milestone(
            self.test_course_key,
            self.test_content_key,
            self.relationship_types['FULFILLS'],
            milestone
        )
        api.add_user_milestone(self.serialized_test_user, milestone)
        self.assertGreater(milestone['id'], 0)
        api.remove_milestone(milestone['id'])

        with self.assertNumQueries(3):
            milestone = api.add_milestone(milestone_data, propagate=False)
Example #4
0
 def test_add_milestone_active_exists(self):
     """ Unit Test: test_add_milestone_active_exists"""
     milestone_data = {
         'name': 'local_milestone',
         'display_name': 'Local Milestone',
         'namespace': six.text_type(self.test_course_key),
         'description': 'Local Milestone Description'
     }
     milestone = api.add_milestone(milestone_data)
     self.assertGreater(milestone['id'], 0)
     with self.assertNumQueries(1):
         milestone = api.add_milestone(milestone_data)
Example #5
0
 def test_add_milestone_active_exists(self):
     """ Unit Test: test_add_milestone_active_exists"""
     milestone_data = {
         'name': 'local_milestone',
         'display_name': 'Local Milestone',
         'namespace': unicode(self.test_course_key),
         'description': 'Local Milestone Description'
     }
     milestone = api.add_milestone(milestone_data)
     self.assertGreater(milestone['id'], 0)
     with self.assertNumQueries(1):
         milestone = api.add_milestone(milestone_data)
Example #6
0
    def test_add_milestone_inactive_to_active(self):
        """ Unit Test: test_add_milestone_inactive_milestone_present"""
        milestone_data = {
            'name': 'local_milestone',
            'display_name': 'Local Milestone',
            'namespace': unicode(self.test_course_key),
            'description': 'Local Milestone Description'
        }
        milestone = api.add_milestone(milestone_data)
        self.assertGreater(milestone['id'], 0)
        api.remove_milestone(milestone['id'])

        with self.assertNumQueries(5):
            milestone = api.add_milestone(milestone_data)
Example #7
0
    def test_add_milestone_invalid_data_throws_exceptions(self):
        """ Unit Test: test_add_milestone_invalid_namespaces_throw_exceptions"""
        with self.assertNumQueries(0):
            with self.assertRaises(exceptions.InvalidMilestoneException):
                api.add_milestone({
                    'name': 'local_milestone',
                    'display_name': 'Local Milestone',
                    'namespace': '',  # Should throw an exception
                    'description': 'Local Milestone Description'
                })

        with self.assertNumQueries(0):
            with self.assertRaises(exceptions.InvalidMilestoneException):
                api.add_milestone({
                    'name': 'Local Milestone',  # Missing namespace should throw exception
                    'description': 'Local Milestone Description'
                })

        with self.assertNumQueries(0):
            with self.assertRaises(exceptions.InvalidMilestoneException):
                api.add_milestone({
                    'name': '',  # Empty name should throw an exception on create
                    'namespace': 'fixed',
                    'description': 'Local Milestone Description 2'
                })

        with self.assertNumQueries(0):
            with self.assertRaises(exceptions.InvalidMilestoneException):
                api.add_milestone({
                    'namespace': 'fixed',  # Missing name should throw exception
                    'description': 'Local Milestone Description 2'
                })
def add_milestone(milestone_data):
    """
    Client API operation adapter/wrapper
    """
    if not settings.FEATURES.get('MILESTONES_APP'):
        return None
    return milestones_api.add_milestone(milestone_data)
def add_prerequisite_course(course_key, prerequisite_course_key):
    """
    It would create a milestone, then it would set newly created
    milestones as requirement for course referred by `course_key`
    and it would set newly created milestone as fulfilment
    milestone for course referred by `prerequisite_course_key`.
    """
    if not settings.FEATURES.get('ENABLE_PREREQUISITE_COURSES', False):
        return None
    from milestones import api as milestones_api
    milestone_name = _(
        'Course {course_id} requires {prerequisite_course_id}').format(
            course_id=unicode(course_key),
            prerequisite_course_id=unicode(prerequisite_course_key))
    milestone = milestones_api.add_milestone({
        'name':
        milestone_name,
        'namespace':
        unicode(prerequisite_course_key),
        'description':
        _('System defined milestone'),
    })
    # add requirement course milestone
    milestones_api.add_course_milestone(course_key, 'requires', milestone)

    # add fulfillment course milestone
    milestones_api.add_course_milestone(prerequisite_course_key, 'fulfills',
                                        milestone)
Example #10
0
def add_milestone(milestone_data):
    """
    Client API operation adapter/wrapper
    """
    if not ENABLE_MILESTONES_APP.is_enabled():
        return None
    return milestones_api.add_milestone(milestone_data)
Example #11
0
    def test_fetch_course_content_milestones_null_keys(self):
        """ Unit Test: test_fetch_course_content_milestones_null_keys"""
        namespace = '{}.entrance_exams'.format(unicode(self.test_course_key))
        milestone1 = api.add_milestone({
            'display_name': 'Test Milestone',
            'name': 'test_milestone',
            'namespace': namespace,
            'description': 'Test Milestone Description',
        })
        api.add_course_content_milestone(
            self.test_course_key,
            self.test_content_key,
            self.relationship_types['REQUIRES'],
            milestone1
        )
        milestones = data.fetch_milestones(milestone={'namespace': namespace})
        self.assertEqual(len(milestones), 1)

        ccms = data.fetch_course_content_milestones(
            content_key=self.test_content_key,
            course_key=None,
            relationship=None
        )
        self.assertEqual(len(ccms), 1)

        ccms = data.fetch_course_content_milestones(
            content_key=None,
            course_key=self.test_course_key,
            relationship=None
        )
        self.assertEqual(len(ccms), 1)
def add_milestone(milestone_data):
    """
    Client API operation adapter/wrapper
    """
    if not settings.FEATURES.get('MILESTONES_APP'):
        return None
    return milestones_api.add_milestone(milestone_data)
Example #13
0
def add_prerequisite(course_key, prereq_content_key):
    """
    Creates a new Milestone and CourseContentMilestone indicating that
    the given course content fulfills a prerequisite for gating

    Arguments:
        course_key (str|CourseKey): The course key
        prereq_content_key (str|UsageKey): The prerequisite content usage key

    Returns:
        None
    """
    milestone = milestones_api.add_milestone(
        {
            'name':
            _('Gating milestone for {usage_key}').format(
                usage_key=unicode(prereq_content_key)),
            'namespace':
            "{usage_key}{qualifier}".format(
                usage_key=prereq_content_key,
                qualifier=GATING_NAMESPACE_QUALIFIER),
            'description':
            _('System defined milestone'),
        },
        propagate=False)
    milestones_api.add_course_content_milestone(course_key, prereq_content_key,
                                                'fulfills', milestone)
Example #14
0
    def test_get_courses_milestones_with_invalid_relationship_type(self):
        """ Unit Test: test_get_courses_milestones_with_invalid_relationship_type """
        api.add_course_milestone(
            self.test_course_key,
            self.relationship_types['REQUIRES'],
            self.test_milestone
        )
        api.add_course_milestone(
            self.test_prerequisite_course_key,
            self.relationship_types['REQUIRES'],
            self.test_milestone
        )
        local_milestone = api.add_milestone({
            'display_name': 'Local Milestone',
            'name': 'local_milestone',
            'namespace': six.text_type(self.test_course_key),
            'description': 'Local Milestone Description'
        })
        api.add_course_milestone(
            self.test_course_key,
            self.relationship_types['FULFILLS'],
            local_milestone
        )

        with self.assertNumQueries(1):
            requirer_milestones = api.get_courses_milestones(
                [self.test_course_key],
                'INVALID RELATIONSHIP TYPE'
            )
        self.assertEqual(len(requirer_milestones), 0)
Example #15
0
 def test_get_course_tabs_list_entrance_exam_enabled(self):
     """
     Unit Test: test_get_course_tabs_list_entrance_exam_enabled
     """
     entrance_exam = ItemFactory.create(
         category="chapter",
         parent_location=self.course.location,
         data="Exam Data",
         display_name="Entrance Exam")
     entrance_exam.is_entrance_exam = True
     milestone = {
         'name': 'Test Milestone',
         'namespace':
         '{}.entrance_exams'.format(unicode(self.course.id)),
         'description': 'Testing Courseware Tabs'
     }
     self.course.entrance_exam_enabled = True
     self.course.entrance_exam_id = unicode(entrance_exam.location)
     milestone = milestones_api.add_milestone(milestone)
     milestones_api.add_course_milestone(
         unicode(self.course.id), self.relationship_types['REQUIRES'],
         milestone)
     milestones_api.add_course_content_milestone(
         unicode(self.course.id), unicode(entrance_exam.location),
         self.relationship_types['FULFILLS'], milestone)
     course_tab_list = get_course_tab_list(self.course, self.user)
     self.assertEqual(len(course_tab_list), 2)
     self.assertEqual(course_tab_list[0]['tab_id'], 'courseware')
     self.assertEqual(course_tab_list[0]['name'], 'Entrance Exam')
     self.assertEqual(course_tab_list[1]['tab_id'], 'instructor')
Example #16
0
    def test_get_courses_milestones(self):
        """ Unit Test: test_get_courses_milestones """
        api.add_course_milestone(
            self.test_course_key,
            self.relationship_types['REQUIRES'],
            self.test_milestone
        )
        api.add_course_milestone(
            self.test_prerequisite_course_key,
            self.relationship_types['REQUIRES'],
            self.test_milestone
        )
        local_milestone = api.add_milestone({
            'display_name': 'Local Milestone',
            'name': 'local_milestone',
            'namespace': unicode(self.test_course_key),
            'description': 'Local Milestone Description'
        })
        api.add_course_milestone(
            self.test_course_key,
            self.relationship_types['FULFILLS'],
            local_milestone
        )
        with self.assertNumQueries(2):
            requirer_milestones = api.get_courses_milestones(
                [self.test_course_key, self.test_prerequisite_course_key],
                self.relationship_types['REQUIRES']
            )
        self.assertEqual(len(requirer_milestones), 2)

        with self.assertNumQueries(1):
            requirer_milestones = api.get_courses_milestones(
                [self.test_course_key],
            )
        self.assertEqual(len(requirer_milestones), 2)
def add_prerequisite_course(course_key, prerequisite_course_key):
    """
    It would create a milestone, then it would set newly created
    milestones as requirement for course referred by `course_key`
    and it would set newly created milestone as fulfillment
    milestone for course referred by `prerequisite_course_key`.
    """
    if not is_prerequisite_courses_enabled():
        return None
    milestone_name = _(
        'Course {course_id} requires {prerequisite_course_id}').format(
            course_id=unicode(course_key),
            prerequisite_course_id=unicode(prerequisite_course_key))
    milestone = milestones_api.add_milestone({
        'name':
        milestone_name,
        'namespace':
        unicode(prerequisite_course_key),
        'description':
        _('System defined milestone'),
    })
    # add requirement course milestone
    milestones_api.add_course_milestone(course_key, 'requires', milestone)

    # add fulfillment course milestone
    milestones_api.add_course_milestone(prerequisite_course_key, 'fulfills',
                                        milestone)
Example #18
0
    def test_milestones_fulfillment_paths_contains_special_characters(self):
        """
        Unit Test: test_get_course_milestones_fulfillment_paths works correctly when milestone have some special
        characters.
        """
        namespace = unicode(self.test_course_key)
        name = '�ťÉśt_Àübùr�'
        local_milestone_1 = api.add_milestone({
            'display_name':
            'Local Milestone 1',
            'name':
            name,
            'namespace':
            namespace,
            'description':
            'Local Milestone 1 Description'
        })

        # Specify the milestone requirements
        api.add_course_milestone(self.test_course_key,
                                 self.relationship_types['REQUIRES'],
                                 local_milestone_1)

        # Specify the milestone fulfillments (via course and content)
        api.add_course_milestone(self.test_prerequisite_course_key,
                                 self.relationship_types['FULFILLS'],
                                 local_milestone_1)
        with self.assertNumQueries(4):
            paths = api.get_course_milestones_fulfillment_paths(
                self.test_course_key, self.serialized_test_user)

        # Set up the key values we'll use to access/assert the response
        milestone_key_1 = '{}.{}'.format(local_milestone_1['namespace'],
                                         local_milestone_1['name'])
        self.assertEqual(len(paths[milestone_key_1]['courses']), 1)
Example #19
0
    def test_fetch_course_content_milestones_null_keys(self):
        """ Unit Test: test_fetch_course_content_milestones_null_keys"""
        namespace = '{}.entrance_exams'.format(
            six.text_type(self.test_course_key))
        milestone1 = api.add_milestone({
            'display_name':
            'Test Milestone',
            'name':
            'test_milestone',
            'namespace':
            namespace,
            'description':
            'Test Milestone Description',
        })
        api.add_course_content_milestone(self.test_course_key,
                                         self.test_content_key,
                                         self.relationship_types['REQUIRES'],
                                         milestone1)
        milestones = data.fetch_milestones(milestone={'namespace': namespace})
        self.assertEqual(len(milestones), 1)

        ccms = data.fetch_course_content_milestones(
            content_key=self.test_content_key,
            course_key=None,
            relationship=None)
        self.assertEqual(len(ccms), 1)

        ccms = data.fetch_course_content_milestones(
            content_key=None,
            course_key=self.test_course_key,
            relationship=None)
        self.assertEqual(len(ccms), 1)
Example #20
0
    def test_get_courses_milestones(self):
        """ Unit Test: test_get_courses_milestones """
        api.add_course_milestone(self.test_course_key,
                                 self.relationship_types['REQUIRES'],
                                 self.test_milestone)
        api.add_course_milestone(self.test_prerequisite_course_key,
                                 self.relationship_types['REQUIRES'],
                                 self.test_milestone)
        local_milestone = api.add_milestone({
            'display_name':
            'Local Milestone',
            'name':
            'local_milestone',
            'namespace':
            unicode(self.test_course_key),
            'description':
            'Local Milestone Description'
        })
        api.add_course_milestone(self.test_course_key,
                                 self.relationship_types['FULFILLS'],
                                 local_milestone)
        with self.assertNumQueries(2):
            requirer_milestones = api.get_courses_milestones(
                [self.test_course_key, self.test_prerequisite_course_key],
                self.relationship_types['REQUIRES'])
        self.assertEqual(len(requirer_milestones), 2)

        with self.assertNumQueries(1):
            requirer_milestones = api.get_courses_milestones(
                [self.test_course_key], )
        self.assertEqual(len(requirer_milestones), 2)
Example #21
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)
Example #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)

        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 #23
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 #24
0
 def test_get_course_tabs_list_entrance_exam_enabled(self):
     """
     Unit Test: test_get_course_tabs_list_entrance_exam_enabled
     """
     entrance_exam = ItemFactory.create(
         category="chapter", parent_location=self.course.location,
         data="Exam Data", display_name="Entrance Exam"
     )
     entrance_exam.is_entrance_exam = True
     milestone = {
         'name': 'Test Milestone',
         'namespace': '{}.entrance_exams'.format(unicode(self.course.id)),
         'description': 'Testing Courseware Tabs'
     }
     self.course.entrance_exam_enabled = True
     self.course.entrance_exam_id = unicode(entrance_exam.location)
     milestone = milestones_api.add_milestone(milestone)
     milestones_api.add_course_milestone(
         unicode(self.course.id),
         self.relationship_types['REQUIRES'],
         milestone
     )
     milestones_api.add_course_content_milestone(
         unicode(self.course.id),
         unicode(entrance_exam.location),
         self.relationship_types['FULFILLS'],
         milestone
     )
     course_tab_list = get_course_tab_list(self.course, self.user)
     self.assertEqual(len(course_tab_list), 2)
     self.assertEqual(course_tab_list[0]['tab_id'], 'courseware')
     self.assertEqual(course_tab_list[0]['name'], 'Entrance Exam')
     self.assertEqual(course_tab_list[1]['tab_id'], 'instructor')
Example #25
0
def add_milestone(milestone_data):
    """
    Client API operation adapter/wrapper
    """
    if not settings.FEATURES.get('MILESTONES_APP', False):
        return None
    from milestones import api as milestones_api
    return milestones_api.add_milestone(milestone_data)
def add_milestone(milestone_data):
    """
    Client API operation adapter/wrapper
    """
    if not settings.FEATURES.get('MILESTONES_APP', False):
        return None
    from milestones import api as milestones_api
    return milestones_api.add_milestone(milestone_data)
Example #27
0
 def test_get_milestones(self):
     """ Unit Test: test_get_milestones """
     namespace = 'test_get_milestones'
     api.add_milestone({
         'display_name': 'Local Milestone 1',
         'name': 'local_milestone_1',
         'namespace': namespace,
         'description': 'Local Milestone 1 Description'
     })
     api.add_milestone({
         'display_name': 'Local Milestone 2',
         'name': 'local_milestone_2',
         'namespace': namespace,
         'description': 'Local Milestone 2 Description'
     })
     with self.assertNumQueries(1):
         milestones = api.get_milestones(namespace=namespace)
     self.assertEqual(len(milestones), 2)
Example #28
0
 def test_get_milestones(self):
     """ Unit Test: test_get_milestones """
     namespace = 'test_get_milestones'
     api.add_milestone({
         'display_name': 'Local Milestone 1',
         'name': 'local_milestone_1',
         'namespace': namespace,
         'description': 'Local Milestone 1 Description'
     })
     api.add_milestone({
         'display_name': 'Local Milestone 2',
         'name': 'local_milestone_2',
         'namespace': namespace,
         'description': 'Local Milestone 2 Description'
     })
     with self.assertNumQueries(1):
         milestones = api.get_milestones(namespace=namespace)
     self.assertEqual(len(milestones), 2)
Example #29
0
 def test_add_milestone(self):
     """ Unit Test: test_add_milestone"""
     with self.assertNumQueries(2):
         milestone = api.add_milestone({
             'name': 'local_milestone',
             'display_name': 'Local Milestone',
             'namespace': six.text_type(self.test_course_key),
             'description': 'Local Milestone Description'
         })
     self.assertGreater(milestone['id'], 0)
Example #30
0
 def test_fetch_milestone_courses_no_relationship_type(self):
     """ Unit Test: test_fetch_milestone_courses_no_relationship_type"""
     milestone1 = api.add_milestone({
         'display_name': 'Test Milestone',
         'name': 'test_milestone',
         'namespace': unicode(self.test_course_key),
         'description': 'Test Milestone Description',
     })
     api.add_course_milestone(self.test_course_key, 'fulfills', milestone1)
     self.assertEqual(len(data.fetch_milestone_courses(milestone1)), 1)
Example #31
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)
Example #32
0
 def setUp(self):
     """
     Milestones API Test Case scaffolding
     """
     super(MilestonesApiTestCase, self).setUp()
     self.test_milestone = api.add_milestone({
         'name': 'test_milestone',
         'display_name': 'Test Milestone',
         'namespace': six.text_type(self.test_course_key),
         'description': 'Test Milestone Description',
     })
     self.relationship_types = api.get_milestone_relationship_types()
Example #33
0
def _create_entrance_exam(request, course_key, entrance_exam_minimum_score_pct=None):
    """
    Internal workflow operation to create an entrance exam
    """
    # Provide a default value for the minimum score percent if nothing specified
    if entrance_exam_minimum_score_pct is None:
        entrance_exam_minimum_score_pct = float(settings.ENTRANCE_EXAM_MIN_SCORE_PCT)

    # Confirm the course exists
    course = modulestore().get_course(course_key)
    if course is None:
        return HttpResponse(status=400)

    # Create the entrance exam item (currently it's just a chapter)
    payload = {
        "category": "chapter",
        "display_name": "Entrance Exam",
        "parent_locator": unicode(course.location),
        "is_entrance_exam": True,
        "in_entrance_exam": True,
    }
    factory = RequestFactory()
    internal_request = factory.post("/", json.dumps(payload), content_type="application/json")
    internal_request.user = request.user
    created_item = json.loads(create_item(internal_request).content)

    # Set the entrance exam metadata flags for this course
    # Reload the course so we don't overwrite the new child reference
    course = modulestore().get_course(course_key)
    metadata = {
        "entrance_exam_enabled": True,
        "entrance_exam_minimum_score_pct": entrance_exam_minimum_score_pct / 100,
        "entrance_exam_id": created_item["locator"],
    }
    CourseMetadata.update_from_dict(metadata, course, request.user)

    # Add an entrance exam milestone if one does not already exist
    milestone_namespace = generate_milestone_namespace(NAMESPACE_CHOICES["ENTRANCE_EXAM"], course_key)
    milestones = milestones_api.get_milestones(milestone_namespace)
    if len(milestones):
        milestone = milestones[0]
    else:
        description = "Autogenerated during {} entrance exam creation.".format(unicode(course.id))
        milestone = milestones_api.add_milestone(
            {"name": "Completed Course Entrance Exam", "namespace": milestone_namespace, "description": description}
        )
    relationship_types = milestones_api.get_milestone_relationship_types()
    milestones_api.add_course_milestone(unicode(course.id), relationship_types["REQUIRES"], milestone)
    milestones_api.add_course_content_milestone(
        unicode(course.id), created_item["locator"], relationship_types["FULFILLS"], milestone
    )

    return HttpResponse(status=201)
Example #34
0
    def test_add_milestone_inactive_milestone_with_relationships(self):
        """ Unit Test: test_add_milestone_inactive_milestone_with_relationships"""
        milestone_data = {
            'name': 'local_milestone',
            'display_name': 'Local Milestone',
            'namespace': unicode(self.test_course_key),
            'description': 'Local Milestone Description'
        }
        milestone = api.add_milestone(milestone_data)
        api.add_course_milestone(self.test_course_key,
                                 self.relationship_types['REQUIRES'],
                                 milestone)
        api.add_course_content_milestone(self.test_course_key,
                                         self.test_content_key,
                                         self.relationship_types['FULFILLS'],
                                         milestone)
        api.add_user_milestone(self.serialized_test_user, milestone)
        self.assertGreater(milestone['id'], 0)
        api.remove_milestone(milestone['id'])

        with self.assertNumQueries(9):
            milestone = api.add_milestone(milestone_data)
Example #35
0
 def test_add_milestone(self):
     """ Unit Test: test_add_milestone"""
     with self.assertNumQueries(2):
         milestone = api.add_milestone({
             'name':
             'local_milestone',
             'display_name':
             'Local Milestone',
             'namespace':
             unicode(self.test_course_key),
             'description':
             'Local Milestone Description'
         })
     self.assertGreater(milestone['id'], 0)
Example #36
0
 def test_fetch_milestone_courses_no_relationship_type(self):
     """ Unit Test: test_fetch_milestone_courses_no_relationship_type"""
     milestone1 = api.add_milestone({
         'display_name':
         'Test Milestone',
         'name':
         'test_milestone',
         'namespace':
         six.text_type(self.test_course_key),
         'description':
         'Test Milestone Description',
     })
     api.add_course_milestone(self.test_course_key, 'fulfills', milestone1)
     self.assertEqual(len(data.fetch_milestone_courses(milestone1)), 1)
    def test_get_required_content_with_anonymous_user(self):
        course = CourseFactory()

        required_content = milestones_helpers.get_required_content(course.id, AnonymousUser())
        assert required_content == []

        # NOTE (CCB): The initial version of anonymous courseware access is very simple. We avoid accidentally
        # exposing locked content by simply avoiding anonymous access altogether for courses runs with milestones.
        milestone = milestones_api.add_milestone({
            'name': 'test',
            'namespace': 'test',
        })
        milestones_helpers.add_course_milestone(str(course.id), 'requires', milestone)
        with pytest.raises(InvalidUserException):
            milestones_helpers.get_required_content(course.id, AnonymousUser())
Example #38
0
 def test_fetch_milestone_course_content_no_relationship_type(self):
     """ Unit Test: test_fetch_milestone_course_content_no_relationship_type"""
     milestone1 = api.add_milestone({
         'display_name': 'Test Milestone',
         'name': 'test_milestone',
         'namespace': six.text_type(self.test_course_key),
         'description': 'Test Milestone Description',
     })
     api.add_course_content_milestone(
         self.test_course_key,
         self.test_content_key,
         self.relationship_types['FULFILLS'],
         milestone1
     )
     self.assertEqual(len(data.fetch_milestone_course_content(milestone1)), 1)
    def test_get_required_content_with_anonymous_user(self):
        course = CourseFactory()

        required_content = milestones_helpers.get_required_content(course.id, AnonymousUser())
        assert required_content == []

        # NOTE (CCB): The initial version of anonymous courseware access is very simple. We avoid accidentally
        # exposing locked content by simply avoiding anonymous access altogether for courses runs with milestones.
        milestone = milestones_api.add_milestone({
            'name': 'test',
            'namespace': 'test',
        })
        milestones_helpers.add_course_milestone(str(course.id), 'requires', milestone)
        with pytest.raises(InvalidUserException):
            milestones_helpers.get_required_content(course.id, AnonymousUser())
Example #40
0
 def setUp(self):
     """
     Milestones API Test Case scaffolding
     """
     super(MilestonesApiTestCase, self).setUp()
     self.test_milestone = api.add_milestone({
         'name':
         'test_milestone',
         'display_name':
         'Test Milestone',
         'namespace':
         unicode(self.test_course_key),
         'description':
         'Test Milestone Description',
     })
     self.relationship_types = api.get_milestone_relationship_types()
Example #41
0
    def test_fetch_user_milestones_missing_match_criteria_throws_exception(self):
        """ Unit Test: test_fetch_user_milestones_missing_match_criteria_throws_exception """
        milestone1 = api.add_milestone({
            'display_name': 'Test Milestone',
            'name': 'test_milestone',
            'namespace': unicode(self.test_course_key),
            'description': 'Test Milestone Description',
        })
        api.add_user_milestone(self.serialized_test_user, milestone1)

        with self.assertRaises(exceptions.InvalidMilestoneException):
            data.fetch_user_milestones(self.serialized_test_user, {})

        # Ensure we cover all remaining logical branches per coverage.py
        data.fetch_user_milestones(
            self.serialized_test_user,
            {'id': milestone1['id']}
        )
Example #42
0
 def test_fetch_milestone_course_content_no_relationship_type(self):
     """ Unit Test: test_fetch_milestone_course_content_no_relationship_type"""
     milestone1 = api.add_milestone({
         'display_name':
         'Test Milestone',
         'name':
         'test_milestone',
         'namespace':
         unicode(self.test_course_key),
         'description':
         'Test Milestone Description',
     })
     api.add_course_content_milestone(self.test_course_key,
                                      self.test_content_key,
                                      self.relationship_types['FULFILLS'],
                                      milestone1)
     self.assertEqual(len(data.fetch_milestone_course_content(milestone1)),
                      1)
Example #43
0
def add_prerequisite_course(course_key, prerequisite_course_key):
    """
    It would create a milestone, then it would set newly created
    milestones as requirement for course referred by `course_key`
    and it would set newly created milestone as fulfilment
    milestone for course referred by `prerequisite_course_key`.
    """
    if settings.FEATURES.get('MILESTONES_APP', False):
        # create a milestone
        milestone = add_milestone({
            'name': _('Course {} requires {}'.format(unicode(course_key), unicode(prerequisite_course_key))),
            'namespace': unicode(prerequisite_course_key),
            'description': _('System defined milestone'),
        })
        # add requirement course milestone
        add_course_milestone(course_key, 'requires', milestone)

        # add fulfillment course milestone
        add_course_milestone(prerequisite_course_key, 'fulfills', milestone)
def add_prerequisite_course(course_key, prerequisite_course_key):
    """
    It would create a milestone, then it would set newly created
    milestones as requirement for course referred by `course_key`
    and it would set newly created milestone as fulfilment
    milestone for course referred by `prerequisite_course_key`.
    """
    if settings.FEATURES.get('MILESTONES_APP', False):
        # create a milestone
        milestone = add_milestone({
            'name': _('Course {} requires {}'.format(unicode(course_key), unicode(prerequisite_course_key))),
            'namespace': unicode(prerequisite_course_key),
            'description': _('System defined milestone'),
        })
        # add requirement course milestone
        add_course_milestone(course_key, 'requires', milestone)

        # add fulfillment course milestone
        add_course_milestone(prerequisite_course_key, 'fulfills', milestone)
    def setUp(self):
        """
        Test case scaffolding
        """
        super(MilestonesHelpersTestCase, self).setUp()
        self.course = CourseFactory.create(
            metadata={
                'entrance_exam_enabled': True,
            }
        )

        self.user = {'id': '123'}

        self.milestone = milestones_api.add_milestone({
            'name': 'Test Milestone',
            'namespace': 'doesnt.matter',
            'description': 'Testing Milestones Helpers Library',
        })

        MilestoneRelationshipType.objects.get_or_create(name='requires')
        MilestoneRelationshipType.objects.get_or_create(name='fulfills')
Example #46
0
    def test_add_milestone_invalid_data_throws_exceptions(self):
        """ Unit Test: test_add_milestone_invalid_namespaces_throw_exceptions"""
        with self.assertNumQueries(0):
            with self.assertRaises(exceptions.InvalidMilestoneException):
                api.add_milestone({
                    'name': 'local_milestone',
                    'display_name': 'Local Milestone',
                    'namespace': '',  # Should throw an exception
                    'description': 'Local Milestone Description'
                })

        with self.assertNumQueries(0):
            with self.assertRaises(exceptions.InvalidMilestoneException):
                api.add_milestone({
                    'name':
                    'Local Milestone',  # Missing namespace should throw exception
                    'description': 'Local Milestone Description'
                })

        with self.assertNumQueries(0):
            with self.assertRaises(exceptions.InvalidMilestoneException):
                api.add_milestone({
                    'name':
                    '',  # Empty name should throw an exception on create
                    'namespace':
                    'fixed',
                    'description':
                    'Local Milestone Description 2'
                })

        with self.assertNumQueries(0):
            with self.assertRaises(exceptions.InvalidMilestoneException):
                api.add_milestone({
                    'namespace':
                    'fixed',  # Missing name should throw exception
                    'description':
                    'Local Milestone Description 2'
                })
Example #47
0
    def setUp(self):
        """
        Test case scaffolding
        """
        super(MilestonesHelpersTestCase, self).setUp()
        self.course = CourseFactory.create(
            metadata={
                'entrance_exam_enabled': True,
            }
        )

        self.user = {'id': '123'}

        self.milestone = milestones_api.add_milestone({
            'name': 'Test Milestone',
            'namespace': 'doesnt.matter',
            'description': 'Testing Milestones Helpers Library',
        })

        MilestoneRelationshipType.objects.get_or_create(name='requires')
        MilestoneRelationshipType.objects.get_or_create(name='fulfills')
Example #48
0
    def test_fetch_user_milestones_missing_match_criteria_throws_exception(
            self):
        """ Unit Test: test_fetch_user_milestones_missing_match_criteria_throws_exception """
        milestone1 = api.add_milestone({
            'display_name':
            'Test Milestone',
            'name':
            'test_milestone',
            'namespace':
            six.text_type(self.test_course_key),
            'description':
            'Test Milestone Description',
        })
        api.add_user_milestone(self.serialized_test_user, milestone1)

        with self.assertRaises(exceptions.InvalidMilestoneException):
            data.fetch_user_milestones(self.serialized_test_user, {})

        # Ensure we cover all remaining logical branches per coverage.py
        data.fetch_user_milestones(self.serialized_test_user,
                                   {'id': milestone1['id']})
def add_prerequisite_course(course_key, prerequisite_course_key):
    """
    It would create a milestone, then it would set newly created
    milestones as requirement for course referred by `course_key`
    and it would set newly created milestone as fulfillment
    milestone for course referred by `prerequisite_course_key`.
    """
    if not is_prerequisite_courses_enabled():
        return None
    milestone_name = _('Course {course_id} requires {prerequisite_course_id}').format(
        course_id=unicode(course_key),
        prerequisite_course_id=unicode(prerequisite_course_key)
    )
    milestone = milestones_api.add_milestone({
        'name': milestone_name,
        'namespace': unicode(prerequisite_course_key),
        'description': _('System defined milestone'),
    })
    # add requirement course milestone
    milestones_api.add_course_milestone(course_key, 'requires', milestone)

    # add fulfillment course milestone
    milestones_api.add_course_milestone(prerequisite_course_key, 'fulfills', milestone)
Example #50
0
def add_prerequisite(course_key, prereq_content_key):
    """
    Creates a new Milestone and CourseContentMilestone indicating that
    the given course content fulfills a prerequisite for gating

    Arguments:
        course_key (str|CourseKey): The course key
        prereq_content_key (str|UsageKey): The prerequisite content usage key

    Returns:
        None
    """
    milestone = milestones_api.add_milestone(
        {
            'name': _('Gating milestone for {usage_key}').format(usage_key=unicode(prereq_content_key)),
            'namespace': "{usage_key}{qualifier}".format(
                usage_key=prereq_content_key,
                qualifier=GATING_NAMESPACE_QUALIFIER
            ),
            'description': _('System defined milestone'),
        },
        propagate=False
    )
    milestones_api.add_course_content_milestone(course_key, prereq_content_key, 'fulfills', milestone)
Example #51
0
    def test_milestones_fulfillment_paths_contains_special_characters(self):
        """
        Unit Test: test_get_course_milestones_fulfillment_paths works correctly when milestone have some special
        characters.
        """
        namespace = six.text_type(self.test_course_key)
        name = '�ťÉśt_Àübùr�'
        local_milestone_1 = api.add_milestone({
            'display_name': 'Local Milestone 1',
            'name': name,
            'namespace': namespace,
            'description': 'Local Milestone 1 Description'
        })

        # Specify the milestone requirements
        api.add_course_milestone(
            self.test_course_key,
            self.relationship_types['REQUIRES'],
            local_milestone_1
        )

        # Specify the milestone fulfillments (via course and content)
        api.add_course_milestone(
            self.test_prerequisite_course_key,
            self.relationship_types['FULFILLS'],
            local_milestone_1
        )
        with self.assertNumQueries(4):
            paths = api.get_course_milestones_fulfillment_paths(
                self.test_course_key,
                self.serialized_test_user
            )

        # Set up the key values we'll use to access/assert the response
        milestone_key_1 = '{}.{}'.format(local_milestone_1['namespace'], local_milestone_1['name'])
        self.assertEqual(len(paths[milestone_key_1]['courses']), 1)
Example #52
0
def add_prerequisite_course(course_key, prerequisite_course_key):
    """
    It would create a milestone, then it would set newly created
    milestones as requirement for course referred by `course_key`
    and it would set newly created milestone as fulfilment
    milestone for course referred by `prerequisite_course_key`.
    """
    if not settings.FEATURES.get('ENABLE_PREREQUISITE_COURSES', False):
        return None
    from milestones import api as milestones_api
    milestone_name = _('Course {course_id} requires {prerequisite_course_id}').format(
        course_id=unicode(course_key),
        prerequisite_course_id=unicode(prerequisite_course_key)
    )
    milestone = milestones_api.add_milestone({
        'name': milestone_name,
        'namespace': unicode(prerequisite_course_key),
        'description': _('System defined milestone'),
    })
    # add requirement course milestone
    milestones_api.add_course_milestone(course_key, 'requires', milestone)

    # add fulfillment course milestone
    milestones_api.add_course_milestone(prerequisite_course_key, 'fulfills', milestone)
 def setUp(self):
     """
     Test case scaffolding
     """
     super(EntranceExamTestCases, self).setUp()
     self.course = CourseFactory.create(metadata={"entrance_exam_enabled": True})
     chapter = ItemFactory.create(parent=self.course, display_name="Overview")
     ItemFactory.create(parent=chapter, display_name="Welcome")
     ItemFactory.create(parent=self.course, category="chapter", display_name="Week 1")
     ItemFactory.create(parent=chapter, category="sequential", display_name="Lesson 1")
     ItemFactory.create(category="instructor", parent=self.course, data="Instructor Tab", display_name="Instructor")
     self.entrance_exam = ItemFactory.create(
         parent=self.course, category="chapter", display_name="Entrance Exam Section - Chapter 1"
     )
     self.exam_1 = ItemFactory.create(
         parent=self.entrance_exam,
         category="sequential",
         display_name="Exam Sequential - Subsection 1",
         graded=True,
         metadata={"in_entrance_exam": True},
     )
     subsection = ItemFactory.create(parent=self.exam_1, category="vertical", display_name="Exam Vertical - Unit 1")
     self.problem_1 = ItemFactory.create(
         parent=subsection, category="problem", display_name="Exam Problem - Problem 1"
     )
     self.problem_2 = ItemFactory.create(
         parent=subsection, category="problem", display_name="Exam Problem - Problem 2"
     )
     self.problem_3 = ItemFactory.create(
         parent=subsection, category="problem", display_name="Exam Problem - Problem 3"
     )
     milestone_namespace = generate_milestone_namespace(NAMESPACE_CHOICES["ENTRANCE_EXAM"], self.course.id)
     self.milestone = {
         "name": "Test Milestone",
         "namespace": milestone_namespace,
         "description": "Testing Courseware Entrance Exam Chapter",
     }
     MilestoneRelationshipType.objects.create(name="requires", active=True)
     MilestoneRelationshipType.objects.create(name="fulfills", active=True)
     self.milestone_relationship_types = milestones_api.get_milestone_relationship_types()
     self.milestone = milestones_api.add_milestone(self.milestone)
     milestones_api.add_course_milestone(
         unicode(self.course.id), self.milestone_relationship_types["REQUIRES"], self.milestone
     )
     milestones_api.add_course_content_milestone(
         unicode(self.course.id),
         unicode(self.entrance_exam.location),
         self.milestone_relationship_types["FULFILLS"],
         self.milestone,
     )
     user = UserFactory()
     self.request = RequestFactory()
     self.request.user = user
     self.request.COOKIES = {}
     self.request.META = {}
     self.request.is_secure = lambda: True
     self.request.get_host = lambda: "edx.org"
     self.request.method = "GET"
     self.field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
         self.course.id, user, self.entrance_exam
     )
     self.entrance_exam.is_entrance_exam = True
     self.entrance_exam.in_entrance_exam = True
     self.course.entrance_exam_enabled = True
     self.course.entrance_exam_minimum_score_pct = 0.50
     self.course.entrance_exam_id = unicode(self.entrance_exam.scope_ids.usage_id)
     modulestore().update_item(self.course, user.id)  # pylint: disable=no-member
Example #54
0
    def test_get_course_unfulfilled_milestones(self):
        """ Unit Test: test_get_course_unfulfilled_milestones """
        namespace = 'test_get_milestones'
        milestone1 = api.add_milestone({
            'name':
            'localmilestone1',
            'display_name':
            'Local Milestone 1',
            'namespace':
            namespace,
            'description':
            'Local Milestone 1 Description'
        })
        api.add_course_milestone(self.test_course_key,
                                 self.relationship_types['REQUIRES'],
                                 milestone1)

        milestone2 = api.add_milestone({
            'name':
            'localmilestone2',
            'display_name':
            'Local Milestone 2',
            'namespace':
            namespace,
            'description':
            'Local Milestone 2 Description'
        })
        api.add_course_milestone(self.test_course_key,
                                 self.relationship_types['REQUIRES'],
                                 milestone2)

        # Confirm that the course has only two milestones, and that the User still needs to collect both
        course_milestones = api.get_course_milestones(self.test_course_key)
        self.assertEqual(len(course_milestones), 2)
        with self.assertNumQueries(2):
            required_milestones = api.get_course_required_milestones(
                self.test_course_key, self.serialized_test_user)

        # Link the User to Milestone 2 (this one is now 'collected')
        api.add_user_milestone(self.serialized_test_user, milestone2)
        user_milestones = api.get_user_milestones(self.serialized_test_user,
                                                  namespace=namespace)
        self.assertEqual(len(user_milestones), 1)
        self.assertEqual(user_milestones[0]['id'], milestone2['id'])

        # Only Milestone 1 should be listed as 'required' for the course at this point
        with self.assertNumQueries(2):
            required_milestones = api.get_course_required_milestones(
                self.test_course_key, self.serialized_test_user)
        self.assertEqual(len(required_milestones), 1)
        self.assertEqual(required_milestones[0]['id'], milestone1['id'])

        # Link the User to Milestone 1 (this one is now 'collected', as well)
        api.add_user_milestone(self.serialized_test_user, milestone1)
        user_milestones = api.get_user_milestones(self.serialized_test_user,
                                                  namespace=namespace)
        self.assertEqual(len(user_milestones), 2)

        # And there should be no more Milestones required for this User+Course
        with self.assertNumQueries(2):
            required_milestones = api.get_course_required_milestones(
                self.test_course_key, self.serialized_test_user)
        self.assertEqual(len(required_milestones), 0)
Example #55
0
    def test_get_course_milestones_fulfillment_paths(self):  # pylint: disable=too-many-statements
        """
        Unit Test: test_get_course_milestones_fulfillment_paths
        """
        # Create three milestones in order tto cover all logical branches
        namespace = unicode(self.test_course_key)
        local_milestone_1 = api.add_milestone({
            'display_name':
            'Local Milestone 1',
            'name':
            'local_milestone_1',
            'namespace':
            namespace,
            'description':
            'Local Milestone 1 Description'
        })
        local_milestone_2 = api.add_milestone({
            'display_name':
            'Local Milestone 2',
            'name':
            'local_milestone_2',
            'namespace':
            namespace,
            'description':
            'Local Milestone 2 Description'
        })
        local_milestone_3 = api.add_milestone({
            'display_name':
            'Local Milestone 3',
            'name':
            'local_milestone_3',
            'namespace':
            namespace,
            'description':
            'Local Milestone 3 Description'
        })

        # Specify the milestone requirements
        api.add_course_milestone(self.test_course_key,
                                 self.relationship_types['REQUIRES'],
                                 local_milestone_1)
        api.add_course_milestone(self.test_course_key,
                                 self.relationship_types['REQUIRES'],
                                 local_milestone_2)
        api.add_course_milestone(self.test_course_key,
                                 self.relationship_types['REQUIRES'],
                                 local_milestone_3)

        # Specify the milestone fulfillments (via course and content)
        api.add_course_milestone(self.test_prerequisite_course_key,
                                 self.relationship_types['FULFILLS'],
                                 local_milestone_1)
        api.add_course_milestone(self.test_prerequisite_course_key,
                                 self.relationship_types['FULFILLS'],
                                 local_milestone_2)
        api.add_course_content_milestone(
            self.test_course_key,
            UsageKey.from_string('i4x://the/content/key/123456789'),
            self.relationship_types['FULFILLS'], local_milestone_2)
        api.add_course_content_milestone(
            self.test_course_key,
            UsageKey.from_string('i4x://the/content/key/123456789'),
            self.relationship_types['FULFILLS'], local_milestone_3)
        api.add_course_content_milestone(
            self.test_course_key,
            UsageKey.from_string('i4x://the/content/key/987654321'),
            self.relationship_types['FULFILLS'], local_milestone_3)

        # Confirm the starting state for this test (user has no milestones, course requires three)
        self.assertEqual(
            len(
                api.get_user_milestones(self.serialized_test_user,
                                        namespace=namespace)), 0)
        self.assertEqual(
            len(
                api.get_course_required_milestones(self.test_course_key,
                                                   self.serialized_test_user)),
            3)
        # Check the possible fulfillment paths for the milestones for this course
        with self.assertNumQueries(8):
            paths = api.get_course_milestones_fulfillment_paths(
                self.test_course_key, self.serialized_test_user)

        # Set up the key values we'll use to access/assert the response
        milestone_key_1 = '{}.{}'.format(local_milestone_1['namespace'],
                                         local_milestone_1['name'])
        milestone_key_2 = '{}.{}'.format(local_milestone_2['namespace'],
                                         local_milestone_2['name'])
        milestone_key_3 = '{}.{}'.format(local_milestone_3['namespace'],
                                         local_milestone_3['name'])

        # First round of assertions
        self.assertEqual(len(paths[milestone_key_1]['courses']), 1)
        self.assertIsNone(paths[milestone_key_1].get('content'))
        self.assertEqual(len(paths[milestone_key_2]['courses']), 1)
        self.assertEqual(len(paths[milestone_key_2]['content']), 1)
        self.assertIsNone(paths[milestone_key_3].get('courses'))
        self.assertEqual(len(paths[milestone_key_3]['content']), 2)

        # Collect the first milestone (two should remain)
        api.add_user_milestone(self.serialized_test_user, local_milestone_1)
        self.assertEqual(
            len(
                api.get_user_milestones(self.serialized_test_user,
                                        namespace=namespace)), 1)
        self.assertEqual(
            len(
                api.get_course_required_milestones(self.test_course_key,
                                                   self.serialized_test_user)),
            2)
        # Check the remaining fulfillment paths for the milestones for this course
        with self.assertNumQueries(6):
            paths = api.get_course_milestones_fulfillment_paths(
                self.test_course_key, self.serialized_test_user)
        self.assertIsNone(paths.get(milestone_key_1))
        self.assertEqual(len(paths[milestone_key_2]['courses']), 1)
        self.assertEqual(len(paths[milestone_key_2]['content']), 1)
        self.assertIsNone(paths[milestone_key_3].get('courses'))
        self.assertEqual(len(paths[milestone_key_3]['content']), 2)

        # Collect the second milestone (one should remain)
        api.add_user_milestone(self.serialized_test_user, local_milestone_2)
        self.assertEqual(
            len(
                api.get_user_milestones(self.serialized_test_user,
                                        namespace=namespace)), 2)
        self.assertEqual(
            len(
                api.get_course_required_milestones(self.test_course_key,
                                                   self.serialized_test_user)),
            1)
        # Check the remaining fulfillment paths for the milestones for this course
        with self.assertNumQueries(4):
            paths = api.get_course_milestones_fulfillment_paths(
                self.test_course_key, self.serialized_test_user)
        self.assertIsNone(paths.get(milestone_key_1))
        self.assertIsNone(paths.get(milestone_key_2))
        self.assertIsNone(paths[milestone_key_3].get('courses'))
        self.assertEqual(len(paths[milestone_key_3]['content']), 2)

        # Collect the third milestone
        api.add_user_milestone(self.serialized_test_user, local_milestone_3)
        self.assertEqual(
            len(
                api.get_user_milestones(self.serialized_test_user,
                                        namespace=namespace)), 3)
        self.assertEqual(
            len(
                api.get_course_required_milestones(self.test_course_key,
                                                   self.serialized_test_user)),
            0)
        # Check the remaining fulfillment paths for the milestones for this course
        with self.assertNumQueries(2):
            paths = api.get_course_milestones_fulfillment_paths(
                self.test_course_key, self.serialized_test_user)
        self.assertIsNone(paths.get(milestone_key_1))
        self.assertIsNone(paths.get(milestone_key_2))
        self.assertIsNone(paths.get(milestone_key_3))
Example #56
0
    def test_get_course_unfulfilled_milestones(self):
        """ Unit Test: test_get_course_unfulfilled_milestones """
        namespace = 'test_get_milestones'
        milestone1 = api.add_milestone({
            'name': 'localmilestone1',
            'display_name': 'Local Milestone 1',
            'namespace': namespace,
            'description': 'Local Milestone 1 Description'
        })
        api.add_course_milestone(
            self.test_course_key,
            self.relationship_types['REQUIRES'],
            milestone1
        )

        milestone2 = api.add_milestone({
            'name': 'localmilestone2',
            'display_name': 'Local Milestone 2',
            'namespace': namespace,
            'description': 'Local Milestone 2 Description'
        })
        api.add_course_milestone(
            self.test_course_key,
            self.relationship_types['REQUIRES'],
            milestone2
        )

        # Confirm that the course has only two milestones, and that the User still needs to collect both
        course_milestones = api.get_course_milestones(self.test_course_key)
        self.assertEqual(len(course_milestones), 2)
        with self.assertNumQueries(2):
            required_milestones = api.get_course_required_milestones(
                self.test_course_key,
                self.serialized_test_user
            )

        # Link the User to Milestone 2 (this one is now 'collected')
        api.add_user_milestone(self.serialized_test_user, milestone2)
        user_milestones = api.get_user_milestones(self.serialized_test_user, namespace=namespace)
        self.assertEqual(len(user_milestones), 1)
        self.assertEqual(user_milestones[0]['id'], milestone2['id'])

        # Only Milestone 1 should be listed as 'required' for the course at this point
        with self.assertNumQueries(2):
            required_milestones = api.get_course_required_milestones(
                self.test_course_key,
                self.serialized_test_user
            )
        self.assertEqual(len(required_milestones), 1)
        self.assertEqual(required_milestones[0]['id'], milestone1['id'])

        # Link the User to Milestone 1 (this one is now 'collected', as well)
        api.add_user_milestone(self.serialized_test_user, milestone1)
        user_milestones = api.get_user_milestones(self.serialized_test_user, namespace=namespace)
        self.assertEqual(len(user_milestones), 2)

        # And there should be no more Milestones required for this User+Course
        with self.assertNumQueries(2):
            required_milestones = api.get_course_required_milestones(
                self.test_course_key,
                self.serialized_test_user
            )
        self.assertEqual(len(required_milestones), 0)
Example #57
0
    def test_get_course_milestones_fulfillment_paths(self):  # pylint: disable=too-many-statements
        """
        Unit Test: test_get_course_milestones_fulfillment_paths
        """
        # Create three milestones in order tto cover all logical branches
        namespace = six.text_type(self.test_course_key)
        local_milestone_1 = api.add_milestone({
            'display_name': 'Local Milestone 1',
            'name': 'local_milestone_1',
            'namespace': namespace,
            'description': 'Local Milestone 1 Description'
        })
        local_milestone_2 = api.add_milestone({
            'display_name': 'Local Milestone 2',
            'name': 'local_milestone_2',
            'namespace': namespace,
            'description': 'Local Milestone 2 Description'
        })
        local_milestone_3 = api.add_milestone({
            'display_name': 'Local Milestone 3',
            'name': 'local_milestone_3',
            'namespace': namespace,
            'description': 'Local Milestone 3 Description'
        })

        # Specify the milestone requirements
        api.add_course_milestone(
            self.test_course_key,
            self.relationship_types['REQUIRES'],
            local_milestone_1
        )
        api.add_course_milestone(
            self.test_course_key,
            self.relationship_types['REQUIRES'],
            local_milestone_2
        )
        api.add_course_milestone(
            self.test_course_key,
            self.relationship_types['REQUIRES'],
            local_milestone_3
        )

        # Specify the milestone fulfillments (via course and content)
        api.add_course_milestone(
            self.test_prerequisite_course_key,
            self.relationship_types['FULFILLS'],
            local_milestone_1
        )
        api.add_course_milestone(
            self.test_prerequisite_course_key,
            self.relationship_types['FULFILLS'],
            local_milestone_2
        )
        api.add_course_content_milestone(
            self.test_course_key,
            UsageKey.from_string('i4x://the/content/key/123456789'),
            self.relationship_types['FULFILLS'],
            local_milestone_2
        )
        api.add_course_content_milestone(
            self.test_course_key,
            UsageKey.from_string('i4x://the/content/key/123456789'),
            self.relationship_types['FULFILLS'],
            local_milestone_3
        )
        api.add_course_content_milestone(
            self.test_course_key,
            UsageKey.from_string('i4x://the/content/key/987654321'),
            self.relationship_types['FULFILLS'],
            local_milestone_3
        )

        # Confirm the starting state for this test (user has no milestones, course requires three)
        self.assertEqual(
            len(api.get_user_milestones(self.serialized_test_user, namespace=namespace)), 0)
        self.assertEqual(
            len(api.get_course_required_milestones(self.test_course_key, self.serialized_test_user)),
            3
        )
        # Check the possible fulfillment paths for the milestones for this course
        with self.assertNumQueries(8):
            paths = api.get_course_milestones_fulfillment_paths(
                self.test_course_key,
                self.serialized_test_user
            )

        # Set up the key values we'll use to access/assert the response
        milestone_key_1 = '{}.{}'.format(local_milestone_1['namespace'], local_milestone_1['name'])
        milestone_key_2 = '{}.{}'.format(local_milestone_2['namespace'], local_milestone_2['name'])
        milestone_key_3 = '{}.{}'.format(local_milestone_3['namespace'], local_milestone_3['name'])

        # First round of assertions
        self.assertEqual(len(paths[milestone_key_1]['courses']), 1)
        self.assertIsNone(paths[milestone_key_1].get('content'))
        self.assertEqual(len(paths[milestone_key_2]['courses']), 1)
        self.assertEqual(len(paths[milestone_key_2]['content']), 1)
        self.assertIsNone(paths[milestone_key_3].get('courses'))
        self.assertEqual(len(paths[milestone_key_3]['content']), 2)

        # Collect the first milestone (two should remain)
        api.add_user_milestone(self.serialized_test_user, local_milestone_1)
        self.assertEqual(
            len(api.get_user_milestones(self.serialized_test_user, namespace=namespace)), 1)
        self.assertEqual(
            len(api.get_course_required_milestones(self.test_course_key, self.serialized_test_user)),
            2
        )
        # Check the remaining fulfillment paths for the milestones for this course
        with self.assertNumQueries(6):
            paths = api.get_course_milestones_fulfillment_paths(
                self.test_course_key,
                self.serialized_test_user
            )
        self.assertIsNone(paths.get(milestone_key_1))
        self.assertEqual(len(paths[milestone_key_2]['courses']), 1)
        self.assertEqual(len(paths[milestone_key_2]['content']), 1)
        self.assertIsNone(paths[milestone_key_3].get('courses'))
        self.assertEqual(len(paths[milestone_key_3]['content']), 2)

        # Collect the second milestone (one should remain)
        api.add_user_milestone(self.serialized_test_user, local_milestone_2)
        self.assertEqual(
            len(api.get_user_milestones(self.serialized_test_user, namespace=namespace)), 2)
        self.assertEqual(
            len(api.get_course_required_milestones(self.test_course_key, self.serialized_test_user)),
            1
        )
        # Check the remaining fulfillment paths for the milestones for this course
        with self.assertNumQueries(4):
            paths = api.get_course_milestones_fulfillment_paths(
                self.test_course_key,
                self.serialized_test_user
            )
        self.assertIsNone(paths.get(milestone_key_1))
        self.assertIsNone(paths.get(milestone_key_2))
        self.assertIsNone(paths[milestone_key_3].get('courses'))
        self.assertEqual(len(paths[milestone_key_3]['content']), 2)

        # Collect the third milestone
        api.add_user_milestone(self.serialized_test_user, local_milestone_3)
        self.assertEqual(
            len(api.get_user_milestones(self.serialized_test_user, namespace=namespace)), 3)
        self.assertEqual(
            len(api.get_course_required_milestones(self.test_course_key, self.serialized_test_user)),
            0
        )
        # Check the remaining fulfillment paths for the milestones for this course
        with self.assertNumQueries(2):
            paths = api.get_course_milestones_fulfillment_paths(
                self.test_course_key,
                self.serialized_test_user
            )
        self.assertIsNone(paths.get(milestone_key_1))
        self.assertIsNone(paths.get(milestone_key_2))
        self.assertIsNone(paths.get(milestone_key_3))
 def setUp(self):
     """
     Test case scaffolding
     """
     super(EntranceExamTestCases, self).setUp()
     self.course = CourseFactory.create(metadata={
         'entrance_exam_enabled': True,
     })
     chapter = ItemFactory.create(parent=self.course,
                                  display_name='Overview')
     ItemFactory.create(parent=chapter, display_name='Welcome')
     ItemFactory.create(parent=self.course,
                        category='chapter',
                        display_name="Week 1")
     ItemFactory.create(parent=chapter,
                        category='sequential',
                        display_name="Lesson 1")
     ItemFactory.create(category="instructor",
                        parent=self.course,
                        data="Instructor Tab",
                        display_name="Instructor")
     self.entrance_exam = ItemFactory.create(
         parent=self.course,
         category="chapter",
         display_name="Entrance Exam Section - Chapter 1")
     self.exam_1 = ItemFactory.create(
         parent=self.entrance_exam,
         category='sequential',
         display_name="Exam Sequential - Subsection 1",
         graded=True,
         metadata={'in_entrance_exam': True})
     subsection = ItemFactory.create(parent=self.exam_1,
                                     category='vertical',
                                     display_name='Exam Vertical - Unit 1')
     self.problem_1 = ItemFactory.create(
         parent=subsection,
         category="problem",
         display_name="Exam Problem - Problem 1")
     self.problem_2 = ItemFactory.create(
         parent=subsection,
         category="problem",
         display_name="Exam Problem - Problem 2")
     self.problem_3 = ItemFactory.create(
         parent=subsection,
         category="problem",
         display_name="Exam Problem - Problem 3")
     milestone_namespace = generate_milestone_namespace(
         NAMESPACE_CHOICES['ENTRANCE_EXAM'], self.course.id)
     self.milestone = {
         'name': 'Test Milestone',
         'namespace': milestone_namespace,
         'description': 'Testing Courseware Entrance Exam Chapter',
     }
     MilestoneRelationshipType.objects.create(name='requires', active=True)
     MilestoneRelationshipType.objects.create(name='fulfills', active=True)
     self.milestone_relationship_types = milestones_api.get_milestone_relationship_types(
     )
     self.milestone = milestones_api.add_milestone(self.milestone)
     milestones_api.add_course_milestone(
         unicode(self.course.id),
         self.milestone_relationship_types['REQUIRES'], self.milestone)
     milestones_api.add_course_content_milestone(
         unicode(self.course.id), unicode(self.entrance_exam.location),
         self.milestone_relationship_types['FULFILLS'], self.milestone)
     user = UserFactory()
     self.request = RequestFactory()
     self.request.user = user
     self.request.COOKIES = {}
     self.request.META = {}
     self.request.is_secure = lambda: True
     self.request.get_host = lambda: "edx.org"
     self.request.method = 'GET'
     self.field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
         self.course.id, user, self.entrance_exam)
     self.entrance_exam.is_entrance_exam = True
     self.entrance_exam.in_entrance_exam = True
     self.course.entrance_exam_enabled = True
     self.course.entrance_exam_minimum_score_pct = 0.50
     self.course.entrance_exam_id = unicode(
         self.entrance_exam.scope_ids.usage_id)
     modulestore().update_item(self.course, user.id)  # pylint: disable=no-member
Example #59
0
def _create_entrance_exam(request,
                          course_key,
                          entrance_exam_minimum_score_pct=None):
    """
    Internal workflow operation to create an entrance exam
    """
    # Provide a default value for the minimum score percent if nothing specified
    if entrance_exam_minimum_score_pct is None:
        entrance_exam_minimum_score_pct = float(
            settings.ENTRANCE_EXAM_MIN_SCORE_PCT)

    # Confirm the course exists
    course = modulestore().get_course(course_key)
    if course is None:
        return HttpResponse(status=400)

    # Create the entrance exam item (currently it's just a chapter)
    payload = {
        'category': "chapter",
        'display_name': "Entrance Exam",
        'parent_locator': unicode(course.location),
        'is_entrance_exam': True,
        'in_entrance_exam': True,
    }
    factory = RequestFactory()
    internal_request = factory.post('/',
                                    json.dumps(payload),
                                    content_type="application/json")
    internal_request.user = request.user
    created_item = json.loads(create_item(internal_request).content)

    # Set the entrance exam metadata flags for this course
    # Reload the course so we don't overwrite the new child reference
    course = modulestore().get_course(course_key)
    metadata = {
        'entrance_exam_enabled': True,
        'entrance_exam_minimum_score_pct':
        entrance_exam_minimum_score_pct / 100,
        'entrance_exam_id': created_item['locator'],
    }
    CourseMetadata.update_from_dict(metadata, course, request.user)

    # Add an entrance exam milestone if one does not already exist
    milestone_namespace = generate_milestone_namespace(
        NAMESPACE_CHOICES['ENTRANCE_EXAM'], course_key)
    milestones = milestones_api.get_milestones(milestone_namespace)
    if len(milestones):
        milestone = milestones[0]
    else:
        description = 'Autogenerated during {} entrance exam creation.'.format(
            unicode(course.id))
        milestone = milestones_api.add_milestone({
            'name': 'Completed Course Entrance Exam',
            'namespace': milestone_namespace,
            'description': description
        })
    relationship_types = milestones_api.get_milestone_relationship_types()
    milestones_api.add_course_milestone(unicode(course.id),
                                        relationship_types['REQUIRES'],
                                        milestone)
    milestones_api.add_course_content_milestone(unicode(course.id),
                                                created_item['locator'],
                                                relationship_types['FULFILLS'],
                                                milestone)

    return HttpResponse(status=201)