def render(element_html, element_index, data):
    element = lxml.html.fragment_fromstring(element_html)
    digits = pl.get_integer_attrib(element, 'digits', 2)

    matlab_data = ''
    python_data = 'import numpy as np\n\n'
    for child in element:
        if child.tag == 'variable':
            # Raise exception of variable does not have a name
            pl.check_attribs(child, required_attribs=['params_name'], optional_attribs=[])

            # Get name of variable
            var_name = pl.get_string_attrib(child, 'params_name')

            # Get value of variable, raising exception if variable does not exist
            var_data = data['params'].get(var_name, None)
            if var_data is None:
                raise Exception('No value in data["params"] for variable %s in pl_matrix_output element' % var_name)

            # If the variable is in a format generated by pl.to_json, convert it
            # back to a standard type (otherwise, do nothing)
            var_data = pl.from_json(var_data)

            if np.isscalar(var_data):
                prefix = ''
                suffix = ''
            else:
                # Wrap the variable in an ndarray (if it's already one, this does nothing)
                var_data = np.array(var_data)
                # Check shape of variable
                if var_data.ndim != 2:
                    raise Exception('Value in data["params"] for variable %s in pl_matrix_output element must be a scalar or a 2D array' % var_name)
                # Create prefix/suffix so python string is np.array( ... )
                prefix = 'np.array('
                suffix = ')'

            # Create string for matlab and python format
            matlab_data += pl.inner_html(child) + ' = ' + pl.string_from_2darray(var_data, language='matlab', digits=digits) + ';\n'
            python_data += pl.inner_html(child) + ' = ' + prefix + pl.string_from_2darray(var_data, language='python', digits=digits) + suffix + '\n'

    html_params = {
        'default_is_matlab': True,
        'matlab_data': matlab_data,
        'python_data': python_data,
        'element_index': element_index,
        'uuid': pl.get_uuid()
    }

    with open('pl_matrix_output.mustache', 'r', encoding='utf-8') as f:
        html = chevron.render(f, html_params).strip()

    return html
예제 #2
0
def parse(element_html, data):
    element = lxml.html.fragment_fromstring(element_html)
    answers_name = pl.get_string_attrib(element, 'answers-name')
    answer = data['submitted_answers'].get(answers_name, None)

    # Blank option should be available, but cause format error when submitted.
    valid_options = [' ']

    for child in element:
        if child.tag in ['pl-answer']:
            pl.check_attribs(child,
                             required_attribs=[],
                             optional_attribs=['correct'])
            child_html = pl.inner_html(child).strip()
            valid_options.append(child_html)

    if answer is BLANK_ANSWER:
        data['format_errors'][
            answers_name] = 'The submitted answer was left blank.'

    if answer is None:
        data['format_errors'][answers_name] = 'No answer was submitted.'

    if answer not in valid_options:
        data['format_errors'][answers_name] = 'Invalid option submitted.'
예제 #3
0
def render(element_html, data):
    element = lxml.html.fragment_fromstring(element_html)
    language = pl.get_string_attrib(element, 'language', LANGUAGE_DEFAULT)
    no_highlight = pl.get_boolean_attrib(element, 'no-highlight',
                                         NO_HIGHLIGHT_DEFAULT)
    specify_language = (language is not None) and (not no_highlight)
    source_file_name = pl.get_string_attrib(element, 'source-file-name',
                                            SOURCE_FILE_NAME_DEFAULT)
    prevent_select = pl.get_boolean_attrib(element, 'prevent-select',
                                           PREVENT_SELECT_DEFAULT)
    highlight_lines = pl.get_string_attrib(element, 'highlight-lines',
                                           HIGHLIGHT_LINES_DEFAULT)
    highlight_lines_color = pl.get_string_attrib(
        element, 'highlight-lines-color', HIGHLIGHT_LINES_COLOR_DEFAULT)

    if source_file_name is not None:
        base_path = data['options']['question_path']
        file_path = os.path.join(base_path, source_file_name)
        if not os.path.exists(file_path):
            raise Exception(f'Unknown file path: "{file_path}".')
        f = open(file_path, 'r')
        code = ''
        for line in f.readlines():
            code += line
        code = code[:-1]
        f.close()
        # Automatically escape code in file source (important for: html/xml).
        code = escape(code)
    else:
        # Strip a single leading newline from the code, if present. This
        # avoids having spurious newlines because of HTML like:
        #
        # <pl-code>
        # some_code
        # </pl-code>
        #
        # which technically starts with a newline, but we probably
        # don't want a blank line at the start of the code block.
        code = pl.inner_html(element)
        if len(code) > 1 and code[0] == '\r' and code[1] == '\n':
            code = code[2:]
        elif len(code) > 0 and (code[0] == '\n' or code[0] == '\r'):
            code = code[1:]

    if highlight_lines is not None:
        code = highlight_lines_in_code(code, highlight_lines,
                                       highlight_lines_color)

    html_params = {
        'specify_language': specify_language,
        'language': language,
        'no_highlight': no_highlight,
        'code': code,
        'prevent_select': prevent_select,
    }

    with open('pl-code.mustache', 'r', encoding='utf-8') as f:
        html = chevron.render(f, html_params).strip()

    return html
예제 #4
0
def render(element_html, element_index, data):
    element = lxml.html.fragment_fromstring(element_html)
    language = pl.get_string_attrib(element, 'language', None)
    no_highlight = pl.get_boolean_attrib(element, 'no-highlight', False)
    specify_language = (language is not None) and (not no_highlight)

    # Strip a single leading newline from the code, if present. This
    # avoids having spurious newlines because of HTML like:
    #
    # <pl-code>
    # some_code
    # </pl-code>
    #
    # which technically starts with a newline, but we probably
    # don't want a blank line at the start of the code block.
    code = pl.inner_html(element)
    if len(code) > 1 and code[0] == '\r' and code[1] == '\n':
        code = code[2:]
    elif len(code) > 0 and (code[0] == '\n' or code[0] == '\r'):
        code = code[1:]

    html_params = {
        'specify_language': specify_language,
        'language': language,
        'no_highlight': no_highlight,
        'code': code,
    }

    with open('pl-code.mustache', 'r', encoding='utf-8') as f:
        html = chevron.render(f, html_params).strip()

    return html
예제 #5
0
def get_options(element, data):
    options = []
    for child in element:
        if child.tag in ['pl-answer']:
            pl.check_attribs(child, required_attribs=[], optional_attribs=['correct'])
            child_html = pl.inner_html(child).strip()
            options.append(child_html)
    return options
