def test_create_bad_params(self): """ Confirms create will fail if params are missing. """ del self.params["earned_graded"] with self.assertRaises(IntegrityError): PersistentSubsectionGrade.create_grade(**self.params)
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_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 __init__(self, context, users): self.certs = _CertificateBulkContext(context, users) self.teams = _TeamBulkContext(context, users) self.enrollments = _EnrollmentBulkContext(context, users) bulk_cache_cohorts(context.course_id, users) BulkRoleCache.prefetch(users) PersistentCourseGrade.prefetch(context.course_id, users) PersistentSubsectionGrade.prefetch(context.course_id, users) BulkCourseTags.prefetch(context.course_id, users)
def test_update_or_create_grade(self, already_created): created_grade = PersistentSubsectionGrade.create_grade(**self.params) if already_created else None self.params["earned_all"] = 7 updated_grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) self.assertEqual(updated_grade.earned_all, 7) if already_created: self.assertEqual(created_grade.id, updated_grade.id) self.assertEqual(created_grade.earned_all, 6)
def test_logging_for_save(self): with patch('lms.djangoapps.grades.models.log') as log_mock: PersistentSubsectionGrade.save_grade(**self.params) read_grade = PersistentSubsectionGrade.read_grade( user_id=self.params["user_id"], usage_key=self.params["usage_key"], ) log_mock.info.assert_called_with( u"Persistent Grades: Grade model saved: {0}".format(read_grade) )
def _assert_grades_absent_for_courses(self, course_keys): """ Assert grades for given courses do not exist. """ for course_key in course_keys: with self.assertRaises(PersistentCourseGrade.DoesNotExist): PersistentCourseGrade.read_course_grade(self.user_ids[0], course_key) for subsection_key in self.subsection_keys_by_course[course_key]: with self.assertRaises(PersistentSubsectionGrade.DoesNotExist): PersistentSubsectionGrade.read_grade(self.user_ids[0], subsection_key)
def test_save(self, already_created): if already_created: PersistentSubsectionGrade.objects.create(**self.params) module_prefix = "lms.djangoapps.grades.models.PersistentSubsectionGrade." with patch( module_prefix + "objects.get_or_create", wraps=PersistentSubsectionGrade.objects.get_or_create ) as mock_get_or_create: with patch(module_prefix + "update") as mock_update: PersistentSubsectionGrade.save_grade(**self.params) self.assertTrue(mock_get_or_create.called) self.assertEqual(mock_update.called, already_created)
def _assert_grades_absent_for_courses(self, course_keys, db_table=None): """ Assert grades for given courses do not exist. """ for course_key in course_keys: if db_table == "course" or db_table is None: with self.assertRaises(PersistentCourseGrade.DoesNotExist): PersistentCourseGrade.read_course_grade(self.user_ids[0], course_key) if db_table == "subsection" or db_table is None: for subsection_key in self.subsection_keys_by_course[course_key]: with self.assertRaises(PersistentSubsectionGrade.DoesNotExist): PersistentSubsectionGrade.read_grade(self.user_ids[0], subsection_key)
def bulk_gradebook_view_context(course_key, users): """ Prefetches all course and subsection grades in the given course for the given list of users, also, fetch all the score relavant data, storing the result in a RequestCache and deleting grades on context exit. """ PersistentSubsectionGrade.prefetch(course_key, users) PersistentCourseGrade.prefetch(course_key, users) CourseEnrollment.bulk_fetch_enrollment_states(users, course_key) cohorts.bulk_cache_cohorts(course_key, users) BulkRoleCache.prefetch(users) yield PersistentSubsectionGrade.clear_prefetched_data(course_key) PersistentCourseGrade.clear_prefetched_data(course_key)
def test_create(self): """ Tests model creation, and confirms error when trying to recreate model. """ created_grade = PersistentSubsectionGrade.create_grade(**self.params) with self.assertNumQueries(1): read_grade = PersistentSubsectionGrade.read_grade( user_id=self.params["user_id"], usage_key=self.params["usage_key"], ) self.assertEqual(created_grade, read_grade) self.assertEqual(read_grade.visible_blocks.blocks, self.block_records) with self.assertRaises(IntegrityError): PersistentSubsectionGrade.create_grade(**self.params)
def update(self, subsection, only_if_higher=None, score_deleted=False): """ Updates the SubsectionGrade object for the student and subsection. """ self._log_event(log.debug, u"update, subsection: {}".format(subsection.location), subsection) calculated_grade = CreateSubsectionGrade( subsection, self.course_data.structure, self._submissions_scores, self._csm_scores, ) if should_persist_grades(self.course_data.course_key): if only_if_higher: try: grade_model = PersistentSubsectionGrade.read_grade(self.student.id, subsection.location) except PersistentSubsectionGrade.DoesNotExist: pass else: orig_subsection_grade = ReadSubsectionGrade(subsection, grade_model, self) if not is_score_higher_or_equal( orig_subsection_grade.graded_total.earned, orig_subsection_grade.graded_total.possible, calculated_grade.graded_total.earned, calculated_grade.graded_total.possible, ): return orig_subsection_grade grade_model = calculated_grade.update_or_create_model(self.student, score_deleted) self._update_saved_subsection_grade(subsection.location, grade_model) return calculated_grade
def setUp(self, **kwargs): super(GradesServiceTests, self).setUp() self.service = GradesService() self.course = CourseFactory.create(org='edX', number='DemoX', display_name='Demo_Course') self.subsection = ItemFactory.create(parent=self.course, category="subsection", display_name="Subsection") self.user = UserFactory() self.grade = PersistentSubsectionGrade.update_or_create_grade( user_id=self.user.id, course_id=self.course.id, usage_key=self.subsection.location, first_attempted=None, visible_blocks=[], earned_all=6.0, possible_all=6.0, earned_graded=5.0, possible_graded=5.0 ) self.signal_patcher = patch('lms.djangoapps.grades.signals.signals.SUBSECTION_OVERRIDE_CHANGED.send') self.mock_signal = self.signal_patcher.start() self.id_patcher = patch('lms.djangoapps.grades.services.create_new_event_transaction_id') self.mock_create_id = self.id_patcher.start() self.mock_create_id.return_value = 1 self.type_patcher = patch('lms.djangoapps.grades.services.set_event_transaction_type') self.mock_set_type = self.type_patcher.start() self.flag_patcher = patch('lms.djangoapps.grades.services.waffle_flags') self.mock_waffle_flags = self.flag_patcher.start() self.mock_waffle_flags.return_value = { REJECTED_EXAM_OVERRIDES_GRADE: MockWaffleFlag(True) }
def update(self, subsection, only_if_higher=None): """ Updates the SubsectionGrade object for the student and subsection. """ # Save ourselves the extra queries if the course does not persist # subsection grades. if not PersistentGradesEnabledFlag.feature_enabled(self.course.id): return self._log_event(log.warning, u"update, subsection: {}".format(subsection.location), subsection) calculated_grade = SubsectionGrade(subsection).init_from_structure( self.student, self.course_structure, self._submissions_scores, self._csm_scores, ) if only_if_higher: try: grade_model = PersistentSubsectionGrade.read_grade(self.student.id, subsection.location) except PersistentSubsectionGrade.DoesNotExist: pass else: orig_subsection_grade = SubsectionGrade(subsection).init_from_model( self.student, grade_model, self.course_structure, self._submissions_scores, self._csm_scores, ) if not is_score_higher( orig_subsection_grade.graded_total.earned, orig_subsection_grade.graded_total.possible, calculated_grade.graded_total.earned, calculated_grade.graded_total.possible, ): return orig_subsection_grade grade_model = calculated_grade.update_or_create_model(self.student) self._update_saved_subsection_grade(subsection.location, grade_model) return calculated_grade
def test_update_grade(self): """ Tests model update, and confirms error when updating a nonexistent model. """ with self.assertRaises(PersistentSubsectionGrade.DoesNotExist): PersistentSubsectionGrade.update_grade(**self.params) PersistentSubsectionGrade.objects.create(**self.params) self.params['earned_all'] = 12 self.params['earned_graded'] = 8 PersistentSubsectionGrade.update_grade(**self.params) read_grade = PersistentSubsectionGrade.read_grade( user_id=self.params["user_id"], usage_key=self.params["usage_key"], ) self.assertEqual(read_grade.earned_all, 12) self.assertEqual(read_grade.earned_graded, 8)
def update_or_create_model(self, student): """ Saves or updates the subsection grade in a persisted model. """ if self._should_persist_per_attempted: self._log_event(log.debug, u"update_or_create_model", student) return PersistentSubsectionGrade.update_or_create_grade(**self._persisted_model_params(student))
def test_update_or_create_attempted(self, is_active, expected_first_attempted): with freeze_time(now()): if expected_first_attempted is None: expected_first_attempted = now() with waffle.waffle().override(waffle.ESTIMATE_FIRST_ATTEMPTED, active=is_active): grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) self.assertEqual(grade.first_attempted, expected_first_attempted)
def _assert_grades_exist_for_courses(self, course_keys): """ Assert grades for given courses exist. """ for course_key in course_keys: self.assertIsNotNone(PersistentCourseGrade.read_course_grade(self.user_ids[0], course_key)) for subsection_key in self.subsection_keys_by_course[course_key]: self.assertIsNotNone(PersistentSubsectionGrade.read_grade(self.user_ids[0], subsection_key))
def test_update_or_create_grade(self, already_created): created_grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) if already_created else None self.params["earned_all"] = 7 updated_grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) self.assertEqual(updated_grade.earned_all, 7) if already_created: self.assertEqual(created_grade.id, updated_grade.id) self.assertEqual(created_grade.earned_all, 6) with self.assertNumQueries(1): read_grade = PersistentSubsectionGrade.read_grade( user_id=self.params["user_id"], usage_key=self.params["usage_key"], ) self.assertEqual(updated_grade, read_grade) self.assertEqual(read_grade.visible_blocks.blocks, self.block_records)
def test_persistent_grades_enabled_on_course(self, default_store, num_mongo_queries, num_sql_queries): with self.store.default_store(default_store): self.set_up_course(enable_persistent_grades=True) with check_mongo_calls(num_mongo_queries): with self.assertNumQueries(num_sql_queries): self._apply_recalculate_subsection_grade() self.assertIsNotNone(PersistentCourseGrade.read(self.user.id, self.course.id)) self.assertGreater(len(PersistentSubsectionGrade.bulk_read_grades(self.user.id, self.course.id)), 0)
def test_unattempted(self): self.params['first_attempted'] = None self.params['earned_all'] = 0.0 self.params['earned_graded'] = 0.0 grade = PersistentSubsectionGrade.create_grade(**self.params) self.assertIsNone(grade.first_attempted) self.assertEqual(grade.earned_all, 0.0) self.assertEqual(grade.earned_graded, 0.0)
def bulk_create_models(cls, student, subsection_grades, course_key): """ Saves the subsection grade in a persisted model. """ return PersistentSubsectionGrade.bulk_create_grades( [subsection_grade._persisted_model_params(student) for subsection_grade in subsection_grades], # pylint: disable=protected-access course_key, )
def test_persistent_grades_not_enabled_on_course(self, default_store, num_mongo_queries, num_sql_queries): with self.store.default_store(default_store): self.set_up_course(enable_persistent_grades=False) with check_mongo_calls(num_mongo_queries): with self.assertNumQueries(num_sql_queries): self._apply_recalculate_subsection_grade() with self.assertRaises(PersistentCourseGrade.DoesNotExist): PersistentCourseGrade.read(self.user.id, self.course.id) self.assertEqual(len(PersistentSubsectionGrade.bulk_read_grades(self.user.id, self.course.id)), 0)
def bulk_create_models(cls, student, subsection_grades, course_key): """ Saves the subsection grade in a persisted model. """ params = [ subsection_grade._persisted_model_params(student) for subsection_grade in subsection_grades # pylint: disable=protected-access if subsection_grade._should_persist_per_attempted() # pylint: disable=protected-access ] return PersistentSubsectionGrade.bulk_create_grades(params, course_key)
def bulk_create_models(cls, student, subsection_grades, course_key): """ Saves the subsection grade in a persisted model. """ subsection_grades = filter(lambda subs_grade: subs_grade._should_persist_per_attempted, subsection_grades) return PersistentSubsectionGrade.bulk_create_grades( [subsection_grade._persisted_model_params(student) for subsection_grade in subsection_grades], # pylint: disable=protected-access course_key, )
def _assert_grades_exist_for_courses(self, course_keys, db_table=None): """ Assert grades for given courses exist. """ for course_key in course_keys: if db_table == "course" or db_table is None: self.assertIsNotNone(PersistentCourseGrade.read_course_grade(self.user_ids[0], course_key)) if db_table == "subsection" or db_table is None: for subsection_key in self.subsection_keys_by_course[course_key]: self.assertIsNotNone(PersistentSubsectionGrade.read_grade(self.user_ids[0], subsection_key))
def _update_or_create_grades(self, courses_keys=None): """ Creates grades for all courses and subsections. """ if courses_keys is None: courses_keys = self.course_keys course_grade_params = { "course_version": "JoeMcEwing", "course_edited_timestamp": datetime( year=2016, month=8, day=1, hour=18, minute=53, second=24, microsecond=354741, ), "percent_grade": 77.7, "letter_grade": "Great job", "passed": True, } subsection_grade_params = { "course_version": "deadbeef", "subtree_edited_timestamp": "2016-08-01 18:53:24.354741", "earned_all": 6.0, "possible_all": 12.0, "earned_graded": 6.0, "possible_graded": 8.0, "visible_blocks": MagicMock(), "attempted": True, } for course_key in courses_keys: for user_id in self.user_ids: course_grade_params['user_id'] = user_id course_grade_params['course_id'] = course_key PersistentCourseGrade.update_or_create_course_grade(**course_grade_params) for subsection_key in self.subsection_keys_by_course[course_key]: subsection_grade_params['user_id'] = user_id subsection_grade_params['usage_key'] = subsection_key PersistentSubsectionGrade.update_or_create_grade(**subsection_grade_params)
def _get_bulk_cached_subsection_grades(self): """ Returns and caches (for future access) the results of a bulk retrieval of all subsection grades in the course. """ if self._cached_subsection_grades is None: self._cached_subsection_grades = { record.full_usage_key: record for record in PersistentSubsectionGrade.bulk_read_grades(self.student.id, self.course.id) } return self._cached_subsection_grades
def save(self, student, subsection, course): """ Persist the SubsectionGrade. """ visible_blocks = [ BlockRecord(location, weight, score.possible) for location, (score, weight) in self.locations_to_weighted_scores.iteritems() ] PersistentSubsectionGrade.save_grade( user_id=student.id, usage_key=self.location, course_version=getattr(course, 'course_version', None), subtree_edited_timestamp=subsection.subtree_edited_on, earned_all=self.all_total.earned, possible_all=self.all_total.possible, earned_graded=self.graded_total.earned, possible_graded=self.graded_total.possible, visible_blocks=visible_blocks, )
def _get_saved_subsection_grade(self, subsection_usage_key): """ Returns the saved value of the subsection grade for the given subsection usage key, caching the value. Returns None if not found. """ if self._cached_subsection_grades is None: self._cached_subsection_grades = { record.full_usage_key: record for record in PersistentSubsectionGrade.bulk_read_grades(self.student.id, self.course.id) } return self._cached_subsection_grades.get(subsection_usage_key)
def test_override_is_created(self): """ Test that when we make multiple requests to update grades for the same user/subsection, the score from the most recent request is recorded. """ with override_waffle_flag(self.waffle_flag, active=True): self.login_staff() post_data = [{ 'user_id': self.student.id, 'usage_id': text_type( self.subsections[self.chapter_1.location][0].location), 'grade': { 'earned_all_override': 3, 'possible_all_override': 3, 'earned_graded_override': 2, 'possible_graded_override': 2, }, }, { 'user_id': self.student.id, 'usage_id': text_type( self.subsections[self.chapter_1.location][1].location), 'grade': { 'earned_all_override': 1, 'possible_all_override': 4, 'earned_graded_override': 1, 'possible_graded_override': 4, }, }] resp = self.client.post( self.get_url(), data=json.dumps(post_data), content_type='application/json', ) expected_data = [ { 'user_id': self.student.id, 'usage_id': text_type( self.subsections[self.chapter_1.location][0].location), 'success': True, 'reason': None, }, { 'user_id': self.student.id, 'usage_id': text_type( self.subsections[self.chapter_1.location][1].location), 'success': True, 'reason': None, }, ] self.assertEqual(status.HTTP_202_ACCEPTED, resp.status_code) self.assertEqual(expected_data, resp.data) second_post_data = [ { 'user_id': self.student.id, 'usage_id': text_type( self.subsections[self.chapter_1.location][1].location), 'grade': { 'earned_all_override': 3, 'possible_all_override': 4, 'earned_graded_override': 3, 'possible_graded_override': 4, }, }, ] self.client.post( self.get_url(), data=json.dumps(second_post_data), content_type='application/json', ) GradeFields = namedtuple('GradeFields', [ 'earned_all', 'possible_all', 'earned_graded', 'possible_graded' ]) # We should now have PersistentSubsectionGradeOverride records corresponding to # our bulk-update request, and PersistentSubsectionGrade records with grade values # equal to those of the override. for usage_key, expected_grades in ( (self.subsections[self.chapter_1.location][0].location, GradeFields(3, 3, 2, 2)), (self.subsections[self.chapter_1.location][1].location, GradeFields(3, 4, 3, 4)), ): # this selects related PersistentSubsectionGradeOverride objects. grade = PersistentSubsectionGrade.read_grade( user_id=self.student.id, usage_key=usage_key, ) for field_name in expected_grades._fields: expected_value = getattr(expected_grades, field_name) self.assertEqual(expected_value, getattr(grade, field_name)) self.assertEqual( expected_value, getattr(grade.override, field_name + '_override'))
def clear_prefetched_course_grades(course_key): _PersistentCourseGrade.clear_prefetched_data(course_key) _PersistentSubsectionGrade.clear_prefetched_data(course_key)
def prefetch_course_and_subsection_grades(course_key, users): _PersistentCourseGrade.prefetch(course_key, users) _PersistentSubsectionGrade.prefetch(course_key, users)
def update_or_create_model(self, student): """ Saves or updates the subsection grade in a persisted model. """ self._log_event(log.info, u"update_or_create_model", student) return PersistentSubsectionGrade.update_or_create_grade(**self._persisted_model_params(student))
def get(self, request, subsection_id): """ Returns subection grade data, override grade data and a history of changes made to a specific users specific subsection grade. Args: subsection_id: String representation of a usage_key, which is an opaque key of a persistant subection grade. user_id: An integer represenation of a user """ try: usage_key = UsageKey.from_string(subsection_id) except InvalidKeyError: raise self.api_error(status_code=status.HTTP_404_NOT_FOUND, developer_message='Invalid UsageKey', error_code='invalid_usage_key') if not has_course_author_access(request.user, usage_key.course_key): raise DeveloperErrorViewMixin.api_error( status_code=status.HTTP_403_FORBIDDEN, developer_message= 'The requesting user does not have course author permissions.', error_code='user_permissions', ) try: user_id = int(request.GET.get('user_id')) except ValueError: raise self.api_error(status_code=status.HTTP_404_NOT_FOUND, developer_message='Invalid UserID', error_code='invalid_user_id') try: original_grade = PersistentSubsectionGrade.read_grade( user_id, usage_key) except PersistentSubsectionGrade.DoesNotExist: results = SubsectionGradeResponseSerializer({ 'original_grade': None, 'override': None, 'history': [], 'subsection_id': usage_key, 'user_id': user_id, 'course_id': None, }) return Response(results.data) try: override = original_grade.override history = PersistentSubsectionGradeOverrideHistory.objects.filter( override_id=override.id) except PersistentSubsectionGradeOverride.DoesNotExist: override = None history = [] results = SubsectionGradeResponseSerializer({ 'original_grade': original_grade, 'override': override, 'history': history, 'subsection_id': original_grade.usage_key, 'user_id': original_grade.user_id, 'course_id': original_grade.course_id, }) return Response(results.data)
def test_first_attempted_not_changed_on_update(self): PersistentSubsectionGrade.update_or_create_grade(**self.params) moment = now() grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) self.assertLess(grade.first_attempted, moment)
def test_update_or_create_inconsistent_unattempted(self): self.params['attempted'] = False self.params['earned_all'] = 1.0 self.params['earned_graded'] = 1.0 with self.assertRaises(ValidationError): PersistentSubsectionGrade.update_or_create_grade(**self.params)
def test_unattempted_save_does_not_remove_attempt(self): PersistentSubsectionGrade.update_or_create_grade(**self.params) self.params['first_attempted'] = None grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) self.assertIsInstance(grade.first_attempted, datetime) self.assertEqual(grade.earned_all, 6.0)
def test_non_optional_fields(self, field, error): del self.params[field] with self.assertRaises(error): PersistentSubsectionGrade.create_grade(**self.params)
def test_update_or_create_attempted(self): grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) self.assertIsInstance(grade.first_attempted, datetime)
def test_create_inconsistent_unattempted(self): self.params['attempted'] = False with self.assertRaises(ValidationError): PersistentSubsectionGrade.create_grade(**self.params)
def test_create_event(self): with patch('lms.djangoapps.grades.events.tracker') as tracker_mock: grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) self._assert_tracker_emitted_event(tracker_mock, grade)
def get(self, request, subsection_id): """ Returns subection grade data, override grade data and a history of changes made to a specific users specific subsection grade. Args: subsection_id: String representation of a usage_key, which is an opaque key of a persistant subection grade. user_id: An integer represenation of a user """ try: usage_key = UsageKey.from_string(subsection_id) except InvalidKeyError: raise self.api_error(status_code=status.HTTP_404_NOT_FOUND, developer_message='Invalid UsageKey', error_code='invalid_usage_key') if not has_course_author_access(request.user, usage_key.course_key): raise DeveloperErrorViewMixin.api_error( status_code=status.HTTP_403_FORBIDDEN, developer_message= 'The requesting user does not have course author permissions.', error_code='user_permissions', ) try: user_id = int(request.GET.get('user_id')) except ValueError: raise self.api_error(status_code=status.HTTP_404_NOT_FOUND, developer_message='Invalid UserID', error_code='invalid_user_id') success = True err_msg = "" override = None history = [] history_record_limit = request.GET.get('history_record_limit') if history_record_limit is not None: try: history_record_limit = int(history_record_limit) except ValueError: history_record_limit = 0 try: original_grade = PersistentSubsectionGrade.read_grade( user_id, usage_key) if original_grade is not None and hasattr(original_grade, 'override'): override = original_grade.override # pylint: disable=no-member history = list( PersistentSubsectionGradeOverride.history.filter( grade_id=original_grade.id).order_by('history_date') [:history_record_limit]) grade_data = { 'earned_all': original_grade.earned_all, 'possible_all': original_grade.possible_all, 'earned_graded': original_grade.earned_graded, 'possible_graded': original_grade.possible_graded, } except PersistentSubsectionGrade.DoesNotExist: try: grade_data = self._get_grade_data_for_not_attempted_assignment( user_id, usage_key) except SubsectionUnavailableToUserException as exc: success = False err_msg = str(exc) grade_data = { 'earned_all': 0, 'possible_all': 0, 'earned_graded': 0, 'possible_graded': 0, } response_data = { 'success': success, 'original_grade': grade_data, 'override': override, 'history': history, 'subsection_id': usage_key, 'user_id': user_id, 'course_id': usage_key.course_key, } if not success: response_data['error_message'] = err_msg results = SubsectionGradeResponseSerializer(response_data) return Response(results.data)
def create_model(self, student): """ Saves the subsection grade in a persisted model. """ self._log_event(log.debug, u"create_model", student) return PersistentSubsectionGrade.create_grade(**self._persisted_model_params(student))
def test_course_version_is_optional(self): del self.params["course_version"] PersistentSubsectionGrade.create_grade(**self.params)
def test_non_optional_fields(self, field, error): del self.params[field] with pytest.raises(error): PersistentSubsectionGrade.update_or_create_grade(**self.params)
def test_optional_fields(self, field): del self.params[field] PersistentSubsectionGrade.update_or_create_grade(**self.params)
def test_update_or_create_attempted(self, is_active, expected_first_attempted): if expected_first_attempted is None: expected_first_attempted = now() with waffle.waffle().override(waffle.ESTIMATE_FIRST_ATTEMPTED, active=is_active): grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) self.assertEqual(grade.first_attempted, expected_first_attempted)