def test_delete_submission_scores(self, _mock_signal): user = UserFactory() problem_location = self.course_key.make_usage_key('dummy', 'module') # Create a student module for the user StudentModule.objects.create( student=user, course_id=self.course_key, module_state_key=problem_location, state=json.dumps({}) ) # Create a submission and score for the student using the submissions API student_item = { 'student_id': anonymous_id_for_user(user, self.course_key), 'course_id': self.course_key.to_deprecated_string(), 'item_id': problem_location.to_deprecated_string(), 'item_type': 'openassessment' } submission = sub_api.create_submission(student_item, 'test answer') sub_api.set_score(submission['uuid'], 1, 2) # Delete student state using the instructor dash reset_student_attempts( self.course_key, user, problem_location, requesting_user=user, delete_module=True, ) # Verify that the student's scores have been reset in the submissions API score = sub_api.get_score(student_item) self.assertIs(score, None)
def test_reset_team_attempts(self): self.setup_team() team_ora_location = self.team_enabled_ora.location # All teammates should have a student module (except lazy_teammate) self.assertIsNotNone( self.get_student_module(self.user, team_ora_location)) self.assertIsNotNone( self.get_student_module(self.teammate_a, team_ora_location)) self.assertIsNotNone( self.get_student_module(self.teammate_b, team_ora_location)) self.assert_no_student_module(self.lazy_teammate, team_ora_location) reset_student_attempts(self.course_key, self.user, team_ora_location, requesting_user=self.user) # Everyone's state should have had the attempts set to zero but otherwise unchanged attempt_reset_team_state_dict = dict(self.team_state_dict) attempt_reset_team_state_dict['attempts'] = 0 def _assert_student_module(user): student_module = self.get_student_module(user, team_ora_location) self.assertIsNotNone(student_module) student_state = json.loads(student_module.state) self.assertDictEqual(student_state, attempt_reset_team_state_dict) _assert_student_module(self.user) _assert_student_module(self.teammate_a) _assert_student_module(self.teammate_b) # Still should have no state self.assert_no_student_module(self.lazy_teammate, team_ora_location)
def test_reset_student_attempts_children(self): parent_state = json.loads(self.get_state(self.parent.location)) self.assertEqual(parent_state['attempts'], 32) self.assertEqual(parent_state['otherstuff'], 'alsorobots') child_state = json.loads(self.get_state(self.child.location)) self.assertEqual(child_state['attempts'], 10) self.assertEqual(child_state['whatever'], 'things') unrelated_state = json.loads(self.get_state(self.unrelated.location)) self.assertEqual(unrelated_state['attempts'], 12) self.assertEqual(unrelated_state['brains'], 'zombie') reset_student_attempts(self.course_key, self.user, self.parent.location, requesting_user=self.user) parent_state = json.loads(self.get_state(self.parent.location)) self.assertEqual(json.loads(self.get_state(self.parent.location))['attempts'], 0) self.assertEqual(parent_state['otherstuff'], 'alsorobots') child_state = json.loads(self.get_state(self.child.location)) self.assertEqual(child_state['attempts'], 0) self.assertEqual(child_state['whatever'], 'things') unrelated_state = json.loads(self.get_state(self.unrelated.location)) self.assertEqual(unrelated_state['attempts'], 12) self.assertEqual(unrelated_state['brains'], 'zombie')
def test_reset_student_attempts_children(self): parent_state = json.loads(self.get_state(self.parent.location)) assert parent_state['attempts'] == 32 assert parent_state['otherstuff'] == 'alsorobots' child_state = json.loads(self.get_state(self.child.location)) assert child_state['attempts'] == 10 assert child_state['whatever'] == 'things' unrelated_state = json.loads(self.get_state(self.unrelated.location)) assert unrelated_state['attempts'] == 12 assert unrelated_state['brains'] == 'zombie' reset_student_attempts(self.course_key, self.user, self.parent.location, requesting_user=self.user) parent_state = json.loads(self.get_state(self.parent.location)) assert json.loads(self.get_state( self.parent.location))['attempts'] == 0 assert parent_state['otherstuff'] == 'alsorobots' child_state = json.loads(self.get_state(self.child.location)) assert child_state['attempts'] == 0 assert child_state['whatever'] == 'things' unrelated_state = json.loads(self.get_state(self.unrelated.location)) assert unrelated_state['attempts'] == 12 assert unrelated_state['brains'] == 'zombie'
def test_delete_submission_scores_attempts_children(self): parent_state = json.loads(self.get_state(self.parent.location)) self.assertEqual(parent_state['attempts'], 32) self.assertEqual(parent_state['otherstuff'], 'alsorobots') child_state = json.loads(self.get_state(self.child.location)) self.assertEqual(child_state['attempts'], 10) self.assertEqual(child_state['whatever'], 'things') unrelated_state = json.loads(self.get_state(self.unrelated.location)) self.assertEqual(unrelated_state['attempts'], 12) self.assertEqual(unrelated_state['brains'], 'zombie') reset_student_attempts( self.course_key, self.user, self.parent.location, requesting_user=self.user, delete_module=True, ) self.assertRaises(StudentModule.DoesNotExist, self.get_state, self.parent.location) self.assertRaises(StudentModule.DoesNotExist, self.get_state, self.child.location) unrelated_state = json.loads(self.get_state(self.unrelated.location)) self.assertEqual(unrelated_state['attempts'], 12) self.assertEqual(unrelated_state['brains'], 'zombie')
def test_delete_submission_scores(self, _mock_signal): user = UserFactory() problem_location = self.course_key.make_usage_key('dummy', 'module') # Create a student module for the user StudentModule.objects.create( student=user, course_id=self.course_key, module_state_key=problem_location, state=json.dumps({}) ) # Create a submission and score for the student using the submissions API student_item = { 'student_id': anonymous_id_for_user(user, self.course_key), 'course_id': self.course_key.to_deprecated_string(), 'item_id': problem_location.to_deprecated_string(), 'item_type': 'openassessment' } submission = sub_api.create_submission(student_item, 'test answer') sub_api.set_score(submission['uuid'], 1, 2) # Delete student state using the instructor dash reset_student_attempts( self.course_key, user, problem_location, requesting_user=user, delete_module=True, ) # Verify that the student's scores have been reset in the submissions API score = sub_api.get_score(student_item) self.assertIs(score, None)
def test_reset_student_attempts_children(self): parent_state = json.loads(self.get_state(self.parent.location)) self.assertEqual(parent_state['attempts'], 32) self.assertEqual(parent_state['otherstuff'], 'alsorobots') child_state = json.loads(self.get_state(self.child.location)) self.assertEqual(child_state['attempts'], 10) self.assertEqual(child_state['whatever'], 'things') unrelated_state = json.loads(self.get_state(self.unrelated.location)) self.assertEqual(unrelated_state['attempts'], 12) self.assertEqual(unrelated_state['brains'], 'zombie') reset_student_attempts(self.course_key, self.user, self.parent.location, requesting_user=self.user) parent_state = json.loads(self.get_state(self.parent.location)) self.assertEqual( json.loads(self.get_state(self.parent.location))['attempts'], 0) self.assertEqual(parent_state['otherstuff'], 'alsorobots') child_state = json.loads(self.get_state(self.child.location)) self.assertEqual(child_state['attempts'], 0) self.assertEqual(child_state['whatever'], 'things') unrelated_state = json.loads(self.get_state(self.unrelated.location)) self.assertEqual(unrelated_state['attempts'], 12) self.assertEqual(unrelated_state['brains'], 'zombie')
def test_delete_team_attempts_no_team_fallthrough(self, _mock_signal): self.setup_team() team_ora_location = self.team_enabled_ora.location # Remove self.user from the team CourseTeamMembership.objects.get(user=self.user, team=self.team).delete() # All teammates should have a student module (except lazy_teammate) self.assertIsNotNone( self.get_student_module(self.user, team_ora_location)) self.assertIsNotNone( self.get_student_module(self.teammate_a, team_ora_location)) self.assertIsNotNone( self.get_student_module(self.teammate_b, team_ora_location)) self.assert_no_student_module(self.lazy_teammate, team_ora_location) reset_student_attempts(self.course_key, self.user, team_ora_location, requesting_user=self.user, delete_module=True) # self.user should be deleted, but no other teammates should be affected. self.assert_no_student_module(self.user, team_ora_location) self.assertIsNotNone( self.get_student_module(self.teammate_a, team_ora_location)) self.assertIsNotNone( self.get_student_module(self.teammate_b, team_ora_location)) self.assert_no_student_module(self.lazy_teammate, team_ora_location)
def test_delete_submission_scores_attempts_children(self): parent_state = json.loads(self.get_state(self.parent.location)) self.assertEqual(parent_state['attempts'], 32) self.assertEqual(parent_state['otherstuff'], 'alsorobots') child_state = json.loads(self.get_state(self.child.location)) self.assertEqual(child_state['attempts'], 10) self.assertEqual(child_state['whatever'], 'things') unrelated_state = json.loads(self.get_state(self.unrelated.location)) self.assertEqual(unrelated_state['attempts'], 12) self.assertEqual(unrelated_state['brains'], 'zombie') reset_student_attempts( self.course_key, self.user, self.parent.location, requesting_user=self.user, delete_module=True, ) self.assertRaises(StudentModule.DoesNotExist, self.get_state, self.parent.location) self.assertRaises(StudentModule.DoesNotExist, self.get_state, self.child.location) unrelated_state = json.loads(self.get_state(self.unrelated.location)) self.assertEqual(unrelated_state['attempts'], 12) self.assertEqual(unrelated_state['brains'], 'zombie')
def delete_student_attempt(self, student_identifier, course_id, content_id, requesting_user): """ Deletes student state for a problem. requesting_user may be kept as an audit trail. Takes some of the following query parameters - student_identifier is an email or username - content_id is a url-name of a problem - course_id is the id for the course """ course_id = CourseKey.from_string(course_id) try: student = get_student_from_identifier(student_identifier) except ObjectDoesNotExist: err_msg = ( 'Error occurred while attempting to reset student attempts for user ' '{student_identifier} for content_id {content_id}. ' 'User does not exist!'.format( student_identifier=student_identifier, content_id=content_id ) ) log.error(err_msg) return try: module_state_key = UsageKey.from_string(content_id) except InvalidKeyError: err_msg = ( f'Invalid content_id {content_id}!' ) log.error(err_msg) return if student: try: enrollment.reset_student_attempts( course_id, student, module_state_key, requesting_user=requesting_user, delete_module=True, ) except (StudentModule.DoesNotExist, enrollment.sub_api.SubmissionError): err_msg = ( 'Error occurred while attempting to reset student attempts for user ' '{student_identifier} for content_id {content_id}.'.format( student_identifier=student_identifier, content_id=content_id ) ) log.error(err_msg)
def delete_student_attempt(self, student_identifier, course_id, content_id, requesting_user): """ Deletes student state for a problem. requesting_user may be kept as an audit trail. Takes some of the following query parameters - student_identifier is an email or username - content_id is a url-name of a problem - course_id is the id for the course """ course_id = CourseKey.from_string(course_id) try: student = get_student_from_identifier(student_identifier) except ObjectDoesNotExist: err_msg = ( 'Error occurred while attempting to reset student attempts for user ' '{student_identifier} for content_id {content_id}. ' 'User does not exist!'.format( student_identifier=student_identifier, content_id=content_id ) ) log.error(err_msg) return try: module_state_key = UsageKey.from_string(content_id) except InvalidKeyError: err_msg = ( 'Invalid content_id {content_id}!'.format(content_id=content_id) ) log.error(err_msg) return if student: try: enrollment.reset_student_attempts( course_id, student, module_state_key, requesting_user=requesting_user, delete_module=True, ) except (StudentModule.DoesNotExist, enrollment.sub_api.SubmissionError): err_msg = ( 'Error occurred while attempting to reset student attempts for user ' '{student_identifier} for content_id {content_id}.'.format( student_identifier=student_identifier, content_id=content_id ) ) log.error(err_msg)
def delete_student_attempt(self, student_identifier, course_id, content_id, requesting_user): """ Deletes student state for a problem. requesting_user may be kept as an audit trail. Takes some of the following query parameters - student_identifier is an email or username - content_id is a url-name of a problem - course_id is the id for the course """ course_id = CourseKey.from_string(course_id) try: student = get_user_by_username_or_email(student_identifier) except ObjectDoesNotExist: err_msg = ( 'Error occurred while attempting to reset student attempts for user ' f'{student_identifier} for content_id {content_id}. ' 'User does not exist!') log.error(err_msg) return try: module_state_key = UsageKey.from_string(content_id) except InvalidKeyError: err_msg = (f'Invalid content_id {content_id}!') log.error(err_msg) return if student: try: enrollment.reset_student_attempts( course_id, student, module_state_key, requesting_user=requesting_user, delete_module=True, ) except (StudentModule.DoesNotExist, enrollment.sub_api.SubmissionError): err_msg = ( 'Error occurred while attempting to reset student attempts for user ' f'{student_identifier} for content_id {content_id}.') log.error(err_msg) # In some cases, reset_student_attempts does not clear the entire exam's completion state. # One example of this is an exam with multiple units (verticals) within it and the learner # never viewing one of the units. All of the content in that unit will still be marked complete, # but the reset code is unable to handle clearing the completion in that scenario. update_exam_completion_task.apply_async( (student_identifier, content_id, 0.0))
def test_reset_student_attempts(self): msk = self.course_key.make_usage_key('dummy', 'module') original_state = json.dumps({'attempts': 32, 'otherstuff': 'alsorobots'}) StudentModule.objects.create( student=self.user, course_id=self.course_key, module_state_key=msk, state=original_state ) # lambda to reload the module state from the database module = lambda: StudentModule.objects.get(student=self.user, course_id=self.course_key, module_state_key=msk) self.assertEqual(json.loads(module().state)['attempts'], 32) reset_student_attempts(self.course_key, self.user, msk, requesting_user=self.user) self.assertEqual(json.loads(module().state)['attempts'], 0)
def test_reset_student_attempts(self): msk = self.course_key.make_usage_key('dummy', 'module') original_state = json.dumps({'attempts': 32, 'otherstuff': 'alsorobots'}) StudentModule.objects.create( student=self.user, course_id=self.course_key, module_state_key=msk, state=original_state ) # lambda to reload the module state from the database module = lambda: StudentModule.objects.get(student=self.user, course_id=self.course_key, module_state_key=msk) self.assertEqual(json.loads(module().state)['attempts'], 32) reset_student_attempts(self.course_key, self.user, msk, requesting_user=self.user) self.assertEqual(json.loads(module().state)['attempts'], 0)
def test_delete_student_state(self): self.submit_question_answer('p1', {'2_1': 'choice_choice_2'}) with patch('lms.djangoapps.instructor.enrollment.tracker') as enrollment_tracker: with patch('lms.djangoapps.grades.events.tracker') as events_tracker: reset_student_attempts( self.course.id, self.student, self.problem.location, self.instructor, delete_module=True, ) course = self.store.get_course(self.course.id, depth=0) event_transaction_id = enrollment_tracker.method_calls[0][1][1]['event_transaction_id'] enrollment_tracker.emit.assert_called_with( events.STATE_DELETED_EVENT_TYPE, { 'user_id': str(self.student.id), 'course_id': str(self.course.id), 'problem_id': str(self.problem.location), 'instructor_id': str(self.instructor.id), 'event_transaction_id': event_transaction_id, 'event_transaction_type': events.STATE_DELETED_EVENT_TYPE, } ) events_tracker.emit.assert_has_calls( [ mock_call( events.COURSE_GRADE_CALCULATED, { 'percent_grade': 0.0, 'grading_policy_hash': 'ChVp0lHGQGCevD0t4njna/C44zQ=', 'user_id': str(self.student.id), 'letter_grade': '', 'event_transaction_id': event_transaction_id, 'event_transaction_type': events.STATE_DELETED_EVENT_TYPE, 'course_id': str(self.course.id), 'course_edited_timestamp': str(course.subtree_edited_on), 'course_version': str(course.course_version), } ), mock_call( events.COURSE_GRADE_NOW_FAILED_EVENT_TYPE, { 'user_id': str(self.student.id), 'event_transaction_id': event_transaction_id, 'event_transaction_type': events.STATE_DELETED_EVENT_TYPE, 'course_id': str(self.course.id), } ), ], any_order=True, )
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_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_delete_student_attempts(self, _mock_signal): msk = self.course_key.make_usage_key('dummy', 'module') original_state = json.dumps({'attempts': 32, 'otherstuff': 'alsorobots'}) StudentModule.objects.create( student=self.user, course_id=self.course_key, module_state_key=msk, state=original_state ) assert StudentModule.objects.filter( student=self.user, course_id=self.course_key, module_state_key=msk).count() == 1 reset_student_attempts(self.course_key, self.user, msk, requesting_user=self.user, delete_module=True) assert StudentModule.objects.filter( student=self.user, course_id=self.course_key, module_state_key=msk).count() == 0
def test_delete_team_attempts(self, _mock_signal): self.setup_team() team_ora_location = self.team_enabled_ora.location # All teammates should have a student module (except lazy_teammate) self.assertIsNotNone(self.get_student_module(self.user, team_ora_location)) self.assertIsNotNone(self.get_student_module(self.teammate_a, team_ora_location)) self.assertIsNotNone(self.get_student_module(self.teammate_b, team_ora_location)) self.assert_no_student_module(self.lazy_teammate, team_ora_location) reset_student_attempts( self.course_key, self.user, team_ora_location, requesting_user=self.user, delete_module=True ) # No one should have a state now self.assert_no_student_module(self.user, team_ora_location) self.assert_no_student_module(self.teammate_a, team_ora_location) self.assert_no_student_module(self.teammate_b, team_ora_location) self.assert_no_student_module(self.lazy_teammate, team_ora_location)
def test_delete_student_attempts(self, _mock_signal): msk = self.course_key.make_usage_key('dummy', 'module') original_state = json.dumps({'attempts': 32, 'otherstuff': 'alsorobots'}) StudentModule.objects.create( student=self.user, course_id=self.course_key, module_state_key=msk, state=original_state ) self.assertEqual( StudentModule.objects.filter( student=self.user, course_id=self.course_key, module_state_key=msk ).count(), 1) reset_student_attempts(self.course_key, self.user, msk, requesting_user=self.user, delete_module=True) self.assertEqual( StudentModule.objects.filter( student=self.user, course_id=self.course_key, module_state_key=msk ).count(), 0)
def test_delete_student_state(self): self.submit_question_answer('p1', {'2_1': 'choice_choice_2'}) with patch('lms.djangoapps.instructor.enrollment.tracker') as enrollment_tracker: with patch('lms.djangoapps.grades.events.tracker') as events_tracker: reset_student_attempts( self.course.id, self.student, self.problem.location, self.instructor, delete_module=True, ) course = self.store.get_course(self.course.id, depth=0) event_transaction_id = enrollment_tracker.method_calls[0][1][1]['event_transaction_id'] enrollment_tracker.emit.assert_called_with( events.STATE_DELETED_EVENT_TYPE, { 'user_id': unicode(self.student.id), 'course_id': unicode(self.course.id), 'problem_id': unicode(self.problem.location), 'instructor_id': unicode(self.instructor.id), 'event_transaction_id': event_transaction_id, 'event_transaction_type': events.STATE_DELETED_EVENT_TYPE, } ) events_tracker.emit.assert_called_with( events.COURSE_GRADE_CALCULATED, { 'percent_grade': 0.0, 'grading_policy_hash': u'ChVp0lHGQGCevD0t4njna/C44zQ=', 'user_id': unicode(self.student.id), 'letter_grade': u'', 'event_transaction_id': event_transaction_id, 'event_transaction_type': events.STATE_DELETED_EVENT_TYPE, 'course_id': unicode(self.course.id), 'course_edited_timestamp': unicode(course.subtree_edited_on), 'course_version': unicode(course.course_version), } )
def test_delete_submission_scores(self, mock_send_signal): user = UserFactory() problem_location = self.course_key.make_usage_key('dummy', 'module') # Create a student module for the user StudentModule.objects.create(student=user, course_id=self.course_key, module_state_key=problem_location, state=json.dumps({})) # Create a submission and score for the student using the submissions API student_item = { 'student_id': anonymous_id_for_user(user, self.course_key), 'course_id': text_type(self.course_key), 'item_id': text_type(problem_location), 'item_type': 'openassessment' } submission = sub_api.create_submission(student_item, 'test answer') sub_api.set_score(submission['uuid'], 1, 2) # Delete student state using the instructor dash mock_send_signal.reset_mock() reset_student_attempts( self.course_key, user, problem_location, requesting_user=user, delete_module=True, ) # Make sure our grades signal receivers handled the reset properly mock_send_signal.assert_called_once() assert mock_send_signal.call_args[1]['weighted_earned'] == 0 # Verify that the student's scores have been reset in the submissions API score = sub_api.get_score(student_item) self.assertIs(score, None)
def post(self, request): serializer = BulkResetStudentAttemptsSerializer(data=request.data) results = {"course": "", "errors": {}, "problems": {}} if serializer.is_valid(): course_id = serializer.data.get('course_id') results["course"] = course_id try: course_key = CourseKey.from_string(course_id) email_extension = serializer.data.get('email_extension') identifiers = serializer.data.get('identifiers') problems = serializer.data.get('problems') reset_state = serializer.data.get('reset_state') all_students, list_of_identifiers =\ get_identifiers(identifiers, email_extension) list_of_students = [] if not all_students: list_of_students, has_errors, get_students_errors =\ get_students(list_of_identifiers) if has_errors: results["errors"].update(get_students_errors) for problem in problems: problem = problem.replace(" ", "") try: module_state_key =\ UsageKey.from_string( problem ).map_into_course( course_key ) results["problems"].update({problem: {}}) if all_students: message = "Reset attempts for 'all' students is " "in development. Nothing has been resetted." results['problems'].update( {problem: { "message": message }}) # submit_reset_problem_attempts_for_all_students( # request, module_state_key # ) # results['problems'].update( # { # problem: { # "students": "all", # "task": "created" # } # } # ) else: for student in list_of_students: try: enrollment.reset_student_attempts( course_key, student, module_state_key, requesting_user=request.user, delete_module=reset_state) message = "Problem resetted" results["problems"][problem].update( {student.username: message}) except StudentModule.DoesNotExist: error_message =\ _("Module does not exist.") results["problems"][problem].update( {student.username: error_message}) except sub_api.SubmissionError: error_message =\ _("An error occurred while " "deleting the score.") results["problems"][problem].update( {student.username: error_message}) except InvalidKeyError: error_message = _("The problem does not exist") results["errors"].update({problem: error_message}) return Response(results, status=status.HTTP_200_OK) except InvalidKeyError: error_message = _("Invalid course id: '{}'".format(course_id)) return\ Response( {"error": error_message}, status=status.HTTP_400_BAD_REQUEST ) else: return\ Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def test_delete_student_state_events(self, models_tracker, handlers_tracker, enrollment_tracker): # submit answer self.submit_question_answer('p1', {'2_1': 'choice_choice_2'}) # check logging to make sure id's are tracked correctly across events event_transaction_id = handlers_tracker.emit.mock_calls[0][1][1]['event_transaction_id'] for call in models_tracker.emit.mock_calls: self.assertEqual(event_transaction_id, call[1][1]['event_transaction_id']) self.assertEqual(unicode(SUBMITTED_TYPE), call[1][1]['event_transaction_type']) handlers_tracker.emit.assert_called_with( unicode(SUBMITTED_TYPE), { 'user_id': unicode(self.student.id), 'event_transaction_id': event_transaction_id, 'event_transaction_type': unicode(SUBMITTED_TYPE), 'course_id': unicode(self.course.id), 'problem_id': unicode(self.problem.location), 'weighted_earned': 2.0, 'weighted_possible': 2.0, } ) course = self.store.get_course(self.course.id, depth=0) models_tracker.emit.assert_called_with( u'edx.grades.course.grade_calculated', { 'course_version': unicode(course.course_version), 'percent_grade': 0.02, 'grading_policy_hash': u'ChVp0lHGQGCevD0t4njna/C44zQ=', 'user_id': unicode(self.student.id), 'letter_grade': u'', 'event_transaction_id': event_transaction_id, 'event_transaction_type': unicode(SUBMITTED_TYPE), 'course_id': unicode(self.course.id), 'course_edited_timestamp': unicode(course.subtree_edited_on), } ) models_tracker.reset_mock() handlers_tracker.reset_mock() # delete state reset_student_attempts(self.course.id, self.student, self.problem.location, self.instructor, delete_module=True) # check logging to make sure id's are tracked correctly across events event_transaction_id = enrollment_tracker.method_calls[0][1][1]['event_transaction_id'] # make sure the id is propagated throughout the event flow for call in models_tracker.emit.mock_calls: self.assertEqual(event_transaction_id, call[1][1]['event_transaction_id']) self.assertEqual(unicode(STATE_DELETED_TYPE), call[1][1]['event_transaction_type']) # ensure we do not log a problem submitted event when state is deleted handlers_tracker.assert_not_called() enrollment_tracker.emit.assert_called_with( unicode(STATE_DELETED_TYPE), { 'user_id': unicode(self.student.id), 'course_id': unicode(self.course.id), 'problem_id': unicode(self.problem.location), 'instructor_id': unicode(self.instructor.id), 'event_transaction_id': event_transaction_id, 'event_transaction_type': unicode(STATE_DELETED_TYPE), } ) course = modulestore().get_course(self.course.id, depth=0) models_tracker.emit.assert_called_with( u'edx.grades.course.grade_calculated', { 'percent_grade': 0.0, 'grading_policy_hash': u'ChVp0lHGQGCevD0t4njna/C44zQ=', 'user_id': unicode(self.student.id), 'letter_grade': u'', 'event_transaction_id': event_transaction_id, 'event_transaction_type': unicode(STATE_DELETED_TYPE), 'course_id': unicode(self.course.id), 'course_edited_timestamp': unicode(course.subtree_edited_on), 'course_version': unicode(course.course_version), } ) enrollment_tracker.reset_mock() models_tracker.reset_mock() handlers_tracker.reset_mock()
def test_delete_student_state_events(self, models_tracker, handlers_tracker, enrollment_tracker): # submit answer self.submit_question_answer('p1', {'2_1': 'choice_choice_2'}) # check logging to make sure id's are tracked correctly across events event_transaction_id = handlers_tracker.emit.mock_calls[0][1][1][ 'event_transaction_id'] for call in models_tracker.emit.mock_calls: self.assertEqual(event_transaction_id, call[1][1]['event_transaction_id']) self.assertEqual(unicode(SUBMITTED_TYPE), call[1][1]['event_transaction_type']) handlers_tracker.emit.assert_called_with( unicode(SUBMITTED_TYPE), { 'user_id': unicode(self.student.id), 'event_transaction_id': event_transaction_id, 'event_transaction_type': unicode(SUBMITTED_TYPE), 'course_id': unicode(self.course.id), 'problem_id': unicode(self.problem.location), 'weighted_earned': 2.0, 'weighted_possible': 2.0, }) course = self.store.get_course(self.course.id, depth=0) models_tracker.emit.assert_called_with( u'edx.grades.course.grade_calculated', { 'course_version': unicode(course.course_version), 'percent_grade': 0.02, 'grading_policy_hash': u'ChVp0lHGQGCevD0t4njna/C44zQ=', 'user_id': unicode(self.student.id), 'letter_grade': u'', 'event_transaction_id': event_transaction_id, 'event_transaction_type': unicode(SUBMITTED_TYPE), 'course_id': unicode(self.course.id), 'course_edited_timestamp': unicode(course.subtree_edited_on), }) models_tracker.reset_mock() handlers_tracker.reset_mock() # delete state reset_student_attempts(self.course.id, self.student, self.problem.location, self.instructor, delete_module=True) # check logging to make sure id's are tracked correctly across events event_transaction_id = enrollment_tracker.method_calls[0][1][1][ 'event_transaction_id'] # make sure the id is propagated throughout the event flow for call in models_tracker.emit.mock_calls: self.assertEqual(event_transaction_id, call[1][1]['event_transaction_id']) self.assertEqual(unicode(STATE_DELETED_TYPE), call[1][1]['event_transaction_type']) # ensure we do not log a problem submitted event when state is deleted handlers_tracker.assert_not_called() enrollment_tracker.emit.assert_called_with( unicode(STATE_DELETED_TYPE), { 'user_id': unicode(self.student.id), 'course_id': unicode(self.course.id), 'problem_id': unicode(self.problem.location), 'instructor_id': unicode(self.instructor.id), 'event_transaction_id': event_transaction_id, 'event_transaction_type': unicode(STATE_DELETED_TYPE), }) course = modulestore().get_course(self.course.id, depth=0) models_tracker.emit.assert_called_with( u'edx.grades.course.grade_calculated', { 'percent_grade': 0.0, 'grading_policy_hash': u'ChVp0lHGQGCevD0t4njna/C44zQ=', 'user_id': unicode(self.student.id), 'letter_grade': u'', 'event_transaction_id': event_transaction_id, 'event_transaction_type': unicode(STATE_DELETED_TYPE), 'course_id': unicode(self.course.id), 'course_edited_timestamp': unicode(course.subtree_edited_on), 'course_version': unicode(course.course_version), }) enrollment_tracker.reset_mock() models_tracker.reset_mock() handlers_tracker.reset_mock()