예제 #6
0
def categorize_matches(element, data):
    """Get provided statements and options from the pl-matching element"""
    options = {}
    statements = []
    index = 0

    # Sort the elements so that pl-options come first.
    children = element[:]
    children.sort(key=lambda child: child.tag)

    def make_option(name, html):
        nonlocal index
        option = {'index': index, 'name': name, 'html': html}
        index += 1
        return option

    for child in children:
        if child.tag in ['pl-option', 'pl_option']:
            pl.check_attribs(child,
                             required_attribs=[],
                             optional_attribs=['name'])
            child_html = pl.inner_html(child)
            option_name = pl.get_string_attrib(child, 'name', child_html)

            # An option object has: index of appearance in the pl-matching element;
            # the name attribute; and the html content.
            option = make_option(option_name, child_html)
            options[option_name] = option

        elif child.tag in ['pl-statement', 'pl_statement']:
            pl.check_attribs(child,
                             required_attribs=['match'],
                             optional_attribs=[])
            child_html = pl.inner_html(child)
            match_name = pl.get_string_attrib(child, 'match')
            if match_name not in options:
                new_option = make_option(match_name, match_name)
                options[match_name] = new_option

            # A statement object has: the name attribute of the correct matching option; and
            # the html content.
            statement = {'match': match_name, 'html': child_html}
            statements.append(statement)

    return list(options.values()), statements
    def prepare_tag(html_tags, index, group=None):
        if html_tags.tag != 'pl-answer':
            raise Exception(
                'Any html tags nested inside <pl-order-blocks> must be <pl-answer> or <pl-block-group>. \
                Any html tags nested inside <pl-block-group> must be <pl-answer>'
            )

        if grading_method == 'external':
            pl.check_attribs(html_tags,
                             required_attribs=[],
                             optional_attribs=['correct'])
        elif grading_method == 'unordered':
            pl.check_attribs(html_tags,
                             required_attribs=[],
                             optional_attribs=['correct', 'indent'])
        elif grading_method in ['ranking', 'ordered']:
            pl.check_attribs(html_tags,
                             required_attribs=[],
                             optional_attribs=['correct', 'ranking', 'indent'])
        elif grading_method == 'dag':
            pl.check_attribs(html_tags,
                             required_attribs=[],
                             optional_attribs=['correct', 'tag', 'depends'])

        is_correct = pl.get_boolean_attrib(html_tags, 'correct',
                                           PL_ANSWER_CORRECT_DEFAULT)
        answer_indent = pl.get_integer_attrib(html_tags, 'indent', None)
        inner_html = pl.inner_html(html_tags)
        ranking = pl.get_integer_attrib(html_tags, 'ranking', -1)

        tag = pl.get_string_attrib(html_tags, 'tag', None)
        depends = pl.get_string_attrib(html_tags, 'depends', '')
        depends = depends.strip().split(',') if depends else []

        if check_indentation is False and answer_indent is not None:
            raise Exception(
                '<pl-answer> should not specify indentation if indentation is disabled.'
            )

        answer_data_dict = {
            'inner_html': inner_html,
            'indent': answer_indent,
            'ranking': ranking,
            'index': index,
            'tag': tag,  # only used with DAG grader
            'depends': depends,  # only used with DAG grader
            'group': group  # only used with DAG grader
        }
        if is_correct:
            correct_answers.append(answer_data_dict)
        else:
            incorrect_answers.append(answer_data_dict)
예제 #8
0
def get_options(element, data):
    answers_name = pl.get_string_attrib(element, 'answers-name')
    submitted_answer = data.get('submitted_answers', {}).get(answers_name, None)
    options = []
    for child in element:
        if child.tag in ['pl-answer']:
            pl.check_attribs(child, required_attribs=[], optional_attribs=['correct'])
            child_html = pl.inner_html(child).strip()
            options.append({
                'value': child_html,
                'selected': (child_html == submitted_answer)
            })
    return options
예제 #9
0
def render(element_html, data):
    element = lxml.html.fragment_fromstring(element_html)
    hide_in_question = pl.get_boolean_attrib(element, 'question',
                                             QUESTION_DEFAULT)
    hide_in_submission = pl.get_boolean_attrib(element, 'submission',
                                               SUBMISSION_DEFAULT)
    hide_in_answer = pl.get_boolean_attrib(element, 'answer', ANSWER_DEFAULT)
    if (data['panel'] == 'question' and not hide_in_question) \
            or (data['panel'] == 'submission' and not hide_in_submission) \
            or (data['panel'] == 'answer' and not hide_in_answer):
        element = lxml.html.fragment_fromstring(element_html)
        return pl.inner_html(element)
    else:
        return ''
예제 #10
0
def get_solution(element, data):
    solution = []
    for child in element:
        if child.tag in ['pl-answer']:
            pl.check_attribs(child, required_attribs=[], optional_attribs=['correct'])
            is_correct = pl.get_boolean_attrib(child, 'correct', False)
            child_html = pl.inner_html(child).strip()
            if is_correct:
                solution.append(child_html)

    if len(solution) > 1:
        raise Exception('Multiple correct answers were set')

    return solution[0]
예제 #11
0
def render(element_html, data):
    element = lxml.html.fragment_fromstring(element_html)
    language = pl.get_string_attrib(element, 'language', None)
    no_highlight = pl.get_boolean_attrib(element, 'no-highlight', False)
    specify_language = (language is not None) and (not no_highlight)
    source_file_name = pl.get_string_attrib(element, 'source-file-name', None)
    prevent_select = pl.get_boolean_attrib(element, 'prevent-select', False)

    if source_file_name is not None:
        base_path = data['options']['question_path']
        file_path = os.path.join(base_path, source_file_name)
        if not os.path.exists(file_path):
            raise Exception(f'Unknown file path: "{file_path}".')
        f = open(file_path, 'r')
        code = ''
        for line in f.readlines():
            code += line
        code = code[:-1]
        f.close()
    else:
        # Strip a single leading newline from the code, if present. This
        # avoids having spurious newlines because of HTML like:
        #
        # <pl-code>
        # some_code
        # </pl-code>
        #
        # which technically starts with a newline, but we probably
        # don't want a blank line at the start of the code block.
        code = pl.inner_html(element)
        if len(code) > 1 and code[0] == '\r' and code[1] == '\n':
            code = code[2:]
        elif len(code) > 0 and (code[0] == '\n' or code[0] == '\r'):
            code = code[1:]

    html_params = {
        'specify_language': specify_language,
        'language': language,
        'no_highlight': no_highlight,
        'code': code,
        'prevent_select': prevent_select,
    }

    with open('pl-code.mustache', 'r', encoding='utf-8') as f:
        html = chevron.render(f, html_params).strip()

    return html
