From 264fadc1f7b1e450b760d9a413b3c82beec84b2d Mon Sep 17 00:00:00 2001 From: Irtaza Akram Date: Mon, 2 Feb 2026 09:21:28 +0500 Subject: [PATCH] fix: adds problem block --- requirements/base.in | 13 + requirements/base.txt | 127 +- requirements/dev.txt | 190 +- requirements/doc.txt | 160 +- requirements/quality.txt | 146 +- requirements/test.txt | 161 +- .../assets/fixtures/checkbox_problem.html | 14 + .../assets/fixtures/codeinput_problem.html | 20 + .../problem/assets/fixtures/imageinput.html | 30 + .../assets/fixtures/imageinput.underscore | 30 + .../assets/fixtures/jsinput_problem.html | 60 + .../assets/fixtures/matlabinput_problem.html | 48 + .../problem/assets/fixtures/problem.html | 8 + .../assets/fixtures/problem_content.html | 44 + .../assets/fixtures/problem_content_1240.html | 23 + .../assets/fixtures/radiobutton_problem.html | 14 + .../problem/assets/karma_runner.js | 12 + .../problem/assets/spec/collapsible_spec.js | 130 + .../problem/assets/spec/display_spec.js | 1176 +++ .../problem/assets/spec/imageinput_spec.js | 132 + .../spec/spec_helpers/accessibility_tools.js | 256 + .../spec/spec_helpers/add_ajax_prefix.js | 5 + .../assets/spec/spec_helpers/ajax_prefix.js | 24 + .../assets/spec/spec_helpers/helper.js | 325 + .../problem/assets/spec/spec_helpers/i18n.js | 10 + .../spec/spec_helpers/jasmine-extensions.js | 284 + .../spec/spec_helpers/jasmine-imagediff.js | 401 ++ .../spec/spec_helpers/jasmine-waituntil.js | 47 + .../assets/spec/spec_helpers/logger.js | 102 + .../problem/assets/static/applets/Protex.jar | Bin 0 -> 212767 bytes .../problem/assets/static/applets/genex.jar | Bin 0 -> 227154 bytes .../assets/static/css/ProblemBlockDisplay.css | 2304 ++++++ .../static/images/vsepr/AX2E0-3D-balls.png | Bin 0 -> 26362 bytes .../static/images/vsepr/AX2E1-3D-balls.png | Bin 0 -> 51741 bytes .../static/images/vsepr/AX2E2-3D-balls.png | Bin 0 -> 66279 bytes .../static/images/vsepr/AX2E3-3D-balls.png | Bin 0 -> 65642 bytes .../static/images/vsepr/AX3E0-3D-balls.png | Bin 0 -> 53410 bytes .../static/images/vsepr/AX3E1-3D-balls.png | Bin 0 -> 51196 bytes .../static/images/vsepr/AX3E2-3D-balls.png | Bin 0 -> 55912 bytes .../static/images/vsepr/AX4E0-3D-balls.png | Bin 0 -> 41665 bytes .../static/images/vsepr/AX4E1-3D-balls.png | Bin 0 -> 62210 bytes .../static/images/vsepr/AX4E2-3D-balls.png | Bin 0 -> 71325 bytes .../static/images/vsepr/AX5E1-3D-balls.png | Bin 0 -> 58554 bytes .../static/images/vsepr/AX5E2-3D-balls.png | Bin 0 -> 64400 bytes .../static/images/vsepr/AX6E0-3D-balls.png | Bin 0 -> 47023 bytes .../static/images/vsepr/AX6E1-3D-balls.png | Bin 0 -> 53875 bytes .../static/images/vsepr/AX7E0-3D-balls.png | Bin 0 -> 46381 bytes .../static/images/vsepr/AX8E0-3D-balls.png | Bin 0 -> 55764 bytes .../static/images/vsepr/AX9E0-3D-balls.png | Bin 0 -> 54388 bytes .../static/images/vsepr/Bent-3D-balls.png | Bin 0 -> 51200 bytes .../static/images/vsepr/Linear-3D-balls.png | Bin 0 -> 36709 bytes .../static/images/vsepr/Linear-stick.png | Bin 0 -> 42098 bytes .../images/vsepr/Octahedral-3D-balls.png | Bin 0 -> 50231 bytes .../static/images/vsepr/Octahedral-stick.png | Bin 0 -> 98842 bytes .../vsepr/Pentagonal-bipyramidal-3D-balls.png | Bin 0 -> 49557 bytes .../vsepr/Pentagonal-planar-3D-balls.png | Bin 0 -> 55783 bytes .../vsepr/Pentagonal-pyramidal-3D-balls.png | Bin 0 -> 58082 bytes .../images/vsepr/Pyramidal-3D-balls.png | Bin 0 -> 53022 bytes .../static/images/vsepr/Seesaw-3D-balls.png | Bin 0 -> 54193 bytes .../vsepr/Square-antiprismatic-3D-balls.png | Bin 0 -> 69611 bytes .../images/vsepr/Square-planar-3D-balls.png | Bin 0 -> 57831 bytes .../static/images/vsepr/T-shaped-3D-balls.png | Bin 0 -> 45726 bytes .../images/vsepr/Tetrahedral-3D-balls.png | Bin 0 -> 43145 bytes .../static/images/vsepr/Tetrahedral-stick.png | Bin 0 -> 65541 bytes .../static/images/vsepr/Trigonal-3D-balls.png | Bin 0 -> 45910 bytes .../vsepr/Trigonal-bipyramidal-3D-balls.png | Bin 0 -> 38729 bytes .../vsepr/Trigonal-bipyramidal-stick.png | Bin 0 -> 82648 bytes .../images/vsepr/Trigonal-planar-stick.png | Bin 0 -> 62006 bytes .../problem/assets/static/js/.gitignore | 3 + .../problem/assets/static/js/capa/README | 1 + .../assets/static/js/capa/annotationinput.js | 98 + .../js/capa/chemical_equation_preview.js | 36 + .../assets/static/js/capa/choicetextinput.js | 73 + .../static/js/capa/design-protein-2d.js | 85 + .../assets/static/js/capa/drag_and_drop.js | 25 + .../js/capa/drag_and_drop/base_image.js | 45 + .../js/capa/drag_and_drop/config_parser.js | 271 + .../static/js/capa/drag_and_drop/container.js | 13 + .../js/capa/drag_and_drop/draggable_events.js | 143 + .../js/capa/drag_and_drop/draggable_logic.js | 400 ++ .../js/capa/drag_and_drop/draggables.js | 281 + .../static/js/capa/drag_and_drop/main.js | 107 + .../static/js/capa/drag_and_drop/scroller.js | 162 + .../static/js/capa/drag_and_drop/state.js | 95 + .../static/js/capa/drag_and_drop/targets.js | 266 + .../js/capa/drag_and_drop/update_input.js | 373 + .../assets/static/js/capa/edit-a-gene.js | 71 + .../static/js/capa/fixtures/jsinput.html | 53 + ...1B31BA00E7CE7B6BD63DD13A8586A45.cache.html | 652 ++ ...3308EE54E8033A708B414CAC05B0C32.cache.html | 642 ++ ...AC57DC6EC8C1D8672DDF6E6D4EF57CC.cache.html | 628 ++ ...B4F4D4EFA24CDE2E4287CC07897F249.cache.html | 654 ++ ...069AC107D79C29D6237614AC340F0C0.cache.html | 652 ++ ...6220FCC8B9234FEAD8D826A73C6D2A4.cache.html | 642 ++ .../static/js/capa/genex/clear.cache.gif | Bin 0 -> 43 bytes .../assets/static/js/capa/genex/genex.css | 122 + .../static/js/capa/genex/genex.nocache.js | 18 + .../assets/static/js/capa/genex/hosted.html | 365 + .../static/js/capa/genex/images/circles.png | Bin 0 -> 753 bytes .../js/capa/genex/images/circles_ie6.png | Bin 0 -> 316 bytes .../static/js/capa/genex/images/corner.png | Bin 0 -> 615 bytes .../js/capa/genex/images/corner_ie6.png | Bin 0 -> 325 bytes .../static/js/capa/genex/images/hborder.png | Bin 0 -> 1135 bytes .../js/capa/genex/images/hborder_ie6.png | Bin 0 -> 595 bytes .../js/capa/genex/images/thumb_horz.png | Bin 0 -> 78 bytes .../js/capa/genex/images/thumb_vertical.png | Bin 0 -> 85 bytes .../static/js/capa/genex/images/vborder.png | Bin 0 -> 174 bytes .../js/capa/genex/images/vborder_ie6.png | Bin 0 -> 135 bytes .../js/capa/jsinput/jsinput_example.css | 9 + .../js/capa/jsinput/jsinput_example.html | 15 + .../static/js/capa/jsinput/jsinput_example.js | 86 + ...9CC89519B0E1FCB47B935AC9FE13D7B.cache.html | 743 ++ ...E05B1CD5BFCAF7D53C7C64D84318178.cache.html | 750 ++ ...824A958AB642DC2213DFFDAC640BEAA.cache.html | 760 ++ ...9267DE8FB02F8B995B4A58C66C76E29.cache.html | 758 ++ ...275492F7098103BCB05F4F86ABF6218.cache.html | 750 ++ ...3301B0E65F38C7FCF2EF3764B3BB0B6.cache.html | 754 ++ .../static/js/capa/protex/clear.cache.gif | Bin 0 -> 43 bytes .../assets/static/js/capa/protex/hosted.html | 365 + .../assets/static/js/capa/protex/protex.css | 57 + .../static/js/capa/protex/protex.nocache.js | 18 + .../js/capa/protex/scrollTableLoading.gif | Bin 0 -> 1434 bytes .../assets/static/js/capa/schematicinput.js | 49 + .../spec/formula_equation_preview_spec.js | 453 ++ .../static/js/capa/spec/jsinput_spec.js | 28 + .../js/capa/src/formula_equation_preview.js | 213 + .../assets/static/js/capa/src/jschannel.js | 790 +++ .../assets/static/js/capa/src/jsinput.js | 221 + .../js/capa/symbolic_mathjax_preprocessor.js | 35 + .../problem/assets/static/js/collapsible.js | 121 + .../problem/assets/static/js/display.js | 1411 ++++ .../problem/assets/static/js/imageinput.js | 54 + .../assets/static/js/javascript_loader.js | 97 + .../problem/assets/static/js/schematic.js | 6291 +++++++++++++++++ .../static/js/vendor/codemirror-compressed.js | 11 + .../problem/assets/static/js/xmodule.js | 103 + xblocks_contrib/problem/capa/__init__.py | 0 xblocks_contrib/problem/capa/capa_problem.py | 1229 ++++ xblocks_contrib/problem/capa/checker.py | 171 + xblocks_contrib/problem/capa/correctmap.py | 217 + xblocks_contrib/problem/capa/customrender.py | 195 + xblocks_contrib/problem/capa/errors.py | 62 + xblocks_contrib/problem/capa/inputtypes.py | 1804 +++++ xblocks_contrib/problem/capa/registry.py | 57 + xblocks_contrib/problem/capa/responsetypes.py | 3832 ++++++++++ .../problem/capa/safe_exec/README.rst | 43 + .../problem/capa/safe_exec/__init__.py | 3 + .../problem/capa/safe_exec/exceptions.py | 21 + .../problem/capa/safe_exec/lazymod.py | 43 + .../problem/capa/safe_exec/remote_exec.py | 143 + .../problem/capa/safe_exec/safe_exec.py | 506 ++ .../problem/capa/safe_exec/tests/__init__.py | 0 .../tests/test_files/pylib/constant.py | 1 + .../capa/safe_exec/tests/test_lazymod.py | 62 + .../capa/safe_exec/tests/test_remote_exec.py | 35 + .../capa/safe_exec/tests/test_safe_exec.py | 784 ++ .../capa/templates/annotationinput.html | 61 + .../capa/templates/chemicalequationinput.html | 19 + .../problem/capa/templates/choicegroup.html | 41 + .../problem/capa/templates/choicetext.html | 61 + .../problem/capa/templates/clarification.html | 10 + .../problem/capa/templates/codeinput.html | 31 + .../capa/templates/crystallography.html | 29 + .../capa/templates/designprotein2dinput.html | 25 + .../capa/templates/drag_and_drop_input.html | 27 + .../capa/templates/editageneinput.html | 32 + .../problem/capa/templates/editamolecule.html | 32 + .../capa/templates/filesubmission.html | 16 + .../capa/templates/formulaequationinput.html | 29 + .../problem/capa/templates/imageinput.html | 33 + .../problem/capa/templates/jsinput.html | 42 + .../problem/capa/templates/mathstring.html | 8 + .../problem/capa/templates/matlabinput.html | 121 + .../problem/capa/templates/optioninput.html | 20 + .../problem/capa/templates/problem.html | 154 + .../problem/capa/templates/problem_ajax.html | 16 + .../capa/templates/problem_notifications.html | 19 + .../capa/templates/schematicinput.html | 18 + .../problem/capa/templates/solutionspan.html | 3 + .../problem/capa/templates/status_span.html | 12 + .../problem/capa/templates/textline.html | 39 + .../problem/capa/templates/vsepr_input.html | 38 + .../problem/capa/tests/__init__.py | 0 xblocks_contrib/problem/capa/tests/helpers.py | 120 + .../capa/tests/response_xml_factory.py | 940 +++ .../problem/capa/tests/test_answer_pool.py | 672 ++ .../problem/capa/tests/test_capa_problem.py | 824 +++ .../problem/capa/tests/test_correctmap.py | 195 + .../problem/capa/tests/test_customrender.py | 87 + .../problem/capa/tests/test_errors.py | 63 + .../capa/tests/test_files/dynamath_input.txt | 43 + .../capa/tests/test_files/extended_hints.xml | 50 + .../test_files/extended_hints_checkbox.xml | 119 + .../test_files/extended_hints_dropdown.xml | 42 + .../extended_hints_multiple_choice.xml | 35 + ...tended_hints_multiple_choice_with_html.xml | 15 + .../extended_hints_numeric_input.xml | 41 + .../test_files/extended_hints_text_input.xml | 81 + .../test_files/extended_hints_with_errors.xml | 14 + .../test_files/filename_convert_test.txt | 1 + .../test_files/js/mersenne-twister-min.js | 216 + .../test_files/js/test_problem_display.js | 58 + .../test_files/js/test_problem_generator.js | 36 + .../test_files/js/test_problem_grader.js | 56 + .../capa/tests/test_files/js/xproblem.js | 74 + .../tests/test_files/snuggletex_2x+3y.xml | 235 + .../tests/test_files/snuggletex_correct.html | 556 ++ .../tests/test_files/snuggletex_wrong.html | 258 + .../tests/test_files/snuggletex_x+x+3y.xml | 225 + .../tests/test_files/targeted_feedback.xml | 50 + .../test_files/targeted_feedback_multiple.xml | 91 + .../capa/tests/test_hint_functionality.py | 1135 +++ .../problem/capa/tests/test_html_render.py | 318 + .../capa/tests/test_input_templates.py | 1188 ++++ .../problem/capa/tests/test_inputtypes.py | 1734 +++++ .../problem/capa/tests/test_responsetypes.py | 2793 ++++++++ .../problem/capa/tests/test_shuffle.py | 320 + .../capa/tests/test_targeted_feedback.py | 654 ++ .../problem/capa/tests/test_util.py | 197 + .../capa/tests/test_xqueue_interface.py | 150 + .../capa/tests/test_xqueue_submission.py | 125 + xblocks_contrib/problem/capa/util.py | 251 + .../problem/capa/xqueue_interface.py | 278 + .../problem/capa/xqueue_submission.py | 111 + xblocks_contrib/problem/capa_block.py | 2469 +++++++ xblocks_contrib/problem/markup.py | 74 + xblocks_contrib/problem/problem.py | 104 - .../problem/static/css/problem.css | 9 - .../problem/static/js/src/problem.js | 38 - xblocks_contrib/problem/stringify.py | 30 + .../problem/templates/problem.html | 7 - .../problem/tests/data/capa/prog1.py | 1 + .../problem/tests/data/capa/prog2.py | 1882 +++++ .../problem/tests/data/capa/prog3.py | 1 + .../problem/tests/test_capa_block.py | 4079 +++++++++++ xblocks_contrib/problem/tests/test_problem.py | 26 - .../problem/tests/test_stringify.py | 47 + xblocks_contrib/problem/xmlparser.py | 148 + 238 files changed, 61502 insertions(+), 222 deletions(-) create mode 100644 xblocks_contrib/problem/assets/fixtures/checkbox_problem.html create mode 100644 xblocks_contrib/problem/assets/fixtures/codeinput_problem.html create mode 100644 xblocks_contrib/problem/assets/fixtures/imageinput.html create mode 100644 xblocks_contrib/problem/assets/fixtures/imageinput.underscore create mode 100644 xblocks_contrib/problem/assets/fixtures/jsinput_problem.html create mode 100644 xblocks_contrib/problem/assets/fixtures/matlabinput_problem.html create mode 100644 xblocks_contrib/problem/assets/fixtures/problem.html create mode 100644 xblocks_contrib/problem/assets/fixtures/problem_content.html create mode 100644 xblocks_contrib/problem/assets/fixtures/problem_content_1240.html create mode 100644 xblocks_contrib/problem/assets/fixtures/radiobutton_problem.html create mode 100644 xblocks_contrib/problem/assets/karma_runner.js create mode 100644 xblocks_contrib/problem/assets/spec/collapsible_spec.js create mode 100644 xblocks_contrib/problem/assets/spec/display_spec.js create mode 100644 xblocks_contrib/problem/assets/spec/imageinput_spec.js create mode 100644 xblocks_contrib/problem/assets/spec/spec_helpers/accessibility_tools.js create mode 100644 xblocks_contrib/problem/assets/spec/spec_helpers/add_ajax_prefix.js create mode 100644 xblocks_contrib/problem/assets/spec/spec_helpers/ajax_prefix.js create mode 100644 xblocks_contrib/problem/assets/spec/spec_helpers/helper.js create mode 100644 xblocks_contrib/problem/assets/spec/spec_helpers/i18n.js create mode 100644 xblocks_contrib/problem/assets/spec/spec_helpers/jasmine-extensions.js create mode 100644 xblocks_contrib/problem/assets/spec/spec_helpers/jasmine-imagediff.js create mode 100644 xblocks_contrib/problem/assets/spec/spec_helpers/jasmine-waituntil.js create mode 100644 xblocks_contrib/problem/assets/spec/spec_helpers/logger.js create mode 100644 xblocks_contrib/problem/assets/static/applets/Protex.jar create mode 100644 xblocks_contrib/problem/assets/static/applets/genex.jar create mode 100644 xblocks_contrib/problem/assets/static/css/ProblemBlockDisplay.css create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX2E0-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX2E1-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX2E2-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX2E3-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX3E0-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX3E1-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX3E2-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX4E0-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX4E1-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX4E2-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX5E1-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX5E2-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX6E0-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX6E1-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX7E0-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX8E0-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/AX9E0-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Bent-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Linear-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Linear-stick.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Octahedral-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Octahedral-stick.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Pentagonal-bipyramidal-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Pentagonal-planar-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Pentagonal-pyramidal-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Pyramidal-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Seesaw-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Square-antiprismatic-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Square-planar-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/T-shaped-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Tetrahedral-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Tetrahedral-stick.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Trigonal-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Trigonal-bipyramidal-3D-balls.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Trigonal-bipyramidal-stick.png create mode 100644 xblocks_contrib/problem/assets/static/images/vsepr/Trigonal-planar-stick.png create mode 100644 xblocks_contrib/problem/assets/static/js/.gitignore create mode 100644 xblocks_contrib/problem/assets/static/js/capa/README create mode 100644 xblocks_contrib/problem/assets/static/js/capa/annotationinput.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/chemical_equation_preview.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/choicetextinput.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/design-protein-2d.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/drag_and_drop.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/drag_and_drop/base_image.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/drag_and_drop/config_parser.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/drag_and_drop/container.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/drag_and_drop/draggable_events.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/drag_and_drop/draggable_logic.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/drag_and_drop/draggables.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/drag_and_drop/main.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/drag_and_drop/scroller.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/drag_and_drop/state.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/drag_and_drop/targets.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/drag_and_drop/update_input.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/edit-a-gene.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/fixtures/jsinput.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/21B31BA00E7CE7B6BD63DD13A8586A45.cache.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/63308EE54E8033A708B414CAC05B0C32.cache.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/7AC57DC6EC8C1D8672DDF6E6D4EF57CC.cache.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/9B4F4D4EFA24CDE2E4287CC07897F249.cache.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/A069AC107D79C29D6237614AC340F0C0.cache.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/C6220FCC8B9234FEAD8D826A73C6D2A4.cache.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/clear.cache.gif create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/genex.css create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/genex.nocache.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/hosted.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/images/circles.png create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/images/circles_ie6.png create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/images/corner.png create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/images/corner_ie6.png create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/images/hborder.png create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/images/hborder_ie6.png create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/images/thumb_horz.png create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/images/thumb_vertical.png create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/images/vborder.png create mode 100644 xblocks_contrib/problem/assets/static/js/capa/genex/images/vborder_ie6.png create mode 100644 xblocks_contrib/problem/assets/static/js/capa/jsinput/jsinput_example.css create mode 100644 xblocks_contrib/problem/assets/static/js/capa/jsinput/jsinput_example.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/jsinput/jsinput_example.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/protex/39CC89519B0E1FCB47B935AC9FE13D7B.cache.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/protex/6E05B1CD5BFCAF7D53C7C64D84318178.cache.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/protex/C824A958AB642DC2213DFFDAC640BEAA.cache.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/protex/D9267DE8FB02F8B995B4A58C66C76E29.cache.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/protex/F275492F7098103BCB05F4F86ABF6218.cache.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/protex/F3301B0E65F38C7FCF2EF3764B3BB0B6.cache.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/protex/clear.cache.gif create mode 100644 xblocks_contrib/problem/assets/static/js/capa/protex/hosted.html create mode 100644 xblocks_contrib/problem/assets/static/js/capa/protex/protex.css create mode 100644 xblocks_contrib/problem/assets/static/js/capa/protex/protex.nocache.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/protex/scrollTableLoading.gif create mode 100644 xblocks_contrib/problem/assets/static/js/capa/schematicinput.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/spec/formula_equation_preview_spec.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/spec/jsinput_spec.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/src/formula_equation_preview.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/src/jschannel.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/src/jsinput.js create mode 100644 xblocks_contrib/problem/assets/static/js/capa/symbolic_mathjax_preprocessor.js create mode 100644 xblocks_contrib/problem/assets/static/js/collapsible.js create mode 100644 xblocks_contrib/problem/assets/static/js/display.js create mode 100644 xblocks_contrib/problem/assets/static/js/imageinput.js create mode 100644 xblocks_contrib/problem/assets/static/js/javascript_loader.js create mode 100644 xblocks_contrib/problem/assets/static/js/schematic.js create mode 100644 xblocks_contrib/problem/assets/static/js/vendor/codemirror-compressed.js create mode 100644 xblocks_contrib/problem/assets/static/js/xmodule.js create mode 100644 xblocks_contrib/problem/capa/__init__.py create mode 100644 xblocks_contrib/problem/capa/capa_problem.py create mode 100755 xblocks_contrib/problem/capa/checker.py create mode 100644 xblocks_contrib/problem/capa/correctmap.py create mode 100644 xblocks_contrib/problem/capa/customrender.py create mode 100644 xblocks_contrib/problem/capa/errors.py create mode 100644 xblocks_contrib/problem/capa/inputtypes.py create mode 100644 xblocks_contrib/problem/capa/registry.py create mode 100644 xblocks_contrib/problem/capa/responsetypes.py create mode 100644 xblocks_contrib/problem/capa/safe_exec/README.rst create mode 100644 xblocks_contrib/problem/capa/safe_exec/__init__.py create mode 100644 xblocks_contrib/problem/capa/safe_exec/exceptions.py create mode 100644 xblocks_contrib/problem/capa/safe_exec/lazymod.py create mode 100644 xblocks_contrib/problem/capa/safe_exec/remote_exec.py create mode 100644 xblocks_contrib/problem/capa/safe_exec/safe_exec.py create mode 100644 xblocks_contrib/problem/capa/safe_exec/tests/__init__.py create mode 100644 xblocks_contrib/problem/capa/safe_exec/tests/test_files/pylib/constant.py create mode 100644 xblocks_contrib/problem/capa/safe_exec/tests/test_lazymod.py create mode 100644 xblocks_contrib/problem/capa/safe_exec/tests/test_remote_exec.py create mode 100644 xblocks_contrib/problem/capa/safe_exec/tests/test_safe_exec.py create mode 100644 xblocks_contrib/problem/capa/templates/annotationinput.html create mode 100644 xblocks_contrib/problem/capa/templates/chemicalequationinput.html create mode 100644 xblocks_contrib/problem/capa/templates/choicegroup.html create mode 100644 xblocks_contrib/problem/capa/templates/choicetext.html create mode 100644 xblocks_contrib/problem/capa/templates/clarification.html create mode 100644 xblocks_contrib/problem/capa/templates/codeinput.html create mode 100644 xblocks_contrib/problem/capa/templates/crystallography.html create mode 100644 xblocks_contrib/problem/capa/templates/designprotein2dinput.html create mode 100644 xblocks_contrib/problem/capa/templates/drag_and_drop_input.html create mode 100644 xblocks_contrib/problem/capa/templates/editageneinput.html create mode 100644 xblocks_contrib/problem/capa/templates/editamolecule.html create mode 100644 xblocks_contrib/problem/capa/templates/filesubmission.html create mode 100644 xblocks_contrib/problem/capa/templates/formulaequationinput.html create mode 100644 xblocks_contrib/problem/capa/templates/imageinput.html create mode 100644 xblocks_contrib/problem/capa/templates/jsinput.html create mode 100644 xblocks_contrib/problem/capa/templates/mathstring.html create mode 100644 xblocks_contrib/problem/capa/templates/matlabinput.html create mode 100644 xblocks_contrib/problem/capa/templates/optioninput.html create mode 100644 xblocks_contrib/problem/capa/templates/problem.html create mode 100644 xblocks_contrib/problem/capa/templates/problem_ajax.html create mode 100644 xblocks_contrib/problem/capa/templates/problem_notifications.html create mode 100644 xblocks_contrib/problem/capa/templates/schematicinput.html create mode 100644 xblocks_contrib/problem/capa/templates/solutionspan.html create mode 100644 xblocks_contrib/problem/capa/templates/status_span.html create mode 100644 xblocks_contrib/problem/capa/templates/textline.html create mode 100644 xblocks_contrib/problem/capa/templates/vsepr_input.html create mode 100644 xblocks_contrib/problem/capa/tests/__init__.py create mode 100644 xblocks_contrib/problem/capa/tests/helpers.py create mode 100644 xblocks_contrib/problem/capa/tests/response_xml_factory.py create mode 100644 xblocks_contrib/problem/capa/tests/test_answer_pool.py create mode 100644 xblocks_contrib/problem/capa/tests/test_capa_problem.py create mode 100644 xblocks_contrib/problem/capa/tests/test_correctmap.py create mode 100644 xblocks_contrib/problem/capa/tests/test_customrender.py create mode 100644 xblocks_contrib/problem/capa/tests/test_errors.py create mode 100644 xblocks_contrib/problem/capa/tests/test_files/dynamath_input.txt create mode 100644 xblocks_contrib/problem/capa/tests/test_files/extended_hints.xml create mode 100644 xblocks_contrib/problem/capa/tests/test_files/extended_hints_checkbox.xml create mode 100644 xblocks_contrib/problem/capa/tests/test_files/extended_hints_dropdown.xml create mode 100644 xblocks_contrib/problem/capa/tests/test_files/extended_hints_multiple_choice.xml create mode 100644 xblocks_contrib/problem/capa/tests/test_files/extended_hints_multiple_choice_with_html.xml create mode 100644 xblocks_contrib/problem/capa/tests/test_files/extended_hints_numeric_input.xml create mode 100644 xblocks_contrib/problem/capa/tests/test_files/extended_hints_text_input.xml create mode 100644 xblocks_contrib/problem/capa/tests/test_files/extended_hints_with_errors.xml create mode 100644 xblocks_contrib/problem/capa/tests/test_files/filename_convert_test.txt create mode 100644 xblocks_contrib/problem/capa/tests/test_files/js/mersenne-twister-min.js create mode 100644 xblocks_contrib/problem/capa/tests/test_files/js/test_problem_display.js create mode 100644 xblocks_contrib/problem/capa/tests/test_files/js/test_problem_generator.js create mode 100644 xblocks_contrib/problem/capa/tests/test_files/js/test_problem_grader.js create mode 100644 xblocks_contrib/problem/capa/tests/test_files/js/xproblem.js create mode 100644 xblocks_contrib/problem/capa/tests/test_files/snuggletex_2x+3y.xml create mode 100644 xblocks_contrib/problem/capa/tests/test_files/snuggletex_correct.html create mode 100644 xblocks_contrib/problem/capa/tests/test_files/snuggletex_wrong.html create mode 100644 xblocks_contrib/problem/capa/tests/test_files/snuggletex_x+x+3y.xml create mode 100644 xblocks_contrib/problem/capa/tests/test_files/targeted_feedback.xml create mode 100644 xblocks_contrib/problem/capa/tests/test_files/targeted_feedback_multiple.xml create mode 100644 xblocks_contrib/problem/capa/tests/test_hint_functionality.py create mode 100644 xblocks_contrib/problem/capa/tests/test_html_render.py create mode 100644 xblocks_contrib/problem/capa/tests/test_input_templates.py create mode 100644 xblocks_contrib/problem/capa/tests/test_inputtypes.py create mode 100644 xblocks_contrib/problem/capa/tests/test_responsetypes.py create mode 100644 xblocks_contrib/problem/capa/tests/test_shuffle.py create mode 100644 xblocks_contrib/problem/capa/tests/test_targeted_feedback.py create mode 100644 xblocks_contrib/problem/capa/tests/test_util.py create mode 100644 xblocks_contrib/problem/capa/tests/test_xqueue_interface.py create mode 100644 xblocks_contrib/problem/capa/tests/test_xqueue_submission.py create mode 100644 xblocks_contrib/problem/capa/util.py create mode 100644 xblocks_contrib/problem/capa/xqueue_interface.py create mode 100644 xblocks_contrib/problem/capa/xqueue_submission.py create mode 100644 xblocks_contrib/problem/capa_block.py create mode 100644 xblocks_contrib/problem/markup.py delete mode 100644 xblocks_contrib/problem/problem.py delete mode 100644 xblocks_contrib/problem/static/css/problem.css delete mode 100644 xblocks_contrib/problem/static/js/src/problem.js create mode 100644 xblocks_contrib/problem/stringify.py delete mode 100644 xblocks_contrib/problem/templates/problem.html create mode 100644 xblocks_contrib/problem/tests/data/capa/prog1.py create mode 100644 xblocks_contrib/problem/tests/data/capa/prog2.py create mode 100644 xblocks_contrib/problem/tests/data/capa/prog3.py create mode 100644 xblocks_contrib/problem/tests/test_capa_block.py delete mode 100644 xblocks_contrib/problem/tests/test_problem.py create mode 100644 xblocks_contrib/problem/tests/test_stringify.py create mode 100644 xblocks_contrib/problem/xmlparser.py diff --git a/requirements/base.in b/requirements/base.in index ed641fdc..22557c64 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -1,10 +1,23 @@ # Core requirements for using this application -c constraints.txt +beautifulsoup4 +chem +ddt +defusedxml django-statici18n +edx-codejail edx-i18n-tools edx-opaque-keys +edx-submissions +edx-toggles +html5lib nh3 +numpy oauthlib +openedx-calc openedx-django-pyfs +pillow +random2 +shapely XBlock diff --git a/requirements/base.txt b/requirements/base.txt index f98c619d..96c65baf 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -8,29 +8,77 @@ appdirs==1.4.4 # via fs asgiref==3.11.0 # via django -boto3==1.42.34 +beautifulsoup4==4.14.3 + # via -r requirements/base.in +boto3==1.42.39 # via fs-s3fs -botocore==1.42.34 +botocore==1.42.39 # via # boto3 # s3transfer +cffi==2.0.0 + # via pynacl +chem==2.0.0 + # via -r requirements/base.in +click==8.3.1 + # via + # code-annotations + # edx-django-utils + # nltk +code-annotations==2.3.0 + # via edx-toggles +ddt==1.7.2 + # via -r requirements/base.in +defusedxml==0.7.1 + # via -r requirements/base.in django==5.2.10 # via # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # django-appconf + # django-crum + # django-model-utils # django-statici18n + # django-waffle + # djangorestframework + # edx-django-release-util + # edx-django-utils # edx-i18n-tools + # edx-submissions + # edx-toggles + # jsonfield # openedx-django-pyfs django-appconf==1.2.0 # via django-statici18n +django-crum==0.7.9 + # via + # edx-django-utils + # edx-toggles +django-model-utils==5.0.0 + # via edx-submissions django-statici18n==2.6.0 # via -r requirements/base.in +django-waffle==5.0.0 + # via + # edx-django-utils + # edx-toggles +djangorestframework==3.16.1 + # via edx-submissions dnspython==2.8.0 # via pymongo +edx-codejail==4.1.0 + # via -r requirements/base.in +edx-django-release-util==1.5.0 + # via edx-submissions +edx-django-utils==8.0.1 + # via edx-toggles edx-i18n-tools==1.9.0 # via -r requirements/base.in edx-opaque-keys==3.0.0 # via -r requirements/base.in +edx-submissions==3.12.2 + # via -r requirements/base.in +edx-toggles==5.4.1 + # via -r requirements/base.in fs==2.4.16 # via # fs-s3fs @@ -38,14 +86,23 @@ fs==2.4.16 # xblock fs-s3fs==1.1.1 # via openedx-django-pyfs +html5lib==1.1 + # via -r requirements/base.in +jinja2==3.1.6 + # via code-annotations jmespath==1.1.0 # via # boto3 # botocore +joblib==1.5.3 + # via nltk +jsonfield==3.2.0 + # via edx-submissions lxml[html-clean]==6.0.2 # via # edx-i18n-tools # lxml-html-clean + # openedx-calc # xblock lxml-html-clean==0.4.3 # via lxml @@ -53,49 +110,109 @@ mako==1.3.10 # via xblock markupsafe==3.0.3 # via + # chem + # jinja2 # mako + # openedx-calc # xblock +mpmath==1.3.0 + # via sympy nh3==0.3.2 # via -r requirements/base.in +nltk==3.9.2 + # via chem +numpy==2.4.2 + # via + # -r requirements/base.in + # chem + # openedx-calc + # scipy + # shapely oauthlib==3.3.1 # via -r requirements/base.in +openedx-calc==4.0.3 + # via -r requirements/base.in openedx-django-pyfs==3.8.0 # via -r requirements/base.in path==16.16.0 # via edx-i18n-tools +pillow==12.1.0 + # via -r requirements/base.in polib==1.2.0 # via edx-i18n-tools +psutil==7.2.2 + # via edx-django-utils +pycparser==3.0 + # via cffi pymongo==4.16.0 # via edx-opaque-keys +pynacl==1.6.2 + # via edx-django-utils +pyparsing==3.3.2 + # via + # chem + # openedx-calc python-dateutil==2.9.0.post0 # via # botocore # xblock +python-slugify==8.0.4 + # via code-annotations pytz==2025.2 - # via xblock + # via + # edx-submissions + # xblock pyyaml==6.0.3 # via + # code-annotations + # edx-django-release-util # edx-i18n-tools # xblock +random2==1.0.2 + # via -r requirements/base.in +regex==2026.1.15 + # via nltk s3transfer==0.16.0 # via boto3 +scipy==1.17.0 + # via chem +shapely==2.1.2 + # via -r requirements/base.in simplejson==3.20.2 # via xblock six==1.17.0 # via + # edx-codejail + # edx-django-release-util # fs # fs-s3fs + # html5lib # python-dateutil +soupsieve==2.8.3 + # via beautifulsoup4 sqlparse==0.5.5 # via django stevedore==5.6.0 - # via edx-opaque-keys + # via + # code-annotations + # edx-django-utils + # edx-opaque-keys +sympy==1.14.0 + # via openedx-calc +text-unidecode==1.3 + # via python-slugify +tqdm==4.67.2 + # via nltk typing-extensions==4.15.0 - # via edx-opaque-keys + # via + # beautifulsoup4 + # edx-opaque-keys urllib3==2.6.3 # via botocore web-fragments==3.1.0 # via xblock +webencodings==0.5.1 + # via html5lib webob==1.8.9 # via xblock xblock==5.3.0 diff --git a/requirements/dev.txt b/requirements/dev.txt index b90adc1d..84fc4a9d 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -24,17 +24,21 @@ astroid==4.0.3 # -r requirements/quality.txt # pylint # pylint-celery +beautifulsoup4==4.14.3 + # via + # -r requirements/quality.txt + # -r requirements/test.txt binaryornot==0.4.4 # via # -r requirements/quality.txt # -r requirements/test.txt # cookiecutter -boto3==1.42.34 +boto3==1.42.39 # via # -r requirements/quality.txt # -r requirements/test.txt # fs-s3fs -botocore==1.42.34 +botocore==1.42.39 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -44,7 +48,7 @@ build==1.4.0 # via # -r requirements/pip-tools.txt # pip-tools -cachetools==6.2.5 +cachetools==7.0.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -54,6 +58,11 @@ certifi==2026.1.4 # -r requirements/quality.txt # -r requirements/test.txt # requests +cffi==2.0.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # pynacl chardet==5.2.0 # via # -r requirements/quality.txt @@ -66,6 +75,10 @@ charset-normalizer==3.4.4 # -r requirements/quality.txt # -r requirements/test.txt # requests +chem==2.0.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt click==8.3.1 # via # -r requirements/pip-tools.txt @@ -74,7 +87,9 @@ click==8.3.1 # click-log # code-annotations # cookiecutter + # edx-django-utils # edx-lint + # nltk # pip-tools click-log==0.4.0 # via @@ -85,6 +100,7 @@ code-annotations==2.3.0 # -r requirements/quality.txt # -r requirements/test.txt # edx-lint + # edx-toggles colorama==0.4.6 # via # -r requirements/quality.txt @@ -104,6 +120,10 @@ ddt==1.7.2 # via # -r requirements/quality.txt # -r requirements/test.txt +defusedxml==0.7.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt diff-cover==10.2.0 # via -r requirements/dev.in dill==0.4.1 @@ -121,8 +141,17 @@ django==5.2.10 # -r requirements/quality.txt # -r requirements/test.txt # django-appconf + # django-crum + # django-model-utils # django-statici18n + # django-waffle + # djangorestframework + # edx-django-release-util + # edx-django-utils # edx-i18n-tools + # edx-submissions + # edx-toggles + # jsonfield # openedx-django-pyfs # xblock-sdk django-appconf==1.2.0 @@ -130,15 +159,51 @@ django-appconf==1.2.0 # -r requirements/quality.txt # -r requirements/test.txt # django-statici18n +django-crum==0.7.9 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-django-utils + # edx-toggles +django-model-utils==5.0.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-submissions django-statici18n==2.6.0 # via # -r requirements/quality.txt # -r requirements/test.txt +django-waffle==5.0.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-django-utils + # edx-toggles +djangorestframework==3.16.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-submissions dnspython==2.8.0 # via # -r requirements/quality.txt # -r requirements/test.txt # pymongo +edx-codejail==4.1.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt +edx-django-release-util==1.5.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-submissions +edx-django-utils==8.0.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-toggles edx-i18n-tools==1.9.0 # via # -r requirements/dev.in @@ -150,6 +215,14 @@ edx-opaque-keys==3.0.0 # via # -r requirements/quality.txt # -r requirements/test.txt +edx-submissions==3.12.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt +edx-toggles==5.4.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt filelock==3.20.3 # via # -r requirements/quality.txt @@ -169,6 +242,10 @@ fs-s3fs==1.1.1 # -r requirements/test.txt # openedx-django-pyfs # xblock-sdk +html5lib==1.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt idna==3.11 # via # -r requirements/quality.txt @@ -196,12 +273,23 @@ jmespath==1.1.0 # -r requirements/test.txt # boto3 # botocore +joblib==1.5.3 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # nltk +jsonfield==3.2.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-submissions lxml[html-clean]==6.0.2 # via # -r requirements/quality.txt # -r requirements/test.txt # edx-i18n-tools # lxml-html-clean + # openedx-calc # xblock # xblock-sdk lxml-html-clean==0.4.3 @@ -223,8 +311,10 @@ markupsafe==3.0.3 # via # -r requirements/quality.txt # -r requirements/test.txt + # chem # jinja2 # mako + # openedx-calc # xblock mccabe==0.7.0 # via @@ -235,14 +325,36 @@ mdurl==0.1.2 # -r requirements/quality.txt # -r requirements/test.txt # markdown-it-py +mpmath==1.3.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # sympy nh3==0.3.2 # via # -r requirements/quality.txt # -r requirements/test.txt +nltk==3.9.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # chem +numpy==2.4.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # chem + # openedx-calc + # scipy + # shapely oauthlib==3.3.1 # via # -r requirements/quality.txt # -r requirements/test.txt +openedx-calc==4.0.3 + # via + # -r requirements/quality.txt + # -r requirements/test.txt openedx-django-pyfs==3.8.0 # via # -r requirements/quality.txt @@ -262,6 +374,10 @@ path==16.16.0 # -r requirements/quality.txt # -r requirements/test.txt # edx-i18n-tools +pillow==12.1.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt pip-tools==7.5.2 # via -r requirements/pip-tools.txt platformdirs==4.5.1 @@ -284,8 +400,18 @@ polib==1.2.0 # -r requirements/quality.txt # -r requirements/test.txt # edx-i18n-tools +psutil==7.2.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-django-utils pycodestyle==2.14.0 # via -r requirements/quality.txt +pycparser==3.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # cffi pydocstyle==6.3.0 # via -r requirements/quality.txt pygments==2.19.2 @@ -320,6 +446,17 @@ pymongo==4.16.0 # -r requirements/quality.txt # -r requirements/test.txt # edx-opaque-keys +pynacl==1.6.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-django-utils +pyparsing==3.3.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # chem + # openedx-calc pypng==0.20220715.0 # via # -r requirements/quality.txt @@ -366,6 +503,7 @@ pytz==2025.2 # via # -r requirements/quality.txt # -r requirements/test.txt + # edx-submissions # xblock pyyaml==6.0.3 # via @@ -373,15 +511,25 @@ pyyaml==6.0.3 # -r requirements/test.txt # code-annotations # cookiecutter + # edx-django-release-util # edx-i18n-tools # xblock +random2==1.0.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt +regex==2026.1.15 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # nltk requests==2.32.5 # via # -r requirements/quality.txt # -r requirements/test.txt # cookiecutter # xblock-sdk -rich==14.3.1 +rich==14.3.2 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -391,6 +539,15 @@ s3transfer==0.16.0 # -r requirements/quality.txt # -r requirements/test.txt # boto3 +scipy==1.17.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # chem +shapely==2.1.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt simplejson==3.20.2 # via # -r requirements/quality.txt @@ -401,14 +558,22 @@ six==1.17.0 # via # -r requirements/quality.txt # -r requirements/test.txt + # edx-codejail + # edx-django-release-util # edx-lint # fs # fs-s3fs + # html5lib # python-dateutil snowballstemmer==3.0.1 # via # -r requirements/quality.txt # pydocstyle +soupsieve==2.8.3 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # beautifulsoup4 sqlparse==0.5.5 # via # -r requirements/quality.txt @@ -419,7 +584,13 @@ stevedore==5.6.0 # -r requirements/quality.txt # -r requirements/test.txt # code-annotations + # edx-django-utils # edx-opaque-keys +sympy==1.14.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # openedx-calc text-unidecode==1.3 # via # -r requirements/quality.txt @@ -433,10 +604,16 @@ tox==4.34.1 # via # -r requirements/quality.txt # -r requirements/test.txt +tqdm==4.67.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # nltk typing-extensions==4.15.0 # via # -r requirements/quality.txt # -r requirements/test.txt + # beautifulsoup4 # edx-opaque-keys tzdata==2025.3 # via @@ -460,6 +637,11 @@ web-fragments==3.1.0 # -r requirements/test.txt # xblock # xblock-sdk +webencodings==0.5.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # html5lib webob==1.8.9 # via # -r requirements/quality.txt diff --git a/requirements/doc.txt b/requirements/doc.txt index 9ddd94a5..6dd742a5 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -20,30 +20,32 @@ asgiref==3.11.0 # via # -r requirements/test.txt # django -babel==2.17.0 +babel==2.18.0 # via # pydata-sphinx-theme # sphinx backports-tarfile==1.2.0 # via jaraco-context beautifulsoup4==4.14.3 - # via pydata-sphinx-theme + # via + # -r requirements/test.txt + # pydata-sphinx-theme binaryornot==0.4.4 # via # -r requirements/test.txt # cookiecutter -boto3==1.42.34 +boto3==1.42.39 # via # -r requirements/test.txt # fs-s3fs -botocore==1.42.34 +botocore==1.42.39 # via # -r requirements/test.txt # boto3 # s3transfer build==1.4.0 # via -r requirements/doc.in -cachetools==6.2.5 +cachetools==7.0.0 # via # -r requirements/test.txt # tox @@ -52,7 +54,9 @@ certifi==2026.1.4 # -r requirements/test.txt # requests cffi==2.0.0 - # via cryptography + # via + # -r requirements/test.txt + # pynacl chardet==5.2.0 # via # -r requirements/test.txt @@ -62,13 +66,19 @@ charset-normalizer==3.4.4 # via # -r requirements/test.txt # requests +chem==2.0.0 + # via -r requirements/test.txt click==8.3.1 # via # -r requirements/test.txt # code-annotations # cookiecutter + # edx-django-utils + # nltk code-annotations==2.3.0 - # via -r requirements/test.txt + # via + # -r requirements/test.txt + # edx-toggles colorama==0.4.6 # via # -r requirements/test.txt @@ -81,10 +91,10 @@ coverage[toml]==7.13.2 # via # -r requirements/test.txt # pytest-cov -cryptography==46.0.3 - # via secretstorage ddt==1.7.2 # via -r requirements/test.txt +defusedxml==0.7.1 + # via -r requirements/test.txt distlib==0.4.0 # via # -r requirements/test.txt @@ -94,16 +104,43 @@ django==5.2.10 # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # -r requirements/test.txt # django-appconf + # django-crum + # django-model-utils # django-statici18n + # django-waffle + # djangorestframework + # edx-django-release-util + # edx-django-utils # edx-i18n-tools + # edx-submissions + # edx-toggles + # jsonfield # openedx-django-pyfs # xblock-sdk django-appconf==1.2.0 # via # -r requirements/test.txt # django-statici18n +django-crum==0.7.9 + # via + # -r requirements/test.txt + # edx-django-utils + # edx-toggles +django-model-utils==5.0.0 + # via + # -r requirements/test.txt + # edx-submissions django-statici18n==2.6.0 # via -r requirements/test.txt +django-waffle==5.0.0 + # via + # -r requirements/test.txt + # edx-django-utils + # edx-toggles +djangorestframework==3.16.1 + # via + # -r requirements/test.txt + # edx-submissions dnspython==2.8.0 # via # -r requirements/test.txt @@ -117,10 +154,24 @@ docutils==0.21.2 # readme-renderer # restructuredtext-lint # sphinx +edx-codejail==4.1.0 + # via -r requirements/test.txt +edx-django-release-util==1.5.0 + # via + # -r requirements/test.txt + # edx-submissions +edx-django-utils==8.0.1 + # via + # -r requirements/test.txt + # edx-toggles edx-i18n-tools==1.9.0 # via -r requirements/test.txt edx-opaque-keys==3.0.0 # via -r requirements/test.txt +edx-submissions==3.12.2 + # via -r requirements/test.txt +edx-toggles==5.4.1 + # via -r requirements/test.txt filelock==3.20.3 # via # -r requirements/test.txt @@ -137,6 +188,8 @@ fs-s3fs==1.1.1 # -r requirements/test.txt # openedx-django-pyfs # xblock-sdk +html5lib==1.1 + # via -r requirements/test.txt id==1.5.0 # via twine idna==3.11 @@ -157,10 +210,6 @@ jaraco-context==6.1.0 # via keyring jaraco-functools==4.4.0 # via keyring -jeepney==0.9.0 - # via - # keyring - # secretstorage jinja2==3.1.6 # via # -r requirements/test.txt @@ -172,6 +221,14 @@ jmespath==1.1.0 # -r requirements/test.txt # boto3 # botocore +joblib==1.5.3 + # via + # -r requirements/test.txt + # nltk +jsonfield==3.2.0 + # via + # -r requirements/test.txt + # edx-submissions keyring==25.7.0 # via twine lxml[html-clean]==6.0.2 @@ -179,6 +236,7 @@ lxml[html-clean]==6.0.2 # -r requirements/test.txt # edx-i18n-tools # lxml-html-clean + # openedx-calc # xblock # xblock-sdk lxml-html-clean==0.4.3 @@ -196,8 +254,10 @@ markdown-it-py==4.0.0 markupsafe==3.0.3 # via # -r requirements/test.txt + # chem # jinja2 # mako + # openedx-calc # xblock mdurl==0.1.2 # via @@ -207,12 +267,29 @@ more-itertools==10.8.0 # via # jaraco-classes # jaraco-functools +mpmath==1.3.0 + # via + # -r requirements/test.txt + # sympy nh3==0.3.2 # via # -r requirements/test.txt # readme-renderer +nltk==3.9.2 + # via + # -r requirements/test.txt + # chem +numpy==2.4.2 + # via + # -r requirements/test.txt + # chem + # openedx-calc + # scipy + # shapely oauthlib==3.3.1 # via -r requirements/test.txt +openedx-calc==4.0.3 + # via -r requirements/test.txt openedx-django-pyfs==3.8.0 # via -r requirements/test.txt packaging==26.0 @@ -229,6 +306,8 @@ path==16.16.0 # via # -r requirements/test.txt # edx-i18n-tools +pillow==12.1.0 + # via -r requirements/test.txt platformdirs==4.5.1 # via # -r requirements/test.txt @@ -244,8 +323,14 @@ polib==1.2.0 # via # -r requirements/test.txt # edx-i18n-tools +psutil==7.2.2 + # via + # -r requirements/test.txt + # edx-django-utils pycparser==3.0 - # via cffi + # via + # -r requirements/test.txt + # cffi pydata-sphinx-theme==0.15.4 # via sphinx-book-theme pygments==2.19.2 @@ -262,6 +347,15 @@ pymongo==4.16.0 # via # -r requirements/test.txt # edx-opaque-keys +pynacl==1.6.2 + # via + # -r requirements/test.txt + # edx-django-utils +pyparsing==3.3.2 + # via + # -r requirements/test.txt + # chem + # openedx-calc pypng==0.20220715.0 # via # -r requirements/test.txt @@ -295,16 +389,24 @@ python-slugify==8.0.4 pytz==2025.2 # via # -r requirements/test.txt + # edx-submissions # xblock pyyaml==6.0.3 # via # -r requirements/test.txt # code-annotations # cookiecutter + # edx-django-release-util # edx-i18n-tools # xblock +random2==1.0.2 + # via -r requirements/test.txt readme-renderer==44.0 # via twine +regex==2026.1.15 + # via + # -r requirements/test.txt + # nltk requests==2.32.5 # via # -r requirements/test.txt @@ -320,7 +422,7 @@ restructuredtext-lint==2.0.2 # via doc8 rfc3986==2.0.0 # via twine -rich==14.3.1 +rich==14.3.2 # via # -r requirements/test.txt # cookiecutter @@ -331,8 +433,12 @@ s3transfer==0.16.0 # via # -r requirements/test.txt # boto3 -secretstorage==3.5.0 - # via keyring +scipy==1.17.0 + # via + # -r requirements/test.txt + # chem +shapely==2.1.2 + # via -r requirements/test.txt simplejson==3.20.2 # via # -r requirements/test.txt @@ -341,13 +447,18 @@ simplejson==3.20.2 six==1.17.0 # via # -r requirements/test.txt + # edx-codejail + # edx-django-release-util # fs # fs-s3fs + # html5lib # python-dateutil snowballstemmer==3.0.1 # via sphinx soupsieve==2.8.3 - # via beautifulsoup4 + # via + # -r requirements/test.txt + # beautifulsoup4 sphinx==9.0.4 # via # -r requirements/doc.in @@ -376,13 +487,22 @@ stevedore==5.6.0 # -r requirements/test.txt # code-annotations # doc8 + # edx-django-utils # edx-opaque-keys +sympy==1.14.0 + # via + # -r requirements/test.txt + # openedx-calc text-unidecode==1.3 # via # -r requirements/test.txt # python-slugify tox==4.34.1 # via -r requirements/test.txt +tqdm==4.67.2 + # via + # -r requirements/test.txt + # nltk twine==6.2.0 # via -r requirements/doc.in typing-extensions==4.15.0 @@ -410,6 +530,10 @@ web-fragments==3.1.0 # -r requirements/test.txt # xblock # xblock-sdk +webencodings==0.5.1 + # via + # -r requirements/test.txt + # html5lib webob==1.8.9 # via # -r requirements/test.txt diff --git a/requirements/quality.txt b/requirements/quality.txt index e8a2f22d..6cb73a0e 100644 --- a/requirements/quality.txt +++ b/requirements/quality.txt @@ -20,20 +20,22 @@ astroid==4.0.3 # via # pylint # pylint-celery +beautifulsoup4==4.14.3 + # via -r requirements/test.txt binaryornot==0.4.4 # via # -r requirements/test.txt # cookiecutter -boto3==1.42.34 +boto3==1.42.39 # via # -r requirements/test.txt # fs-s3fs -botocore==1.42.34 +botocore==1.42.39 # via # -r requirements/test.txt # boto3 # s3transfer -cachetools==6.2.5 +cachetools==7.0.0 # via # -r requirements/test.txt # tox @@ -41,6 +43,10 @@ certifi==2026.1.4 # via # -r requirements/test.txt # requests +cffi==2.0.0 + # via + # -r requirements/test.txt + # pynacl chardet==5.2.0 # via # -r requirements/test.txt @@ -50,19 +56,24 @@ charset-normalizer==3.4.4 # via # -r requirements/test.txt # requests +chem==2.0.0 + # via -r requirements/test.txt click==8.3.1 # via # -r requirements/test.txt # click-log # code-annotations # cookiecutter + # edx-django-utils # edx-lint + # nltk click-log==0.4.0 # via edx-lint code-annotations==2.3.0 # via # -r requirements/test.txt # edx-lint + # edx-toggles colorama==0.4.6 # via # -r requirements/test.txt @@ -77,6 +88,8 @@ coverage[toml]==7.13.2 # pytest-cov ddt==1.7.2 # via -r requirements/test.txt +defusedxml==0.7.1 + # via -r requirements/test.txt dill==0.4.1 # via pylint distlib==0.4.0 @@ -88,26 +101,67 @@ django==5.2.10 # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # -r requirements/test.txt # django-appconf + # django-crum + # django-model-utils # django-statici18n + # django-waffle + # djangorestframework + # edx-django-release-util + # edx-django-utils # edx-i18n-tools + # edx-submissions + # edx-toggles + # jsonfield # openedx-django-pyfs # xblock-sdk django-appconf==1.2.0 # via # -r requirements/test.txt # django-statici18n +django-crum==0.7.9 + # via + # -r requirements/test.txt + # edx-django-utils + # edx-toggles +django-model-utils==5.0.0 + # via + # -r requirements/test.txt + # edx-submissions django-statici18n==2.6.0 # via -r requirements/test.txt +django-waffle==5.0.0 + # via + # -r requirements/test.txt + # edx-django-utils + # edx-toggles +djangorestframework==3.16.1 + # via + # -r requirements/test.txt + # edx-submissions dnspython==2.8.0 # via # -r requirements/test.txt # pymongo +edx-codejail==4.1.0 + # via -r requirements/test.txt +edx-django-release-util==1.5.0 + # via + # -r requirements/test.txt + # edx-submissions +edx-django-utils==8.0.1 + # via + # -r requirements/test.txt + # edx-toggles edx-i18n-tools==1.9.0 # via -r requirements/test.txt edx-lint==5.6.0 # via -r requirements/quality.in edx-opaque-keys==3.0.0 # via -r requirements/test.txt +edx-submissions==3.12.2 + # via -r requirements/test.txt +edx-toggles==5.4.1 + # via -r requirements/test.txt filelock==3.20.3 # via # -r requirements/test.txt @@ -124,6 +178,8 @@ fs-s3fs==1.1.1 # -r requirements/test.txt # openedx-django-pyfs # xblock-sdk +html5lib==1.1 + # via -r requirements/test.txt idna==3.11 # via # -r requirements/test.txt @@ -146,11 +202,20 @@ jmespath==1.1.0 # -r requirements/test.txt # boto3 # botocore +joblib==1.5.3 + # via + # -r requirements/test.txt + # nltk +jsonfield==3.2.0 + # via + # -r requirements/test.txt + # edx-submissions lxml[html-clean]==6.0.2 # via # -r requirements/test.txt # edx-i18n-tools # lxml-html-clean + # openedx-calc # xblock # xblock-sdk lxml-html-clean==0.4.3 @@ -168,8 +233,10 @@ markdown-it-py==4.0.0 markupsafe==3.0.3 # via # -r requirements/test.txt + # chem # jinja2 # mako + # openedx-calc # xblock mccabe==0.7.0 # via pylint @@ -177,10 +244,27 @@ mdurl==0.1.2 # via # -r requirements/test.txt # markdown-it-py +mpmath==1.3.0 + # via + # -r requirements/test.txt + # sympy nh3==0.3.2 # via -r requirements/test.txt +nltk==3.9.2 + # via + # -r requirements/test.txt + # chem +numpy==2.4.2 + # via + # -r requirements/test.txt + # chem + # openedx-calc + # scipy + # shapely oauthlib==3.3.1 # via -r requirements/test.txt +openedx-calc==4.0.3 + # via -r requirements/test.txt openedx-django-pyfs==3.8.0 # via -r requirements/test.txt packaging==26.0 @@ -193,6 +277,8 @@ path==16.16.0 # via # -r requirements/test.txt # edx-i18n-tools +pillow==12.1.0 + # via -r requirements/test.txt platformdirs==4.5.1 # via # -r requirements/test.txt @@ -209,8 +295,16 @@ polib==1.2.0 # via # -r requirements/test.txt # edx-i18n-tools +psutil==7.2.2 + # via + # -r requirements/test.txt + # edx-django-utils pycodestyle==2.14.0 # via -r requirements/quality.in +pycparser==3.0 + # via + # -r requirements/test.txt + # cffi pydocstyle==6.3.0 # via -r requirements/quality.in pygments==2.19.2 @@ -236,6 +330,15 @@ pymongo==4.16.0 # via # -r requirements/test.txt # edx-opaque-keys +pynacl==1.6.2 + # via + # -r requirements/test.txt + # edx-django-utils +pyparsing==3.3.2 + # via + # -r requirements/test.txt + # chem + # openedx-calc pypng==0.20220715.0 # via # -r requirements/test.txt @@ -267,20 +370,28 @@ python-slugify==8.0.4 pytz==2025.2 # via # -r requirements/test.txt + # edx-submissions # xblock pyyaml==6.0.3 # via # -r requirements/test.txt # code-annotations # cookiecutter + # edx-django-release-util # edx-i18n-tools # xblock +random2==1.0.2 + # via -r requirements/test.txt +regex==2026.1.15 + # via + # -r requirements/test.txt + # nltk requests==2.32.5 # via # -r requirements/test.txt # cookiecutter # xblock-sdk -rich==14.3.1 +rich==14.3.2 # via # -r requirements/test.txt # cookiecutter @@ -288,6 +399,12 @@ s3transfer==0.16.0 # via # -r requirements/test.txt # boto3 +scipy==1.17.0 + # via + # -r requirements/test.txt + # chem +shapely==2.1.2 + # via -r requirements/test.txt simplejson==3.20.2 # via # -r requirements/test.txt @@ -296,12 +413,19 @@ simplejson==3.20.2 six==1.17.0 # via # -r requirements/test.txt + # edx-codejail + # edx-django-release-util # edx-lint # fs # fs-s3fs + # html5lib # python-dateutil snowballstemmer==3.0.1 # via pydocstyle +soupsieve==2.8.3 + # via + # -r requirements/test.txt + # beautifulsoup4 sqlparse==0.5.5 # via # -r requirements/test.txt @@ -310,7 +434,12 @@ stevedore==5.6.0 # via # -r requirements/test.txt # code-annotations + # edx-django-utils # edx-opaque-keys +sympy==1.14.0 + # via + # -r requirements/test.txt + # openedx-calc text-unidecode==1.3 # via # -r requirements/test.txt @@ -319,9 +448,14 @@ tomlkit==0.14.0 # via pylint tox==4.34.1 # via -r requirements/test.txt +tqdm==4.67.2 + # via + # -r requirements/test.txt + # nltk typing-extensions==4.15.0 # via # -r requirements/test.txt + # beautifulsoup4 # edx-opaque-keys tzdata==2025.3 # via @@ -341,6 +475,10 @@ web-fragments==3.1.0 # -r requirements/test.txt # xblock # xblock-sdk +webencodings==0.5.1 + # via + # -r requirements/test.txt + # html5lib webob==1.8.9 # via # -r requirements/test.txt diff --git a/requirements/test.txt b/requirements/test.txt index 15b53e11..ea1287d1 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -14,33 +14,47 @@ asgiref==3.11.0 # via # -r requirements/base.txt # django +beautifulsoup4==4.14.3 + # via -r requirements/base.txt binaryornot==0.4.4 # via cookiecutter -boto3==1.42.34 +boto3==1.42.39 # via # -r requirements/base.txt # fs-s3fs -botocore==1.42.34 +botocore==1.42.39 # via # -r requirements/base.txt # boto3 # s3transfer -cachetools==6.2.5 +cachetools==7.0.0 # via tox certifi==2026.1.4 # via requests +cffi==2.0.0 + # via + # -r requirements/base.txt + # pynacl chardet==5.2.0 # via # binaryornot # tox charset-normalizer==3.4.4 # via requests +chem==2.0.0 + # via -r requirements/base.txt click==8.3.1 # via + # -r requirements/base.txt # code-annotations # cookiecutter + # edx-django-utils + # nltk code-annotations==2.3.0 - # via -r requirements/test.in + # via + # -r requirements/base.txt + # -r requirements/test.in + # edx-toggles colorama==0.4.6 # via tox cookiecutter==2.6.0 @@ -48,33 +62,78 @@ cookiecutter==2.6.0 coverage[toml]==7.13.2 # via pytest-cov ddt==1.7.2 - # via -r requirements/test.in + # via + # -r requirements/base.txt + # -r requirements/test.in +defusedxml==0.7.1 + # via -r requirements/base.txt distlib==0.4.0 # via virtualenv # via # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # -r requirements/base.txt # django-appconf + # django-crum + # django-model-utils # django-statici18n + # django-waffle + # djangorestframework + # edx-django-release-util + # edx-django-utils # edx-i18n-tools + # edx-submissions + # edx-toggles + # jsonfield # openedx-django-pyfs # xblock-sdk django-appconf==1.2.0 # via # -r requirements/base.txt # django-statici18n +django-crum==0.7.9 + # via + # -r requirements/base.txt + # edx-django-utils + # edx-toggles +django-model-utils==5.0.0 + # via + # -r requirements/base.txt + # edx-submissions django-statici18n==2.6.0 # via -r requirements/base.txt +django-waffle==5.0.0 + # via + # -r requirements/base.txt + # edx-django-utils + # edx-toggles +djangorestframework==3.16.1 + # via + # -r requirements/base.txt + # edx-submissions dnspython==2.8.0 # via # -r requirements/base.txt # pymongo +edx-codejail==4.1.0 + # via -r requirements/base.txt +edx-django-release-util==1.5.0 + # via + # -r requirements/base.txt + # edx-submissions +edx-django-utils==8.0.1 + # via + # -r requirements/base.txt + # edx-toggles edx-i18n-tools==1.9.0 # via -r requirements/base.txt edx-opaque-keys==3.0.0 # via # -r requirements/base.txt # -r requirements/test.in +edx-submissions==3.12.2 + # via -r requirements/base.txt +edx-toggles==5.4.1 + # via -r requirements/base.txt filelock==3.20.3 # via # tox @@ -90,12 +149,15 @@ fs-s3fs==1.1.1 # -r requirements/base.txt # openedx-django-pyfs # xblock-sdk +html5lib==1.1 + # via -r requirements/base.txt idna==3.11 # via requests iniconfig==2.3.0 # via pytest jinja2==3.1.6 # via + # -r requirements/base.txt # code-annotations # cookiecutter jmespath==1.1.0 @@ -103,11 +165,20 @@ jmespath==1.1.0 # -r requirements/base.txt # boto3 # botocore +joblib==1.5.3 + # via + # -r requirements/base.txt + # nltk +jsonfield==3.2.0 + # via + # -r requirements/base.txt + # edx-submissions lxml[html-clean]==6.0.2 # via # -r requirements/base.txt # edx-i18n-tools # lxml-html-clean + # openedx-calc # xblock # xblock-sdk lxml-html-clean==0.4.3 @@ -123,15 +194,34 @@ markdown-it-py==4.0.0 markupsafe==3.0.3 # via # -r requirements/base.txt + # chem # jinja2 # mako + # openedx-calc # xblock mdurl==0.1.2 # via markdown-it-py +mpmath==1.3.0 + # via + # -r requirements/base.txt + # sympy nh3==0.3.2 # via -r requirements/base.txt +nltk==3.9.2 + # via + # -r requirements/base.txt + # chem +numpy==2.4.2 + # via + # -r requirements/base.txt + # chem + # openedx-calc + # scipy + # shapely oauthlib==3.3.1 # via -r requirements/base.txt +openedx-calc==4.0.3 + # via -r requirements/base.txt openedx-django-pyfs==3.8.0 # via -r requirements/base.txt packaging==26.0 @@ -143,6 +233,8 @@ path==16.16.0 # via # -r requirements/base.txt # edx-i18n-tools +pillow==12.1.0 + # via -r requirements/base.txt platformdirs==4.5.1 # via # tox @@ -156,6 +248,14 @@ polib==1.2.0 # via # -r requirements/base.txt # edx-i18n-tools +psutil==7.2.2 + # via + # -r requirements/base.txt + # edx-django-utils +pycparser==3.0 + # via + # -r requirements/base.txt + # cffi pygments==2.19.2 # via # pytest @@ -164,6 +264,15 @@ pymongo==4.16.0 # via # -r requirements/base.txt # edx-opaque-keys +pynacl==1.6.2 + # via + # -r requirements/base.txt + # edx-django-utils +pyparsing==3.3.2 + # via + # -r requirements/base.txt + # chem + # openedx-calc pypng==0.20220715.0 # via xblock-sdk pyproject-api==1.10.0 @@ -184,29 +293,44 @@ python-dateutil==2.9.0.post0 # xblock python-slugify==8.0.4 # via + # -r requirements/base.txt # code-annotations # cookiecutter pytz==2025.2 # via # -r requirements/base.txt + # edx-submissions # xblock pyyaml==6.0.3 # via # -r requirements/base.txt # code-annotations # cookiecutter + # edx-django-release-util # edx-i18n-tools # xblock +random2==1.0.2 + # via -r requirements/base.txt +regex==2026.1.15 + # via + # -r requirements/base.txt + # nltk requests==2.32.5 # via # cookiecutter # xblock-sdk -rich==14.3.1 +rich==14.3.2 # via cookiecutter s3transfer==0.16.0 # via # -r requirements/base.txt # boto3 +scipy==1.17.0 + # via + # -r requirements/base.txt + # chem +shapely==2.1.2 + # via -r requirements/base.txt simplejson==3.20.2 # via # -r requirements/base.txt @@ -215,9 +339,16 @@ simplejson==3.20.2 six==1.17.0 # via # -r requirements/base.txt + # edx-codejail + # edx-django-release-util # fs # fs-s3fs + # html5lib # python-dateutil +soupsieve==2.8.3 + # via + # -r requirements/base.txt + # beautifulsoup4 sqlparse==0.5.5 # via # -r requirements/base.txt @@ -226,14 +357,26 @@ stevedore==5.6.0 # via # -r requirements/base.txt # code-annotations + # edx-django-utils # edx-opaque-keys +sympy==1.14.0 + # via + # -r requirements/base.txt + # openedx-calc text-unidecode==1.3 - # via python-slugify + # via + # -r requirements/base.txt + # python-slugify tox==4.34.1 # via -r requirements/test.in +tqdm==4.67.2 + # via + # -r requirements/base.txt + # nltk typing-extensions==4.15.0 # via # -r requirements/base.txt + # beautifulsoup4 # edx-opaque-keys tzdata==2025.3 # via arrow @@ -249,6 +392,10 @@ web-fragments==3.1.0 # -r requirements/base.txt # xblock # xblock-sdk +webencodings==0.5.1 + # via + # -r requirements/base.txt + # html5lib webob==1.8.9 # via # -r requirements/base.txt diff --git a/xblocks_contrib/problem/assets/fixtures/checkbox_problem.html b/xblocks_contrib/problem/assets/fixtures/checkbox_problem.html new file mode 100644 index 00000000..053f9b31 --- /dev/null +++ b/xblocks_contrib/problem/assets/fixtures/checkbox_problem.html @@ -0,0 +1,14 @@ +
+ + + +
diff --git a/xblocks_contrib/problem/assets/fixtures/codeinput_problem.html b/xblocks_contrib/problem/assets/fixtures/codeinput_problem.html new file mode 100644 index 00000000..904cdce0 --- /dev/null +++ b/xblocks_contrib/problem/assets/fixtures/codeinput_problem.html @@ -0,0 +1,20 @@ +
+ + + Press ESC then TAB or click outside of the code editor to exit +
+ + correct + +
+
diff --git a/xblocks_contrib/problem/assets/fixtures/imageinput.html b/xblocks_contrib/problem/assets/fixtures/imageinput.html new file mode 100644 index 00000000..d77f4b8e --- /dev/null +++ b/xblocks_contrib/problem/assets/fixtures/imageinput.html @@ -0,0 +1,30 @@ + + + +
+ +
+
+ +
+
+
+ + + Status: unanswered + +
diff --git a/xblocks_contrib/problem/assets/fixtures/imageinput.underscore b/xblocks_contrib/problem/assets/fixtures/imageinput.underscore new file mode 100644 index 00000000..a797aa44 --- /dev/null +++ b/xblocks_contrib/problem/assets/fixtures/imageinput.underscore @@ -0,0 +1,30 @@ + + + +
+ +
+
+ +
+
+
+ + + Status: unanswered + +
diff --git a/xblocks_contrib/problem/assets/fixtures/jsinput_problem.html b/xblocks_contrib/problem/assets/fixtures/jsinput_problem.html new file mode 100644 index 00000000..1c382ed8 --- /dev/null +++ b/xblocks_contrib/problem/assets/fixtures/jsinput_problem.html @@ -0,0 +1,60 @@ +

