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 )
def test_remove_content_references(self): """ Unit Test: test_remove_content_references """ # Add a course dependency on the test milestone api.add_course_milestone( self.test_course_key, self.relationship_types['REQUIRES'], self.test_milestone ) self.assertEqual(len(api.get_course_milestones(self.test_course_key)), 1) # Add a content fulfillment for the test milestone api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['FULFILLS'], self.test_milestone ) milestones = api.get_course_content_milestones(self.test_course_key, self.test_content_key) self.assertEqual(len(milestones), 1) # Remove the content dependency with self.assertNumQueries(2): api.remove_content_references(self.test_content_key) milestones = api.get_course_content_milestones(self.test_course_key, self.test_content_key) self.assertEqual(len(milestones), 0)
def test_add_course_content_milestone_inactive_to_active_update_requirements(self): """ Unit Test: test_add_course_content_milestone_inactive_to_active_update_requirement """ api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone, {'min_score': 60} ) api.remove_course_content_milestone( self.test_course_key, self.test_content_key, self.test_milestone ) with self.assertNumQueries(3): api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone, {'min_score': 80} ) milestone = api.get_course_content_milestones( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'] ) for m in milestone: self.assertEqual(m['requirements'], {'min_score': 80})
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')
def set_required_content(course_key, gated_content_key, prereq_content_key, min_score='', min_completion=''): """ Adds a `requires` milestone relationship for the given gated_content_key if a prerequisite prereq_content_key is provided. If prereq_content_key is None, removes the `requires` milestone relationship. Arguments: course_key (str|CourseKey): The course key gated_content_key (str|UsageKey): The gated content usage key prereq_content_key (str|UsageKey): The prerequisite content usage key min_score (str|int): The minimum score min_completion (str|int): The minimum completion percentage Returns: None """ milestone = None for gating_milestone in find_gating_milestones(course_key, gated_content_key, 'requires'): if not prereq_content_key or prereq_content_key not in gating_milestone.get('namespace'): milestones_api.remove_course_content_milestone(course_key, gated_content_key, gating_milestone) else: milestone = gating_milestone if prereq_content_key: _validate_min_score(min_score) requirements = {'min_score': min_score, 'min_completion': min_completion} if not milestone: milestone = _get_prerequisite_milestone(prereq_content_key) milestones_api.add_course_content_milestone(course_key, gated_content_key, 'requires', milestone, requirements)
def test_remove_course_content_milestone(self): """ Unit Test: test_remove_course_content_milestone """ api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone ) requirer_milestones = api.get_course_content_milestones( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'] ) self.assertEqual(len(requirer_milestones), 1) with self.assertNumQueries(2): api.remove_course_content_milestone( self.test_course_key, self.test_content_key, self.test_milestone ) requirer_milestones = api.get_course_content_milestones( self.test_course_key, self.test_content_key ) self.assertEqual(len(requirer_milestones), 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_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)
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)
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)
def set_required_content(course_key, gated_content_key, prereq_content_key, min_score): """ Adds a `requires` milestone relationship for the given gated_content_key if a prerequisite prereq_content_key is provided. If prereq_content_key is None, removes the `requires` milestone relationship. Arguments: course_key (str|CourseKey): The course key gated_content_key (str|UsageKey): The gated content usage key prereq_content_key (str|UsageKey): The prerequisite content usage key min_score (str|int): The minimum score Returns: None """ milestone = None for gating_milestone in find_gating_milestones(course_key, gated_content_key, 'requires'): if not prereq_content_key or prereq_content_key not in gating_milestone.get('namespace'): milestones_api.remove_course_content_milestone(course_key, gated_content_key, gating_milestone) else: milestone = gating_milestone if prereq_content_key: _validate_min_score(min_score) requirements = {'min_score': min_score} if not milestone: milestone = _get_prerequisite_milestone(prereq_content_key) milestones_api.add_course_content_milestone(course_key, gated_content_key, 'requires', milestone, requirements)
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)
def test_add_course_content_milestone(self): """ Unit Test: test_add_course_content_milestone """ with self.assertNumQueries(3): api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone ) requirer_milestones = api.get_course_content_milestones( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'] ) self.assertEqual(len(requirer_milestones), 1) with self.assertNumQueries(3): api.add_course_content_milestone( self.test_prerequisite_course_key, self.test_content_key, self.relationship_types['FULFILLS'], self.test_milestone, {'min_score': 80} ) fulfiller_milestones = api.get_course_content_milestones( self.test_prerequisite_course_key, self.test_content_key, self.relationship_types['FULFILLS'] ) self.assertEqual(len(fulfiller_milestones), 1)
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)
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')
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' )
def test_add_course_content_milestone_active_exists(self): """ Unit Test: test_add_course_content_milestone_active_exists """ api.add_course_content_milestone(self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone) with self.assertNumQueries(2): api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone)
def test_add_course_content_milestone_invalid_requirements(self): """ Unit Test: test_add_course_content_milestone_invalid_requirements """ with self.assertRaises(exceptions.InvalidCourseContentMilestoneRequirementsException): api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone, set() # Not JSON serializable )
def test_add_course_content_milestone_invalid_requirements(self): """ Unit Test: test_add_course_content_milestone_invalid_requirements """ with self.assertRaises( exceptions.InvalidCourseContentMilestoneRequirementsException): api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone, set() # Not JSON serializable )
def test_get_course_content_milestones(self): """ Unit Test: test_get_course_content_milestones """ api.add_course_content_milestone(self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone) with self.assertNumQueries(2): requirer_milestones = api.get_course_content_milestones( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES']) self.assertEqual(len(requirer_milestones), 1)
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)
def test_add_course_content_milestone_bogus_content_key(self): """ Unit Test: test_add_course_content_milestone_bogus_content_key """ with self.assertNumQueries(0): with self.assertRaises(exceptions.InvalidContentKeyException): api.add_course_content_milestone(self.test_course_key, '234290jweovsu', 'whatever', self.test_milestone) with self.assertNumQueries(0): with self.assertRaises(exceptions.InvalidContentKeyException): api.add_course_content_milestone(self.test_course_key, None, 'whatever', self.test_milestone)
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)
def test_add_course_content_milestone_inactive_to_active(self): """ Unit Test: test_add_course_content_milestone_inactive_to_active """ api.add_course_content_milestone(self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone) api.remove_course_content_milestone(self.test_course_key, self.test_content_key, self.test_milestone) with self.assertNumQueries(3): api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone)
def test_get_course_content_milestones_with_fulfilled_user_milestones( self): """ Unit Test: test_get_course_content_milestones_with_fulfilled_user_milestones """ user = {'id': self.test_user.id} api.add_course_content_milestone(self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone) api.add_user_milestone(user, self.test_milestone) with self.assertNumQueries(2): requirer_milestones = api.get_course_content_milestones( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], {'id': self.test_user.id}) self.assertEqual(len(requirer_milestones), 0)
def test_add_course_content_milestone_active_exists(self): """ Unit Test: test_add_course_content_milestone_active_exists """ api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone ) with self.assertNumQueries(2): api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone )
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)
def test_get_course_content_milestones_with_unfulfilled_user_milestones(self): """ Unit Test: test_get_course_content_milestones_with_unfulfilled_user_milestones """ api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone ) with self.assertNumQueries(2): requirer_milestones = api.get_course_content_milestones( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], {'id': self.test_user.id} ) self.assertEqual(len(requirer_milestones), 1)
def test_add_course_content_milestone_bogus_milestone_relationship_type( self): """ Unit Test: test_add_course_content_milestone_bogus_milestone_relationship_type """ with self.assertNumQueries(0): with self.assertRaises( exceptions.InvalidMilestoneRelationshipTypeException): api.add_course_content_milestone(self.test_course_key, self.test_content_key, 'whatever', self.test_milestone) with self.assertNumQueries(0): with self.assertRaises( exceptions.InvalidMilestoneRelationshipTypeException): api.add_course_content_milestone(self.test_course_key, self.test_content_key, None, self.test_milestone)
def add_course_content_milestone(course_id, content_id, relationship, milestone): """ Client API operation adapter/wrapper """ if not ENABLE_MILESTONES_APP.is_enabled(): return None return milestones_api.add_course_content_milestone(course_id, content_id, relationship, milestone)
def add_course_content_milestone(course_id, content_id, relationship, milestone): """ Client API operation adapter/wrapper """ if not settings.FEATURES.get('MILESTONES_APP'): return None return milestones_api.add_course_content_milestone(course_id, content_id, relationship, milestone)
def add_course_content_milestone(course_id, content_id, relationship, milestone): """ Client API operation adapter/wrapper """ if not settings.FEATURES.get('MILESTONES_APP'): return None return milestones_api.add_course_content_milestone(course_id, content_id, relationship, milestone)
def test_add_course_content_milestone_bogus_milestone_relationship_type(self): """ Unit Test: test_add_course_content_milestone_bogus_milestone_relationship_type """ with self.assertNumQueries(1): with self.assertRaises(exceptions.InvalidMilestoneRelationshipTypeException): api.add_course_content_milestone( self.test_course_key, self.test_content_key, 'whatever', self.test_milestone ) with self.assertNumQueries(1): with self.assertRaises(exceptions.InvalidMilestoneRelationshipTypeException): api.add_course_content_milestone( self.test_course_key, self.test_content_key, None, self.test_milestone )
def test_add_course_content_milestone_bogus_content_key(self): """ Unit Test: test_add_course_content_milestone_bogus_content_key """ with self.assertNumQueries(0): with self.assertRaises(exceptions.InvalidContentKeyException): api.add_course_content_milestone( self.test_course_key, '234290jweovsu', 'whatever', self.test_milestone ) with self.assertNumQueries(0): with self.assertRaises(exceptions.InvalidContentKeyException): api.add_course_content_milestone( self.test_course_key, None, 'whatever', self.test_milestone )
def test_add_course_content_milestone_active_exists_update_requirements( self): """ Unit Test: test_add_course_content_milestone_active_exists_update_requirement """ api.add_course_content_milestone(self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone, {'min_score': 60}) with self.assertNumQueries(3): api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone, {'min_score': 80}) milestone = api.get_course_content_milestones( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES']) for m in milestone: self.assertEqual(m['requirements'], {'min_score': 80})
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_add_course_content_milestone(self): """ Unit Test: test_add_course_content_milestone """ with self.assertNumQueries(3): api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone) requirer_milestones = api.get_course_content_milestones( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES']) self.assertEqual(len(requirer_milestones), 1) with self.assertNumQueries(3): api.add_course_content_milestone( self.test_prerequisite_course_key, self.test_content_key, self.relationship_types['FULFILLS'], self.test_milestone, {'min_score': 80}) fulfiller_milestones = api.get_course_content_milestones( self.test_prerequisite_course_key, self.test_content_key, self.relationship_types['FULFILLS']) self.assertEqual(len(fulfiller_milestones), 1)
def test_add_course_content_milestone_inactive_to_active(self): """ Unit Test: test_add_course_content_milestone_inactive_to_active """ api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone ) api.remove_course_content_milestone( self.test_course_key, self.test_content_key, self.test_milestone ) with self.assertNumQueries(3): api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone )
def test_get_course_content_milestones_without_content_key(self): """ Unit Test: test_get_course_content_milestones_without_content_key """ api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone ) api.add_course_content_milestone( self.test_course_key, self.test_alternate_content_key, self.relationship_types['REQUIRES'], self.test_milestone ) with self.assertNumQueries(2): requirer_milestones = api.get_course_content_milestones( self.test_course_key, None, self.relationship_types['REQUIRES'] ) self.assertEqual(len(requirer_milestones), 2)
def test_get_course_content_milestones_with_invalid_relationship(self): """ Unit Test: test_get_course_content_milestones_with_invalid_relationship """ api.add_course_content_milestone( self.test_course_key, self.test_content_key, self.relationship_types['REQUIRES'], self.test_milestone ) api.add_course_content_milestone( self.test_course_key, self.test_alternate_content_key, self.relationship_types['REQUIRES'], self.test_milestone ) with self.assertNumQueries(1): requirer_milestones = api.get_course_content_milestones( self.test_course_key, self.test_content_key, 'INVALID RELATIONSHIP TYPE' ) self.assertEqual(len(requirer_milestones), 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)
def test_remove_content_references(self): """ Unit Test: test_remove_content_references """ # Add a course dependency on the test milestone api.add_course_milestone(self.test_course_key, self.relationship_types['REQUIRES'], self.test_milestone) self.assertEqual(len(api.get_course_milestones(self.test_course_key)), 1) # Add a content fulfillment for the test milestone api.add_course_content_milestone(self.test_course_key, self.test_content_key, self.relationship_types['FULFILLS'], self.test_milestone) milestones = api.get_course_content_milestones(self.test_course_key, self.test_content_key) self.assertEqual(len(milestones), 1) # Remove the content dependency with self.assertNumQueries(2): api.remove_content_references(self.test_content_key) milestones = api.get_course_content_milestones(self.test_course_key, self.test_content_key) self.assertEqual(len(milestones), 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)
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': 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['REQUIRES'], milestone1) milestone2 = api.add_milestone({ 'display_name': 'Test Milestone 2', 'name': 'test_milestone2', 'namespace': six.text_type(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')
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
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))
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 = _get_default_entrance_exam_minimum_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, } parent_locator = unicode(course.location) created_block = create_xblock( parent_locator=parent_locator, user=request.user, category='chapter', display_name='Entrance Exam', is_entrance_exam=True ) # 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': unicode(entrance_exam_minimum_score_pct), 'entrance_exam_id': unicode(created_block.location), } CourseMetadata.update_from_dict(metadata, course, request.user) # Create the entrance exam section item. create_xblock( parent_locator=unicode(created_block.location), user=request.user, category='sequential', display_name=_('Entrance Exam - Subsection') ) # 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), unicode(created_block.location), relationship_types['FULFILLS'], milestone ) return HttpResponse(status=201)
def setUp(self): """ Test case scaffolding """ super(EntranceExamTestCases, self).setUp() self.course = CourseFactory.create( metadata={ 'entrance_exam_enabled': True, } ) self.chapter = ItemFactory.create( parent=self.course, display_name='Overview' ) ItemFactory.create( parent=self.chapter, display_name='Welcome' ) ItemFactory.create( parent=self.course, category='chapter', display_name="Week 1" ) self.chapter_subsection = ItemFactory.create( parent=self.chapter, category='sequential', display_name="Lesson 1" ) chapter_vertical = ItemFactory.create( parent=self.chapter_subsection, category='vertical', display_name='Lesson 1 Vertical - Unit 1' ) ItemFactory.create( parent=chapter_vertical, category="problem", display_name="Problem - Unit 1 Problem 1" ) ItemFactory.create( parent=chapter_vertical, category="problem", display_name="Problem - Unit 1 Problem 2" ) 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", is_entrance_exam=True, in_entrance_exam=True ) self.exam_1 = ItemFactory.create( parent=self.entrance_exam, category='sequential', display_name="Exam Sequential - Subsection 1", graded=True, 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.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 self.client.login(username=self.request.user.username, password="******") CourseEnrollment.enroll(self.request.user, self.course.id) self.expected_locked_toc = ( [ { 'active': True, 'sections': [ { 'url_name': u'Exam_Sequential_-_Subsection_1', 'display_name': u'Exam Sequential - Subsection 1', 'graded': True, 'format': '', 'due': None, 'active': True } ], 'url_name': u'Entrance_Exam_Section_-_Chapter_1', 'display_name': u'Entrance Exam Section - Chapter 1' } ] ) self.expected_unlocked_toc = ( [ { 'active': False, 'sections': [ { 'url_name': u'Welcome', 'display_name': u'Welcome', 'graded': False, 'format': '', 'due': None, 'active': False }, { 'url_name': u'Lesson_1', 'display_name': u'Lesson 1', 'graded': False, 'format': '', 'due': None, 'active': False } ], 'url_name': u'Overview', 'display_name': u'Overview' }, { 'active': False, 'sections': [], 'url_name': u'Week_1', 'display_name': u'Week 1' }, { 'active': False, 'sections': [], 'url_name': u'Instructor', 'display_name': u'Instructor' }, { 'active': True, 'sections': [ { 'url_name': u'Exam_Sequential_-_Subsection_1', 'display_name': u'Exam Sequential - Subsection 1', 'graded': True, 'format': '', 'due': None, 'active': True } ], 'url_name': u'Entrance_Exam_Section_-_Chapter_1', 'display_name': u'Entrance Exam Section - Chapter 1' } ] )
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
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)
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))