예제 #12
0
def categorize_options(element, data):
    """Get provided correct and incorrect answers"""
    correct_answers = []
    incorrect_answers = []
    index = 0
    for child in element:
        if child.tag in ['pl-answer', 'pl_answer']:
            pl.check_attribs(child,
                             required_attribs=[],
                             optional_attribs=['correct'])
            correct = pl.get_boolean_attrib(child, 'correct', False)
            child_html = pl.inner_html(child)
            answer_tuple = (index, correct, child_html)
            if correct:
                correct_answers.append(answer_tuple)
            else:
                incorrect_answers.append(answer_tuple)
            index += 1

    file_path = pl.get_string_attrib(element, 'external-json',
                                     EXTERNAL_JSON_DEFAULT)
    if file_path is not EXTERNAL_JSON_DEFAULT:
        correct_attrib = pl.get_string_attrib(
            element, 'external-json-correct-key',
            EXTERNAL_JSON_CORRECT_KEY_DEFAULT)
        incorrect_attrib = pl.get_string_attrib(
            element, 'external-json-incorrect-key',
            EXTERNAL_JSON_INCORRECT_KEY_DEFAULT)
        if pathlib.PurePath(file_path).is_absolute():
            json_file = file_path
        else:
            json_file = pathlib.PurePath(
                data['options']['question_path']).joinpath(file_path)
        try:
            with open(json_file, mode='r', encoding='utf-8') as f:
                obj = json.load(f)
                for text in obj.get(correct_attrib, []):
                    correct_answers.append((index, True, text))
                    index += 1
                for text in obj.get(incorrect_attrib, []):
                    incorrect_answers.append((index, False, text))
                    index += 1
        except FileNotFoundError:
            raise Exception(
                f'JSON answer file: "{json_file}" could not be found')
    return correct_answers, incorrect_answers
예제 #13
0
def categorize_options(element):
    """Get provided correct and incorrect answers"""
    correct_answers = []
    incorrect_answers = []
    index = 0
    for child in element:
        if child.tag in ['pl-answer', 'pl_answer']:
            pl.check_attribs(child, required_attribs=[], optional_attribs=['correct'])
            correct = pl.get_boolean_attrib(child, 'correct', False)
            child_html = pl.inner_html(child)
            answer_tuple = (index, correct, child_html)
            if correct:
                correct_answers.append(answer_tuple)
            else:
                incorrect_answers.append(answer_tuple)
            index += 1
    return correct_answers, incorrect_answers
예제 #14
0
def render(element_html, element_index, data):
    element = lxml.html.fragment_fromstring(element_html)
    digits = pl.get_integer_attrib(element, 'digits', 2)

    html = '<pre>\n% Data in MATLAB format\n'
    for child in element:
        if child.tag == 'variable':
            pl.check_attribs(child,
                             required_attribs=['params_name'],
                             optional_attribs=[])
            var_name = pl.get_string_attrib(child, 'params_name')
            var_data = data['params'].get(var_name, None)
            if var_data is None:
                raise Exception(
                    'No value in data["params"] for variable %s in matrix_output element'
                    % var_name)
            html += pl.inner_html(child) \
                + ' = ' \
                + pl.numpy_to_matlab((var_data if np.isscalar(var_data) else np.array(var_data)), ndigits=digits) + ';' \
                + '\n'
    html += '</pre>'
    return html
예제 #15
0
def get_objects(element, data):
    obj_list = []

    for child in element:
        is_stl = (child.tag in ['pl-threejs-stl', 'pl_threejs_stl'])
        is_txt = (child.tag in ['pl-threejs-txt', 'pl_threejs_txt'])
        if not (is_stl or is_txt):
            continue

        # Type-specific check and get (stl)
        if is_stl:
            # Attributes
            pl.check_attribs(child,
                             required_attribs=['file-name'],
                             optional_attribs=[
                                 'file-directory', 'frame', 'color',
                                 'position', 'orientation', 'format', 'scale',
                                 'opacity'
                             ])
            # - file-name (and file-directory)
            file_url = get_file_url(child, data)
            # - type
            object_type = 'stl'
            # - object
            specific = {'type': object_type, 'file_url': file_url}

        # Type-specific check and get (txt)
        if is_txt:
            # Attributes
            pl.check_attribs(child,
                             required_attribs=[],
                             optional_attribs=[
                                 'frame', 'color', 'position', 'orientation',
                                 'format', 'scale', 'opacity'
                             ])
            # - text
            text = pl.inner_html(child)
            # - type
            object_type = 'txt'
            # - object
            specific = {'type': object_type, 'text': text}

        # Common
        # - frame
        frame = pl.get_string_attrib(child, 'frame', 'body')
        if frame not in ['body', 'space']:
            raise Exception(
                '"frame" must be either "body" or "space": {:s}'.format(frame))
        if frame == 'body':
            default_color = '#e84a27'
            default_opacity = 0.7
        else:
            default_color = '#13294b'
            default_opacity = 0.4
        # - color
        color = pl.get_color_attrib(child, 'color', default_color)
        # - opacity
        opacity = pl.get_float_attrib(child, 'opacity', default_opacity)
        # - position
        p = pl.get_string_attrib(child, 'position', '[0, 0, 0]')
        try:
            position = np.array(json.loads(p), dtype=np.float64)
            if position.shape == (3, ):
                position = position.tolist()
            else:
                raise ValueError()
        except Exception:
            raise Exception(
                'attribute "position" must have format [x, y, z]: {:s}'.format(
                    p))
        # - orientation (and format)
        orientation = get_orientation(child, 'orientation', 'format')
        # - scale
        scale = pl.get_float_attrib(child, 'scale', 1.0)

        common = {
            'frame': frame,
            'color': color,
            'opacity': opacity,
            'position': position,
            'quaternion': orientation,
            'scale': scale
        }

        obj = {**specific, **common}
        obj_list.append(obj)

    return obj_list