Custom Javascript Display and Grading

+
+
+ +
+
+ + +
+
+
+ +
+
+ + +
+
+
+
+
+ + + +
+
diff --git a/xblocks_contrib/problem/assets/fixtures/matlabinput_problem.html b/xblocks_contrib/problem/assets/fixtures/matlabinput_problem.html new file mode 100644 index 00000000..93e06ffe --- /dev/null +++ b/xblocks_contrib/problem/assets/fixtures/matlabinput_problem.html @@ -0,0 +1,48 @@ +
+
+ +
+ +
+ + processing + + +

processing

+
+ +
+ Submitted. As soon as a response is returned, this message will be replaced by that feedback. +
+
+
+ +
+
+
+
+
+ + + +
+ +
diff --git a/xblocks_contrib/problem/assets/fixtures/problem.html b/xblocks_contrib/problem/assets/fixtures/problem.html new file mode 100644 index 00000000..841b8dc1 --- /dev/null +++ b/xblocks_contrib/problem/assets/fixtures/problem.html @@ -0,0 +1,8 @@ +
+
+
+
diff --git a/xblocks_contrib/problem/assets/fixtures/problem_content.html b/xblocks_contrib/problem/assets/fixtures/problem_content.html new file mode 100644 index 00000000..9f252b6f --- /dev/null +++ b/xblocks_contrib/problem/assets/fixtures/problem_content.html @@ -0,0 +1,44 @@ +

