Commit 6e1db5b7 by Eric Fischer

Clear Submission

Remove a submission from ORA by cancelling it to remove from grading
pools, then passing through to submissions.
parent ab77af40
......@@ -492,24 +492,52 @@ class StaffAreaMixin(object):
'msg': self._(u"An error occurred while rescheduling tasks: {}".format(ex))
}
def clear_student_state(self, user_id, course_id, item_id):
"""
This xblock method is called (from our LMS runtime, which defines this method signature) to clear student state
for a given problem. It will cancel the workflow using traditional methods to remove it from the grading pools,
and pass through to the submissions API to orphan the submission so that the user can create a new one.
"""
# Note that student_item cannot be constructed using get_student_item_dict, since we're in a staff context
student_item = {
'course_id': course_id,
'student_id': user_id,
'item_id': item_id,
'item_type': 'openassessment',
}
# There *should* only be one submission, but the logic is easy to extend for multiples so we may as well do it
submissions = submission_api.get_submissions(student_item)
for sub in submissions:
# Remove the submission from grading pools
self._cancel_workflow(sub['uuid'], "Student state cleared")
# Tell the submissions API to orphan the submission to prevent it from being accessed
submission_api.reset_score(
user_id,
course_id,
item_id,
clear_state=True # pylint: disable=unexpected-keyword-arg
)
# TODO: try to remove the above pylint disable once edx-submissions release is done
@XBlock.json_handler
@require_course_staff("STUDENT_INFO", with_json_handler=True)
def cancel_submission(self, data, suffix=''):
def cancel_submission(self, data, suffix=''): # pylint: disable=W0613
"""
This will cancel the assessment + peer workflow for the particular submission.
This will cancel the assessment + peer workflow for the particular submission.
Args:
data (dict): Data contain two attributes: submission_uuid and
comments. submission_uuid is id of submission which is to be
removed from the grading pool. Comments is the reason given
by the user.
Args:
data (dict): Data contain two attributes: submission_uuid and
comments. submission_uuid is id of submission which is to be
removed from the grading pool. Comments is the reason given
by the user.
suffix (not used)
suffix (not used)
Return:
Json serializable dict with the following elements:
'success': (bool) Indicates whether or not the workflow cancelled successfully.
'msg': The response (could be error message or success message).
Return:
Json serializable dict with the following elements:
'success': (bool) Indicates whether or not the workflow cancelled successfully.
'msg': The response (could be error message or success message).
"""
submission_uuid = data.get('submission_uuid')
comments = data.get('comments')
......@@ -517,9 +545,15 @@ class StaffAreaMixin(object):
if not comments:
return {"success": False, "msg": self._(u'Please enter valid reason to remove the submission.')}
student_item_dict = self.get_student_item_dict()
return self._cancel_workflow(submission_uuid, comments)
def _cancel_workflow(self, submission_uuid, comments):
"""
Internal helper method to cancel a workflow using the workflow API.
"""
try:
assessment_requirements = self.workflow_requirements()
student_item_dict = self.get_student_item_dict()
# Cancel the related workflow.
workflow_api.cancel_workflow(
submission_uuid=submission_uuid, comments=comments,
......
......@@ -740,6 +740,42 @@ class TestCourseStaff(XBlockHandlerTestCase):
'submission_returned_uuid': submission['uuid']
})
@scenario('data/self_only_scenario.xml', user_id='Bob')
def test_staff_delete_student_state(self, xblock):
# Simulate that we are course staff
xblock.xmodule_runtime = self._create_mock_runtime(
xblock.scope_ids.usage_id, True, False, 'Bob'
)
xblock.runtime._services['user'] = NullUserService() # pylint: disable=protected-access
bob_item = STUDENT_ITEM.copy()
bob_item["item_id"] = xblock.scope_ids.usage_id
# Create a submission for Bob, and corresponding workflow.
submission = self._create_submission(bob_item, {'text': "Bob Answer"}, ['self'])
# Bob assesses himself.
self_api.create_assessment(
submission['uuid'],
STUDENT_ITEM["student_id"],
ASSESSMENT_DICT['options_selected'],
ASSESSMENT_DICT['criterion_feedback'],
ASSESSMENT_DICT['overall_feedback'],
{'criteria': xblock.rubric_criteria},
)
request = namedtuple('Request', 'params')
request.params = {"student_username": 'Bob'}
# Verify that we can see the student's grade
resp = xblock.render_student_info(request)
self.assertIn("final grade", resp.body.lower())
# Clear the submission
xblock.clear_student_state('Bob', 'test_course', xblock.scope_ids.usage_id)
# Verify that the submission was cleared
resp = xblock.render_student_info(request)
self.assertIn("response was not found", resp.body.lower())
def _verify_staff_assessment_context(self, context, required, ungraded=None, in_progress=None):
self.assertEquals(required, context['staff_assessment_required'])
if not required:
......
......@@ -6,7 +6,7 @@
git+https://github.com/edx/XBlock.git@xblock-0.4.1#egg=XBlock==0.4.1
# edx-submissions
git+https://github.com/edx/edx-submissions.git@0.1.3#egg=edx-submissions==0.1.3
git+https://github.com/edx/edx-submissions.git@0.1.4#egg=edx-submissions==0.1.4
# Third Party Requirements
boto>=2.32.1,<3.0.0
......
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