예제 #16
0
def prepare(element_html, data):
    element = lxml.html.fragment_fromstring(element_html)

    required_attribs = ['answers-name']
    optional_attribs = ['weight', 'number-answers', 'min-correct', 'max-correct', 'fixed-order', 'inline', 'hide-answer-panel', 'hide-help-text', 'detailed-help-text', 'partial-credit', 'partial-credit-method', 'hide-letter-keys', 'hide-score-badge']

    pl.check_attribs(element, required_attribs, optional_attribs)
    name = pl.get_string_attrib(element, 'answers-name')

    partial_credit = pl.get_boolean_attrib(element, 'partial-credit', PARTIAL_CREDIT_DEFAULT)
    partial_credit_method = pl.get_string_attrib(element, 'partial-credit-method', None)
    if not partial_credit and partial_credit_method is not None:
        raise Exception('Cannot specify partial-credit-method if partial-credit is not enabled')

    correct_answers = []
    incorrect_answers = []
    index = 0
    for child in element:
        if child.tag in ['pl-answer', 'pl_answer']:
            pl.check_attribs(child, required_attribs=[], optional_attribs=['correct'])
            correct = pl.get_boolean_attrib(child, 'correct', False)
            child_html = pl.inner_html(child)
            answer_tuple = (index, correct, child_html)
            if correct:
                correct_answers.append(answer_tuple)
            else:
                incorrect_answers.append(answer_tuple)
            index += 1

    len_correct = len(correct_answers)
    len_incorrect = len(incorrect_answers)
    len_total = len_correct + len_incorrect

    if len_correct == 0:
        raise ValueError('At least one option must be true.')

    number_answers = pl.get_integer_attrib(element, 'number-answers', len_total)
    min_correct = pl.get_integer_attrib(element, 'min-correct', 1)
    max_correct = pl.get_integer_attrib(element, 'max-correct', len(correct_answers))

    if min_correct < 1:
        raise ValueError('The attribute min-correct is {:d} but must be at least 1'.format(min_correct))

    # FIXME: why enforce a maximum number of options?
    max_answers = 26  # will not display more than 26 checkbox answers

    number_answers = max(0, min(len_total, min(max_answers, number_answers)))
    min_correct = min(len_correct, min(number_answers, max(0, max(number_answers - len_incorrect, min_correct))))
    max_correct = min(len_correct, min(number_answers, max(min_correct, max_correct)))
    if not (0 <= min_correct <= max_correct <= len_correct):
        raise ValueError('INTERNAL ERROR: correct number: (%d, %d, %d, %d)' % (min_correct, max_correct, len_correct, len_incorrect))
    min_incorrect = number_answers - max_correct
    max_incorrect = number_answers - min_correct
    if not (0 <= min_incorrect <= max_incorrect <= len_incorrect):
        raise ValueError('INTERNAL ERROR: incorrect number: (%d, %d, %d, %d)' % (min_incorrect, max_incorrect, len_incorrect, len_correct))

    number_correct = random.randint(min_correct, max_correct)
    number_incorrect = number_answers - number_correct

    sampled_correct = random.sample(correct_answers, number_correct)
    sampled_incorrect = random.sample(incorrect_answers, number_incorrect)

    sampled_answers = sampled_correct + sampled_incorrect
    random.shuffle(sampled_answers)

    fixed_order = pl.get_boolean_attrib(element, 'fixed-order', FIXED_ORDER_DEFAULT)
    if fixed_order:
        # we can't simply skip the shuffle because we already broke the original
        # order by separating into correct/incorrect lists
        sampled_answers.sort(key=lambda a: a[0])  # sort by stored original index

    display_answers = []
    correct_answer_list = []
    for (i, (index, correct, html)) in enumerate(sampled_answers):
        keyed_answer = {'key': pl.index2key(i), 'html': html}
        display_answers.append(keyed_answer)
        if correct:
            correct_answer_list.append(keyed_answer)

    if name in data['params']:
        raise Exception('duplicate params variable name: %s' % name)
    if name in data['correct_answers']:
        raise Exception('duplicate correct_answers variable name: %s' % name)
    data['params'][name] = display_answers
    data['correct_answers'][name] = correct_answer_list
예제 #17
0
def prepare(element_html, data):
    element = lxml.html.fragment_fromstring(element_html)
    required_attribs = ['answers-name']
    optional_attribs = ['weight', 'number-answers', 'fixed-order', 'inline']
    pl.check_attribs(element, required_attribs, optional_attribs)
    name = pl.get_string_attrib(element, 'answers-name')

    correct_answers = []
    incorrect_answers = []
    index = 0
    for child in element:
        if child.tag in ['pl-answer', 'pl_answer']:
            pl.check_attribs(child, required_attribs=[], optional_attribs=['correct'])
            correct = pl.get_boolean_attrib(child, 'correct', False)
            child_html = pl.inner_html(child)
            answer_tuple = (index, correct, child_html)
            if correct:
                correct_answers.append(answer_tuple)
            else:
                incorrect_answers.append(answer_tuple)
            index += 1

    len_correct = len(correct_answers)
    len_incorrect = len(incorrect_answers)
    len_total = len_correct + len_incorrect

    if len_correct < 1:
        raise Exception('pl-multiple-choice element must have at least one correct answer')

    number_answers = pl.get_integer_attrib(element, 'number-answers', len_total)

    number_answers = max(1, min(1 + len_incorrect, number_answers))
    number_correct = 1
    number_incorrect = number_answers - number_correct
    if not (0 <= number_incorrect <= len_incorrect):
        raise Exception('INTERNAL ERROR: number_incorrect: (%d, %d, %d)' % (number_incorrect, len_incorrect, number_answers))

    sampled_correct = random.sample(correct_answers, number_correct)
    sampled_incorrect = random.sample(incorrect_answers, number_incorrect)

    sampled_answers = sampled_correct + sampled_incorrect
    random.shuffle(sampled_answers)

    fixed_order = pl.get_boolean_attrib(element, 'fixed-order', False)
    if fixed_order:
        # we can't simply skip the shuffle because we already broke the original
        # order by separating into correct/incorrect lists
        sampled_answers.sort(key=lambda a: a[0])  # sort by stored original index

    display_answers = []
    correct_answer = None
    for (i, (index, correct, html)) in enumerate(sampled_answers):
        keyed_answer = {'key': chr(ord('a') + i), 'html': html}
        display_answers.append(keyed_answer)
        if correct:
            correct_answer = keyed_answer

    if name in data['params']:
        raise Exception('duplicate params variable name: %s' % name)
    if name in data['correct_answers']:
        raise Exception('duplicate correct_answers variable name: %s' % name)
    data['params'][name] = display_answers
    data['correct_answers'][name] = correct_answer
def render(element_html, data):
    if data['manual_grading']:
        element = lxml.html.fragment_fromstring(element_html)
        return pl.inner_html(element)
    else:
        return ''