Problem Header

+
+
+

${_("Problem Content")}

+
+ + + + +
+ + + + + + + + + +
+ + Explanation +
+
+ +
diff --git a/xblocks_contrib/problem/assets/fixtures/problem_content_1240.html b/xblocks_contrib/problem/assets/fixtures/problem_content_1240.html new file mode 100644 index 00000000..700286b7 --- /dev/null +++ b/xblocks_contrib/problem/assets/fixtures/problem_content_1240.html @@ -0,0 +1,23 @@ +

Problem Header

+
+
+

${_("Problem Content")}

+
+ + + + + + + + + Explanation +
+
+
diff --git a/xblocks_contrib/problem/assets/fixtures/radiobutton_problem.html b/xblocks_contrib/problem/assets/fixtures/radiobutton_problem.html new file mode 100644 index 00000000..21428cc0 --- /dev/null +++ b/xblocks_contrib/problem/assets/fixtures/radiobutton_problem.html @@ -0,0 +1,14 @@ +
+ + + +
diff --git a/xblocks_contrib/problem/assets/karma_runner.js b/xblocks_contrib/problem/assets/karma_runner.js new file mode 100644 index 00000000..62cf12c8 --- /dev/null +++ b/xblocks_contrib/problem/assets/karma_runner.js @@ -0,0 +1,12 @@ +/* eslint-env node */ + +// overwrite the loaded method and manually start the karma after a delay +// Somehow the code initialized in jQuery's onready doesn't get called before karma auto starts + +'use strict'; + +window.__karma__.loaded = function() { + setTimeout(function() { + window.__karma__.start(); + }, 1000); +}; diff --git a/xblocks_contrib/problem/assets/spec/collapsible_spec.js b/xblocks_contrib/problem/assets/spec/collapsible_spec.js new file mode 100644 index 00000000..a924e1cb --- /dev/null +++ b/xblocks_contrib/problem/assets/spec/collapsible_spec.js @@ -0,0 +1,130 @@ +// eslint-disable-next-line no-shadow-restricted-names +(function (undefined) { + "use strict"; + + describe("Collapsible", function () { + var $el, + html, + html_custom, + initialize = function (template) { + setFixtures(template); + $el = $(".collapsible"); + Collapsible.setCollapsibles($el); + }, + disableFx = function () { + $.fx.off = true; + }, + enableFx = function () { + $.fx.off = false; + }; + + beforeEach(function () { + html = + "" + + '
' + + '
shortform message
' + + '
' + + "

