def test_grade_override(self): grade = PersistentSubsectionGrade.create_grade(**self.params) override = PersistentSubsectionGradeOverride(grade=grade, earned_all_override=0.0, earned_graded_override=0.0) override.save() grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) self.assertEqual(grade.earned_all, 0.0) self.assertEqual(grade.earned_graded, 0.0)
def test_get_subsection_grade_percentage_with_override(self): user = self.request.user subsection_key = self.sequence.location with mock_get_score(3, 3): # this update() call creates a persistent grade self.subsection_grade_factory.update(self.sequence) # there should only be one persistent grade persistent_grade = PersistentSubsectionGrade.objects.first() PersistentSubsectionGradeOverride.update_or_create_override( UserFactory( ), # it doesn't matter to us who created the override persistent_grade, earned_graded_override=0, earned_all_override=0, possible_graded_override=3, feature=GradeOverrideFeatureEnum.gradebook, ) # it's important that we stay in the mock_get_score() context here, # since get_subsection_grade_percentage() creates its own SubsectionGradeFactory, # which will in turn make calls to get_score(). grade_percentage = gating_api.get_subsection_grade_percentage( subsection_key, user) assert 0 == grade_percentage
def test_get_subsection_grade_percentage_with_override(self): user = self.request.user subsection_key = self.sequence.location with mock_get_score(3, 3): # this update() call creates a persistent grade self.subsection_grade_factory.update(self.sequence) # there should only be one persistent grade persistent_grade = PersistentSubsectionGrade.objects.first() PersistentSubsectionGradeOverride.update_or_create_override( UserFactory(), # it doesn't matter to us who created the override persistent_grade, earned_graded_override=0, earned_all_override=0, possible_graded_override=3, feature=PersistentSubsectionGradeOverrideHistory.GRADEBOOK, ) # it's important that we stay in the mock_get_score() context here, # since get_subsection_grade_percentage() creates its own SubsectionGradeFactory, # which will in turn make calls to get_score(). grade_percentage = gating_api.get_subsection_grade_percentage(subsection_key, user) assert 0 == grade_percentage
def test_grade_override(self): grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) override = PersistentSubsectionGradeOverride( grade=grade, earned_all_override=0.0, earned_graded_override=0.0) override.save() grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) self.assertEqual(grade.earned_all, 0.0) self.assertEqual(grade.earned_graded, 0.0)
def _create_override(self, request_user, subsection_grade_model, **override_data): """ Helper method to create a `PersistentSubsectionGradeOverride` object and send a `SUBSECTION_OVERRIDE_CHANGED` signal. """ override = PersistentSubsectionGradeOverride.update_or_create_override( requesting_user=request_user, subsection_grade_model=subsection_grade_model, feature=grades_constants.GradeOverrideFeatureEnum.gradebook, **override_data ) set_event_transaction_type(grades_events.SUBSECTION_GRADE_CALCULATED) create_new_event_transaction_id() recalculate_subsection_grade_v3.apply( kwargs=dict( user_id=subsection_grade_model.user_id, anonymous_user_id=None, course_id=text_type(subsection_grade_model.course_id), usage_id=text_type(subsection_grade_model.usage_key), only_if_higher=False, expected_modified_time=to_timestamp(override.modified), score_deleted=False, event_transaction_id=six.text_type(get_event_transaction_id()), event_transaction_type=six.text_type(get_event_transaction_type()), score_db_table=grades_constants.ScoreDatabaseTableEnum.overrides, force_update_subsections=True, ) ) # Emit events to let our tracking system to know we updated subsection grade grades_events.subsection_grade_calculated(subsection_grade_model) return override
def test_grade_override(self): """ Creating a subsection grade override should NOT change the score values of the related PersistentSubsectionGrade. """ grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) override = PersistentSubsectionGradeOverride.update_or_create_override( requesting_user=self.user, subsection_grade_model=grade, earned_all_override=0.0, earned_graded_override=0.0, feature=PersistentSubsectionGradeOverrideHistory.GRADEBOOK, ) grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) self.assertEqual(self.params['earned_all'], grade.earned_all) self.assertEqual(self.params['earned_graded'], grade.earned_graded) # Any score values that aren't specified should use the values from grade as defaults self.assertEqual(0, override.earned_all_override) self.assertEqual(0, override.earned_graded_override) self.assertEqual(grade.possible_all, override.possible_all_override) self.assertEqual(grade.possible_graded, override.possible_graded_override) # An override history record should be created self.assertEqual( 1, PersistentSubsectionGradeOverrideHistory.objects.filter( override_id=override.id).count())
def _create_override(self, request_user, subsection_grade_model, **override_data): """ Helper method to create a `PersistentSubsectionGradeOverride` object and send a `SUBSECTION_OVERRIDE_CHANGED` signal. """ override_data[ 'system'] = grades_constants.GradeOverrideFeatureEnum.gradebook override = PersistentSubsectionGradeOverride.update_or_create_override( requesting_user=request_user, subsection_grade_model=subsection_grade_model, feature=grades_constants.GradeOverrideFeatureEnum.gradebook, **override_data) set_event_transaction_type(grades_events.SUBSECTION_GRADE_CALCULATED) create_new_event_transaction_id() recalculate_subsection_grade_v3.apply(kwargs=dict( user_id=subsection_grade_model.user_id, anonymous_user_id=None, course_id=text_type(subsection_grade_model.course_id), usage_id=text_type(subsection_grade_model.usage_key), only_if_higher=False, expected_modified_time=to_timestamp(override.modified), score_deleted=False, event_transaction_id=six.text_type(get_event_transaction_id()), event_transaction_type=six.text_type(get_event_transaction_type()), score_db_table=grades_constants.ScoreDatabaseTableEnum.overrides, force_update_subsections=True, )) # Emit events to let our tracking system to know we updated subsection grade grades_events.subsection_grade_calculated(subsection_grade_model) return override
def test_grade_override(self): """ Creating a subsection grade override should NOT change the score values of the related PersistentSubsectionGrade. """ grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) override = PersistentSubsectionGradeOverride.update_or_create_override( requesting_user=self.user, subsection_grade_model=grade, earned_all_override=0.0, earned_graded_override=0.0, feature=GradeOverrideFeatureEnum.gradebook, ) grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) self.assertEqual(self.params['earned_all'], grade.earned_all) self.assertEqual(self.params['earned_graded'], grade.earned_graded) history = override.get_history() self.assertEqual(1, len(list(history))) self.assertEqual('+', list(history)[0].history_type) # Any score values that aren't specified should use the values from grade as defaults self.assertEqual(0, override.earned_all_override) self.assertEqual(0, override.earned_graded_override) self.assertEqual(grade.possible_all, override.possible_all_override) self.assertEqual(grade.possible_graded, override.possible_graded_override)
def test_grade_override(self): """ Creating a subsection grade override should NOT change the score values of the related PersistentSubsectionGrade. """ grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) override = PersistentSubsectionGradeOverride.update_or_create_override( requesting_user=self.user, subsection_grade_model=grade, earned_all_override=0.0, earned_graded_override=0.0, feature=GradeOverrideFeatureEnum.gradebook, ) grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) assert self.params['earned_all'] == grade.earned_all assert self.params['earned_graded'] == grade.earned_graded history = override.get_history() assert 1 == len(list(history)) assert '+' == list(history)[0].history_type # Any score values that aren't specified should use the values from grade as defaults assert 0 == override.earned_all_override assert 0 == override.earned_graded_override assert grade.possible_all == override.possible_all_override assert grade.possible_graded == override.possible_graded_override
def update_or_create_override(grade, **kwargs): """ Update or creates a subsection override. """ kwargs['subsection_grade_model'] = grade return _PersistentSubsectionGradeOverride.update_or_create_override( **kwargs)
def test_grade_override(self): """ Creating a subsection grade override should NOT change the score values of the related PersistentSubsectionGrade. """ grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) override = PersistentSubsectionGradeOverride.update_or_create_override( requesting_user=self.user, subsection_grade_model=grade, earned_all_override=0.0, earned_graded_override=0.0, feature=PersistentSubsectionGradeOverrideHistory.GRADEBOOK, ) grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) self.assertEqual(self.params['earned_all'], grade.earned_all) self.assertEqual(self.params['earned_graded'], grade.earned_graded) # Any score values that aren't specified should use the values from grade as defaults self.assertEqual(0, override.earned_all_override) self.assertEqual(0, override.earned_graded_override) self.assertEqual(grade.possible_all, override.possible_all_override) self.assertEqual(grade.possible_graded, override.possible_graded_override) # An override history record should be created self.assertEqual(1, PersistentSubsectionGradeOverrideHistory.objects.filter(override_id=override.id).count())
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 get_subsection_grade_override(user_id, course_key_or_id, usage_key_or_id): """ Finds the subsection grade for user and returns the override for that grade if it exists If override does not exist, returns None. If subsection grade does not exist, will raise an exception. """ usage_key = _get_key(usage_key_or_id, UsageKey) # Verify that a corresponding subsection grade exists for the given user and usage_key # Raises PersistentSubsectionGrade.DoesNotExist if it does not exist. _ = get_subsection_grade(user_id, course_key_or_id, usage_key_or_id) return _PersistentSubsectionGradeOverride.get_override(user_id, usage_key)
def prefetch_grade_overrides_and_visible_blocks(user, course_key): _PersistentSubsectionGradeOverride.prefetch(user.id, course_key) _VisibleBlocks.bulk_read(user.id, course_key)