예제 #19
0
def prepare(element_html, element_index, data):
    element = lxml.html.fragment_fromstring(element_html)
    required_attribs = ['answers_name']
    optional_attribs = ['weight', 'number_answers', 'min_correct', 'max_correct', 'fixed_order', 'inline', 'hide_help_text', 'detailed_help_text']
    pl.check_attribs(element, required_attribs, optional_attribs)
    name = pl.get_string_attrib(element, 'answers_name')

    correct_answers = []
    incorrect_answers = []
    index = 0
    for child in element:
        if child.tag == 'pl_answer':
            pl.check_attribs(child, required_attribs=[], optional_attribs=['correct'])
            correct = pl.get_boolean_attrib(child, 'correct', False)
            child_html = pl.inner_html(child)
            answer_tuple = (index, correct, child_html)
            if correct:
                correct_answers.append(answer_tuple)
            else:
                incorrect_answers.append(answer_tuple)
            index += 1

    len_correct = len(correct_answers)
    len_incorrect = len(incorrect_answers)
    len_total = len_correct + len_incorrect

    number_answers = pl.get_integer_attrib(element, 'number_answers', len_total)
    min_correct = pl.get_integer_attrib(element, 'min_correct', 0)
    max_correct = pl.get_integer_attrib(element, 'max_correct', len(correct_answers))

    number_answers = max(0, min(len_total, min(26, number_answers)))
    min_correct = min(len_correct, min(number_answers, max(0, max(number_answers - len_incorrect, min_correct))))
    max_correct = min(len_correct, min(number_answers, max(min_correct, max_correct)))
    if not (0 <= min_correct <= max_correct <= len_correct):
        raise Exception('INTERNAL ERROR: correct number: (%d, %d, %d, %d)' % (min_correct, max_correct, len_correct, len_incorrect))
    min_incorrect = number_answers - max_correct
    max_incorrect = number_answers - min_correct
    if not (0 <= min_incorrect <= max_incorrect <= len_incorrect):
        raise Exception('INTERNAL ERROR: incorrect number: (%d, %d, %d, %d)' % (min_incorrect, max_incorrect, len_incorrect, len_correct))

    number_correct = random.randint(min_correct, max_correct)
    number_incorrect = number_answers - number_correct

    sampled_correct = random.sample(correct_answers, number_correct)
    sampled_incorrect = random.sample(incorrect_answers, number_incorrect)

    sampled_answers = sampled_correct + sampled_incorrect
    random.shuffle(sampled_answers)

    fixed_order = pl.get_boolean_attrib(element, 'fixed_order', False)
    if fixed_order:
        # we can't simply skip the shuffle because we already broke the original
        # order by separating into correct/incorrect lists
        sampled_answers.sort(key=lambda a: a[0])  # sort by stored original index

    display_answers = []
    correct_answer_list = []
    for (i, (index, correct, html)) in enumerate(sampled_answers):
        keyed_answer = {'key': chr(ord('a') + i), 'html': html}
        display_answers.append(keyed_answer)
        if correct:
            correct_answer_list.append(keyed_answer)

    if name in data['params']:
        raise Exception('duplicate params variable name: %s' % name)
    if name in data['correct_answers']:
        raise Exception('duplicate correct_answers variable name: %s' % name)
    data['params'][name] = display_answers
    data['correct_answers'][name] = correct_answer_list
예제 #20
0
def prepare(element_html, data):
    element = lxml.html.fragment_fromstring(element_html)
    required_attribs = ['answers-name']
    optional_attribs = ['weight', 'number-answers', 'fixed-order', 'inline']
    pl.check_attribs(element, required_attribs, optional_attribs)
    name = pl.get_string_attrib(element, 'answers-name')

    correct_answers = []
    incorrect_answers = []
    index = 0
    for child in element:
        if child.tag in ['pl-answer', 'pl_answer']:
            pl.check_attribs(child, required_attribs=[], optional_attribs=['correct'])
            correct = pl.get_boolean_attrib(child, 'correct', False)
            child_html = pl.inner_html(child)
            answer_tuple = (index, correct, child_html)
            if correct:
                correct_answers.append(answer_tuple)
            else:
                incorrect_answers.append(answer_tuple)
            index += 1

    len_correct = len(correct_answers)
    len_incorrect = len(incorrect_answers)
    len_total = len_correct + len_incorrect

    if len_correct < 1:
        raise Exception('pl-multiple-choice element must have at least one correct answer')

    number_answers = pl.get_integer_attrib(element, 'number-answers', len_total)

    number_answers = max(1, min(1 + len_incorrect, number_answers))
    number_correct = 1
    number_incorrect = number_answers - number_correct
    if not (0 <= number_incorrect <= len_incorrect):
        raise Exception('INTERNAL ERROR: number_incorrect: (%d, %d, %d)' % (number_incorrect, len_incorrect, number_answers))

    sampled_correct = random.sample(correct_answers, number_correct)
    sampled_incorrect = random.sample(incorrect_answers, number_incorrect)

    sampled_answers = sampled_correct + sampled_incorrect
    random.shuffle(sampled_answers)

    fixed_order = pl.get_boolean_attrib(element, 'fixed-order', False)
    if fixed_order:
        # we can't simply skip the shuffle because we already broke the original
        # order by separating into correct/incorrect lists
        sampled_answers.sort(key=lambda a: a[0])  # sort by stored original index

    display_answers = []
    correct_answer = None
    for (i, (index, correct, html)) in enumerate(sampled_answers):
        keyed_answer = {'key': chr(ord('a') + i), 'html': html}
        display_answers.append(keyed_answer)
        if correct:
            correct_answer = keyed_answer

    if name in data['params']:
        raise Exception('duplicate params variable name: %s' % name)
    if name in data['correct_answers']:
        raise Exception('duplicate correct_answers variable name: %s' % name)
    data['params'][name] = display_answers
    data['correct_answers'][name] = correct_answer
def render(element_html, element_index, data):
    if data['panel'] == 'submission':
        element = lxml.html.fragment_fromstring(element_html)
        return pl.inner_html(element)
    else:
        return ''
예제 #22
0
def render(element_html, data):
    element = lxml.html.fragment_fromstring(element_html)
    width = pl.get_float_attrib(element, 'width', None)
    height = pl.get_float_attrib(element, 'height', None)
    background = None

    # Assign layer index in order children are defined
    # Later defined elements will be placed on top of earlier ones
    locations = []
    z_index = 0
    for child in element:
        # Ignore comments
        if isinstance(child, lxml.html.HtmlComment):
            continue

        # Don't do any special processing for backgrounds
        if child.tag == 'pl-background':
            background = pl.inner_html(child)
            continue

        # Otherwise, continue as normal
        valign = pl.get_string_attrib(child, 'valign', VALIGN_DEFAULT)
        halign = pl.get_string_attrib(child, 'halign', HALIGN_DEFAULT)

        left = pl.get_float_attrib(child, 'left', None)
        right = pl.get_float_attrib(child, 'right', None)
        top = pl.get_float_attrib(child, 'top', None)
        bottom = pl.get_float_attrib(child, 'bottom', None)

        # We allow both left/right and top/bottom but only set top and left
        # so we don't have to worry about all the alignment possibilities
        if left is not None:
            x = left
        elif right is not None:
            x = width - right

        if top is not None:
            y = top
        elif bottom is not None:
            y = height - bottom

        hoff = ALIGNMENT_TO_PERC[halign]
        voff = ALIGNMENT_TO_PERC[valign]
        transform = f'translate({hoff}, {voff})'

        style = f'top: {y}px; left: {x}px; transform: {transform}; z-index: {z_index}'
        obj = {
            'html': pl.inner_html(child),
            'outer_style': style,
        }
        locations.append(obj)
        z_index += 1

    html_params = {
        'width': width,
        'height': height,
        'locations': locations,
        'background': background,
        'clip': pl.get_boolean_attrib(element, 'clip', CLIP_DEFAULT)
    }
    with open('pl-overlay.mustache', 'r', encoding='utf-8') as f:
        html = chevron.render(f, html_params).strip()
    return html