longform is visible

" + + "
" + + "
"; + html_custom = + "" + + '
' + + "
shortform message
" + + '
' + + "

longform is visible

" + + "
" + + "
"; + }); + + describe("setCollapsibles", function () { + it("Default container initialized correctly", function () { + initialize(html); + + expect($el.find(".shortform")).toContainElement(".full-top"); + expect($el.find(".shortform")).toContainElement(".full-bottom"); + expect($el.find(".longform")).toBeHidden(); + expect($el.find(".full")).toHandle("click"); + }); + + it("Custom container initialized correctly", function () { + initialize(html_custom); + + expect($el.find(".shortform-custom")).toContainElement(".full-custom"); + expect($el.find(".full-custom")).toHaveText("Show shortform-custom"); + expect($el.find(".longform")).toBeHidden(); + expect($el.find(".full-custom")).toHandle("click"); + }); + }); + + describe("toggleFull", function () { + var assertChanges = function (state, anchorsElClass, showText, hideText) { + var anchors, text; + + if (state == null) { + state = "closed"; + } + + anchors = $el.find("." + anchorsElClass); + + if (state === "closed") { + expect($el.find(".longform")).toBeHidden(); + expect($el).not.toHaveClass("open"); + text = showText; + } else { + expect($el.find(".longform")).toBeVisible(); + expect($el).toHaveClass("open"); + text = hideText; + } + + $.each(anchors, function (index, el) { + expect(el).toHaveText(text); + }); + }; + + beforeEach(function () { + disableFx(); + }); + + afterEach(function () { + enableFx(); + }); + + it("Default container", function () { + var event; + + initialize(html); + + event = jQuery.Event("click", { + target: $el.find(".full").get(0), + }); + + Collapsible.toggleFull(event, "See full output", "Hide output"); + assertChanges("opened", "full", "See full output", "Hide output"); + + Collapsible.toggleFull(event, "See full output", "Hide output"); + assertChanges("closed", "full", "See full output", "Hide output"); + }); + + it("Custom container", function () { + var event; + + initialize(html_custom); + + event = jQuery.Event("click", { + target: $el.find(".full-custom").get(0), + }); + + Collapsible.toggleFull(event, "Show shortform-custom", "Hide shortform-custom"); + assertChanges("opened", "full-custom", "Show shortform-custom", "Hide shortform-custom"); + + Collapsible.toggleFull(event, "Show shortform-custom", "Hide shortform-custom"); + assertChanges("closed", "full-custom", "Show shortform-custom", "Hide shortform-custom"); + }); + }); + }); +}).call(this); diff --git a/xblocks_contrib/problem/assets/spec/display_spec.js b/xblocks_contrib/problem/assets/spec/display_spec.js new file mode 100644 index 00000000..f55106f3 --- /dev/null +++ b/xblocks_contrib/problem/assets/spec/display_spec.js @@ -0,0 +1,1176 @@ +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +describe("Problem", function () { + const problem_content_default = readFixtures("problem_content.html"); + + beforeEach(function () { + // Stub MathJax + window.MathJax = { + Hub: jasmine.createSpyObj("MathJax.Hub", ["getAllJax", "Queue"]), + Callback: jasmine.createSpyObj("MathJax.Callback", ["After"]), + }; + this.stubbedJax = { root: jasmine.createSpyObj("jax.root", ["toMathML"]) }; + MathJax.Hub.getAllJax.and.returnValue([this.stubbedJax]); + window.update_schematics = function () {}; + spyOn(SR, "readText"); + spyOn(SR, "readTexts"); + + // Load this function from spec/helper.js + // Note that if your test fails with a message like: + // 'External request attempted for blah, which is not defined.' + // this msg is coming from the stubRequests function else clause. + jasmine.stubRequests(); + + loadFixtures("problem.html"); + + spyOn(Logger, "log"); + spyOn($.fn, "load").and.callFake(function (url, callback) { + $(this).html(readFixtures("problem_content.html")); + return callback(); + }); + }); + + describe("constructor", function () { + it("set the element from html", function () { + this.problem999 = new Problem(`\ +
\ +
\ +
\ +
\ +`); + expect(this.problem999.element_id).toBe("problem_999"); + }); + + it("set the element from loadFixtures", function () { + this.problem1 = new Problem($(".xblock-student_view")); + expect(this.problem1.element_id).toBe("problem_1"); + }); + }); + + describe("bind", function () { + beforeEach(function () { + spyOn(window, "update_schematics"); + MathJax.Hub.getAllJax.and.returnValue([this.stubbedJax]); + this.problem = new Problem($(".xblock-student_view")); + }); + + it("set mathjax typeset", () => expect(MathJax.Hub.Queue).toHaveBeenCalled()); + + it("update schematics", () => expect(window.update_schematics).toHaveBeenCalled()); + + it("bind answer refresh on button click", function () { + expect($("div.action button")).toHandleWith("click", this.problem.refreshAnswers); + }); + + it("bind the submit button", function () { + expect($(".action .submit")).toHandleWith("click", this.problem.submit_fd); + }); + + it("bind the reset button", function () { + expect($("div.action button.reset")).toHandleWith("click", this.problem.reset); + }); + + it("bind the show button", function () { + expect($(".action .show")).toHandleWith("click", this.problem.show); + }); + + it("bind the save button", function () { + expect($("div.action button.save")).toHandleWith("click", this.problem.save); + }); + + it("bind the math input", function () { + expect($("input.math")).toHandleWith("keyup", this.problem.refreshMath); + }); + }); + + describe("bind_with_custom_input_id", function () { + beforeEach(function () { + spyOn(window, "update_schematics"); + MathJax.Hub.getAllJax.and.returnValue([this.stubbedJax]); + this.problem = new Problem($(".xblock-student_view")); + return $(this).html(readFixtures("problem_content_1240.html")); + }); + + it("bind the submit button", function () { + expect($(".action .submit")).toHandleWith("click", this.problem.submit_fd); + }); + + it("bind the show button", function () { + expect($("div.action button.show")).toHandleWith("click", this.problem.show); + }); + }); + + describe("renderProgressState", function () { + beforeEach(function () { + this.problem = new Problem($(".xblock-student_view")); + }); + + const testProgessData = function ( + problem, + score, + total_possible, + attempts, + graded, + expected_progress_after_render, + ) { + problem.el.data("problem-score", score); + problem.el.data("problem-total-possible", total_possible); + problem.el.data("attempts-used", attempts); + problem.el.data("graded", graded); + expect(problem.$(".problem-progress").html()).toEqual(""); + problem.renderProgressState(); + expect(problem.$(".problem-progress").html()).toEqual(expected_progress_after_render); + }; + + describe('with a status of "none"', function () { + it("reports the number of points possible and graded", function () { + testProgessData(this.problem, 0, 1, 0, "True", "1 point possible (graded)"); + }); + + it("displays the number of points possible when rendering happens with the content", function () { + testProgessData(this.problem, 0, 2, 0, "True", "2 points possible (graded)"); + }); + + it("reports the number of points possible and ungraded", function () { + testProgessData(this.problem, 0, 1, 0, "False", "1 point possible (ungraded)"); + }); + + it("displays ungraded if number of points possible is 0", function () { + testProgessData(this.problem, 0, 0, 0, "False", "0 points possible (ungraded)"); + }); + + it("displays ungraded if number of points possible is 0, even if graded value is True", function () { + testProgessData(this.problem, 0, 0, 0, "True", "0 points possible (ungraded)"); + }); + + it("reports the correct score with status none and >0 attempts", function () { + testProgessData(this.problem, 0, 1, 1, "True", "0/1 point (graded)"); + }); + + it("reports the correct score with >1 weight, status none, and >0 attempts", function () { + testProgessData(this.problem, 0, 2, 2, "True", "0/2 points (graded)"); + }); + }); + + describe("with any other valid status", function () { + it("reports the current score", function () { + testProgessData(this.problem, 1, 1, 1, "True", "1/1 point (graded)"); + }); + + it("shows current score when rendering happens with the content", function () { + testProgessData(this.problem, 2, 2, 1, "True", "2/2 points (graded)"); + }); + + it("reports the current score even if problem is ungraded", function () { + testProgessData(this.problem, 1, 1, 1, "False", "1/1 point (ungraded)"); + }); + }); + + describe('with valid status and string containing an integer like "0" for detail', () => + // These tests are to address a failure specific to Chrome 51 and 52 + + it("shows 0 points possible for the detail", function () { + testProgessData(this.problem, 0, 0, 1, "False", "0 points possible (ungraded)"); + })); + + describe("with a score of null (show_correctness == false)", function () { + it("reports the number of points possible and graded, results hidden", function () { + testProgessData(this.problem, null, 1, 0, "True", "1 point possible (graded, results hidden)"); + }); + + it("reports the number of points possible (plural) and graded, results hidden", function () { + testProgessData(this.problem, null, 2, 0, "True", "2 points possible (graded, results hidden)"); + }); + + it("reports the number of points possible and ungraded, results hidden", function () { + testProgessData(this.problem, null, 1, 0, "False", "1 point possible (ungraded, results hidden)"); + }); + + it("displays ungraded if number of points possible is 0, results hidden", function () { + testProgessData(this.problem, null, 0, 0, "False", "0 points possible (ungraded, results hidden)"); + }); + + it("displays ungraded if number of points possible is 0, even if graded value is True, results hidden", function () { + testProgessData(this.problem, null, 0, 0, "True", "0 points possible (ungraded, results hidden)"); + }); + + it("reports the correct score with status none and >0 attempts, results hidden", function () { + testProgessData(this.problem, null, 1, 1, "True", "1 point possible (graded, results hidden)"); + }); + + it("reports the correct score with >1 weight, status none, and >0 attempts, results hidden", function () { + testProgessData(this.problem, null, 2, 2, "True", "2 points possible (graded, results hidden)"); + }); + }); + }); + + describe("render", function () { + beforeEach(function () { + this.problem = new Problem($(".xblock-student_view")); + this.bind = this.problem.bind; + spyOn(this.problem, "bind"); + }); + + describe("with content given", function () { + beforeEach(function () { + this.problem.render("Hello World"); + }); + + it("render the content", function () { + expect(this.problem.el.html()).toEqual("Hello World"); + }); + + it("re-bind the content", function () { + expect(this.problem.bind).toHaveBeenCalled(); + }); + }); + + describe("with no content given", function () { + beforeEach(function () { + spyOn($, "postWithPrefix").and.callFake((url, callback) => callback({ html: "Hello World" })); + this.problem.render(); + }); + + it("load the content via ajax", function () { + expect(this.problem.el.html()).toEqual("Hello World"); + }); + + it("re-bind the content", function () { + expect(this.problem.bind).toHaveBeenCalled(); + }); + }); + }); + + describe("submit_fd", function () { + beforeEach(function () { + // Insert an input of type file outside of the problem. + $(".xblock-student_view").after(''); + this.problem = new Problem($(".xblock-student_view")); + spyOn(this.problem, "submit"); + }); + + it("submit method is called if input of type file is not in problem", function () { + this.problem.submit_fd(); + expect(this.problem.submit).toHaveBeenCalled(); + }); + }); + + describe("submit", function () { + beforeEach(function () { + this.problem = new Problem($(".xblock-student_view")); + this.problem.answers = "foo=1&bar=2"; + }); + + it("log the problem_check event", function () { + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + let promise; + promise = { + always(callable) { + return callable(); + }, + done(callable) { + return callable(); + }, + }; + return promise; + }); + this.problem.submit(); + expect(Logger.log).toHaveBeenCalledWith("problem_check", "foo=1&bar=2"); + }); + + it("log the problem_graded event, after the problem is done grading.", function () { + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + let promise; + const response = { + success: "correct", + contents: "mock grader response", + }; + callback(response); + promise = { + always(callable) { + return callable(); + }, + done(callable) { + return callable(); + }, + }; + return promise; + }); + this.problem.submit(); + expect(Logger.log).toHaveBeenCalledWith( + "problem_graded", + ["foo=1&bar=2", "mock grader response"], + this.problem.id, + ); + }); + + it("submit the answer for submit", function () { + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + let promise; + promise = { + always(callable) { + return callable(); + }, + done(callable) { + return callable(); + }, + }; + return promise; + }); + this.problem.submit(); + expect($.postWithPrefix).toHaveBeenCalledWith( + "/problem/Problem1/problem_check", + "foo=1&bar=2", + jasmine.any(Function), + ); + }); + + describe("when the response is correct", () => + it("call render with returned content", function () { + const contents = + '

Correctexcellent

' + + '

Yepcorrect

'; + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + let promise; + callback({ success: "correct", contents }); + promise = { + always(callable) { + return callable(); + }, + done(callable) { + return callable(); + }, + }; + return promise; + }); + this.problem.submit(); + expect(this.problem.el).toHaveHtml(contents); + expect(window.SR.readTexts).toHaveBeenCalledWith(["Question 1: excellent", "Question 2: correct"]); + })); + + describe("when the response is incorrect", () => + it("call render with returned content", function () { + const contents = '

Incorrectno, try again

'; + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + let promise; + callback({ success: "incorrect", contents }); + promise = { + always(callable) { + return callable(); + }, + done(callable) { + return callable(); + }, + }; + return promise; + }); + this.problem.submit(); + expect(this.problem.el).toHaveHtml(contents); + expect(window.SR.readTexts).toHaveBeenCalledWith(["no, try again"]); + })); + + it("tests if the submit button is disabled while submitting and the text changes on the button", function () { + const self = this; + const curr_html = this.problem.el.html(); + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + // At this point enableButtons should have been called, making the submit button disabled with text 'submitting' + let promise; + expect(self.problem.submitButton).toHaveAttr("disabled"); + expect(self.problem.submitButtonLabel.text()).toBe("Submitting"); + callback({ + success: "incorrect", // does not matter if correct or incorrect here + contents: curr_html, + }); + promise = { + always(callable) { + return callable(); + }, + done(callable) { + return callable(); + }, + }; + return promise; + }); + // Make sure the submit button is enabled before submitting + $("#input_example_1").val("test").trigger("input"); + expect(this.problem.submitButton).not.toHaveAttr("disabled"); + this.problem.submit(); + // After submit, the button should not be disabled and should have text as 'Submit' + expect(this.problem.submitButtonLabel.text()).toBe("Submit"); + expect(this.problem.submitButton).not.toHaveAttr("disabled"); + }); + }); + + describe("submit button on problems", function () { + beforeEach(function () { + this.problem = new Problem($(".xblock-student_view")); + this.submitDisabled = (disabled) => { + if (disabled) { + expect(this.problem.submitButton).toHaveAttr("disabled"); + } else { + expect(this.problem.submitButton).not.toHaveAttr("disabled"); + } + }; + }); + + describe("some basic tests for submit button", () => + it("should become enabled after a value is entered into the text box", function () { + $("#input_example_1").val("test").trigger("input"); + this.submitDisabled(false); + $("#input_example_1").val("").trigger("input"); + this.submitDisabled(true); + })); + + describe("some advanced tests for submit button", function () { + const radioButtonProblemHtml = readFixtures("radiobutton_problem.html"); + const checkboxProblemHtml = readFixtures("checkbox_problem.html"); + + it("should become enabled after a checkbox is checked", function () { + $("#input_example_1").replaceWith(checkboxProblemHtml); + this.problem.submitAnswersAndSubmitButton(true); + this.submitDisabled(true); + $("#input_1_1_1").click(); + this.submitDisabled(false); + $("#input_1_1_1").click(); + this.submitDisabled(true); + }); + + it("should become enabled after a radiobutton is checked", function () { + $("#input_example_1").replaceWith(radioButtonProblemHtml); + this.problem.submitAnswersAndSubmitButton(true); + this.submitDisabled(true); + $("#input_1_1_1").attr("checked", true).trigger("click"); + this.submitDisabled(false); + $("#input_1_1_1").attr("checked", false).trigger("click"); + this.submitDisabled(true); + }); + + it("should become enabled after a value is selected in a selector", function () { + const html = `\ +
+ +
\ +`; + $("#input_example_1").replaceWith(html); + this.problem.submitAnswersAndSubmitButton(true); + this.submitDisabled(true); + $("#problem_sel select").val("val2").trigger("change"); + this.submitDisabled(false); + $("#problem_sel select").val("val0").trigger("change"); + this.submitDisabled(true); + }); + + it("should become enabled after a radiobutton is checked and a value is entered into the text box", function () { + $(radioButtonProblemHtml).insertAfter("#input_example_1"); + this.problem.submitAnswersAndSubmitButton(true); + this.submitDisabled(true); + $("#input_1_1_1").attr("checked", true).trigger("click"); + this.submitDisabled(true); + $("#input_example_1").val("111").trigger("input"); + this.submitDisabled(false); + $("#input_1_1_1").attr("checked", false).trigger("click"); + this.submitDisabled(true); + }); + + it("should become enabled if there are only hidden input fields", function () { + const html = `\ +\ +`; + $("#input_example_1").replaceWith(html); + this.problem.submitAnswersAndSubmitButton(true); + this.submitDisabled(false); + }); + }); + }); + + describe("reset", function () { + beforeEach(function () { + this.problem = new Problem($(".xblock-student_view")); + }); + + it("log the problem_reset event", function () { + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + let promise; + promise = { + always(callable) { + return callable(); + }, + }; + return promise; + }); + this.problem.answers = "foo=1&bar=2"; + this.problem.reset(); + expect(Logger.log).toHaveBeenCalledWith("problem_reset", "foo=1&bar=2"); + }); + + it("POST to the problem reset page", function () { + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + let promise; + promise = { + always(callable) { + return callable(); + }, + }; + return promise; + }); + this.problem.reset(); + expect($.postWithPrefix).toHaveBeenCalledWith( + "/problem/Problem1/problem_reset", + { id: "i4x://edX/101/problem/Problem1" }, + jasmine.any(Function), + ); + }); + + it("render the returned content", function () { + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + let promise; + callback({ html: "Reset", success: true }); + promise = { + always(callable) { + return callable(); + }, + }; + return promise; + }); + this.problem.reset(); + expect(this.problem.el.html()).toEqual("Reset"); + }); + + it("sends a message to the window SR element", function () { + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + let promise; + callback({ html: "Reset", success: true }); + promise = { + always(callable) { + return callable(); + }, + }; + return promise; + }); + this.problem.reset(); + expect(window.SR.readText).toHaveBeenCalledWith("This problem has been reset."); + }); + + it("shows a notification on error", function () { + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + let promise; + callback({ msg: "Error on reset.", success: false }); + promise = { + always(callable) { + return callable(); + }, + }; + return promise; + }); + this.problem.reset(); + expect($(".notification-gentle-alert .notification-message").text()).toEqual("Error on reset."); + }); + + it("tests that reset does not enable submit or modify the text while resetting", function () { + const self = this; + const curr_html = this.problem.el.html(); + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + // enableButtons should have been called at this point to set them to all disabled + let promise; + expect(self.problem.submitButton).toHaveAttr("disabled"); + expect(self.problem.submitButtonLabel.text()).toBe("Submit"); + callback({ success: "correct", html: curr_html }); + promise = { + always(callable) { + return callable(); + }, + }; + return promise; + }); + // Submit should be disabled + expect(this.problem.submitButton).toHaveAttr("disabled"); + this.problem.reset(); + // Submit should remain disabled + expect(self.problem.submitButton).toHaveAttr("disabled"); + expect(self.problem.submitButtonLabel.text()).toBe("Submit"); + }); + }); + + describe("show problem with column in id", function () { + beforeEach(function () { + this.problem = new Problem($(".xblock-student_view")); + this.problem.el.prepend('
'); + }); + + it("log the problem_show event", function () { + this.problem.show(); + expect(Logger.log).toHaveBeenCalledWith("problem_show", { problem: "i4x://edX/101/problem/Problem1" }); + }); + + it("fetch the answers", function () { + spyOn($, "postWithPrefix"); + this.problem.show(); + expect($.postWithPrefix).toHaveBeenCalledWith("/problem/Problem1/problem_show", jasmine.any(Function)); + }); + + it("show the answers", function () { + spyOn($, "postWithPrefix").and.callFake((url, callback) => + callback({ answers: { "1_1:11": "One", "1_2:12": "Two" } }), + ); + this.problem.show(); + expect($("#answer_1_1\\:11")).toHaveHtml("One"); + expect($("#answer_1_2\\:12")).toHaveHtml("Two"); + }); + + it("disables the show answer button", function () { + spyOn($, "postWithPrefix").and.callFake((url, callback) => callback({ answers: {} })); + this.problem.show(); + expect(this.problem.el.find(".show").attr("disabled")).toEqual("disabled"); + }); + }); + + describe("show", function () { + beforeEach(function () { + this.problem = new Problem($(".xblock-student_view")); + this.problem.el.prepend('
'); + }); + + describe("when the answer has not yet shown", function () { + beforeEach(function () { + expect(this.problem.el.find(".show").attr("disabled")).not.toEqual("disabled"); + }); + + it("log the problem_show event", function () { + this.problem.show(); + expect(Logger.log).toHaveBeenCalledWith("problem_show", { problem: "i4x://edX/101/problem/Problem1" }); + }); + + it("fetch the answers", function () { + spyOn($, "postWithPrefix"); + this.problem.show(); + expect($.postWithPrefix).toHaveBeenCalledWith("/problem/Problem1/problem_show", jasmine.any(Function)); + }); + + it("show the answers", function () { + spyOn($, "postWithPrefix").and.callFake((url, callback) => + callback({ answers: { "1_1": "One", "1_2": "Two" } }), + ); + this.problem.show(); + expect($("#answer_1_1")).toHaveHtml("One"); + expect($("#answer_1_2")).toHaveHtml("Two"); + }); + + it("disables the show answer button", function () { + spyOn($, "postWithPrefix").and.callFake((url, callback) => callback({ answers: {} })); + this.problem.show(); + expect(this.problem.el.find(".show").attr("disabled")).toEqual("disabled"); + }); + + describe("radio text question", function () { + const radio_text_xml = `\ +
+

