diff --git a/graderutils/README.md b/graderutils/README.md index 6259bc4..e281899 100644 --- a/graderutils/README.md +++ b/graderutils/README.md @@ -1,7 +1,7 @@ ## Test configuration The functionality of the test runner is customized by supplying a [yaml](http://yaml.org/) file containing the desired configuration. -The file must conform to [this](schemas/test_config_v1_3.yaml) JSON schema. +The file must conform to [this](schemas/test_config_v1_4.yaml) JSON schema. Graderutils will output JSON schema validation errors if a given test configuration file is invalid. Examples of possible test configurations are found below and in [this](test_config.yaml) example file. diff --git a/graderutils/schemaobjects.py b/graderutils/schemaobjects.py index eda05a6..f950ac2 100644 --- a/graderutils/schemaobjects.py +++ b/graderutils/schemaobjects.py @@ -14,7 +14,7 @@ SCHEMA_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "schemas")) -def build_schemas(version="v1_3"): +def build_schemas(version="v1_4"): """ Build all feedback schemas and the graderutils test_config schema. """ diff --git a/graderutils/schemas/test_config_v1_3.yaml b/graderutils/schemas/test_config_v1_4.yaml similarity index 99% rename from graderutils/schemas/test_config_v1_3.yaml rename to graderutils/schemas/test_config_v1_4.yaml index f0957e3..6969c0a 100644 --- a/graderutils/schemas/test_config_v1_3.yaml +++ b/graderutils/schemas/test_config_v1_4.yaml @@ -112,7 +112,6 @@ properties: - python_whitelist - plain_text_blacklist - plain_text_whitelist - - image_type - labview - xlsm - html diff --git a/graderutils/validation.py b/graderutils/validation.py index cbd3b74..236d48c 100644 --- a/graderutils/validation.py +++ b/graderutils/validation.py @@ -10,7 +10,6 @@ import collections import contextlib import html5lib -import imghdr import importlib import io import re @@ -182,14 +181,6 @@ def ast_dump(source): return '\n'.join(map(ast.dump, ast.walk(ast.parse(source)))) -def get_image_type_errors(image, expected_type): - errors = {} - actual_type = imghdr.what(image) - if actual_type != expected_type: - errors["message"] = "Expected type '{}' but got '{}'.".format(expected_type, actual_type) - return errors - - def _import_module_from_python_file(filename): err = io.StringIO() module = None @@ -362,9 +353,6 @@ def _get_validation_error(validation, filename, config): get_matches = _get_plain_text_whitelist_misses error = get_restricted_syntax_matches(config, get_matches) - elif validation == "image_validation_type": - error = get_image_type_errors(filename) - elif validation == "labview": error = get_labview_errors(filename) diff --git a/graderutils_format/templates/base.html b/graderutils_format/templates/base.html index a73f852..3c48e9d 100644 --- a/graderutils_format/templates/base.html +++ b/graderutils_format/templates/base.html @@ -11,9 +11,9 @@ integrity="sha256-u7e5khyithlIdTpu22PHhENmPcRdFiHRjhAuHcs05RI=" crossorigin="anonymous"> + href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" + integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" + crossorigin="anonymous"> @@ -21,9 +21,9 @@ {% block body %} {% endblock %} - + diff --git a/graderutils_format/templates/default.css b/graderutils_format/templates/default.css index 0acbecc..261a1c3 100644 --- a/graderutils_format/templates/default.css +++ b/graderutils_format/templates/default.css @@ -1,32 +1,21 @@ /* Increase size of labels in grader result panels */ +div.grading-feedback h4 { + margin-top: 0; +} span.feedback-label { font-size: 14px; } -/* Add space between different test groups */ -div.grading-task { - margin-top: 40px; - margin-bottom: 40px; -} -/* Add more separation between last test group and unittest terminal output */ -div.full-test-output { - margin-top: 35px; -} -/* Bootstrap h4 bottom margins are by default a bit too small */ -h4.testgroup-header { - margin-bottom: 20px; -} /* Disable automatic horizontal scroll of preformatted text blocks */ div.pre-wrap pre { white-space: pre-wrap; + border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color); + background-color: var(--bs-tertiary-bg); } -div.warning-messages { - margin-top: 35px; -} -div.grader-info { - color: gray; +div.pre-wrap pre:last-of-type { + margin-block: 0px 0px; } span.iotester-warning { - color: red; + color: var(--bs-danger); } span.iotester-basic { color: #a5409e; @@ -38,44 +27,39 @@ span.iotester-correct { background-color: #8cde8c; } span.iotester-input { - background-color: #49d8ff; + background-color: #7ed9f5; } -/* Traceback button top margin is by default a bit too small */ -button.btn-traceback { - margin-top: 10px; +[data-bs-theme="dark"] span.iotester-basic { + color: #d0a7ff; } -/* A table is used for wrapping long panel text to multiple lines and to prevent it from pushing badges and labels down */ -div.panel-default > .panel-heading { - display: table; - width: 100%; +[data-bs-theme="dark"] span.iotester-incorrect { + background-color: #8b2e2e; } -/* Table cell for the text part of panel titles */ -div.panel-text { - display: table-cell; +[data-bs-theme="dark"] span.iotester-correct { + background-color: #1e6e1e; } -/* Vertically center and reserve space for the badges and labels of panel titles */ -div.panel-annotation { - display: table-cell; - vertical-align: middle; - min-width: 200px; +[data-bs-theme="dark"] span.iotester-input { + background-color: #0e5a8a; } -/* Add space between panel titles and points badges, and increase badge size */ -div.grading-task .panel-default > .panel-heading .badge { +div.grading-feedback .accordion-header .badge { font-size: 14px; - margin-right: 10px; -} -/* Green color for badges and labels when tests pass */ -div.grading-task .panel-default > .panel-heading .color-passed { - color: #fff; - background-color: #00803c; -} -/* Red color for badges and labels when tests fail */ -div.grading-task .panel-default > .panel-heading .color-failed { - color: #fff; - background-color: #a50000; -} -/* Orange color for badges and labels when errors occur */ -div.grading-task .panel-default > .panel-heading .color-error { - color: #fff; - background-color: #a6670e; +} +span.panel-title { + color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); + font-weight: 700; +} +/* Fix borders with vertically spaced accordion */ +div.spaced-accordion > .accordion-item { + border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color); + border-radius: var(--bs-accordion-border-radius); + margin-top: 1rem; +} +div.spaced-accordion > .accordion-item > .accordion-header > .accordion-button:not(.collapsed) { + border-top-left-radius: var(--bs-accordion-inner-border-radius); + border-top-right-radius: var(--bs-accordion-inner-border-radius); + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} +div.spaced-accordion > .accordion-item > .accordion-header > .accordion-button.collapsed { + border-radius: var(--bs-accordion-inner-border-radius); } diff --git a/graderutils_format/templates/feedback.html b/graderutils_format/templates/feedback.html index a6ae766..769ff35 100644 --- a/graderutils_format/templates/feedback.html +++ b/graderutils_format/templates/feedback.html @@ -18,152 +18,161 @@