예제 #23
0
def render(element_html, data):
    if data['panel'] == 'answer':
        element = lxml.html.fragment_fromstring(element_html)
        return pl.inner_html(element)
    else:
        return ''
예제 #24
0
def test_inner_html():
    e = lxml.html.fragment_fromstring('<div>test</div>')
    assert pl.inner_html(e) == 'test'

    e = lxml.html.fragment_fromstring('<div>test&gt;test</div>')
    assert pl.inner_html(e) == 'test&gt;test'
예제 #25
0
def categorize_options(element, data):
    """Get provided correct and incorrect answers"""
    correct_answers = []
    incorrect_answers = []
    index = 0
    for child in element:
        if child.tag in ['pl-answer', 'pl_answer']:
            pl.check_attribs(child,
                             required_attribs=[],
                             optional_attribs=['score', 'correct', 'feedback'])
            correct = pl.get_boolean_attrib(child, 'correct', False)
            child_html = pl.inner_html(child)
            child_feedback = pl.get_string_attrib(child, 'feedback',
                                                  FEEDBACK_DEFAULT)

            default_score = SCORE_CORRECT_DEFAULT if correct else SCORE_INCORRECT_DEFAULT
            score = pl.get_float_attrib(child, 'score', default_score)

            if not (SCORE_INCORRECT_DEFAULT <= score <= SCORE_CORRECT_DEFAULT):
                raise Exception(
                    f'Score {score} is invalid, must be in the range [0.0, 1.0].'
                )

            if correct and score != SCORE_CORRECT_DEFAULT:
                raise Exception('Correct answers must give full credit.')

            answer_tuple = (index, correct, child_html, child_feedback, score)
            if correct:
                correct_answers.append(answer_tuple)
            else:
                incorrect_answers.append(answer_tuple)
            index += 1

    file_path = pl.get_string_attrib(element, 'external-json',
                                     EXTERNAL_JSON_DEFAULT)
    if file_path is not EXTERNAL_JSON_DEFAULT:
        correct_attrib = pl.get_string_attrib(
            element, 'external-json-correct-key',
            EXTERNAL_JSON_CORRECT_KEY_DEFAULT)
        incorrect_attrib = pl.get_string_attrib(
            element, 'external-json-incorrect-key',
            EXTERNAL_JSON_INCORRECT_KEY_DEFAULT)
        if pathlib.PurePath(file_path).is_absolute():
            json_file = file_path
        else:
            json_file = pathlib.PurePath(
                data['options']['question_path']).joinpath(file_path)
        try:
            with open(json_file, mode='r', encoding='utf-8') as f:
                obj = json.load(f)
                for text in obj.get(correct_attrib, []):
                    correct_answers.append(
                        (index, True, text, None, SCORE_CORRECT_DEFAULT))
                    index += 1
                for text in obj.get(incorrect_attrib, []):
                    incorrect_answers.append(
                        (index, False, text, None, SCORE_INCORRECT_DEFAULT))
                    index += 1
        except FileNotFoundError:
            raise Exception(
                f'JSON answer file: "{json_file}" could not be found')
    return correct_answers, incorrect_answers
예제 #26
0
def get_objects(element, data):
    obj_list = []

    for child in element:
        is_stl = (child.tag in ['pl-threejs-stl', 'pl_threejs_stl'])
        is_txt = (child.tag in ['pl-threejs-txt', 'pl_threejs_txt'])
        if not (is_stl or is_txt):
            continue

        # Type-specific check and get (stl)
        if is_stl:
            # Attributes
            pl.check_attribs(child, required_attribs=['file-name'], optional_attribs=['file-directory', 'frame', 'color', 'position', 'orientation', 'format', 'scale', 'opacity'])
            # - file-name (and file-directory)
            file_url = get_file_url(child, data)
            # - type
            object_type = 'stl'
            # - object
            specific = {
                'type': object_type,
                'file_url': file_url
            }

        # Type-specific check and get (txt)
        if is_txt:
            # Attributes
            pl.check_attribs(child, required_attribs=[], optional_attribs=['frame', 'color', 'position', 'orientation', 'format', 'scale', 'opacity'])
            # - text
            text = pl.inner_html(child)
            # - type
            object_type = 'txt'
            # - object
            specific = {
                'type': object_type,
                'text': text
            }

        # Common
        # - frame
        frame = pl.get_string_attrib(child, 'frame', 'body')
        if frame not in ['body', 'space']:
            raise Exception('"frame" must be either "body" or "space": {:s}'.format(frame))
        if frame == 'body':
            default_color = '#e84a27'
            default_opacity = 0.7
        else:
            default_color = '#13294b'
            default_opacity = 0.4
        # - color
        color = pl.get_color_attrib(child, 'color', default_color)
        # - opacity
        opacity = pl.get_float_attrib(child, 'opacity', default_opacity)
        # - position
        p = pl.get_string_attrib(child, 'position', '[0, 0, 0]')
        try:
            position = np.array(json.loads(p), dtype=np.float64)
            if position.shape == (3,):
                position = position.tolist()
            else:
                raise ValueError()
        except Exception:
            raise Exception('attribute "position" must have format [x, y, z]: {:s}'.format(p))
        # - orientation (and format)
        orientation = get_orientation(child, 'orientation', 'format')
        # - scale
        scale = pl.get_float_attrib(child, 'scale', 1.0)

        common = {
            'frame': frame,
            'color': color,
            'opacity': opacity,
            'position': position,
            'quaternion': orientation,
            'scale': scale
        }

        obj = {**specific, **common}
        obj_list.append(obj)

    return obj_list
