def test_reset_different_student_item(self, changed): # Create a submissions for two students submission = sub_api.create_submission(self.STUDENT_ITEM, "test answer") sub_api.set_score(submission["uuid"], 1, 2) other_student = copy.copy(self.STUDENT_ITEM) other_student.update(changed) submission = sub_api.create_submission(other_student, "other test answer") sub_api.set_score(submission["uuid"], 3, 4) # Reset the score for the first student sub_api.reset_score( self.STUDENT_ITEM["student_id"], self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["item_id"] ) # The first student's scores should be reset self.assertIs(sub_api.get_score(self.STUDENT_ITEM), None) scores = sub_api.get_scores(self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["student_id"]) self.assertNotIn(self.STUDENT_ITEM["item_id"], scores) # But the second student should still have a score score = sub_api.get_score(other_student) self.assertEqual(score["points_earned"], 3) self.assertEqual(score["points_possible"], 4) scores = sub_api.get_scores(other_student["course_id"], other_student["student_id"]) self.assertIn(other_student["item_id"], scores)
def test_reset_different_student_item(self, changed): # Create a submissions for two students submission = sub_api.create_submission(self.STUDENT_ITEM, 'test answer') sub_api.set_score(submission['uuid'], 1, 2) other_student = copy.copy(self.STUDENT_ITEM) other_student.update(changed) submission = sub_api.create_submission(other_student, 'other test answer') sub_api.set_score(submission['uuid'], 3, 4) # Reset the score for the first student sub_api.reset_score( self.STUDENT_ITEM['student_id'], self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['item_id'], ) # The first student's scores should be reset self.assertIs(sub_api.get_score(self.STUDENT_ITEM), None) scores = sub_api.get_scores(self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['student_id']) self.assertNotIn(self.STUDENT_ITEM['item_id'], scores) # But the second student should still have a score score = sub_api.get_score(other_student) self.assertEqual(score['points_earned'], 3) self.assertEqual(score['points_possible'], 4) scores = sub_api.get_scores(other_student['course_id'], other_student['student_id']) self.assertIn(other_student['item_id'], scores)
def test_override_with_no_score(self): sub_api.score_override( self.STUDENT_ITEM, 8, 10, ) self.assertEqual(sub_api.get_score(self.STUDENT_ITEM)['points_earned'], 8) self.assertEqual(sub_api.get_score(self.STUDENT_ITEM)['points_possible'], 10)
def test_peer_evaluation_workflow(self): tim = self._create_student_and_submission("Tim", "Tim's answer") bob = self._create_student_and_submission("Bob", "Bob's answer") sally = self._create_student_and_submission("Sally", "Sally's answer") jim = self._create_student_and_submission("Jim", "Jim's answer") buffy = self._create_student_and_submission("Buffy", "Buffy's answer") xander = self._create_student_and_submission("Xander", "Xander's answer") # Tim should not have a score, because he has not evaluated enough # peer submissions. scores = sub_api.get_score(STUDENT_ITEM) self.assertFalse(scores) self.assertFalse(api.has_finished_required_evaluating("Tim", REQUIRED_GRADED)) api.create_evaluation( bob["uuid"], "Tim", REQUIRED_GRADED, REQUIRED_GRADED_BY, ASSESSMENT_DICT ) api.create_evaluation( sally["uuid"], "Tim", REQUIRED_GRADED, REQUIRED_GRADED_BY, ASSESSMENT_DICT ) self.assertFalse(api.has_finished_required_evaluating("Tim", REQUIRED_GRADED)) api.create_evaluation( jim["uuid"], "Tim", REQUIRED_GRADED, REQUIRED_GRADED_BY, ASSESSMENT_DICT ) self.assertFalse(api.has_finished_required_evaluating("Tim", REQUIRED_GRADED)) api.create_evaluation( buffy["uuid"], "Tim", REQUIRED_GRADED, REQUIRED_GRADED_BY, ASSESSMENT_DICT ) self.assertFalse(api.has_finished_required_evaluating("Tim", REQUIRED_GRADED)) api.create_evaluation( xander["uuid"], "Tim", REQUIRED_GRADED, REQUIRED_GRADED_BY, ASSESSMENT_DICT ) self.assertTrue(api.has_finished_required_evaluating("Tim", REQUIRED_GRADED)) # Tim should not have a score, because his submission does not have # enough evaluations. scores = sub_api.get_score(STUDENT_ITEM) self.assertFalse(scores) api.create_evaluation( tim["uuid"], "Bob", REQUIRED_GRADED, REQUIRED_GRADED_BY, ASSESSMENT_DICT ) api.create_evaluation( tim["uuid"], "Sally", REQUIRED_GRADED, REQUIRED_GRADED_BY, ASSESSMENT_DICT ) api.create_evaluation( tim["uuid"], "Jim", REQUIRED_GRADED, REQUIRED_GRADED_BY, ASSESSMENT_DICT ) # Tim has met the critera, and should now have a score. scores = sub_api.get_score(STUDENT_ITEM) self.assertTrue(scores) self.assertEqual(6, scores[0]["points_earned"]) self.assertEqual(12, scores[0]["points_possible"])
def test_override_with_no_score(self): sub_api.score_override( self.STUDENT_ITEM, 8, 10, ) self.assertEqual( sub_api.get_score(self.STUDENT_ITEM)['points_earned'], 8) self.assertEqual( sub_api.get_score(self.STUDENT_ITEM)['points_possible'], 10)
def test_override_with_one_score(self): # Create a submission for the student and score it submission = sub_api.create_submission(self.STUDENT_ITEM, 'test answer') sub_api.set_score(submission['uuid'], 1, 10) sub_api.score_override( self.STUDENT_ITEM, 5, 10, ) self.assertEqual(sub_api.get_score(self.STUDENT_ITEM)['points_earned'], 5) self.assertEqual(sub_api.get_score(self.STUDENT_ITEM)['points_possible'], 10)
def test_delete_submission_scores(self): user = UserFactory() course_id = 'ora2/1/1' item_id = 'i4x://ora2/1/openassessment/b3dce2586c9c4876b73e7f390e42ef8f' # Create a student module for the user StudentModule.objects.create( student=user, course_id=course_id, module_state_key=item_id, state=json.dumps({}) ) # Create a submission and score for the student using the submissions API student_item = { 'student_id': anonymous_id_for_user(user, course_id), 'course_id': course_id, 'item_id': item_id, '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(course_id, user, item_id, 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 get_score(self, id=None): """ Get student's current score. """ score = submissions_api.get_score(self.student_submission_id(id)) if score: return score['points_earned']
def test_delete_student_state_resets_scores(self): item_id = 'i4x://MITx/999/openassessment/b3dce2586c9c4876b73e7f390e42ef8f' # Create a student module for the user StudentModule.objects.create( student=self.student, course_id=self.course.id, module_state_key=item_id, state=json.dumps({}) ) # Create a submission and score for the student using the submissions API student_item = { 'student_id': anonymous_id_for_user(self.student, self.course.id), 'course_id': self.course.id, 'item_id': item_id, '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 url = reverse('instructor_dashboard_legacy', kwargs={'course_id': self.course.id}) response = self.client.post(url, { 'action': 'Delete student state for module', 'unique_student_identifier': self.student.email, 'problem_for_student': 'openassessment/b3dce2586c9c4876b73e7f390e42ef8f', }) self.assertEqual(response.status_code, 200) # 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_delete_submission_scores(self, _lti_mock): 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 code_submit(self, data, suffix=''): """ AJAX handler for Submit button """ if (self.grader_id == ''): return { 'error': 'You can not submit the assignment as no grader_id is configured' } submission = sub_api.create_submission(self.student_item_key, data) submitted_code = data["submitted_code"] grading_result = java_code_grader.grade(self.grader_id, submitted_code) if grading_result.has_key("error"): sub_api.set_score(submission['uuid'], 0, self.max_points) else: sub_api.set_score(submission['uuid'], self.max_points, self.max_points) new_score = sub_api.get_score(self.student_item_key) submit_result = { 'points_earned': new_score['points_earned'], 'points_possible': new_score['points_possible'] } if grading_result.has_key("error"): submit_result["error"] = grading_result["error"] return submit_result
def _has_database_updated_with_new_score( user_id, scored_block_usage_key, expected_modified_time, score_deleted, ): """ Returns whether the database has been updated with the expected new score values for the given problem and user. """ score = get_score(user_id, scored_block_usage_key) if score is None: # score should be None only if it was deleted. # Otherwise, it hasn't yet been saved. return score_deleted elif score.module_type == 'openassessment': anon_id = anonymous_id_for_user(User.objects.get(id=user_id), scored_block_usage_key.course_key) course_id = unicode(scored_block_usage_key.course_key) item_id = unicode(scored_block_usage_key) api_score = sub_api.get_score({ "student_id": anon_id, "course_id": course_id, "item_id": item_id, "item_type": "openassessment" }) reported_modified_time = api_score.created_at else: reported_modified_time = score.modified return reported_modified_time >= expected_modified_time
def test_delete_student_state_resets_scores(self): problem_location = self.course.id.make_usage_key('dummy', 'module') # Create a student module for the user StudentModule.objects.create(student=self.student, course_id=self.course.id, 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(self.student, self.course.id), 'course_id': self.course.id.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 url = reverse( 'instructor_dashboard_legacy', kwargs={'course_id': self.course.id.to_deprecated_string()}) response = self.client.post( url, { 'action': 'Delete student state for module', 'unique_student_identifier': self.student.email, 'problem_for_student': problem_location.to_deprecated_string(), }) self.assertEqual(response.status_code, 200) # 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_then_add_score(self): # Create a submission for the student and score it submission = sub_api.create_submission(self.STUDENT_ITEM, 'test answer') sub_api.set_score(submission['uuid'], 1, 2) # Reset scores sub_api.reset_score( self.STUDENT_ITEM['student_id'], self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['item_id'], ) # Score the student again sub_api.set_score(submission['uuid'], 3, 4) # Expect that the new score is available score = sub_api.get_score(self.STUDENT_ITEM) self.assertEqual(score['points_earned'], 3) self.assertEqual(score['points_possible'], 4) scores = sub_api.get_scores(self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['student_id']) self.assertIn(self.STUDENT_ITEM['item_id'], scores) self.assertEqual(scores[self.STUDENT_ITEM['item_id']], (3, 4))
def _has_database_updated_with_new_score( user_id, scored_block_usage_key, expected_modified_time, score_deleted, ): """ Returns whether the database has been updated with the expected new score values for the given problem and user. """ score = get_score(user_id, scored_block_usage_key) if score is None: # score should be None only if it was deleted. # Otherwise, it hasn't yet been saved. return score_deleted elif score.module_type == 'openassessment': anon_id = anonymous_id_for_user(User.objects.get(id=user_id), scored_block_usage_key.course_key) course_id = unicode(scored_block_usage_key.course_key) item_id = unicode(scored_block_usage_key) api_score = sub_api.get_score( { "student_id": anon_id, "course_id": course_id, "item_id": item_id, "item_type": "openassessment" } ) reported_modified_time = api_score.created_at else: reported_modified_time = score.modified return reported_modified_time >= expected_modified_time
def test_override_with_one_score(self): # Create a submission for the student and score it submission = sub_api.create_submission(self.STUDENT_ITEM, 'test answer') sub_api.set_score(submission['uuid'], 1, 10) sub_api.score_override( self.STUDENT_ITEM, 5, 10, ) self.assertEqual( sub_api.get_score(self.STUDENT_ITEM)['points_earned'], 5) self.assertEqual( sub_api.get_score(self.STUDENT_ITEM)['points_possible'], 10)
def test_delete_submission_scores(self): 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, 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 get_score(self, user): """ Return student's current score. """ score = submissions_api.get_score(self.get_submission_id(user)) if score: return score['points_earned']
def test_delete_student_state_resets_scores(self): item_id = 'i4x://MITx/999/openassessment/b3dce2586c9c4876b73e7f390e42ef8f' # Create a student module for the user StudentModule.objects.create( student=self.student, course_id=self.course.id, module_state_key=item_id, state=json.dumps({}) ) # Create a submission and score for the student using the submissions API student_item = { 'student_id': anonymous_id_for_user(self.student, self.course.id), 'course_id': self.course.id, 'item_id': item_id, '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 url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id}) response = self.client.post(url, { 'action': 'Delete student state for module', 'unique_student_identifier': self.student.email, 'problem_for_student': 'openassessment/b3dce2586c9c4876b73e7f390e42ef8f', }) self.assertEqual(response.status_code, 200) # 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_delete_student_state_resets_scores(self): problem_location = self.course.id.make_usage_key('dummy', 'module') # Create a student module for the user StudentModule.objects.create( student=self.student, course_id=self.course.id, 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(self.student, self.course.id), 'course_id': self.course.id.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 url = reverse('instructor_dashboard_legacy', kwargs={'course_id': self.course.id.to_deprecated_string()}) response = self.client.post(url, { 'action': 'Delete student state for module', 'unique_student_identifier': self.student.email, 'problem_for_student': problem_location.to_deprecated_string(), }) self.assertEqual(response.status_code, 200) # 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_delete_submission_scores(self): 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, 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 get_score(self, student_id=None): """ Return student's current score. """ score = submissions_api.get_score( self.get_student_item_dict(student_id)) if score: return score['points_earned']
def point_string(self): if self.show_points_earned: score = sub_api.get_score(self.student_item_key) if score != None: return str(score['points_earned']) + ' / ' + str( score['points_possible']) + ' point(s)' return str(self.max_points) + ' point(s) possible'
def test_reset_with_no_scores(self): sub_api.reset_score( self.STUDENT_ITEM["student_id"], self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["item_id"] ) self.assertIs(sub_api.get_score(self.STUDENT_ITEM), None) scores = sub_api.get_scores(self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["student_id"]) self.assertEqual(len(scores), 0)
def get_score(self, student_id=None): """ Return student's current score. """ score = submissions_api.get_score( self.get_student_item_dict(student_id) ) if score: return score['points_earned']
def test_override_after_reset_score(self): # Create a submission for the student and score it submission = sub_api.create_submission(self.STUDENT_ITEM, 'test answer') sub_api.set_score(submission['uuid'], 1, 10) # Reset score sub_api.reset_score( self.STUDENT_ITEM['student_id'], self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['item_id'], ) sub_api.score_override( self.STUDENT_ITEM, 5, 10, ) self.assertEqual(sub_api.get_score(self.STUDENT_ITEM)['points_earned'], 5) self.assertEqual(sub_api.get_score(self.STUDENT_ITEM)['points_possible'], 10)
def test_clear_state(self): # Create a submission, give it a score, and verify that score exists submission = api.create_submission(STUDENT_ITEM, ANSWER_ONE) api.set_score(submission["uuid"], 11, 12) score = api.get_score(STUDENT_ITEM) self._assert_score(score, 11, 12) self.assertEqual(score['submission_uuid'], submission['uuid']) # Reset the score with clear_state=True # This should set the submission's score to None, and make it unavailable to get_submissions api.reset_score( STUDENT_ITEM["student_id"], STUDENT_ITEM["course_id"], STUDENT_ITEM["item_id"], clear_state=True, ) score = api.get_score(STUDENT_ITEM) self.assertIsNone(score) subs = api.get_submissions(STUDENT_ITEM) self.assertEqual(subs, [])
def test_reset_with_no_scores(self): sub_api.reset_score( self.STUDENT_ITEM['student_id'], self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['item_id'], ) self.assertIs(sub_api.get_score(self.STUDENT_ITEM), None) scores = sub_api.get_scores(self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['student_id']) self.assertEqual(len(scores), 0)
def setUp(self): super(TestOraAggregateDataIntegration, self).setUp() # Create submissions and assessments self.submission = self._create_submission(STUDENT_ITEM) self.scorer_submission = self._create_submission(SCORER_ITEM) self.earned_points = 1 self.possible_points = 2 peer_api.get_submission_to_assess(self.scorer_submission['uuid'], 1) self.assessment = self._create_assessment(self.scorer_submission['uuid']) sub_api.set_score(self.submission['uuid'], self.earned_points, self.possible_points) self.score = sub_api.get_score(STUDENT_ITEM) peer_api.get_score(self.submission['uuid'], {'must_be_graded_by': 1, 'must_grade': 0}) self._create_assessment_feedback(self.submission['uuid'])
def test_override_after_reset_score(self): # Create a submission for the student and score it submission = sub_api.create_submission(self.STUDENT_ITEM, 'test answer') sub_api.set_score(submission['uuid'], 1, 10) # Reset score sub_api.reset_score( self.STUDENT_ITEM['student_id'], self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['item_id'], ) sub_api.score_override( self.STUDENT_ITEM, 5, 10, ) self.assertEqual( sub_api.get_score(self.STUDENT_ITEM)['points_earned'], 5) self.assertEqual( sub_api.get_score(self.STUDENT_ITEM)['points_possible'], 10)
def test_reset_with_one_score(self): # Create a submission for the student and score it submission = sub_api.create_submission(self.STUDENT_ITEM, "test answer") sub_api.set_score(submission["uuid"], 1, 2) # Reset scores sub_api.reset_score( self.STUDENT_ITEM["student_id"], self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["item_id"] ) # Expect that no scores are available for the student self.assertIs(sub_api.get_score(self.STUDENT_ITEM), None) scores = sub_api.get_scores(self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["student_id"]) self.assertEqual(len(scores), 0)
def _has_db_updated_with_new_score(self, scored_block_usage_key, **kwargs): """ Returns whether the database has been updated with the expected new score values for the given problem and user. """ if kwargs[ 'score_db_table'] == ScoreDatabaseTableEnum.courseware_student_module: score = get_score(kwargs['user_id'], scored_block_usage_key) found_modified_time = score.modified if score is not None else None elif kwargs['score_db_table'] == ScoreDatabaseTableEnum.submissions: score = sub_api.get_score({ "student_id": kwargs['anonymous_user_id'], "course_id": six.text_type(scored_block_usage_key.course_key), "item_id": six.text_type(scored_block_usage_key), "item_type": scored_block_usage_key.block_type, }) found_modified_time = score['created_at'] if score is not None else None else: assert kwargs['score_db_table'] == ScoreDatabaseTableEnum.overrides from . import api score = api.get_subsection_grade_override( user_id=kwargs['user_id'], course_key_or_id=kwargs['course_id'], usage_key_or_id=kwargs['usage_id']) found_modified_time = score.modified if score is not None else None if score is None: # score should be None only if it was deleted. # Otherwise, it hasn't yet been saved. db_is_updated = kwargs['score_deleted'] else: db_is_updated = found_modified_time >= from_timestamp( kwargs['expected_modified_time']) if not db_is_updated: log.info( u"Grades: tasks._has_database_updated_with_new_score is False. Task ID: {}. Kwargs: {}. Found " u"modified time: {}".format( self.request.id, kwargs, found_modified_time, )) return db_is_updated
def test_asynch_generate_score(self, xblock, mock_is_admin): # Test that AI grading, which creates assessments asynchronously, # updates the workflow so students can receive a score. mock_is_admin.return_value = True # Train classifiers for the problem self.request(xblock, 'schedule_training', json.dumps({}), response_format='json') # Submit a response self.request(xblock, 'submit', self.SUBMISSION, response_format='json') # BEFORE viewing the grade page, check that we get a score score = sub_api.get_score(xblock.get_student_item_dict()) self.assertIsNot(score, None) self.assertEqual(score['submission_uuid'], xblock.submission_uuid)
def setUp(self): super(TestOraAggregateDataIntegration, self).setUp() self.maxDiff = None # pylint: disable=invalid-name # Create submissions and assessments self.submission = self._create_submission(STUDENT_ITEM) self.scorer_submission = self._create_submission(SCORER_ITEM) self.earned_points = 1 self.possible_points = 2 peer_api.get_submission_to_assess(self.scorer_submission['uuid'], 1) self.assessment = self._create_assessment(self.scorer_submission['uuid']) self.assertEqual(self.assessment['parts'][0]['criterion']['label'], "criterion_1") sub_api.set_score(self.submission['uuid'], self.earned_points, self.possible_points) self.score = sub_api.get_score(STUDENT_ITEM) peer_api.get_score(self.submission['uuid'], {'must_be_graded_by': 1, 'must_grade': 0}) self._create_assessment_feedback(self.submission['uuid'])
def test_reset_with_multiple_scores(self): # Create a submission for the student and score it submission = sub_api.create_submission(self.STUDENT_ITEM, 'test answer') sub_api.set_score(submission['uuid'], 1, 2) sub_api.set_score(submission['uuid'], 2, 2) # Reset scores sub_api.reset_score( self.STUDENT_ITEM['student_id'], self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['item_id'], ) # Expect that no scores are available for the student self.assertIs(sub_api.get_score(self.STUDENT_ITEM), None) scores = sub_api.get_scores(self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['student_id']) self.assertEqual(len(scores), 0)
def _has_db_updated_with_new_score(self, scored_block_usage_key, **kwargs): """ Returns whether the database has been updated with the expected new score values for the given problem and user. """ if kwargs['score_db_table'] == ScoreDatabaseTableEnum.courseware_student_module: score = get_score(kwargs['user_id'], scored_block_usage_key) found_modified_time = score.modified if score is not None else None elif kwargs['score_db_table'] == ScoreDatabaseTableEnum.submissions: score = sub_api.get_score( { "student_id": kwargs['anonymous_user_id'], "course_id": unicode(scored_block_usage_key.course_key), "item_id": unicode(scored_block_usage_key), "item_type": scored_block_usage_key.block_type, } ) found_modified_time = score['created_at'] if score is not None else None else: assert kwargs['score_db_table'] == ScoreDatabaseTableEnum.overrides score = GradesService().get_subsection_grade_override( user_id=kwargs['user_id'], course_key_or_id=kwargs['course_id'], usage_key_or_id=kwargs['usage_id'] ) found_modified_time = score.modified if score is not None else None if score is None: # score should be None only if it was deleted. # Otherwise, it hasn't yet been saved. db_is_updated = kwargs['score_deleted'] else: db_is_updated = found_modified_time >= from_timestamp(kwargs['expected_modified_time']) if not db_is_updated: log.info( u"Grades: tasks._has_database_updated_with_new_score is False. Task ID: {}. Kwargs: {}. Found " u"modified time: {}".format( self.request.id, kwargs, found_modified_time, ) ) return db_is_updated
def test_reset_with_one_score(self): # Create a submission for the student and score it submission = sub_api.create_submission(self.STUDENT_ITEM, 'test answer') sub_api.set_score(submission['uuid'], 1, 2) # Reset scores sub_api.reset_score( self.STUDENT_ITEM['student_id'], self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['item_id'], ) # Expect that no scores are available for the student self.assertIs(sub_api.get_score(self.STUDENT_ITEM), None) scores = sub_api.get_scores(self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['student_id']) self.assertEqual(len(scores), 0)
def test_reset_then_add_score(self): # Create a submission for the student and score it submission = sub_api.create_submission(self.STUDENT_ITEM, "test answer") sub_api.set_score(submission["uuid"], 1, 2) # Reset scores sub_api.reset_score( self.STUDENT_ITEM["student_id"], self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["item_id"] ) # Score the student again sub_api.set_score(submission["uuid"], 3, 4) # Expect that the new score is available score = sub_api.get_score(self.STUDENT_ITEM) self.assertEqual(score["points_earned"], 3) self.assertEqual(score["points_possible"], 4) scores = sub_api.get_scores(self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["student_id"]) self.assertIn(self.STUDENT_ITEM["item_id"], scores) self.assertEqual(scores[self.STUDENT_ITEM["item_id"]], (3, 4))
def test_delete_submission_scores(self, _mock_send_signal, mock_set_receiver, mock_reset_receiver): 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 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_set_receiver.assert_not_called() mock_reset_receiver.assert_called_once() # 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_delete_submission_scores(self, _mock_send_signal, mock_set_receiver, mock_reset_receiver): 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 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_set_receiver.assert_not_called() mock_reset_receiver.assert_called_once() # 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_then_add_score(self): # Create a submission for the student and score it submission = sub_api.create_submission(self.STUDENT_ITEM, 'test answer') sub_api.set_score(submission['uuid'], 1, 2) # Reset scores sub_api.reset_score( self.STUDENT_ITEM['student_id'], self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['item_id'], ) # Score the student again sub_api.set_score(submission['uuid'], 3, 4) # Expect that the new score is available score = sub_api.get_score(self.STUDENT_ITEM) self.assertEqual(score['points_earned'], 3) self.assertEqual(score['points_possible'], 4) scores = sub_api.get_scores(self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['student_id']) self.assertIn(self.STUDENT_ITEM['item_id'], scores) item_score = scores[self.STUDENT_ITEM['item_id']] self.assertEqual((item_score['points_earned'], item_score['points_possible']), (3, 4))
def student_view(self, *args, **kwargs): """ Отображение MultiEngineXBlock студенту (LMS). """ scenarios = self.load_scenarios context = { "display_name": self.display_name, "weight": self.weight, "question": self.question, "correct_answer": self.correct_answer, "answer": self.answer, "attempts": self.attempts, "student_state_json": self.student_state_json, "student_view_template": self.student_view_template, "scenario": self.scenario, "scenarios": scenarios, } # Rescore student score = submissions_api.get_score(self.get_student_item_dict()) # It's temporary! It's crutch, not magick. self.runtime.publish(self, 'grade', { 'value': self.points, 'max_value': self.weight, }) if self.max_attempts != 0: context["max_attempts"] = self.max_attempts if self.past_due(): context["past_due"] = True if self.answer != '{}': context["points"] = self.points if answer_opportunity(self): context["answer_opportunity"] = True if self.is_course_staff() is True or self.is_instructor() is True: context['is_course_staff'] = True fragment = Fragment() fragment.add_content( render_template( 'static/html/multiengine.html', context ) ) js_urls = ( 'static/js/multiengine.js', ) css_urls = ( 'static/css/multiengine.css', ) self.load_resources(js_urls, css_urls, fragment) fragment.initialize_js('MultiEngineXBlock') return fragment
def score(self): """Score: The student score.""" api_score = submissions_api.get_score(self.submission_id) if api_score: return Score(api_score['points_earned'], api_score['points_possible'])
def student_view(self, *args, **kwargs): """ Отображение MultiEngineXBlock студенту (LMS). """ scenarios = self.load_scenarios context = { "display_name": self.display_name, "weight": self.weight, "question": self.question, "correct_answer": self.correct_answer, #"answer": self.answer, "attempts": self.attempts, "student_state_json": self.student_state_json, "student_view_template": self.student_view_template, "scenario": self.scenario, "scenarios": scenarios, } # Rescore student score = submissions_api.get_score(self.get_student_item_dict()) # It's temporary! It's crutch, not magick. self.runtime.publish(self, 'grade', { 'value': self.points, 'max_value': self.weight, }) if self.max_attempts != 0: context["max_attempts"] = self.max_attempts if self.past_due(): context["past_due"] = True if self.answer != '{}': context["points"] = self.points if answer_opportunity(self): context["answer_opportunity"] = True if self.is_course_staff() is True or self.is_instructor() is True: context['is_course_staff'] = True fragment = Fragment() fragment.add_content( render_template( 'static/html/multiengine.html', context ) ) js_urls = ( 'static/js/multiengine.js', ) css_urls = ( 'static/css/multiengine.css', ) self.load_resources(js_urls, css_urls, fragment) fragment.initialize_js('MultiEngineXBlock') return fragment
def handle_grades_file(self, file): """Read the file and set score and finalize flag and fresh flag and grade date and comment. Note: We expect a csv file following this structure: +----------+------+----------+----------------------+--------------+-----------+----------------------+-------+-----------+----------+ | Username | Name | Filename | Uploaded at | Fresh answer | Finalized | Grade Date | Grade | Max grade | Comment | +----------+------+----------+----------------------+--------------+-----------+----------------------+-------+-----------+----------+ | student1 | S.St | 5.PNG | 10-03-2020 16:01 MSK | False | False | 10-03-2020 16:05 MSK | 23 | 100 | bad work | +----------+------+----------+----------------------+--------------+-----------+----------------------+-------+-----------+----------+ | student2 | Mary | 7.JPG | 15-03-2020 11:01 MSK | True | False | | | 100 | | +----------+------+----------+----------------------+--------------+-----------+----------------------+-------+-----------+----------+ Args: file: The grades csv file. """ grades_file = csv.DictReader(file, [ 'username', 'fullname', 'filename', 'timestamp', 'fresh', 'finalized', 'date_fin', 'score', 'max_score', 'comment' ], delimiter=',') for line, row in enumerate(grades_file): if line: user = get_user_by_username_or_email(row['username']) module = self.get_or_create_student_module(user) state = json.loads(module.state) student_id = anonymous_id_for_user( user, CourseKey.from_string(self.block_course_id)) score = submissions_api.get_score( self.get_student_item_dict(student_id)) new = False if score and score['points_earned'] == row['score']: pass elif score or row['score']: new = True submission = self.get_submission(student_id) if not submission: continue uuid = submission['uuid'] if new: submissions_api.set_score(uuid, row['score'], self.max_score()) submission_obj = Submission.objects.get(uuid=uuid) if submission_obj.answer['finalized'] != json.loads( row['finalized'].lower()): submission_obj.answer['finalized'] = json.loads( row['finalized'].lower()) submission_obj.save() new = True if row['comment']: try: if state['comment'].encode('utf-8') != row['comment']: new = True state['comment'] = row['comment'] except: state.update({'comment': row['comment']}) new = True if new: state['date_fin'] = force_text(django_now()) state['fresh'] = False module.state = json.dumps(state) module.save()
def test_get_score(self): self.test_create_score() scores = api.get_score(STUDENT_ITEM) self._assert_score(scores[0], 11, 12)
def test_get_score(self): submission = api.create_submission(STUDENT_ITEM, ANSWER_ONE) api.set_score(submission["uuid"], 11, 12) score = api.get_score(STUDENT_ITEM) self._assert_score(score, 11, 12) self.assertEqual(score['submission_uuid'], submission['uuid'])
def test_get_score_no_student_id(self): student_item = copy.deepcopy(STUDENT_ITEM) student_item['student_id'] = None self.assertIs(api.get_score(student_item), None)
def test_peer_assessment_workflow(self): tim_sub, tim = self._create_student_and_submission("Tim", "Tim's answer") bob_sub, bob = self._create_student_and_submission("Bob", "Bob's answer") sally_sub, sally = self._create_student_and_submission("Sally", "Sally's answer") jim_sub, jim = self._create_student_and_submission("Jim", "Jim's answer") self._create_student_and_submission("Buffy", "Buffy's answer") self._create_student_and_submission("Xander", "Xander's answer") # Tim should not have a score, because he has not evaluated enough # peer submissions. requirements = { "peer": { "must_grade": REQUIRED_GRADED, "must_be_graded_by": REQUIRED_GRADED_BY, } } score = workflow_api.get_workflow_for_submission( tim_sub["uuid"], requirements )["score"] self.assertIsNone(score) for i in range(5): self.assertEquals((False, i), peer_api.has_finished_required_evaluating(tim_sub['uuid'], REQUIRED_GRADED)) sub = peer_api.get_submission_to_assess(tim_sub['uuid'], REQUIRED_GRADED) peer_api.create_assessment( tim_sub["uuid"], tim["student_id"], ASSESSMENT_DICT['options_selected'], ASSESSMENT_DICT['criterion_feedback'], ASSESSMENT_DICT['overall_feedback'], RUBRIC_DICT, REQUIRED_GRADED_BY, ) self.assertEquals((True, 5), peer_api.has_finished_required_evaluating(tim_sub['uuid'], REQUIRED_GRADED)) # Tim should not have a score, because his submission does not have # enough assessments. self.assertIsNone(sub_api.get_score(STUDENT_ITEM)) sub = peer_api.get_submission_to_assess(bob_sub['uuid'], REQUIRED_GRADED) self.assertEqual(sub["uuid"], tim_sub["uuid"]) peer_api.create_assessment( bob_sub["uuid"], bob["student_id"], ASSESSMENT_DICT['options_selected'], ASSESSMENT_DICT['criterion_feedback'], ASSESSMENT_DICT['overall_feedback'], RUBRIC_DICT, REQUIRED_GRADED_BY, ) sub = peer_api.get_submission_to_assess(sally_sub['uuid'], REQUIRED_GRADED) self.assertEqual(sub["uuid"], tim_sub["uuid"]) peer_api.create_assessment( sally_sub["uuid"], sally["student_id"], ASSESSMENT_DICT_FAIL['options_selected'], ASSESSMENT_DICT_FAIL['criterion_feedback'], ASSESSMENT_DICT_FAIL['overall_feedback'], RUBRIC_DICT, REQUIRED_GRADED_BY, ) sub = peer_api.get_submission_to_assess(jim_sub['uuid'], REQUIRED_GRADED) self.assertEqual(sub["uuid"], tim_sub["uuid"]) peer_api.create_assessment( jim_sub["uuid"], jim["student_id"], ASSESSMENT_DICT_PASS['options_selected'], ASSESSMENT_DICT_PASS['criterion_feedback'], ASSESSMENT_DICT_PASS['overall_feedback'], RUBRIC_DICT, REQUIRED_GRADED_BY, ) # Tim has met the critera, and should now be complete. requirements = { 'must_grade': REQUIRED_GRADED, 'must_be_graded_by': REQUIRED_GRADED_BY } self.assertTrue(peer_api.is_complete(tim_sub["uuid"], requirements))