def test_enable_disable_globally(self): """ Ensures that the flag, once enabled globally, can also be disabled. """ with persistent_grades_feature_flags( global_flag=True, enabled_for_all_courses=True, ): self.assertTrue(PersistentGradesEnabledFlag.feature_enabled()) self.assertTrue( PersistentGradesEnabledFlag.feature_enabled(self.course_id_1)) with persistent_grades_feature_flags( global_flag=True, enabled_for_all_courses=False, ): self.assertTrue(PersistentGradesEnabledFlag.feature_enabled()) self.assertFalse( PersistentGradesEnabledFlag.feature_enabled( self.course_id_1)) with persistent_grades_feature_flags(global_flag=False, ): self.assertFalse( PersistentGradesEnabledFlag.feature_enabled()) self.assertFalse( PersistentGradesEnabledFlag.feature_enabled( self.course_id_1))
def test_no_recursion_without_persistent_grades(self): """ Course grade signals should not be fired recursively when persistent grades are disabled. """ self.mock_process_signal = Mock() # pylint: disable=attribute-defined-outside-init def handler(**kwargs): """ Mock signal receiver. """ self.mock_process_signal() with persistent_grades_feature_flags(global_flag=False, enabled_for_all_courses=False, course_id=self.course.id, enabled_for_course=False): with override_waffle_switch(AUTO_CERTIFICATE_GENERATION, active=True), mock_get_score(2, 2): COURSE_GRADE_NOW_PASSED.connect(handler) try: CourseGradeFactory().update(self.request.user, self.course) except RecursionError: pytest.fail( "The COURSE_GRADE_NOW_PASSED signal fired recursively." ) self.mock_process_signal.assert_called_once() COURSE_GRADE_NOW_PASSED.disconnect(handler)
def test_create(self): """ Tests to ensure that a persistent subsection grade is created, saved, then fetched on re-request. """ with persistent_grades_feature_flags(global_flag=True, enabled_for_all_courses=False, course_id=self.course.id, enabled_for_course=True): with patch( 'lms.djangoapps.grades.new.subsection_grade.SubsectionGradeFactory._save_grade', wraps=self.subsection_grade_factory._save_grade # pylint: disable=protected-access ) as mock_save_grades: with patch( 'lms.djangoapps.grades.new.subsection_grade.SubsectionGradeFactory._get_saved_grade', wraps=self.subsection_grade_factory._get_saved_grade # pylint: disable=protected-access ) as mock_get_saved_grade: with self.assertNumQueries(22): grade_a = self.subsection_grade_factory.create( self.sequence, self.course_structure, self.course) self.assertTrue(mock_get_saved_grade.called) self.assertTrue(mock_save_grades.called) mock_get_saved_grade.reset_mock() mock_save_grades.reset_mock() with self.assertNumQueries(4): grade_b = self.subsection_grade_factory.create( self.sequence, self.course_structure, self.course) self.assertTrue(mock_get_saved_grade.called) self.assertFalse(mock_save_grades.called) self.assertEqual(grade_a.url_name, grade_b.url_name) self.assertEqual(grade_a.all_total, grade_b.all_total)
def test_course_grade_logging(self): grade_factory = CourseGradeFactory(self.request.user) with persistent_grades_feature_flags(global_flag=True, enabled_for_all_courses=False, course_id=self.course.id, enabled_for_course=True): with patch( 'lms.djangoapps.grades.new.course_grade.log') as log_mock: # the course grade has not been created, so we expect each grade to be created log_statement = u''.join(( u"compute_and_update, read_only: {0}, subsections read/created: {1}/{2}, blocks ", u"accessed: {3}, total graded subsections: {4}")).format( False, 0, 3, 3, 2) self._create_course_grade_and_check_logging( grade_factory, log_mock.warning, log_statement) log_mock.reset_mock() # the course grade has been created, so we expect to read it from the db log_statement = u"load_persisted_grade" self._create_course_grade_and_check_logging( grade_factory, log_mock.info, log_statement) log_mock.reset_mock() # only problem submission, a subsection grade update triggers # a course grade update self.submit_question_answer(u'test_problem_1', {u'2_1': u'choice_choice_2'}) log_statement = u''.join(( u"compute_and_update, read_only: {0}, subsections read/created: {1}/{2}, blocks ", u"accessed: {3}, total graded subsections: {4}")).format( False, 3, 0, 3, 2) self._create_course_grade_and_check_logging( grade_factory, log_mock.warning, log_statement)
def test_course_grade_logging(self): grade_factory = CourseGradeFactory() with persistent_grades_feature_flags(global_flag=True, enabled_for_all_courses=False, course_id=self.course.id, enabled_for_course=True): with patch('lms.djangoapps.grades.new.course_grade_factory.log' ) as log_mock: # returns Zero when no grade, with ASSUME_ZERO_GRADE_IF_ABSENT with waffle().override(ASSUME_ZERO_GRADE_IF_ABSENT, active=True): self._create_course_grade_and_check_logging( grade_factory.create, log_mock, u'CreateZero') # read, but not persisted self._create_course_grade_and_check_logging( grade_factory.create, log_mock, u'Update') # update and persist self._create_course_grade_and_check_logging( grade_factory.update, log_mock, u'Update') # read from persistence, using create self._create_course_grade_and_check_logging( grade_factory.create, log_mock, u'Read') # read from persistence, using read self._create_course_grade_and_check_logging( grade_factory.read, log_mock, u'Read')
def test_course_grade_logging(self): grade_factory = CourseGradeFactory(self.request.user) with persistent_grades_feature_flags( global_flag=True, enabled_for_all_courses=False, course_id=self.course.id, enabled_for_course=True ): with patch('lms.djangoapps.grades.new.course_grade.log') as log_mock: # the course grade has not been created, so we expect each grade to be created self._create_course_grade_and_check_logging( grade_factory, log_mock, read_only=False, subsections_read=0, subsections_created=3, blocks_accessed=3, total_graded_subsections=2) # the course grade has been created, so we expect each grade to be read self._create_course_grade_and_check_logging( grade_factory, log_mock, read_only=False, subsections_read=3, subsections_created=0, blocks_accessed=3, total_graded_subsections=2, )
def test_enable_disable_course_flag(self): """ Ensures that the flag, once enabled for a course, can also be disabled. """ with persistent_grades_feature_flags(global_flag=True, enabled_for_all_courses=False, course_id=self.course_id_1, enabled_for_course=True): self.assertTrue( PersistentGradesEnabledFlag.feature_enabled(self.course_id_1)) # Prior to TNL-5698, creating a second object would fail due to db constraints with persistent_grades_feature_flags(global_flag=True, enabled_for_all_courses=False, course_id=self.course_id_1, enabled_for_course=False): self.assertFalse( PersistentGradesEnabledFlag.feature_enabled( self.course_id_1))
def test_enable_disable_course_flag(self): """ Ensures that the flag, once enabled for a course, can also be disabled. """ with persistent_grades_feature_flags( global_flag=True, enabled_for_all_courses=False, course_id=self.course_id_1, enabled_for_course=True ): self.assertTrue(PersistentGradesEnabledFlag.feature_enabled(self.course_id_1)) # Prior to TNL-5698, creating a second object would fail due to db constraints with persistent_grades_feature_flags( global_flag=True, enabled_for_all_courses=False, course_id=self.course_id_1, enabled_for_course=False ): self.assertFalse(PersistentGradesEnabledFlag.feature_enabled(self.course_id_1))
def test_enable_disable_globally(self): """ Ensures that the flag, once enabled globally, can also be disabled. """ with persistent_grades_feature_flags( global_flag=True, enabled_for_all_courses=True, ): self.assertTrue(PersistentGradesEnabledFlag.feature_enabled()) self.assertTrue(PersistentGradesEnabledFlag.feature_enabled(self.course_id_1)) with persistent_grades_feature_flags( global_flag=True, enabled_for_all_courses=False, ): self.assertTrue(PersistentGradesEnabledFlag.feature_enabled()) self.assertFalse(PersistentGradesEnabledFlag.feature_enabled(self.course_id_1)) with persistent_grades_feature_flags( global_flag=False, ): self.assertFalse(PersistentGradesEnabledFlag.feature_enabled()) self.assertFalse(PersistentGradesEnabledFlag.feature_enabled(self.course_id_1))
def test_course_grade_feature_gating(self, feature_flag, course_setting): # Grades are only saved if the feature flag and the advanced setting are # both set to True. grade_factory = CourseGradeFactory() with persistent_grades_feature_flags( global_flag=feature_flag, enabled_for_all_courses=False, course_id=self.course.id, enabled_for_course=course_setting ): with patch('lms.djangoapps.grades.models.PersistentCourseGrade.read') as mock_read_grade: grade_factory.read(self.request.user, self.course) self.assertEqual(mock_read_grade.called, feature_flag and course_setting)
def test_course_grade_feature_gating(self, feature_flag, course_setting): # Grades are only saved if the feature flag and the advanced setting are # both set to True. grade_factory = CourseGradeFactory(self.request.user) with persistent_grades_feature_flags( global_flag=feature_flag, enabled_for_all_courses=False, course_id=self.course.id, enabled_for_course=course_setting ): with patch('lms.djangoapps.grades.new.course_grade._pretend_to_save_course_grades') as mock_save_grades: grade_factory.create(self.course) self.assertEqual(mock_save_grades.called, feature_flag and course_setting)
def test_subsection_grade_feature_gating(self, feature_flag, course_setting): # Grades are only saved if the feature flag and the advanced setting are # both set to True. with patch( 'lms.djangoapps.grades.models.PersistentSubsectionGrade.bulk_read_grades' ) as mock_read_saved_grade: with persistent_grades_feature_flags( global_flag=feature_flag, enabled_for_all_courses=False, course_id=self.course.id, enabled_for_course=course_setting ): self.subsection_grade_factory.create(self.sequence) self.assertEqual(mock_read_saved_grade.called, feature_flag and course_setting)
def test_subsection_grade_feature_gating(self, feature_flag, course_setting): # Grades are only saved if the feature flag and the advanced setting are # both set to True. with patch( 'lms.djangoapps.grades.models.PersistentSubsectionGrade.bulk_read_grades' ) as mock_read_saved_grade: with persistent_grades_feature_flags( global_flag=feature_flag, enabled_for_all_courses=False, course_id=self.course.id, enabled_for_course=course_setting ): self.subsection_grade_factory.create(self.sequence) self.assertEqual(mock_read_saved_grade.called, feature_flag and course_setting)
def test_persistent_grades_feature_flags(self, global_flag, enabled_for_all_courses, enabled_for_course_1): with persistent_grades_feature_flags( global_flag=global_flag, enabled_for_all_courses=enabled_for_all_courses, course_id=self.course_id_1, enabled_for_course=enabled_for_course_1 ): assert PersistentGradesEnabledFlag.feature_enabled() == global_flag assert PersistentGradesEnabledFlag.feature_enabled( self.course_id_1 ) == (global_flag and (enabled_for_all_courses or enabled_for_course_1)) assert PersistentGradesEnabledFlag.feature_enabled( self.course_id_2 ) == (global_flag and enabled_for_all_courses)
def test_persistent_grades_feature_flags(self, global_flag, enabled_for_all_courses, enabled_for_course_1): with persistent_grades_feature_flags( global_flag=global_flag, enabled_for_all_courses=enabled_for_all_courses, course_id=self.course_id_1, enabled_for_course=enabled_for_course_1 ): self.assertEqual(PersistentGradesEnabledFlag.feature_enabled(), global_flag) self.assertEqual( PersistentGradesEnabledFlag.feature_enabled(self.course_id_1), global_flag and (enabled_for_all_courses or enabled_for_course_1) ) self.assertEqual( PersistentGradesEnabledFlag.feature_enabled(self.course_id_2), global_flag and enabled_for_all_courses )
def test_override_is_visible(self): with persistent_grades_feature_flags(global_flag=True): chapter = ItemFactory(parent=self.course, category='chapter') subsection = ItemFactory.create(parent=chapter, category="sequential", display_name="Subsection") CourseEnrollment.enroll(self.user, self.course.id) params = { "user_id": self.user.id, "usage_key": subsection.location, "course_version": self.course.course_version, "subtree_edited_timestamp": "2016-08-01 18:53:24.354741Z", "earned_all": 6.0, "possible_all": 12.0, "earned_graded": 6.0, "possible_graded": 8.0, "visible_blocks": [], "first_attempted": datetime.now(), } created_grade = PersistentSubsectionGrade.update_or_create_grade( **params) proctoring_failure_comment = "Failed Test Proctoring" PersistentSubsectionGradeOverride.update_or_create_override( requesting_user=self.staff_user, subsection_grade_model=created_grade, earned_all_override=0.0, earned_graded_override=0.0, system=GradeOverrideFeatureEnum.proctoring, feature=GradeOverrideFeatureEnum.proctoring, comment=proctoring_failure_comment) response = self.client.get(self.url) assert response.status_code == 200 sections = response.data['section_scores'] overridden_subsection = sections[1]['subsections'][0] override_entry = overridden_subsection["override"] assert override_entry[ 'system'] == GradeOverrideFeatureEnum.proctoring assert override_entry['reason'] == proctoring_failure_comment
def test_course_grade_logging(self): grade_factory = CourseGradeFactory() with persistent_grades_feature_flags( global_flag=True, enabled_for_all_courses=False, course_id=self.course.id, enabled_for_course=True ): with patch('lms.djangoapps.grades.new.course_grade.log') as log_mock: # the course grade has not been created, so we expect each grade to be created log_statement = u''.join(( u"compute_and_update, read_only: {0}, subsections read/created: {1}/{2}, blocks ", u"accessed: {3}, total graded subsections: {4}" )).format(False, 0, 3, 3, 2) self._create_course_grade_and_check_logging( grade_factory, log_mock.warning, log_statement ) log_mock.reset_mock() # the course grade has been created, so we expect to read it from the db log_statement = u"load_persisted_grade" self._create_course_grade_and_check_logging( grade_factory, log_mock.info, log_statement ) log_mock.reset_mock() # only problem submission, a subsection grade update triggers # a course grade update self.submit_question_answer(u'test_problem_1', {u'2_1': u'choice_choice_2'}) log_statement = u''.join(( u"compute_and_update, read_only: {0}, subsections read/created: {1}/{2}, blocks ", u"accessed: {3}, total graded subsections: {4}" )).format(False, 3, 0, 3, 2) self._create_course_grade_and_check_logging( grade_factory, log_mock.warning, log_statement )
def test_course_grade_logging(self): grade_factory = CourseGradeFactory() with persistent_grades_feature_flags( global_flag=True, enabled_for_all_courses=False, course_id=self.course.id, enabled_for_course=True ): with patch('lms.djangoapps.grades.new.course_grade_factory.log') as log_mock: # read, but not persisted self._create_course_grade_and_check_logging(grade_factory.create, log_mock.info, u'Update') # update and persist self._create_course_grade_and_check_logging(grade_factory.update, log_mock.info, u'Update') # read from persistence, using create self._create_course_grade_and_check_logging(grade_factory.create, log_mock.debug, u'Read') # read from persistence, using read self._create_course_grade_and_check_logging(grade_factory.read, log_mock.debug, u'Read')
def test_course_grade_logging(self): grade_factory = CourseGradeFactory() with persistent_grades_feature_flags( global_flag=True, enabled_for_all_courses=False, course_id=self.course.id, enabled_for_course=True ): with patch('lms.djangoapps.grades.new.course_grade_factory.log') as log_mock: # read, but not persisted self._create_course_grade_and_check_logging(grade_factory.create, log_mock.info, u'Update') # update and persist self._create_course_grade_and_check_logging(grade_factory.update, log_mock.info, u'Update') # read from persistence, using create self._create_course_grade_and_check_logging(grade_factory.create, log_mock.debug, u'Read') # read from persistence, using read self._create_course_grade_and_check_logging(grade_factory.read, log_mock.debug, u'Read')
def test_create(self): """ Tests to ensure that a persistent subsection grade is created, saved, then fetched on re-request. """ with persistent_grades_feature_flags( global_flag=True, enabled_for_all_courses=False, course_id=self.course.id, enabled_for_course=True ): with patch( 'lms.djangoapps.grades.new.subsection_grade.SubsectionGradeFactory._save_grade', wraps=self.subsection_grade_factory._save_grade # pylint: disable=protected-access ) as mock_save_grades: with patch( 'lms.djangoapps.grades.new.subsection_grade.SubsectionGradeFactory._get_saved_grade', wraps=self.subsection_grade_factory._get_saved_grade # pylint: disable=protected-access ) as mock_get_saved_grade: with self.assertNumQueries(22): grade_a = self.subsection_grade_factory.create( self.sequence, self.course_structure, self.course ) self.assertTrue(mock_get_saved_grade.called) self.assertTrue(mock_save_grades.called) mock_get_saved_grade.reset_mock() mock_save_grades.reset_mock() with self.assertNumQueries(4): grade_b = self.subsection_grade_factory.create( self.sequence, self.course_structure, self.course ) self.assertTrue(mock_get_saved_grade.called) self.assertFalse(mock_save_grades.called) self.assertEqual(grade_a.url_name, grade_b.url_name) self.assertEqual(grade_a.all_total, grade_b.all_total)
def test_course_grade_logging(self): grade_factory = CourseGradeFactory() with persistent_grades_feature_flags( global_flag=True, enabled_for_all_courses=False, course_id=self.course.id, enabled_for_course=True ): with patch('lms.djangoapps.grades.new.course_grade_factory.log') as log_mock: # returns Zero when no grade, with ASSUME_ZERO_GRADE_IF_ABSENT with waffle().override(ASSUME_ZERO_GRADE_IF_ABSENT, active=True): self._create_course_grade_and_check_logging(grade_factory.create, log_mock, u'CreateZero') # read, but not persisted self._create_course_grade_and_check_logging(grade_factory.create, log_mock, u'Update') # update and persist self._create_course_grade_and_check_logging(grade_factory.update, log_mock, u'Update') # read from persistence, using create self._create_course_grade_and_check_logging(grade_factory.create, log_mock, u'Read') # read from persistence, using read self._create_course_grade_and_check_logging(grade_factory.read, log_mock, u'Read')