Commit 53afc52b by Diana Huang Committed by Andy Armstrong

New staff mixin and tests for mixin and api.

parent af813766
......@@ -31,6 +31,7 @@ from openassessment.xblock.studio_mixin import StudioMixin
from openassessment.xblock.xml import parse_from_xml, serialize_content_to_xml
from openassessment.xblock.staff_area_mixin import StaffAreaMixin
from openassessment.xblock.workflow_mixin import WorkflowMixin
from openassessment.xblock.staff_assessment_mixin import StaffAssessmentMixin
from openassessment.workflow.errors import AssessmentWorkflowError
from openassessment.xblock.student_training_mixin import StudentTrainingMixin
from openassessment.xblock.validation import validator
......@@ -106,6 +107,7 @@ class OpenAssessmentBlock(
SubmissionMixin,
PeerAssessmentMixin,
SelfAssessmentMixin,
StaffAssessmentMixin,
StudioMixin,
GradeMixin,
LeaderboardMixin,
......
"""
A mixin for staff grading.
"""
import logging
from staff_area_mixin import require_course_staff
from xblock.core import XBlock
from openassessment.assessment.api import staff as staff_api
from openassessment.assessment.errors import (
StaffAssessmentRequestError, StaffAssessmentInternalError
)
from .data_conversion import create_rubric_dict
from .resolve_dates import DISTANT_FUTURE
from .data_conversion import clean_criterion_feedback, create_submission_dict
logger = logging.getLogger(__name__)
class StaffAssessmentMixin(object):
"""
This mixin is for all staff-assessment related endpoints.
"""
@XBlock.json_handler
@require_course_staff("STUDENT_INFO")
def staff_assess(self, data, suffix=''):
"""
Create a staff assessment from a staff submission.
"""
if 'options_selected' not in data:
return {'success': False, 'msg': self._(u"Missing options_selected key in request")}
if 'overall_feedback' not in data:
return {'success': False, 'msg': self._('Must provide overall feedback in the assessment')}
if 'criterion_feedback' not in data:
return {'success': False, 'msg': self._('Must provide feedback for criteria in the assessment')}
if 'submission_uuid' not in data:
return {'success': False, 'msg': self._(u"Missing the submission id of the submission being assessed.")}
try:
assessment = staff_api.create_assessment(
data['submission_uuid'],
self.get_student_item_dict()["student_id"],
data['options_selected'],
clean_criterion_feedback(self.rubric_criteria, data['criterion_feedback']),
data['overall_feedback'],
create_rubric_dict(self.prompts, self.rubric_criteria_with_labels)
)
self.publish_assessment_event("openassessmentblock.staff_assessment", assessment)
except StaffAssessmentRequestError:
logger.warning(
u"An error occurred while submitting a staff assessment "
u"for the submission {}".format(self.submission_uuid),
exc_info=True
)
msg = self._(u"Your staff assessment could not be submitted.")
return {'success': False, 'msg': msg}
except StaffAssessmentInternalError:
logger.exception(
u"An error occurred while submitting a staff assessment "
u"for the submission {}".format(self.submission_uuid),
)
msg = self._(u"Your staff assessment could not be submitted.")
return {'success': False, 'msg': msg}
else:
return {'success': True, 'msg': u""}
@XBlock.handler
@require_course_staff("STUDENT_INFO")
def render_staff_assessment(self, data, suffix=''):
"""
Render the staff assessment for the given student.
"""
try:
submission_uuid = data.get("submission_uuid")
path, context = self.self_path_and_context(submission_uuid)
except:
msg = u"Could not retrieve staff assessment for submission {}".format(self.submission_uuid)
logger.exception(msg)
return self.render_error(self._(u"An unexpected error occurred."))
else:
return self.render_assessment(path, context)
def staff_path_and_context(self, submission_uuid):
"""
Retrieve the correct template path and template context for the handler to render.
Args:
submission_uuid (str) -
"""
#TODO: add in the workflow for staff grading instead of assuming it's allowed.
submission = submission_api.get_submission(self.submission_uuid)
context = {'allow_latex': self.allow_latex}
context["rubric_criteria"] = self.rubric_criteria_with_labels
context["estimated_time"] = "20 minutes" # TODO: Need to configure this.
context["self_submission"] = create_submission_dict(submission, self.prompts)
# Determine if file upload is supported for this XBlock.
context["allow_file_upload"] = self.allow_file_upload
context['self_file_url'] = self.get_download_url_from_submission(submission)
#TODO: Replace with the staff assessment template when it's been built.
path = 'openassessmentblock/self/oa_self_assessment.html'
return path, context
# -*- coding: utf-8 -*-
"""
Tests for staff assessment handlers in Open Assessment XBlock.
"""
import json
import mock
import copy
from openassessment.assessment.api import staff as staff_api
from openassessment.xblock.data_conversion import create_rubric_dict
from .base import XBlockHandlerTestCase, scenario
class StaffAssessmentTestBase(XBlockHandlerTestCase):
maxDiff = None
SUBMISSION = (u'ՇﻉรՇ', u'รપ๒๓ٱรรٱѻก')
ASSESSMENT = {
'options_selected': {u'𝓒𝓸𝓷𝓬𝓲𝓼𝓮': u'ﻉซƈﻉɭɭﻉกՇ', u'Form': u'Fair'},
'criterion_feedback': {},
'overall_feedback': ""
}
def set_staff_access(self, xblock):
xblock.xmodule_runtime = mock.Mock(user_is_staff=True)
xblock.xmodule_runtime.anonymous_student_id = 'Bob'
class TestStaffAssessment(StaffAssessmentTestBase):
@scenario('data/self_assessment_scenario.xml', user_id='Bob')
def test_staff_assess_handler(self, xblock):
student_item = xblock.get_student_item_dict()
# Create a submission for the student
submission = xblock.create_submission(student_item, self.SUBMISSION)
# Submit a staff-assessment
self.set_staff_access(xblock)
self.ASSESSMENT['submission_uuid'] = submission['uuid']
resp = self.request(xblock, 'staff_assess', json.dumps(self.ASSESSMENT), response_format='json')
self.assertTrue(resp['success'])
# Expect that a staff-assessment was created
assessment = staff_api.get_latest_assessment(submission['uuid'])
self.assertEqual(assessment['submission_uuid'], submission['uuid'])
self.assertEqual(assessment['points_earned'], 5)
self.assertEqual(assessment['points_possible'], 6)
self.assertEqual(assessment['scorer_id'], 'Bob')
self.assertEqual(assessment['score_type'], 'ST')
self.assertEqual(assessment['feedback'], u'')
parts = sorted(assessment['parts'])
self.assertEqual(len(parts), 2)
self.assertEqual(parts[0]['option']['criterion']['name'], u'Form')
self.assertEqual(parts[0]['option']['name'], 'Fair')
self.assertEqual(parts[1]['option']['criterion']['name'], u'𝓒𝓸𝓷𝓬𝓲𝓼𝓮')
self.assertEqual(parts[1]['option']['name'], u'ﻉซƈﻉɭɭﻉกՇ')
# get the assessment scores by criteria
assessment_by_crit = staff_api.get_assessment_scores_by_criteria(submission["uuid"])
self.assertEqual(assessment_by_crit[u'𝓒𝓸𝓷𝓬𝓲𝓼𝓮'], 3)
self.assertEqual(assessment_by_crit[u'Form'], 2)
score = staff_api.get_score(submission["uuid"], None)
self.assertEqual(assessment['points_earned'], score['points_earned'])
self.assertEqual(assessment['points_possible'], score['points_possible'])
@scenario('data/self_assessment_scenario.xml', user_id='Bob')
def test_staff_assess_permission_error(self, xblock):
# Create a submission for the student
student_item = xblock.get_student_item_dict()
xblock.create_submission(student_item, self.SUBMISSION)
resp = self.request(xblock, 'staff_assess', json.dumps(self.ASSESSMENT))
self.assertIn("You do not have permission", resp)
@scenario('data/self_assessment_scenario.xml', user_id='Bob')
def test_staff_assess_invalid_options(self, xblock):
student_item = xblock.get_student_item_dict()
# Create a submission for the student
submission = xblock.create_submission(student_item, self.SUBMISSION)
self.set_staff_access(xblock)
self.ASSESSMENT['submission_uuid'] = submission['uuid']
for key in self.ASSESSMENT:
assessment_copy = copy.copy(self.ASSESSMENT)
del assessment_copy[key]
resp = self.request(xblock, 'staff_assess', json.dumps(assessment_copy), response_format='json')
self.assertFalse(resp['success'])
self.assertIn('msg', resp)
@scenario('data/self_assessment_scenario.xml', user_id='bob')
def test_staff_assess_assessment_error(self, xblock):
student_item = xblock.get_student_item_dict()
# Create a submission for the student
submission = xblock.create_submission(student_item, self.SUBMISSION)
self.set_staff_access(xblock)
self.ASSESSMENT['submission_uuid'] = submission['uuid']
with mock.patch('openassessment.xblock.staff_assessment_mixin.staff_api') as mock_api:
# Simulate a error
mock_api.create_assessment.side_effect = staff_api.StaffAssessmentRequestError
resp = self.request(xblock, 'staff_assess', json.dumps(self.ASSESSMENT), response_format='json')
self.assertFalse(resp['success'])
self.assertIn('msg', resp)
# Simulate a different error
mock_api.create_assessment.side_effect = staff_api.StaffAssessmentInternalError
resp = self.request(xblock, 'staff_assess', json.dumps(self.ASSESSMENT), response_format='json')
self.assertFalse(resp['success'])
self.assertIn('msg', resp)
class TestStaffAssessmentRender(StaffAssessmentTestBase):
#TODO: test success when staff assessment template exists
@scenario('data/self_assessment_scenario.xml', user_id='Bob')
def test_render_staff_assessment_permission_error(self, xblock):
# Create a submission for the student
student_item = xblock.get_student_item_dict()
xblock.create_submission(student_item, self.SUBMISSION)
resp = self.request(xblock, 'render_staff_assessment', json.dumps(self.ASSESSMENT))
self.assertIn("You do not have permission", resp)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment