def test(element_html, data):
    element = lxml.html.fragment_fromstring(element_html)
    name = pl.get_string_attrib(element, 'answers-name')
    weight = pl.get_integer_attrib(element, 'weight', 1)

    result = random.choices(['correct', 'incorrect', 'invalid'], [5, 5, 1])[0]
    if result == 'correct':
        data['raw_submitted_answers'][name] = str(pl.from_json(data['correct_answers'][name]))
        data['partial_scores'][name] = {'score': 1, 'weight': weight}
    elif result == 'incorrect':
        data['raw_submitted_answers'][name] = str(pl.from_json(data['correct_answers'][name])) + ' + {:d}'.format(random.randint(1, 100))
        data['partial_scores'][name] = {'score': 0, 'weight': weight}
    elif result == 'invalid':
        invalid_type = random.choice(['float', 'complex', 'expression', 'function', 'variable', 'syntax', 'escape', 'comment'])
        if invalid_type == 'float':
            data['raw_submitted_answers'][name] = 'x + 1.234'
            s = 'Your answer contains the floating-point number ' + str(1.234) + '. '
            s += 'All numbers must be expressed as integers (or ratios of integers). '
            s += '<br><br><pre>' + phs.point_to_error('x + 1.234', 4) + '</pre>'
            data['format_errors'][name] = s
        elif invalid_type == 'complex':
            data['raw_submitted_answers'][name] = 'x + (1+2j)'
            s = 'Your answer contains the complex number ' + str(2j) + '. '
            s += 'All numbers must be expressed as integers (or ratios of integers). '
            s += '<br><br><pre>' + phs.point_to_error('x + (1+2j)', 7) + '</pre>'
            data['format_errors'][name] = s
        elif invalid_type == 'expression':
            data['raw_submitted_answers'][name] = '1 and 0'
            s = 'Your answer has an invalid expression. '
            s += '<br><br><pre>' + phs.point_to_error('1 and 0', 0) + '</pre>'
            data['format_errors'][name] = s
        elif invalid_type == 'function':
            data['raw_submitted_answers'][name] = 'atan(x)'
            s = 'Your answer calls an invalid function "' + 'atan' + '". '
            s += '<br><br><pre>' + phs.point_to_error('atan(x)', 0) + '</pre>'
            data['format_errors'][name] = s
        elif invalid_type == 'variable':
            data['raw_submitted_answers'][name] = 'x + y'
            s = 'Your answer refers to an invalid variable "' + 'y' + '". '
            s += '<br><br><pre>' + phs.point_to_error('x + y', 4) + '</pre>'
            data['format_errors'][name] = s
        elif invalid_type == 'syntax':
            data['raw_submitted_answers'][name] = 'x +* 1'
            s = 'Your answer has a syntax error. '
            s += '<br><br><pre>' + phs.point_to_error('x +* 1', 4) + '</pre>'
            data['format_errors'][name] = s
        elif invalid_type == 'escape':
            data['raw_submitted_answers'][name] = 'x + 1\\n'
            s = 'Your answer must not contain the character "\\". '
            s += '<br><br><pre>' + phs.point_to_error('x + 1\\n', 5) + '</pre>'
            data['format_errors'][name] = s
        elif invalid_type == 'comment':
            data['raw_submitted_answers'][name] = 'x # some text'
            s = 'Your answer must not contain the character "#". '
            s += '<br><br><pre>' + phs.point_to_error('x # some text', 2) + '</pre>'
            data['format_errors'][name] = s
        else:
            raise Exception('invalid invalid_type: %s' % invalid_type)
    else:
        raise Exception('invalid result: %s' % result)
