'Helper method for verifying LTI 2.0 JSON object contained in the body of the request.
The json_str must be loadable.  It can either be an dict (object) or an array whose first element is an dict,
in which case that first dict is considered.
The dict must have the "@type" key with value equal to "Result",
"resultScore" key with value equal to a number [0, 1],
The "@context" key must be present, but we don\'t do anything with it.  And the "comment" key may be
present, in which case it must be a string.
Arguments:
json_str (unicode):  The body of the LTI 2.0 results service request, which is a JSON string]
Returns:
(float, str):  (score, [optional]comment) if verification checks out
Raises:
LTIError (with message) if verification fails'

def parse_lti_2_0_result_json(self, json_str):

    try:
        json_obj = json.loads(json_str)
    except (ValueError, TypeError):
        msg = 'Supplied    JSON    string    in    request    body    could    not    be    decoded:    {}'.format(json_str)
        log.info('[LTI]    {}'.format(msg))
        raise LTIError(msg)
    if (not isinstance(json_obj, dict)):
        if (isinstance(json_obj, list) and (len(json_obj) >= 1) and isinstance(json_obj[0], dict)):
            json_obj = json_obj[0]
        else:
            msg = 'Supplied    JSON    string    is    a    list    that    does    not    contain    an    object    as    the    first    element.    {}'.format(json_str)
            log.info('[LTI]    {}'.format(msg))
            raise LTIError(msg)
    result_type = json_obj.get('@type')
    if (result_type != 'Result'):
        msg = "JSON    object    does    not    contain    correct    @type    attribute    (should    be    'Result',    is    {})".format(result_type)
        log.info('[LTI]    {}'.format(msg))
        raise LTIError(msg)
    REQUIRED_KEYS = ['@context']
    for key in REQUIRED_KEYS:
        if (key not in json_obj):
            msg = 'JSON    object    does    not    contain    required    key    {}'.format(key)
            log.info('[LTI]    {}'.format(msg))
            raise LTIError(msg)
    if ('resultScore' not in json_obj):
        return (None, json_obj.get('comment', ''))
    try:
        score = float(json_obj.get('resultScore', 'unconvertable'))
        if (not (0 <= score <= 1)):
            msg = 'score    value    outside    the    permitted    range    of    0-1.'
            log.info('[LTI]    {}'.format(msg))
            raise LTIError(msg)
    except (TypeError, ValueError) as err:
        msg = 'Could    not    convert    resultScore    to    float:    {}'.format(err.message)
        log.info('[LTI]    {}'.format(msg))
        raise LTIError(msg)
    return (score, json_obj.get('comment', ''))