예제 #27
0
def render(element_html, data):
    element = lxml.html.fragment_fromstring(element_html)
    digits = pl.get_integer_attrib(element, 'digits', 2)
    show_matlab = pl.get_boolean_attrib(element, 'show-matlab', True)
    show_mathematica = pl.get_boolean_attrib(element, 'show-mathematica', True)
    show_python = pl.get_boolean_attrib(element, 'show-python', True)
    default_tab = pl.get_string_attrib(element, 'default-tab', 'matlab')

    tab_list = ['matlab', 'mathematica', 'python']
    if default_tab not in tab_list:
        raise Exception(f'invalid default-tab: {default_tab}')

    # Setting the default tab
    displayed_tab = [show_matlab, show_mathematica, show_python]
    if not any(displayed_tab):
        raise Exception('All tabs have been hidden from display. At least one tab must be shown.')

    default_tab_index = tab_list.index(default_tab)
    # If not displayed, make first visible tab the default
    if not displayed_tab[default_tab_index]:
        first_display = displayed_tab.index(True)
        default_tab = tab_list[first_display]
    default_tab_index = tab_list.index(default_tab)

    # Active tab should be the default tab
    default_tab_list = [False, False, False]
    default_tab_list[default_tab_index] = True
    [active_tab_matlab, active_tab_mathematica, active_tab_python] = default_tab_list

    # Process parameter data
    matlab_data = ''
    mathematica_data = ''
    python_data = 'import numpy as np\n\n'
    for child in element:
        if child.tag == 'variable':
            # Raise exception if variable does not have a name
            pl.check_attribs(child, required_attribs=['params-name'], optional_attribs=['comment', 'digits'])

            # Get name of variable
            var_name = pl.get_string_attrib(child, 'params-name')

            # Get value of variable, raising exception if variable does not exist
            var_data = data['params'].get(var_name, None)
            if var_data is None:
                raise Exception('No value in data["params"] for variable %s in pl-variable-output element' % var_name)

            # If the variable is in a format generated by pl.to_json, convert it
            # back to a standard type (otherwise, do nothing)
            var_data = pl.from_json(var_data)

            # Get comment, if it exists
            var_matlab_comment = ''
            var_mathematica_comment = ''
            var_python_comment = ''
            if pl.has_attrib(child, 'comment'):
                var_comment = pl.get_string_attrib(child, 'comment')
                var_matlab_comment = f' % {var_comment}'
                var_mathematica_comment = f' (* {var_comment} *)'
                var_python_comment = f' # {var_comment}'

            # Get digit for child, if it exists
            if not pl.has_attrib(child, 'digits'):
                var_digits = digits
            else:
                var_digits = pl.get_string_attrib(child, 'digits')

            # Assembling Python array formatting
            if np.isscalar(var_data):
                prefix = ''
                suffix = ''
            else:
                # Wrap the variable in an ndarray (if it's already one, this does nothing)
                var_data = np.array(var_data)
                # Check shape of variable
                if var_data.ndim > 2:
                    raise Exception('Value in data["params"] for variable %s in pl-variable-output element must be a scalar, a vector, or a 2D array' % var_name)
                # Create prefix/suffix so python string is np.array( ... )
                prefix = 'np.array('
                suffix = ')'

            # Mathematica reserved letters: C D E I K N O
            mathematica_reserved = ['C', 'D', 'E', 'I', 'K', 'N', 'O']
            if pl.inner_html(child) in mathematica_reserved:
                mathematica_suffix = 'm'
            else:
                mathematica_suffix = ''

            # Create string for matlab and python format
            var_name_disp = pl.inner_html(child)
            var_matlab_data = pl.string_from_numpy(var_data, language='matlab', digits=var_digits)
            var_mathematica = pl.string_from_numpy(var_data, language='mathematica', digits=var_digits)
            var_python_data = pl.string_from_numpy(var_data, language='python', digits=var_digits)

            matlab_data += f'{var_name_disp} = {var_matlab_data};{var_matlab_comment}\n'
            mathematica_data += f'{var_name_disp}{mathematica_suffix} = {var_mathematica};{var_mathematica_comment}\n'
            python_data += f'{var_name_disp} = {prefix}{var_python_data}{suffix}{var_python_comment}\n'

    html_params = {
        'active_tab_matlab': active_tab_matlab,
        'active_tab_mathematica': active_tab_mathematica,
        'active_tab_python': active_tab_python,
        'show_matlab': show_matlab,
        'show_mathematica': show_mathematica,
        'show_python': show_python,
        'matlab_data': matlab_data,
        'mathematica_data': mathematica_data,
        'python_data': python_data,
        'uuid': pl.get_uuid()
    }

    with open('pl-variable-output.mustache', 'r', encoding='utf-8') as f:
        html = chevron.render(f, html_params).strip()

    return html