Total points: {{ points }} of {{ maxPoints }}

{% endif %} {% if testsRun %} -

Total tests run: {{ testsRun }}

+

Total tests run: {{ testsRun }}

{% endif %} {% if runningTime %} -

Total running time of tests: {{ "%.3f"|format(runningTime) }} s.

+

Total running time of tests: {{ "%.3f"|format(runningTime) }} s.

{% endif %} -{% for result_group in resultGroups %} -{% set result_group_loop = loop %} - -
- {% if result_group.maxPoints %} -

{{ result_group.title }} points: {{ result_group.points }} of {{ result_group.maxPoints }}

- {% else %} -

{{ result_group.title }}

- {% endif %} - {% if result_group.description %} -
{{ result_group.description }}
- {% endif %} - - {% for result in result_group.testResults %} - {% set test_result_loop = loop %} - -
-
-
- {{ result.title }} -
-
- {% if result.status == "passed" %} - - {% if result.maxPoints %} - {{ result.points }} / {{ result.maxPoints }} - points - {% endif %} - {% elif result.status == "failed" %} - - {% if result.maxPoints %} - {{ result.points }} / {{ result.maxPoints }} - points - {% endif %} - {% elif result.status == "error" %} - - {% if result.maxPoints %} - {{ result.points }} / {{ result.maxPoints }} - points - {% endif %} - {% endif %} -
-
-
- {% block result_panel scoped %} -
- - {% if result.header %} -

{{ result.header }}

- {% endif %} - - {% if result.testOutput %} - {% if not result.iotesterData %} -
{{ result.testOutput|e }}
- {% elif result.status == "error" %} -
{{ result.testOutput|e }}
-

Feedback:

