def test_grades_view_oauth2(self): """ Test the grades view using OAuth2 Authentication """ self.users_and_problem_setup() CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id) query_string = urllib.urlencode({'course_id': self.course.id, 'username': self.user.username}) url = '/api/server/mobile/v1/users/courses/grades/?{}'.format(query_string) self.request.user = self.user answer_problem(self.course, self.request, self.problem, score=1, max_value=1) # Try with no authentication: self.client.logout() response = self.client.get(url) self.assertEqual(response.status_code, 401) user_grade = 0.8 StudentGradebook.objects.update_or_create( user=self.user, course_id=self.course.id, defaults={ 'grade': user_grade, 'proforma_grade': 1, 'is_passed': True, } ) # Now, try with a valid token header: token = self.create_oauth2_token(self.user) response = self.client.get(url, HTTP_AUTHORIZATION="Bearer {0}".format(token)) self.assertEqual(response.status_code, 200) self.assertEqual(response.data['course_grade'], user_grade)
def test_orphaned_xblock(self): with patch( 'lms.djangoapps.gating.signals.gating_api.evaluate_prerequisite' ) as mock_handler: self.assertFalse(mock_handler.called) answer_problem(self.course, self.request, self.orphan, 1, 1) self.assertFalse(mock_handler.called)
def test_invalid_min_score(self, earned, max_possible, result): self.setup_gating_milestone(None) answer_problem(self.course, self.request, self.gating_prob1, earned, max_possible) self.verify_user_has_prereq_milestone(self.user, expected_has_milestone=result)
def test_ungating_when_fulfilled(self, earned, max_possible, result): self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=False) self.assert_access_to_gated_content(self.non_staff_user, expected_access=False) answer_problem(self.course, self.request, self.gating_prob1, earned, max_possible) self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=result) self.assert_access_to_gated_content(self.non_staff_user, expected_access=result)
def test_ungating_when_fulfilled(self, earned, max_possible, result): self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=False) self.assert_access_to_gated_content(self.non_staff_user) answer_problem(self.course, self.request, self.gating_prob1, earned, max_possible) self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=result) self.assert_access_to_gated_content(self.non_staff_user)
def test_ungating_when_fulfilled(self, earned, max_possible, result): self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=False) self.assert_access_to_gated_content(self.non_staff_user) with completion_waffle.waffle().override(completion_waffle.ENABLE_COMPLETION_TRACKING, True): answer_problem(self.course, self.request, self.gating_prob1, earned, max_possible) self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=result) self.assert_access_to_gated_content(self.non_staff_user)
def test_ungating_when_fulfilled(self, earned, max_possible, result): self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=False) self.assert_access_to_gated_content(self.non_staff_user) with override_waffle_switch(ENABLE_COMPLETION_TRACKING_SWITCH, True): answer_problem(self.course, self.request, self.gating_prob1, earned, max_possible) self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=result) self.assert_access_to_gated_content(self.non_staff_user)
def test_min_score_achieved(self, earned, max_possible, result): self.setup_gating_milestone(50) self.verify_user_has_prereq_milestone(self.user, expected_has_milestone=False) self.verify_access_to_gated_content(self.user, expected_access=False) answer_problem(self.course, self.request, self.gating_prob1, earned, max_possible) self.verify_user_has_prereq_milestone(self.user, expected_has_milestone=result) self.verify_access_to_gated_content(self.user, expected_access=result)
def test_delete_student_state(self, _crum_mock): problem_location = self.problem.location self._get_subsection_grade_and_verify(0, 1, 0, 1) answer_problem(course=self.course, request=self.request, problem=self.problem, score=1, max_value=1) self._get_subsection_grade_and_verify(1, 1, 1, 1) # Delete student state using the instructor dash reset_student_attempts( self.course.id, self.user, problem_location, requesting_user=self.instructor, delete_module=True, ) # Verify that the student's grades are reset self._get_subsection_grade_and_verify(0, 1, 0, 1)
def test_gated_content_always_in_grades(self): # start with a grade from a non-gated subsection answer_problem(self.course, self.request, self.prob3, 10, 10) # verify gated status and overall course grade percentage self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=False) self.assert_access_to_gated_content(self.non_staff_user, expected_access=False) self.assert_course_grade(self.non_staff_user, .33) # fulfill the gated requirements answer_problem(self.course, self.request, self.gating_prob1, 10, 10) # verify gated status and overall course grade percentage self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=True) self.assert_access_to_gated_content(self.non_staff_user, expected_access=True) self.assert_course_grade(self.non_staff_user, .67)
def test_gated_content_always_in_grades(self): # start with a grade from a non-gated subsection answer_problem(self.course, self.request, self.prob3, 10, 10) # verify gated status and overall course grade percentage self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=False) self.assert_access_to_gated_content(self.non_staff_user) self.assert_course_grade(self.non_staff_user, .33) # fulfill the gated requirements answer_problem(self.course, self.request, self.gating_prob1, 10, 10) # verify gated status and overall course grade percentage self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=True) self.assert_access_to_gated_content(self.non_staff_user) self.assert_course_grade(self.non_staff_user, .67)
def test_gated_content_always_in_grades(self): with override_waffle_switch(completion_waffle.ENABLE_COMPLETION_TRACKING_SWITCH, True): # start with a grade from a non-gated subsection answer_problem(self.course, self.request, self.prob3, 10, 10) # verify gated status and overall course grade percentage self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=False) self.assert_access_to_gated_content(self.non_staff_user) self.assert_course_grade(self.non_staff_user, .33) # fulfill the gated requirements answer_problem(self.course, self.request, self.gating_prob1, 10, 10) # verify gated status and overall course grade percentage self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=True) self.assert_access_to_gated_content(self.non_staff_user) self.assert_course_grade(self.non_staff_user, .67)
def test_course_grade_considers_subsection_grade_visibility( self, is_staff, expected_percent): """ Verify that the grade & is_passing info we send out is for visible grades only. Assumes that grading policy is the default one (search for DEFAULT_GRADING_POLICY). """ if is_staff: self.switch_to_staff() CourseEnrollment.enroll(self.user, self.course.id) tomorrow = now() + timedelta(days=1) with self.store.bulk_operations(self.course.id): never = self.add_subsection_with_problem(format='Homework', show_correctness='never') always = self.add_subsection_with_problem( format='Midterm Exam', show_correctness='always') past_due = self.add_subsection_with_problem( format='Final Exam', show_correctness='past_due', due=tomorrow) answer_problem(self.course, get_mock_request(self.user), never) answer_problem(self.course, get_mock_request(self.user), always) answer_problem(self.course, get_mock_request(self.user), past_due) # First, confirm the grade in the database - it should never change based on user state. # This is midterm and final and a single problem added together. assert CourseGradeFactory().read(self.user, self.course).percent == 0.72 response = self.client.get(self.url) assert response.status_code == 200 assert response.data['course_grade']['percent'] == expected_percent assert response.data['course_grade']['is_passing'] == (expected_percent >= 0.5)
def test_user_course_grades_and_course_average(self): self.users_and_problem_setup() # Just two users for now CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id) CourseEnrollmentFactory.create(user=self.user2, course_id=self.course.id) # Ensure the the baseline score and average are 0 self.login_and_enroll() response = self.api_response( expected_response_code=None, data={'course_id': unicode(self.course.id), 'username': self.user.username} ) self.assertEqual(response.status_code, 200) self.assertEqual(response.data['course_grade'], 0) self.assertEqual(response.data['course_average_grade'], 0) # Answer some problems self.request.user = self.user answer_problem(self.course, self.request, self.problem, score=1, max_value=1) self.request.user = self.user2 answer_problem(self.course, self.request, self.problem, score=1, max_value=5) # Calculate the expected course average user_grade = 0.1 StudentGradebook.objects.update_or_create( user=self.user, course_id=self.course.id, defaults={ 'grade': user_grade, 'proforma_grade': 1, 'is_passed': True, } ) user2_grade = 0.3 StudentGradebook.objects.update_or_create( user=self.user2, course_id=self.course.id, defaults={ 'grade': user2_grade, 'proforma_grade': 1, 'is_passed': True, } ) course_avg_grade = (user_grade + user2_grade) / float(2) self.assertTrue(course_avg_grade > 0) self.login_and_enroll() response = self.api_response( expected_response_code=None, data={'course_id': unicode(self.course.id), 'username': self.user.username} ) self.assertEqual(response.status_code, 200) self.assertEqual(response.data['course_grade'], user_grade) self.assertEqual(response.data['course_average_grade'], course_avg_grade) # Enroll another user, with a higher grade, # and check that the average grade is updated self.request.user = self.user3 CourseEnrollmentFactory.create(user=self.user3, course_id=self.course.id) answer_problem(self.course, self.request, self.problem, score=10, max_value=1) user3_grade = 0.5 StudentGradebook.objects.update_or_create( user=self.user3, course_id=self.course.id, defaults={ 'grade': user3_grade, 'proforma_grade': 1, 'is_passed': True, } ) new_course_avg_grade = (user_grade + user2_grade + user3_grade) / float(3) response = self.api_response( expected_response_code=None, data={'course_id': unicode(self.course.id), 'username': self.user.username} ) self.assertEqual(response.status_code, 200) self.assertEqual(response.data['course_grade'], user_grade) self.assertEqual(response.data['course_average_grade'], new_course_avg_grade) self.assertTrue(new_course_avg_grade > course_avg_grade)
def setUp(self): super(TestExpectedGrading, self).setUp() self.course = CourseFactory.create() with self.store.bulk_operations(self.course.id): self.a = ItemFactory.create(parent=self.course, category="chapter", display_name="a") metadata = {'format': 'Homework', 'graded': True} self.b = ItemFactory.create(parent=self.a, category="sequential", display_name="b", metadata=metadata) self.c = ItemFactory.create(parent=self.a, category="sequential", display_name="c", metadata=metadata) self.d = ItemFactory.create(parent=self.b, category="vertical", display_name="d") self.e = ItemFactory.create(parent=self.b, category="vertical", display_name="e") self.f = ItemFactory.create(parent=self.b, category="vertical", display_name="f") self.g = ItemFactory.create(parent=self.c, category="vertical", display_name="g") metadata.pop('graded') self.h = ItemFactory.create(parent=self.d, category="problem", display_name="h", metadata=metadata) self.i = ItemFactory.create(parent=self.d, category="problem", display_name="i", metadata=metadata) self.j = ItemFactory.create(parent=self.e, category="problem", display_name="j", metadata=metadata) self.k = ItemFactory.create(parent=self.e, category="html", display_name="k") self.l = ItemFactory.create(parent=self.e, category="problem", display_name="l", metadata=metadata) self.m = ItemFactory.create(parent=self.f, category="html", display_name="m") self.n = ItemFactory.create(parent=self.g, category="problem", display_name="n", metadata=metadata) grading_policy = { "GRADER": [ { "type": "Homework", "min_count": 1, "drop_count": 0, "short_label": "HW", "weight": 1.0, }, ], "GRADE_CUTOFFS": { "Pass": 0.5, }, } self.course.set_grading_policy(grading_policy) self.store.update_item(self.course, 0) self.course = self.store.get_course(self.course.id) self.request = get_mock_request(UserFactory()) CourseEnrollment.enroll(self.request.user, self.course.id) answer_problem(self.course, self.request, self.h, score=2, max_value=5) answer_problem(self.course, self.request, self.i, score=3, max_value=5) answer_problem(self.course, self.request, self.j, score=0, max_value=1) answer_problem(self.course, self.request, self.l, score=1, max_value=3) answer_problem(self.course, self.request, self.n, score=6, max_value=10)
def _build_from_tree(self, tree): """ Builds course from tree. Tree is stored in dict in a view like this: { "SectionName1":{ "SubsectionName11_or_smth_else":("assignment_category" if graded, { "BlockName111":(weight or None(means 'html'), { "ProblemOrHtmlName":(earn, max) } }) } } """ course = self.course request = self.request self.course_tree = {} to_answer = [] for section_name, section_tree in tree.iteritems(): current_section = ItemFactory.create(parent=course, category="chapter", display_name=section_name) self.course_tree[section_name] = current_section for subsection_name, subsection_pair in section_tree.iteritems(): assignment_category, subsection_tree = subsection_pair current_subsection = ItemFactory.create( parent=current_section, category="sequential", display_name=subsection_name, graded=assignment_category is not None, format=assignment_category or "") self.course_tree[subsection_name] = current_subsection for unit_name, unit_pair in subsection_tree.iteritems(): weight, unit_tree = unit_pair metadata = {} if (weight is None) else {"weight": weight} current_unit = ItemFactory.create( parent=current_subsection, category="vertical", display_name=unit_name, metadata=metadata) self.course_tree[unit_name] = current_unit for problem_name, problem_pair in unit_tree.iteritems(): earned, max_ = problem_pair kwargs = { "parent": current_unit, "display_name": problem_name, "category": "html" if max_ is None else "problem" } if assignment_category and not (max is None): kwargs["format"] = assignment_category current_problem = ItemFactory.create(**kwargs) if not (max_ is None): to_answer.append((current_problem, (earned, max_))) self.course_tree[problem_name] = current_problem # Somehow 'answer_problem' must be after course building, otherwise # items not created for problem, pair in to_answer: earned, max_ = pair answer_problem(course, request, problem, score=earned, max_value=max_)
def test_invalid_min_score(self, earned, max_possible, result, mock_log): self.setup_gating_milestone(None) answer_problem(self.course, self.request, self.gating_prob1, earned, max_possible) self.verify_user_has_prereq_milestone(self.user, expected_has_milestone=result) self.assertTrue(mock_log.called)
def test_orphaned_xblock(self): with patch('lms.djangoapps.gating.signals.gating_api.evaluate_prerequisite') as mock_handler: self.assertFalse(mock_handler.called) answer_problem(self.course, self.request, self.orphan, 1, 1) self.assertFalse(mock_handler.called)
def test_no_prerequisites(self, mock_milestones): answer_problem(self.course, self.request, self.gating_prob1, 1, 1) self.assertFalse(mock_milestones.called)
def test_no_gated_content(self, mock_milestones): gating_api.add_prerequisite(self.course.id, self.seq1.location) answer_problem(self.course, self.request, self.gating_prob1, 1, 1) self.assertFalse(mock_milestones.called)