+ +
+
+ +
+
+
+ + +

+ +
+ + +

+
+
+ + +

+
+
+
\ +`; + beforeEach(function () { + // Append a radiotextresponse problem to the problem, so we can check it's javascript functionality + this.problem.el.prepend(radio_text_xml); + }); + + it("sets the correct class on the section for the correct choice", function () { + spyOn($, "postWithPrefix").and.callFake((url, callback) => + callback({ answers: { "1_2_1": ["1_2_1_choiceinput_0bc"], "1_2_1_choiceinput_0bc": "3" } }), + ); + this.problem.show(); + + expect($("#forinput1_2_1_choiceinput_0bc").attr("class")).toEqual("choicetextgroup_show_correct"); + expect($("#answer_1_2_1_choiceinput_0bc").text()).toEqual("3"); + expect($("#answer_1_2_1_choiceinput_1bc").text()).toEqual(""); + expect($("#answer_1_2_1_choiceinput_2bc").text()).toEqual(""); + }); + + it("Should not disable input fields", function () { + spyOn($, "postWithPrefix").and.callFake((url, callback) => + callback({ answers: { "1_2_1": ["1_2_1_choiceinput_0bc"], "1_2_1_choiceinput_0bc": "3" } }), + ); + this.problem.show(); + expect($("input#1_2_1_choiceinput_0bc").attr("disabled")).not.toEqual("disabled"); + expect($("input#1_2_1_choiceinput_1bc").attr("disabled")).not.toEqual("disabled"); + expect($("input#1_2_1_choiceinput_2bc").attr("disabled")).not.toEqual("disabled"); + expect($("input#1_2_1").attr("disabled")).not.toEqual("disabled"); + }); + }); + + describe("imageinput", function () { + let el, height, width; + const imageinput_html = readFixtures("imageinput.underscore"); + + const DEFAULTS = { + id: "12345", + width: "300", + height: "400", + }; + + beforeEach(function () { + this.problem = new Problem($(".xblock-student_view")); + this.problem.el.prepend(_.template(imageinput_html)(DEFAULTS)); + }); + + const assertAnswer = (problem, data) => { + stubRequest(data); + problem.show(); + + $.each(data["answers"], (id, answer) => { + const img = getImage(answer); + el = $(`#inputtype_${id}`); + expect(img).toImageDiffEqual(el.find("canvas")[0]); + }); + }; + + var stubRequest = (data) => { + spyOn($, "postWithPrefix").and.callFake((url, callback) => callback(data)); + }; + + var getImage = (coords, c_width, c_height) => { + let ctx, reg; + const types = { + rectangle: (coords) => { + reg = /^\(([0-9]+),([0-9]+)\)-\(([0-9]+),([0-9]+)\)$/; + const rects = coords.replace(/\s*/g, "").split(/;/); + + $.each(rects, (index, rect) => { + const { abs } = Math; + const points = reg.exec(rect); + if (points) { + width = abs(points[3] - points[1]); + height = abs(points[4] - points[2]); + + return ctx.rect(points[1], points[2], width, height); + } + }); + + ctx.stroke(); + ctx.fill(); + }, + + regions: (coords) => { + const parseCoords = (coords) => { + reg = JSON.parse(coords); + + if (typeof reg[0][0][0] === "undefined") { + reg = [reg]; + } + + return reg; + }; + + return $.each(parseCoords(coords), (index, region) => { + ctx.beginPath(); + $.each(region, (index, point) => { + if (index === 0) { + return ctx.moveTo(point[0], point[1]); + } else { + return ctx.lineTo(point[0], point[1]); + } + }); + + ctx.closePath(); + ctx.stroke(); + ctx.fill(); + }); + }, + }; + + const canvas = document.createElement("canvas"); + canvas.width = c_width || 100; + canvas.height = c_height || 100; + + if (canvas.getContext) { + ctx = canvas.getContext("2d"); + } else { + console.log("Canvas is not supported."); + } + + ctx.fillStyle = "rgba(255,255,255,.3)"; + ctx.strokeStyle = "#FF0000"; + ctx.lineWidth = "2"; + + $.each(coords, (key, value) => { + if (types[key] != null && value) { + return types[key](value); + } + }); + + return canvas; + }; + + it("rectangle is drawn correctly", function () { + assertAnswer(this.problem, { + answers: { + 12345: { + rectangle: "(10,10)-(30,30)", + regions: null, + }, + }, + }); + }); + + it("region is drawn correctly", function () { + assertAnswer(this.problem, { + answers: { + 12345: { + rectangle: null, + regions: "[[10,10],[30,30],[70,30],[20,30]]", + }, + }, + }); + }); + + it("mixed shapes are drawn correctly", function () { + assertAnswer(this.problem, { + answers: { + 12345: { + rectangle: "(10,10)-(30,30);(5,5)-(20,20)", + regions: `[ + [[50,50],[40,40],[70,30],[50,70]], + [[90,95],[95,95],[90,70],[70,70]] +]`, + }, + }, + }); + }); + + it("multiple image inputs draw answers on separate canvases", function () { + const data = { + id: "67890", + width: "400", + height: "300", + }; + + this.problem.el.prepend(_.template(imageinput_html)(data)); + assertAnswer(this.problem, { + answers: { + 12345: { + rectangle: null, + regions: "[[10,10],[30,30],[70,30],[20,30]]", + }, + 67890: { + rectangle: "(10,10)-(30,30)", + regions: null, + }, + }, + }); + }); + + it("dictionary with answers doesn't contain answer for current id", function () { + spyOn(console, "log"); + stubRequest({ answers: {} }); + this.problem.show(); + el = $("#inputtype_12345"); + expect(el.find("canvas")).not.toExist(); + expect(console.log).toHaveBeenCalledWith("Answer is absent for image input with id=12345"); + }); + }); + }); + }); + + describe("save", function () { + beforeEach(function () { + this.problem = new Problem($(".xblock-student_view")); + this.problem.answers = "foo=1&bar=2"; + }); + + it("log the problem_save event", function () { + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + let promise; + promise = { + always(callable) { + return callable(); + }, + }; + return promise; + }); + this.problem.save(); + expect(Logger.log).toHaveBeenCalledWith("problem_save", "foo=1&bar=2"); + }); + + it("POST to save problem", function () { + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + let promise; + promise = { + always(callable) { + return callable(); + }, + }; + return promise; + }); + this.problem.save(); + expect($.postWithPrefix).toHaveBeenCalledWith( + "/problem/Problem1/problem_save", + "foo=1&bar=2", + jasmine.any(Function), + ); + }); + + it("tests that save does not enable the submit button or change the text when submit is originally disabled", function () { + const self = this; + const curr_html = this.problem.el.html(); + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + // enableButtons should have been called at this point and the submit button should be unaffected + let promise; + expect(self.problem.submitButton).toHaveAttr("disabled"); + expect(self.problem.submitButtonLabel.text()).toBe("Submit"); + callback({ success: "correct", html: curr_html }); + promise = { + always(callable) { + return callable(); + }, + }; + return promise; + }); + // Expect submit to be disabled and labeled properly at the start + expect(this.problem.submitButton).toHaveAttr("disabled"); + expect(this.problem.submitButtonLabel.text()).toBe("Submit"); + this.problem.save(); + // Submit button should have the same state after save has completed + expect(this.problem.submitButton).toHaveAttr("disabled"); + expect(this.problem.submitButtonLabel.text()).toBe("Submit"); + }); + + it("tests that save does not disable the submit button or change the text when submit is originally enabled", function () { + const self = this; + const curr_html = this.problem.el.html(); + spyOn($, "postWithPrefix").and.callFake(function (url, answers, callback) { + // enableButtons should have been called at this point, and the submit button should be disabled while submitting + let promise; + expect(self.problem.submitButton).toHaveAttr("disabled"); + expect(self.problem.submitButtonLabel.text()).toBe("Submit"); + callback({ success: "correct", html: curr_html }); + promise = { + always(callable) { + return callable(); + }, + }; + return promise; + }); + // Expect submit to be enabled and labeled properly at the start after adding an input + $("#input_example_1").val("test").trigger("input"); + expect(this.problem.submitButton).not.toHaveAttr("disabled"); + expect(this.problem.submitButtonLabel.text()).toBe("Submit"); + this.problem.save(); + // Submit button should have the same state after save has completed + expect(this.problem.submitButton).not.toHaveAttr("disabled"); + expect(this.problem.submitButtonLabel.text()).toBe("Submit"); + }); + }); + + describe("refreshMath", function () { + beforeEach(function () { + this.problem = new Problem($(".xblock-student_view")); + $("#input_example_1").val("E=mc^2"); + this.problem.refreshMath({ target: $("#input_example_1").get(0) }); + }); + + it("should queue the conversion and MathML element update", function () { + expect(MathJax.Hub.Queue).toHaveBeenCalledWith( + ["Text", this.stubbedJax, "E=mc^2"], + [this.problem.updateMathML, this.stubbedJax, $("#input_example_1").get(0)], + ); + }); + }); + + describe("updateMathML", function () { + beforeEach(function () { + this.problem = new Problem($(".xblock-student_view")); + this.stubbedJax.root.toMathML.and.returnValue(""); + }); + + describe("when there is no exception", function () { + beforeEach(function () { + this.problem.updateMathML(this.stubbedJax, $("#input_example_1").get(0)); + }); + + it("convert jax to MathML", () => expect($("#input_example_1_dynamath")).toHaveValue("")); + }); + + describe("when there is an exception", function () { + beforeEach(function () { + const error = new Error(); + error.restart = true; + this.stubbedJax.root.toMathML.and.throwError(error); + this.problem.updateMathML(this.stubbedJax, $("#input_example_1").get(0)); + }); + + it("should queue up the exception", function () { + expect(MathJax.Callback.After).toHaveBeenCalledWith([this.problem.refreshMath, this.stubbedJax], true); + }); + }); + }); + + describe("refreshAnswers", function () { + beforeEach(function () { + this.problem = new Problem($(".xblock-student_view")); + this.problem.el.html(`\ + +
{{ tag_prompt|safe }}
+
    + {% for option in options %} +
  • + {% if has_options_value %} + {% if option.choice == status.classname and status == status.classname %} + + {% include "status_span.html" with status=status %} + + {% endif %} + {% endif %} + {{ option.description|safe }} +
  • + {% endfor %} +