- {% endif %} - {% endif %} - - {% if result.iotesterData %} - {% if result.iotesterData.warning %} -
{{ result.iotesterData.warning }}
- {% endif %} - {% if result.iotesterData.feedback %} -
{{ result.iotesterData.feedback }}
- {% endif %} - {% if result.iotesterData.customFeedback %} -
{{ result.iotesterData.customFeedback }}
- {% endif %} - {% endif %} - - {% block result_panel_after_output scoped %} - {% endblock %} - - {% if result.runningTime %} -
Running time: {{ "%.3f"|format(result.runningTime) }} s.
- {% endif %} - - {% if result.status == "error" %} - -
-
{{ result.fullTestOutput|e }}
+
+ {% for result_group in resultGroups %} + {% set result_group_loop = loop %} +
+ {% if result_group.maxPoints %} +

{{ result_group.title }} points: {{ result_group.points }} of {{ result_group.maxPoints }}

+ {% else %} +

{{ result_group.title }}

+ {% endif %} + {% if result_group.description %} +
{{ result_group.description }}
+ {% endif %} + +
+ {% for result in result_group.testResults %} + {% set test_result_loop = loop %} +
+
+ +
+
+
+ {% block result_panel scoped %} +
+ {% if result.header %} +

{{ result.header }}

+ {% endif %} + + {% if result.testOutput %} + {% if not result.iotesterData %} +
{{ result.testOutput|e }}
+ {% elif result.status == "error" %} +
{{ result.testOutput|e }}
+

Feedback:

+ {% endif %} + {% endif %} + + {% if result.iotesterData %} + {% if result.iotesterData.warning %} +
{{ result.iotesterData.warning }}
+ {% endif %} + {% if result.iotesterData.feedback %} +
{{ result.iotesterData.feedback }}
+ {% endif %} + {% if result.iotesterData.customFeedback %} +
{{ result.iotesterData.customFeedback }}
+ {% endif %} + {% endif %} + + {% block result_panel_after_output scoped %} + {% endblock %} + + {% if result.runningTime %} +
Running time: {{ "%.3f"|format(result.runningTime) }} s.
+ {% endif %} + + {% if result.status == "error" %} + +
+
{{ result.fullTestOutput|e }}
+
+ {% endif %} + + {% if result.footer %} +

{{ result.footer }}

+ {% endif %} +
+ {% endblock %} +
- {% endif %} - - {% if result.footer %} -

{{ result.footer }}

- {% endif %} -
- {% endblock %} - + {% endfor %}
- {% endfor %} -
-{% if result_group.fullOutput %} -
-
-
- - Full terminal output for {{ result_group.title }} -
-
- + {% if result_group.fullOutput %} +
+
+
+ +
+
+
+
{{ result_group.fullOutput|e }}
+
+
-
-
{{ result_group.fullOutput|e }}
-
-
-{% endif %} + {% endif %} + + {% endfor %} -{% endfor %} - -{% if warningMessages %} -
- {% for warning in warningMessages %} - {% set warnings_loop = loop %} -
-
-
- Graderutils message + {% if warningMessages %} +
+ {% for warning in warningMessages %} + {% set warnings_loop = loop %} +
+
+
+
+
+
{{ warning|e }}
+
+
-
-
{{ warning|e }}
-
+ {% endfor %}
- {% endfor %} + {% endif %}
-{% endif %} {% block feedback_end %} {% endblock %} diff --git a/setup.py b/setup.py index 49c6b88..e0bf79f 100644 --- a/setup.py +++ b/setup.py @@ -41,11 +41,11 @@ packages=find_packages(), include_package_data=True, install_requires=[ - 'PyYAML ~= 6.0', # Parse configuration files - 'Jinja2 ~= 3.1.1', # Render HTML feedback with templates - 'html5lib ~= 1.1', # Parse HTML - 'hypothesis ~= 6.43.1', # Generics for UnitTests - 'jsonschema ~= 4.4.0', # Validators for JSON schemas - 'python_jsonschema_objects ~= 0.4.1', # JSON schema to Python object mappings + 'PyYAML ~= 6.0', # Parse configuration files + 'Jinja2 ~= 3.1.1', # Render HTML feedback with templates + 'html5lib ~= 1.1', # Parse HTML + 'hypothesis ~= 6.43.1', # Generics for UnitTests + 'jsonschema ~= 4.4.0', # Validators for JSON schemas + 'python_jsonschema_objects ~= 0.4.1', # JSON schema to Python object mappings ], )