'Parses XML from request.body and returns parsed data
XML body should contain nsmap with namespace, that is specified in LTI specs.
Returns tuple: imsx_messageIdentifier, sourcedId, score, action
Raises Exception if can\'t parse.'

@classmethod
def parse_grade_xml_body(cls, body):

    lti_spec_namespace = 'http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0'
    namespaces = {'def': lti_spec_namespace}
    data = body.strip().encode('utf-8')
    parser = etree.XMLParser(ns_clean=True, recover=True, encoding='utf-8')
    root = etree.fromstring(data, parser=parser)
    imsx_messageIdentifier = (root.xpath('//def:imsx_messageIdentifier', namespaces=namespaces)[0].text or '')
    sourcedId = root.xpath('//def:sourcedId', namespaces=namespaces)[0].text
    score = root.xpath('//def:textString', namespaces=namespaces)[0].text
    action = root.xpath('//def:imsx_POXBody', namespaces=namespaces)[0].getchildren()[0].tag.replace((('{' + lti_spec_namespace) + '}'), '')
    score = float(score)
    if (not (0 <= score <= 1)):
        raise LTIError('score    value    outside    the    permitted    range    of    0-1.')
    return (imsx_messageIdentifier, sourcedId, score, action)