+ {% if debug %} +
+ Rendered with value: +
+
{{ value|safe }}
+ Current input value: +
+ +
+ {% else %} + + {% endif %} + {% include "status_span.html" with status=status status_id=id %} +

+
+
+{% if msg %}{{ msg|safe }}{% endif %} diff --git a/xblocks_contrib/problem/capa/templates/chemicalequationinput.html b/xblocks_contrib/problem/capa/templates/chemicalequationinput.html new file mode 100644 index 00000000..5d2f19e4 --- /dev/null +++ b/xblocks_contrib/problem/capa/templates/chemicalequationinput.html @@ -0,0 +1,19 @@ +
+
+
+ +

+ {{ value }} + {% include "status_span.html" with status=status status_id=id %} +

+
+

+
+
diff --git a/xblocks_contrib/problem/capa/templates/choicegroup.html b/xblocks_contrib/problem/capa/templates/choicegroup.html new file mode 100644 index 00000000..0c9e7427 --- /dev/null +++ b/xblocks_contrib/problem/capa/templates/choicegroup.html @@ -0,0 +1,41 @@ +
+
+ {% if response_data.label %} + {{ response_data.label }} + {% endif %} + {% for description_id, description_text in response_data.descriptions.items %} +

{{ description_text|safe }}

+ {% endfor %} + {% for choice_id, choice_label in choices %} +
+ + +
+ {% endfor %} + +
+
+ {% if show_correctness != 'never' %} + {% include "status_span.html" with status=status status_id=id %} + {% else %} + {% include "status_span.html" with status=status status_id=id hide_correctness=True %} + {% endif %} +
+ {% if show_correctness == "never" %} + {% if value or status != "unsubmitted" %}
{{ submitted_message|safe }}
{% endif %} + {% endif %} + {% if msg %}{{ msg|safe }}{% endif %} +
diff --git a/xblocks_contrib/problem/capa/templates/choicetext.html b/xblocks_contrib/problem/capa/templates/choicetext.html new file mode 100644 index 00000000..26816522 --- /dev/null +++ b/xblocks_contrib/problem/capa/templates/choicetext.html @@ -0,0 +1,61 @@ +{% load i18n %} +{% load static %} +{% with element_checked=False %} + {% for choice_id, _ in choices %} + {% if choice_id in value %} + {% with element_checked=True %}{% endwith %} + {% endif %} + {% endfor %} +
+
+
+
+ {% for choice_id, choice_description in choices %} +
+ + {% for content_node in choice_description %} + {% if content_node.type == 'text' %} + {{ content_node.contents }} + {% else %} + {% with my_id=content_node.contents|default:'' %} + {% with my_val=value.my_id|default:'' %} + + {% endwith %} + {% endwith %} + {% endif %} + {{ content_node.tail_text }} + {% endfor %} +