def test(element_html, data):
    element = lxml.html.fragment_fromstring(element_html)
    name = pl.get_string_attrib(element, 'answers-name')
    weight = pl.get_integer_attrib(element, 'weight', 1)

    result = random.choices(['correct', 'incorrect', 'invalid'], [5, 5, 1])[0]
    if result == 'correct':
        data['raw_submitted_answers'][name] = str(pl.from_json(data['correct_answers'][name]))
        data['partial_scores'][name] = {'score': 1, 'weight': weight}
    elif result == 'incorrect':
        data['raw_submitted_answers'][name] = str(pl.from_json(data['correct_answers'][name])) + ' + {:d}'.format(random.randint(1, 100))
        data['partial_scores'][name] = {'score': 0, 'weight': weight}
    elif result == 'invalid':
        invalid_type = random.choice(['float', 'complex', 'expression', 'function', 'variable', 'syntax', 'escape', 'comment'])
        if invalid_type == 'float':
            data['raw_submitted_answers'][name] = 'x + 1.234'
            s = 'Your answer contains the floating-point number ' + str(1.234) + '. '
            s += 'All numbers must be expressed as integers (or ratios of integers). '
            s += '<br><br><pre>' + phs.point_to_error('x + 1.234', 4) + '</pre>'
            data['format_errors'][name] = s
        elif invalid_type == 'complex':
            data['raw_submitted_answers'][name] = 'x + (1+2j)'
            s = 'Your answer contains the complex number ' + str(2j) + '. '
            s += 'All numbers must be expressed as integers (or ratios of integers). '
            s += '<br><br><pre>' + phs.point_to_error('x + (1+2j)', 7) + '</pre>'
            data['format_errors'][name] = s
        elif invalid_type == 'expression':
            data['raw_submitted_answers'][name] = '1 and 0'
            s = 'Your answer has an invalid expression. '
            s += '<br><br><pre>' + phs.point_to_error('1 and 0', 0) + '</pre>'
            data['format_errors'][name] = s
        elif invalid_type == 'function':
            data['raw_submitted_answers'][name] = 'atan(x)'
            s = 'Your answer calls an invalid function "' + 'atan' + '". '
            s += '<br><br><pre>' + phs.point_to_error('atan(x)', 0) + '</pre>'
            data['format_errors'][name] = s
        elif invalid_type == 'variable':
            data['raw_submitted_answers'][name] = 'x + y'
            s = 'Your answer refers to an invalid variable "' + 'y' + '". '
            s += '<br><br><pre>' + phs.point_to_error('x + y', 4) + '</pre>'
            data['format_errors'][name] = s
        elif invalid_type == 'syntax':
            data['raw_submitted_answers'][name] = 'x +* 1'
            s = 'Your answer has a syntax error. '
            s += '<br><br><pre>' + phs.point_to_error('x +* 1', 4) + '</pre>'
            data['format_errors'][name] = s
        elif invalid_type == 'escape':
            data['raw_submitted_answers'][name] = 'x + 1\\n'
            s = 'Your answer must not contain the character "\\". '
            s += '<br><br><pre>' + phs.point_to_error('x + 1\\n', 5) + '</pre>'
            data['format_errors'][name] = s
        elif invalid_type == 'comment':
            data['raw_submitted_answers'][name] = 'x # some text'
            s = 'Your answer must not contain the character "#". '
            s += '<br><br><pre>' + phs.point_to_error('x # some text', 2) + '</pre>'
            data['format_errors'][name] = s
        else:
            raise Exception('invalid invalid_type: %s' % invalid_type)
    else:
        raise Exception('invalid result: %s' % result)
