├── .eslintrc.js ├── .flake8 ├── .github ├── FUNDING.yml └── workflows │ └── python-package.yml ├── .gitignore ├── .jshintrc ├── .prettierignore ├── .readthedocs.yml ├── ACKNOWLEDGEMENTS.rst ├── CONTRIBUTING.rst ├── I18N.md ├── LICENSE.txt ├── README.rst ├── codechat_config.yaml ├── conf.py ├── images └── runeCompo-index.png ├── index.rst ├── makeRelease.sh ├── package-lock.json ├── package.json ├── pavement.py ├── poetry.lock ├── public └── index.html ├── pyproject.toml ├── pytest.ini ├── runestone ├── __init__.py ├── __main__.py ├── accessibility │ ├── __init__.py │ ├── accessibility.py │ ├── css │ │ ├── accessibility.css │ │ ├── accessibilitydarkest.css │ │ └── accessibilitylight.css │ └── toctree.rst ├── activecode │ ├── LabTestHelper.java │ ├── README.md │ ├── __init__.py │ ├── actest.html │ ├── activecode.py │ ├── css │ │ ├── activecode.css │ │ └── close.png │ ├── js │ │ ├── acfactory.js │ │ ├── activecode-i18n.en.js │ │ ├── activecode-i18n.pt-br.js │ │ ├── activecode-i18n.sr-Cyrl.js │ │ ├── activecode.js │ │ ├── activecode_html.js │ │ ├── activecode_js.js │ │ ├── activecode_sql.js │ │ ├── audiotour.js │ │ ├── coach-python-pyflakes.js │ │ ├── debugger.js │ │ ├── extractUnitResults.js │ │ ├── livecode.js │ │ ├── md5.js │ │ ├── sharedb.js │ │ ├── skulpt-stdlib.js │ │ ├── skulpt.min.js │ │ ├── skulpt.min.js.gz │ │ ├── skulpt.min.js.map │ │ └── timed_activecode.js │ ├── test │ │ ├── __init__.py │ │ ├── _sources │ │ │ ├── _static │ │ │ │ ├── LutherBellPic.jpg │ │ │ │ └── test.db │ │ │ ├── index.rst │ │ │ ├── progresspage.rst │ │ │ └── skulptfeatures.rst │ │ ├── build │ │ │ └── .gitignore │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_activecode.py │ ├── textfield.py │ └── toctree.rst ├── animation │ ├── README.rst │ ├── __init__.py │ ├── animation.py │ ├── animationrefactor.html │ ├── animationrefactor.js │ ├── chart.html │ ├── jqchart │ │ ├── gChartBasic.html │ │ ├── jquery.gchart.ext.js │ │ ├── jquery.gchart.ext.min.js │ │ ├── jquery.gchart.ext.pack.js │ │ ├── jquery.gchart.graphviz.js │ │ ├── jquery.gchart.graphviz.min.js │ │ ├── jquery.gchart.graphviz.pack.js │ │ ├── jquery.gchart.icons.js │ │ ├── jquery.gchart.icons.min.js │ │ ├── jquery.gchart.icons.pack.js │ │ ├── jquery.gchart.js │ │ ├── jquery.gchart.min.js │ │ └── jquery.gchart.pack.js │ ├── js │ │ ├── animationbase.js │ │ ├── sortmodels.js │ │ └── sortviewers.js │ ├── searchmodels.js │ ├── simpletree.html │ ├── simpletree.js │ ├── sortingbase.html │ ├── sortingdemo.html │ ├── sortingdemo.js │ ├── sortingpackage.html │ ├── sortingpackage.js │ └── toctree.rst ├── assignment │ ├── __init__.py │ └── toctree.rst ├── blockly │ ├── README.rst │ ├── __init__.py │ ├── blockly.py │ ├── js │ │ ├── blockly_compressed.js │ │ ├── blocks_compressed.js │ │ ├── javascript_compressed.js │ │ ├── media │ │ │ ├── 1x1.gif │ │ │ ├── click.mp3 │ │ │ ├── click.ogg │ │ │ ├── click.wav │ │ │ ├── delete.mp3 │ │ │ ├── delete.ogg │ │ │ ├── delete.wav │ │ │ ├── handclosed.cur │ │ │ ├── handopen.cur │ │ │ ├── quote0.png │ │ │ ├── quote1.png │ │ │ ├── trashbody.png │ │ │ ├── trashlid.png │ │ │ └── tree.png │ │ ├── msg │ │ │ └── js │ │ │ │ └── en.js │ │ └── python_compressed.js │ └── toctree.rst ├── cellbotics │ ├── __init__.py │ ├── js │ │ ├── auto-bind.js │ │ ├── ble.js │ │ ├── permissions_polyfill.js │ │ ├── sensor_polyfill │ │ │ ├── geolocation-sensor.js │ │ │ ├── motion-sensors.js │ │ │ ├── sensor.js │ │ │ └── toctree.rst │ │ └── simple_sensor.js │ ├── test │ │ ├── __init__.py │ │ ├── _sources │ │ │ └── index.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ ├── test_cellbotics.py │ │ └── toctree.rst │ └── toctree.rst ├── chapterdb │ ├── __init__.py │ ├── dbchapterinfo.py │ └── toctree.rst ├── clickableArea │ ├── README.md │ ├── __init__.py │ ├── clickable.py │ ├── css │ │ └── clickable.css │ ├── js │ │ ├── clickable.js │ │ └── timedclickable.js │ ├── test │ │ ├── __init__.py │ │ ├── _sources │ │ │ └── index.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_clickableArea.py │ └── toctree.rst ├── codelens │ ├── README.rst │ ├── __init__.py │ ├── css │ │ ├── modal-basic.css │ │ ├── pytutor.css │ │ └── x.png │ ├── js │ │ ├── codelens.js │ │ └── pytutor-embed.bundle.js │ ├── pg_encoder.py │ ├── pg_logger.py │ ├── test │ │ ├── _sources │ │ │ └── index.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_codelens.py │ ├── toctree.rst │ └── visualizer.py ├── common │ ├── README.rst │ ├── __init__.py │ ├── css │ │ ├── active.png │ │ ├── completed.png │ │ ├── presenter_mode.css │ │ ├── runestone-custom-sphinx-bootstrap.css │ │ └── user-highlights.css │ ├── images │ │ └── play_overlay_icon.png │ ├── js │ │ ├── bookfuncs.js │ │ ├── jquery.highlight.js │ │ ├── jquery.idle-timer.js │ │ ├── jquery_i18n │ │ │ ├── jquery.i18n.emitter.bidi.js │ │ │ ├── jquery.i18n.emitter.js │ │ │ ├── jquery.i18n.fallbacks.js │ │ │ ├── jquery.i18n.js │ │ │ ├── jquery.i18n.language.js │ │ │ ├── jquery.i18n.messagestore.js │ │ │ └── jquery.i18n.parser.js │ │ ├── presenter_mode.js │ │ ├── pretext.js │ │ ├── renderComponent.js │ │ ├── runestonebase.js │ │ ├── theme.js │ │ └── user-highlights.js │ ├── project_template │ │ ├── _sources │ │ │ └── index.rst │ │ ├── _static │ │ │ ├── .gitignore │ │ │ └── clock.png │ │ ├── _templates │ │ │ └── plugin_layouts │ │ │ │ └── sphinx_bootstrap │ │ │ │ ├── globaltoc.html │ │ │ │ ├── layout.html │ │ │ │ ├── localtoc.html │ │ │ │ ├── progress.html │ │ │ │ ├── relations.html │ │ │ │ ├── sourcelink.html │ │ │ │ ├── static │ │ │ │ ├── bootstrap-sphinx.css_t │ │ │ │ ├── bootstrap-sphinx.js │ │ │ │ └── img │ │ │ │ │ ├── Facebook.png │ │ │ │ │ ├── Facebook_icon.png │ │ │ │ │ ├── RAIcon.png │ │ │ │ │ ├── Twitter.png │ │ │ │ │ ├── Twitter_icon.png │ │ │ │ │ └── logo_small.png │ │ │ │ ├── subchapter.html │ │ │ │ ├── subchaptoc.html │ │ │ │ └── theme.conf │ │ ├── conf.tmpl │ │ └── pavement.tmpl │ ├── question_number.py │ ├── runestonedirective.py │ ├── test │ │ ├── __init__.py │ │ └── test_error │ │ │ ├── __init__.py │ │ │ ├── _sources │ │ │ └── index.rst │ │ │ ├── conf.py │ │ │ ├── pavement.py │ │ │ └── test_add_js_error.py │ ├── toctree.rst │ └── xmlcommon.py ├── conftest.py ├── datafile │ ├── README.md │ ├── __init__.py │ ├── css │ │ └── datafile.css │ ├── js │ │ └── datafile.js │ ├── test │ │ ├── _sources │ │ │ ├── LutherBellPic.jpg │ │ │ └── index.rst │ │ ├── conf.py │ │ └── pavement.py │ └── toctree.rst ├── disqus │ ├── __init__.py │ ├── disqus.py │ └── toctree.rst ├── dragndrop │ ├── README.md │ ├── __init__.py │ ├── css │ │ └── dragndrop.css │ ├── dragndrop.py │ ├── js │ │ ├── DragDropTouch.js │ │ ├── dragndrop-i18n.en.js │ │ ├── dragndrop-i18n.pt-br.js │ │ ├── dragndrop-i18n.sr-Cyrl.js │ │ ├── dragndrop.js │ │ └── timeddnd.js │ ├── test │ │ ├── __init__.py │ │ ├── _sources │ │ │ └── index.rst │ │ ├── conf.py │ │ ├── drag_and_drop_helper.js │ │ ├── jquery_load_helper.js │ │ ├── pavement.py │ │ └── test_dragndrop.py │ └── toctree.rst ├── fitb │ ├── __init__.py │ ├── css │ │ └── fitb.css │ ├── fitb.py │ ├── js │ │ ├── fitb-i18n.en.js │ │ ├── fitb-i18n.pt-br.js │ │ ├── fitb-i18n.sr-Cyrl.js │ │ ├── fitb.js │ │ └── timedfitb.js │ ├── test │ │ ├── __init__.py │ │ ├── _sources │ │ │ └── index.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_fitb.py │ └── toctree.rst ├── groupsub │ ├── __init__.py │ ├── css │ │ └── groupsub.css │ ├── groupsub.py │ ├── js │ │ └── groupsub.js │ └── test │ │ ├── _sources │ │ └── index.rst │ │ ├── conf.py │ │ └── pavement.py ├── hparsons │ ├── README.md │ ├── __init__.py │ ├── css │ │ ├── hljs-xcode.css │ │ └── hparsons.css │ ├── hparsons.py │ ├── js │ │ ├── BlockFeedback.js │ │ ├── SQLFeedback.js │ │ ├── blockGrader.js │ │ ├── hparsons.js │ │ └── hparsonsFeedback.js │ ├── test.rst │ ├── test │ │ ├── _sources │ │ │ ├── _static │ │ │ │ ├── hptest.db │ │ │ │ └── test.db │ │ │ └── index.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_hparsons.py │ └── toctree.rst ├── khanex │ ├── README.txt │ ├── __init__.py │ ├── js │ │ ├── khan-exercise.js │ │ └── khanex.js │ ├── khanex.py │ └── test │ │ ├── _sources │ │ ├── Khanex │ │ │ ├── TestKhanex.rst │ │ │ └── toctree.rst │ │ └── index.rst │ │ ├── conf.py │ │ └── pavement.py ├── lp │ ├── __init__.py │ ├── css │ │ └── html4css1.css │ ├── for_loop.py │ ├── inlinesyntaxhighlight.py │ ├── js │ │ └── lp.js │ ├── lp.py │ ├── lp_common_lib.py │ ├── test │ │ ├── _sources │ │ │ ├── index.rst │ │ │ └── lp_tester.s │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_lp.py │ └── toctree.rst ├── matrixeq │ ├── __init__.py │ ├── css │ │ └── matrixeq.css │ ├── js │ │ └── matrixeq.js │ ├── matrixeq.py │ ├── test │ │ ├── __init__.py │ │ ├── _sources │ │ │ └── index.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_matrixeq.py │ └── toctree.rst ├── mchoice │ ├── .gitignore │ ├── README.md │ ├── __init__.py │ ├── assess.py │ ├── assessbase.py │ ├── css │ │ ├── bootstrap.min.css │ │ └── mchoice.css │ ├── js │ │ ├── mchoice-i18n.en.js │ │ ├── mchoice-i18n.pt-br.js │ │ ├── mchoice-i18n.sr-Cyrl.js │ │ ├── mchoice.js │ │ └── timedmc.js │ ├── multiplechoice.py │ ├── test │ │ ├── __init__.py │ │ ├── _sources │ │ │ └── index.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_assess.py │ └── toctree.rst ├── meta │ ├── __init__.py │ ├── meta.py │ └── toctree.rst ├── overview.rst ├── parsons │ ├── README.md │ ├── README.rst │ ├── __init__.py │ ├── css │ │ ├── images │ │ │ ├── parsons-ll.svg │ │ │ ├── parsons-lr.svg │ │ │ ├── parsons-rl.svg │ │ │ └── parsons-rr.svg │ │ ├── parsons.css │ │ └── prettify.css │ ├── js │ │ ├── dagGrader.js │ │ ├── dagHelpers.js │ │ ├── hammer.min.js │ │ ├── lineGrader.js │ │ ├── parsons-i18n.en.js │ │ ├── parsons-i18n.pt-br.js │ │ ├── parsons-i18n.sr-Cyrl.js │ │ ├── parsons.js │ │ ├── parsonsBlock.js │ │ ├── parsonsLine.js │ │ ├── prettify.js │ │ └── timedparsons.js │ ├── parsons.py │ ├── test │ │ ├── __init__.py │ │ ├── _sources │ │ │ └── index.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_parsons.py │ └── toctree.rst ├── poll │ ├── __init__.py │ ├── css │ │ └── poll.css │ ├── js │ │ └── poll.js │ ├── poll.py │ ├── test │ │ ├── __init__.py │ │ ├── _sources │ │ │ └── index.rst │ │ ├── build │ │ │ └── .gitignore │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_poll.py │ └── toctree.rst ├── pretext │ ├── README.rst │ ├── __init__.py │ ├── chapter_pop.py │ ├── runestone-manifest.xml │ └── toctree.rst ├── question │ ├── __init__.py │ ├── question.py │ ├── test │ │ ├── __init__.py │ │ ├── _sources │ │ │ └── index.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_question.py │ └── toctree.rst ├── quizly │ ├── README.txt │ ├── __init__.py │ ├── js │ │ └── quizly.js │ ├── quizly.py │ ├── test │ │ ├── _sources │ │ │ ├── Quizly │ │ │ │ ├── TestQuizly.rst │ │ │ │ └── toctree.rst │ │ │ └── index.rst │ │ ├── conf.py │ │ └── pavement.py │ └── toctree.rst ├── reveal │ ├── README.md │ ├── __init__.py │ ├── css │ │ └── reveal.css │ ├── js │ │ └── reveal.js │ ├── reveal.py │ ├── test │ │ ├── __init__.py │ │ ├── _sources │ │ │ └── index.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_reveal.py │ └── toctree.rst ├── selectquestion │ ├── __init__.py │ ├── css │ │ └── selectquestion.css │ ├── js │ │ └── selectone.js │ ├── selectone.py │ └── toctree.rst ├── server │ ├── __init__.py │ ├── componentdb.py │ ├── toctree.rst │ └── utils.py ├── shared_conftest.py ├── shortanswer │ ├── README.md │ ├── __init__.py │ ├── css │ │ └── shortanswer.css │ ├── js │ │ ├── sa_template.html │ │ ├── shortanswer.js │ │ └── timed_shortanswer.js │ ├── shortanswer.py │ ├── test │ │ ├── __init__.py │ │ ├── _sources │ │ │ ├── Figures │ │ │ │ └── LutherBellPic.jpg │ │ │ └── index.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_shortanswer.py │ └── toctree.rst ├── showeval │ ├── README.md │ ├── __init__.py │ ├── css │ │ └── showEval.css │ ├── js │ │ ├── LICENSE.txt │ │ └── showEval.js │ ├── showeval.py │ ├── test │ │ ├── __init__.py │ │ ├── _sources │ │ │ ├── index.rst │ │ │ ├── replace.rst │ │ │ └── trace.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_showEval.py │ └── toctree.rst ├── spreadsheet │ ├── __init__.py │ ├── css │ │ ├── japp.css │ │ ├── jexcel.css │ │ ├── jexcel.min.css │ │ └── spreadsheet.css │ ├── js │ │ └── spreadsheet.js │ ├── spreadsheet.py │ ├── test │ │ ├── _sources │ │ │ ├── Iris.csv │ │ │ └── index.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_spreadsheet.py │ └── toctree.rst ├── tabbedStuff │ ├── README.md │ ├── __init__.py │ ├── css │ │ └── tabbedstuff.css │ ├── js │ │ └── tabbedstuff.js │ ├── tabbedStuff.py │ ├── test │ │ ├── __init__.py │ │ ├── _sources │ │ │ └── index.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_tabbedStuff.py │ └── toctree.rst ├── timed │ ├── __init__.py │ ├── css │ │ └── timed.css │ ├── js │ │ └── timed.js │ ├── test │ │ ├── _sources │ │ │ ├── index.rst │ │ │ └── multiquestion.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_timed.py │ ├── timedassessment.py │ └── toctree.rst ├── utility │ ├── __init__.py │ ├── toctree.rst │ └── utility.py ├── video │ ├── __init__.py │ ├── css │ │ └── video.css │ ├── js │ │ └── runestonevideo.js │ ├── test │ │ ├── _sources │ │ │ └── index.rst │ │ ├── conf.py │ │ └── pavement.py │ ├── toctree.rst │ └── video.py ├── wavedrom │ ├── __init__.py │ ├── js │ │ └── wavedrom.js │ ├── test │ │ ├── _sources │ │ │ └── index.rst │ │ ├── conf.py │ │ ├── pavement.py │ │ └── test_wavedrom.py │ └── wavedrom.py ├── webgldemo │ ├── __init__.py │ ├── css │ │ ├── webgldemo.css │ │ └── webglinteractive.css │ ├── js │ │ ├── FileSaver.min.js │ │ └── webglinteractive.js │ ├── test │ │ ├── _sources │ │ │ ├── 01_introduction │ │ │ │ ├── nested │ │ │ │ │ └── test02.rst │ │ │ │ └── test01.rst │ │ │ └── index.rst │ │ └── pavement.py │ ├── toctree.rst │ └── webgldemo.py └── webwork │ ├── js │ └── webwork.js │ └── toctree.rst ├── scripts ├── buildptx.py ├── dist2xml.py └── makePtx.sh ├── toxml.py ├── webpack.config.js └── webpack.index.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | jquery: true, 6 | }, 7 | extends: "eslint:recommended", 8 | parserOptions: { 9 | ecmaVersion: 12, 10 | sourceType: "module", 11 | }, 12 | rules: {}, 13 | globals: { 14 | eBookConfig: "readonly", 15 | require: "readonly", 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length=88 3 | ignore=F821, 4 | W503, 5 | E203, # space before : 6 | E501, 7 | E265, 8 | E266, # too many # 9 | E711, 10 | E712 # web2py needs to compare to == True or == False for queries 11 | 12 | builtins=settings, 13 | request, 14 | response, 15 | db, 16 | auth, 17 | redirect, 18 | XML, 19 | URL, 20 | T, 21 | HTTP, 22 | cache, 23 | gluon, 24 | verifyInstructorStatus 25 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # ********* 2 | # |docname| 3 | # ********* 4 | # These are supported funding model platforms 5 | 6 | #github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 7 | patreon: runestone 8 | #open_collective: # Replace with a single Open Collective username 9 | #ko_fi: # Replace with a single Ko-fi username 10 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 11 | #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 12 | #liberapay: # Replace with a single Liberapay username 13 | #issuehunt: # Replace with a single IssueHunt username 14 | #otechie: # Replace with a single Otechie username 15 | #custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | custom: ["https://www.paypal.me/runestoneinteractive"] -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Runestone Components Tests 5 | 6 | on: 7 | push: 8 | branches: [master, peer_support] 9 | pull_request: 10 | branches: [master, peer_support] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | env: 16 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} 17 | strategy: 18 | matrix: 19 | python-version: ["3.8", "3.9"] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - uses: actions/setup-node@v2.1.2 24 | - uses: act10ns/slack@v1 25 | with: 26 | status: starting 27 | channel: "#builds" 28 | if: always() 29 | 30 | - name: Install npm dependencies 31 | run: | 32 | npm install 33 | - name: Set up Python ${{ matrix.python-version }} 34 | uses: actions/setup-python@v2 35 | with: 36 | python-version: ${{ matrix.python-version }} 37 | - name: Install Python dependencies 38 | run: | 39 | # Install Poetry per the `instructions `. 40 | curl -sSL https://install.python-poetry.org | python3 - 41 | $HOME/.local/bin/poetry install 42 | - name: Tests 43 | run: | 44 | $HOME/.local/bin/poetry run pytest 45 | 46 | - uses: act10ns/slack@v1 47 | with: 48 | status: ${{ job.status }} 49 | steps: ${{ toJson(steps) }} 50 | channel: "#builds" 51 | if: always() 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ********* 2 | # |docname| 3 | # ********* 4 | *.py[cod] 5 | *.log 6 | .venv 7 | jsdist 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Packages 13 | *.egg 14 | *.egg-info 15 | build 16 | eggs 17 | parts 18 | bin 19 | var 20 | sdist 21 | dist/* 22 | develop-eggs 23 | .installed.cfg 24 | __pycache__ 25 | .cache 26 | _deploy 27 | 28 | # Installer logs 29 | pip-log.txt 30 | 31 | # Unit test / coverage reports 32 | .coverage 33 | .tox 34 | nosetests.xml 35 | 36 | # Translations 37 | *.mo 38 | 39 | # Mr Developer 40 | .mr.developer.cfg 41 | .project 42 | .pydevproject 43 | .idea 44 | bower_componenets/ 45 | .venv 46 | *~ 47 | 48 | # Files produced by runestone builds. 49 | runestone/*/test/build_info 50 | **/sphinx_settings.json 51 | build_info 52 | runestone/dist*.tgz 53 | dist/ 54 | 55 | # IDEs 56 | .vscode/ 57 | .vs/ 58 | **/sphinx-enki-info.txt 59 | # Mac stuff 60 | .DS_Store 61 | node_modules 62 | runestone/dist 63 | runestone/.DS_Store 64 | runestone/assess/.DS_Store 65 | runestone/.DS_Store 66 | runestone/.DS_Store 67 | runestone/assess/.DS_Store 68 | .DS_Store 69 | .history 70 | .nova 71 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "eBookConfig", 4 | "$", 5 | "jQuery", 6 | "Sk", 7 | "allVisualizers", 8 | "console" 9 | ], 10 | "esversion": 8, 11 | "quotmark": true, 12 | "strict": "implied", 13 | "sub": true, 14 | "browser": true, 15 | "maxerr": 200, 16 | "globalstrict": true, 17 | "devel": true 18 | 19 | } -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/_templates -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # ******************************************** 2 | # |docname| - Read the Docs configuration file 3 | # ******************************************** 4 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 5 | 6 | # Required 7 | version: 2 8 | 9 | # TODO: need some way to invoke a runestone build. Idea: a mock conf.py that runs the build in a subprocess. 10 | sphinx: 11 | configuration: conf.py 12 | 13 | # Optionally set the version of Python and requirements required to build your docs 14 | python: 15 | version: 3.8 16 | install: 17 | - method: pip 18 | path: . -------------------------------------------------------------------------------- /ACKNOWLEDGEMENTS.rst: -------------------------------------------------------------------------------- 1 | **************** 2 | Acknowledgements 3 | **************** 4 | Runestone Interactive would never have come to life without many other open source projects and people. This is an attempt to acknowledge those projects and people. 5 | 6 | 7 | * CodeLens -- Philip Guo and the Python Tutor Project -- pythontutor.come 8 | * ActiveCode -- The Skulpt project -- skulpt.org 9 | * Parson's Problems -- Ville Karavirta and the team at Georgia Tech who has added tons 10 | * showEval -- Al Sweigart 11 | * Short Answer -- Cory Bart at Virginia Tech 12 | * the Blockly project @ Google 13 | * The Sphinx and Docutils projects 14 | 15 | Luther College 16 | ============== 17 | * Brad Miller 18 | * David Ranum 19 | * Roman Yasinovskyy 20 | * Summer Research Students 21 | 22 | * Isaac Dontje-Lindell 23 | * Isaiah Mayerchak 24 | * Kirby Olson 25 | * Robert Miller 26 | * Devin Hanggi 27 | * Hillary Gardener 28 | * Tyler Conzett 29 | 30 | Georgia Tech 31 | ============ 32 | * Barbara Ericson 33 | * Mark Guzdial 34 | * Jeff Rick 35 | * Tommy Hubbard 36 | * Siwei Li 37 | * Matt Moldovan 38 | 39 | University of Michigan 40 | ====================== 41 | * Paul Resnick 42 | * Jackie Cohen 43 | * Charles Severence 44 | 45 | Mississippi State University 46 | ============================ 47 | * Bryan Jones 48 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011-2015 Bradley N. Miller 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | # -------------------------------------------------------------------------------- /images/runeCompo-index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RunestoneInteractive/RunestoneComponents/d05ee263a5cc9d9c029cfc31f8f8ecdcf669fb11/images/runeCompo-index.png -------------------------------------------------------------------------------- /index.rst: -------------------------------------------------------------------------------- 1 | ******************** 2 | Runestone Components 3 | ******************** 4 | This site documents the working of the Runestone Components. See the `Runestone Interactive Overview `_ or the `Runestone instructor's guide `_. 5 | 6 | Demo linking to the Runestone Server docs: :ref:`assignments/grades_report endpoint`. 7 | 8 | 9 | Getting started 10 | =============== 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | README 15 | CONTRIBUTING 16 | I18N 17 | 18 | 19 | Components 20 | ========== 21 | .. toctree:: 22 | :maxdepth: 1 23 | :glob: 24 | 25 | runestone/overview 26 | runestone/*/toctree 27 | runestone/__init__.py 28 | runestone/__main__.py 29 | runestone/conftest.py 30 | runestone/shared_conftest.py 31 | 32 | 33 | Packaging 34 | ========= 35 | .. toctree:: 36 | :maxdepth: 2 37 | 38 | setup.py 39 | setup.cfg 40 | MANIFEST.in 41 | webpack.config.js 42 | 43 | Misc 44 | ==== 45 | .. toctree:: 46 | :maxdepth: 2 47 | 48 | ACKNOWLEDGEMENTS 49 | .github/FUNDING.yml 50 | runestone/conftest.py 51 | runestone/shared_conftest.py 52 | .gitignore 53 | .github/workflows/python-package.yml 54 | .readthedocs.yml 55 | conf.py 56 | codechat_config.yaml 57 | requirements-dev.in 58 | requirements.in 59 | public/index.html 60 | makeRelease.sh 61 | -------------------------------------------------------------------------------- /makeRelease.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ********* 3 | # |docname| 4 | # ********* 5 | 6 | set -e 7 | 8 | if [ $# -eq 0 ] 9 | then 10 | echo "Usage: makeRelease " 11 | exit 12 | fi 13 | 14 | while true; do 15 | read -p "Did you update/commit the version in pyproject.toml " yn 16 | case $yn in 17 | [Yy]* ) break;; 18 | [Nn]* ) exit;; 19 | * ) echo "Please answer yes or no.";; 20 | esac 21 | done 22 | 23 | if [ -z "$(git status --porcelain)" ]; then 24 | echo "Working directory clean" 25 | else 26 | read -p "You have uncommitted changes do you really want to release? " yn 27 | case $yn in 28 | [Yy]* ) break;; 29 | [Nn]* ) exit;; 30 | * ) echo "Please answer yes or no.";; 31 | esac 32 | fi 33 | 34 | echo "Building webpack bundle" 35 | npm run dist 36 | 37 | echo "building python package" 38 | poetry build 39 | poetry publish 40 | 41 | echo "tagging this release and pushing to github" 42 | 43 | git tag -a $1 -m 'tag new version' 44 | git push --follow-tags 45 | gh release create v$1 --generate-notes 46 | 47 | if [ -d ~/.virtualenvs/json2xml ] 48 | then 49 | 50 | echo "Creating dist for PreTeXt" 51 | source ~/.virtualenvs/json2xml/bin/activate 52 | python scripts/dist2xml.py $1 53 | cd runestone 54 | tar --strip-components 1 -zcf dist-$1.tgz dist/* 55 | echo "Installing release on CDN" 56 | scp dist-$1.tgz balance.runestoneacademy.org:~/ 57 | ssh balance.runestoneacademy.org /home/bmiller/bin/install_release.sh $1 58 | mv dist-$1.tgz ../jsdist 59 | cp dist/webpack_static_imports.xml ~/Pretext/pretext/xsl/support/runestone-services.xml 60 | cp dist/webpack_static_imports.xml ~/.ptx/xsl/support/runestone-services.xml 61 | else 62 | echo "Warning: no json2xml ve found skipping pretext" 63 | fi 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WebComponents", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/runestone.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "check-env": "node -e 'console.log(process.env)' | grep npm", 9 | "build": "webpack --mode=development", 10 | "buildj": "webpack --mode=development --profile --json > stats.json", 11 | "watch": "webpack --mode=development --watch", 12 | "serve": "cd dist; python -m http.server 8080; cd ..", 13 | "dist": "webpack --mode=production", 14 | "distj": "webpack --mode=production --profile --json > stats.json", 15 | "analyze": "webpack-bundle-analyzer stats.json runestone/dist" 16 | }, 17 | "keywords": [], 18 | "author": "", 19 | "license": "ISC", 20 | "devDependencies": { 21 | "compression-webpack-plugin": "^9.0.0", 22 | "copy-webpack-plugin": "^9.0.0", 23 | "css-loader": "^6.0.0", 24 | "css-minimizer-webpack-plugin": "^3.0.0", 25 | "html-loader": "^3.0.0", 26 | "html-webpack-plugin": "^5.0.0", 27 | "mini-css-extract-plugin": "^2.0.0", 28 | "webpack": "^5.61.0", 29 | "webpack-bundle-analyzer": "^4.0.0", 30 | "webpack-cli": "^4.0.0" 31 | }, 32 | "dependencies": { 33 | "-": "0.0.1", 34 | "bootstrap": "3.4.1", 35 | "codemirror": "^5.59.4", 36 | "handsontable": "7.2.2", 37 | "jexcel": "^3.9.1", 38 | "jquery-ui": "1.10.4", 39 | "micro-parsons": "https://github.com/amy21206/micro-parsons-element/releases/download/v0.1.4/micro-parsons-0.1.4.tgz", 40 | "select2": "^4.1.0-rc.0", 41 | "sql.js": "1.5.0", 42 | "vega-embed": "3.14.0", 43 | "wavedrom": "^2.0.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | # **************************** 2 | # |docname| - Configure pytest 3 | # **************************** 4 | [pytest] 5 | # See `Registering marks `_; this mark is defined in `selenium_module_fixture`. 6 | markers = 7 | exit_status_success: Determine if the runestone build exit code is checked by ``selenium_module_fixture``. -------------------------------------------------------------------------------- /runestone/accessibility/__init__.py: -------------------------------------------------------------------------------- 1 | # ************************************ 2 | # |docname| - the accessibility module 3 | # ************************************ 4 | from .accessibility import * 5 | -------------------------------------------------------------------------------- /runestone/accessibility/accessibility.py: -------------------------------------------------------------------------------- 1 | """ 2 | ********************************************** 3 | |docname| - select an accessibility stylesheet 4 | ********************************************** 5 | By setting the `accessibility_style` config value (in `conf.py <../common/project_template/conf.tmpl>` of 6 | an interactive book project) you can select what accessibility stylesheet 7 | you want to add (``normal``, ``light``, ``darkest``, or ``none``). 8 | 9 | An accessibility stylesheet covers: 10 | 11 | - Change of nav bar to color code based on Users using either mouse or keyboard to navigate the menu 12 | - Adjusting bootstrap buttons to invert color on active and on focus for accessibility for users 13 | - Changing default bootstrap buttons to follow WCAG 2.0 guidelines 14 | 15 | | ``acessibility.css`` reflects WCAG 2.0 AA compliance 16 | | ``acessibilitydarkest.css`` reflects WCAG 2.0 AAA compliance 17 | | ``accessibilitylight.css`` doesn't change bootstrap colors but adds inversion 18 | 19 | Personally we prefered WCAG 2.0 AA compliance, so ``accessibility.css`` 20 | reflects ideal changes 21 | 22 | .. figure:: https://i.imgur.com/jFzkI6g.jpg 23 | 24 | This shows the different CSS files from most compliance (left) to least compliance (right). 25 | """ 26 | 27 | 28 | def setup(app): 29 | app.add_config_value("accessibility_style", "normal", "html") 30 | 31 | # Since the ``init_values()`` method of the ``app.config`` object (Config class) 32 | # is not invoked yet (will be invoked after the setup of all extensions), 33 | # we need to access the raw config dictionary here. 34 | acc_style = app.config._raw_config.get("accessibility_style", "normal") 35 | 36 | if acc_style == "normal": 37 | app.add_autoversioned_stylesheet("accessibility.css") 38 | elif acc_style == "light": 39 | app.add_autoversioned_stylesheet("accessibilitylight.css") 40 | elif acc_style == "darkest": 41 | app.add_autoversioned_stylesheet("accessibilitydarkest.css") 42 | -------------------------------------------------------------------------------- /runestone/accessibility/toctree.rst: -------------------------------------------------------------------------------- 1 | ************************************************* 2 | Accessibility 3 | ************************************************* 4 | .. toctree:: 5 | :maxdepth: 1 6 | :glob: 7 | 8 | *.py 9 | css/*.css 10 | -------------------------------------------------------------------------------- /runestone/activecode/README.md: -------------------------------------------------------------------------------- 1 | ### activecode 2 | 3 | ```html 4 |
 5 | def main()
 6 |     print("hello world")
 7 |     
 8 | main()
 9 | 
10 | ``` 11 | 12 | The body of the ``pre`` tag contains code to be loaded into the editor initially. The following attributes are options and control what pieces and parts of the component will be visible. 13 | 14 | * ``data-component`` attribute identifies this as an activecode component 15 | * ``class`` The usual CSS class options 16 | * ``id`` must be unique in the document 17 | * ``data-lang`` for activecode can be python javascript or html 18 | * ``data-autorun`` run this activecode as soon as the page is loaded 19 | * ``data-hidecode`` make the editor hidden initially 20 | * ``data-include`` list of ids of other activecodes. The code form each will be prepended to the code to run 21 | * ``data-timelimit`` either False to turn off runtime limit checking or an integer representing the number of milliseconds until timeout. 22 | * ``data-coach`` add a button to display code coach information 23 | * ``data-codelens`` add a button "Run this in Codelens" 24 | 25 | 26 | For Python to work in the browser you must also obtain and include via a script tag ``skulpt.min.js`` and 27 | ``skulpt-stdlib.js`` along with ``codemirror.js`` and of course ``activecode.js`` 28 | 29 | Soon, many of these requirements will be incorporated into one handy ``runestone.js`` file. 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /runestone/activecode/__init__.py: -------------------------------------------------------------------------------- 1 | # ********* 2 | # |docname| 3 | # ********* 4 | from .activecode import * 5 | -------------------------------------------------------------------------------- /runestone/activecode/css/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RunestoneInteractive/RunestoneComponents/d05ee263a5cc9d9c029cfc31f8f8ecdcf669fb11/runestone/activecode/css/close.png -------------------------------------------------------------------------------- /runestone/activecode/js/activecode-i18n.sr-Cyrl.js: -------------------------------------------------------------------------------- 1 | $.i18n().load({ 2 | "sr-Cyrl": { 3 | msg_activecode_load_history: "Учитај историју", 4 | msg_activecode_audio_tour: "Аудио тура", 5 | msg_activecode_loaded_code: "Ваш изворни код је учитан.", 6 | msg_activecode_no_saved_code: "Не постоји снимљен код.", 7 | msg_activecode_run_code: "Покрени програм", 8 | msg_activecode_show_feedback: "Прикажи резултат", 9 | msg_activecode_show_code: "Прикажи код", 10 | msg_activecode_hide_code: "Затвори код", 11 | msg_activecode_show_codelens: "Корак по корак", 12 | msg_activecode_show_in_codelens: "Корак по корак", 13 | msg_activecode_hide_codelens: "Затвори корак по корак", 14 | 15 | msg_sctivecode_parse_error: 16 | "Синтаксна грешка (parse error) значи да Пајтон не разуме синтаксу у линији кога на коју порука о грешци указује. Типични примери овакве грешке су заборавлјена двотачка код 'if' или 'for' исказа или заборављена запета између аргумената код позива функције", 17 | msg_sctivecode_parse_error_fix: 18 | "Да бисте исправили синтаксну грешку треба пажњиво да погледате линију изворног кода на коју указује поруга о грешци и можда претходну линину изворног кода. Проверите да ли су поштована сва синтаксна правила Пајтона.", 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /runestone/activecode/js/activecode_html.js: -------------------------------------------------------------------------------- 1 | import { ActiveCode } from "./activecode.js"; 2 | 3 | export default class HTMLActiveCode extends ActiveCode { 4 | constructor(opts) { 5 | super(opts); 6 | this.code = $(" 20 | 21 | 22 | 23 |
24 |
25 | 26 |
27 |
Your current saved answer is shown above.
28 | 29 | 30 |

shortanswer (question1)

31 | 32 | -------------------------------------------------------------------------------- /runestone/shortanswer/js/timed_shortanswer.js: -------------------------------------------------------------------------------- 1 | import ShortAnswer from "./shortanswer.js"; 2 | 3 | export default class TimedShortAnswer extends ShortAnswer { 4 | constructor(opts) { 5 | super(opts); 6 | this.renderTimedIcon(this.containerDiv); 7 | this.hideButtons(); 8 | } 9 | hideButtons() { 10 | $(this.submitButton).hide(); 11 | } 12 | renderTimedIcon(component) { 13 | // renders the clock icon on timed components. The component parameter 14 | // is the element that the icon should be appended to. 15 | var timeIconDiv = document.createElement("div"); 16 | var timeIcon = document.createElement("img"); 17 | $(timeIcon).attr({ 18 | src: "../_static/clock.png", 19 | style: "width:15px;height:15px", 20 | }); 21 | timeIconDiv.className = "timeTip"; 22 | timeIconDiv.title = ""; 23 | timeIconDiv.appendChild(timeIcon); 24 | $(component).prepend(timeIconDiv); 25 | } 26 | checkCorrectTimed() { 27 | return "I"; // we ignore this in the grading 28 | } 29 | hideFeedback() { 30 | $(this.feedbackDiv).hide(); 31 | } 32 | } 33 | 34 | if (typeof window.component_factory === "undefined") { 35 | window.component_factory = {}; 36 | } 37 | 38 | window.component_factory.shortanswer = function (opts) { 39 | if (opts.timed) { 40 | return new TimedShortAnswer(opts); 41 | } 42 | return new ShortAnswer(opts); 43 | }; 44 | -------------------------------------------------------------------------------- /runestone/shortanswer/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RunestoneInteractive/RunestoneComponents/d05ee263a5cc9d9c029cfc31f8f8ecdcf669fb11/runestone/shortanswer/test/__init__.py -------------------------------------------------------------------------------- /runestone/shortanswer/test/_sources/Figures/LutherBellPic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RunestoneInteractive/RunestoneComponents/d05ee263a5cc9d9c029cfc31f8f8ecdcf669fb11/runestone/shortanswer/test/_sources/Figures/LutherBellPic.jpg -------------------------------------------------------------------------------- /runestone/shortanswer/test/_sources/index.rst: -------------------------------------------------------------------------------- 1 | =============================== 2 | Testing: Short Answer Questions 3 | =============================== 4 | 5 | .. Here is were you specify the content and order of your new book. 6 | 7 | .. Each section heading (e.g. "SECTION 1: A Random Section") will be 8 | a heading in the table of contents. Source files that should be 9 | generated and included in that section should be placed on individual 10 | lines, with one line separating the first source filename and the 11 | :maxdepth: line. 12 | 13 | .. Sources can also be included from subfolders of this directory. 14 | (e.g. "DataStructures/queues.rst"). 15 | 16 | 17 | Short Answer 18 | ------------ 19 | 20 | .. shortanswer:: test_short_answer_1 21 | 22 | What are the colors in the rainbow? 23 | 24 | 25 | .. shortanswer:: test_short_answer_2 26 | :optional: 27 | :mathjax: 28 | 29 | What are the colors in the rainbow? 30 | What is meaning of :math:`\pi r^2` 31 | How about an image? 32 | 33 | .. image:: Figures/LutherBellPic.jpg 34 | :width: 200 35 | 36 | This is the famous Luther Bell! 37 | -------------------------------------------------------------------------------- /runestone/shortanswer/test/pavement.py: -------------------------------------------------------------------------------- 1 | import paver 2 | from paver.easy import * 3 | import paver.setuputils 4 | 5 | paver.setuputils.install_distutils_tasks() 6 | import os, sys 7 | from runestone.server import get_dburl 8 | from sphinxcontrib import paverutils 9 | import pkg_resources 10 | 11 | sys.path.append(os.getcwd()) 12 | 13 | home_dir = os.getcwd() 14 | master_url = "http://127.0.0.1:8000" 15 | master_app = "runestone" 16 | serving_dir = "./build/sa_test" 17 | dest = "../../static" 18 | 19 | options( 20 | sphinx=Bunch(docroot="."), 21 | build=Bunch( 22 | builddir="./build/sa_test", 23 | sourcedir="_sources", 24 | outdir="./build/sa_test", 25 | confdir=".", 26 | project_name="sa_test", 27 | template_args={ 28 | "course_id": "sa_test", 29 | "login_required": "false", 30 | "appname": master_app, 31 | "loglevel": 0, 32 | "course_url": master_url, 33 | "use_services": "false", 34 | "python3": "false", 35 | "dburl": "", 36 | "downloads_enabled": "true", 37 | "enable_chatcodes": "false", 38 | "allow_pairs": "false", 39 | "basecourse": "sa_test", 40 | }, 41 | ), 42 | ) 43 | 44 | version = pkg_resources.require("runestone")[0].version 45 | options.build.template_args["runestone_version"] = version 46 | 47 | # If DBURL is in the environment override dburl 48 | options.build.template_args["dburl"] = get_dburl(outer=locals()) 49 | 50 | from runestone import build # build is called implicitly by the paver driver. 51 | -------------------------------------------------------------------------------- /runestone/shortanswer/test/test_shortanswer.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test Short Answer question directive 3 | """ 4 | 5 | __author__ = "yasinovskyy" 6 | 7 | DIV_ID = "test_short_answer_1" 8 | 9 | 10 | def get_sa(selenium_utils): 11 | selenium_utils.wait_until_ready(DIV_ID) 12 | selenium_utils.scroll_to_top() 13 | return selenium_utils.driver.find_element_by_id(DIV_ID) 14 | 15 | 16 | def click_button(sa_element): 17 | sa_element.find_element_by_tag_name("button").click() 18 | 19 | 20 | def test_sa1(selenium_utils_get): 21 | """No input. Button not clicked""" 22 | t1 = get_sa(selenium_utils_get) 23 | fb = t1.find_element_by_id(f"{DIV_ID}_feedback") 24 | assert "alert-danger" in fb.get_attribute("class") 25 | 26 | 27 | def test_sa2(selenium_utils_get): 28 | """No input. Button clicked""" 29 | t1 = get_sa(selenium_utils_get) 30 | click_button(t1) 31 | fb = t1.find_element_by_id(f"{DIV_ID}_feedback") 32 | assert "alert-success" in fb.get_attribute("class") 33 | 34 | 35 | def test_sa3(selenium_utils_get): 36 | """Answer entered""" 37 | t1 = get_sa(selenium_utils_get) 38 | ta = t1.find_element_by_id(f"{DIV_ID}_solution") 39 | ta.clear() 40 | ta.send_keys("My answer") 41 | 42 | click_button(t1) 43 | 44 | fb = t1.find_element_by_id(f"{DIV_ID}_feedback") 45 | assert fb is not None 46 | assert "alert-success" in fb.get_attribute("class") 47 | 48 | 49 | # TODO: this is the same as ``_test_sa3``. 50 | def test_sa4(selenium_utils_get): 51 | """Answer entered and cleared""" 52 | t1 = get_sa(selenium_utils_get) 53 | ta = t1.find_element_by_id(f"{DIV_ID}_solution") 54 | ta.clear() 55 | ta.send_keys("My answer") 56 | 57 | click_button(t1) 58 | 59 | fb = t1.find_element_by_id(f"{DIV_ID}_feedback") 60 | assert fb is not None 61 | assert "alert-success" in fb.get_attribute("class") 62 | -------------------------------------------------------------------------------- /runestone/shortanswer/toctree.rst: -------------------------------------------------------------------------------- 1 | ************************************************* 2 | Accessibility 3 | ************************************************* 4 | .. toctree:: 5 | :maxdepth: 1 6 | :glob: 7 | 8 | *.py 9 | js/*.js 10 | css/*.css 11 | test/test_*.py 12 | -------------------------------------------------------------------------------- /runestone/showeval/README.md: -------------------------------------------------------------------------------- 1 |

ShowEval

2 | 3 | ```html 4 |
5 | 6 | 7 |

8 |
9 |
10 | ``` 11 | 12 | The body of the of div with `id=unique_id_goes_here` is the container for the animated code. The div placed immediately above the animation container contains any lines that should be displayed sequentially before the animated code. 13 | 14 | The directive itself can be written in a page's `.rst` file using the templated syntax below. 15 | 16 | ``` 17 | .. showeval:: unique_id_goes_here 18 | :trace_mode: boolean 19 | 20 | ~~Prerequisite Information~~ 21 | 22 | ~~~~ 23 | 24 | ~~Steps~~ 25 | ``` 26 | 27 | The following option controls how the animation behaves. 28 | 29 | * ``trace_mode`` is a required option that can either be `true` or `false` and dictates which of the two animation modes should be used. 30 | 31 | **Trace Mode** (`:trace_mode: true`) will print out a new line for each step of the animation, extending the `evalCont` with `id=unique_id_goes_here` mentioned above. 32 | 33 | **Replace Mode** (`:trace_mode: false`) will display and animate a single line presented in the `evalCont` with `id=unique_id_goes_here` mentioned above. 34 | 35 | 36 | Thanks to Al Sweigart for the inspiration and code that got this feature going! 37 | 38 | See: https://github.com/asweigart/showeval -------------------------------------------------------------------------------- /runestone/showeval/__init__.py: -------------------------------------------------------------------------------- 1 | from .showeval import * 2 | -------------------------------------------------------------------------------- /runestone/showeval/css/showEval.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | .evalCont { 4 | font-family: monospace; 5 | line-height: 1.5em; 6 | padding: 9.5px; 7 | background-color: #F5F5F5; 8 | border: 1px solid #CCC; 9 | border-radius: 4px; 10 | display: block; 11 | width: auto; 12 | color: #333 13 | } 14 | .anno { 15 | width: auto; 16 | min-width: 150px; 17 | max-width: 250px; 18 | max-height: 100px; 19 | background-color: #E2EBF6; 20 | border: 1px solid #A6B3B6; 21 | display: block; 22 | position: absolute; 23 | left: 80%; 24 | top: 5px; 25 | overflow: scroll; 26 | font-family: sans-serif; 27 | box-shadow: -2px 1px 5px #CCC; 28 | } 29 | .currentStepDiv { 30 | position: relative; 31 | } 32 | -------------------------------------------------------------------------------- /runestone/showeval/js/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Al Sweigart 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the PyAutoGUI nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /runestone/showeval/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RunestoneInteractive/RunestoneComponents/d05ee263a5cc9d9c029cfc31f8f8ecdcf669fb11/runestone/showeval/test/__init__.py -------------------------------------------------------------------------------- /runestone/showeval/test/_sources/index.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | Testing: ShowEval Questions 3 | =========================== 4 | 5 | ShowEval Trace Mode 6 | ------------------- 7 | 8 | .. showeval:: showEval_0 9 | :trace_mode: true 10 | 11 | eggs = ['dogs', 'cats', 'moose'] 12 | ~~~~ 13 | 14 | ''.join({{eggs}}{{['dogs', 'cats', 'moose']}}).upper().join(eggs) 15 | {{''.join(['dogs', 'cats', 'moose'])}}{{'dogscatsmoose'}}.upper().join(eggs) ##This is a comment and can be, really any length. if it becomes unruly, then a vertical scrollbar will appear to help out! 16 | {{'dogscatsmoose'.upper()}}{{'DOGSCATSMOOSE'}}.join(eggs) 17 | 'DOGSCATSMOOSE'.join({{eggs}}{{['dogs', 'cats', 'moose']}}) 18 | {{'DOGSCATSMOOSE'.join(['dogs', 'cats', 'moose'])}}{{'dogsDOGSCATSMOOSEcatsDOGSCATSMOOSEmoose'}} 19 | 20 | 21 | ShowEval Replace Mode 22 | --------------------- 23 | 24 | .. showeval:: showEval_1 25 | :trace_mode: false 26 | 27 | eggs = ['dogs', 'cats', 'moose'] 28 | ~~~~ 29 | 30 | ''.join({{eggs}}{{['dogs', 'cats', 'moose']}}).upper().join(eggs) 31 | {{''.join(['dogs', 'cats', 'moose'])}}{{'dogscatsmoose'}}.upper().join(eggs) ##What is evaluated next? 32 | {{'dogscatsmoose'.upper()}}{{'DOGSCATSMOOSE'}}.join(eggs) 33 | 'DOGSCATSMOOSE'.join({{eggs}}{{['dogs', 'cats', 'moose']}}) 34 | {{'DOGSCATSMOOSE'.join(['dogs', 'cats', 'moose'])}}{{'dogsDOGSCATSMOOSEcatsDOGSCATSMOOSEmoose'}} 35 | -------------------------------------------------------------------------------- /runestone/showeval/test/_sources/replace.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | Testing: ShowEval Questions 3 | =========================== 4 | 5 | ShowEval Replace Mode 6 | --------------------- 7 | 8 | .. showeval:: showEval_2 9 | :trace_mode: false 10 | 11 | eggs = ['dogs', 'cats', 'moose'] 12 | ~~~~ 13 | 14 | ''.join({{eggs}}{{['dogs', 'cats', 'moose']}}).upper().join(eggs) 15 | {{''.join(['dogs', 'cats', 'moose'])}}{{'dogscatsmoose'}}.upper().join(eggs) 16 | {{'dogscatsmoose'.upper()}}{{'DOGSCATSMOOSE'}}.join(eggs) 17 | 'DOGSCATSMOOSE'.join({{eggs}}{{['dogs', 'cats', 'moose']}}) 18 | {{'DOGSCATSMOOSE'.join(['dogs', 'cats', 'moose'])}}{{'dogsDOGSCATSMOOSEcatsDOGSCATSMOOSEmoose'}} 19 | -------------------------------------------------------------------------------- /runestone/showeval/test/_sources/trace.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | Testing: ShowEval Questions 3 | =========================== 4 | 5 | ShowEval Trace Mode 6 | ------------------- 7 | 8 | .. showeval:: showEval_3 9 | :trace_mode: true 10 | 11 | eggs = ['dogs', 'cats', 'moose'] 12 | ~~~~ 13 | 14 | ''.join({{eggs}}{{['dogs', 'cats', 'moose']}}).upper().join(eggs) 15 | {{''.join(['dogs', 'cats', 'moose'])}}{{'dogscatsmoose'}}.upper().join(eggs) 16 | {{'dogscatsmoose'.upper()}}{{'DOGSCATSMOOSE'}}.join(eggs) 17 | 'DOGSCATSMOOSE'.join({{eggs}}{{['dogs', 'cats', 'moose']}}) 18 | {{'DOGSCATSMOOSE'.join(['dogs', 'cats', 'moose'])}}{{'dogsDOGSCATSMOOSEcatsDOGSCATSMOOSEmoose'}} 19 | -------------------------------------------------------------------------------- /runestone/showeval/test/pavement.py: -------------------------------------------------------------------------------- 1 | import paver 2 | from paver.easy import * 3 | import paver.setuputils 4 | 5 | paver.setuputils.install_distutils_tasks() 6 | import os, sys 7 | from runestone.server import get_dburl 8 | from sphinxcontrib import paverutils 9 | import pkg_resources 10 | 11 | sys.path.append(os.getcwd()) 12 | 13 | home_dir = os.getcwd() 14 | master_url = "http://127.0.0.1:8000" 15 | master_app = "runestone" 16 | serving_dir = "./build/showeval_test" 17 | dest = "./static" 18 | 19 | options( 20 | sphinx=Bunch(docroot="."), 21 | build=Bunch( 22 | builddir="./build/showeval_test", 23 | sourcedir="_sources", 24 | outdir="./build/showeval_test", 25 | confdir=".", 26 | project_name="showeval_test", 27 | template_args={ 28 | "course_id": "showeval_test", 29 | "login_required": "false", 30 | "appname": master_app, 31 | "loglevel": 0, 32 | "course_url": master_url, 33 | "use_services": "false", 34 | "python3": "false", 35 | "dburl": "", 36 | "downloads_enabled": "true", 37 | "enable_chatcodes": "false", 38 | "allow_pairs": "false", 39 | "basecourse": "showeval_test", 40 | "dynamic_pages": False, 41 | }, 42 | ), 43 | ) 44 | 45 | version = pkg_resources.require("runestone")[0].version 46 | options.build.template_args["runestone_version"] = version 47 | 48 | # If DBURL is in the environment override dburl 49 | options.build.template_args["dburl"] = get_dburl(outer=locals()) 50 | 51 | from runestone import build # build is called implicitly by the paver driver. 52 | -------------------------------------------------------------------------------- /runestone/showeval/toctree.rst: -------------------------------------------------------------------------------- 1 | ************************************************* 2 | Accessibility 3 | ************************************************* 4 | .. toctree:: 5 | :maxdepth: 1 6 | :glob: 7 | 8 | *.py 9 | js/*.js 10 | css/*.css 11 | test/test_*.py 12 | -------------------------------------------------------------------------------- /runestone/spreadsheet/__init__.py: -------------------------------------------------------------------------------- 1 | from .spreadsheet import * 2 | -------------------------------------------------------------------------------- /runestone/spreadsheet/css/spreadsheet.css: -------------------------------------------------------------------------------- 1 | div.runestone .jexcel_container { 2 | max-height: 550px; 3 | overflow: scroll; 4 | max-width: 800px; 5 | overflow: scroll; 6 | } -------------------------------------------------------------------------------- /runestone/spreadsheet/test/_sources/index.rst: -------------------------------------------------------------------------------- 1 | ====================== 2 | Spreadsheets Test Page 3 | ====================== 4 | 5 | .. Here is were you specify the content and order of your new book. 6 | 7 | .. Each section heading (e.g. "SECTION 1: A Random Section") will be 8 | a heading in the table of contents. Source files that should be 9 | generated and included in that section should be placed on individual 10 | lines, with one line separating the first source filename and the 11 | :maxdepth: line. 12 | 13 | .. Sources can also be included from subfolders of this directory. 14 | (e.g. "DataStructures/queues.rst"). 15 | 16 | Cells in the spreadsheet that are graded are initially colored light blue. When the grade button is pressed a test report is printed out, and the cells that were correct are colored with a light green background. The cells that were not correct have a light redish background 17 | 18 | .. spreadsheet:: test_spreadsheet_1 19 | :mindimensions: 6,5 20 | :colwidths: 200,100,100 21 | :coltitles: 'name','year','price','foo' 22 | 23 | Google, 1998, 807.80 24 | Apple, 1976, 116.52 25 | Yahoo, 1994, 38.66 26 | ,,=sum(c1:c3) 27 | 28 | ==== 29 | assert A3 == Yahoo 30 | assert B3 == 1994 31 | 32 | 33 | .. spreadsheet:: test_spreadsheet_2 34 | :fromcsv: Iris.csv 35 | :colwidths: 50,100,100,100,100 36 | 37 | ==== 38 | assert A151 == 150 39 | 40 | 41 | .. spreadsheet:: test_spreadsheet_3 42 | :colwidths: 200,200 43 | :coltitles: 'my formula', 'result' 44 | 45 | "=CONCATENATE(""abc"", ""xyz"")","=CONCATENATE(""abc"", ""xyz"")" 46 | '=1+1, 2 47 | -------------------------------------------------------------------------------- /runestone/spreadsheet/test/test_spreadsheet.py: -------------------------------------------------------------------------------- 1 | def test_ss_autograde(selenium_utils_get): 2 | selenium_utils_get.wait_until_ready("test_spreadsheet_1") 3 | 4 | t2 = selenium_utils_get.driver.find_element_by_id("test_spreadsheet_1") 5 | rb = t2.find_element_by_class_name("run-button") 6 | rb.click() 7 | out = selenium_utils_get.driver.find_element_by_id("test_spreadsheet_1_stdout") 8 | assert "You passed 2 out of 2 tests" in out.text 9 | 10 | selenium_utils_get.wait_until_ready("test_spreadsheet_2") 11 | t2 = selenium_utils_get.driver.find_element_by_id("test_spreadsheet_2") 12 | rb = t2.find_element_by_class_name("run-button") 13 | rb.click() 14 | out = selenium_utils_get.driver.find_element_by_id("test_spreadsheet_2_stdout") 15 | assert "You passed 1 out of 1 tests" in out.text 16 | -------------------------------------------------------------------------------- /runestone/spreadsheet/toctree.rst: -------------------------------------------------------------------------------- 1 | ************************************************* 2 | Accessibility 3 | ************************************************* 4 | .. toctree:: 5 | :maxdepth: 1 6 | :glob: 7 | 8 | *.py 9 | js/*.js 10 | css/*.css 11 | test/test_*.py 12 | -------------------------------------------------------------------------------- /runestone/tabbedStuff/README.md: -------------------------------------------------------------------------------- 1 |

Tabbed Stuff

2 | 3 | ```html 4 |
5 |
6 | Stuff in tab 1. 7 |
8 |
9 | Stuff in tab 2. 10 |
11 |
12 | Stuff in tab 3. 13 |
14 |
15 | ``` 16 | 17 | Here the div tag represents the entire Tabbed Stuff component to be rendered. 18 | Each Tabbed Stuff component contains a series of Tab components, which are also div elements. 19 | Each Tab component can contain anything from text to other working components such as Multiple Choice, Activecode, etc. 20 | By default, the first tab is opened on page load, but this can be changed by the presence of data-inactive in the Tabbed Stuff tag or data-active in one of the tabs. 21 | 22 | Option spec: 23 | 24 |
    25 |
  • data-component="tabbedStuff" Identifies this as a Tabbed Stuff component
  • 26 |
  • id Must be unique in the document
  • 27 |
  • data-inactive Ensures that no tabs are open by default on page load--this overrides any data-active attribute in a tab tag.
  • 28 |
29 | 30 | Option spec for each tab: 31 | 32 |
    33 |
  • data-component="tab" Identifies this as a Tab component
  • 34 |
  • data-tabname This is the text that appears on the top of the tab that users can click to open the tab.
  • 35 |
  • data-active Specifies this tab to be opened on page load--only one per Tabbed Stuff component, and is overridden by the presence of data-inactive in the Tabbed Stuff tag. The default tab to be opened on page load is the first tab.
  • 36 |
37 | -------------------------------------------------------------------------------- /runestone/tabbedStuff/__init__.py: -------------------------------------------------------------------------------- 1 | from .tabbedStuff import * 2 | -------------------------------------------------------------------------------- /runestone/tabbedStuff/css/tabbedstuff.css: -------------------------------------------------------------------------------- 1 | .tab-pane { 2 | padding: 20px 15px 10px 15px; 3 | } 4 | 5 | .question .nav.nav-tabs { 6 | padding-top: 6px; 7 | background-color: #CFCCB8; 8 | border-color: #fbeed5; 9 | border-radius: 4px; 10 | } 11 | 12 | .question .nav.nav-tabs li { 13 | margin-right: 1px; 14 | margin-left: 6px; 15 | } 16 | 17 | .question .nav.nav-tabs li > a { 18 | background-color: #E6E2CC; 19 | border-bottom-color: #fcf8e3; 20 | color: #555; 21 | } 22 | 23 | .question .nav-tabs > li > a:hover { 24 | border-color: #b4b19d; 25 | border-bottom-width:0; 26 | } 27 | 28 | .question .nav.nav-tabs li.active > a { 29 | background-color: #faf7df; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /runestone/tabbedStuff/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RunestoneInteractive/RunestoneComponents/d05ee263a5cc9d9c029cfc31f8f8ecdcf669fb11/runestone/tabbedStuff/test/__init__.py -------------------------------------------------------------------------------- /runestone/tabbedStuff/test/_sources/index.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | Testing: Tabbed Questions 3 | ========================= 4 | 5 | .. Here is were you specify the content and order of your new book. 6 | 7 | .. Each section heading (e.g. "SECTION 1: A Random Section") will be 8 | a heading in the table of contents. Source files that should be 9 | generated and included in that section should be placed on individual 10 | lines, with one line separating the first source filename and the 11 | :maxdepth: line. 12 | 13 | .. Sources can also be included from subfolders of this directory. 14 | (e.g. "DataStructures/queues.rst"). 15 | 16 | 17 | .. tabbed:: exercise1 18 | 19 | .. tab:: Tab 1 20 | 21 | Hello! 22 | 23 | .. fillintheblank: fitb-label 24 | 25 | |blank|, a needle pulling thread. 26 | 27 | - :so: Correct. 28 | :sew: Wrong word, correct sound. 29 | :x: Watch the Sound of Music again! 30 | 31 | .. tab:: Tab 2 32 | 33 | Goodbye! 34 | -------------------------------------------------------------------------------- /runestone/tabbedStuff/test/pavement.py: -------------------------------------------------------------------------------- 1 | import paver 2 | from paver.easy import * 3 | import paver.setuputils 4 | 5 | paver.setuputils.install_distutils_tasks() 6 | import os, sys 7 | from runestone.server import get_dburl 8 | from sphinxcontrib import paverutils 9 | import pkg_resources 10 | 11 | sys.path.append(os.getcwd()) 12 | 13 | home_dir = os.getcwd() 14 | master_url = "http://127.0.0.1:8000" 15 | master_app = "runestone" 16 | serving_dir = "./build/tabbed-test" 17 | dest = "../../static" 18 | 19 | options( 20 | sphinx=Bunch(docroot="."), 21 | build=Bunch( 22 | builddir="./build/tabbed-test", 23 | sourcedir="_sources", 24 | outdir="./build/tabbed-test", 25 | confdir=".", 26 | project_name="tabbed-test", 27 | template_args={ 28 | "course_id": "tabbed-test", 29 | "login_required": "false", 30 | "appname": master_app, 31 | "loglevel": 0, 32 | "course_url": master_url, 33 | "use_services": "false", 34 | "python3": "false", 35 | "dburl": "", 36 | "downloads_enabled": "true", 37 | "enable_chatcodes": "false", 38 | "allow_pairs": "false", 39 | "basecourse": "tabbed-test", 40 | }, 41 | ), 42 | ) 43 | 44 | version = pkg_resources.require("runestone")[0].version 45 | options.build.template_args["runestone_version"] = version 46 | 47 | # If DBURL is in the environment override dburl 48 | options.build.template_args["dburl"] = get_dburl(outer=locals()) 49 | 50 | from runestone import build # build is called implicitly by the paver driver. 51 | -------------------------------------------------------------------------------- /runestone/tabbedStuff/test/test_tabbedStuff.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test Tabbed stuff directive 3 | """ 4 | 5 | __author__ = "yasinovskyy" 6 | 7 | 8 | def test_t1(selenium_utils): 9 | """Initial view. Tab 1 is visible, tab 2 is hidden""" 10 | su = selenium_utils 11 | su.get("index.html") 12 | su.wait_until_ready("exercise1") 13 | 14 | e1 = su.driver.find_element_by_id("exercise1") 15 | 16 | t1 = e1.find_element_by_class_name("active") 17 | tp1 = e1.find_element_by_id("exercise1-0") 18 | 19 | assert "Tab 1" == t1.text 20 | assert "Hello!" == tp1.text 21 | 22 | 23 | def test_t2(selenium_utils): 24 | """Tab 2 is visible, tab 1 is hidden""" 25 | su = selenium_utils 26 | su.get("index.html") 27 | su.wait_until_ready("exercise1") 28 | e1 = su.driver.find_element_by_id("exercise1") 29 | 30 | btn_tab2 = e1.find_element_by_link_text("Tab 2") 31 | btn_tab2.click() 32 | 33 | t1 = e1.find_element_by_class_name("active") 34 | tp1 = e1.find_element_by_id("exercise1-1") 35 | 36 | assert "Tab 2" == t1.text 37 | assert "Goodbye!" == tp1.text 38 | 39 | 40 | def test_t3(selenium_utils): 41 | """Tab 2 is selected, then tab 1""" 42 | su = selenium_utils 43 | su.get("index.html") 44 | su.wait_until_ready("exercise1") 45 | e1 = su.driver.find_element_by_id("exercise1") 46 | 47 | btn_tab2 = e1.find_element_by_link_text("Tab 2") 48 | btn_tab2.click() 49 | 50 | btn_tab1 = e1.find_element_by_link_text("Tab 1") 51 | btn_tab1.click() 52 | 53 | t1 = e1.find_element_by_class_name("active") 54 | tp1 = e1.find_element_by_id("exercise1-0") 55 | 56 | assert "Tab 1" == t1.text 57 | assert "Hello!" == tp1.text 58 | -------------------------------------------------------------------------------- /runestone/tabbedStuff/toctree.rst: -------------------------------------------------------------------------------- 1 | ************************************************* 2 | Accessibility 3 | ************************************************* 4 | .. toctree:: 5 | :maxdepth: 1 6 | :glob: 7 | 8 | *.py 9 | js/*.js 10 | css/*.css 11 | test/test_*.py 12 | -------------------------------------------------------------------------------- /runestone/timed/__init__.py: -------------------------------------------------------------------------------- 1 | from .timedassessment import * 2 | -------------------------------------------------------------------------------- /runestone/timed/test/_sources/index.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | Practice Timed Exam 3 | ===================== 4 | 5 | The following questions make up Set #1 of the Practice Exam Questions. These questions are designed to be similar to those on the AP CSP exam. You should finish these questions within 17 minutes to stay on track with the timing of the actual exam. During the actual exam, you will be provided with the AP CS Reference Sheet, which can be found here. 6 | 7 | Click the “Start” button when you are ready to begin the exam. Click the “Pause” button to pause the exam (you will not be able to see the questions when the exam is paused). It will show how much time you have used, but you have unlimited time. Click on the “Finish Exam” button at the end when you are done. The number correct, number wrong, and number skipped will be displayed at the bottom of the page. Feedback for each answer will also be shown as well as your answer. 8 | 9 | You will not be able to change your answers after you hit the “Finish Exam” button. 10 | 11 | * `restructuredText Docs `_ 12 | * `Runestone Docs `_ 13 | * Join the discussion on our `Google Group `_ 14 | * Tell us about problems on `Github `_ 15 | 16 | .. timed:: time_q1 17 | 18 | 19 | .. mchoice:: time_test_1_q1 20 | 21 | What color is a stop sign? 22 | 23 | - red 24 | 25 | + Red it is. 26 | 27 | - brown 28 | 29 | - Not brown. 30 | 31 | - blue 32 | 33 | - Not blue. 34 | 35 | - gray 36 | 37 | - Not gray. 38 | 39 | 40 | .. toctree:: 41 | 42 | multiquestion.rst 43 | -------------------------------------------------------------------------------- /runestone/timed/test/pavement.py: -------------------------------------------------------------------------------- 1 | import paver 2 | from paver.easy import * 3 | import paver.setuputils 4 | 5 | paver.setuputils.install_distutils_tasks() 6 | import os, sys 7 | from runestone.server import get_dburl 8 | from sphinxcontrib import paverutils 9 | import pkg_resources 10 | 11 | sys.path.append(os.getcwd()) 12 | 13 | home_dir = os.getcwd() 14 | master_url = "http://127.0.0.1:8000" 15 | master_app = "runestone" 16 | serving_dir = "./build/ParsonsTests" 17 | dest = "../../static" 18 | 19 | options( 20 | sphinx=Bunch(docroot="."), 21 | build=Bunch( 22 | builddir="./build/ParsonsTests", 23 | sourcedir="_sources", 24 | outdir="./build/ParsonsTests", 25 | confdir=".", 26 | project_name="ParsonsTests", 27 | template_args={ 28 | "course_id": "ParsonsTests", 29 | "login_required": "false", 30 | "appname": master_app, 31 | "loglevel": 0, 32 | "course_url": master_url, 33 | "use_services": "false", 34 | "python3": "false", 35 | "dburl": "", 36 | "downloads_enabled": "true", 37 | "enable_chatcodes": "false", 38 | "allow_pairs": "false", 39 | "basecourse": "ParsonsTests", 40 | }, 41 | ), 42 | ) 43 | 44 | version = pkg_resources.require("runestone")[0].version 45 | options.build.template_args["runestone_version"] = version 46 | 47 | # If DBURL is in the environment override dburl 48 | options.build.template_args["dburl"] = get_dburl(outer=locals()) 49 | 50 | from runestone import build # build is called implicitly by the paver driver. 51 | -------------------------------------------------------------------------------- /runestone/timed/toctree.rst: -------------------------------------------------------------------------------- 1 | ************************************************* 2 | Timed Exam Questions 3 | ************************************************* 4 | See the `overview book `_ for user documentation. 5 | 6 | .. toctree:: 7 | :maxdepth: 1 8 | :glob: 9 | 10 | *.py 11 | js/*.js 12 | css/*.css 13 | test/test_*.py 14 | -------------------------------------------------------------------------------- /runestone/utility/__init__.py: -------------------------------------------------------------------------------- 1 | from .utility import * 2 | -------------------------------------------------------------------------------- /runestone/utility/toctree.rst: -------------------------------------------------------------------------------- 1 | ************************************************* 2 | Accessibility 3 | ************************************************* 4 | .. toctree:: 5 | :maxdepth: 1 6 | :glob: 7 | 8 | *.py 9 | js/*.js 10 | css/*.css 11 | test/test_*.py 12 | -------------------------------------------------------------------------------- /runestone/video/__init__.py: -------------------------------------------------------------------------------- 1 | from .video import * 2 | -------------------------------------------------------------------------------- /runestone/video/css/video.css: -------------------------------------------------------------------------------- 1 | .exercises { 2 | background-color: #f0ffff; 3 | } 4 | 5 | figcaption { 6 | margin: 0.75em 0; 7 | text-align: center; 8 | font: italic 13px/18px Cambria, Georgia, "Times New Roman", Times, serif; 9 | } 10 | 11 | img.bookfig { 12 | margin-left: auto; 13 | margin-right: auto; 14 | } 15 | 16 | .cl_caption { 17 | text-align: center; 18 | font-weight: bold; 19 | } 20 | 21 | .runestone iframe { 22 | display: block; 23 | margin-left: auto; 24 | margin-right: auto; 25 | } 26 | -------------------------------------------------------------------------------- /runestone/video/test/_sources/index.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | Video Test Project 3 | ================== 4 | 5 | 6 | Lets try a YouTube Video 7 | 8 | 9 | .. youtube:: eDQ19ahXsSk 10 | :divid: goog_keyvalpairs 11 | :height: 315 12 | :width: 560 13 | :align: left 14 | 15 | -------------------------------------------------------------------------------- /runestone/video/toctree.rst: -------------------------------------------------------------------------------- 1 | ************************************************* 2 | Accessibility 3 | ************************************************* 4 | .. toctree:: 5 | :maxdepth: 1 6 | :glob: 7 | 8 | *.py 9 | js/*.js 10 | css/*.css 11 | test/test_*.py 12 | -------------------------------------------------------------------------------- /runestone/wavedrom/__init__.py: -------------------------------------------------------------------------------- 1 | from .wavedrom import setup 2 | -------------------------------------------------------------------------------- /runestone/wavedrom/js/wavedrom.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // |docname| - JavaScript for the WaveDrom library 3 | // *********************************************** 4 | "use strict"; 5 | 6 | // This took a fair amount of experimenting to figure out how to make this work with NPM and Webpack. Sigh. Here's the working result. 7 | // 8 | // This has already been packaged for the web with browserify, so we can just import it for the side effects (it defines ``window.WaveDrom``. Importing from the ``lib/`` folder produces a lot of unsatisfied imports when using webpack. 9 | import "wavedrom/wavedrom.min.js"; 10 | 11 | // WaveSkin isn't defined globally, so import the default export to get access to it. It defines a single variable, assuming that the variable will be assigned to the ``window``. Here, it's not. So... 12 | import WaveSkin from "wavedrom/skins/default.js"; 13 | // ...make the required WaveSkin (needed by WaveDrom) available globally. 14 | window.WaveSkin = WaveSkin; 15 | 16 | // Run the render after the dynamic load is done. 17 | $(document).on("runestone:login-complete", window.WaveDrom.ProcessAll); 18 | -------------------------------------------------------------------------------- /runestone/wavedrom/test/_sources/index.rst: -------------------------------------------------------------------------------- 1 | ************* 2 | Wavedrom test 3 | ************* 4 | 5 | .. wavedrom:: 6 | :caption: Two signals, PB1 (the pushbutton) and LED1, change over time. 7 | 8 | signal: [ 9 | {name: 'PB1', wave: '1.0..1..0..1..', node: '..a..b..c..d', phase: 0.5}, 10 | {name: 'LED1', wave: '0.1.....0....', node: '..f.....g'}, 11 | ], edge: [ 12 | 'a~f', 'c~g', 13 | ], 14 | -------------------------------------------------------------------------------- /runestone/wavedrom/test/test_wavedrom.py: -------------------------------------------------------------------------------- 1 | def test_1(selenium_utils_get): 2 | # After the render, Wavedrom will create a div with a numbered ID. If this exists, then the render worked. Don't try waiting for the component to be ready, since this isn't a Runestone Component -- it doesn't emit a ready signal. 3 | selenium_utils_get.driver.find_element_by_id("WaveDrom_Display_0") 4 | -------------------------------------------------------------------------------- /runestone/webgldemo/__init__.py: -------------------------------------------------------------------------------- 1 | from .webgldemo import * 2 | -------------------------------------------------------------------------------- /runestone/webgldemo/css/webgldemo.css: -------------------------------------------------------------------------------- 1 | /* Major sections */ 2 | .webgldemo_container { 3 | background-color: #fcf8e3; 4 | box-sizing: border-box; 5 | width: 100%; 6 | display: block; 7 | margin-bottom: 1em; 8 | } 9 | 10 | .webgldemo_canvas { 11 | background-color: #fcf8e3; 12 | box-sizing: border-box; 13 | } 14 | 15 | .webgldemo_controls { 16 | } 17 | -------------------------------------------------------------------------------- /runestone/webgldemo/test/_sources/01_introduction/nested/test02.rst: -------------------------------------------------------------------------------- 1 | .. Copyright (C) Wayne Brown 2 | Permission is granted to copy, distribute 3 | and/or modify this document under the terms of the GNU Free Documentation 4 | License, Version 1.3 or any later version published by the Free Software 5 | Foundation; with Invariant Sections being Forward, Prefaces, and 6 | Contributor List, no Front-Cover Texts, and no Back-Cover Texts. A copy of 7 | the license is included in the section entitled "GNU Free Documentation 8 | License". 9 | 10 | Testing Nested Folders 11 | ---------------------- 12 | 13 | A simple webgl demo directive: 14 | 15 | .. Code-Block:: text 16 | 17 | .. webgldemo:: W1 18 | :htmlprogram: _static/01_example01/scale_about_origin.html 19 | 20 | .. webgldemo:: W1 21 | :htmlprogram: _static/01_example01/scale_about_origin.html 22 | 23 | --------------------------------------------------------------------- 24 | 25 | A simple webglinteractive directive that edits the shaders of a webgl program: 26 | 27 | .. Code-Block:: text 28 | 29 | .. webglinteractive:: W5 30 | :htmlprogram: _static/01_example02/simple_pyramid.html 31 | :editlist: _static/shaders/uniform_color.vert, _static/shaders/uniform_color.frag 32 | 33 | .. webglinteractive:: W5 34 | :htmlprogram: _static/01_example02/simple_pyramid.html 35 | :editlist: _static/shaders/uniform_color.vert, _static/shaders/uniform_color.frag 36 | 37 | -------------------------------------------------------------------------------- /runestone/webgldemo/test/_sources/index.rst: -------------------------------------------------------------------------------- 1 | .. Copyright (C) Wayne Brown 2 | Permission is granted to copy, distribute 3 | and/or modify this document under the terms of the GNU Free Documentation 4 | License, Version 1.3 or any later version published by the Free Software 5 | Foundation; with Invariant Sections being Forward, Prefaces, and 6 | Contributor List, no Front-Cover Texts, and no Back-Cover Texts. A copy of 7 | the license is included in the section entitled "GNU Free Documentation 8 | License". 9 | 10 | Table of Contents 11 | ::::::::::::::::: 12 | 13 | SECTION 1: Introduction 14 | ----------------------- 15 | 16 | .. toctree:: 17 | :titlesonly: 18 | :maxdepth: 1 19 | 20 | 01_introduction/test01 21 | 01_introduction/nested/test02 22 | 23 | A simple webgl demo directive: 24 | 25 | .. Code-Block:: text 26 | 27 | .. webgldemo:: W1 28 | :htmlprogram: _static/01_example01/scale_about_origin.html 29 | 30 | .. webgldemo:: W1 31 | :htmlprogram: _static/01_example01/scale_about_origin.html 32 | 33 | A simple webglinteractive directive that edits the shaders of a webgl program: 34 | 35 | .. Code-Block:: text 36 | 37 | .. webglinteractive:: W5 38 | :htmlprogram: _static/01_example02/simple_pyramid.html 39 | :editlist: _static/shaders/uniform_color.vert, _static/shaders/uniform_color.frag 40 | 41 | .. webglinteractive:: W5 42 | :htmlprogram: _static/01_example02/simple_pyramid.html 43 | :editlist: _static/shaders/uniform_color.vert, _static/shaders/uniform_color.frag 44 | 45 | 46 | -------------------------------------------------------------------------------- /runestone/webgldemo/test/pavement.py: -------------------------------------------------------------------------------- 1 | import paver 2 | from paver.easy import * 3 | import paver.setuputils 4 | 5 | paver.setuputils.install_distutils_tasks() 6 | import os, sys 7 | from runestone.server import get_dburl 8 | from sphinxcontrib import paverutils 9 | import pkg_resources 10 | 11 | sys.path.append(os.getcwd()) 12 | 13 | home_dir = os.getcwd() 14 | master_url = "http://127.0.0.1:8000" 15 | master_app = "runestone" 16 | serving_dir = "./build/testwebgldemo" 17 | dest = "../../static" 18 | 19 | options( 20 | sphinx=Bunch(docroot="."), 21 | build=Bunch( 22 | builddir="./build/testwebgldemo", 23 | sourcedir="_sources", 24 | outdir="./build/testwebgldemo", 25 | confdir=".", 26 | project_name="testwebgldemo", 27 | template_args={ 28 | "course_id": "testwebgldemo", 29 | "login_required": "false", 30 | "appname": master_app, 31 | "loglevel": 0, 32 | "course_url": master_url, 33 | "use_services": "false", 34 | "python3": "false", 35 | "dburl": "", 36 | "downloads_enabled": "true", 37 | "enable_chatcodes": "false", 38 | "allow_pairs": "false", 39 | "basecourse": "testwebgldemo", 40 | }, 41 | ), 42 | ) 43 | 44 | version = pkg_resources.require("runestone")[0].version 45 | options.build.template_args["runestone_version"] = version 46 | 47 | # If DBURL is in the environment override dburl 48 | options.build.template_args["dburl"] = get_dburl(outer=locals()) 49 | 50 | from runestone import build # build is called implicitly by the paver driver. 51 | -------------------------------------------------------------------------------- /runestone/webgldemo/toctree.rst: -------------------------------------------------------------------------------- 1 | ************************************************* 2 | Accessibility 3 | ************************************************* 4 | .. toctree:: 5 | :maxdepth: 1 6 | :glob: 7 | 8 | *.py 9 | js/*.js 10 | css/*.css 11 | test/test_*.py 12 | -------------------------------------------------------------------------------- /runestone/webwork/toctree.rst: -------------------------------------------------------------------------------- 1 | ************************************************* 2 | Accessibility 3 | ************************************************* 4 | .. toctree:: 5 | :maxdepth: 1 6 | :glob: 7 | 8 | *.py 9 | js/*.js 10 | css/*.css 11 | test/test_*.py 12 | -------------------------------------------------------------------------------- /scripts/buildptx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Just a quick wrapper around what is in runestone utils to make it easier to do a full build 4 | # of a PreTeXt book outside of docker 5 | import os 6 | import subprocess 7 | import sys 8 | import pathlib 9 | from runestone.server.utils import _build_ptx_book 10 | 11 | p = pathlib.Path.cwd() 12 | 13 | if len(sys.argv) < 2: 14 | if (p / "project.ptx").exists(): # we are in a pretext project 15 | print(f"Building {p.name} in place") 16 | bookname = p.name 17 | else: 18 | print("You must either name a book or be in the book's folder") 19 | exit(-1) 20 | 21 | elif p.name != sys.argv[1] and "BOOK_PATH" in os.environ: 22 | print("Not in the right folder") 23 | newp = pathlib.Path(os.environ["BOOK_PATH"], sys.argv[1]) 24 | print(f"Trying to change to {newp}") 25 | if newp.exists(): 26 | os.chdir(newp) 27 | bookname = sys.argv[1] 28 | else: 29 | print(f"{newp} does not exist") 30 | print("Build Failed") 31 | exit(-1) 32 | 33 | 34 | class Config: 35 | def __init__(self): 36 | conf = os.environ.get("WEB2PY_CONFIG", "production") 37 | if conf == "production": 38 | self.dburl = os.environ.get("DBURL") 39 | elif conf == "development": 40 | self.dburl = os.environ.get("DEV_DBURL") 41 | elif conf == "test": 42 | self.dburl = os.environ.get("TEST_DBURL") 43 | else: 44 | print("Incorrect WEB2PY_CONFIG") 45 | 46 | 47 | config = Config() 48 | assert bookname 49 | 50 | res = _build_ptx_book(config, False, "runestone-manifest.xml", bookname) 51 | if not res: 52 | print("build failed") 53 | exit(-1) 54 | 55 | res = subprocess.run(f"chgrp -R www-data .", shell=True, capture_output=True) 56 | if res.returncode != 0: 57 | print("failed to change group") 58 | exit(-1) 59 | res = subprocess.run(f"chmod -R go+rw .", shell=True, capture_output=True) 60 | if res.returncode != 0: 61 | print("failed to change permissions") 62 | exit(-1) 63 | -------------------------------------------------------------------------------- /scripts/dist2xml.py: -------------------------------------------------------------------------------- 1 | import json 2 | from json2xml import json2xml 3 | from json2xml.utils import readfromstring 4 | import sys 5 | import os 6 | 7 | if len(sys.argv) < 2: 8 | print("Usage: dist2xml.py version_number [cdn_url]") 9 | print(" version_number should match the latest version of runestone components") 10 | print(" cdn_url defaults to https://runestone.academy/cdn/runestone") 11 | print(" The environment variable COMPDIR can be set to the root of RunestoneComponents") 12 | print(" Otherwise you are best off running this command from the root of the RunestoneComponents distribution") 13 | sys.exit(1) 14 | 15 | if "COMPDIR" in os.environ: 16 | data_path = os.path.join(os.environ["COMPDIR"], "runestone/dist") 17 | else: 18 | print("Warning: No COMPDIR environment variable set using . for RunestoneComponents") 19 | data_path = "runestone/dist" 20 | 21 | data = json.loads( 22 | open(os.path.join(data_path, "webpack_static_imports.json")).read()) 23 | 24 | if sys.argv[1] == "test": 25 | data['cdn-url'] = sys.argv[2] if len(sys.argv) > 2 else "_static/" 26 | else: 27 | print("Warning: You did not specify a URL for the cdn will use Runestone.Academy") 28 | data['cdn-url'] = "https://runestone.academy/cdn/runestone/" 29 | 30 | data['version'] = sys.argv[1] 31 | 32 | with open(os.path.join(data_path, "webpack_static_imports.xml"), "w") as f: 33 | f.write(json2xml.Json2xml(data).to_xml()) 34 | -------------------------------------------------------------------------------- /scripts/makePtx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if (( $# < 1 )); then 4 | echo "please provide version number" 5 | exit 6 | fi 7 | 8 | source ~/.virtualenvs/compdev39/bin/activate 9 | npm run dist 10 | source ~/.virtualenvs/json2xml/bin/activate 11 | python scripts/dist2xml.py $1 > runestone/dist/webpack_static_imports.xml 12 | cd runestone 13 | tar zcf dist-$1.tgz dist 14 | deactivate 15 | -------------------------------------------------------------------------------- /toxml.py: -------------------------------------------------------------------------------- 1 | from json2xml import json2xml 2 | from json2xml.utils import readfromurl, readfromstring, readfromjson 3 | 4 | # get the xml from an URL that return json 5 | # data = readfromurl("https://coderwall.com/vinitcool76.json") 6 | # print(json2xml.Json2xml(data).to_xml()) 7 | 8 | # # get the xml from a json string 9 | # data = readfromstring( 10 | # '{"login":"mojombo","id":1,"avatar_url":"https://avatars0.githubusercontent.com/u/1?v=4"}' 11 | # ) 12 | # print(json2xml.Json2xml(data).to_xml()) 13 | 14 | # get the data from an URL 15 | data = readfromjson("runestone/dist/webpack_static_imports.json") 16 | print(json2xml.Json2xml(data).to_xml()) --------------------------------------------------------------------------------