+
+ {% endfor %} + +
+ +
+ {% if input_type == 'checkbox' or not value or status.classname == 'incomplete' or status.classname == 'unsubmitted' or status.classname == 'unanswered' %} + {% include "status_span.html" with status=status status_id=id %} + {% endif %} +
+ {% if show_correctness == "never" %} + {% if value or status != "unsubmitted" %}
{{ submitted_message }}
{% endif %} + {% endif %} + {% if msg %}{{ msg|safe }}{% endif %} +
+
+{% endwith %} diff --git a/xblocks_contrib/problem/capa/templates/clarification.html b/xblocks_contrib/problem/capa/templates/clarification.html new file mode 100644 index 00000000..85bc3dc6 --- /dev/null +++ b/xblocks_contrib/problem/capa/templates/clarification.html @@ -0,0 +1,10 @@ + + + ({{ clarification }}) + diff --git a/xblocks_contrib/problem/capa/templates/codeinput.html b/xblocks_contrib/problem/capa/templates/codeinput.html new file mode 100644 index 00000000..47e934a5 --- /dev/null +++ b/xblocks_contrib/problem/capa/templates/codeinput.html @@ -0,0 +1,31 @@ +{% load i18n %} +
+ {% if response_data.label %} + + {% else %} + + {% endif %} + + {{ code_mirror_exit_message }} +
+ {% include "status_span.html" with status=status status_id=id %} + {% if status == 'queued' %}{% endif %} + {% if hidden %}
{% endif %} +