def parse(element_html, data):
    element = lxml.html.fragment_fromstring(element_html)
    name = pl.get_string_attrib(element, 'answers-name')
    variables = get_variables_list(pl.get_string_attrib(element, 'variables', None))
    allow_complex = pl.get_boolean_attrib(element, 'allow-complex', False)
    imaginary_unit = pl.get_string_attrib(element, 'imaginary-unit-for-display', 'i')

    # Get submitted answer or return parse_error if it does not exist
    a_sub = data['submitted_answers'].get(name, None)
    if not a_sub:
        data['format_errors'][name] = 'No submitted answer.'
        data['submitted_answers'][name] = None
        return

    # Parse the submitted answer and put the result in a string
    try:
        # Replace '^' with '**' wherever it appears. In MATLAB, either can be used
        # for exponentiation. In python, only the latter can be used.
        a_sub = a_sub.replace('^', '**')

        # Strip whitespace
        a_sub = a_sub.strip()

        # Convert safely to sympy
        a_sub_parsed = phs.convert_string_to_sympy(a_sub, variables, allow_complex=allow_complex)

        # If complex numbers are not allowed, raise error if expression has the imaginary unit
        if (not allow_complex) and (a_sub_parsed.has(sympy.I)):
            a_sub_parsed = a_sub_parsed.subs(sympy.I, sympy.Symbol(imaginary_unit))
            s = 'Your answer was simplified to this, which contains a complex number (denoted ${:s}$): $${:s}$$'.format(imaginary_unit, sympy.latex(a_sub_parsed))
            data['format_errors'][name] = s
            data['submitted_answers'][name] = None
            return

        # Store result as json.
        a_sub_json = phs.sympy_to_json(a_sub_parsed, allow_complex=allow_complex)
    except phs.HasFloatError as err:
        s = 'Your answer contains the floating-point number ' + str(err.n) + '. '
        s += 'All numbers must be expressed as integers (or ratios of integers). '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except phs.HasComplexError as err:
        s = 'Your answer contains the complex number ' + str(err.n) + '. '
        s += 'All numbers must be expressed as integers (or ratios of integers). '
        if allow_complex:
            s += 'To include a complex number in your expression, write it as the product of an integer with the imaginary unit <code>i</code> or <code>j</code>. '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except phs.HasInvalidExpressionError as err:
        s = 'Your answer has an invalid expression. '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except phs.HasInvalidFunctionError as err:
        s = 'Your answer calls an invalid function "' + err.text + '". '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except phs.HasInvalidVariableError as err:
        s = 'Your answer refers to an invalid variable "' + err.text + '". '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except phs.HasParseError as err:
        s = 'Your answer has a syntax error. '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except phs.HasEscapeError as err:
        s = 'Your answer must not contain the character "\\". '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except phs.HasCommentError as err:
        s = 'Your answer must not contain the character "#". '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except Exception as err:
        data['format_errors'][name] = 'Invalid format.'
        data['submitted_answers'][name] = None
        return

    # Make sure we can parse the json again
    try:
        # Convert safely to sympy
        phs.json_to_sympy(a_sub_json, allow_complex=allow_complex)

        # Finally, store the result
        data['submitted_answers'][name] = a_sub_json
    except Exception as err:
        s = 'Your answer was simplified to this, which contains an invalid expression: $${:s}$$'.format(sympy.latex(a_sub_parsed))
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
def parse(element_html, data):
    element = lxml.html.fragment_fromstring(element_html)
    name = pl.get_string_attrib(element, 'answers-name')
    variables = get_variables_list(pl.get_string_attrib(element, 'variables', None))
    allow_complex = pl.get_boolean_attrib(element, 'allow-complex', False)
    imaginary_unit = pl.get_string_attrib(element, 'imaginary-unit-for-display', 'i')

    # Get submitted answer or return parse_error if it does not exist
    a_sub = data['submitted_answers'].get(name, None)
    if not a_sub:
        data['format_errors'][name] = 'No submitted answer.'
        data['submitted_answers'][name] = None
        return

    # Parse the submitted answer and put the result in a string
    try:
        # Replace '^' with '**' wherever it appears. In MATLAB, either can be used
        # for exponentiation. In python, only the latter can be used.
        a_sub = a_sub.replace('^', '**')

        # Strip whitespace
        a_sub = a_sub.strip()

        # Convert safely to sympy
        a_sub_parsed = phs.convert_string_to_sympy(a_sub, variables, allow_complex=allow_complex)

        # If complex numbers are not allowed, raise error if expression has the imaginary unit
        if (not allow_complex) and (a_sub_parsed.has(sympy.I)):
            a_sub_parsed = a_sub_parsed.subs(sympy.I, sympy.Symbol(imaginary_unit))
            s = 'Your answer was simplified to this, which contains a complex number (denoted ${:s}$): $${:s}$$'.format(imaginary_unit, sympy.latex(a_sub_parsed))
            data['format_errors'][name] = s
            data['submitted_answers'][name] = None
            return

        # Store result as json.
        a_sub_json = phs.sympy_to_json(a_sub_parsed, allow_complex=allow_complex)
    except phs.HasFloatError as err:
        s = 'Your answer contains the floating-point number ' + str(err.n) + '. '
        s += 'All numbers must be expressed as integers (or ratios of integers). '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except phs.HasComplexError as err:
        s = 'Your answer contains the complex number ' + str(err.n) + '. '
        s += 'All numbers must be expressed as integers (or ratios of integers). '
        if allow_complex:
            s += 'To include a complex number in your expression, write it as the product of an integer with the imaginary unit <code>i</code> or <code>j</code>. '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except phs.HasInvalidExpressionError as err:
        s = 'Your answer has an invalid expression. '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except phs.HasInvalidFunctionError as err:
        s = 'Your answer calls an invalid function "' + err.text + '". '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except phs.HasInvalidVariableError as err:
        s = 'Your answer refers to an invalid variable "' + err.text + '". '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except phs.HasParseError as err:
        s = 'Your answer has a syntax error. '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except phs.HasEscapeError as err:
        s = 'Your answer must not contain the character "\\". '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except phs.HasCommentError as err:
        s = 'Your answer must not contain the character "#". '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
        return
    except Exception:
        data['format_errors'][name] = 'Invalid format.'
        data['submitted_answers'][name] = None
        return

    # Make sure we can parse the json again
    try:
        # Convert safely to sympy
        phs.json_to_sympy(a_sub_json, allow_complex=allow_complex)

        # Finally, store the result
        data['submitted_answers'][name] = a_sub_json
    except Exception:
        s = 'Your answer was simplified to this, which contains an invalid expression: $${:s}$$'.format(sympy.latex(a_sub_parsed))
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
def parse(element_html, element_index, data):
    element = lxml.html.fragment_fromstring(element_html)
    name = pl.get_string_attrib(element, 'answers_name')
    variables = get_variables_list(
        pl.get_string_attrib(element, 'variables', None))

    # Get submitted answer or return parse_error if it does not exist
    a_sub = data['submitted_answers'].get(name, None)
    if not a_sub:
        data['format_errors'][name] = 'No submitted answer.'
        data['submitted_answers'][name] = None
        return

    try:
        # Replace '^' with '**' wherever it appears. In MATLAB, either can be used
        # for exponentiation. In python, only the latter can be used.
        a_sub = a_sub.replace('^', '**')

        # Strip whitespace
        a_sub = a_sub.strip()

        # Convert safely to sympy
        a_sub = phs.convert_string_to_sympy(a_sub, variables)

        # Store result as a string.
        data['submitted_answers'][name] = str(a_sub)
    except phs.HasFloatError as err:
        s = 'Your answer contains the floating-point number ' + str(
            err.n) + '. '
        s += 'All numbers must be expressed as integers (or ratios of integers). '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
    except phs.HasComplexError as err:
        s = 'Your answer contains the complex number ' + str(err.n) + '. '
        s += 'All numbers must be expressed as integers (or ratios of integers). '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
    except phs.HasInvalidExpressionError as err:
        s = 'Your answer has an invalid expression. '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
    except phs.HasInvalidFunctionError as err:
        s = 'Your answer calls an invalid function "' + err.text + '". '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
    except phs.HasInvalidVariableError as err:
        s = 'Your answer refers to an invalid variable "' + err.text + '". '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
    except phs.HasParseError as err:
        s = 'Your answer has a syntax error. '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
    except phs.HasEscapeError as err:
        s = 'Your answer must not contain the character "\\". '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
    except phs.HasCommentError as err:
        s = 'Your answer must not contain the character "#". '
        s += '<br><br><pre>' + phs.point_to_error(a_sub, err.offset) + '</pre>'
        data['format_errors'][name] = s
        data['submitted_answers'][name] = None
    except Exception as err:
        data['format_errors'][name] = 'Invalid format.'
        data['submitted_answers'][name] = None