def test_create_feedback_on_an_assessment(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") peer_api.get_submission_to_assess(bob_sub['uuid'], 1) assessment = 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, ) peer_api.get_submission_to_assess(tim_sub['uuid'], 1) 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, ) peer_api.get_score( tim_sub["uuid"], { 'must_grade': 1, 'must_be_graded_by': 1 } ) feedback = peer_api.get_assessment_feedback(tim_sub['uuid']) self.assertIsNone(feedback) peer_api.set_assessment_feedback( { 'submission_uuid': tim_sub['uuid'], 'feedback_text': 'Bob is a jerk!', 'options': [ 'I disliked this assessment', 'I felt this assessment was unfair', ] } ) saved_feedback = peer_api.get_assessment_feedback(tim_sub['uuid']) self.assertIsNot(saved_feedback, None) self.assertEquals(saved_feedback['submission_uuid'], assessment['submission_uuid']) self.assertEquals(saved_feedback['feedback_text'], 'Bob is a jerk!') self.assertItemsEqual(saved_feedback['options'], [ {'text': 'I disliked this assessment'}, {'text': 'I felt this assessment was unfair'}, ]) self.assertEquals(saved_feedback["assessments"][0]["submission_uuid"], assessment["submission_uuid"])
def test_submit_feedback(self, xblock): # Create submissions and assessments self.create_submission_and_assessments(xblock, self.SUBMISSION, self.PEERS, PEER_ASSESSMENTS, SELF_ASSESSMENT) # Submit feedback on the assessments payload = json.dumps({ 'feedback_text': u'I disliked my assessment', 'feedback_options': [u'Option 1', u'Option 2'], }) resp = self.request(xblock, 'submit_feedback', payload, response_format='json') self.assertTrue(resp['success']) # Verify that the feedback was created in the database feedback = peer_api.get_assessment_feedback(xblock.submission_uuid) self.assertIsNot(feedback, None) self.assertEqual(feedback['feedback_text'], u'I disliked my assessment') self.assertItemsEqual(feedback['options'], [{ 'text': u'Option 1' }, { 'text': u'Option 2' }])
def test_submit_feedback_no_options(self, xblock): # Create submissions and assessments self.create_submission_and_assessments( xblock, self.SUBMISSION, self.PEERS, PEER_ASSESSMENTS, SELF_ASSESSMENT ) # Submit feedback on the assessments with no options specified payload = json.dumps({ 'feedback_text': u'I disliked my assessment', 'feedback_options': [], }) resp = self.request(xblock, 'submit_feedback', payload, response_format='json') self.assertTrue(resp['success']) # Verify that the feedback was created in the database feedback = peer_api.get_assessment_feedback(xblock.submission_uuid) self.assertIsNot(feedback, None) self.assertItemsEqual(feedback['options'], [])
def test_submit_feedback_no_options(self, xblock): # Create submissions and assessments self._create_submission_and_assessments( xblock, self.SUBMISSION, self.PEERS, self.ASSESSMENTS, self.ASSESSMENTS[0] ) # Submit feedback on the assessments with no options specified payload = json.dumps({ 'feedback_text': u'I disliked my assessment', 'feedback_options': [], }) resp = self.request(xblock, 'submit_feedback', payload, response_format='json') self.assertTrue(resp['success']) # Verify that the feedback was created in the database feedback = peer_api.get_assessment_feedback(xblock.submission_uuid) self.assertIsNot(feedback, None) self.assertItemsEqual(feedback['options'], [])
def test_submit_feedback(self, xblock): # Create submissions and assessments self._create_submission_and_assessments( xblock, self.SUBMISSION, self.PEERS, self.ASSESSMENTS, self.ASSESSMENTS[0] ) # Submit feedback on the assessments payload = json.dumps({ 'feedback_text': u'I disliked my assessment', 'feedback_options': [u'Option 1', u'Option 2'], }) resp = self.request(xblock, 'submit_feedback', payload, response_format='json') self.assertTrue(resp['success']) # Verify that the feedback was created in the database feedback = peer_api.get_assessment_feedback(xblock.submission_uuid) self.assertIsNot(feedback, None) self.assertEqual(feedback['feedback_text'], u'I disliked my assessment') self.assertItemsEqual( feedback['options'], [{'text': u'Option 1'}, {'text': u'Option 2'}] )
def render_grade_complete(self, workflow): """ Render the grade complete state. Args: workflow (dict): The serialized Workflow model. Returns: tuple of context (dict), template_path (string) """ # Import is placed here to avoid model import at project startup. from openassessment.assessment.api import peer as peer_api from openassessment.assessment.api import self as self_api from openassessment.assessment.api import staff as staff_api from submissions import api as sub_api # Peer specific stuff... assessment_steps = self.assessment_steps submission_uuid = workflow['submission_uuid'] staff_assessment = None self_assessment = None feedback = None peer_assessments = [] has_submitted_feedback = False if "peer-assessment" in assessment_steps: peer_api.get_score(submission_uuid, self.workflow_requirements()["peer"]) feedback = peer_api.get_assessment_feedback(submission_uuid) peer_assessments = [ self._assessment_grade_context(peer_assessment) for peer_assessment in peer_api.get_assessments(submission_uuid) ] has_submitted_feedback = feedback is not None if "self-assessment" in assessment_steps: self_assessment = self._assessment_grade_context( self_api.get_assessment(submission_uuid) ) raw_staff_assessment = staff_api.get_latest_staff_assessment(submission_uuid) if raw_staff_assessment: staff_assessment = self._assessment_grade_context(raw_staff_assessment) feedback_text = feedback.get('feedback', '') if feedback else '' student_submission = sub_api.get_submission(submission_uuid) # We retrieve the score from the workflow, which in turn retrieves # the score for our current submission UUID. # We look up the score by submission UUID instead of student item # to ensure that the score always matches the rubric. # It's possible for the score to be `None` even if the workflow status is "done" # when all the criteria in the rubric are feedback-only (no options). score = workflow['score'] context = { 'score': score, 'feedback_text': feedback_text, 'has_submitted_feedback': has_submitted_feedback, 'student_submission': create_submission_dict(student_submission, self.prompts), 'peer_assessments': peer_assessments, 'grade_details': self.grade_details( submission_uuid, peer_assessments=peer_assessments, self_assessment=self_assessment, staff_assessment=staff_assessment, ), 'file_upload_type': self.file_upload_type, 'allow_latex': self.allow_latex, 'file_urls': self.get_download_urls_from_submission(student_submission), 'xblock_id': self.get_xblock_id() } return ('openassessmentblock/grade/oa_grade_complete.html', context)
def render_grade_complete(self, workflow): """ Render the grade complete state. Args: workflow (dict): The serialized Workflow model. Returns: tuple of context (dict), template_path (string) """ # Import is placed here to avoid model import at project startup. from openassessment.assessment.api import peer as peer_api from openassessment.assessment.api import self as self_api from openassessment.assessment.api import staff as staff_api from submissions import api as sub_api # Peer specific stuff... assessment_steps = self.assessment_steps submission_uuid = workflow['submission_uuid'] staff_assessment = None self_assessment = None feedback = None peer_assessments = [] has_submitted_feedback = False if "peer-assessment" in assessment_steps: peer_api.get_score(submission_uuid, self.workflow_requirements()["peer"]) feedback = peer_api.get_assessment_feedback(submission_uuid) peer_assessments = [ self._assessment_grade_context(peer_assessment) for peer_assessment in peer_api.get_assessments(submission_uuid) ] has_submitted_feedback = feedback is not None if "self-assessment" in assessment_steps: self_assessment = self._assessment_grade_context( self_api.get_assessment(submission_uuid)) raw_staff_assessment = staff_api.get_latest_staff_assessment( submission_uuid) if raw_staff_assessment: staff_assessment = self._assessment_grade_context( raw_staff_assessment) feedback_text = feedback.get('feedback', '') if feedback else '' student_submission = sub_api.get_submission(submission_uuid) # We retrieve the score from the workflow, which in turn retrieves # the score for our current submission UUID. # We look up the score by submission UUID instead of student item # to ensure that the score always matches the rubric. # It's possible for the score to be `None` even if the workflow status is "done" # when all the criteria in the rubric are feedback-only (no options). score = workflow['score'] context = { 'score': score, 'feedback_text': feedback_text, 'has_submitted_feedback': has_submitted_feedback, 'student_submission': create_submission_dict(student_submission, self.prompts), 'peer_assessments': peer_assessments, 'grade_details': self.grade_details( submission_uuid, peer_assessments=peer_assessments, self_assessment=self_assessment, staff_assessment=staff_assessment, ), 'file_upload_type': self.file_upload_type, 'allow_latex': self.allow_latex, 'prompts_type': self.prompts_type, 'file_urls': self.get_download_urls_from_submission(student_submission), 'xblock_id': self.get_xblock_id() } return ('openassessmentblock/grade/oa_grade_complete.html', context)
def render_grade_complete(self, workflow): """ Render the grade complete state. Args: workflow (dict): The serialized Workflow model. Returns: tuple of context (dict), template_path (string) """ # Peer specific stuff... assessment_steps = self.assessment_steps submission_uuid = workflow['submission_uuid'] example_based_assessment = None self_assessment = None feedback = None peer_assessments = [] has_submitted_feedback = False if "peer-assessment" in assessment_steps: feedback = peer_api.get_assessment_feedback(submission_uuid) peer_assessments = [ self._assessment_grade_context(asmnt) for asmnt in peer_api.get_assessments(submission_uuid) ] has_submitted_feedback = feedback is not None if "self-assessment" in assessment_steps: self_assessment = self._assessment_grade_context( self_api.get_assessment(submission_uuid)) if "example-based-assessment" in assessment_steps: example_based_assessment = self._assessment_grade_context( ai_api.get_latest_assessment(submission_uuid)) feedback_text = feedback.get('feedback', '') if feedback else '' student_submission = sub_api.get_submission(submission_uuid) # We retrieve the score from the workflow, which in turn retrieves # the score for our current submission UUID. # We look up the score by submission UUID instead of student item # to ensure that the score always matches the rubric. # It's possible for the score to be `None` even if the workflow status is "done" # when all the criteria in the rubric are feedback-only (no options). score = workflow['score'] context = { 'score': score, 'feedback_text': feedback_text, 'student_submission': student_submission, 'peer_assessments': peer_assessments, 'self_assessment': self_assessment, 'example_based_assessment': example_based_assessment, 'rubric_criteria': self._rubric_criteria_grade_context(peer_assessments, self_assessment), 'has_submitted_feedback': has_submitted_feedback, 'allow_file_upload': self.allow_file_upload, 'file_url': self.get_download_url_from_submission(student_submission) } # Update the scores we will display to the user # Note that we are updating a *copy* of the rubric criteria stored in # the XBlock field max_scores = peer_api.get_rubric_max_scores(submission_uuid) median_scores = None if "peer-assessment" in assessment_steps: median_scores = peer_api.get_assessment_median_scores( submission_uuid) elif "self-assessment" in assessment_steps: median_scores = self_api.get_assessment_scores_by_criteria( submission_uuid) elif "example-based-assessment" in assessment_steps: median_scores = ai_api.get_assessment_scores_by_criteria( submission_uuid) if median_scores is not None and max_scores is not None: for criterion in context["rubric_criteria"]: # Although we prevent course authors from modifying criteria post-release, # it's still possible for assessments created by course staff to # have criteria that differ from the current problem definition. # It's also possible to circumvent the post-release restriction # if course authors directly import a course into Studio. # If this happens, we simply leave the score blank so that the grade # section can render without error. criterion["median_score"] = median_scores.get( criterion["name"], '') criterion["total_value"] = max_scores.get( criterion["name"], '') return ('openassessmentblock/grade/oa_grade_complete.html', context)
def render_grade_complete(self, workflow): """ Render the grade complete state. Args: workflow (dict): The serialized Workflow model. Returns: tuple of context (dict), template_path (string) """ # Peer specific stuff... assessment_steps = self.assessment_steps submission_uuid = workflow['submission_uuid'] example_based_assessment = None self_assessment = None feedback = None peer_assessments = [] has_submitted_feedback = False if "peer-assessment" in assessment_steps: feedback = peer_api.get_assessment_feedback(submission_uuid) peer_assessments = [ self._assessment_grade_context(asmnt) for asmnt in peer_api.get_assessments(submission_uuid) ] has_submitted_feedback = feedback is not None if "self-assessment" in assessment_steps: self_assessment = self._assessment_grade_context( self_api.get_assessment(submission_uuid) ) if "example-based-assessment" in assessment_steps: example_based_assessment = self._assessment_grade_context( ai_api.get_latest_assessment(submission_uuid) ) feedback_text = feedback.get('feedback', '') if feedback else '' student_submission = sub_api.get_submission(submission_uuid) # We retrieve the score from the workflow, which in turn retrieves # the score for our current submission UUID. # We look up the score by submission UUID instead of student item # to ensure that the score always matches the rubric. # It's possible for the score to be `None` even if the workflow status is "done" # when all the criteria in the rubric are feedback-only (no options). score = workflow['score'] context = { 'score': score, 'feedback_text': feedback_text, 'student_submission': student_submission, 'peer_assessments': peer_assessments, 'self_assessment': self_assessment, 'example_based_assessment': example_based_assessment, 'rubric_criteria': self._rubric_criteria_grade_context(peer_assessments, self_assessment), 'has_submitted_feedback': has_submitted_feedback, 'allow_file_upload': self.allow_file_upload, 'allow_latex': self.allow_latex, 'file_url': self.get_download_url_from_submission(student_submission) } # Update the scores we will display to the user # Note that we are updating a *copy* of the rubric criteria stored in # the XBlock field max_scores = peer_api.get_rubric_max_scores(submission_uuid) median_scores = None if "peer-assessment" in assessment_steps: median_scores = peer_api.get_assessment_median_scores(submission_uuid) elif "self-assessment" in assessment_steps: median_scores = self_api.get_assessment_scores_by_criteria(submission_uuid) elif "example-based-assessment" in assessment_steps: median_scores = ai_api.get_assessment_scores_by_criteria(submission_uuid) if median_scores is not None and max_scores is not None: for criterion in context["rubric_criteria"]: # Although we prevent course authors from modifying criteria post-release, # it's still possible for assessments created by course staff to # have criteria that differ from the current problem definition. # It's also possible to circumvent the post-release restriction # if course authors directly import a course into Studio. # If this happens, we simply leave the score blank so that the grade # section can render without error. criterion["median_score"] = median_scores.get(criterion["name"], '') criterion["total_value"] = max_scores.get(criterion["name"], '') return ('openassessmentblock/grade/oa_grade_complete.html', context)
def render_grade_complete(self, workflow): """ Render the grade complete state. Args: workflow (dict): The serialized Workflow model. Returns: tuple of context (dict), template_path (string) """ # Peer specific stuff... assessment_steps = self.assessment_steps submission_uuid = workflow['submission_uuid'] if "peer-assessment" in assessment_steps: feedback = peer_api.get_assessment_feedback(submission_uuid) peer_assessments = peer_api.get_assessments(submission_uuid) has_submitted_feedback = feedback is not None else: feedback = None peer_assessments = [] has_submitted_feedback = False if "self-assessment" in assessment_steps: self_assessment = self_api.get_assessment(submission_uuid) else: self_assessment = None feedback_text = feedback.get('feedback', '') if feedback else '' student_submission = sub_api.get_submission(submission_uuid) # We retrieve the score from the workflow, which in turn retrieves # the score for our current submission UUID. # We look up the score by submission UUID instead of student item # to ensure that the score always matches the rubric. score = workflow['score'] context = { 'score': score, 'feedback_text': feedback_text, 'student_submission': student_submission, 'peer_assessments': peer_assessments, 'self_assessment': self_assessment, 'rubric_criteria': self._rubric_criteria_with_feedback(peer_assessments), 'has_submitted_feedback': has_submitted_feedback, } # Update the scores we will display to the user # Note that we are updating a *copy* of the rubric criteria stored in # the XBlock field max_scores = peer_api.get_rubric_max_scores(submission_uuid) if "peer-assessment" in assessment_steps: median_scores = peer_api.get_assessment_median_scores(submission_uuid) elif "self-assessment" in assessment_steps: median_scores = self_api.get_assessment_scores_by_criteria(submission_uuid) if median_scores is not None and max_scores is not None: for criterion in context["rubric_criteria"]: criterion["median_score"] = median_scores[criterion["name"]] criterion["total_value"] = max_scores[criterion["name"]] return ('openassessmentblock/grade/oa_grade_complete.html', context)
def test_get_assessment_feedback_error(self, mock_filter): mock_filter.side_effect = DatabaseError("Oh no.") tim_answer, tim = self._create_student_and_submission("Tim", "Tim's answer", MONDAY) peer_api.get_assessment_feedback(tim_answer['uuid'])
def render_grade_complete(self, workflow): """ Render the grade complete state. Args: workflow (dict): The serialized Workflow model. Returns: tuple of context (dict), template_path (string) """ # Peer specific stuff... assessment_steps = self.assessment_steps submission_uuid = workflow['submission_uuid'] if "peer-assessment" in assessment_steps: feedback = peer_api.get_assessment_feedback(submission_uuid) peer_assessments = peer_api.get_assessments(submission_uuid) has_submitted_feedback = feedback is not None else: feedback = None peer_assessments = [] has_submitted_feedback = False if "self-assessment" in assessment_steps: self_assessment = self_api.get_assessment(submission_uuid) else: self_assessment = None feedback_text = feedback.get('feedback', '') if feedback else '' student_submission = sub_api.get_submission(submission_uuid) # We retrieve the score from the workflow, which in turn retrieves # the score for our current submission UUID. # We look up the score by submission UUID instead of student item # to ensure that the score always matches the rubric. score = workflow['score'] context = { 'score': score, 'feedback_text': feedback_text, 'student_submission': student_submission, 'peer_assessments': peer_assessments, 'self_assessment': self_assessment, 'rubric_criteria': self._rubric_criteria_with_feedback(peer_assessments), 'has_submitted_feedback': has_submitted_feedback, } # Update the scores we will display to the user # Note that we are updating a *copy* of the rubric criteria stored in # the XBlock field max_scores = peer_api.get_rubric_max_scores(submission_uuid) if "peer-assessment" in assessment_steps: median_scores = peer_api.get_assessment_median_scores( submission_uuid) elif "self-assessment" in assessment_steps: median_scores = self_api.get_assessment_scores_by_criteria( submission_uuid) if median_scores is not None and max_scores is not None: for criterion in context["rubric_criteria"]: criterion["median_score"] = median_scores[criterion["name"]] criterion["total_value"] = max_scores[criterion["name"]] return ('openassessmentblock/grade/oa_grade_complete.html', context)