{{ status.display_name }}

+
+ +
{{ msg|safe }}
+
diff --git a/xblocks_contrib/problem/capa/templates/crystallography.html b/xblocks_contrib/problem/capa/templates/crystallography.html new file mode 100644 index 00000000..dc1fe0ff --- /dev/null +++ b/xblocks_contrib/problem/capa/templates/crystallography.html @@ -0,0 +1,29 @@ +{% load static %} +
+
+
+ Lattice: + +
+
+
+
+ {% if status == 'unsubmitted' or status == 'correct' or status == 'incorrect' or status == 'partially-correct' or status == 'incomplete' %} +
+ {% endif %} + + {% include "status_span.html" with status=status status_id=id %} +

+ {% if msg %}{{ msg|safe }}{% endif %} + {% if status == 'unsubmitted' or status == 'correct' or status == 'incorrect' or status == 'partially-correct' or status == 'incomplete' %} +
+ {% endif %} +
diff --git a/xblocks_contrib/problem/capa/templates/designprotein2dinput.html b/xblocks_contrib/problem/capa/templates/designprotein2dinput.html new file mode 100644 index 00000000..b272b14e --- /dev/null +++ b/xblocks_contrib/problem/capa/templates/designprotein2dinput.html @@ -0,0 +1,25 @@ +{% load static %} +
+
+
+ {% if status == 'unsubmitted' or status == 'correct' or status == 'incorrect' or status == 'partially-correct' or status == 'incomplete' %} +
+ {% endif %} +
+ + + + {% include "status_span.html" with status=status status_id=id %} +

+ {% if status == 'unsubmitted' or status == 'correct' or status == 'incorrect' or status == 'partially-correct' or status == 'incomplete' %} +
+{% endif %} +
diff --git a/xblocks_contrib/problem/capa/templates/drag_and_drop_input.html b/xblocks_contrib/problem/capa/templates/drag_and_drop_input.html new file mode 100644 index 00000000..f8fd5526 --- /dev/null +++ b/xblocks_contrib/problem/capa/templates/drag_and_drop_input.html @@ -0,0 +1,27 @@ +{% load static %} +
+
+ +
+ {% if status == 'unsubmitted' or status == 'correct' or status == 'incorrect' or status == 'partially-correct' or status == 'incomplete' %} +
+ {% endif %} + +

{% include "status_span.html" with status=status status_id=id %}

+

+ {% if msg %}{{ msg|safe }}{% endif %} + {% if status == 'unsubmitted' or status == 'correct' or status == 'incorrect' or status == 'partially-correct' or status == 'incomplete' %} +
+ {% endif %} +
diff --git a/xblocks_contrib/problem/capa/templates/editageneinput.html b/xblocks_contrib/problem/capa/templates/editageneinput.html new file mode 100644 index 00000000..b800bfa7 --- /dev/null +++ b/xblocks_contrib/problem/capa/templates/editageneinput.html @@ -0,0 +1,32 @@ +{% load static %} +
+
+
+ {% if status == 'unsubmitted' or status == 'correct' or status == 'incorrect' or status == 'partially-correct' or status == 'incomplete' %} +
+ {% endif %} +
+ + + + + +

+ {% include "status_span.html" with status=status status_id=id %} +

+

+{% if status == 'unsubmitted' or status == 'correct' or status == 'incorrect' or status == 'partially-correct' or status == 'incomplete' %} +
+{% endif %} +
diff --git a/xblocks_contrib/problem/capa/templates/editamolecule.html b/xblocks_contrib/problem/capa/templates/editamolecule.html new file mode 100644 index 00000000..1ef3314c --- /dev/null +++ b/xblocks_contrib/problem/capa/templates/editamolecule.html @@ -0,0 +1,32 @@ +
+
+ {% if status == 'unsubmitted' or status == 'correct' or status == 'incorrect' or status == 'partially-correct' or status == 'incomplete' %} +
+ {% endif %} +
+
+ + +

+

+ {% include "status_span.html" with status=status status_id=id %} +

+
+ {% if status == 'unsubmitted' or status == 'correct' or status == 'incorrect' or status == 'partially-correct' or status == 'incomplete' %} +
+ {% endif %} +
diff --git a/xblocks_contrib/problem/capa/templates/filesubmission.html b/xblocks_contrib/problem/capa/templates/filesubmission.html new file mode 100644 index 00000000..1d493e8c --- /dev/null +++ b/xblocks_contrib/problem/capa/templates/filesubmission.html @@ -0,0 +1,16 @@ +
+
+ {{ status.display_name }} + {% if status == 'queued' %}{% endif %} +

{{ status }}

+ +
+
{{ msg|safe }}
+
diff --git a/xblocks_contrib/problem/capa/templates/formulaequationinput.html b/xblocks_contrib/problem/capa/templates/formulaequationinput.html new file mode 100644 index 00000000..f75bcfe9 --- /dev/null +++ b/xblocks_contrib/problem/capa/templates/formulaequationinput.html @@ -0,0 +1,29 @@ +{% load static %} +
+
+ {% if response_data.label %} + + {% endif %} + {% for description_id, description_text in response_data.descriptions.items %} +

{{ description_text }}

+ {% endfor %} + + {{ trailing_text }} + {% include "status_span.html" with status=status status_id=id %} +

+
+ \(\) + Loading +
+
+
+ {% if msg %}{{ msg|safe }}{% endif %} +
diff --git a/xblocks_contrib/problem/capa/templates/imageinput.html b/xblocks_contrib/problem/capa/templates/imageinput.html new file mode 100644 index 00000000..b630933f --- /dev/null +++ b/xblocks_contrib/problem/capa/templates/imageinput.html @@ -0,0 +1,33 @@ +{% load static %} +
+ +
+
+ Selection indicator +
+
+
+ + {% include "status_span.html" with status=status status_id=id %} +
diff --git a/xblocks_contrib/problem/capa/templates/jsinput.html b/xblocks_contrib/problem/capa/templates/jsinput.html new file mode 100644 index 00000000..56dec9cd --- /dev/null +++ b/xblocks_contrib/problem/capa/templates/jsinput.html @@ -0,0 +1,42 @@ +
+
+
+ {% if status == 'unsubmitted' or status == 'submitted' or status == 'correct' or status == 'incorrect' or status == 'partially-correct' or status == 'incomplete' %} +
+ {% endif %} +