예제 #28
0
def render(element_html, data):
    element = lxml.html.fragment_fromstring(element_html)
    digits = pl.get_integer_attrib(element, 'digits', DIGITS_DEFAULT)
    show_matlab = pl.get_boolean_attrib(element, 'show-matlab',
                                        SHOW_MATLAB_DEFAULT)
    show_mathematica = pl.get_boolean_attrib(element, 'show-mathematica',
                                             SHOW_MATHEMATICA_DEFAULT)
    show_python = pl.get_boolean_attrib(element, 'show-python',
                                        SHOW_PYTHON_DEFAULT)
    default_tab = pl.get_string_attrib(element, 'default-tab',
                                       DEFAULT_TAB_DEFAULT)

    tab_list = ['matlab', 'mathematica', 'python']
    if default_tab not in tab_list:
        raise Exception(f'invalid default-tab: {default_tab}')

    # Setting the default tab
    displayed_tab = [show_matlab, show_mathematica, show_python]
    if not any(displayed_tab):
        raise Exception(
            'All tabs have been hidden from display. At least one tab must be shown.'
        )

    default_tab_index = tab_list.index(default_tab)
    # If not displayed, make first visible tab the default
    if not displayed_tab[default_tab_index]:
        first_display = displayed_tab.index(True)
        default_tab = tab_list[first_display]
    default_tab_index = tab_list.index(default_tab)

    # Active tab should be the default tab
    default_tab_list = [False, False, False]
    default_tab_list[default_tab_index] = True
    [active_tab_matlab, active_tab_mathematica,
     active_tab_python] = default_tab_list

    # Process parameter data
    matlab_data = ''
    mathematica_data = ''
    python_data = 'import numpy as np\n\n'
    for child in element:
        if child.tag == 'variable':
            # Raise exception if variable does not have a name
            pl.check_attribs(child,
                             required_attribs=['params-name'],
                             optional_attribs=['comment', 'digits'])

            # Get name of variable
            var_name = pl.get_string_attrib(child, 'params-name')

            # Get value of variable, raising exception if variable does not exist
            var_data = data['params'].get(var_name, None)
            if var_data is None:
                raise Exception(
                    'No value in data["params"] for variable %s in pl-variable-output element'
                    % var_name)

            # If the variable is in a format generated by pl.to_json, convert it
            # back to a standard type (otherwise, do nothing)
            var_data = pl.from_json(var_data)

            # Get comment, if it exists
            var_matlab_comment = ''
            var_mathematica_comment = ''
            var_python_comment = ''
            if pl.has_attrib(child, 'comment'):
                var_comment = pl.get_string_attrib(child, 'comment')
                var_matlab_comment = f' % {var_comment}'
                var_mathematica_comment = f' (* {var_comment} *)'
                var_python_comment = f' # {var_comment}'

            # Get digit for child, if it exists
            if not pl.has_attrib(child, 'digits'):
                var_digits = digits
            else:
                var_digits = pl.get_string_attrib(child, 'digits')

            # Assembling Python array formatting
            if np.isscalar(var_data):
                prefix = ''
                suffix = ''
            else:
                # Wrap the variable in an ndarray (if it's already one, this does nothing)
                var_data = np.array(var_data)
                # Check shape of variable
                if var_data.ndim > 2:
                    raise Exception(
                        'Value in data["params"] for variable %s in pl-variable-output element must be a scalar, a vector, or a 2D array'
                        % var_name)
                # Create prefix/suffix so python string is np.array( ... )
                prefix = 'np.array('
                suffix = ')'

            # Mathematica reserved letters: C D E I K N O
            mathematica_reserved = ['C', 'D', 'E', 'I', 'K', 'N', 'O']
            if pl.inner_html(child) in mathematica_reserved:
                mathematica_suffix = 'm'
            else:
                mathematica_suffix = ''

            # Create string for matlab and python format
            var_name_disp = pl.inner_html(child)
            var_matlab_data = pl.string_from_numpy(var_data,
                                                   language='matlab',
                                                   digits=var_digits)
            var_mathematica = pl.string_from_numpy(var_data,
                                                   language='mathematica',
                                                   digits=var_digits)
            var_python_data = pl.string_from_numpy(var_data,
                                                   language='python',
                                                   digits=var_digits)

            matlab_data += f'{var_name_disp} = {var_matlab_data};{var_matlab_comment}\n'
            mathematica_data += f'{var_name_disp}{mathematica_suffix} = {var_mathematica};{var_mathematica_comment}\n'
            python_data += f'{var_name_disp} = {prefix}{var_python_data}{suffix}{var_python_comment}\n'

    html_params = {
        'active_tab_matlab': active_tab_matlab,
        'active_tab_mathematica': active_tab_mathematica,
        'active_tab_python': active_tab_python,
        'show_matlab': show_matlab,
        'show_mathematica': show_mathematica,
        'show_python': show_python,
        'matlab_data': matlab_data,
        'mathematica_data': mathematica_data,
        'python_data': python_data,
        'uuid': pl.get_uuid()
    }

    with open('pl-variable-output.mustache', 'r', encoding='utf-8') as f:
        html = chevron.render(f, html_params).strip()

    return html
예제 #29
0
def render(element_html, data):
    element = lxml.html.fragment_fromstring(element_html)
    language = pl.get_string_attrib(element, 'language', LANGUAGE_DEFAULT)
    no_highlight = pl.get_boolean_attrib(element, 'no-highlight',
                                         NO_HIGHLIGHT_DEFAULT)
    specify_language = (language is not None) and (not no_highlight)
    source_file_name = pl.get_string_attrib(element, 'source-file-name',
                                            SOURCE_FILE_NAME_DEFAULT)
    prevent_select = pl.get_boolean_attrib(element, 'prevent-select',
                                           PREVENT_SELECT_DEFAULT)
    highlight_lines = pl.get_string_attrib(element, 'highlight-lines',
                                           HIGHLIGHT_LINES_DEFAULT)
    highlight_lines_color = pl.get_string_attrib(
        element, 'highlight-lines-color', HIGHLIGHT_LINES_COLOR_DEFAULT)

    if source_file_name is not None:
        base_path = data['options']['question_path']
        file_path = os.path.join(base_path, source_file_name)
        if not os.path.exists(file_path):
            raise Exception(f'Unknown file path: "{file_path}".')
        f = open(file_path, 'r')
        code = ''
        for line in f.readlines():
            code += line

        # Chop off ending newlines
        if code[:-2] == '\r\n':
            code = code[:-2]
        if code[:-1] == '\n':
            code = code[:-1]

        f.close()
        # Automatically escape code in file source (important for: html/xml).
        code = escape(code)
    else:
        # Strip a single leading newline from the code, if present. This
        # avoids having spurious newlines because of HTML like:
        #
        # <pl-code>
        # some_code
        # </pl-code>
        #
        # which technically starts with a newline, but we probably
        # don't want a blank line at the start of the code block.
        code = pl.inner_html(element)
        if len(code) > 1 and code[0] == '\r' and code[1] == '\n':
            code = code[2:]
        elif len(code) > 0 and (code[0] == '\n' or code[0] == '\r'):
            code = code[1:]

    if specify_language:
        lexer = get_lexer_by_name(language)
    else:
        lexer = NoHighlightingLexer()

    formatter_opts = {
        'style': 'friendly',
        'cssclass': 'mb-2 rounded',
        'prestyles': 'padding: 0.5rem; margin-bottom: 0px',
        'noclasses': True
    }
    if highlight_lines is not None:
        formatter_opts['hl_lines'] = parse_highlight_lines(highlight_lines)
        formatter_opts['hl_color'] = highlight_lines_color
    formatter = HighlightingHtmlFormatter(**formatter_opts)

    code = pygments.highlight(unescape(code), lexer, formatter)

    html_params = {
        'no_highlight': no_highlight,
        'code': code,
        'prevent_select': prevent_select,
    }

    with open('pl-code.mustache', 'r', encoding='utf-8') as f:
        html = chevron.render(f, html_params).strip()

    return html
예제 #30
0
    def test_inner_html(self):
        e = lxml.html.fragment_fromstring('<div>test</div>')
        self.assertEqual(pl.inner_html(e), 'test')

        e = lxml.html.fragment_fromstring('<div>test&gt;test</div>')
        self.assertEqual(pl.inner_html(e), 'test&gt;test')
예제 #31
0
def render(element_html, data):
    if data['panel'] == 'submission':
        element = lxml.html.fragment_fromstring(element_html)
        return pl.inner_html(element)
    else:
        return ''