'This is called by courseware.module_render, to handle an AJAX call.
Used only for grading. Returns XML response.
Example of request body from LTI provider::
<?xml version = "1.0" encoding = "UTF-8"?>
<imsx_POXEnvelopeRequest xmlns = "some_link (may be not required)">
<imsx_POXHeader>
<imsx_POXRequestHeaderInfo>
<imsx_version>V1.0</imsx_version>
<imsx_messageIdentifier>528243ba5241b</imsx_messageIdentifier>
</imsx_POXRequestHeaderInfo>
</imsx_POXHeader>
<imsx_POXBody>
<replaceResultRequest>
<resultRecord>
<sourcedGUID>
<sourcedId>feb-123-456-2929::28883</sourcedId>
</sourcedGUID>
<result>
<resultScore>
<language>en-us</language>
<textString>0.4</textString>
</resultScore>
</result>
</resultRecord>
</replaceResultRequest>
</imsx_POXBody>
</imsx_POXEnvelopeRequest>
Example of correct/incorrect answer XML body:: see response_xml_template.'

@XBlock.handler
def grade_handler(self, request, suffix):

    response_xml_template = textwrap.dedent('                                                <?xml    version="1.0"    encoding="UTF-8"?>\n                                                <imsx_POXEnvelopeResponse    xmlns    =    "http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">\n                                                                <imsx_POXHeader>\n                                                                                <imsx_POXResponseHeaderInfo>\n                                                                                                <imsx_version>V1.0</imsx_version>\n                                                                                                <imsx_messageIdentifier>{imsx_messageIdentifier}</imsx_messageIdentifier>\n                                                                                                <imsx_statusInfo>\n                                                                                                                <imsx_codeMajor>{imsx_codeMajor}</imsx_codeMajor>\n                                                                                                                <imsx_severity>status</imsx_severity>\n                                                                                                                <imsx_description>{imsx_description}</imsx_description>\n                                                                                                                <imsx_messageRefIdentifier>\n                                                                                                                </imsx_messageRefIdentifier>\n                                                                                                </imsx_statusInfo>\n                                                                                </imsx_POXResponseHeaderInfo>\n                                                                </imsx_POXHeader>\n                                                                <imsx_POXBody>{response}</imsx_POXBody>\n                                                </imsx_POXEnvelopeResponse>\n                                ')
    unsupported_values = {'imsx_codeMajor': 'unsupported', 'imsx_description': 'Target    does    not    support    the    requested    operation.', 'imsx_messageIdentifier': 'unknown', 'response': ''}
    failure_values = {'imsx_codeMajor': 'failure', 'imsx_description': 'The    request    has    failed.', 'imsx_messageIdentifier': 'unknown', 'response': ''}
    if ((not self.accept_grades_past_due) and self.is_past_due()):
        failure_values['imsx_description'] = 'Grade    is    past    due'
        return Response(response_xml_template.format(**failure_values), content_type='application/xml')
    try:
        (imsx_messageIdentifier, sourcedId, score, action) = self.parse_grade_xml_body(request.body)
    except Exception as e:
        error_message = ('Request    body    XML    parsing    error:    ' + escape(e.message))
        log.debug(('[LTI]:    ' + error_message))
        failure_values['imsx_description'] = error_message
        return Response(response_xml_template.format(**failure_values), content_type='application/xml')
    try:
        self.verify_oauth_body_sign(request)
    except (ValueError, LTIError) as e:
        failure_values['imsx_messageIdentifier'] = escape(imsx_messageIdentifier)
        error_message = ('OAuth    verification    error:    ' + escape(e.message))
        failure_values['imsx_description'] = error_message
        log.debug(('[LTI]:    ' + error_message))
        return Response(response_xml_template.format(**failure_values), content_type='application/xml')
    real_user = self.system.get_real_user(urllib.unquote(sourcedId.split(':')[(-1)]))
    if (not real_user):
        failure_values['imsx_messageIdentifier'] = escape(imsx_messageIdentifier)
        failure_values['imsx_description'] = 'User    not    found.'
        return Response(response_xml_template.format(**failure_values), content_type='application/xml')
    if (action == 'replaceResultRequest'):
        self.set_user_module_score(real_user, score, self.max_score())
        values = {'imsx_codeMajor': 'success', 'imsx_description': 'Score    for    {sourced_id}    is    now    {score}'.format(sourced_id=sourcedId, score=score), 'imsx_messageIdentifier': escape(imsx_messageIdentifier), 'response': '<replaceResultResponse/>'}
        log.debug('[LTI]:    Grade    is    saved.')
        return Response(response_xml_template.format(**values), content_type='application/xml')
    unsupported_values['imsx_messageIdentifier'] = escape(imsx_messageIdentifier)
    log.debug('[LTI]:    Incorrect    action.')
    return Response(response_xml_template.format(**unsupported_values), content_type='application/xml')
