def enter_grade(self, request, suffix=''): """ """ require(self.is_course_staff()) user = get_user_by_username_or_email(request.params.get('user')) score = request.params.get('score') comment = request.params.get('comment') if not score: return Response( json_body={"error": "Enter a valid grade"}, status_code=400, ) try: score = int(score) except ValueError: return Response( json_body={"error": "Enter a valid grade"}, status_code=400, ) submission_id = self.get_submission_id(user) submission = submissions_api.create_submission(submission_id, {'comment': comment}) submissions_api.set_score(submission['uuid'], score, self.max_score()) self.get_or_create_student_module(user, score, comment) return Response(json_body={"success": "success"})
def set_staff_score(self, score, reason=None): """ Set a staff score for the workflow. Allows for staff scores to be set on a submission, with annotations to provide an audit trail if needed. This method can be used for both required staff grading, and staff overrides. Args: score (dict): A dict containing 'points_earned', 'points_possible', and 'staff_id'. is_override (bool): Optionally True if staff is overriding a previous score. reason (string): An optional parameter specifying the reason for the staff grade. A default value will be used in the event that this parameter is not provided. """ if reason is None: reason = "A staff member has defined the score for this submission" sub_dict = sub_api.get_submission_and_student(self.submission_uuid) sub_api.reset_score( sub_dict['student_item']['student_id'], self.course_id, self.item_id, emit_signal=False ) sub_api.set_score( self.submission_uuid, score["points_earned"], score["points_possible"], annotation_creator=score["staff_id"], annotation_type=self.STAFF_ANNOTATION_TYPE, annotation_reason=reason )
def test_get_scores(self): student_item = copy.deepcopy(STUDENT_ITEM) student_item["course_id"] = "get_scores_course" student_item["item_id"] = "i4x://a/b/c/s1" s1 = api.create_submission(student_item, "Hello World") student_item["item_id"] = "i4x://a/b/c/s2" s2 = api.create_submission(student_item, "Hello World") student_item["item_id"] = "i4x://a/b/c/s3" s3 = api.create_submission(student_item, "Hello World") api.set_score(s1['uuid'], 3, 5) api.set_score(s1['uuid'], 4, 5) api.set_score(s1['uuid'], 2, 5) # Should overwrite previous lines api.set_score(s2['uuid'], 0, 10) api.set_score(s3['uuid'], 4, 4) # Getting the scores for a user should never take more than one query with self.assertNumQueries(1): scores = api.get_scores( student_item["course_id"], student_item["student_id"] ) self.assertEqual( scores, { u"i4x://a/b/c/s1": (2, 5), u"i4x://a/b/c/s2": (0, 10), u"i4x://a/b/c/s3": (4, 4), } )
def test_get_scores(self): student_item = copy.deepcopy(STUDENT_ITEM) student_item["course_id"] = "get_scores_course" student_item["item_id"] = "i4x://a/b/c/s1" s1 = api.create_submission(student_item, "Hello World") student_item["item_id"] = "i4x://a/b/c/s2" s2 = api.create_submission(student_item, "Hello World") student_item["item_id"] = "i4x://a/b/c/s3" s3 = api.create_submission(student_item, "Hello World") api.set_score(s1['uuid'], 3, 5) api.set_score(s1['uuid'], 4, 5) api.set_score(s1['uuid'], 2, 5) # Should overwrite previous lines api.set_score(s2['uuid'], 0, 10) api.set_score(s3['uuid'], 4, 4) # Getting the scores for a user should never take more than one query with self.assertNumQueries(1): scores = api.get_scores(student_item["course_id"], student_item["student_id"]) self.assertEqual( scores, { u"i4x://a/b/c/s1": (2, 5), u"i4x://a/b/c/s2": (0, 10), u"i4x://a/b/c/s3": (4, 4), })
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 enter_grade(self, request, suffix=''): # pylint: disable=unused-argument """ Persist a score for a student given by staff. """ require(self.is_course_staff()) module = StudentModule.objects.get(pk=request.params['module_id']) state = json.loads(module.state) score = int(request.params['grade']) if self.is_instructor(): uuid = request.params['submission_id'] submissions_api.set_score(uuid, score, self.max_score()) else: state['staff_score'] = score state['comment'] = request.params.get('comment', '') module.state = json.dumps(state) module.save() log.info( "enter_grade for course:%s module:%s student:%s", module.course_id, module.module_state_key, module.student.username ) return Response(json_body=self.staff_grading_data())
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 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 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_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): 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 _check_if_finished_and_create_score(student_item, submission, required_evaluations_for_student, required_evaluations_for_submission): """Basic function for checking if a student is finished with peer workflow. Checks if the student is finished with the peer evaluation workflow. If the student already has a final grade calculated, there is no need to proceed. If they do not have a grade, the student has a final grade calculated. """ if Score.objects.filter(student_item=student_item): return finished_evaluating = has_finished_required_evaluating( student_item.student_id, required_evaluations_for_student ) evaluations = PeerEvaluation.objects.filter(submission=submission) submission_finished = evaluations.count() >= required_evaluations_for_submission scores = [] for evaluation in evaluations: scores.append(evaluation.points_earned) if finished_evaluating and submission_finished: submission_api.set_score( StudentItemSerializer(student_item).data, SubmissionSerializer(submission).data, _calculate_final_score(scores), evaluations[0].points_possible )
def make_student(self, block, name, **state): answer = {} for key in ("sha1", "mimetype", "filename"): if key in state: answer[key] = state.pop(key) score = state.pop("score", None) user = User(username=name) user.save() profile = UserProfile(user=user, name=name) profile.save() module = StudentModule( module_state_key=block.location, student=user, course_id=self.course_id, state=json.dumps(state) ) module.save() anonymous_id = anonymous_id_for_user(user, self.course_id) item = StudentItem(student_id=anonymous_id, course_id=self.course_id, item_id=block.block_id, item_type="sga") item.save() if answer: student_id = block.student_submission_id(anonymous_id) submission = submissions_api.create_submission(student_id, answer) if score is not None: submissions_api.set_score(submission["uuid"], score, block.max_score()) else: submission = None self.addCleanup(item.delete) self.addCleanup(profile.delete) self.addCleanup(module.delete) self.addCleanup(user.delete) return {"module": module, "item": item, "submission": submission}
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 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 setUp(self): """ Create a submission and score. """ super().setUp() self.submission = sub_api.create_submission(self.STUDENT_ITEM, "test answer") sub_api.set_score(self.submission['uuid'], self.SCORE["points_earned"], self.SCORE["points_possible"])
def set_score(self, score): """ Set a score for the workflow. Scores are persisted via the Submissions API, separate from the Workflow Data. Score is associated with the same submission_uuid as this workflow Args: score (dict): A dict containing 'points_earned' and 'points_possible'. """ sub_api.set_score(self.submission_uuid, score["points_earned"], score["points_possible"]) # This should be replaced by using the event tracking API, but # that's not quite ready yet. So we're making this temp hack. emit_event({ "context": { "course_id": self.course_id }, "event": { "submission_uuid": self.submission_uuid, "points_earned": score["points_earned"], "points_possible": score["points_possible"], }, "event_source": "server", "event_type": "openassessment.workflow.score", "time": datetime.utcnow(), })
def test_submissions_api_overrides_scores(self): """ Check that answering incorrectly is graded properly. """ self.basic_setup() self.submit_question_answer('p1', {'2_1': 'Correct'}) self.submit_question_answer('p2', {'2_1': 'Correct'}) self.submit_question_answer('p3', {'2_1': 'Incorrect'}) self.check_grade_percent(0.67) self.assertEqual(self.get_course_grade().letter_grade, 'B') # But now, set the score with the submissions API and watch # as it overrides the score read from StudentModule and our # student gets an A instead. self._stop_signal_patch() student_item = { 'student_id': anonymous_id_for_user(self.student_user, self.course.id), 'course_id': unicode(self.course.id), 'item_id': unicode(self.problem_location('p3')), 'item_type': 'problem' } submission = submissions_api.create_submission(student_item, 'any answer') submissions_api.set_score(submission['uuid'], 1, 1) self.check_grade_percent(1.0) self.assertEqual(self.get_course_grade().letter_grade, 'A')
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_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_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 set_score(self, score): """ Set a score for the workflow. Scores are persisted via the Submissions API, separate from the Workflow Data. Score is associated with the same submission_uuid as this workflow Args: score (dict): A dict containing 'points_earned' and 'points_possible'. """ sub_api.set_score( self.submission_uuid, score["points_earned"], score["points_possible"] ) # This should be replaced by using the event tracking API, but # that's not quite ready yet. So we're making this temp hack. emit_event({ "context": { "course_id": self.course_id }, "event": { "submission_uuid": self.submission_uuid, "points_earned": score["points_earned"], "points_possible": score["points_possible"], }, "event_source": "server", "event_type": "openassessment.workflow.score", "time": datetime.utcnow(), })
def score_update(self, submission_result): parent = super(ScilabXBlock, self) if hasattr(parent, 'score_update'): parent.score_update(submission_result) submission_uid, validation_key = submission_result.lms_key.split('+') # TODO: Validate submission # submission = submissions_api.get_submission(submission_uid) submissions_api.set_score(submission_uid, int(100 * submission_result.score), 100, annotation_reason=submission_result.msg, annotation_creator='xblock_scilab', annotation_type='check') self.points = submission_result.score self.runtime.publish( self, 'grade', { 'value': submission_result.score * self.max_score(), 'max_value': self.max_score() }) annotation = ifmo_submissions_api.get_annotation( self.student_submission_dict()) self.message = None try: message = (json.loads(annotation.get('reason'))['message']).strip() if message: self.message = u"<b>Результат последней проверки:</b> %s" % message except (ValueError, KeyError): pass
def _create_submissions_and_scores( self, xblock, submissions_and_scores, submission_key=None, points_possible=10 ): """ Create submissions and scores that should be displayed by the leaderboard. Args: xblock (OpenAssessmentBlock) submisions_and_scores (list): List of `(submission, score)` tuples, where `submission` is the essay text (string) and `score` is the integer number of points earned. Keyword Args: points_possible (int): The total number of points possible for this problem submission_key (string): The key to use in the submission dict. If None, use the submission value itself instead of embedding it in a dictionary. """ for num, (submission, points_earned) in enumerate(submissions_and_scores): # Assign a unique student ID # These aren't displayed by the leaderboard, so we can set them # to anything without affecting the test. student_item = xblock.get_student_item_dict() # adding rand number to the student_id to make it unique. student_item['student_id'] = u'student {num} {num2}'.format(num=num, num2=randint(2, 1000)) if submission_key is not None: answer = {submission_key: submission} else: answer = submission # Create a submission sub = sub_api.create_submission(student_item, answer) # Create a score for the submission sub_api.set_score(sub['uuid'], points_earned, points_possible)
def enter_grade(self, request, suffix=''): # pylint: disable=unused-argument """ Persist a score for a student given by staff. """ require(self.is_course_staff()) score = request.params.get('grade', None) module = self.get_student_module(request.params['module_id']) if not score: return Response(json_body=self.validate_score_message( module.course_id, module.student.username)) state = json.loads(module.state) try: score = int(score) except ValueError: return Response(json_body=self.validate_score_message( module.course_id, module.student.username)) if self.is_instructor(): uuid = request.params['submission_id'] submissions_api.set_score(uuid, score, self.max_score()) else: state['staff_score'] = score state['comment'] = request.params.get('comment', '') module.state = json.dumps(state) module.save() log.info("enter_grade for course:%s module:%s student:%s", module.course_id, module.module_state_key, module.student.username) return Response(json_body=self.staff_grading_data())
def _create_submissions_and_scores( self, xblock, submissions_and_scores, submission_key="text", points_possible=10 ): """ Create submissions and scores that should be displayed by the leaderboard. Args: xblock (OpenAssessmentBlock) submisions_and_scores (list): List of `(submission, score)` tuples, where `submission` is the essay text (string) and `score` is the integer number of points earned. Keyword Args: points_possible (int): The total number of points possible for this problem submission_key (string): The key to use in the submission dict. If None, use the submission value itself instead of embedding it in a dictionary. """ for num, (submission, points_earned) in enumerate(submissions_and_scores): # Assign a unique student ID # These aren't displayed by the leaderboard, so we can set them # to anything without affecting the test. student_item = xblock.get_student_item_dict() # adding rand number to the student_id to make it unique. student_item['student_id'] = "student {num} {num2}".format(num=num, num2=randint(2, 1000)) if submission_key is not None: answer = {submission_key: submission} else: answer = submission # Create a submission sub = sub_api.create_submission(student_item, answer) # Create a score for the submission sub_api.set_score(sub['uuid'], points_earned, points_possible)
def test_create_score(self): submission = api.create_submission(STUDENT_ITEM, ANSWER_ONE) student_item = self._get_student_item(STUDENT_ITEM) self._assert_submission(submission, ANSWER_ONE, student_item.pk, 1) api.set_score(submission["uuid"], 11, 12) score = api.get_latest_score_for_submission(submission["uuid"]) self._assert_score(score, 11, 12)
def test_create_score(self): submission = api.create_submission(STUDENT_ITEM, ANSWER_ONE) student_item = self._get_student_item(STUDENT_ITEM) self._assert_submission(submission, ANSWER_ONE, student_item.pk, 1) api.set_score(submission["uuid"], 11, 12) score = api.get_latest_score_for_submission(submission["uuid"]) self._assert_score(score, 11, 12) self.assertFalse(ScoreAnnotation.objects.all().exists())
def make_student(self, block, name, make_state=True, **state): """ Create a student along with submission state. """ answer = {} module = None for key in ('sha1', 'mimetype', 'filename', 'finalized'): if key in state: answer[key] = state.pop(key) score = state.pop('score', None) with transaction.atomic(): user = User(username=name) user.save() profile = UserProfile(user=user, name=name) profile.save() if make_state: module = StudentModule( module_state_key=block.location, student=user, course_id=self.course_id, state=json.dumps(state)) module.save() anonymous_id = anonymous_id_for_user(user, self.course_id) item = StudentItem( student_id=anonymous_id, course_id=self.course_id, item_id=block.block_id, item_type='sga') item.save() if answer: student_id = block.get_student_item_dict(anonymous_id) submission = submissions_api.create_submission(student_id, answer) if score is not None: submissions_api.set_score( submission['uuid'], score, block.max_score()) else: submission = None self.addCleanup(item.delete) self.addCleanup(profile.delete) self.addCleanup(user.delete) if make_state: self.addCleanup(module.delete) return { 'module': module, 'item': item, 'submission': submission } return { 'item': item, 'submission': submission }
def make_student(self, block, name, make_state=True, **state): """ Create a student along with submission state. """ answer = {} module = None for key in ('sha1', 'mimetype', 'filename', 'finalized'): if key in state: answer[key] = state.pop(key) score = state.pop('score', None) with transaction.atomic(): user = User(username=name, email='{}@example.com'.format(name)) user.save() profile = UserProfile(user=user, name=name) profile.save() if make_state: module = StudentModule( module_state_key=block.location, student=user, course_id=self.course_id, state=json.dumps(state)) module.save() anonymous_id = anonymous_id_for_user(user, self.course_id) item = StudentItem( student_id=anonymous_id, course_id=self.course_id, item_id=block.block_id, item_type='sga') item.save() if answer: student_id = block.get_student_item_dict(anonymous_id) submission = submissions_api.create_submission(student_id, answer) if score is not None: submissions_api.set_score( submission['uuid'], score, block.max_score()) else: submission = None self.addCleanup(item.delete) self.addCleanup(profile.delete) self.addCleanup(user.delete) if make_state: self.addCleanup(module.delete) return { 'module': module, 'item': item, 'submission': submission } return { 'item': item, 'submission': submission }
def test_get_score_for_submission_hidden_score(self): # Create a "hidden" score for the submission # (by convention, a score with points possible set to 0) submission = api.create_submission(STUDENT_ITEM, ANSWER_ONE) api.set_score(submission["uuid"], 0, 0) # Expect that the retrieved score is None score = api.get_latest_score_for_submission(submission['uuid']) self.assertIs(score, None)
def student_submit(self, data, suffix=''): """ AJAX handler for Submit button """ submitted_expression_values = json.loads( data['submitted_expression_values']) self.deserialize_data_from_context(data) # prepare "expressions" data for formula_service.evaluate_submission(variables, expressions) formula_service_expressions = {} for expression_name, expression in self.expressions.iteritems( ): # expressions is unicode??? formula_service_expressions[expression_name] = [ expression, submitted_expression_values[expression_name] ] # prepare "variables" data for formula_service.evaluate_submission(variables, expressions) formula_service_variables = {} # TODO cache the following loop for var_name, var_value in self.generated_variables.iteritems(): formula_service_variables[var_name] = [ self.variables[var_name], var_value ] # ask cexprtk to verify the submission evaluation_result = formula_service.evaluate_submission( formula_service_variables, formula_service_expressions) points_earned = self.max_points for expr_name, point in evaluation_result.iteritems(): if (point == 0): points_earned = 0 break # save the submission submission_data = { 'generated_question': data['saved_generated_question'], 'expression_values': data['submitted_expression_values'], 'variable_values': data['serialized_generated_variables'] } submission = sub_api.create_submission(self.student_item_key, submission_data) sub_api.set_score(submission['uuid'], points_earned, self.max_points) submit_result = {} submit_result['point_string'] = self.point_string # disable the "Submit" button once the submission attempts reach max_attemps value self.attempt_number = submission['attempt_number'] submit_result['attempt_number'] = self.attempt_number_string if (self.attempt_number >= self.max_attempts): submit_result['submit_disabled'] = 'disabled' else: submit_result['submit_disabled'] = '' return submit_result
def test_database_error(self, create_mock): # 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) # Simulate a database error when creating the reset score create_mock.side_effect = DatabaseError("Test error") with self.assertRaises(sub_api.SubmissionInternalError): sub_api.reset_score( self.STUDENT_ITEM["student_id"], self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["item_id"] )
def test_get_scores(self): student_item = copy.deepcopy(STUDENT_ITEM) student_item["course_id"] = "get_scores_course" student_item["item_id"] = "i4x://a/b/c/s1" s1 = api.create_submission(student_item, "Hello World") student_item["item_id"] = "i4x://a/b/c/s2" s2 = api.create_submission(student_item, "Hello World") student_item["item_id"] = "i4x://a/b/c/s3" s3 = api.create_submission(student_item, "Hello World") api.set_score(s1['uuid'], 3, 5) api.set_score(s1['uuid'], 4, 5) api.set_score(s1['uuid'], 2, 5) # Should overwrite previous lines api.set_score(s2['uuid'], 0, 10) api.set_score(s3['uuid'], 4, 4) # Getting the scores for a user should never take more than one query with self.assertNumQueries(1): scores = api.get_scores( student_item["course_id"], student_item["student_id"] ) self.assertEqual( scores, { 'i4x://a/b/c/s1': { 'created_at': now(), 'points_earned': 2, 'points_possible': 5, 'student_item': 1, 'submission': 1, 'submission_uuid': s1['uuid'], }, 'i4x://a/b/c/s2': { 'created_at': now(), 'points_earned': 0, 'points_possible': 10, 'student_item': 2, 'submission': 2, 'submission_uuid': s2['uuid'], }, 'i4x://a/b/c/s3': { 'created_at': now(), 'points_earned': 4, 'points_possible': 4, 'student_item': 3, 'submission': 3, 'submission_uuid': s3['uuid'], }, } )
def test_get_scores(self): student_item = copy.deepcopy(STUDENT_ITEM) student_item["course_id"] = "get_scores_course" student_item["item_id"] = "i4x://a/b/c/s1" s1 = api.create_submission(student_item, "Hello World") student_item["item_id"] = "i4x://a/b/c/s2" s2 = api.create_submission(student_item, "Hello World") student_item["item_id"] = "i4x://a/b/c/s3" s3 = api.create_submission(student_item, "Hello World") api.set_score(s1['uuid'], 3, 5) api.set_score(s1['uuid'], 4, 5) api.set_score(s1['uuid'], 2, 5) # Should overwrite previous lines api.set_score(s2['uuid'], 0, 10) api.set_score(s3['uuid'], 4, 4) # Getting the scores for a user should never take more than one query with self.assertNumQueries(1): scores = api.get_scores( student_item["course_id"], student_item["student_id"] ) self.assertEqual( scores, { u'i4x://a/b/c/s1': { 'created_at': now(), 'points_earned': 2, 'points_possible': 5, 'student_item': 1, 'submission': 1, 'submission_uuid': s1['uuid'], }, u'i4x://a/b/c/s2': { 'created_at': now(), 'points_earned': 0, 'points_possible': 10, 'student_item': 2, 'submission': 2, 'submission_uuid': s2['uuid'], }, u'i4x://a/b/c/s3': { 'created_at': now(), 'points_earned': 4, 'points_possible': 4, 'student_item': 3, 'submission': 3, 'submission_uuid': s3['uuid'], }, } )
def test_set_score_signal(self, send_mock): submission = api.create_submission(STUDENT_ITEM, ANSWER_ONE) api.set_score(submission['uuid'], 11, 12) # Verify that the send method was properly called send_mock.assert_called_with( sender=None, points_possible=12, points_earned=11, anonymous_user_id=STUDENT_ITEM['student_id'], course_id=STUDENT_ITEM['course_id'], item_id=STUDENT_ITEM['item_id'])
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_set_score_signal(self, send_mock): submission = api.create_submission(STUDENT_ITEM, ANSWER_ONE) api.set_score(submission['uuid'], 11, 12) # Verify that the send method was properly called send_mock.assert_called_with( sender=None, points_possible=12, points_earned=11, anonymous_user_id=STUDENT_ITEM['student_id'], course_id=STUDENT_ITEM['course_id'], item_id=STUDENT_ITEM['item_id'] )
def set_score(self, score): """ Set a score for the workflow. Scores are persisted via the Submissions API, separate from the Workflow Data. Score is associated with the same submission_uuid as this workflow Args: score (dict): A dict containing 'points_earned' and 'points_possible'. """ sub_api.set_score(self.submission_uuid, score["points_earned"], score["points_possible"])
def test_reset_then_get_score_for_submission(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"] ) # If we're retrieving the score for a particular submission, # instead of a student item, then we should STILL get a score. self.assertIsNot(sub_api.get_latest_score_for_submission(submission["uuid"]), None)
def score_update(self, submission_result): parent = super(ImageMagickXBlock, self) if hasattr(parent, 'score_update'): parent.score_update(submission_result) submission_uid, validation_key = submission_result.lms_key.split('+') # TODO: Validate submission # submission = submissions_api.get_submission(submission_uid) submissions_api.set_score(submission_uid, int(100 * submission_result.score), 100, annotation_reason=submission_result.msg or "undefined", annotation_creator=self.submission_type, annotation_type='check') self.points = submission_result.score self.runtime.publish( self, 'grade', { 'value': submission_result.score * self.max_score(), 'max_value': self.max_score() }) self.latest_check = None try: annotation = ifmo_submissions_api.get_annotation( self.student_submission_dict()) message_dict = json.loads(annotation.get('reason')) # edx-submission prevents datetime to be converted to str, do it manually # TODO: Create own serializer for Score model score = annotation.get("score") score["created_at"] = str(score["created_at"]) self.latest_check = { "score": score, "report_file": message_dict.get('report_file', ''), "report_storage": REPORT_STORAGE, "message": message_dict.get( 'err_output', 'Произошла неизвестная ошибка при сравнении изображений.'), } except (ValueError, KeyError): pass
def enter_grade(self, request, suffix=''): require(self.is_course_staff()) module = StudentModule.objects.get(pk=request.params['module_id']) state = json.loads(module.state) score = int(request.params['grade']) if self.is_instructor(): uuid = request.params['submission_id'] submissions_api.set_score(uuid, score, self.max_score()) else: state['staff_score'] = score state['comment'] = request.params.get('comment', '') module.state = json.dumps(state) module.save() return Response(json_body=self.staff_grading_data())
def test_database_error(self, create_mock): # 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) # Simulate a database error when creating the reset score create_mock.side_effect = DatabaseError("Test error") with self.assertRaises(sub_api.SubmissionInternalError): sub_api.reset_score( self.STUDENT_ITEM['student_id'], self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['item_id'], )
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_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 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_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 setUp(self): """ Create a submission and score. """ self.submission = sub_api.create_submission(self.STUDENT_ITEM, "test answer") self.score = sub_api.set_score( self.submission['uuid'], self.SCORE["points_earned"], self.SCORE["points_possible"] )
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 test_override_doesnt_overwrite_submission_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, 8, 10, ) submission_score = sub_api.get_latest_score_for_submission(submission['uuid']) self.assertEqual(submission_score['points_earned'], 1) self.assertEqual(submission_score['points_possible'], 10) override_score = sub_api.get_score_override(self.STUDENT_ITEM) self.assertEqual(override_score['points_earned'], 8) self.assertEqual(override_score['points_possible'], 10)