def test_current_line_rest(self): args = { 'template': 'first line\nsecond line\n {{ foo } bar', 'data': {'foo': 'xx'} } # self.assertRaisesRegex does not exist in python2.6 for _ in range(10): try: chevron.render(**args) except chevron.ChevronError as error: self.assertEqual(error.msg, 'unclosed tag at line 3')
def test_no_opening_tag(self): args = { 'template': 'oops, no opening tag {{/ closing_tag }}', 'data': {'foo': 'xx'} } try: chevron.render(**args) except chevron.ChevronError as error: self.assertEqual(error.msg, 'Trying to close tag "closing_tag"\n' 'Looks like it was not opened.\n' 'line 2')
def test_callable_1(self): args_passed = {} def first(content, render): args_passed['content'] = content args_passed['render'] = render return "not implemented" args = { 'template': '{{{postcode}}} {{#first}} {{{city}}} || {{{town}}} ' '|| {{{village}}} || {{{state}}} {{/first}}', 'data': { "postcode": "1234", "city": "Mustache City", "state": "Nowhere", "first": first, } } result = chevron.render(**args) expected = '1234 not implemented' template_content = " {{& city }} || {{& town }} || {{& village }} "\ "|| {{& state }} " self.assertEqual(result, expected) self.assertEqual(args_passed['content'], template_content)
def render(element_html, data): element = lxml.html.fragment_fromstring(element_html) script_name = pl.get_string_attrib(element, 'script-name', None) with open(os.path.join(data['options']['question_path'], script_name)) as f: script = f.read() width = pl.get_string_attrib(element, 'width', '500') height = pl.get_string_attrib(element, 'height', '300') params_names = pl.get_string_attrib(element, 'param-names', None) if params_names is None: client_params = {} else: params_names = params_names.split(sep=',') client_params = {key: data['params'][key] for key in params_names} html_params = { 'script': script, 'width': width, 'height': height, 'client_params': client_params, 'uuid': pl.get_uuid(), } with open('pl-prairiedraw-figure.mustache') as f: html = chevron.render(f, html_params).strip() return html
def render(element_html, data): if data['panel'] != 'question': return '' element = lxml.html.fragment_fromstring(element_html) uuid = pl.get_uuid() raw_file_names = pl.get_string_attrib(element, 'file-names', '') file_names = get_file_names_as_array(raw_file_names) file_names_json = json.dumps(file_names, allow_nan=False) answer_name = get_answer_name(raw_file_names) html_params = {'name': answer_name, 'file_names': file_names_json, 'uuid': uuid} files = data['submitted_answers'].get('_files', None) if files is not None: # Filter out any files not part of this element's file_names filtered_files = [x for x in files if x.get('name', '') in file_names] html_params['has_files'] = True html_params['files'] = json.dumps(filtered_files, allow_nan=False) else: html_params['has_files'] = False with open('pl-file-upload.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() return html
def build_dockerfile(self, arch_data): if_build = ".build" if self.data["STAGE"] else "" dockerfile_tmpl_path = f'Dockerfile{if_build}.tmpl' dockerfile_path = f'build/{arch_data["ARCH"]["name"]}/Dockerfile{if_build}' with open(dockerfile_tmpl_path, 'r') as f: dockerfile = chevron.render(f, arch_data) with open(dockerfile_path, 'w') as f: f.write(str(dockerfile))
def render(element_html, data): element = lxml.html.fragment_fromstring(element_html) file_name = pl.get_string_attrib(element, 'file-name', '') answer_name = get_answer_name(file_name) quill_theme = pl.get_string_attrib(element, 'quill-theme', QUILL_THEME_DEFAULT) placeholder = pl.get_string_attrib(element, 'placeholder', PLACEHOLDER_DEFAULT) uuid = pl.get_uuid() source_file_name = pl.get_string_attrib(element, 'source-file-name', SOURCE_FILE_NAME_DEFAULT) directory = pl.get_string_attrib(element, 'directory', DIRECTORY_DEFAULT) element_text = element_inner_html(element) if data['panel'] == 'question' or data['panel'] == 'submission': html_params = { 'name': answer_name, 'file_name': file_name, 'quill_theme': quill_theme, 'placeholder': placeholder, 'editor_uuid': uuid, 'question': data['panel'] == 'question', 'submission': data['panel'] == 'submission', 'read_only': 'true' if data['panel'] == 'submission' else 'false' } if source_file_name is not None: if directory == 'serverFilesCourse': directory = data['options']['server_files_course_path'] elif directory == 'clientFilesCourse': directory = data['options']['client_files_course_path'] else: directory = os.path.join(data['options']['question_path'], directory) file_path = os.path.join(directory, source_file_name) text_display = open(file_path).read() else: if element_text is not None: text_display = str(element_text) else: text_display = '' html_params['original_file_contents'] = base64.b64encode(text_display.encode('UTF-8').strip()).decode() submitted_file_contents = data['submitted_answers'].get(answer_name, None) if submitted_file_contents: html_params['current_file_contents'] = submitted_file_contents else: html_params['current_file_contents'] = html_params['original_file_contents'] html_params['question'] = data['panel'] == 'question' with open('pl-rich-text-editor.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() elif data['panel'] == 'answer': html = '' else: raise Exception('Invalid panel type: ' + data['panel']) return html
def main(): parser = argparse.ArgumentParser() parser.add_argument("json") parser.add_argument("cpp_output") parser.add_argument("h_output") args = parser.parse_args() css_properties = json.load(open(args.json)) properties = [] keywords = [] for name in css_properties: css_property = css_properties[name] properties.append({ 'property_name': name, 'property_enum': css_property_type(name), 'property_default': css_property_default(css_property), 'property_inherited': css_property_inherited(css_property), 'property_value_type': css_property_value_type(css_property), 'keywords': css_property_keywords(name, css_property) }) valid_keywords = css_property.get('valid-keywords', []) for keyword in valid_keywords: keywords.append({'keyword': format_keyword_enum(name, keyword)}) d = {'properties': properties, 'keywords': keywords} cpp_template_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'css_property.cpp.mustache') cpp_template_str = open(cpp_template_path).read() open(args.cpp_output, 'w').write(chevron.render(cpp_template_str, d)) h_template_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'css_property.h.mustache') h_template_str = open(h_template_path).read() open(args.h_output, 'w').write(chevron.render(h_template_str, d))
def get_format_string(is_complex=False, allow_fractions=False, message=None): params = { 'complex': is_complex, 'format_error': True, 'allow_fractions': allow_fractions, 'format_error_message': message } with open('pl-number-input.mustache', 'r', encoding='utf-8') as f: return chevron.render(f, params).strip()
def chevron_vars_for( self, version=None, spec_path=None, paths_relative_to=PathRelativeTo.VERSION_OUTPUT_DIR, ): chevron_vars = { "github_repo_name": self.github_repo, "github_repo_org": self.github_org, "language_name": self.language, "library_version": self.library_version, "user_agent_client_name": self.user_agent_client_name, } chevron_vars["github_repo_url"] = chevron.render( constants.GITHUB_REPO_URL_TEMPLATE, chevron_vars) if version: spec_repo_relpath = "." top_level_relpath = os.path.join(constants.SPEC_REPO_GENERATED_DIR, self.github_repo) if paths_relative_to == PathRelativeTo.VERSION_OUTPUT_DIR: version_output_dir = self.generated_lang_version_dir_for( version) # where is the spec repo relative to version_output_dir version_output_dir_nesting_level = len( version_output_dir.strip("/").split("/")) spec_repo_relpath = "../" * version_output_dir_nesting_level top_level_relpath = ("../" * (version_output_dir_nesting_level - 2) or ".") templates_dir = os.path.join( spec_repo_relpath, constants.SPEC_REPO_TEMPLATES_DIR, self.language, version, ) language_oapi_config_path = os.path.join( constants.SPEC_REPO_CONFIG_DIR, constants.SPEC_REPO_LANGUAGES_CONFIG_DIR, "{lang}_{v}.json".format(lang=self.language, v=version), ) language_config_path = os.path.join(spec_repo_relpath, language_oapi_config_path) config_dir = os.path.join(spec_repo_relpath, constants.SPEC_REPO_CONFIG_DIR) chevron_vars.update({ "config_dir": config_dir, "language_config": language_config_path, "spec_version": version, "templates_dir": templates_dir, "top_level_dir": top_level_relpath, "version_output_dir": ".", }) if spec_path: full_spec_path = os.path.join(spec_repo_relpath, spec_path) chevron_vars["full_spec_path"] = full_spec_path return chevron_vars
def test_unicode_basic(self): args = { 'template': '(╯°□°)╯︵ ┻━┻' } result = chevron.render(**args) expected = '(╯°□°)╯︵ ┻━┻' self.assertEqual(result, expected)
def render(element_html, element_index, data): if data['panel'] == 'submission': html_params = {'submission': True, 'graded': True, 'uuid': pl.get_uuid()} feedback = data['feedback'] html_params['graded'] = bool(feedback) grading_succeeded = bool(feedback.get('succeeded', None)) html_params['grading_succeeded'] = grading_succeeded if not grading_succeeded: html_params['message'] = feedback.get('message', None) else: results = feedback.get('results', None) if grading_succeeded and results: html_params['succeeded'] = bool(results.get('succeeded', None)) html_params['score'] = format(results.get('score', 0) * 100, '.2f').rstrip('0').rstrip('.') html_params['achieved_max_points'] = (results.get('score', 0) >= 1.0) html_params['results_color'] = '#4CAF50' if (results.get('score', 0) >= 1.0) else '#F44336' html_params['has_message'] = bool(results.get('message', False)) html_params['message'] = results.get('message', None) html_params['has_output'] = bool(results.get('output', False)) html_params['output'] = results.get('output', None) html_params['has_message_or_output'] = bool(html_params['has_message'] or html_params['has_output']) results_tests = results.get('tests', None) html_params['has_tests'] = bool(results.get('tests', None)) if results_tests: html_params['points'] = sum(test['points'] for test in results_tests) html_params['max_points'] = sum(test['max_points'] for test in results_tests) # We need to build a new tests array to massage data a bit tests = [] for index, results_test in enumerate(results_tests): test = {} test['index'] = index test['name'] = results_test.get('name', '') test['has_message'] = bool(results_test.get('message', None)) test['message'] = results_test.get('message', None) test['has_output'] = bool(results_test.get('output', None)) test['output'] = results_test.get('output', None) test['max_points'] = results_test.get('max_points') test['points'] = results_test.get('points') correct = test['max_points'] == test['points'] test['results_color'] = '#4CAF50' if correct else '#F44336' test['results_icon'] = 'glyphicon-ok' if correct else 'glyphicon-remove' test['has_description'] = bool(results_test.get('description', None)) test['description'] = results_test.get('description', None) tests.append(test) html_params['tests'] = tests with open('pl_external_grader_results.mustache', 'r') as f: html = chevron.render(f, html_params).strip() else: html = '' return html
def render(element_html, data): html_params = { 'input_nodes': data['params']['input_nodes'], 'output_nodes': data['params']['output_nodes'], 'logic_ops': data['params']['logic_ops'], 'image_url': data['options']['client_files_element_url'] } with open('logic-element.mustache', 'r') as f: return chevron.render(f, html_params).strip()
def test_partials_via_file(self): args = { 'template': 'Hello, {{> thing }}!', # defaults to . 'partials_path': 'partials/', # defaults to mustache 'partials_ext': 'txt', } # ./partials/thing.ms will be read and rendered print(chevron.render(**args))
def render_alias_typedef(type_collection: TypeCollection, type_name): context = { 'template': "typedef {{ aliased }} {{ type_name }};", 'data': { 'type_name': type_name, 'aliased': type_collection.resolve(type_name) } } return chevron.render(**context)
def test_file_mustache(self): with open('test.mustache', 'r') as f: print( chevron.render( f, { 'username': "******", 'password': "******", 'company': 'uxsino', 'adj': 'asshole' }))
def test_listed_data(self): args = { 'template': '{{# . }}({{ . }}){{/ . }}', 'data': [1, 2, 3, 4, 5] } result = chevron.render(**args) expected = '(1)(2)(3)(4)(5)' self.assertEqual(result, expected)
def get_sensor_json(obs_json): input_values = {} app_id = obs_json["app_id"] dev_id = obs_json["dev_id"] input_values["name"] = dev_id input_values[ "description"] = f"sensemakersams app_id: {app_id}, dev_id: {dev_id}" input_values["metadata"] = "missing" with open("sta_templates/sensor.json.template") as template_file: return chevron.render(template_file, input_values)
def test_unicode_inside_list(self): args = { 'template': '{{#list}}{{.}}{{/list}}', 'data': {'list': ['☠']} } result = chevron.render(**args) expected = '☠' self.assertEqual(result, expected)
def test_nest_loops_with_same_key(self): args = { 'template': 'A{{#x}}B{{#x}}{{.}}{{/x}}C{{/x}}D', 'data': {'x': ['z', 'x']} } result = chevron.render(**args) expected = 'ABzxCBzxCD' self.assertEqual(result, expected)
def test_unicode_variable(self): args = { 'template': '{{ table_flip }}', 'data': {'table_flip': '(╯°□°)╯︵ ┻━┻'} } result = chevron.render(**args) expected = '(╯°□°)╯︵ ┻━┻' self.assertEqual(result, expected)
def test_recursion(self): args = { 'template': '{{# 1.2 }}{{# data }}{{.}}{{/ data }}{{/ 1.2 }}', 'data': {'1': {'2': [{'data': ["1", "2", "3"]}]}} } result = chevron.render(**args) expected = '123' self.assertEqual(result, expected)
def test_unicode_partial(self): args = { 'template': '{{> table_flip }}', 'partials_dict': {'table_flip': '(╯°□°)╯︵ ┻━┻'} } result = chevron.render(**args) expected = '(╯°□°)╯︵ ┻━┻' self.assertEqual(result, expected)
def main(): orgs = sorted(list(_orgs), key=lambda x: x.name.lower()) # Ensure an even number of cells, leaving one to render blankly if necessary. if len(orgs) % 2 == 1: orgs.append(Org("", "", "")) org_pairs = tuple( OrgPair(orgs[i], orgs[i + 1]) for i in range(0, len(orgs), 2)) buf = pkgutil.get_data("generate_user_list", "user_list_templates/table.html.mustache") print(chevron.render(buf.decode(), context={"org_pairs": org_pairs}))
def replace_macro(m): arg_values = [ a.strip(' ') for a in m.group(1).split('|') if a.strip(' ') ] if len(macro['args']) != len(arg_values): raise RuntimeError(f'invalid macro call "{m.group()}"') else: return chevron.render(macro['body'], data=dict(zip(macro['args'], arg_values)))
def generate(data): data["params"]["names_for_user"] = [] data["params"]["names_from_user"] = [ {"name": "getFoolingSetElement", "type": "function"}, {"name": "getDistinguishingSuffix", "type": "function"} ] with open(data["options"]["server_files_course_path"] + "/fooling_sets/question_base.html") as f: data["params"]["html"] = chevron.render(f, data).strip()
def use_template(templates_dir, template_name): cur_template_dir = os.path.join(templates_dir, template_name) if not os.path.exists(cur_template_dir): raise Exception('the specified template "' + template_name + '" does not exist') config = default_config config_file = os.path.join(cur_template_dir, 'config.json') if os.path.isfile(config_file): with open(config_file, 'r') as infile: config = json.load(infile) options = {} for option in config['options']: name = option['option'] datatype = option['type'] if datatype == 'text': options[name] = input(name + ':') elif datatype == 'bool': answer = '' while answer not in ['y', 'n']: answer = input(name + '? (y/n):') options[name] = True if answer == 'y' else False for target in config['targets']: target_filename = chevron.render(target['out'], options) if not target_filename: continue target_path = os.path.join(os.getcwd(), target_filename) os.makedirs(os.path.dirname(target_path), exist_ok=True) source_path = os.path.join(cur_template_dir, target['source']) with open(source_path, 'r') as infile: with open(target_path, 'w') as outfile: translation_args = { 'template': infile.read(), 'partials_path': cur_template_dir, 'partials_ext': 'partial.html', 'data': options, } translated = chevron.render(**translation_args) outfile.write(translated)
def __render_template(index_patterns: str, number_of_shards: int): with open("template/index_template.mustache", "r") as f: result = chevron.render( f, { "index_patterns": index_patterns, "number_of_shards": number_of_shards }, ) logger.info("Result of the template rendering from file: %s", result) return result
def update_environment_vars(files: List[FilePath], environment: Dict[str, str]): update_instances_environment_vars(environment=environment) set_os_environment_vars(environment=environment) os.makedirs(RENDER_DIR, exist_ok=True) for file in files: copyfile(file, f'{RENDER_DIR}/{file.name}') with open(file, 'r') as org_file, open(f'{RENDER_DIR}/{file.name}', 'w') as rendered_file: content_to_be_rendered = chevron.render(org_file, environment) rendered_file.write(content_to_be_rendered)
def set_alert_field_using_field_data(self, source, field_name): ''' Sets the content of a field using the chevron format ''' field_value = self.config[field_name] if '{{source}}' in field_value: field_value = field_value.replace( '{{source}}', json.dumps(source, sort_keys=True, indent=4)) field_value = str(chevron.render(field_value, source)) return field_value
def render_state_file_text(self): status = TrainingStatus.NEW if self.model_to_load is not None: status = TrainingStatus.NEW_LOAD_MODEL with open(resource_path('templates/state.yml.mustache'), 'r') as f: return (chevron.render(f, { 'status': status, 'iterations_run': 0, 'latest_model_saved': '' }))
def generate_vmargs(node_name, cookie): script_dir = os.path.dirname(__file__) #<-- absolute dir the script is in rel_path = "templates/vm.args.mustache" template_path = os.path.join(script_dir, rel_path) vmargs_path = "/release-config/vm.args" with open(template_path, "r", encoding='utf8') as f: template = f.read() vmargs = chevron.render(template, {"MY_NODE_NAME": node_name, "MY_COOKIE": cookie}) with open(vmargs_path, "w", encoding='utf8') as g: g.write(vmargs)
def render(element_html, data): # Get workspace url # TODO: Improve UX if key undefined (https://github.com/PrairieLearn/PrairieLearn/pull/2665#discussion_r449319839) workspace_url = data['options']['workspace_url'] # Create and return html html_params = {'workspace_url': workspace_url} with open('pl-workspace.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() return html
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) highlight_lines = pl.get_string_attrib(element, 'highlight-lines', None) highlight_lines_color = pl.get_string_attrib(element, 'highlight-lines-color', '#b3d7ff') 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
def test_namedtuple_data(self): NT = collections.namedtuple('NT', ['foo', 'bar']) args = { 'template': '{{foo}} {{bar}}', 'data': NT('hello', 'world') } result = chevron.render(**args) expected = 'hello world' self.assertEqual(result, expected)
def test_inverted_coercion(self): args = { 'template': '{{#object}}{{^child}}{{.}}{{/child}}{{/object}}', 'data': {'object': [ 'foo', 'bar', {'child': True}, 'baz' ]} } result = chevron.render(**args) expected = 'foobarbaz' self.assertEqual(result, expected)
def test_falsy(self): args = { 'template': '{{null}}{{false}}{{list}}{{dict}}{{zero}}', 'data': {'null': None, 'false': False, 'list': [], 'dict': {}, 'zero': 0 } } result = chevron.render(**args) expected = 'False0' self.assertEqual(result, expected)
def test_complex(self): class Complex: def __init__(self): self.attr = 42 args = { 'template': '{{comp.attr}} {{int.attr}}', 'data': {'comp': Complex(), 'int': 1 } } result = chevron.render(**args) expected = '42 ' self.assertEqual(result, expected)
def render(element_html, data): if data['panel'] != 'question': return '' element = lxml.html.fragment_fromstring(element_html) file_name = pl.get_string_attrib(element, 'file-name', '') answer_name = get_answer_name(file_name) editor_config_function = pl.get_string_attrib(element, 'editor-config-function', None) ace_mode = pl.get_string_attrib(element, 'ace-mode', None) ace_theme = pl.get_string_attrib(element, 'ace-theme', None) uuid = pl.get_uuid() source_file_name = pl.get_string_attrib(element, 'source-file-name', None) html_params = { 'name': answer_name, 'file_name': file_name, 'ace_mode': ace_mode, 'ace_theme': ace_theme, 'editor_config_function': editor_config_function, 'uuid': uuid } if source_file_name is not None: file_path = os.path.join(data['options']['question_path'], source_file_name) text_display = open(file_path).read() else: if element.text is not None: text_display = str(element.text) else: text_display = '' html_params['original_file_contents'] = base64.b64encode(text_display.encode('UTF-8').strip()).decode() submitted_file_contents = data['submitted_answers'].get(answer_name, None) if submitted_file_contents: html_params['current_file_contents'] = submitted_file_contents else: html_params['current_file_contents'] = html_params['original_file_contents'] if data['panel'] == 'question': html_params['question'] = True with open('pl-file-editor.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() else: html = '' return html
def run_benchmark(): parser = argparse.ArgumentParser() parser.add_argument("-e", "--executable", help="path to the benchmark executable") parser.add_argument("-c", "--configuration", help="path to the benchmark configuration") args = parser.parse_args() configuration = json.load(open(args.configuration)) os.makedirs("data", exist_ok=True) results = [] for index, experiment in enumerate(configuration["experiments"]): results += [run_experiment(args.executable, configuration["algorithms"], experiment, index)] template = open("charts.mustache").read() html = chevron.render(template, {"chart": results}) open("charts.html", "w").write(html)
def render(element_html, data): element = lxml.html.fragment_fromstring(element_html) engine = pl.get_string_attrib(element, 'engine', 'dot') # Read the contents of this element as the data to render # we dump the string to json to ensure that newlines are # properly encoded graphviz_data = json.dumps(str(element.text)) html_params = { 'uuid': pl.get_uuid(), 'workerURL': '/node_modules/viz.js/full.render.js', 'data': graphviz_data, 'engine': engine, } with open('pl-graphviz-render.mustache') as f: html = chevron.render(f, html_params).strip() return html
def render(element_html, data): element = lxml.html.fragment_fromstring(element_html) # Get file name or raise exception if one does not exist file_name = pl.get_string_attrib(element, 'file-name') # Get type (default is static) file_type = pl.get_string_attrib(element, 'type', 'static') # Get directory (default is clientFilesQuestion) file_directory = pl.get_string_attrib(element, 'directory', 'clientFilesQuestion') # Get base url, which depends on the type and directory if file_type == 'static': if file_directory == 'clientFilesQuestion': base_url = data['options']['client_files_question_url'] elif file_directory == 'clientFilesCourse': base_url = data['options']['client_files_course_url'] else: raise ValueError('directory "{}" is not valid for type "{}" (must be "clientFilesQuestion" or "clientFilesCourse")'.format(file_directory, file_type)) elif file_type == 'dynamic': if pl.has_attrib(element, 'directory'): raise ValueError('no directory ("{}") can be provided for type "{}"'.format(file_directory, file_type)) else: base_url = data['options']['client_files_question_dynamic_url'] else: raise ValueError('type "{}" is not valid (must be "static" or "dynamic")'.format(file_type)) # Get full url file_url = os.path.join(base_url, file_name) # Get width (optional) width = pl.get_string_attrib(element, 'width', None) # Create and return html html_params = {'src': file_url, 'width': width} with open('pl-figure.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() return html
def test_callable_2(self): def first(content, render): result = render(content) result = [x.strip() for x in result.split(" || ") if x.strip()] return result[0] args = { 'template': '{{{postcode}}} {{#first}} {{{city}}} || {{{town}}} ' '|| {{{village}}} || {{{state}}} {{/first}}', 'data': { "postcode": "1234", "town": "Mustache Town", "state": "Nowhere", "first": first, } } result = chevron.render(**args) expected = '1234 Mustache Town' self.assertEqual(result, expected)
def render(element_html, data): if data['panel'] != 'submission': return '' html_params = {'uuid': pl.get_uuid()} # Fetch the list of required files for this question required_file_names = data['params'].get('_required_file_names', []) html_params['required_files'] = required_file_names # Fetch any submitted files submitted_files = data['submitted_answers'].get('_files', []) # Pass through format errors from the file input elements html_params['errors'] = data['format_errors'].get('_files', []) # Decode and reshape files into a useful form if len(submitted_files) > 0: files = [] for idx, file in enumerate(submitted_files): try: contents = base64.b64decode(file['contents'] or '').decode() except UnicodeDecodeError: contents = 'Unable to decode file.' files.append({ 'name': file['name'], 'contents': contents, 'index': idx }) html_params['has_files'] = True html_params['files'] = files else: html_params['has_files'] = False with open('pl-file-preview.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() return html
def test_callable_3(self): '''Test generating some data within the function ''' def first(content, render): result = render(content, {'city': "Injected City"}) result = [x.strip() for x in result.split(" || ") if x.strip()] return result[0] args = { 'template': '{{{postcode}}} {{#first}} {{{city}}} || {{{town}}} ' '|| {{{village}}} || {{{state}}} {{/first}}', 'data': { "postcode": "1234", "town": "Mustache Town", "state": "Nowhere", "first": first, } } result = chevron.render(**args) expected = '1234 Injected City' self.assertEqual(result, expected)
def render(element_html, data): element = lxml.html.fragment_fromstring(element_html) name = pl.get_string_attrib(element, 'answers-name') label = pl.get_string_attrib(element, 'label', None) variables_string = pl.get_string_attrib(element, 'variables', None) variables = get_variables_list(variables_string) display = pl.get_string_attrib(element, 'display', 'inline') allow_complex = pl.get_boolean_attrib(element, 'allow-complex', False) imaginary_unit = pl.get_string_attrib(element, 'imaginary-unit-for-display', 'i') if data['panel'] == 'question': editable = data['editable'] raw_submitted_answer = data['raw_submitted_answers'].get(name, None) operators = ', '.join(['cos', 'sin', 'tan', 'exp', 'log', 'sqrt', '( )', '+', '-', '*', '/', '^', '**']) constants = ', '.join(['pi, e']) info_params = { 'format': True, 'variables': variables_string, 'operators': operators, 'constants': constants, 'allow_complex': allow_complex, } with open('pl-symbolic-input.mustache', 'r', encoding='utf-8') as f: info = chevron.render(f, info_params).strip() with open('pl-symbolic-input.mustache', 'r', encoding='utf-8') as f: info_params.pop('format', None) info_params['shortformat'] = True shortinfo = chevron.render(f, info_params).strip() html_params = { 'question': True, 'name': name, 'label': label, 'editable': editable, 'info': info, 'shortinfo': shortinfo, 'uuid': pl.get_uuid(), 'allow_complex': allow_complex, } partial_score = data['partial_scores'].get(name, {'score': None}) score = partial_score.get('score', None) if score is not None: try: score = float(score) if score >= 1: html_params['correct'] = True elif score > 0: html_params['partial'] = math.floor(score * 100) else: html_params['incorrect'] = True except Exception: raise ValueError('invalid score' + score) if display == 'inline': html_params['inline'] = True elif display == 'block': html_params['block'] = True else: raise ValueError('method of display "%s" is not valid (must be "inline" or "block")' % display) if raw_submitted_answer is not None: html_params['raw_submitted_answer'] = escape(raw_submitted_answer) with open('pl-symbolic-input.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() elif data['panel'] == 'submission': parse_error = data['format_errors'].get(name, None) html_params = { 'submission': True, 'label': label, 'parse_error': parse_error, 'uuid': pl.get_uuid() } if parse_error is None: a_sub = data['submitted_answers'][name] if isinstance(a_sub, str): # this is for backward-compatibility a_sub = phs.convert_string_to_sympy(a_sub, variables, allow_complex=allow_complex) else: a_sub = phs.json_to_sympy(a_sub, allow_complex=allow_complex) a_sub = a_sub.subs(sympy.I, sympy.Symbol(imaginary_unit)) html_params['a_sub'] = sympy.latex(a_sub) else: raw_submitted_answer = data['raw_submitted_answers'].get(name, None) if raw_submitted_answer is not None: html_params['raw_submitted_answer'] = escape(raw_submitted_answer) partial_score = data['partial_scores'].get(name, {'score': None}) score = partial_score.get('score', None) if score is not None: try: score = float(score) if score >= 1: html_params['correct'] = True elif score > 0: html_params['partial'] = math.floor(score * 100) else: html_params['incorrect'] = True except Exception: raise ValueError('invalid score' + score) if display == 'inline': html_params['inline'] = True elif display == 'block': html_params['block'] = True else: raise ValueError('method of display "%s" is not valid (must be "inline" or "block")' % display) with open('pl-symbolic-input.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() elif data['panel'] == 'answer': a_tru = data['correct_answers'].get(name, None) if a_tru is not None: if isinstance(a_tru, str): # this is so instructors can specify the true answer simply as a string a_tru = phs.convert_string_to_sympy(a_tru, variables, allow_complex=allow_complex) else: a_tru = phs.json_to_sympy(a_tru, allow_complex=allow_complex) a_tru = a_tru.subs(sympy.I, sympy.Symbol(imaginary_unit)) html_params = { 'answer': True, 'label': label, 'a_tru': sympy.latex(a_tru) } with open('pl-symbolic-input.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() else: html = '' else: raise Exception('Invalid panel type: %s' % data['panel']) return html
def test(): result = chevron.render(template, data) if result != expected: error = 'Test failed:\n-- got --\n{}\n-- expected --\n{}' raise Exception(error.format(result, expected))
def render(element_html, data): html_params = { 'number': data['params']['random_number'] } with open('course-element.mustache', 'r') as f: return chevron.render(f, html_params).strip()
def render(element_html, data): element = lxml.html.fragment_fromstring(element_html) answer_name = pl.get_string_attrib(element, 'answer-name') uuid = pl.get_uuid() body_position = get_position(element, 'body-position', default=[0, 0, 0]) body_orientation = get_orientation(element, 'body-orientation', 'body-pose-format') camera_position = get_position(element, 'camera-position', default=[5, 2, 2], must_be_nonzero=True) body_cantranslate = pl.get_boolean_attrib(element, 'body-cantranslate', True) body_canrotate = pl.get_boolean_attrib(element, 'body-canrotate', True) camera_canmove = pl.get_boolean_attrib(element, 'camera-canmove', True) text_pose_format = pl.get_string_attrib(element, 'text-pose-format', 'matrix') if text_pose_format not in ['matrix', 'quaternion', 'homogeneous']: raise Exception('attribute "text-pose-format" must be either "matrix", "quaternion", or homogeneous') objects = get_objects(element, data) if data['panel'] == 'question': will_be_graded = pl.get_boolean_attrib(element, 'grade', True) show_pose = pl.get_boolean_attrib(element, 'show-pose-in-question', True) # Restore pose of body and camera, if available - otherwise use values # from attributes (note that restored pose will also have camera_orientation, # which we currently ignore because the camera is always z up and looking # at the origin of the space frame). # # Be careful. It's possible that data['submitted_answers'][answer_name] # exists but is None (due to some other error). So we need to use None # as the default and to check if the result - either from the existing # value or the default value - is None. pose_default = { 'body_quaternion': body_orientation, 'body_position': body_position, 'camera_position': camera_position } pose = data['submitted_answers'].get(answer_name, None) if pose is None: pose = pose_default # These are passed as arguments to PLThreeJS constructor in client code options = { 'uuid': uuid, 'pose': dict_to_b64(pose), 'pose_default': dict_to_b64(pose_default), 'body_cantranslate': body_cantranslate, 'body_canrotate': body_canrotate, 'camera_canmove': camera_canmove, 'text_pose_format': text_pose_format, 'objects': objects } # These are used for templating html_params = { 'question': True, 'uuid': uuid, 'answer_name': answer_name, 'show_bodybuttons': body_cantranslate or body_canrotate, 'show_toggle': body_cantranslate and body_canrotate, 'show_reset': body_cantranslate or body_canrotate or camera_canmove, 'show_pose': show_pose, 'show_instructions': will_be_graded, 'tol_translation': '{:.2f}'.format(pl.get_float_attrib(element, 'tol-translation', 0.5)), 'tol_rotation': '{:.1f}'.format(pl.get_float_attrib(element, 'tol-rotation', 5)), 'default_is_python': True, 'options': json.dumps(options, allow_nan=False) } with open('pl-threejs.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() elif data['panel'] == 'submission': will_be_graded = pl.get_boolean_attrib(element, 'grade', True) if not will_be_graded: return '' show_pose = pl.get_boolean_attrib(element, 'show-pose-in-submitted-answer', True) # Get submitted answer pose = data['submitted_answers'].get(answer_name) # These are passed as arguments to PLThreeJS constructor in client code options = { 'uuid': uuid, 'pose': dict_to_b64(pose), 'body_cantranslate': False, 'body_canrotate': False, 'camera_canmove': False, 'text_pose_format': text_pose_format, 'objects': objects } # These are used for templating html_params = { 'submission': True, 'uuid': uuid, 'answer_name': answer_name, 'show_bodybuttons': False, 'show_toggle': False, 'show_pose': show_pose, 'default_is_python': True, 'options': json.dumps(options, allow_nan=False) } partial_score = data['partial_scores'].get(answer_name, None) if partial_score is not None: html_params['error_in_translation'] = str(np.abs(np.round(partial_score['feedback']['error_in_translation'], 2))) html_params['error_in_rotation'] = str(np.abs(np.round(partial_score['feedback']['error_in_rotation'], 1))) html_params['show_feedback'] = True score = partial_score.get('score', None) if score is not None: try: score = float(score) if score >= 1: html_params['correct'] = True elif score > 0: html_params['partial'] = math.floor(score * 100) else: html_params['incorrect'] = True except Exception: raise ValueError('invalid score' + score) with open('pl-threejs.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() elif data['panel'] == 'answer': will_be_graded = pl.get_boolean_attrib(element, 'grade', True) if not will_be_graded: return '' show_pose = pl.get_boolean_attrib(element, 'show-pose-in-correct-answer', True) # Get submitted answer pose = data['submitted_answers'].get(answer_name, None) if pose is None: # If we are here, an error has occurred. Replace pose with its default. # (Only pose['camera_position'] is actually used.) pose = { 'body_quaternion': body_orientation, 'body_position': body_position, 'camera_position': camera_position } # Get correct answer a = data['correct_answers'].get(answer_name, None) if a is None: return '' # Convert correct answer to Quaternion, then to [x, y, z, w] f = pl.get_string_attrib(element, 'answer-pose-format', 'rpy') p, q = parse_correct_answer(f, a) p = p.tolist() q = np.roll(q.elements, -1).tolist() # Replace body pose with correct answer pose['body_position'] = p pose['body_quaternion'] = q # These are passed as arguments to PLThreeJS constructor in client code options = { 'uuid': uuid, 'pose': dict_to_b64(pose), 'body_cantranslate': False, 'body_canrotate': False, 'camera_canmove': False, 'text_pose_format': text_pose_format, 'objects': objects } # These are used for templating html_params = { 'answer': True, 'uuid': uuid, 'answer_name': answer_name, 'show_bodybuttons': False, 'show_toggle': False, 'show_pose': show_pose, 'default_is_python': True, 'options': json.dumps(options, allow_nan=False) } with open('pl-threejs.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() else: raise Exception('Invalid panel type: %s' % data['panel']) return html
def render(element_html, data): element = lxml.html.fragment_fromstring(element_html) name = pl.get_string_attrib(element, 'answers-name') label = pl.get_string_attrib(element, 'label', None) if '_pl_matrix_input_format' in data['submitted_answers']: format_type = data['submitted_answers']['_pl_matrix_input_format'].get(name, 'matlab') else: format_type = 'matlab' if data['panel'] == 'question': editable = data['editable'] raw_submitted_answer = data['raw_submitted_answers'].get(name, None) # Get comparison parameters and info strings comparison = pl.get_string_attrib(element, 'comparison', 'relabs') if comparison == 'relabs': rtol = pl.get_float_attrib(element, 'rtol', 1e-2) atol = pl.get_float_attrib(element, 'atol', 1e-8) if (rtol < 0): raise ValueError('Attribute rtol = {:g} must be non-negative'.format(rtol)) if (atol < 0): raise ValueError('Attribute atol = {:g} must be non-negative'.format(atol)) info_params = {'format': True, 'relabs': True, 'rtol': '{:g}'.format(rtol), 'atol': '{:g}'.format(atol)} elif comparison == 'sigfig': digits = pl.get_integer_attrib(element, 'digits', 2) if (digits < 0): raise ValueError('Attribute digits = {:d} must be non-negative'.format(digits)) info_params = {'format': True, 'sigfig': True, 'digits': '{:d}'.format(digits), 'comparison_eps': 0.51 * (10**-(digits - 1))} elif comparison == 'decdig': digits = pl.get_integer_attrib(element, 'digits', 2) if (digits < 0): raise ValueError('Attribute digits = {:d} must be non-negative'.format(digits)) info_params = {'format': True, 'decdig': True, 'digits': '{:d}'.format(digits), 'comparison_eps': 0.51 * (10**-(digits - 0))} else: raise ValueError('method of comparison "%s" is not valid (must be "relabs", "sigfig", or "decdig")' % comparison) info_params['allow_complex'] = pl.get_boolean_attrib(element, 'allow-complex', False) with open('pl-matrix-input.mustache', 'r', encoding='utf-8') as f: info = chevron.render(f, info_params).strip() with open('pl-matrix-input.mustache', 'r', encoding='utf-8') as f: info_params.pop('format', None) info_params['shortformat'] = True shortinfo = chevron.render(f, info_params).strip() html_params = { 'question': True, 'name': name, 'label': label, 'editable': editable, 'info': info, 'shortinfo': shortinfo, 'uuid': pl.get_uuid() } partial_score = data['partial_scores'].get(name, {'score': None}) score = partial_score.get('score', None) if score is not None: try: score = float(score) if score >= 1: html_params['correct'] = True elif score > 0: html_params['partial'] = math.floor(score * 100) else: html_params['incorrect'] = True except Exception: raise ValueError('invalid score' + score) if raw_submitted_answer is not None: html_params['raw_submitted_answer'] = escape(raw_submitted_answer) with open('pl-matrix-input.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() elif data['panel'] == 'submission': parse_error = data['format_errors'].get(name, None) html_params = { 'submission': True, 'label': label, 'parse_error': parse_error, 'uuid': pl.get_uuid() } if parse_error is None: # Get submitted answer, raising an exception if it does not exist a_sub = data['submitted_answers'].get(name, None) if a_sub is None: raise Exception('submitted answer is None') # If answer is in a format generated by pl.to_json, convert it # back to a standard type (otherwise, do nothing) a_sub = pl.from_json(a_sub) # Wrap answer in an ndarray (if it's already one, this does nothing) a_sub = np.array(a_sub) # Format answer as a string html_params['a_sub'] = pl.string_from_2darray(a_sub, language=format_type, digits=12, presentation_type='g') else: raw_submitted_answer = data['raw_submitted_answers'].get(name, None) if raw_submitted_answer is not None: html_params['raw_submitted_answer'] = escape(raw_submitted_answer) partial_score = data['partial_scores'].get(name, {'score': None}) score = partial_score.get('score', None) if score is not None: try: score = float(score) if score >= 1: html_params['correct'] = True elif score > 0: html_params['partial'] = math.floor(score * 100) else: html_params['incorrect'] = True except Exception: raise ValueError('invalid score' + score) with open('pl-matrix-input.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() elif data['panel'] == 'answer': # Get true answer - do nothing if it does not exist a_tru = pl.from_json(data['correct_answers'].get(name, None)) if a_tru is not None: a_tru = np.array(a_tru) # Get comparison parameters comparison = pl.get_string_attrib(element, 'comparison', 'relabs') if comparison == 'relabs': rtol = pl.get_float_attrib(element, 'rtol', 1e-2) atol = pl.get_float_attrib(element, 'atol', 1e-8) # FIXME: render correctly with respect to rtol and atol matlab_data = pl.string_from_2darray(a_tru, language='matlab', digits=12, presentation_type='g') python_data = pl.string_from_2darray(a_tru, language='python', digits=12, presentation_type='g') elif comparison == 'sigfig': digits = pl.get_integer_attrib(element, 'digits', 2) matlab_data = pl.string_from_2darray(a_tru, language='matlab', digits=digits, presentation_type='sigfig') python_data = pl.string_from_2darray(a_tru, language='python', digits=digits, presentation_type='sigfig') elif comparison == 'decdig': digits = pl.get_integer_attrib(element, 'digits', 2) matlab_data = pl.string_from_2darray(a_tru, language='matlab', digits=digits, presentation_type='f') python_data = pl.string_from_2darray(a_tru, language='python', digits=digits, presentation_type='f') else: raise ValueError('method of comparison "%s" is not valid (must be "relabs", "sigfig", or "decdig")' % comparison) html_params = { 'answer': True, 'label': label, 'matlab_data': matlab_data, 'python_data': python_data, 'uuid': pl.get_uuid() } if format_type == 'matlab': html_params['default_is_matlab'] = True else: html_params['default_is_python'] = True with open('pl-matrix-input.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() else: html = '' else: raise Exception('Invalid panel type: %s' % data['panel']) return html
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
def render(element_html, data): if data['panel'] == 'submission': html_params = {'submission': True, 'graded': True, 'uuid': pl.get_uuid()} feedback = data['feedback'] html_params['graded'] = bool(feedback) grading_succeeded = bool(feedback.get('succeeded', None)) html_params['grading_succeeded'] = grading_succeeded if not grading_succeeded: html_params['message'] = feedback.get('message', None) else: results = feedback.get('results', None) if grading_succeeded and results: html_params['succeeded'] = bool(results.get('succeeded', None)) html_params['score'] = format(results.get('score', 0) * 100, '.2f').rstrip('0').rstrip('.') html_params['achieved_max_points'] = (results.get('score', 0) >= 1.0) html_params['results_color'] = '#4CAF50' if (results.get('score', 0) >= 1.0) else '#F44336' html_params['has_message'] = bool(results.get('message', False)) html_params['message'] = results.get('message', None) html_params['has_output'] = bool(results.get('output', False)) html_params['output'] = results.get('output', None) html_params['has_message_or_output'] = bool(html_params['has_message'] or html_params['has_output']) results_tests = results.get('tests', None) html_params['has_tests'] = bool(results.get('tests', None)) if results_tests: # Let's not assume that people give us a valid array of tests # If any test is missing either points or max_points, we'll # disable detailed scores for all questions tests_missing_points = False for test in results_tests: if test.get('points', None) is None: tests_missing_points = True if test.get('max_points', None) is None: tests_missing_points = True html_params['tests_missing_points'] = tests_missing_points if not tests_missing_points: html_params['points'] = sum(test['points'] for test in results_tests) html_params['max_points'] = sum(test['max_points'] for test in results_tests) # We need to build a new tests array to massage data a bit tests = [] for index, results_test in enumerate(results_tests): test = {} test['index'] = index test['name'] = results_test.get('name', '') test['has_message'] = bool(results_test.get('message', None)) test['message'] = results_test.get('message', None) test['has_output'] = bool(results_test.get('output', None)) test['output'] = results_test.get('output', None) test['has_description'] = bool(results_test.get('description', None)) test['description'] = results_test.get('description', None) if not tests_missing_points: test['max_points'] = results_test.get('max_points') test['points'] = results_test.get('points') correct = test['max_points'] == test['points'] test['results_color'] = '#4CAF50' if correct else '#F44336' test['results_icon'] = 'fa-check' if correct else 'fa-times' tests.append(test) html_params['tests'] = tests with open('pl-external-grader-results.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() else: html = '' return html
def render(element_html, data): element = lxml.html.fragment_fromstring(element_html) name = pl.get_string_attrib(element, 'answers-name') label = pl.get_string_attrib(element, 'label', None) suffix = pl.get_string_attrib(element, 'suffix', None) display = pl.get_string_attrib(element, 'display', 'inline') if data['panel'] == 'question': editable = data['editable'] raw_submitted_answer = data['raw_submitted_answers'].get(name, None) # Get info strings info_params = {'format': True} with open('pl-integer-input.mustache', 'r', encoding='utf-8') as f: info = chevron.render(f, info_params).strip() with open('pl-integer-input.mustache', 'r', encoding='utf-8') as f: info_params.pop('format', None) info_params['shortformat'] = True shortinfo = chevron.render(f, info_params).strip() html_params = { 'question': True, 'name': name, 'label': label, 'suffix': suffix, 'editable': editable, 'info': info, 'shortinfo': shortinfo, 'uuid': pl.get_uuid() } partial_score = data['partial_scores'].get(name, {'score': None}) score = partial_score.get('score', None) if score is not None: try: score = float(score) if score >= 1: html_params['correct'] = True elif score > 0: html_params['partial'] = math.floor(score * 100) else: html_params['incorrect'] = True except Exception: raise ValueError('invalid score' + score) if display == 'inline': html_params['inline'] = True elif display == 'block': html_params['block'] = True else: raise ValueError('method of display "%s" is not valid (must be "inline" or "block")' % display) if raw_submitted_answer is not None: html_params['raw_submitted_answer'] = escape(raw_submitted_answer) with open('pl-integer-input.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() elif data['panel'] == 'submission': parse_error = data['format_errors'].get(name, None) html_params = { 'submission': True, 'label': label, 'parse_error': parse_error, 'uuid': pl.get_uuid() } if parse_error is None: # Get submitted answer, raising an exception if it does not exist a_sub = data['submitted_answers'].get(name, None) if a_sub is None: raise Exception('submitted answer is None') # If answer is in a format generated by pl.to_json, convert it # back to a standard type (otherwise, do nothing) a_sub = pl.from_json(a_sub) html_params['suffix'] = suffix html_params['a_sub'] = '{:d}'.format(a_sub) else: raw_submitted_answer = data['raw_submitted_answers'].get(name, None) if raw_submitted_answer is not None: html_params['raw_submitted_answer'] = escape(raw_submitted_answer) partial_score = data['partial_scores'].get(name, {'score': None}) score = partial_score.get('score', None) if score is not None: try: score = float(score) if score >= 1: html_params['correct'] = True elif score > 0: html_params['partial'] = math.floor(score * 100) else: html_params['incorrect'] = True except Exception: raise ValueError('invalid score' + score) with open('pl-integer-input.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() elif data['panel'] == 'answer': a_tru = pl.from_json(data['correct_answers'].get(name, None)) if a_tru is not None: html_params = {'answer': True, 'label': label, 'a_tru': '{:d}'.format(a_tru), 'suffix': suffix} with open('pl-integer-input.mustache', 'r', encoding='utf-8') as f: html = chevron.render(f, html_params).strip() else: html = '' else: raise Exception('Invalid panel type: %s' % data['panel']) return html