From acc45dbea61cd2587c5fd4c9db77d036edc61672 Mon Sep 17 00:00:00 2001 From: Tue Herlau <tuhe@dtu.dk> Date: Thu, 2 Sep 2021 14:40:53 +0200 Subject: [PATCH] Updates --- LICENSE | 19 + README.md | 40 +- autolab/__pycache__/autolab.cpython-38.pyc | Bin 5899 -> 0 bytes autolab/report_autolab.py | 0 build.md | 18 + cs202courseware/ascimenu.py | 43 ++ cs202courseware/ug2report1.py | 14 +- cs202courseware/ug2report1_nohidden.py | 13 +- dist/unitgrade-devel-0.0.1.tar.gz | Bin 0 -> 17133 bytes dist/unitgrade_devel-0.0.1-py3-none-any.whl | Bin 0 -> 18848 bytes .../docker_tango_python/Dockerfile | 0 .../docker_tango_python/requirements.txt | 1 + .../unitgrade-docker/Dockerfile | 4 +- .../home/cs103/Report3_handin_5_of_30.token | Bin 0 -> 113733 bytes .../__pycache__/homework1.cpython-38.pyc | Bin 922 -> 923 bytes .../report3_complete_grade.cpython-38.pyc | Bin 0 -> 50864 bytes .../unitgrade-docker/home}/cs103/homework1.py | 0 .../unitgrade-docker/home}/cs103/report3.py | 6 +- .../home/cs103/report3_complete_grade.py | 338 ++++++++++++ .../home/cs103/report3_grade.py | 340 ++++++++++++ .../unitgrade-docker/requirements.txt | 1 + .../unitgrade-docker/tmp/cs103/homework1.py | 21 + .../unitgrade-docker/tmp/cs103/report3.py | 29 + .../tmp/cs103/report3_complete_grade.py | 338 ++++++++++++ .../tmp/cs103/report3_grade.py | 340 ++++++++++++ examples/02471/instructor/02471/report1.py | 2 +- examples/02631/instructor/programs/.coverage | Bin 0 -> 53248 bytes .../__pycache__/looping.cpython-38.pyc | Bin 0 -> 2019 bytes .../__pycache__/report1intro.cpython-38.pyc | Bin 0 -> 7198 bytes examples/02631/instructor/programs/deploy.py | 62 +++ examples/02631/instructor/programs/looping.py | 64 +++ .../02631/instructor/programs/report1intro.py | 139 +++++ .../instructor/programs/report1intro_grade.py | 337 ++++++++++++ .../programs/unitgrade/Bacteria.pkl | Bin 0 -> 2050 bytes .../programs/unitgrade/ClusterAnalysis.pkl | Bin 0 -> 763 bytes .../programs/unitgrade/FermentationRate.pkl | Bin 0 -> 618 bytes .../programs/unitgrade/RemoveIncomplete.pkl | Bin 0 -> 1444 bytes examples/02631/students/programs/.coverage | Bin 0 -> 53248 bytes .../__pycache__/looping.cpython-38.pyc | Bin 0 -> 2019 bytes .../__pycache__/report1intro.cpython-38.pyc | Bin 0 -> 7198 bytes examples/02631/students/programs/looping.py | 62 +++ .../02631/students/programs/report1intro.py | 142 +++++ .../students/programs/report1intro_grade.py | 339 ++++++++++++ .../students/programs/unitgrade/Bacteria.pkl | Bin 0 -> 2050 bytes .../programs/unitgrade/ClusterAnalysis.pkl | Bin 0 -> 763 bytes .../programs/unitgrade/FermentationRate.pkl | Bin 0 -> 618 bytes .../programs/unitgrade/RemoveIncomplete.pkl | Bin 0 -> 1444 bytes examples/autolab_example/autolab_example.py | 4 +- examples/autolab_example/tmp/cs101/cs101.yml | 6 +- .../tmp/cs101/src/driver_python.py | 6 +- .../tmp/cs101/src/report1_grade.py | 2 +- .../tmp/cs102/src/driver_python.py | 4 +- .../tmp/cs103/src/driver_python.py | 4 +- .../example_docker/instructor/cs103/.coverage | Bin 0 -> 53248 bytes .../cs103/Report3_handin_25_of_30.token | Bin 0 -> 117485 bytes .../cs103/Report3_handin_30_of_30.token | Bin 0 -> 117467 bytes .../__pycache__/homework1.cpython-38.pyc | Bin 833 -> 833 bytes .../cs103/__pycache__/report3.cpython-38.pyc | Bin 1384 -> 1384 bytes .../report3_complete.cpython-38.pyc | Bin 1748 -> 1737 bytes .../example_docker/instructor/cs103/deploy.py | 18 +- .../instructor/cs103/report3.py | 6 +- .../instructor/cs103/report3_complete.py | 4 +- .../cs103/report3_complete_grade.py | 69 ++- .../instructor/cs103/report3_grade.py | 69 ++- .../cs103/unitgrade/AutomaticPass.pkl | Bin 93 -> 4 bytes .../instructor/cs103/unitgrade/Week1.pkl | Bin 87 -> 62 bytes .../tmp/cs103/Report3_handin_5_of_30.token | Bin 128198 -> 0 bytes .../tmp/cs103/report3_complete_grade.py | 345 ------------ .../tmp/cs103/report3_grade.py | 347 ------------ examples/example_docker/run_all_docker.py | 57 ++ .../example_docker/students/cs103/.coverage | Bin 0 -> 53248 bytes .../cs103/Report3_handin_10_of_30.token | Bin 65286 -> 57954 bytes .../__pycache__/homework1.cpython-38.pyc | Bin 992 -> 992 bytes .../__pycache__/homework1.cpython-39.pyc | Bin 833 -> 833 bytes .../cs103/__pycache__/report3.cpython-38.pyc | Bin 1384 -> 1384 bytes .../report3_complete.cpython-38.pyc | Bin 1748 -> 1748 bytes .../report3_complete.cpython-39.pyc | Bin 1246 -> 1764 bytes .../report3_complete_grade.cpython-39.pyc} | Bin 57824 -> 57973 bytes .../__pycache__/report3_grade.cpython-38.pyc | Bin 57968 -> 50620 bytes .../example_docker/students/cs103/report3.py | 6 +- .../students/cs103/report3_grade.py | 69 ++- .../cs103/unitgrade/AutomaticPass.pkl | Bin 93 -> 4 bytes .../students/cs103/unitgrade/Week1.pkl | Bin 87 -> 62 bytes .../Report1Flat_handin_10_of_10.token | Bin 0 -> 65761 bytes .../__pycache__/deploy.cpython-38.pyc | Bin 0 -> 581 bytes .../__pycache__/homework1.cpython-38.pyc | Bin 0 -> 835 bytes .../__pycache__/report1flat.cpython-38.pyc | Bin 0 -> 1204 bytes .../instructor/cs101flat/deploy.py | 15 + .../instructor/cs101flat/homework1.py | 16 + .../instructor/cs101flat/report1flat.py | 24 + .../instructor/cs101flat/report1flat_grade.py | 349 ++++++++++++ .../Report1Flat_handin_0_of_10.token | Bin 0 -> 65468 bytes .../__pycache__/deploy.cpython-38.pyc | Bin 0 -> 581 bytes .../__pycache__/homework1.cpython-38.pyc | Bin 0 -> 994 bytes .../__pycache__/report1flat.cpython-38.pyc | Bin 0 -> 1204 bytes .../report1flat_grade.cpython-38.pyc | Bin 0 -> 57893 bytes .../students/cs101flat/homework1.py | 21 + .../students/cs101flat/report1flat.py | 27 + .../students/cs101flat/report1flat_grade.py | 351 ++++++++++++ .../instructor/cs102/.coverage | Bin 0 -> 53248 bytes .../cs102/Report2_handin_10_of_18.token | Bin 80985 -> 0 bytes .../cs102/Report2_handin_13_of_18.token | Bin 0 -> 60507 bytes .../cs102/Report2_handin_18_of_18.token | Bin 80175 -> 61089 bytes .../cs102/Report2_handin_28_of_28.token | Bin 81409 -> 0 bytes .../cs102/Report2_handin_5_of_18.token | Bin 78385 -> 0 bytes .../cs102/Report2_handin_5_of_28.token | Bin 80005 -> 0 bytes .../cs102/__pycache__/deploy.cpython-38.pyc | Bin 0 -> 760 bytes .../__pycache__/homework1.cpython-38.pyc | Bin 836 -> 836 bytes .../__pycache__/report2_grade.cpython-38.pyc | Bin 65513 -> 59749 bytes .../instructor/cs102/deploy.py | 6 +- .../instructor/cs102/report2.py | 16 +- .../instructor/cs102/report2_grade.py | 182 +------ .../instructor/cs102/unitgrade/Question2.pkl | Bin 384 -> 360 bytes .../instructor/cs102/unitgrade/Week1.pkl | Bin 215 -> 101 bytes .../students/cs102/.coverage | Bin 0 -> 53248 bytes .../cs102/Report2_handin_0_of_18.token | Bin 79868 -> 0 bytes .../cs102/__pycache__/deploy.cpython-38.pyc | Bin 0 -> 760 bytes .../__pycache__/homework1.cpython-38.pyc | Bin 995 -> 836 bytes .../cs102/__pycache__/report2.cpython-38.pyc | Bin 2048 -> 2165 bytes .../__pycache__/report2_grade.cpython-38.pyc | Bin 65631 -> 59749 bytes .../students/cs102/report2.py | 16 +- .../students/cs102/report2_grade.py | 182 +------ .../students/cs102/unitgrade/Question2.pkl | Bin 384 -> 360 bytes .../students/cs102/unitgrade/Week1.pkl | Bin 215 -> 101 bytes .../instructor/cs105/.coverage | Bin 0 -> 53248 bytes .../Report1Jupyter_handin_18_of_18.token | Bin 0 -> 58736 bytes .../__pycache__/homework1.cpython-38.pyc | Bin 0 -> 187 bytes .../cs105/__pycache__/report5.cpython-38.pyc | Bin 0 -> 2312 bytes .../cs105/__pycache__/week2.cpython-38.pyc | Bin 0 -> 466 bytes .../instructor/cs105/deploy.py | 15 + .../instructor/cs105/homework1.py | 1 + .../instructor/cs105/report5.py | 49 ++ .../instructor/cs105/report5_grade.py | 336 ++++++++++++ .../instructor/cs105/unitgrade/Question2.pkl | Bin 0 -> 58 bytes .../instructor/cs105/unitgrade/Week1.pkl | 1 + .../instructor/cs105}/week2.ipynb | 0 .../example_jupyter/students/cs105/.coverage | Bin 0 -> 53248 bytes .../__pycache__/homework1.cpython-38.pyc | Bin 0 -> 187 bytes .../cs105/__pycache__/report5.cpython-38.pyc | Bin 0 -> 2312 bytes .../cs105/__pycache__/week2.cpython-38.pyc | Bin 0 -> 466 bytes .../students/cs105/homework1.py | 4 + .../example_jupyter/students/cs105/report5.py | 52 ++ .../students/cs105/report5_grade.py | 338 ++++++++++++ .../students/cs105/unitgrade/Question2.pkl | Bin 0 -> 58 bytes .../students/cs105/unitgrade/Week1.pkl | 1 + .../students/cs105/week2.ipynb | 69 +++ .../cs101/Report1_handin_10_of_10.token | Bin 77365 -> 58016 bytes .../cs101/__pycache__/deploy.cpython-38.pyc | Bin 0 -> 911 bytes .../__pycache__/report1_grade.cpython-38.pyc | Bin 63801 -> 57383 bytes .../instructor/cs101/deploy.py | 17 +- .../instructor/cs101/report1.py | 4 +- .../instructor/cs101/report1_grade.py | 180 +------ .../cs101/Report1_handin_0_of_10.token | Bin 77002 -> 0 bytes .../cs101/__pycache__/deploy.cpython-38.pyc | Bin 0 -> 911 bytes .../__pycache__/homework1.cpython-38.pyc | Bin 994 -> 835 bytes .../cs101/__pycache__/report1.cpython-38.pyc | Bin 1209 -> 1228 bytes .../__pycache__/report1_grade.cpython-38.pyc | Bin 63919 -> 57383 bytes .../students/cs101/report1.py | 5 +- .../students/cs101/report1_grade.py | 180 +------ pyproject.toml | 6 + pytransform/__init__.py | 454 ---------------- .../__pycache__/__init__.cpython-36.pyc | Bin 11679 -> 0 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 11349 -> 0 bytes pytransform/_pytransform.dll | Bin 1368590 -> 0 bytes setup.py | 49 ++ src/unitgrade_devel.egg-info/PKG-INFO | 317 +++++++++++ src/unitgrade_devel.egg-info/SOURCES.txt | 18 + .../dependency_links.txt | 1 + src/unitgrade_devel.egg-info/requires.txt | 9 + src/unitgrade_devel.egg-info/top_level.txt | 1 + .../unitgrade_private2}/__init__.py | 1 + .../unitgrade_private2/autolab}/__init__.py | 0 .../unitgrade_private2/autolab}/autolab.py | 41 +- .../autolab}/lab_template/Makefile | 0 .../autolab}/lab_template/autograde-Makefile | 0 .../autolab}/lab_template/autograde.tar | Bin .../autolab}/lab_template/hello.rb | 0 .../autolab}/lab_template/hello.yml | 0 .../autolab}/lab_template/src/Makefile | 0 .../lab_template/src/Makefile-handout | 0 .../autolab}/lab_template/src/README | 0 .../autolab}/lab_template/src/README-handout | 0 .../autolab}/lab_template/src/driver.sh | 0 .../lab_template/src/driver_python.py | 4 +- .../autolab}/lab_template/src/hello.c | 0 .../autolab}/lab_template/src/hello.c-handout | 0 .../unitgrade_private2}/deployment.py | 3 +- .../unitgrade_private2}/docker_helpers.py | 83 +-- .../unitgrade_private2}/example/report0.py | 0 .../hidden_create_files.py | 18 +- .../hidden_gather_upload.py | 39 +- .../unitgrade_private2}/token_loader.py | 1 - src/unitgrade_private2/version.py | 1 + tutorial/ncode.py | 499 ++++++++++++++++++ tutorial/ncode2.py | 363 +++++++++++++ tutorial/submission_autograder.py | 5 + tutorial/tutorial.token | 2 +- .../__pycache__/__init__.cpython-36.pyc | Bin 888 -> 0 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 918 -> 0 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 944 -> 0 bytes .../__pycache__/deployment.cpython-38.pyc | Bin 1392 -> 0 bytes .../__pycache__/deployment.cpython-39.pyc | Bin 1425 -> 0 bytes .../__pycache__/docker_helpers.cpython-38.pyc | Bin 3165 -> 0 bytes .../__pycache__/docker_helpers.cpython-39.pyc | Bin 3217 -> 0 bytes .../hidden_create_files.cpython-36.pyc | Bin 3954 -> 0 bytes .../hidden_create_files.cpython-38.pyc | Bin 4588 -> 0 bytes .../hidden_create_files.cpython-39.pyc | Bin 4665 -> 0 bytes .../hidden_gather_upload.cpython-36.pyc | Bin 2775 -> 0 bytes .../hidden_gather_upload.cpython-38.pyc | Bin 4107 -> 0 bytes .../hidden_gather_upload.cpython-39.pyc | Bin 4210 -> 0 bytes .../__pycache__/token_loader.cpython-38.pyc | Bin 978 -> 0 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 181 -> 0 bytes .../codejudge_example/codejudge_sum.py | 35 -- .../codejudge_example/sumfac.py | 6 - 214 files changed, 6013 insertions(+), 2128 deletions(-) create mode 100644 LICENSE delete mode 100644 autolab/__pycache__/autolab.cpython-38.pyc delete mode 100644 autolab/report_autolab.py create mode 100644 build.md create mode 100644 cs202courseware/ascimenu.py create mode 100644 dist/unitgrade-devel-0.0.1.tar.gz create mode 100644 dist/unitgrade_devel-0.0.1-py3-none-any.whl rename {autolab => docker_images}/docker_tango_python/Dockerfile (100%) rename {autolab => docker_images}/docker_tango_python/requirements.txt (86%) rename {examples/example_docker/instructor => docker_images}/unitgrade-docker/Dockerfile (93%) create mode 100644 docker_images/unitgrade-docker/home/cs103/Report3_handin_5_of_30.token rename {examples/example_docker/instructor/unitgrade-docker/tmp => docker_images/unitgrade-docker/home}/cs103/__pycache__/homework1.cpython-38.pyc (73%) create mode 100644 docker_images/unitgrade-docker/home/cs103/__pycache__/report3_complete_grade.cpython-38.pyc rename {examples/example_docker/instructor/unitgrade-docker/tmp => docker_images/unitgrade-docker/home}/cs103/homework1.py (100%) rename {examples/example_docker/instructor/unitgrade-docker/tmp => docker_images/unitgrade-docker/home}/cs103/report3.py (74%) create mode 100644 docker_images/unitgrade-docker/home/cs103/report3_complete_grade.py create mode 100644 docker_images/unitgrade-docker/home/cs103/report3_grade.py rename {examples/example_docker/instructor => docker_images}/unitgrade-docker/requirements.txt (86%) create mode 100644 docker_images/unitgrade-docker/tmp/cs103/homework1.py create mode 100644 docker_images/unitgrade-docker/tmp/cs103/report3.py create mode 100644 docker_images/unitgrade-docker/tmp/cs103/report3_complete_grade.py create mode 100644 docker_images/unitgrade-docker/tmp/cs103/report3_grade.py create mode 100644 examples/02631/instructor/programs/.coverage create mode 100644 examples/02631/instructor/programs/__pycache__/looping.cpython-38.pyc create mode 100644 examples/02631/instructor/programs/__pycache__/report1intro.cpython-38.pyc create mode 100644 examples/02631/instructor/programs/deploy.py create mode 100644 examples/02631/instructor/programs/looping.py create mode 100644 examples/02631/instructor/programs/report1intro.py create mode 100644 examples/02631/instructor/programs/report1intro_grade.py create mode 100644 examples/02631/instructor/programs/unitgrade/Bacteria.pkl create mode 100644 examples/02631/instructor/programs/unitgrade/ClusterAnalysis.pkl create mode 100644 examples/02631/instructor/programs/unitgrade/FermentationRate.pkl create mode 100644 examples/02631/instructor/programs/unitgrade/RemoveIncomplete.pkl create mode 100644 examples/02631/students/programs/.coverage create mode 100644 examples/02631/students/programs/__pycache__/looping.cpython-38.pyc create mode 100644 examples/02631/students/programs/__pycache__/report1intro.cpython-38.pyc create mode 100644 examples/02631/students/programs/looping.py create mode 100644 examples/02631/students/programs/report1intro.py create mode 100644 examples/02631/students/programs/report1intro_grade.py create mode 100644 examples/02631/students/programs/unitgrade/Bacteria.pkl create mode 100644 examples/02631/students/programs/unitgrade/ClusterAnalysis.pkl create mode 100644 examples/02631/students/programs/unitgrade/FermentationRate.pkl create mode 100644 examples/02631/students/programs/unitgrade/RemoveIncomplete.pkl create mode 100644 examples/example_docker/instructor/cs103/.coverage create mode 100644 examples/example_docker/instructor/cs103/Report3_handin_25_of_30.token create mode 100644 examples/example_docker/instructor/cs103/Report3_handin_30_of_30.token delete mode 100644 examples/example_docker/instructor/unitgrade-docker/tmp/cs103/Report3_handin_5_of_30.token delete mode 100644 examples/example_docker/instructor/unitgrade-docker/tmp/cs103/report3_complete_grade.py delete mode 100644 examples/example_docker/instructor/unitgrade-docker/tmp/cs103/report3_grade.py create mode 100644 examples/example_docker/run_all_docker.py create mode 100644 examples/example_docker/students/cs103/.coverage rename examples/example_docker/{instructor/unitgrade-docker/tmp/cs103/__pycache__/report3_complete_grade.cpython-38.pyc => students/cs103/__pycache__/report3_complete_grade.cpython-39.pyc} (90%) create mode 100644 examples/example_flat/instructor/cs101flat/Report1Flat_handin_10_of_10.token create mode 100644 examples/example_flat/instructor/cs101flat/__pycache__/deploy.cpython-38.pyc create mode 100644 examples/example_flat/instructor/cs101flat/__pycache__/homework1.cpython-38.pyc create mode 100644 examples/example_flat/instructor/cs101flat/__pycache__/report1flat.cpython-38.pyc create mode 100644 examples/example_flat/instructor/cs101flat/deploy.py create mode 100644 examples/example_flat/instructor/cs101flat/homework1.py create mode 100644 examples/example_flat/instructor/cs101flat/report1flat.py create mode 100644 examples/example_flat/instructor/cs101flat/report1flat_grade.py create mode 100644 examples/example_flat/students/cs101flat/Report1Flat_handin_0_of_10.token create mode 100644 examples/example_flat/students/cs101flat/__pycache__/deploy.cpython-38.pyc create mode 100644 examples/example_flat/students/cs101flat/__pycache__/homework1.cpython-38.pyc create mode 100644 examples/example_flat/students/cs101flat/__pycache__/report1flat.cpython-38.pyc create mode 100644 examples/example_flat/students/cs101flat/__pycache__/report1flat_grade.cpython-38.pyc create mode 100644 examples/example_flat/students/cs101flat/homework1.py create mode 100644 examples/example_flat/students/cs101flat/report1flat.py create mode 100644 examples/example_flat/students/cs101flat/report1flat_grade.py create mode 100644 examples/example_framework/instructor/cs102/.coverage delete mode 100644 examples/example_framework/instructor/cs102/Report2_handin_10_of_18.token create mode 100644 examples/example_framework/instructor/cs102/Report2_handin_13_of_18.token delete mode 100644 examples/example_framework/instructor/cs102/Report2_handin_28_of_28.token delete mode 100644 examples/example_framework/instructor/cs102/Report2_handin_5_of_18.token delete mode 100644 examples/example_framework/instructor/cs102/Report2_handin_5_of_28.token create mode 100644 examples/example_framework/instructor/cs102/__pycache__/deploy.cpython-38.pyc create mode 100644 examples/example_framework/students/cs102/.coverage delete mode 100644 examples/example_framework/students/cs102/Report2_handin_0_of_18.token create mode 100644 examples/example_framework/students/cs102/__pycache__/deploy.cpython-38.pyc create mode 100644 examples/example_jupyter/instructor/cs105/.coverage create mode 100644 examples/example_jupyter/instructor/cs105/Report1Jupyter_handin_18_of_18.token create mode 100644 examples/example_jupyter/instructor/cs105/__pycache__/homework1.cpython-38.pyc create mode 100644 examples/example_jupyter/instructor/cs105/__pycache__/report5.cpython-38.pyc create mode 100644 examples/example_jupyter/instructor/cs105/__pycache__/week2.cpython-38.pyc create mode 100644 examples/example_jupyter/instructor/cs105/deploy.py create mode 100644 examples/example_jupyter/instructor/cs105/homework1.py create mode 100644 examples/example_jupyter/instructor/cs105/report5.py create mode 100644 examples/example_jupyter/instructor/cs105/report5_grade.py create mode 100644 examples/example_jupyter/instructor/cs105/unitgrade/Question2.pkl create mode 100644 examples/example_jupyter/instructor/cs105/unitgrade/Week1.pkl rename examples/{02471/instructor/02471/week02 => example_jupyter/instructor/cs105}/week2.ipynb (100%) create mode 100644 examples/example_jupyter/students/cs105/.coverage create mode 100644 examples/example_jupyter/students/cs105/__pycache__/homework1.cpython-38.pyc create mode 100644 examples/example_jupyter/students/cs105/__pycache__/report5.cpython-38.pyc create mode 100644 examples/example_jupyter/students/cs105/__pycache__/week2.cpython-38.pyc create mode 100644 examples/example_jupyter/students/cs105/homework1.py create mode 100644 examples/example_jupyter/students/cs105/report5.py create mode 100644 examples/example_jupyter/students/cs105/report5_grade.py create mode 100644 examples/example_jupyter/students/cs105/unitgrade/Question2.pkl create mode 100644 examples/example_jupyter/students/cs105/unitgrade/Week1.pkl create mode 100644 examples/example_jupyter/students/cs105/week2.ipynb create mode 100644 examples/example_simplest/instructor/cs101/__pycache__/deploy.cpython-38.pyc delete mode 100644 examples/example_simplest/students/cs101/Report1_handin_0_of_10.token create mode 100644 examples/example_simplest/students/cs101/__pycache__/deploy.cpython-38.pyc create mode 100644 pyproject.toml delete mode 100644 pytransform/__init__.py delete mode 100644 pytransform/__pycache__/__init__.cpython-36.pyc delete mode 100644 pytransform/__pycache__/__init__.cpython-38.pyc delete mode 100644 pytransform/_pytransform.dll create mode 100644 setup.py create mode 100644 src/unitgrade_devel.egg-info/PKG-INFO create mode 100644 src/unitgrade_devel.egg-info/SOURCES.txt create mode 100644 src/unitgrade_devel.egg-info/dependency_links.txt create mode 100644 src/unitgrade_devel.egg-info/requires.txt create mode 100644 src/unitgrade_devel.egg-info/top_level.txt rename {unitgrade_private2 => src/unitgrade_private2}/__init__.py (97%) rename {unitgrade_private2/codejudge_example => src/unitgrade_private2/autolab}/__init__.py (100%) rename {autolab => src/unitgrade_private2/autolab}/autolab.py (89%) rename {autolab => src/unitgrade_private2/autolab}/lab_template/Makefile (100%) rename {autolab => src/unitgrade_private2/autolab}/lab_template/autograde-Makefile (100%) rename {autolab => src/unitgrade_private2/autolab}/lab_template/autograde.tar (100%) rename {autolab => src/unitgrade_private2/autolab}/lab_template/hello.rb (100%) rename {autolab => src/unitgrade_private2/autolab}/lab_template/hello.yml (100%) rename {autolab => src/unitgrade_private2/autolab}/lab_template/src/Makefile (100%) rename {autolab => src/unitgrade_private2/autolab}/lab_template/src/Makefile-handout (100%) rename {autolab => src/unitgrade_private2/autolab}/lab_template/src/README (100%) rename {autolab => src/unitgrade_private2/autolab}/lab_template/src/README-handout (100%) rename {autolab => src/unitgrade_private2/autolab}/lab_template/src/driver.sh (100%) mode change 100755 => 100644 rename {autolab => src/unitgrade_private2/autolab}/lab_template/src/driver_python.py (96%) rename {autolab => src/unitgrade_private2/autolab}/lab_template/src/hello.c (100%) rename {autolab => src/unitgrade_private2/autolab}/lab_template/src/hello.c-handout (100%) rename {unitgrade_private2 => src/unitgrade_private2}/deployment.py (94%) rename {unitgrade_private2 => src/unitgrade_private2}/docker_helpers.py (58%) rename {unitgrade_private2 => src/unitgrade_private2}/example/report0.py (100%) rename {unitgrade_private2 => src/unitgrade_private2}/hidden_create_files.py (91%) rename {unitgrade_private2 => src/unitgrade_private2}/hidden_gather_upload.py (85%) rename {unitgrade_private2 => src/unitgrade_private2}/token_loader.py (99%) create mode 100644 src/unitgrade_private2/version.py create mode 100644 tutorial/ncode.py create mode 100644 tutorial/ncode2.py delete mode 100644 unitgrade_private2/__pycache__/__init__.cpython-36.pyc delete mode 100644 unitgrade_private2/__pycache__/__init__.cpython-38.pyc delete mode 100644 unitgrade_private2/__pycache__/__init__.cpython-39.pyc delete mode 100644 unitgrade_private2/__pycache__/deployment.cpython-38.pyc delete mode 100644 unitgrade_private2/__pycache__/deployment.cpython-39.pyc delete mode 100644 unitgrade_private2/__pycache__/docker_helpers.cpython-38.pyc delete mode 100644 unitgrade_private2/__pycache__/docker_helpers.cpython-39.pyc delete mode 100644 unitgrade_private2/__pycache__/hidden_create_files.cpython-36.pyc delete mode 100644 unitgrade_private2/__pycache__/hidden_create_files.cpython-38.pyc delete mode 100644 unitgrade_private2/__pycache__/hidden_create_files.cpython-39.pyc delete mode 100644 unitgrade_private2/__pycache__/hidden_gather_upload.cpython-36.pyc delete mode 100644 unitgrade_private2/__pycache__/hidden_gather_upload.cpython-38.pyc delete mode 100644 unitgrade_private2/__pycache__/hidden_gather_upload.cpython-39.pyc delete mode 100644 unitgrade_private2/__pycache__/token_loader.cpython-38.pyc delete mode 100644 unitgrade_private2/codejudge_example/__pycache__/__init__.cpython-38.pyc delete mode 100644 unitgrade_private2/codejudge_example/codejudge_sum.py delete mode 100644 unitgrade_private2/codejudge_example/sumfac.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..335ea9d --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018 The Python Packaging Authority + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 410439e..577ac9a 100644 --- a/README.md +++ b/README.md @@ -55,41 +55,48 @@ if __name__ == "__main__": ``` ### The test: The test consists of individual problems and a report-class. The tests themselves are just regular Unittest (we will see a slightly smarter idea in a moment). For instance: + ```python -from homework1 import reverse_list, add +from looping import reverse_list, add import unittest + class Week1(unittest.TestCase): def test_add(self): - self.assertEqual(add(2,2), 4) + self.assertEqual(add(2, 2), 4) self.assertEqual(add(-100, 5), -95) def test_reverse(self): - self.assertEqual(reverse_list([1,2,3]), [3,2,1]) + self.assertEqual(reverse_list([1, 2, 3]), [3, 2, 1]) ``` A number of tests can be collected into a `Report`, which will allow us to assign points to the tests and use the more advanced features of the framework later. A complete, minimal example: ```python -from unitgrade2.unitgrade2 import Report -from unitgrade2.unitgrade_helpers2 import evaluate_report_student -from homework1 import reverse_list, add +from src.unitgrade2.unitgrade2 import Report +from src.unitgrade2 import evaluate_report_student +from looping import reverse_list, add import unittest + class Week1(unittest.TestCase): def test_add(self): - self.assertEqual(add(2,2), 4) + self.assertEqual(add(2, 2), 4) self.assertEqual(add(-100, 5), -95) def test_reverse(self): - self.assertEqual(reverse_list([1,2,3]), [3,2,1]) + self.assertEqual(reverse_list([1, 2, 3]), [3, 2, 1]) + import cs101 + + class Report1(Report): title = "CS 101 Report 1" questions = [(Week1, 10)] # Include a single question for 10 credits. pack_imports = [cs101] + if __name__ == "__main__": # Uncomment to simply run everything as a unittest: # unittest.main(verbosity=2) @@ -109,7 +116,7 @@ if __name__ == "__main__": setup_grade_file_report(Report1, minify=False, obfuscate=False, execute=False) # Deploy the files using snipper: https://gitlab.compute.dtu.dk/tuhe/snipper - snip_dir.snip_dir(source_dir="../cs101", dest_dir="../../students/cs101", clean_destination_dir=True, exclude=['__pycache__', '*.token', 'deploy.py']) + snip_dir.snip_dir(source_dir="../programs", dest_dir="../../students/programs", clean_destination_dir=True, exclude=['__pycache__', '*.token', 'deploy.py']) ``` - The first line creates the `report1_grade.py` script and any additional data files needed by the tests (none in this case) @@ -193,15 +200,18 @@ also that the students implementations didn't just detect what input was being u return the correct answer. To do that you need hidden tests and external validation. Our new testclass looks like this: + ```python -from unitgrade2.unitgrade2 import UTestCase, Report, hide -from unitgrade2.unitgrade_helpers2 import evaluate_report_student +from src.unitgrade2.unitgrade2 import UTestCase, Report, hide +from src.unitgrade2 import evaluate_report_student + class Week1(UTestCase): """ The first question for week 1. """ + def test_add(self): from cs103.homework1 import add - self.assertEqualC(add(2,2)) + self.assertEqualC(add(2, 2)) self.assertEqualC(add(-100, 5)) @hide @@ -209,14 +219,18 @@ class Week1(UTestCase): # This is a hidden test. The @hide-decorator will allow unitgrade to remove the test. # See the output in the student directory for more information. from cs103.homework1 import add - self.assertEqualC(add(2,2)) + self.assertEqualC(add(2, 2)) + import cs103 + + class Report3(Report): title = "CS 101 Report 3" questions = [(Week1, 20)] # Include a single question for 10 credits. pack_imports = [cs103] + if __name__ == "__main__": evaluate_report_student(Report3()) ``` diff --git a/autolab/__pycache__/autolab.cpython-38.pyc b/autolab/__pycache__/autolab.cpython-38.pyc deleted file mode 100644 index 86ad4a1d06d4f3267ad22b37b7839b186984db04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5899 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 diff --git a/autolab/report_autolab.py b/autolab/report_autolab.py deleted file mode 100644 index e69de29..0000000 diff --git a/build.md b/build.md new file mode 100644 index 0000000..4ba0737 --- /dev/null +++ b/build.md @@ -0,0 +1,18 @@ +# Unitgrade build info + +See https://packaging.python.org/tutorials/packaging-projects/ + +- Build the distribution package using: +``` +py -m pip install --upgrade build && py -m build +``` +- Upload to test repo +``` +py -m pip install --upgrade twine && py -m twine upload --repository testpypi dist/* +``` + +### build and upload (to actual pypi; remember the .pypi token. you can find it in personal dtu repo) +``` +rm -f dists/* && py -m build && twine upload dist/* +``` + diff --git a/cs202courseware/ascimenu.py b/cs202courseware/ascimenu.py new file mode 100644 index 0000000..c5f1561 --- /dev/null +++ b/cs202courseware/ascimenu.py @@ -0,0 +1,43 @@ +from prompt_toolkit.application import Application +from prompt_toolkit.application.current import get_app +from prompt_toolkit.key_binding import KeyBindings +from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous +from prompt_toolkit.layout import Dimension, HSplit, Layout, ScrollablePane +from prompt_toolkit.widgets import Frame, Label, TextArea + +print("hello") + +z = 234 +def main(): + # Create a big layout of many text areas, then wrap them in a `ScrollablePane`. + root_container = Frame( + ScrollablePane( + HSplit( + [ + Frame(TextArea(text=f"label-{i}"), width=Dimension()) + for i in range(20) + ] + ) + ) + # ScrollablePane(HSplit([TextArea(text=f"label-{i}") for i in range(20)])) + ) + + layout = Layout(container=root_container) + + # Key bindings. + kb = KeyBindings() + + @kb.add("c-c") + def exit(event) -> None: + get_app().exit() + + kb.add("down")(focus_next) + kb.add("up")(focus_previous) + + # Create and run application. + application = Application(layout=layout, key_bindings=kb, full_screen=True) + application.run() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/cs202courseware/ug2report1.py b/cs202courseware/ug2report1.py index 586a004..e0e7e05 100644 --- a/cs202courseware/ug2report1.py +++ b/cs202courseware/ug2report1.py @@ -1,16 +1,13 @@ # from unitgrade.unitgrade import QuestionGroup, Report, QPrintItem # from unitgrade.unitgrade_helpers import evaluate_report_student -from cs202courseware import homework1 -import unittest - -from unitgrade2.unitgrade2 import wrapper, UTestCase, cache +from src.unitgrade2.unitgrade2 import UTestCase, cache from unittest import TestCase # from unitgrade2.unitgrade2 import cache -from unitgrade2.unitgrade2 import methodsWithDecorator, hide +from src.unitgrade2.unitgrade2 import hide import random -from cs202courseware.homework1 import reverse_list, my_sum +from cs202courseware.homework1 import my_sum class TestPartial(TestCase): def test_a(self): @@ -83,7 +80,7 @@ class ListQuestion(UTestCase): """ ccc test_integers-short """ self.assertEqual(2,2) -from unitgrade2.unitgrade2 import Report +from src.unitgrade2.unitgrade2 import Report class Report1(Report): title = "CS 202 Report 1" @@ -97,7 +94,6 @@ class Report1(Report): pack_imports = [cs202courseware] # Include this file in .token file a = 234 -import coverage if __name__ == "__main__": """ @@ -113,7 +109,7 @@ if __name__ == "__main__": # print(inspect.getsourcelines(f) ) # How to get all hidden questions. - from unitgrade2.unitgrade_helpers2 import evaluate_report_student + from src.unitgrade2 import evaluate_report_student # cov = coverage.Coverage() # cov.start() diff --git a/cs202courseware/ug2report1_nohidden.py b/cs202courseware/ug2report1_nohidden.py index 180417c..3e94a57 100644 --- a/cs202courseware/ug2report1_nohidden.py +++ b/cs202courseware/ug2report1_nohidden.py @@ -1,17 +1,14 @@ # from unitgrade.unitgrade import QuestionGroup, Report, QPrintItem # from unitgrade.unitgrade_helpers import evaluate_report_student -from cs202courseware import homework1 -import unittest - -from unitgrade2.unitgrade2 import wrapper, UTestCase, cache +from src.unitgrade2.unitgrade2 import UTestCase, cache # from unitgrade2.unitgrade2 import cache -from unitgrade2.unitgrade2 import methodsWithDecorator, hide +from src.unitgrade2.unitgrade2 import methodsWithDecorator, hide import random -from cs101courseware_example.homework1 import reverse_list, my_sum +from cs101courseware_example.homework1 import my_sum class GeneratorQuestion(UTestCase): def genTest(self, n): @@ -57,7 +54,7 @@ class ListQuestion(UTestCase): """ ccc test_integers-short """ self.assertEqual(2,2) -from unitgrade2.unitgrade2 import Report +from src.unitgrade2.unitgrade2 import Report class Report1(Report): title = "CS 202 Report 1" @@ -103,7 +100,7 @@ if __name__ == "__main__": for f in ls: print(inspect.getsourcelines(f) ) # How to get all hidden questions. - from unitgrade2.unitgrade_helpers2 import evaluate_report_student + from src.unitgrade2 import evaluate_report_student evaluate_report_student( Report1() ) diff --git a/dist/unitgrade-devel-0.0.1.tar.gz b/dist/unitgrade-devel-0.0.1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..9a8b82a9d4e5c3c61bd6ced6cace4b2a6bfb40b9 GIT binary patch literal 17133 zcmb2|=HQsP&mfWMzq3_LXmM&$aZE{RMrw>pesXDUYF<fkOle+bNqSLYN@{#TQD#|U zNoq_=W^qYONp3-&c|}EbX?6iZiEc`2S!#~1fu4b$p<YR15yRWs?C{$j+g{Y9>Hc%x zy7}1+zuQdGLYM4L*S7n4v|DqgH2B=#$#eR|ww%=yRoy2!-_d!OzyEh&sFp3y66>>b zX6ntgl-0SqyX<ZG+qXHE(e;(@>*^lwOxFK*`TeJy&zs-ffBSas-8cC)_g}uTd;QO| z=HF#yo6{<#$uIt8`+vIs<mEnr&sXMG|2_A4?%Q<zvhw-%#ou<FkAGA5z2>q1x1as> z(-zM6OSbs^cH{Sx+_!F@+jjZpuV2sW^Yion{?z|>_vO3NhkxuhKmT7|Kj%N^kNv;* z^!zW^`ByLU=f3I3|M%{_<37M3_WA$8kN<BUp3d&RoIU>IAN%@$HFtl$FWXkm^}IfP zJ@@na+w=b4|63`k8@ADY?$SkuSzYhSp0`@eu6*=YYp(0UiuZl%?yQveJNi#3?nvFP zTX$~Usd#_1*xs{7<Dq)ev!nM4R2jMsU6I~?MCF@}pzo24XUBrqt6uWh@z6_Wk3}Qb zMT6460<%kwa#-EJ)3E3z&n?eIo>gy;$xV^tHuqdK@x4=Xl1%TkVtt>xBGz+$G*0hu z<7!?r_mtFDwXa6s?j#f+=4Y)7xqoCUzjFLCrEil=zPaD<@c!{oBciWR<W=#5^R+iw z!<L(Rnwc-SnaVWzz`|`uY@~h*|GB<+rtY-E2mI4?4Zm%C`_QO$I_Ex_O2unUGjm)w zdhB6fWApuCP%<me%ucIiar}ct%Zg?S)fC%yKl{1!hryzgMtlo{`?Y$NBg|fXPS2n6 zNLt6fvq@psv75b%3f??kGD$8^CU32Ek6opS{Jrl6UWq$wxbC#MZrk}O*r>SKfyZ`d z)&=7=8?*g1j|m=d<O;sedqk;g<>^NuvQmd;GZ$9<`SoV`-^t8{6<YV^%l!ZQ;p#!{ zz}xn%_qR^_@$$o)vme`y8CCth^`HBv*cadZcX55kemS|lS)I-QKmU93;7f?i{G2?Q zebxWJePD=_liOSK`{C7x7yBQvuc#=gQM)I%&)TLr?0g^VH;vm?HWo*(-IJZOr-s@7 zd%Z<Xb;Y;-YtN3oKD=2u=j+#xoj1hafA>Gf7hhMgyI$#Dy+vhxO%(%k1?yWmS-HJ- zjd^_cWM$@BUeMhm%wFFv|LDuvf48^axc=<eKlT|1IQati*wp>+pL|&U{+v$p<N5Kj z2TsfLCFK0J-(&k=UqYPz{de;FvOB|nzyDTmvmomD1s)&%(;pd*`sed~ko~rU|9^F5 zSw&5Yab;yqNg0!Uw86#6#<Aa*AHLi??S{Rj%~8|J+M+*v&pNWXLi<*VuBa$EaP~n} z_nmDHf`9%#{Nk`(BTn|s{?=oD8;#BeO)b+A{A*q(v-<zWU-K9JTYmYa&F$K>r49Bv z|NsA6Z@;baimW}`|NWb{Z;qbwzg$o?;@^M4dF64h`tDxI{V)5&FX2|zqSqy6=6!c| zUKeJ4aO2K}Rt}#A_K0)O^Y*-AJU^vE^J8_Oh{~BB=iZEoTN!yQjM_L0XWdgbk6<eA z|MRe>__43a_gmcMT(##el}!AyZ5vbhi=IV4nJTi<=jhG-cr|R-oXY53dk-povv(9e zkUuf!l5o1W|DKPJ4#ci1&&_}L%h5mK?EV7n17U{c_k<59Ht7hvpRn3)KWER|PWfrU zg<{PY9v@w+FMm>O!&z_sh%et8XZtOxxF;;%_phqt&*h8Hj-6&uk*a@GZQ%Dp=+)Wh zCl>54J^WHgO|QyUwd7#D^-r#%BR`)0_!96&U(E2shvYxJ#R2m_m-aRGvlp`MvWQh( zY5x4wBLkUgZvKD5%VtZf-#fmPzbJ5a^}EjZzpc-8pY08gGwS-wYV`5#bU}~O#9MaJ zx(O$P-WTs;jjl88Tj#uY%G>J-Qw|zLW;ewA6aMIUxbL3&U#_KY;w~MjQfCu;lsTn% zB<J7V$;3bDd&1H^uQm2D1r!E9Y<(xd>R`{(nQiH{c*X+VSw4F#vISZuPi@<{F+zg7 zPuqkyaGsCM#+`~q%~E=Q6Lc0_XVvvk(kq-CJV8dkw^2o~pEXtb&$JG~Gi*;5r#$Q} z_!;RSBwYTEeINIsi(E^tpEuc?E$^Z!KTYJ9Po#zFO9>TkeLhwXr@kMY(%*yUiSV3? zZWKx9V|AM~NrnCFa_u?)IaKU~7&0v`?6%6`ZE#LrxVbfV(+pXz^S)tU%G0ZVpI0hW zm+RQBu)BGJrFg{e$>Plyy0hEos=X9yRClbbl05O)rpva?wEWDYWzq+Jemov9sr?B< zTZGH?IGaee4N=ND9S1MD25#Ll|L5E#&nnm6&YKZ)dDV_i_voiwcSPRUbT+A$_5J3u zzWq${WN@mEyj)s_^n(sBSyzS<%{6u%>lF0;|GYFZE8(^MxWCb@<MoYp#T{<et3Da= z+!a~H(#FS_RqJ7Mt$G6AQD?=-W`T}A;f-C>SS+3$Tc&%bLWjv)jivNs@|(8P3<pn0 z>jttqA7F~$-FN7+*zzpzWev@q>V~JJeDyMgGpDQixjk@?S$j=;WzGrFmDcPkm)d&2 zd2U<&RUzZ?Uxu0SSzJPewx8V@PHt46)~(R=*d(l1PRiQa$lo?smpNQ+)0G`Zj1p}x zuI4b@^y9ZR)3yr9X9?Y6FMAl|f3r_9+jS@~X=kZB-!;2qhua!HeR&x;h3S`-^V@ct zsApcaNsHRGmj&*-=Wr=}<-+Yplp0#wR#wPO<X<2cY`=GUgN6TmuO*(s*WO1aHcJ#X zTKFf;^xVS3dH8{Y{`M=+*LoYWrSx&{J$2%R^RDY(yiHx3E1O*I9>1T(qQ*9VcX(V- zfEi;}>^X6-+bpv`@y9$3JhJ0zc*ImY#lB|KzD&XOf;%E+haOsz7i3Z5aGN>grf>R+ z`AjA*COJOr$KAKE6wg&|l@7jrfT3kYp~%b9lsr{F+4gIXoww;T7_+Y4c1U$q%Hi^a z&4K@qt|(=koO4R*(~6agW(rLRDVCZe%~@c+hi9X?`=Przk4*TS!*-m!n%n8jc2$+( zLaNX&Zi|}f0lytLsGK%m`8?EO*3rNh{~sClo=Thdq<=oM${p|5og9l^c<s=<$Sl{_ zyj$IgU)|WbS5NWq+1}Hqy{~LsWxUta%j)*rwUH`<x06)oeeh%o>3Tiy$@y(s5f|Ae zui-NbyxC^+W@*)d3oIAAR&laKah{cm{#d@5a~5;c58<G)^kk!YMT5URGvp^$u;eg0 z>*uRD=5A@(9{hju@-OSMru9|uYKXJHYExeCE~|AzzFL@p`3_&c0K*ZJXWD<3-xT)W zH`CzUnyXoVj`v?M6SB}zconmOZ)@YN2Y#QbBb1Mv=hz~&MtFhIA#R(bq8XA4{xz^L zPJY<Ioc^kSeP`pG@~FGl&bbDw7|)XYu6(ZiLFscRlbEZimo<BWr!5IfnVf8@&r~|c zWCI)Tc2gankaL2LCNbyQJ#OmX^*(fa`eAQ7g~_QNS=FWnhrG0}YM;5`{`d2q<Gpq3 ztA%PG)>-TD3g3G;<4>mqk5j~|gAQMQFEW!*SS7)ewP~*UiRH}w2I6z){qPK`w-xbx ze=NE9MU3DBeIMz`Qi=A@CKaUbdKCTR|GpguAC^YneU`g+=K2CB@8c@^GnLOB6kW5G z>o&{mD^=HS?LM$n)g!E+=R)rbpTBDznnga=S$wnq`B?SU^UAn0yLL_r&FK%QC@66* zdG@SRyynx4b(bPL_G)fal2@@4>&@=pD7HvbqO8G;^@fZ@J&(=Q569=7zVpGxsUm0E zIk$HuK94f`PGmH$X1LuG)M)uzOwaO-Y<*w|qx8)?3phLG+q_?OIUru<33nWmXWpq; zSC1q7%y(F4+;yovz+P{Cp+kH*yL{qOsXd{KZs&B~eP;Xg)`8q@M~pRY{d*_rSi8>N zvitXMoq3*&%niXe4>fnxm+Q?cy4WD(bz<@4d@rRSIR#Iao0)}`PP=PvPMY9d?Y77K zg7l*uo_b7bciopL3QBG7OlWf9ynjYHrQ+E9g3rg29w|PbExFZ5l2awY#q{@)9CkUW zV-9~lF|HDATzTi0ik#&v{g+bpS_uy_L#BnfPPt>Aa6ryuO-|$MhX<C5H)^ce$+YL+ zT(<wKe|a?;eq`8wHEv$hg{u>ny0|Od>-UyWDU`1*ytlx$W!<*f;vK2{H&=9=VE(51 zp(JxswAF2m+=hUe9*deEU1@N4KYNiw|H*>)t}o@Cp7C|w4Jl!H!V)9a;QTBpQ6sCn z)OGLk8%fMw{EJy!Hr9o|yCbzAd&SpFCuUvzs{P3G8OyYpfnryc3UtD|K7NWkHsNtd z%qFX@n~94RAIEKX+#nbG)yR?WBI7y#!rdj)Iv2Mz*S}Saw*Ks3@a4FM3A4MG4%0WI zFU$5#I`C=lM2nf484U~u(^ouo3g>AQSNA!%Mrr{E`#HW(-<McOu2TtKCN=$+Je%PR z-qRwU>w6SbCeLtZm;Jd><zk7fm<YSgCPr)i6T3J!Fn2RfKh9tDS|l&AElRv?KJ$e2 zUru_YDJ*8=Z`@`2SK~s$5sS#L@w*v+@wgm5X(V|t<0|)i;}d^hEw}s4)mBlQ5w|C@ z=mEbFV``+OpQWq&U&f}>Msk%u()T~Jhz{zV#^7$`wr26Y!~aE|XHS!C)RE!autZ*H zML_H33eVz2&iC!t^Rsn}YPPU7S9xEVZ<Sf{wDrgGXRlwg9d!<zuO_4ZVzXeC(2EF{ z9aA10T+JJ*&zfztbW?V!#tN2Id!L?CWMJmt=hbvHIi&F=;*6W;k)>Ow-&L2h3M=@( z;zyj`r=UKuBdiH`cDAeRYGqG-R%f(FV@21cgEfb_ET;*q{;J4%I;CRS%Q>ePAI`E^ z$$w$$0}jQ_>jloWcQv2Ma1M08TGFAz__CnFGqrJ-;mS!niqpR3JdjB^Q)y@`Uw3rJ zfe)>F>h(Aat&0xpJ^fL8>C}RCJl~UAr|eQMkL!wmyYs}=D?%%e6n@^RU4AT4Qb?g_ zkBp))cL4XLl!mOssa)xfg}O&hZ2I55yyR#{w`c7PP41<e#G)kimIpt{tD9BX8tU#= z`ApA2Z0R8z<(txpQClr|?bbZ2D7<Fy^O>!%oX1sxsS72Ys+ahGFXYj<k)6!5uGX7p zVR5pk-p`I$FUFlaO0(W8Ut%))SlYr?Ymxin80&n_857@4So(0?;pfKfKQ%u+6zFra z@sm(vwEnZY?bVAoty`0hs~Ju)%sMY{pD9^jxoD5Zw8np?VY0?wXYMG-+Vq(vnoUHk zWs2&DV-8!CZp>yVZMdPZCid0F8|<Z(k8LI_WSyX9&Sq5}X;={T_>R|W&Ib&PYHlmU zj2r~Yy8eB6So+Gzwv6}iORs>08XeOtp30!zZ#b6REjgBOE%~NMskUm9>Z;QdOPKd8 z6zJgQ7S6o5+x3CpUkTBF1ujoQHs=3PyRYy~q1ND!>On_cIfoYcHCjJ}Zj@MNK0cx; zXf<)|942$4F98Av?@iEe%wiDz7jcXIv>JQsm3t0}MiCRPUGx8U{n@e8Gq<=r_&GUy z3U9~7gBN5E6|`5pI^OX(F}O1DLPpCI$0V=PfLmModL|orw@V!RVOoAo<<!T+{(>jZ zZdeljqwq!G>;qGNT;X1?Y3VI>$UsDT=aw#(yCwSyWUjOH6`T?@`C%bkd~^;+LF<J_ z&)gW_6i1b{2-%q%b8@O_2>MGXZeMwlaorUUJ=KFkmhw!w40=;O*nbF>%ZfR<M?~tG z^p_}=(j=uNoj&iRR-XB2p0JJQ_@#uJE2~=Vv+r^}jLhAv^q_>(O8BkGO19(4vo92_ z2$etN*l_pboz+5Tv!9*~{mS_%<ZI6%ua_2%+J1||_}duY|0tQ-S>(NNp+G}9<Cy}J zo@3flEys_o>52HPsc^tOg)ucq#8dF6FvsC8StXVBaEt5+u}juzpWPW73q^{rD{TBN zETNhs(Imys>S)E!({Pyc`*FFBr$O;2FGp_pI$OujK+CDuW5SlL=Z{!)rp4YkRHh+Y z&S`ML#M`TKN`yqo`i0_|d?yN6jhU7_4!dEjZe>&3wdls><p=V760%m<NH6@qQryqB z%}mDR7JG)8aps!-q+I{Wk1pNSV5ykTQs8@H#qq?Sf&r%(wbfm=AFo*!b@*)W<ACJs zMdnLbJQ=?WUEFzP)A6Mp7uFTN<-MJ=*u`U+J_B!^mhbUfF|41}_B4O9Je<p99yC*r zVYQ2k;3m7)l38l&g8U?Mw*}AB;|}@}u2%J1QO9Op#FV@R%TM!r)fRTXyRxT0K<um2 z%!974E%avE?@eoJdM1C%CDn7Lq*<){8}&9-#q}=)D`QW!FXCe@JtWya|1xWgGw;Ph zqt<tGe~HafFY;4h@tV3%OxpAOW<SqQn--dEIs2PSbL*MIrbgwxr$Wx9m=-wtEn$jZ z_2=VN;f?lgZ*^D4@zl;OzP><P^q-e$V_JajQUjCT(#^SRf4SYz+*)%|@8@*=lh%yo z-7QvZvkGMzXPnHfoG-p+#*@h!du7i&c3yR|kj2I}(f@L2N95P#m)!SftTtX#<sa>I zYPY50$p_m^id%V(pP9Y+9P^Bj2?7_6y69>$SiUmsEPIs7{y=1JKzH=?D37aW^xl4$ z;4w+L_FZ5q*MI$=#^tV`y=+eI)%tkgP5$1$9sK9-KiT;rOnyP|H2<ZG?5z#z7y6!b zDBU-y;T0RpN?kz}wJn=s7oNN7z|@k~c-8%{|BMOuroZT6;_+k;*fY`kOUT2!%d@PL zHW!xgepoVRrAJSo^uFGXwpF`kJ)9mUI{)p)H|&d8^foiQKH0M^Cuxo2!n<NAx6Ve- z;P_U+vC+Krap411787GOp|i4@8WVyh+*FM`6S&d6Hg);gpSl(fQR|A9n|oYKn#&kh zvNLMx1pfx9%Wk|99ua3eH>}!vB+t8KRvM>C;Ki3_iOx$DM5}Hr&Ct5NW{ZM=h4)LN z-Ij~}w%`18PF6SjcJkz$9gG_mKVkWNpGVpyW`U=RY{j<^dv<&*IhhwzuDIuG@Jpxk z){{L!VoV&TpRgu3_$}We@0ir}kmcZskOk{HHZsf4IsN8A%DLFbCzQKhFX!4LEG(^I z8OEsgC;#X@kz;`q_5~ja%*i|9ocO@7^O%u8OU|YZRR{bIrF<+pU$f6g@U!Vs5o2~g z`;QE@E{_epW#1$={+T(W@*q>~PLGz!RauLq@1^U@{p?h4KYFjtqQ>S-je_LYtmFK) zHfw_=dOzIpY>7~i*kQEfmQXRj!J~+cZ<fR!R8;0lvYlNhl=w5SQgqFd3z~b&eWvc3 z=Kb0Fd|iCYde8IE7kz%J^lhqh)S~<ezK*+`4^B`~ao*a#{L5FPV>6Yn-&10{8)Ccg zSgzYC3(jzlk9+1U<tsN)zc@2|qfg}2lsb2j9XIFBt2itdyym|=<EOB50_irjlbJ3@ zIL!OZb8qUkZWZl&CQrN7j>=x+tw=t+*K1W^W7HJ3877i!X6APed|7s7^}6qUdtY8~ z5WE(=&2jSwp4H9loee*4`7Y`3eWbE|&QA6fcg5dWd%W3V<eCxP{W4HZHgKu`0uhx9 z8wI9+l~^WLbv<NKr=j=m#`>p%raw#De0`5cUs~bFH|5^?%_U;G)Ar9g{ZGiQ`g!=L zn~$y=+^T<gRI@Md!0iaBC&F*<KA&ZMc2;)Q>bZvI%W`9^_bhZyTsB+Z;FiBfcj|)o zd(7?y%$>~Ew^j6o;<^K`wDr>tHti1H_TZh(nWo*v(^<Fw{;IRXnt#iKd%0<M4bQIG z@~|b|b6syznc5VtTXP$Wd>roG6;%0caMmruz++oyTX3eKZq?Gw+C7)9DJd`5yMm$E zyCL~t^u`~Tm;-J{PyKe~h_lMw&w{ElPqPBkl{RL%EmIM63Us@azw$?@mY>jzEm@m& zwm&(uPIbDe-ui2^c~|y6Yu1@#9iQ{?h*@eZ`-*+j&A#lMe?UdF?N8@(9nI+YdH$VD zY?EwHuraz87W|#n!Z2gcrvtmHnReFIT}=^kV5r!^bu6gqPt?6%Q6{^JBo9C8%B}T% zto27ju5<aR7g-;6aZ2u&f0kM}tBU)}EMuK3Gh-+5tjl?Ih2@rku99?luaL1(oaVe1 z54{O2d)MUD^M=gw*(r4DrNF~KE2<BN%{gne*>r2_+BTbC^UBWTrk#p=VqjuBVR_ib zB}-DwtWRG#c4yPOJvY=l{%u%z;Nix!^B<msep<^g&-bFJ*(WVO!Ji&)4W*fyg?B4o z^HZ{(Cau)9|AfY3IpMp}>TTP?-%fNfoH=vOO|K<06b;-B)NiDktku2J%RfbM-BNbD zUktxKryiI!+ot}wn0IpZlC+8IWm#7In|7wYFZ+zP)1o7ZrOAuUq`Lo{6#D-)UMb)1 zZefvjAA7tb?^lNn%mSvoixxKj&z$n`ZFJ!6N$xAI+1%e}@X+U;^n$PiwI^KjgKfQc zuQHhyZZQ2;>G|H|`MFoVKS+si<^APR%+|sBVwFpCzR!b<s6{sZzF$r@TosSWY<Qd6 z+GED%XtK!gTW^_h+>$?MlpMld&Jl=`H_Ljs%$WV3+nU5Jx0k547HzeT*7jczUssyW zJ+snFsj|)5m+O>;Lm-<~so;qXysx+>KHYF{N$<;<zZk-_z8+<AykA_fT6(e#+tk*5 z46RK!zD?T5;W%jz+vD>W87_3R&Hs}z^MIwYr<h=f$f8`sq`6@teS7|^@uyBYwq&72 zqT950J{!KN{9PlvYxCU3nIhYB4@@aMFq^IY><(G4uiG}gnzU?cU&cnRdBH!$<M(E7 z4=&g8Iyz_8!|yKMn{O}Hwy%70Dt$7);v$hF>TSQa`_8|uqxksHqM&I{op(K+Az=|_ zmdP;Vj+D`}%EygX*8Benq!~Z5mf*_m>zOE7!raQn#r0Td(p;u*c?NG)wzf*IH+s?^ zlktXCE~?wd^KeJCv(KtvmQP*9U$YhF+1|>&#Jh1_8-Gz%|DBsF-&NZ72AN;zX<PG! zZ>sdKNvmFFu2>OL=eyuyl=gnzw9`>iA3ns1HTW&kC@$ORGjB!cz4?s&=eF)zwxqv5 zNW$c5>=qZBS}yiCFM|s;*RBlJX<by}WjHVM*!*WbYbs7R&9OHsRAhdY=GN#qCHcR3 zi2V-BwL6mROn5FDE!=neL+a#JGC}3WRkx<?yRZ9J+x*z++G6_x&x^{gQCmOGtayC> zxxzigx#{m&js7Wo>)bqhM$tm?6>Uq`UVfu+u1H4F<xyyEeTzk}&x2s6&~gpi%^NqI z5-zY@x$tB-H_N=uwI?^`-0~1J`?t9F|A!4f`m4dLqM{A|U)^mqmbZ8NGyVBpKf`~I z^U8nk?EU{qqM!FlB6H~t2U)X#_Cd>ZcU{0&EqVXg{|z7Q{~i5kZ)I)vuf6yG#t2U? z&YoK~KWZP>=j4~qdn<PC|K{h`X8(>awt0T!-1eEj_dA4gdde($KUIw5($1xUA}aU4 zm+$ssS`&NTMxWs)qgJ7`^Op}yyY8E>w)nzZT9bEno|*T5?U(=0<<0z8uKTxM<k$a0 zOa7zGk=-d`tdIER|Kql#@3MY@|J-sGZ(4uEpD)`ER{q|1Idg~&qon`cmknF1YY*-} zp}SjuTP}~U_y4tj{?FI{&;0lOyU3sSFV!#l-+lGE_5bn<|L5Kn`oDj7_RIf|ZTvO< zC`L*z^RDkXwprauf$?_G2RY5?&2u&8<t;sJ(p78jvFUv7N?osQ7FKa~$DFSRo(s5i zIoDu<K3lVnSMxQAoV=8<bx}h7ryjoF6}`!BR(<p$vBULy3Pcv}ToWI?Z1T5msoMwB zE(xT4;dpZ<<=GL3cT&FWj}Pd6xbOO4d9Tzey;;xaa)14|=W=%PiAm~89fvfnK2FHx zSe~?{so1P-P4aWQoQ&^Vb5;bsIL{XD?5%Gy!+x)qxw86g^JTjGcX;18z9Zf`y6RD{ zIQzRz(|#L$xUl2$`os&J`wJMjLJvqJ@87mQ|MH^~XMPvVR9@DV<7Kz&*xdy?vo>0M zt^0U%RuOY1o7|GstU6AwYW=o9%c}v6@Za5L|LG9RzJ1m<!SAOPURbYvqpo1X(JJpq zTh3$b7rZX*H(Xcycf*{n%`?`SU#nX5?Wk<dPtQ$XgiKFG|J1i^Ov*TF@p8WO-M!{X zPxgW4CVUs|oyRqwtN!hbIH|4M_Xz!X2%4T)9Q{W<zh{49Zp)uJtBZUZnKm>B_x<%M zJ(`&IHuR`|)XdcyJ<WGK^0IdD6clA||GD1tKEtz4`9Ahn&X1+u$9-KH6dbzfC(jh8 zP4U0??SH%{K=$(7)e0x<=3hVjbk3e9n}QqF1$ws{RdM}Uv;5tJn7J2b>;1Kum;Cc+ z)+2vr{ilx{SysRPbibe`q2kZ?8pgXS%-a@SFL>m8tn5{Uo#3y8YhMKfs(v_sUHBxw zs`1?BS$BW6{_C5tx?bbLy2Ia6&z_zfd)}|?<7}JTrH^ycU5l1W%bwC)wPKg6s_mTZ z*N)fdv8IH54>}vMOXit#+s(5-9X2S><`P`hbV$nY?whyyM;srXW)ynqp&lc7MKt&J z(%@$grX-|Q9s87Y%%OSR>|K{kMY>J?O9-vM`akM#e&GMzzjN}R?>;wgD%1ZRpX4{( zjsO1V{oA)|?f)O$Z*6_vYOZ}}`nNyPfAYWozxeTg@fqeXZ(5K3ug~~1zwpABqaT-D zHk0bwR(JK{52Zi%S$j5Qf84+I|Bb)(CBOL})Mx+S_~Sp{AMoTEe}9~Oz3T7t=kA@$ zd-$jP;h*UL=iMvsRj%xwwf@`vNX?tVvCB8_EPQfq?>r5L<ImdkToN|zEZjBceC(N| zFSFk;&X&$!wDWr0j$nQ6w!aBqe?B{Ax6-^?DY$l3d2UeUx0Z#U?%bIml^;}}pSLG{ zYu)d|CvVR)2>Ge|F<bQKg|r9DDyFVhx0Ct%^~I0ni_i8>kD7g7nE%3q=h`+tt@GCx z@8_#8e(a)k{`Q?>{|Q~S#g9Xm@#O7KiT3`Tv$a9>^v$R*LEGXVZ?*pT?ZeZ97hg7> znUH#!>(-Vgv$YF+wKE1SGr~tZqgedL4}G(L&AtDV=C2p||Mbg`!zWKS2dBvFcl#r5 z@wVW`=a2Eb?#fF4{~stPxjEU{eeL}d&+G04tW+?Q6A{jMyl_Hsx#Q7)y<yB(*i^n> z4=6nFUVo-rH{;H4VIo&K+1Qxn<M`N~cD`0hu5Eq3&PgY7@zyKdldZYJ?ga1L|K*D7 z;ztdp+~p$adGih#9Vr(%y-910cH^@H2W0J1DnkN)z7O|SOOikRKk`rhPy640R{z+q z_2^IZ5Bn5G?mze6<t@+tx&L^@o51Z~?q90+{;&IY`<MTXU+#-+-QV^vefQ<suq*%H zyp8(xUxQ!O_x6SAy@^Y_ZwKtWWM&X}tL@aJZ3}~z<gWOWA>H~UL##I9SN_ZWOaFiS z*}vq!CF`&23;y4}yXbFvdiDC`m;V>P+kfrO?Zf#m|0`dvpZ))By!-#vpZ4#q_;|Ox z+U)=GrT<xHzst9fil6qsm|ucn_W!e|>dXGiNB+Bg^Yxp^pXVR_uim?8-<OO#Pyc_< z`5meGKl=Kq`u<NRq8|lLij_F8U2Ld(HbEiw@P}QKRtWZ735jFh7w?s5By~|hmOZ*8 zvhkhyR+jct-(F;`>YFSVc1QnG>621{dM9&{P{a4eX+{}6&Yz>~Yv#m1TpC%iHv6Pp z^(!aNXL$>x^(_L97iJy%;kA8F&&2EN+rLiJxLc#Srr$X3ocPTn(Z8(Yrmtq*f9CG3 zUkmqkIlZ4fYtM%Hy36Kdn|amh`s{CFm1$*O%wcwZ>%#Ay|063LAEiDmHowS~owd%b zPGMvF^s0To3>KWZr6{2tyH`7O`^27|qTAcwtKQwH8_C0d)9Grf+8o#QBLAOrY<qwI zPgUpC|7-umPx=4dYVNtschjeFZ}}6y)&Ki@-T&p=zn9I6{(1k@|FZw`q5tl`&DnqB z-?>x&#s1s>_;o&f!hh`#|Id9pXRQ4nT+$SN-Cte3yZW}+|L8yeL*JL&>;1ob?%lh4 zH~oJ<@96*jj{#GzUyS&(e{Jo(5T27JF>T3Szu)${y!qv*By=&ZL)<|BVjh3(DgI0D z0ge9`cbHgNysA;1G;@B^9toaRmv7&hpg8BzPW>SBId05+iDn+t?>%EH^*2nY`zSs~ zwR7u*|JUR9d}9{<7na3+)&0!sgSU=s`W5x`>EbH;-w)5P<C|VBQ1|Jv#+=|~WjTMR zGzITnT77XBBhSBO^%}B2)_eFx+1am(_!nfc?*8e={OZfump99Q^XCsZ?DunvrR)4V z)7A!V=@Hgnc>n!kR?D=0PtO0J4xe0oc=B$gvV&~0d#eAJ{eR=|RpE7lihf27!|DwO z*s^Cb{dhh<RmF?X_U0ms1~2RAum?$!e4R_|62!8)l+(?USCunv67rX+k*gGO*q|7? zR^(n$l8<^MOPyBq#MG1kt1j=m`njphvjVDae-IWiFxz)<>n}I%O)Glt+&*q~?uz%< zMJ8&#>c^z}FBq(IyqI#}bnt$?)l1d;Ou7Ru|I8@(qZggeDI&s_>yYl=9)9-F3SWc1 zTfF)A)^h13%v)v3c(seYpLtcG!9A6e+iDD&Wm_iR-S|~-ZJ=q%35U5QOmhRbM4a4u z@PA!&_UU8UOIuHbJruY5b~5qDspYjcN=x_GoV8DQ9dyG~jz72AMXWc*aI4~nPZd9k zw`uzyNj$~*TEoBpRF`USb6ccNP%JZZRN)feJj+PFpXrCDso3O%h?>2+#k=mxj@gym ztftD{$(gZR!?|KPo_D9;3}~IT_tcLz|By#2qJ=$CsxgwjA-8sRY0l)IR@HC1N%?#6 zlXl+XQ&ke#ZjC!LEaoP(aK}Y<nLEjGUd{AYF8-_KE!m&!xywjQf10$;iY}giIc=Nf zxF>UMh<?0rb?1?W<cIg$0w3?WesAZ4yp=nbxfoQO+SAJA`p?B@7pu=7Evdp&Ek@Z} zRD)$zU-dISO<~$_|CEz|+RngG@fqhAG4C~q<@mGES)Dzee?l8i(Lr-*Q{|nnmaJx9 zb#Iz<lWU|C^A!=ZTMPJR?|dgKDaO+Iw?W$^q&xCTs!B%W<Kl9Y&(1!~iki1HA2Qig zcg-+5;BK(&(RQ|yN%x(knqEcs`z>P6IC3$vZgZi1%~h|vFL{?ev`sl#q#d?3;lh+f zJ_RRk21xSOAKT3SyZ@*5ghd5PH(I=;k4y6H)_S5YvRL-)$+L1_+`ovNJL6Dx<e}5T zI!0*)+jUycR%k3<WoXa%Wa;mK#x1KSaQD4B*eW?!SmTS~t7Ws78TxHjy{6o&qoZZ) zD_RyR`Xub>+Ux>{Qwt}aa9F<O^n~eFpWPCL-B%<fikw%?d!RO}Me@^)1+y5EXK(*0 zaoD_c`MDMBRhjp$eRBNq^xvo5F%G2>G4-eBa3-p>%{{#M^5)6>`se%nI}%Ut{qZs7 z|J8#JzCMhusIB_;@an;fGj*2KNvd9BJ(~KCIih_>fA`*Pb570(I+^DGkj2k#!Su`i z9XD6YWPDyH^)zt3c3+=d>(rgASM0QKTcr|JwycBkn85ju_wIi&{czHGLi*XCn+ut^ z=2-VI9H=j`%}d$XTdcnK(9)whiz;?(Y4%<>LF(S^zw&JB`TWjzO}VY3qdB+i{RG33 zr?>Xpw67}t^Drks*C8aQx8l=n&m_yp9a3ScU;f0URCF7jH}3hihV292XDO+ZKAUf> z?$i3Tm`5vT%B05d-XzO4Tc3RhYyPAn-m+(h^3~(3m5eG6)~D5q-0$Mpm$l6O;#NQQ zh#4C<>KRpS?Ap5K(A?=^erk&sPm!0{yrWV0)%EilLHZ{-L=?sU7QgyX{jN?f;K?mJ zo*?!ww(l8UFJn6QJ@wbMXRdD5>!ThiPl{c2<mbN0zgn4fp4*%bPvkqkFeLA>o7s~7 z3({%ME938&YH4ikV5sEZnP8@NW_C#avm~k2`@Y;#Q$DqLk^21EkG{EnW-9x#Cy|fk zUtgQVotx6v^=I+VSQRsw<+}6_*OTJipFQ`MO}=-n>XF*cwZ(FR6EZf;O({{F!qFal zWY*W&QvZ8JUm0j+ypImtzjy8I&ySVZp8WA`+qBGNA(zsb7k6fSXjybEbNVKw-q`kT zo}Q~Z7ZOT0$i_yV7Q6bqjpJYL+MRhkqP4SP?Y2lSZkK#|O*X^O;PB2}yjNe|{20&Q zCt|zifR^Z$I1vxIYntZe>P;)1eqO!tlrdOvYH^@@sl$QpNH=NgXD4;cV=9X8Y40g` z)XDF@!)%4=b)I+8Q+O-cvJ97UKl{$48sqiJqm;w$;Q^7TjCH{^no>FE9I_l9RZ3OI zg*h2d_PNCUp4+A;$8Px!V?PN&ty4CKyr%jbE8k@CGjC4nqAqtgL9gRF$`$9kbvLTD z7%j8NZu3!K^i50r$$P@yvZB7GXqm&>PaX#j{^l!;(OtY(Z_3+uZ&n?&=R3Ikj#_Qu z<n&#yW+kusZuD+tXo>jR_J1p4c+y#w8|EKfBKCD7|EAX6L7_#rdUspYB|FaM3v5X` z&6?19j61(ev?R_x%+#s1D$qSb|61eUX9t!vn*?<^t>D<gZg#jkHFRc>hpNy7&D1&Z ze_Z#LtcXgT638)QRp^cKW#3O2pW$~r=@2--!RGYO>Wtd~m!<!2_bK#@+-0#UD^bvE zrb|K11}^?L+jlJ6IVHH~#@Qu)Zil^A)hZZAzVX<oz)}9`ywk+nJUb<%VuUV!y2fnt zdxO2>lQqv6S1rmi=hDBEclSkef%kmXBIA(LPmf)=b>d_D!mUbOC-+HcJ*%Fr-INk~ zSTuFt67C}hCWTFzb#7nn&joW+w$*L<61Z(;)Y^>PyEl5i?`#Y;S!n1|9h)4gy5Z`- zT3>sKii#=xIr?(<uFBM!Mh9>BC)8l0zN285`_ZMFA}?!3nqAYj=4w8$_ji)p#v6e% z`&YeM+7R2lR&k;Hj*l5_cNTA)T)HaO;YP<{$@JraM>?N>I;9pBU6Z>?eATgiY>VwP zW?l<qw%y8gj^#n)wTI`Frd+i1&Wk?ma^bjy&u^*RNk@bN+icHGU@}@z&dYLeWf=Rb z$OSnLr4Qo|2TU*Y{I_I9Lsb1rmyNTdg4<hGE~quKn^x*?`Xm3?G=c4`tfv2?Sw$nO z4=jAkG?8VUf?47Mr_CX(%$CyXX5TlQX?ztXqAR#`RnLPfVj=NIwuef1Yg=4&zpPlg zX;0!($B-Le>a)LB@t;-x8Tu<=)y}AWf7kDspvszX)o4~&CFjG`xs|;I|MT9yUU}yC z)L$?DNBq}+{Xg@s{o4P_zy7b8@IPJe>B;G`z1DyJ@7`^FzWd+&+q=)N{r^1K#+CW& zVHw%fdC5LKf4!c1<|np2^qTJTHfdty{`X#2{|ediZ7R8Y>Xpvg`~%C{4vFqubY@lX zp_m8P{wFO9IDbiFRnO-su9tjSEzK-u+ADq#cU#=M>~Lg(wld2C%e6ValPhI@Cq24w zwDM)7OQe+XzbSQ#xOlHItaT8N%=#ShXRT{gVY0%x<EpQk7hNb3=DuJi{KNCsid&t> z?pNr~YJ1FTIY05T<Hfr=2cIm_tIU(wr)489_iM?60}oqYxC<ui3YcQDqHLc_Uf=yw z3pcHr@3q}pH)rYH%rrH_tra2PYy#BQF|faFJ&?NS{JbJ&*OM>2Qa6}Y2cL8)E#6w+ zcIn$vTMyrxUFG}Y6(c-nT+mC_aNs@E(Rex~@U$6YWOc8-4)X-9;&$hpw^KIf8&yo& z74hSQ#c$#J()Gc9fjYPM+?L$Bfu}e#+0aq%v$D}Ci=5<(^A@jqG{@Jd__6?VYsB7u zqYTeg;layOuRdNNSIigrXZoj&v!2ID+`C{V(QGEuzr$&@f$B7CZze7Mh?P6-z3v5^ z{vZAC{*M2*f9Bo(^ncp_`V6zf>ks}fx4L`j@BMe5@5Y|~Z-4A_i{z8QOLyOW5?!2a z{%y|F5UFL>KX?1e9$p!Gw^z&f&ra=-S5DLZbe?egKVkYlBTq%G-Dg&%O*`x=)4S)* zt}dU)b`t_lW+nKUoO7`)U2geu;jRBBs}(Dmp9E{oQS^S=@c!^_)6{1Z?@hZYcUZV` zt#jegyK|*~oX;&wbT88TU^IEzl(%i4Yc`oyO18z_nc>R0XP!P=$?8=x>r}nia?2{R zJF9&zX9d1wlmB-yddEg<@ta1O;(y}SHCQhBGyT(tPxpDh>HJSTD-)s^x9L&E>c6?C zl6PFTP4_+T#WAr+^w6H>|0kCm=*e7vYf9<4SySHbi<eyJbL;Jc)jH*I;mh`Lf4XV( z>#OqfcF~UG?Z<<p8V-HZSX8VSH%m(F@rIM{`}Sp<hHPKW^+{z<<5kb(@+nOw^O_yr zvb7h!Z_#lnlbdjUp&X~x{R#Iq*NNSVzv;|uXTB`ZByUwTo5t<UEc~<ik8WsTGIu{) zdTRE;rrfJ$GsBj9zI9vQx<_}>!MLq4`(I?APTnb2dGCafWwqEj_WVTAmFWvt{_2$} z_`fag&!#k{NYBWtHVa=^r0eY4#(A%J!|LNH(;mc~p8j{4ROyu0B6qqPuXlvM<oPc5 zH~+51!5bagH!f)`td+0&q5CmtW6T7JUa6^~kxv#Y{BV1ftM$J4PWz5kc@+uH$rIeI zpHxOW-F@9>>AB(3(}ImmO)2*rrC(cS&sqG=N#FV6KNYvVYxx^OCN0wB^(aj5z8iEa zhWWkVmu(44UEXc|v$gr$`q!Ek{5uVVY@-%)vhdaYZ}~r!sfzE;%Vy3eT06hI@LQlA zbouv!fG2nTLem3^CRRMQ@e|DrX8-CUyPG+3T0m$2^k*q=ZB&<XZsoeHard<A_1iqo z=2|Oay#Hh+&3+v8MQrZb6T-ab4tmA7e+*r;{@xtdNzMs-)jrtjwyccfJK-az#BbVn zk*`T<ThoT4zJKf{{Mx(a3Dd<{)B27IZ|pZ@cPq)9dSx5mdAWb)y*C6I444k|JYRPI zj%&a6x2N@bx_a7t#Yc+PIu|xB`8Sc{H}lC|w~Frhe!gw>=L^Fvee<=u&T};`Tvh#Y z=I7kbLq4B-l0`#f&u7<#YBisejqg2^z?_}WzGnJ{O^3COteCB*3G!wHh*+%GzkS-& zWp_wq)}cU&uj>0&?VlGqW7Zl+@B1ruPQEuQIOa+IquqyFX3V;iS)7p5X#GiQYyCgr z_uq0f{ff@a^~tIf*b&Hk|J2*PfxnBiXDqmX>aWyGu~qLLgyc?GWf>@%zoIv~@zI;J zH!`B*cUUmq6q(p8qaresxjp<r+rzb!Hapwzo&8|>W|OX_mUSum!TAc&OWahpFV9Jw zDRoft?4FBP8%_qT)3}gVqWx&X({nct&zaGC%V_SF_!W-N3O!7_C$mRXe&U^{c{28h zW@vo*o#L8_2JbF^h~>|+EI;2HHaGvF@#Y4(2alg}L@H;o%$WbI`R3~1Gqk_F&bjko z-q-xbdU@A}7MWsG#Z5dGwS+NezqP;iFZu8OH=pwp{!e^oB~#S#zRC6oM@j$R^yfGI z4FBCf_gQxK|KEZ0xa##&KN<?{-qMp%7F2)p>NH8dP211uglu`dVCIW?KPrUIEYpiS zA@RX>&l$cgNz4NG@2EIFewM~|<@GeZ4VsBAzyIIyVAZV)lNW!-b$iM0x8|SNV>6fk z{=ap$bmp5~d)KDsI%Vc%Hq4nRz0B^H@<-ESHS2HY-!v<*ztk^#^QblR*QaW|cD~Bx z`y$j9PCq3PraRH}>J&zirJ@`&Rv$gUJ2{)jNaT6b*NnzDGnzPFoc7zaMPeO`@p-Qd zb<3E0veQ1h>+1YzIi+#oV0^J6>z9r5V=P@+mHlOB-kBYJ@`N|{0w3-CmT6M1jtmY> zad}oAi_8nBJ9Z^Xs=S|ZftTU$Ho+aAAKrM|`I)<!^WTk0-%n+pnR>3+UoPr{vK6D6 z9!Fn^h*7IxZOM|D-xno$JQ5e~S$gK?{Njz=$)ZJzF07n>X_CIUQ!hir%YNVDEmLQ$ ze)HMAy{T}wqrrg-{LHqK+2XRSTJrZv|JYw)bZPGV7xNO<8iuue-2Kpcp7LTdon*<c z&8K?q2rNChEV(Rpj_R@13Z6FRH_8q=PPi^V`{>vICm6Mhekbb)-gP=D#(DY>(?k2S zNxjwAIu!(7S<aZrk$YT0vyV5#;r=H7SKd1X?TlBoX`RcLK3%}KKcd7$OYC;X<j=nZ zGj&7$2nu|YGFh7zdb2jmR=YQ2!X1;u<i(zcJzS@nRH&R?wj$?ct=Z45XFFr6v&Gj& z%4KsZt=MG8RQhx8y~(R@9GZRdW<%BMa*p?lewJ+A>twRDUo-Z|GPW1TbMNsiztFe% zUq-^8W0EIrJ@=PAT{~&ktO+wO`}le$mb&-*DEY5c59>S^m+UiZS>_z}jdS&OnSM%f zN_YP4)0Omj@|UewzCYgbBSs<br}*=|AD?%#Pt*CL{@3ik<SP!ToiB1$$((!H`QSp& z;?<2^tHh5omoD}GRZwp5?{3P>o`kuTu2D&R6<M2t_P6c-!EL3cE9j=E^}=aq$zB`B z(~?~pVNU11N585`365O;C;Hn-`)g%4gQmo;YGa$6T2UUe#i!m*B2gl+eAfAb<y-V5 zpG`W@^z-?aZ8p#I=PA!lP5)w3dj9*I-&0w&&s;rWdi{)WO1x^?;>A;0=55btkeCpB z;_AJ_eUp8|Hu0VLH~X^VjDRytn(t!`zc`!6btVe%PSrfUBYW-1u8xk&49_%n@y*p? zTJmJK*0oHbXMeAr)JmVCz58wZRlna$Coh`w{kqJOug5Jn7wqp!D7AZ><SNV=#kcQ^ zrjPfoTeA+hrM}bsw}k1R)}O9bT%6CETrLTB@(G7X6|a2sYhyY?#uA1-+@Ccy6el&V z7WOmVED=1Rhu5sauE#@}(O|Oe5~)w0H4dy6+`7tJBZ@)uSoq4o&3!preyzUvpqABT zr9<YHt=&3n9<V6S`7An_ZQ;_u37a20U8i*7HE+pw#gli10u7n&7O*arX)@d6(G;TR zn2}c@^S7JncapyN+3vKLLX}B;Ith<e;?_hMZ-{!hsDm%Cr<5zr;j_odo}SV@XAQeF zwDw$@9KJN;$-jMP&q=)6lo<HL{-@*A>5~hYB>AQq%wSrjUMzN4q}lCVmVaH~s?5dj zR%#p%+&*(H$4pk6qAl~Dr*@b$Nr#-=@auDH-c1``QKcO|yLYACIa)a5@A|!mQm6d< zDx9%$;?!kR-e$}!HefmyTCl#y>Qm$A4}F1G!&~^ntS!=VzArmmSiMq*Jv)5j?fh4h z3>n2XzKT2H{Fr;HeCDn8vczMBf*b#}l^Ms~jA2TDR_i8wRrr<ec8mQB^~)+AH27Wl zpmIjfc4dXs{)M$RmKK6mPTP-m{7y@6b>NB$3X7}}{jGTRorq$SbXI3n^ONYGneWzJ z$W)oRbEA1)*u80sqQ1Uvf1qDmduorvu{WvxN$gtZF7I(QTO!f*HaYOQz}Zb4>PJmC zz7t-0^<wIYt-bdy-l~Z<wo5&I_=-_)pm<r|*(<klkKVp^O7Qfp+$W8@(=yg~$5cf9 zx${_TYEH!Dsb^|-F1F;$4~k!t!CG@*r@6t+=)&OOiE1l*pRLW<zI3CMe}**Q7n8YP zci&zl>nJ+w{@Q<w<E~6_us<8KE&ZkG-1_*WWbZXO+asrjMld_X%nh_CoL(OL{0gs^ z>l6Rq>hA6C_TP?JH!R^irho9q!Z%C(_9zr=w>skU>Kx<aN7+*SjN4b-U7;>Bi*N0& zB@NE{N;)~mE-blV;=JVY{D696)dh{2JD7f$P4C*RskYH$?kvvitf=Q(tM>NIkWyn% z=dXVtxa9nk`vL2cVwN2-FJJ$v^y{iu%_`9s+C9CB_L^4T$ei{iJ^hKkh3dh>($iHY z=}!<A3Dn$K>~koM(fq03Jo7A#Z4+$#XSF7@UHg~)cK@}1+4KKv{LYV4+i{<vX8zyr zm4^ST%gcA)-uUf){l>r7e?MXKHu<ueM^@|iE&aQ@-!W*tefvRt*=Kw8zz(IEB^#IT zQOk7s_~@=hR8X66TF%9{^2L1>od;K~_WpZ8`-8}d#4EFTvp9eJeO+R^c-^n7H(OM0 zXhq8ZJ3FZ(Meg<~AM@t{ERmaLL`>M*C31gLZ`{9Q2bX;N@-n#4_`1{5eL5|Uf1gK% zg>5~S*s5Ex=<3SM7QfkxwZGh0y-D))2aaa9YYV?5tg$ZbxqP>T=WtQz?2O{71nV2- zTNmHlI&*fx7S<bLSN-QY$1Pye{y%f|_1P*~vO(=f=5zINy03}upXM<uNY*$YVeZ_5 z*H<hu-}tJ1JeN94{JKK7siHdH)PUmkG79fAes@nhwXnW)%a>JonQAkief@s_{iOD+ zZ>ITz7rqzm6kVIkZ0WIAJ4X6M=<)15pKeT8zoGtV<CkB?8y-}w*ksN1`6|OKNr{iK zAD<Uk&fHLIs-4O8eknKiE<e4O(dQQRl#5H~iX=TsNI5N*^)yv)dyJfUacx&nkI%DZ zLOWHjYEH>mpy0as)wDx%*Potm5*hL&;qgPstZ$D#uT=VYahF(BIa9+Wo97Fsn24rY ztUU2{nb7XSu5JF2mzdVqh_bTHeJj<K>XEhQ%(F$=61JP89lz}kF*7gFnp+)gkbNp^ zZOvq<-M&4s#+T+kFMA*NL-X;s`|>>|hEe>+t4@S7uKb;NIDmIeuIuUqwY^`xj^(Vk zfB4St)3jYouhz{vnsT>*duGwKo~Q5rbx%lj@hW)y@=&kf^~?4hx36fY+t0hyI_LYB zr4zj^iWMvkHa4kE{mrPQEnX+!`+IBDTjqIw&d)U^wKr`{3KBDMm^Jk%<MAX_>EGYA zCVCqF(yFU@+ny(|=0eE4DjCDG=eM>_|19>RkU4gTR!y+;+Fb9Q-77B@HEjG|_%b<9 zGIVOxw5?Gg3vH+0EO}9KLeVSrozX|N(+3(KF60*c`EcP}HfO_>*pH2iCeMg1e`M{Q z^*;5$&AE-IW^-<SrWpT>J1)t2om8vkxqzNDiHkbks~cCxpRd~Ur26!ypr_y67R{;= z_nXst&BMn340z|F!7Z*`*MmxaY0S%eYjHnUS2$tzHn-1)S&DNbt@ux{ZfNy3b<d4I z!xIvJ_<Cn`?Xz0H`v(Lo)}AsERj_(jVN;;WV)kl<^|A?j;+hx_w0i4R{*7YV{F!aW z4UQ@$<>>wYPWj(Se83qR$FuP0uV2$r&wcnk%XY5ob*H6^rfpY$?JD*!@!rIp(OVaO zYb-ppJ;+ns|0R=p|MY(c1=ZOk58X5DJ0WnmXT$MKN3BbL)eH(YE#_5gw7y!W`s3Q~ zo?GtubE;S7y!@r=mg|4h_Nn(9ohvhLwJ_#5%|CP8fM5Og8U3un7yj&T#HO06mamW8 zk))e-)co(Q%Nu_51$0JPd@0Zoxxg2B(<wbOBC%A-QI50a%8hTG=9>g6&p5`oSUP3a zeLl$ITj<BPblTB8&2zf*CkpHK3&k1S{i$BRr-WJcyXn#INxL0nQrPX+q&yOM>XDuC zCc4A!V$m!Au*h={<JLw#<9ybZowf5nm(<EPDFu_XT;&#@v75kJ_E0|m?8T6W)yWe} zh1Oh<mT>2CE?}%VQFK`4)76{pE(sR-4%r4@9-VmF;43J(_HXv{#9x=6UB8p_sPC#B zkNd%*HHr-GI$MJ(wHqeN=FRI@kGvXx^i$i1wV@N1as6tkWc_V?VP?8_c|f#kVE~`& z$x4yn`1IeWe#)iy+}U+ad&c}lf-a@YRo+!sTE;Z*es|@S<`nyTmm|K)-p*p%cc){| z-_ufJ)4H|q_^CWEYh`s_d*kH#Wi1CkYqN>9i0I7XHq(;cwW7((IG}CCHI}E=CLgX` zULK`X`AejF@7X{_<)((R7hb0_l|IxRyk#Qz>r<nb@WQ!9H!d^0{m(4<cNK^1OuJ(r zI4(H;dKu*Cy`rTf?E&-5s}s2EF8xc~dT{xz?VE&OdN*Cx*yovXvtMIX!={<i;Tanj z+N(F)|8CA;`v13BY5mcEI|Jtgtu{F^O~%Lgew^kUAHR%~VL>a6moCqb>zeTE<gZfK z7aWI;*DZ|f>0BAPyV*$e<+@Cs)NAqvzi;?+HmGja+x=ikijcjLVe8GqX}Zq7tP#B7 z7cDM{&dWddKmPlgp9WDZT}Ncrv1~v6+_EssQs&!{o<5iD9fc=2@A*x?EH*hc;_{mN z&F}ANq&x4-I#Fhlm9cWIsrSFwiqBFKJZ}Ydb9@rCopek<G~h{COP%?xk9WVEnB=3# zyL@HW7jeh#8dvv~m68sLIqDySY7Zv}nrPRolRsXO1Fpc1)z|*JJ8$d%sNeZp|Arqu z;S}<bRZ`*I>Hixx$DaDXVe{tPxBL4)hPdXKD(w_|vpeNiYL$+uL3r0~$r%^lbP6{e z@;k<RGCFAMjzcN4pR9|0;^Ucd^lFbm)!#$;p0kYoeRfS?xj%8fmevdVZAaV}oZ~;m znfFOGVr5j*AD8Q54?kDVofUCAB+YVPQ0O%-9)mO5m$R615-wht*{zj!g4r#qqwmyw zhBvOxbK*UJE3|QD%}YL_WGkV1tRrbshoNrh1<f9jopauHsqjC&UNlu`@1v>(<vN$I zikd8Gi~j%I{`FZe>l0T+&pw)M$5Y(9FhPW|TtaW}r|VOU0t2pfeLC)ty5)prWtixa z$Yk%(*=J{WW@@jH|FJ!Y-I(p!2DeFqAFf_ml&BY+*3zDGCYbHIg~Zf2i^zFfj{K-L z=$x*g@IY|Nu{)jFJJennhrXOMVd@#CV%D7&ZjV3QzI845mGg5Rb7t354`Lg)^?1uJ zkj%fn<3nt3T8LoCiFa@8s?%QXc=})W@Bg>|=IfX3zCG>#_CNon{>8`s`ZeiE{`b%S zZ@+jm^Z9>kYwPN1|Lc3)Gou=}FIc<&rgEbE?;FSMzHRKUeUprrd4B)h&;J?OS97mn I;9z0^0CmcBxc~qF literal 0 HcmV?d00001 diff --git a/dist/unitgrade_devel-0.0.1-py3-none-any.whl b/dist/unitgrade_devel-0.0.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..6e566dfa026e00890940fe2102371f73794be2be GIT binary patch literal 18848 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%0Dr| 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%0Dv<2BZ>`#AK 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 L!oa}b3gQ6(fkJ0l literal 0 HcmV?d00001 diff --git a/autolab/docker_tango_python/Dockerfile b/docker_images/docker_tango_python/Dockerfile similarity index 100% rename from autolab/docker_tango_python/Dockerfile rename to docker_images/docker_tango_python/Dockerfile diff --git a/autolab/docker_tango_python/requirements.txt b/docker_images/docker_tango_python/requirements.txt similarity index 86% rename from autolab/docker_tango_python/requirements.txt rename to docker_images/docker_tango_python/requirements.txt index 9db6120..0a73d68 100644 --- a/autolab/docker_tango_python/requirements.txt +++ b/docker_images/docker_tango_python/requirements.txt @@ -4,3 +4,4 @@ jinja2 tabulate compress_pickle pyfiglet +colorama \ No newline at end of file diff --git a/examples/example_docker/instructor/unitgrade-docker/Dockerfile b/docker_images/unitgrade-docker/Dockerfile similarity index 93% rename from examples/example_docker/instructor/unitgrade-docker/Dockerfile rename to docker_images/unitgrade-docker/Dockerfile index 08764b5..98a4007 100644 --- a/examples/example_docker/instructor/unitgrade-docker/Dockerfile +++ b/docker_images/unitgrade-docker/Dockerfile @@ -5,7 +5,7 @@ FROM python:3.8-slim-buster RUN apt-get -y update RUN apt-get -y install git -WORKDIR /app +WORKDIR /home # Remember to include requirements. COPY requirements.txt requirements.txt @@ -16,6 +16,6 @@ RUN pip3 install -r requirements.txt COPY . . -ADD . /app +ADD . /home # CMD [ "python3", "app.py"] diff --git a/docker_images/unitgrade-docker/home/cs103/Report3_handin_5_of_30.token b/docker_images/unitgrade-docker/home/cs103/Report3_handin_5_of_30.token new file mode 100644 index 0000000000000000000000000000000000000000..a861bfbfbcc029d09e49a0a278598a0e2355ee66 GIT binary patch literal 113733 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 literal 0 HcmV?d00001 diff --git a/examples/example_docker/instructor/unitgrade-docker/tmp/cs103/__pycache__/homework1.cpython-38.pyc b/docker_images/unitgrade-docker/home/cs103/__pycache__/homework1.cpython-38.pyc similarity index 73% rename from examples/example_docker/instructor/unitgrade-docker/tmp/cs103/__pycache__/homework1.cpython-38.pyc rename to docker_images/unitgrade-docker/home/cs103/__pycache__/homework1.cpython-38.pyc index 6403436716a672ff6c51a7026fc0edf847aa3213..d57142a7eeb21f6b172586b47613efbfeadc1082 100644 GIT binary patch delta 32 ocmbQmKAW98l$V!_fq{X+Z?3^c?$?ZL68ahWxv86fGtOrO0C~&^&;S4c delta 31 ncmbQuK8u|@l$V!_fq{X+^q<Z~?$?a0;`)gN1)Kjc&SwMwb(09I diff --git a/docker_images/unitgrade-docker/home/cs103/__pycache__/report3_complete_grade.cpython-38.pyc b/docker_images/unitgrade-docker/home/cs103/__pycache__/report3_complete_grade.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aa3a3187faae8d51a53e55a82bd75e0521299a19 GIT binary patch literal 50864 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 OIXGDuSr}QEKq>*r9~HI$ literal 0 HcmV?d00001 diff --git a/examples/example_docker/instructor/unitgrade-docker/tmp/cs103/homework1.py b/docker_images/unitgrade-docker/home/cs103/homework1.py similarity index 100% rename from examples/example_docker/instructor/unitgrade-docker/tmp/cs103/homework1.py rename to docker_images/unitgrade-docker/home/cs103/homework1.py diff --git a/examples/example_docker/instructor/unitgrade-docker/tmp/cs103/report3.py b/docker_images/unitgrade-docker/home/cs103/report3.py similarity index 74% rename from examples/example_docker/instructor/unitgrade-docker/tmp/cs103/report3.py rename to docker_images/unitgrade-docker/home/cs103/report3.py index c97b5a4..f83bb53 100644 --- a/examples/example_docker/instructor/unitgrade-docker/tmp/cs103/report3.py +++ b/docker_images/unitgrade-docker/home/cs103/report3.py @@ -1,8 +1,8 @@ """ Example student code. This file is automatically generated from the files in the instructor-directory """ -from unitgrade2.unitgrade2 import UTestCase, Report, hide -from unitgrade2.unitgrade_helpers2 import evaluate_report_student +from src.unitgrade2.unitgrade2 import UTestCase, Report +from src.unitgrade2 import evaluate_report_student class Week1(UTestCase): """ The first question for week 1. """ @@ -24,4 +24,6 @@ class Report3(Report): pack_imports = [cs103] if __name__ == "__main__": + # from unitgrade_private2.hidden_gather_upload import gather_upload_to_campusnet + # gather_upload_to_campusnet(Report3()) evaluate_report_student(Report3()) diff --git a/docker_images/unitgrade-docker/home/cs103/report3_complete_grade.py b/docker_images/unitgrade-docker/home/cs103/report3_complete_grade.py new file mode 100644 index 0000000..8ea5f2e --- /dev/null +++ b/docker_images/unitgrade-docker/home/cs103/report3_complete_grade.py @@ -0,0 +1,338 @@ + +import numpy as np +from tabulate import tabulate +from datetime import datetime +import pyfiglet +import unittest +import inspect +import os +import argparse +import time + +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: +To run all tests in a report: + +> python assignment1_dp.py + +To run only question 2 or question 2.1 + +> python assignment1_dp.py -q 2 +> python assignment1_dp.py -q 2.1 + +Note this scripts does not grade your report. To grade your report, use: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('-q', nargs='?', type=str, default=None, help='Only evaluate this question (e.g.: -q 2)') +parser.add_argument('--showexpected', action="store_true", help='Show the expected/desired result') +parser.add_argument('--showcomputed', action="store_true", help='Show the answer your code computes') +parser.add_argument('--unmute', action="store_true", help='Show result of print(...) commands in code') +parser.add_argument('--passall', action="store_true", help='Automatically pass all tests. Useful when debugging.') + +def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False): + args = parser.parse_args() + if question is None and args.q is not None: + question = args.q + if "." in question: + question, qitem = [int(v) for v in question.split(".")] + else: + question = int(question) + + if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file: + raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation") + + if unmute is None: + unmute = args.unmute + if passall is None: + passall = args.passall + + results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute, + show_tol_err=show_tol_err) + + + if question is None: + print("Provisional evaluation") + tabulate(table_data) + table = table_data + print(tabulate(table)) + print(" ") + + fr = inspect.getouterframes(inspect.currentframe())[1].filename + gfile = os.path.basename(fr)[:-3] + "_grade.py" + if os.path.exists(gfile): + print("Note your results have not yet been registered. \nTo register your results, please run the file:") + print(">>>", gfile) + print("In the same manner as you ran this file.") + + + return results + + +def upack(q): + # h = zip([(i['w'], i['possible'], i['obtained']) for i in q.values()]) + h =[(i['w'], i['possible'], i['obtained']) for i in q.values()] + h = np.asarray(h) + return h[:,0], h[:,1], h[:,2], + +class UnitgradeTextRunner(unittest.TextTestRunner): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + +class SequentialTestLoader(unittest.TestLoader): + def getTestCaseNames(self, testCaseClass): + test_names = super().getTestCaseNames(testCaseClass) + # testcase_methods = list(testCaseClass.__dict__.keys()) + ls = [] + for C in testCaseClass.mro(): + if issubclass(C, unittest.TestCase): + ls = list(C.__dict__.keys()) + ls + testcase_methods = ls + test_names.sort(key=testcase_methods.index) + return test_names + +def evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False, + show_progress_bar=True, + show_tol_err=False, + big_header=True): + + now = datetime.now() + if big_header: + ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom") + b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) + else: + b = "Unitgrade" + dt_string = now.strftime("%d/%m/%Y %H:%M:%S") + print(b + " v" + __version__ + ", started: " + dt_string+ "\n") + # print("Started: " + dt_string) + s = report.title + if hasattr(report, "version") and report.version is not None: + s += " version " + report.version + print(s, "(use --help for options)" if show_help_flag else "") + # print(f"Loaded answers from: ", report.computed_answers_file, "\n") + table_data = [] + t_start = time.time() + score = {} + loader = SequentialTestLoader() + + for n, (q, w) in enumerate(report.questions): + if question is not None and n+1 != question: + continue + suite = loader.loadTestsFromTestCase(q) + qtitle = q.question_title() if hasattr(q, 'question_title') else q.__qualname__ + q_title_print = "Question %i: %s"%(n+1, qtitle) + print(q_title_print, end="") + q.possible = 0 + q.obtained = 0 + q_ = {} # Gather score in this class. + UTextResult.q_title_print = q_title_print # Hacky + UTextResult.show_progress_bar = show_progress_bar # Hacky. + UTextResult.number = n + UTextResult.nL = report.nL + + res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) + + possible = res.testsRun + obtained = len(res.successes) + + assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun + + obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 + score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} + q.obtained = obtained + q.possible = possible + + s1 = f" * q{n+1}) Total" + s2 = f" {q.obtained}/{w}" + print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 ) + print(" ") + table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"]) + + ws, possible, obtained = upack(score) + possible = int( msum(possible) ) + obtained = int( msum(obtained) ) # Cast to python int + report.possible = possible + report.obtained = obtained + now = datetime.now() + dt_string = now.strftime("%H:%M:%S") + + dt = int(time.time()-t_start) + minutes = dt//60 + seconds = dt - minutes*60 + plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") + + dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", + last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) + + # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total") + + table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) + results = {'total': (obtained, possible), 'details': score} + return results, table_data + + +from tabulate import tabulate +from datetime import datetime +import inspect +import json +import os +import bz2 +import pickle +import os + +def bzwrite(json_str, token): # to get around obfuscation issues + with getattr(bz2, 'open')(token, "wt") as f: + f.write(json_str) + +def gather_imports(imp): + resources = {} + m = imp + # for m in pack_imports: + # print(f"*** {m.__name__}") + f = m.__file__ + # dn = os.path.dirname(f) + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = str(__import__(m.__name__.split('.')[0]).__path__) + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: + top_package = os.path.dirname(m.__file__) + module_import = True + else: + top_package = __import__(m.__name__.split('.')[0]).__path__._path[0] + module_import = False + + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = os.path.dirname(top_package) + import zipfile + # import strea + # zipfile.ZipFile + import io + # file_like_object = io.BytesIO(my_zip_data) + zip_buffer = io.BytesIO() + with zipfile.ZipFile(zip_buffer, 'w') as zip: + # zip.write() + for root, dirs, files in os.walk(top_package): + for file in files: + if file.endswith(".py"): + fpath = os.path.join(root, file) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) + zip.write(fpath, v) + + resources['zipfile'] = zip_buffer.getvalue() + resources['top_package'] = top_package + resources['module_import'] = module_import + return resources, top_package + + if f.endswith("__init__.py"): + for root, dirs, files in os.walk(os.path.dirname(f)): + for file in files: + if file.endswith(".py"): + # print(file) + # print() + v = os.path.relpath(os.path.join(root, file), top_package) + with open(os.path.join(root, file), 'r') as ff: + resources[v] = ff.read() + else: + v = os.path.relpath(f, top_package) + with open(f, 'r') as ff: + resources[v] = ff.read() + return resources + +import argparse +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Use this script to get the score of your report. Example: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('--noprogress', action="store_true", help='Disable progress bars') +parser.add_argument('--autolab', action="store_true", help='Show Autolab results') + +def gather_upload_to_campusnet(report, output_dir=None): + n = report.nL + args = parser.parse_args() + results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, + show_progress_bar=not args.noprogress, + big_header=not args.autolab) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) + # also load the source code of missing files... + + sources = {} + print("") + if not args.autolab: + if len(report.individual_imports) > 0: + print("By uploading the .token file, you verify the files:") + for m in report.individual_imports: + print(">", m.__file__) + print("Are created/modified individually by you in agreement with DTUs exam rules") + report.pack_imports += report.individual_imports + + if len(report.pack_imports) > 0: + print("Including files in upload...") + for k, m in enumerate(report.pack_imports): + nimp, top_package = gather_imports(m) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import + nimp['name'] = m.__name__ + sources[k] = nimp + # if len([k for k in nimp if k not in sources]) > 0: + print(f" * {m.__name__}") + # sources = {**sources, **nimp} + results['sources'] = sources + + if output_dir is None: + output_dir = os.getcwd() + + payload_out_base = report.__class__.__name__ + "_handin" + + obtain, possible = results['total'] + vstring = "_v"+report.version if report.version is not None else "" + + token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) + token = os.path.normpath(os.path.join(output_dir, token)) + + + with open(token, 'wb') as f: + pickle.dump(results, f) + + if not args.autolab: + print(" ") + print("To get credit for your results, please upload the single unmodified file: ") + print(">", token) + # print("To campusnet without any modifications.") + + # print("Now time for some autolab fun") + +def source_instantiate(name, report1_source, payload): + eval("exec")(report1_source, globals()) + pl = pickle.loads(bytes.fromhex(payload)) + report = eval(name)(payload=pl, strict=True) + # report.set_payload(pl) + return report + + + +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n"""\nimport numpy as np\nimport sys\nimport re\nimport threading\nimport tqdm\nimport pickle\nimport os\nfrom io import StringIO\nimport io\nfrom unittest.runner import _WritelnDecorator\nfrom typing import Any\nimport inspect\nimport textwrap\nimport colorama\nfrom colorama import Fore\nfrom functools import _make_key, RLock\nfrom collections import namedtuple\nimport unittest\nimport time\n\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ncolorama.init(autoreset=True) # auto resets your settings after every output\n\ndef gprint(s):\n print(f"{Fore.GREEN}{s}")\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\n\ndef setup_dir_by_class(C, base_dir):\n name = C.__class__.__name__\n return base_dir, name\n\n\nclass Logger(object):\n def __init__(self, buffer):\n assert False\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\n# @classmethod\n# class OrderedClassMembers(type):\n# def __prepare__(self, name, bases):\n# assert False\n# return collections.OrderedDict()\n#\n# def __new__(self, name, bases, classdict):\n# ks = list(classdict.keys())\n# for b in bases:\n# ks += b.__ordered__\n# classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n# return type.__new__(self, name, bases, classdict)\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 120 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q, _) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q, _) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n loader = unittest.TestLoader()\n for q, _ in self.questions:\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self, with_coverage=False):\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = True\n q._report = self\n\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n # print(self.questions)\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n print("q is", q())\n q()._cache_put(\'time\', q.time) # = q.time\n report_cache[q.__qualname__] = q._cache2\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\': True}\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = False\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct + 1)\n if i > 0 and l.find("|", i + 1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = r\'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar", show_progress_bar=True, file=None):\n if file == None:\n file = sys.stdout\n self.file = file\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.01\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n self.pbar = None\n\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar = None\n\n self.file.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=self.file, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\')\n\n for _ in range(self.n - 1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\ndef dprint(first, last, nL, extra = "", file=None, dotsym=\'.\', color=\'white\'):\n if file == None:\n file = sys.stdout\n\n # ss = self.item_title_print\n # state = "PASS" if success else "FAILED"\n dot_parts = (dotsym * max(0, nL - len(last) - len(first)))\n # if self.show_progress_bar or True:\n print(first + dot_parts, end="", file=file)\n # else:\n # print(dot_parts, end="", file=self.cc.file)\n last += extra\n # if tsecs >= 0.5:\n # state += " (" + str(tsecs) + " seconds)"\n print(last, file=file)\n\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n cc = None\n\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n self.successes.append(test)\n self.cc_terminate()\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n self.cc.file.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="", file=self.cc.file)\n else:\n print(dot_parts, end="", file=self.cc.file)\n\n if tsecs >= 0.5:\n state += " (" + str(tsecs) + " seconds)"\n print(state, file=self.cc.file)\n\n def startTest(self, test):\n # j =self.testsRun\n self.testsRun += 1\n # item_title = self.getDescription(test)\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n self.item_title_print = " * q%i.%i) %s" % (UTextResult.number + 1, self.testsRun, item_title)\n estimated_time = 10\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar, file=sys.stdout)\n else:\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n self._stdout = sys.stdout\n sys.stdout = io.StringIO()\n\n def stopTest(self, test):\n sys.stdout = self._stdout\n super().stopTest(test)\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass is None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n print(" " * max(0, self.nL - len(self.cc.title)) + (" (" + str(q_time) + " seconds)" if q_time >= 0.5 else ""))\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n stream = io.StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)))\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n\n return wrapper\n\n\ndef get_hints(ss):\n if ss == None:\n return None\n try:\n ss = textwrap.dedent(ss)\n ss = ss.replace(\'\'\'"""\'\'\', "").strip()\n hints = ["hints:", ]\n j = np.argmax([ss.lower().find(h) for h in hints])\n h = hints[j]\n ss = ss[ss.find(h) + len(h) + 1:]\n ss = "\\n".join([l for l in ss.split("\\n") if not l.strip().startswith(":")])\n ss = textwrap.dedent(ss)\n ss = ss.strip()\n return ss\n except Exception as e:\n print("bad hints", ss, e)\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n _with_coverage = False\n _report = None # The report used. This is very, very hacky and should always be None. Don\'t rely on it!\n\n def capture(self):\n if hasattr(self, \'_stdout\') and self._stdout is not None:\n file = self._stdout\n else:\n # self._stdout = sys.stdout\n # sys._stdout = io.StringIO()\n file = sys.stdout\n return Capturing2(stdout=file)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ is not None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n if self._with_coverage:\n if not hasattr(self._report, \'covcache\'):\n self._report.covcache = {}\n import coverage\n self.cov = coverage.Coverage()\n self.cov.start()\n self.setUp()\n\n def _callTearDown(self):\n self.tearDown()\n if self._with_coverage:\n from pathlib import Path\n from snipper import snipper\n self.cov.stop()\n data = self.cov.get_data()\n base, _, _ = self._report._import_base_relative()\n for file in data.measured_files():\n file = os.path.normpath(file)\n root = Path(base)\n child = Path(file)\n if root in child.parents:\n with open(child, \'r\') as f:\n s = f.read()\n lines = s.splitlines()\n garb = \'GARBAGE\'\n\n lines2 = snipper.censor_code(lines, keep=True)\n assert len(lines) == len(lines2)\n\n for l in data.contexts_by_lineno(file):\n if lines2[l].strip() == garb:\n if self.cache_id() not in self._report.covcache:\n self._report.covcache[self.cache_id()] = {}\n\n rel = os.path.relpath(child, root)\n cc = self._report.covcache[self.cache_id()]\n j = 0\n for j in range(l, -1, -1):\n if "def" in lines2[j] or "class" in lines2[j]:\n break\n from snipper.snipper import gcoms\n fun = lines2[j]\n comments, _ = gcoms("\\n".join(lines2[j:l]))\n if rel not in cc:\n cc[rel] = {}\n cc[rel][fun] = (l, "\\n".join(comments))\n self._cache_put((self.cache_id(), \'coverage\'), self._report.covcache)\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd is None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n sd = self.shortDescriptionStandard()\n title = self._cache_get((self.cache_id(), \'title\'), sd)\n return title if title is not None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome is None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc is not None:\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard())\n\n self._cache2[(self.cache_id(), \'assert\')] = {}\n res = testMethod()\n elapsed = time.time() - t\n self._get_outcome()[self.cache_id()] = res\n self._cache_put((self.cache_id(), "time"), elapsed)\n\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return c, m\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n # sys.stdout = self._stdout\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n self.__class__._cache[\n key] = {} # A new dict. We manually insert it because we have to use that the dict is mutable.\n cache = self._cache_get(key)\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, f"Key {id} not found in cache; framework files missing. Please run deploy()")\n\n # The order of these calls is important. If the method assert fails, we should still store the correct result in cache.\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n assert_fun(first, _expected, *args, **kwargs)\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache is not None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n if os.path.exists(cfile):\n try:\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n except Exception as e:\n print("Bad cache", cfile)\n print(e)\n else:\n print("Warning! data file not found", cfile)\n\n def _feedErrorsToResult(self, result, errors):\n """ Use this to show hints on test failure. """\n if not isinstance(result, UTextResult):\n er = [e for e, v in errors if v != None]\n\n if len(er) > 0:\n hints = []\n key = (self.cache_id(), \'coverage\')\n if self._cache_contains(key):\n CC = self._cache_get(key)\n for id in CC:\n if id == self.cache_id():\n cl, m = id\n gprint(f"> An error occured while solving: {cl}.{m}. The files/methods you need to edit are:") # For the test {id} in {file} you should edit:")\n for file in CC[id]:\n rec = CC[id][file]\n gprint(f"> * {file}")\n for l in rec:\n _, comments = CC[id][file][l]\n hint = get_hints(comments)\n\n if hint != None:\n hints.append(hint)\n gprint(f"> - {l}")\n\n er = er[0]\n doc = er._testMethodDoc\n if doc is not None:\n hint = get_hints(er._testMethodDoc)\n if hint is not None:\n hints = [hint] + hints\n if len(hints) > 0:\n gprint("> Hints:")\n gprint(textwrap.indent("\\n".join(hints), "> "))\n\n super()._feedErrorsToResult(result, errors)\n\n def startTestRun(self):\n # print("asdfsdaf 11", file=sys.stderr)\n super().startTestRun()\n # print("asdfsdaf")\n\n def _callTestMethod(self, method):\n # print("asdfsdaf")\n super()._callTestMethod(method)\n\n\ndef hide(func):\n return func\n\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n# 817\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\nimport inspect\nimport os\nimport argparse\nimport time\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print(b + " v" + __version__ + ", started: " + dt_string+ "\\n")\n # print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print(s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n UTextResult.nL = report.nL\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f" * q{n+1}) Total"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")",\n last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL)\n\n # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n # print(" ")\n # print("="*n)\n # print("Final evaluation")\n # print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n print("")\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f" * {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.normpath(os.path.join(output_dir, token))\n\n\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single unmodified file: ")\n print(">", token)\n # print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n def test_add(self):\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n self.assertEqualC(add(-100, 5))\n\n @hide\n def test_add_hidden(self):\n # This is a hidden test. The @hide-decorator will allow unitgrade to remove the test.\n # See the output in the student directory for more information.\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n\nclass AutomaticPass(UTestCase):\n def test_student_passed(self):\n self.assertEqual(2,2)\n\n @hide\n def test_hidden_fail(self):\n self.assertEqual(2,3)\n\nimport cs103\nclass Report3(Report):\n title = "CS 101 Report 3"\n questions = [(Week1, 20), (AutomaticPass, 10)] # Include a single question for 10 credits.\n pack_imports = [cs103]' +report1_payload = '80049589000000000000007d94288c055765656b31947d942868018c08746573745f6164649486948c066173736572749486947d94284b014aa1ffffff4b004b047568018c0f746573745f6164645f68696464656e948694680586947d944b004b04738c0474696d6594473fe3b8a400000000758c0d4175746f6d6174696350617373947d94680c473fc45a520000000073752e' +name="Report3" + +report = source_instantiate(name, report1_source, report1_payload) +output_dir = os.path.dirname(__file__) +gather_upload_to_campusnet(report, output_dir) \ No newline at end of file diff --git a/docker_images/unitgrade-docker/home/cs103/report3_grade.py b/docker_images/unitgrade-docker/home/cs103/report3_grade.py new file mode 100644 index 0000000..3c64c04 --- /dev/null +++ b/docker_images/unitgrade-docker/home/cs103/report3_grade.py @@ -0,0 +1,340 @@ +""" +Example student code. This file is automatically generated from the files in the instructor-directory +""" +import numpy as np +from tabulate import tabulate +from datetime import datetime +import pyfiglet +import unittest +import inspect +import os +import argparse +import time + +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: +To run all tests in a report: + +> python assignment1_dp.py + +To run only question 2 or question 2.1 + +> python assignment1_dp.py -q 2 +> python assignment1_dp.py -q 2.1 + +Note this scripts does not grade your report. To grade your report, use: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('-q', nargs='?', type=str, default=None, help='Only evaluate this question (e.g.: -q 2)') +parser.add_argument('--showexpected', action="store_true", help='Show the expected/desired result') +parser.add_argument('--showcomputed', action="store_true", help='Show the answer your code computes') +parser.add_argument('--unmute', action="store_true", help='Show result of print(...) commands in code') +parser.add_argument('--passall', action="store_true", help='Automatically pass all tests. Useful when debugging.') + +def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False): + args = parser.parse_args() + if question is None and args.q is not None: + question = args.q + if "." in question: + question, qitem = [int(v) for v in question.split(".")] + else: + question = int(question) + + if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file: + raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation") + + if unmute is None: + unmute = args.unmute + if passall is None: + passall = args.passall + + results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute, + show_tol_err=show_tol_err) + + + if question is None: + print("Provisional evaluation") + tabulate(table_data) + table = table_data + print(tabulate(table)) + print(" ") + + fr = inspect.getouterframes(inspect.currentframe())[1].filename + gfile = os.path.basename(fr)[:-3] + "_grade.py" + if os.path.exists(gfile): + print("Note your results have not yet been registered. \nTo register your results, please run the file:") + print(">>>", gfile) + print("In the same manner as you ran this file.") + + + return results + + +def upack(q): + # h = zip([(i['w'], i['possible'], i['obtained']) for i in q.values()]) + h =[(i['w'], i['possible'], i['obtained']) for i in q.values()] + h = np.asarray(h) + return h[:,0], h[:,1], h[:,2], + +class UnitgradeTextRunner(unittest.TextTestRunner): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + +class SequentialTestLoader(unittest.TestLoader): + def getTestCaseNames(self, testCaseClass): + test_names = super().getTestCaseNames(testCaseClass) + # testcase_methods = list(testCaseClass.__dict__.keys()) + ls = [] + for C in testCaseClass.mro(): + if issubclass(C, unittest.TestCase): + ls = list(C.__dict__.keys()) + ls + testcase_methods = ls + test_names.sort(key=testcase_methods.index) + return test_names + +def evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False, + show_progress_bar=True, + show_tol_err=False, + big_header=True): + + now = datetime.now() + if big_header: + ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom") + b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) + else: + b = "Unitgrade" + dt_string = now.strftime("%d/%m/%Y %H:%M:%S") + print(b + " v" + __version__ + ", started: " + dt_string+ "\n") + # print("Started: " + dt_string) + s = report.title + if hasattr(report, "version") and report.version is not None: + s += " version " + report.version + print(s, "(use --help for options)" if show_help_flag else "") + # print(f"Loaded answers from: ", report.computed_answers_file, "\n") + table_data = [] + t_start = time.time() + score = {} + loader = SequentialTestLoader() + + for n, (q, w) in enumerate(report.questions): + if question is not None and n+1 != question: + continue + suite = loader.loadTestsFromTestCase(q) + qtitle = q.question_title() if hasattr(q, 'question_title') else q.__qualname__ + q_title_print = "Question %i: %s"%(n+1, qtitle) + print(q_title_print, end="") + q.possible = 0 + q.obtained = 0 + q_ = {} # Gather score in this class. + UTextResult.q_title_print = q_title_print # Hacky + UTextResult.show_progress_bar = show_progress_bar # Hacky. + UTextResult.number = n + UTextResult.nL = report.nL + + res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) + + possible = res.testsRun + obtained = len(res.successes) + + assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun + + obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 + score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} + q.obtained = obtained + q.possible = possible + + s1 = f" * q{n+1}) Total" + s2 = f" {q.obtained}/{w}" + print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 ) + print(" ") + table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"]) + + ws, possible, obtained = upack(score) + possible = int( msum(possible) ) + obtained = int( msum(obtained) ) # Cast to python int + report.possible = possible + report.obtained = obtained + now = datetime.now() + dt_string = now.strftime("%H:%M:%S") + + dt = int(time.time()-t_start) + minutes = dt//60 + seconds = dt - minutes*60 + plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") + + dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", + last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) + + # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total") + + table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) + results = {'total': (obtained, possible), 'details': score} + return results, table_data + + +from tabulate import tabulate +from datetime import datetime +import inspect +import json +import os +import bz2 +import pickle +import os + +def bzwrite(json_str, token): # to get around obfuscation issues + with getattr(bz2, 'open')(token, "wt") as f: + f.write(json_str) + +def gather_imports(imp): + resources = {} + m = imp + # for m in pack_imports: + # print(f"*** {m.__name__}") + f = m.__file__ + # dn = os.path.dirname(f) + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = str(__import__(m.__name__.split('.')[0]).__path__) + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: + top_package = os.path.dirname(m.__file__) + module_import = True + else: + top_package = __import__(m.__name__.split('.')[0]).__path__._path[0] + module_import = False + + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = os.path.dirname(top_package) + import zipfile + # import strea + # zipfile.ZipFile + import io + # file_like_object = io.BytesIO(my_zip_data) + zip_buffer = io.BytesIO() + with zipfile.ZipFile(zip_buffer, 'w') as zip: + # zip.write() + for root, dirs, files in os.walk(top_package): + for file in files: + if file.endswith(".py"): + fpath = os.path.join(root, file) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) + zip.write(fpath, v) + + resources['zipfile'] = zip_buffer.getvalue() + resources['top_package'] = top_package + resources['module_import'] = module_import + return resources, top_package + + if f.endswith("__init__.py"): + for root, dirs, files in os.walk(os.path.dirname(f)): + for file in files: + if file.endswith(".py"): + # print(file) + # print() + v = os.path.relpath(os.path.join(root, file), top_package) + with open(os.path.join(root, file), 'r') as ff: + resources[v] = ff.read() + else: + v = os.path.relpath(f, top_package) + with open(f, 'r') as ff: + resources[v] = ff.read() + return resources + +import argparse +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Use this script to get the score of your report. Example: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('--noprogress', action="store_true", help='Disable progress bars') +parser.add_argument('--autolab', action="store_true", help='Show Autolab results') + +def gather_upload_to_campusnet(report, output_dir=None): + n = report.nL + args = parser.parse_args() + results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, + show_progress_bar=not args.noprogress, + big_header=not args.autolab) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) + # also load the source code of missing files... + + sources = {} + print("") + if not args.autolab: + if len(report.individual_imports) > 0: + print("By uploading the .token file, you verify the files:") + for m in report.individual_imports: + print(">", m.__file__) + print("Are created/modified individually by you in agreement with DTUs exam rules") + report.pack_imports += report.individual_imports + + if len(report.pack_imports) > 0: + print("Including files in upload...") + for k, m in enumerate(report.pack_imports): + nimp, top_package = gather_imports(m) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import + nimp['name'] = m.__name__ + sources[k] = nimp + # if len([k for k in nimp if k not in sources]) > 0: + print(f" * {m.__name__}") + # sources = {**sources, **nimp} + results['sources'] = sources + + if output_dir is None: + output_dir = os.getcwd() + + payload_out_base = report.__class__.__name__ + "_handin" + + obtain, possible = results['total'] + vstring = "_v"+report.version if report.version is not None else "" + + token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) + token = os.path.normpath(os.path.join(output_dir, token)) + + + with open(token, 'wb') as f: + pickle.dump(results, f) + + if not args.autolab: + print(" ") + print("To get credit for your results, please upload the single unmodified file: ") + print(">", token) + # print("To campusnet without any modifications.") + + # print("Now time for some autolab fun") + +def source_instantiate(name, report1_source, payload): + eval("exec")(report1_source, globals()) + pl = pickle.loads(bytes.fromhex(payload)) + report = eval(name)(payload=pl, strict=True) + # report.set_payload(pl) + return report + + + +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n"""\nimport numpy as np\nimport sys\nimport re\nimport threading\nimport tqdm\nimport pickle\nimport os\nfrom io import StringIO\nimport io\nfrom unittest.runner import _WritelnDecorator\nfrom typing import Any\nimport inspect\nimport textwrap\nimport colorama\nfrom colorama import Fore\nfrom functools import _make_key, RLock\nfrom collections import namedtuple\nimport unittest\nimport time\n\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ncolorama.init(autoreset=True) # auto resets your settings after every output\n\ndef gprint(s):\n print(f"{Fore.GREEN}{s}")\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\n\ndef setup_dir_by_class(C, base_dir):\n name = C.__class__.__name__\n return base_dir, name\n\n\nclass Logger(object):\n def __init__(self, buffer):\n assert False\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\n# @classmethod\n# class OrderedClassMembers(type):\n# def __prepare__(self, name, bases):\n# assert False\n# return collections.OrderedDict()\n#\n# def __new__(self, name, bases, classdict):\n# ks = list(classdict.keys())\n# for b in bases:\n# ks += b.__ordered__\n# classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n# return type.__new__(self, name, bases, classdict)\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 120 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q, _) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q, _) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n loader = unittest.TestLoader()\n for q, _ in self.questions:\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self, with_coverage=False):\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = True\n q._report = self\n\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n # print(self.questions)\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n print("q is", q())\n q()._cache_put(\'time\', q.time) # = q.time\n report_cache[q.__qualname__] = q._cache2\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\': True}\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = False\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct + 1)\n if i > 0 and l.find("|", i + 1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = r\'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar", show_progress_bar=True, file=None):\n if file == None:\n file = sys.stdout\n self.file = file\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.01\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n self.pbar = None\n\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar = None\n\n self.file.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=self.file, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\')\n\n for _ in range(self.n - 1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\ndef dprint(first, last, nL, extra = "", file=None, dotsym=\'.\', color=\'white\'):\n if file == None:\n file = sys.stdout\n\n # ss = self.item_title_print\n # state = "PASS" if success else "FAILED"\n dot_parts = (dotsym * max(0, nL - len(last) - len(first)))\n # if self.show_progress_bar or True:\n print(first + dot_parts, end="", file=file)\n # else:\n # print(dot_parts, end="", file=self.cc.file)\n last += extra\n # if tsecs >= 0.5:\n # state += " (" + str(tsecs) + " seconds)"\n print(last, file=file)\n\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n cc = None\n\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n self.successes.append(test)\n self.cc_terminate()\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n self.cc.file.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="", file=self.cc.file)\n else:\n print(dot_parts, end="", file=self.cc.file)\n\n if tsecs >= 0.5:\n state += " (" + str(tsecs) + " seconds)"\n print(state, file=self.cc.file)\n\n def startTest(self, test):\n # j =self.testsRun\n self.testsRun += 1\n # item_title = self.getDescription(test)\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n self.item_title_print = " * q%i.%i) %s" % (UTextResult.number + 1, self.testsRun, item_title)\n estimated_time = 10\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar, file=sys.stdout)\n else:\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n self._stdout = sys.stdout\n sys.stdout = io.StringIO()\n\n def stopTest(self, test):\n sys.stdout = self._stdout\n super().stopTest(test)\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass is None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n print(" " * max(0, self.nL - len(self.cc.title)) + (" (" + str(q_time) + " seconds)" if q_time >= 0.5 else ""))\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n stream = io.StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)))\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n\n return wrapper\n\n\ndef get_hints(ss):\n if ss == None:\n return None\n try:\n ss = textwrap.dedent(ss)\n ss = ss.replace(\'\'\'"""\'\'\', "").strip()\n hints = ["hints:", ]\n j = np.argmax([ss.lower().find(h) for h in hints])\n h = hints[j]\n ss = ss[ss.find(h) + len(h) + 1:]\n ss = "\\n".join([l for l in ss.split("\\n") if not l.strip().startswith(":")])\n ss = textwrap.dedent(ss)\n ss = ss.strip()\n return ss\n except Exception as e:\n print("bad hints", ss, e)\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n _with_coverage = False\n _report = None # The report used. This is very, very hacky and should always be None. Don\'t rely on it!\n\n def capture(self):\n if hasattr(self, \'_stdout\') and self._stdout is not None:\n file = self._stdout\n else:\n # self._stdout = sys.stdout\n # sys._stdout = io.StringIO()\n file = sys.stdout\n return Capturing2(stdout=file)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ is not None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n if self._with_coverage:\n if not hasattr(self._report, \'covcache\'):\n self._report.covcache = {}\n import coverage\n self.cov = coverage.Coverage()\n self.cov.start()\n self.setUp()\n\n def _callTearDown(self):\n self.tearDown()\n if self._with_coverage:\n from pathlib import Path\n from snipper import snipper\n self.cov.stop()\n data = self.cov.get_data()\n base, _, _ = self._report._import_base_relative()\n for file in data.measured_files():\n file = os.path.normpath(file)\n root = Path(base)\n child = Path(file)\n if root in child.parents:\n with open(child, \'r\') as f:\n s = f.read()\n lines = s.splitlines()\n garb = \'GARBAGE\'\n\n lines2 = snipper.censor_code(lines, keep=True)\n assert len(lines) == len(lines2)\n\n for l in data.contexts_by_lineno(file):\n if lines2[l].strip() == garb:\n if self.cache_id() not in self._report.covcache:\n self._report.covcache[self.cache_id()] = {}\n\n rel = os.path.relpath(child, root)\n cc = self._report.covcache[self.cache_id()]\n j = 0\n for j in range(l, -1, -1):\n if "def" in lines2[j] or "class" in lines2[j]:\n break\n from snipper.snipper import gcoms\n fun = lines2[j]\n comments, _ = gcoms("\\n".join(lines2[j:l]))\n if rel not in cc:\n cc[rel] = {}\n cc[rel][fun] = (l, "\\n".join(comments))\n self._cache_put((self.cache_id(), \'coverage\'), self._report.covcache)\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd is None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n sd = self.shortDescriptionStandard()\n title = self._cache_get((self.cache_id(), \'title\'), sd)\n return title if title is not None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome is None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc is not None:\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard())\n\n self._cache2[(self.cache_id(), \'assert\')] = {}\n res = testMethod()\n elapsed = time.time() - t\n self._get_outcome()[self.cache_id()] = res\n self._cache_put((self.cache_id(), "time"), elapsed)\n\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return c, m\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n # sys.stdout = self._stdout\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n self.__class__._cache[\n key] = {} # A new dict. We manually insert it because we have to use that the dict is mutable.\n cache = self._cache_get(key)\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, f"Key {id} not found in cache; framework files missing. Please run deploy()")\n\n # The order of these calls is important. If the method assert fails, we should still store the correct result in cache.\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n assert_fun(first, _expected, *args, **kwargs)\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache is not None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n if os.path.exists(cfile):\n try:\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n except Exception as e:\n print("Bad cache", cfile)\n print(e)\n else:\n print("Warning! data file not found", cfile)\n\n def _feedErrorsToResult(self, result, errors):\n """ Use this to show hints on test failure. """\n if not isinstance(result, UTextResult):\n er = [e for e, v in errors if v != None]\n\n if len(er) > 0:\n hints = []\n key = (self.cache_id(), \'coverage\')\n if self._cache_contains(key):\n CC = self._cache_get(key)\n for id in CC:\n if id == self.cache_id():\n cl, m = id\n gprint(f"> An error occured while solving: {cl}.{m}. The files/methods you need to edit are:") # For the test {id} in {file} you should edit:")\n for file in CC[id]:\n rec = CC[id][file]\n gprint(f"> * {file}")\n for l in rec:\n _, comments = CC[id][file][l]\n hint = get_hints(comments)\n\n if hint != None:\n hints.append(hint)\n gprint(f"> - {l}")\n\n er = er[0]\n doc = er._testMethodDoc\n if doc is not None:\n hint = get_hints(er._testMethodDoc)\n if hint is not None:\n hints = [hint] + hints\n if len(hints) > 0:\n gprint("> Hints:")\n gprint(textwrap.indent("\\n".join(hints), "> "))\n\n super()._feedErrorsToResult(result, errors)\n\n def startTestRun(self):\n # print("asdfsdaf 11", file=sys.stderr)\n super().startTestRun()\n # print("asdfsdaf")\n\n def _callTestMethod(self, method):\n # print("asdfsdaf")\n super()._callTestMethod(method)\n\n\ndef hide(func):\n return func\n\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n# 817\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\nimport inspect\nimport os\nimport argparse\nimport time\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print(b + " v" + __version__ + ", started: " + dt_string+ "\\n")\n # print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print(s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n UTextResult.nL = report.nL\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f" * q{n+1}) Total"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")",\n last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL)\n\n # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n # print(" ")\n # print("="*n)\n # print("Final evaluation")\n # print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n print("")\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f" * {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.normpath(os.path.join(output_dir, token))\n\n\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single unmodified file: ")\n print(">", token)\n # print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n def test_add(self):\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n self.assertEqualC(add(-100, 5))\n\n\nclass AutomaticPass(UTestCase):\n def test_student_passed(self):\n self.assertEqual(2,2)\n\n\nimport cs103\nclass Report3(Report):\n title = "CS 101 Report 3"\n questions = [(Week1, 20), (AutomaticPass, 10)] # Include a single question for 10 credits.\n pack_imports = [cs103]' +report1_payload = '80049568000000000000007d94288c055765656b31947d942868018c08746573745f6164649486948c066173736572749486947d94284b014aa1ffffff4b004b04758c0474696d6594473fb71ac800000000758c0d4175746f6d6174696350617373947d946808473fb127100000000073752e' +name="Report3" + +report = source_instantiate(name, report1_source, report1_payload) +output_dir = os.path.dirname(__file__) +gather_upload_to_campusnet(report, output_dir) diff --git a/examples/example_docker/instructor/unitgrade-docker/requirements.txt b/docker_images/unitgrade-docker/requirements.txt similarity index 86% rename from examples/example_docker/instructor/unitgrade-docker/requirements.txt rename to docker_images/unitgrade-docker/requirements.txt index 9db6120..0a73d68 100644 --- a/examples/example_docker/instructor/unitgrade-docker/requirements.txt +++ b/docker_images/unitgrade-docker/requirements.txt @@ -4,3 +4,4 @@ jinja2 tabulate compress_pickle pyfiglet +colorama \ No newline at end of file diff --git a/docker_images/unitgrade-docker/tmp/cs103/homework1.py b/docker_images/unitgrade-docker/tmp/cs103/homework1.py new file mode 100644 index 0000000..3543f1b --- /dev/null +++ b/docker_images/unitgrade-docker/tmp/cs103/homework1.py @@ -0,0 +1,21 @@ +""" +Example student code. This file is automatically generated from the files in the instructor-directory +""" +def reverse_list(mylist): + """ + Given a list 'mylist' returns a list consisting of the same elements in reverse order. E.g. + reverse_list([1,2,3]) should return [3,2,1] (as a list). + """ + # TODO: 1 lines missing. + raise NotImplementedError("Implement function body") + +def add(a,b): + """ Given two numbers `a` and `b` this function should simply return their sum: + > add(a,b) = a+b """ + # TODO: 1 lines missing. + raise NotImplementedError("Implement function body") + +if __name__ == "__main__": + # Problem 1: Write a function which add two numbers + print(f"Your result of 2 + 2 = {add(2,2)}") + print(f"Reversing a small list", reverse_list([2,3,5,7])) diff --git a/docker_images/unitgrade-docker/tmp/cs103/report3.py b/docker_images/unitgrade-docker/tmp/cs103/report3.py new file mode 100644 index 0000000..f83bb53 --- /dev/null +++ b/docker_images/unitgrade-docker/tmp/cs103/report3.py @@ -0,0 +1,29 @@ +""" +Example student code. This file is automatically generated from the files in the instructor-directory +""" +from src.unitgrade2.unitgrade2 import UTestCase, Report +from src.unitgrade2 import evaluate_report_student + +class Week1(UTestCase): + """ The first question for week 1. """ + def test_add(self): + from cs103.homework1 import add + self.assertEqualC(add(2,2)) + self.assertEqualC(add(-100, 5)) + + +class AutomaticPass(UTestCase): + def test_student_passed(self): + self.assertEqual(2,2) + + +import cs103 +class Report3(Report): + title = "CS 101 Report 3" + questions = [(Week1, 20), (AutomaticPass, 10)] # Include a single question for 10 credits. + pack_imports = [cs103] + +if __name__ == "__main__": + # from unitgrade_private2.hidden_gather_upload import gather_upload_to_campusnet + # gather_upload_to_campusnet(Report3()) + evaluate_report_student(Report3()) diff --git a/docker_images/unitgrade-docker/tmp/cs103/report3_complete_grade.py b/docker_images/unitgrade-docker/tmp/cs103/report3_complete_grade.py new file mode 100644 index 0000000..1101b26 --- /dev/null +++ b/docker_images/unitgrade-docker/tmp/cs103/report3_complete_grade.py @@ -0,0 +1,338 @@ + +import numpy as np +from tabulate import tabulate +from datetime import datetime +import pyfiglet +import unittest +import inspect +import os +import argparse +import time + +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: +To run all tests in a report: + +> python assignment1_dp.py + +To run only question 2 or question 2.1 + +> python assignment1_dp.py -q 2 +> python assignment1_dp.py -q 2.1 + +Note this scripts does not grade your report. To grade your report, use: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('-q', nargs='?', type=str, default=None, help='Only evaluate this question (e.g.: -q 2)') +parser.add_argument('--showexpected', action="store_true", help='Show the expected/desired result') +parser.add_argument('--showcomputed', action="store_true", help='Show the answer your code computes') +parser.add_argument('--unmute', action="store_true", help='Show result of print(...) commands in code') +parser.add_argument('--passall', action="store_true", help='Automatically pass all tests. Useful when debugging.') + +def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False): + args = parser.parse_args() + if question is None and args.q is not None: + question = args.q + if "." in question: + question, qitem = [int(v) for v in question.split(".")] + else: + question = int(question) + + if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file: + raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation") + + if unmute is None: + unmute = args.unmute + if passall is None: + passall = args.passall + + results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute, + show_tol_err=show_tol_err) + + + if question is None: + print("Provisional evaluation") + tabulate(table_data) + table = table_data + print(tabulate(table)) + print(" ") + + fr = inspect.getouterframes(inspect.currentframe())[1].filename + gfile = os.path.basename(fr)[:-3] + "_grade.py" + if os.path.exists(gfile): + print("Note your results have not yet been registered. \nTo register your results, please run the file:") + print(">>>", gfile) + print("In the same manner as you ran this file.") + + + return results + + +def upack(q): + # h = zip([(i['w'], i['possible'], i['obtained']) for i in q.values()]) + h =[(i['w'], i['possible'], i['obtained']) for i in q.values()] + h = np.asarray(h) + return h[:,0], h[:,1], h[:,2], + +class UnitgradeTextRunner(unittest.TextTestRunner): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + +class SequentialTestLoader(unittest.TestLoader): + def getTestCaseNames(self, testCaseClass): + test_names = super().getTestCaseNames(testCaseClass) + # testcase_methods = list(testCaseClass.__dict__.keys()) + ls = [] + for C in testCaseClass.mro(): + if issubclass(C, unittest.TestCase): + ls = list(C.__dict__.keys()) + ls + testcase_methods = ls + test_names.sort(key=testcase_methods.index) + return test_names + +def evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False, + show_progress_bar=True, + show_tol_err=False, + big_header=True): + + now = datetime.now() + if big_header: + ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom") + b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) + else: + b = "Unitgrade" + dt_string = now.strftime("%d/%m/%Y %H:%M:%S") + print(b + " v" + __version__ + ", started: " + dt_string+ "\n") + # print("Started: " + dt_string) + s = report.title + if hasattr(report, "version") and report.version is not None: + s += " version " + report.version + print(s, "(use --help for options)" if show_help_flag else "") + # print(f"Loaded answers from: ", report.computed_answers_file, "\n") + table_data = [] + t_start = time.time() + score = {} + loader = SequentialTestLoader() + + for n, (q, w) in enumerate(report.questions): + if question is not None and n+1 != question: + continue + suite = loader.loadTestsFromTestCase(q) + qtitle = q.question_title() if hasattr(q, 'question_title') else q.__qualname__ + q_title_print = "Question %i: %s"%(n+1, qtitle) + print(q_title_print, end="") + q.possible = 0 + q.obtained = 0 + q_ = {} # Gather score in this class. + UTextResult.q_title_print = q_title_print # Hacky + UTextResult.show_progress_bar = show_progress_bar # Hacky. + UTextResult.number = n + UTextResult.nL = report.nL + + res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) + + possible = res.testsRun + obtained = len(res.successes) + + assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun + + obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 + score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} + q.obtained = obtained + q.possible = possible + + s1 = f" * q{n+1}) Total" + s2 = f" {q.obtained}/{w}" + print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 ) + print(" ") + table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"]) + + ws, possible, obtained = upack(score) + possible = int( msum(possible) ) + obtained = int( msum(obtained) ) # Cast to python int + report.possible = possible + report.obtained = obtained + now = datetime.now() + dt_string = now.strftime("%H:%M:%S") + + dt = int(time.time()-t_start) + minutes = dt//60 + seconds = dt - minutes*60 + plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") + + dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", + last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) + + # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total") + + table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) + results = {'total': (obtained, possible), 'details': score} + return results, table_data + + +from tabulate import tabulate +from datetime import datetime +import inspect +import json +import os +import bz2 +import pickle +import os + +def bzwrite(json_str, token): # to get around obfuscation issues + with getattr(bz2, 'open')(token, "wt") as f: + f.write(json_str) + +def gather_imports(imp): + resources = {} + m = imp + # for m in pack_imports: + # print(f"*** {m.__name__}") + f = m.__file__ + # dn = os.path.dirname(f) + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = str(__import__(m.__name__.split('.')[0]).__path__) + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: + top_package = os.path.dirname(m.__file__) + module_import = True + else: + top_package = __import__(m.__name__.split('.')[0]).__path__._path[0] + module_import = False + + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = os.path.dirname(top_package) + import zipfile + # import strea + # zipfile.ZipFile + import io + # file_like_object = io.BytesIO(my_zip_data) + zip_buffer = io.BytesIO() + with zipfile.ZipFile(zip_buffer, 'w') as zip: + # zip.write() + for root, dirs, files in os.walk(top_package): + for file in files: + if file.endswith(".py"): + fpath = os.path.join(root, file) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) + zip.write(fpath, v) + + resources['zipfile'] = zip_buffer.getvalue() + resources['top_package'] = top_package + resources['module_import'] = module_import + return resources, top_package + + if f.endswith("__init__.py"): + for root, dirs, files in os.walk(os.path.dirname(f)): + for file in files: + if file.endswith(".py"): + # print(file) + # print() + v = os.path.relpath(os.path.join(root, file), top_package) + with open(os.path.join(root, file), 'r') as ff: + resources[v] = ff.read() + else: + v = os.path.relpath(f, top_package) + with open(f, 'r') as ff: + resources[v] = ff.read() + return resources + +import argparse +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Use this script to get the score of your report. Example: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('--noprogress', action="store_true", help='Disable progress bars') +parser.add_argument('--autolab', action="store_true", help='Show Autolab results') + +def gather_upload_to_campusnet(report, output_dir=None): + n = report.nL + args = parser.parse_args() + results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, + show_progress_bar=not args.noprogress, + big_header=not args.autolab) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) + # also load the source code of missing files... + + sources = {} + print("") + if not args.autolab: + if len(report.individual_imports) > 0: + print("By uploading the .token file, you verify the files:") + for m in report.individual_imports: + print(">", m.__file__) + print("Are created/modified individually by you in agreement with DTUs exam rules") + report.pack_imports += report.individual_imports + + if len(report.pack_imports) > 0: + print("Including files in upload...") + for k, m in enumerate(report.pack_imports): + nimp, top_package = gather_imports(m) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import + nimp['name'] = m.__name__ + sources[k] = nimp + # if len([k for k in nimp if k not in sources]) > 0: + print(f" * {m.__name__}") + # sources = {**sources, **nimp} + results['sources'] = sources + + if output_dir is None: + output_dir = os.getcwd() + + payload_out_base = report.__class__.__name__ + "_handin" + + obtain, possible = results['total'] + vstring = "_v"+report.version if report.version is not None else "" + + token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) + token = os.path.normpath(os.path.join(output_dir, token)) + + + with open(token, 'wb') as f: + pickle.dump(results, f) + + if not args.autolab: + print(" ") + print("To get credit for your results, please upload the single unmodified file: ") + print(">", token) + # print("To campusnet without any modifications.") + + # print("Now time for some autolab fun") + +def source_instantiate(name, report1_source, payload): + eval("exec")(report1_source, globals()) + pl = pickle.loads(bytes.fromhex(payload)) + report = eval(name)(payload=pl, strict=True) + # report.set_payload(pl) + return report + + + +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n"""\nimport numpy as np\nimport sys\nimport re\nimport threading\nimport tqdm\nimport pickle\nimport os\nfrom io import StringIO\nimport io\nfrom unittest.runner import _WritelnDecorator\nfrom typing import Any\nimport inspect\nimport textwrap\nimport colorama\nfrom colorama import Fore\nfrom functools import _make_key, RLock\nfrom collections import namedtuple\nimport unittest\nimport time\n\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ncolorama.init(autoreset=True) # auto resets your settings after every output\n\ndef gprint(s):\n print(f"{Fore.GREEN}{s}")\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\n\ndef setup_dir_by_class(C, base_dir):\n name = C.__class__.__name__\n return base_dir, name\n\n\nclass Logger(object):\n def __init__(self, buffer):\n assert False\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\n# @classmethod\n# class OrderedClassMembers(type):\n# def __prepare__(self, name, bases):\n# assert False\n# return collections.OrderedDict()\n#\n# def __new__(self, name, bases, classdict):\n# ks = list(classdict.keys())\n# for b in bases:\n# ks += b.__ordered__\n# classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n# return type.__new__(self, name, bases, classdict)\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 120 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q, _) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q, _) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n loader = unittest.TestLoader()\n for q, _ in self.questions:\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self, with_coverage=False):\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = True\n q._report = self\n\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n # print(self.questions)\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n print("q is", q())\n q()._cache_put(\'time\', q.time) # = q.time\n report_cache[q.__qualname__] = q._cache2\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\': True}\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = False\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct + 1)\n if i > 0 and l.find("|", i + 1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = r\'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar", show_progress_bar=True, file=None):\n if file == None:\n file = sys.stdout\n self.file = file\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.01\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n self.pbar = None\n\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar = None\n\n self.file.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=self.file, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\')\n\n for _ in range(self.n - 1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\ndef dprint(first, last, nL, extra = "", file=None, dotsym=\'.\', color=\'white\'):\n if file == None:\n file = sys.stdout\n\n # ss = self.item_title_print\n # state = "PASS" if success else "FAILED"\n dot_parts = (dotsym * max(0, nL - len(last) - len(first)))\n # if self.show_progress_bar or True:\n print(first + dot_parts, end="", file=file)\n # else:\n # print(dot_parts, end="", file=self.cc.file)\n last += extra\n # if tsecs >= 0.5:\n # state += " (" + str(tsecs) + " seconds)"\n print(last, file=file)\n\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n cc = None\n\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n self.successes.append(test)\n self.cc_terminate()\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n self.cc.file.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="", file=self.cc.file)\n else:\n print(dot_parts, end="", file=self.cc.file)\n\n if tsecs >= 0.5:\n state += " (" + str(tsecs) + " seconds)"\n print(state, file=self.cc.file)\n\n def startTest(self, test):\n # j =self.testsRun\n self.testsRun += 1\n # item_title = self.getDescription(test)\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n self.item_title_print = " * q%i.%i) %s" % (UTextResult.number + 1, self.testsRun, item_title)\n estimated_time = 10\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar, file=sys.stdout)\n else:\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n self._stdout = sys.stdout\n sys.stdout = io.StringIO()\n\n def stopTest(self, test):\n sys.stdout = self._stdout\n super().stopTest(test)\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass is None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n print(" " * max(0, self.nL - len(self.cc.title)) + (" (" + str(q_time) + " seconds)" if q_time >= 0.5 else ""))\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n stream = io.StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)))\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n\n return wrapper\n\n\ndef get_hints(ss):\n if ss == None:\n return None\n try:\n ss = textwrap.dedent(ss)\n ss = ss.replace(\'\'\'"""\'\'\', "").strip()\n hints = ["hints:", ]\n j = np.argmax([ss.lower().find(h) for h in hints])\n h = hints[j]\n ss = ss[ss.find(h) + len(h) + 1:]\n ss = "\\n".join([l for l in ss.split("\\n") if not l.strip().startswith(":")])\n ss = textwrap.dedent(ss)\n ss = ss.strip()\n return ss\n except Exception as e:\n print("bad hints", ss, e)\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n _with_coverage = False\n _report = None # The report used. This is very, very hacky and should always be None. Don\'t rely on it!\n\n def capture(self):\n if hasattr(self, \'_stdout\') and self._stdout is not None:\n file = self._stdout\n else:\n # self._stdout = sys.stdout\n # sys._stdout = io.StringIO()\n file = sys.stdout\n return Capturing2(stdout=file)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ is not None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n if self._with_coverage:\n if not hasattr(self._report, \'covcache\'):\n self._report.covcache = {}\n import coverage\n self.cov = coverage.Coverage()\n self.cov.start()\n self.setUp()\n\n def _callTearDown(self):\n self.tearDown()\n if self._with_coverage:\n from pathlib import Path\n from snipper import snipper\n self.cov.stop()\n data = self.cov.get_data()\n base, _, _ = self._report._import_base_relative()\n for file in data.measured_files():\n file = os.path.normpath(file)\n root = Path(base)\n child = Path(file)\n if root in child.parents:\n with open(child, \'r\') as f:\n s = f.read()\n lines = s.splitlines()\n garb = \'GARBAGE\'\n\n lines2 = snipper.censor_code(lines, keep=True)\n assert len(lines) == len(lines2)\n\n for l in data.contexts_by_lineno(file):\n if lines2[l].strip() == garb:\n if self.cache_id() not in self._report.covcache:\n self._report.covcache[self.cache_id()] = {}\n\n rel = os.path.relpath(child, root)\n cc = self._report.covcache[self.cache_id()]\n j = 0\n for j in range(l, -1, -1):\n if "def" in lines2[j] or "class" in lines2[j]:\n break\n from snipper.snipper import gcoms\n fun = lines2[j]\n comments, _ = gcoms("\\n".join(lines2[j:l]))\n if rel not in cc:\n cc[rel] = {}\n cc[rel][fun] = (l, "\\n".join(comments))\n self._cache_put((self.cache_id(), \'coverage\'), self._report.covcache)\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd is None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n sd = self.shortDescriptionStandard()\n title = self._cache_get((self.cache_id(), \'title\'), sd)\n return title if title is not None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome is None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc is not None:\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard())\n\n self._cache2[(self.cache_id(), \'assert\')] = {}\n res = testMethod()\n elapsed = time.time() - t\n self._get_outcome()[self.cache_id()] = res\n self._cache_put((self.cache_id(), "time"), elapsed)\n\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return c, m\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n # sys.stdout = self._stdout\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n self.__class__._cache[\n key] = {} # A new dict. We manually insert it because we have to use that the dict is mutable.\n cache = self._cache_get(key)\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, f"Key {id} not found in cache; framework files missing. Please run deploy()")\n\n # The order of these calls is important. If the method assert fails, we should still store the correct result in cache.\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n assert_fun(first, _expected, *args, **kwargs)\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache is not None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n if os.path.exists(cfile):\n try:\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n except Exception as e:\n print("Bad cache", cfile)\n print(e)\n else:\n print("Warning! data file not found", cfile)\n\n def _feedErrorsToResult(self, result, errors):\n """ Use this to show hints on test failure. """\n if not isinstance(result, UTextResult):\n er = [e for e, v in errors if v != None]\n\n if len(er) > 0:\n hints = []\n key = (self.cache_id(), \'coverage\')\n if self._cache_contains(key):\n CC = self._cache_get(key)\n for id in CC:\n if id == self.cache_id():\n cl, m = id\n gprint(f"> An error occured while solving: {cl}.{m}. The files/methods you need to edit are:") # For the test {id} in {file} you should edit:")\n for file in CC[id]:\n rec = CC[id][file]\n gprint(f"> * {file}")\n for l in rec:\n _, comments = CC[id][file][l]\n hint = get_hints(comments)\n\n if hint != None:\n hints.append(hint)\n gprint(f"> - {l}")\n\n er = er[0]\n doc = er._testMethodDoc\n if doc is not None:\n hint = get_hints(er._testMethodDoc)\n if hint is not None:\n hints = [hint] + hints\n if len(hints) > 0:\n gprint("> Hints:")\n gprint(textwrap.indent("\\n".join(hints), "> "))\n\n super()._feedErrorsToResult(result, errors)\n\n def startTestRun(self):\n # print("asdfsdaf 11", file=sys.stderr)\n super().startTestRun()\n # print("asdfsdaf")\n\n def _callTestMethod(self, method):\n # print("asdfsdaf")\n super()._callTestMethod(method)\n\n\ndef hide(func):\n return func\n\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n# 817\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\nimport inspect\nimport os\nimport argparse\nimport time\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print(b + " v" + __version__ + ", started: " + dt_string+ "\\n")\n # print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print(s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n UTextResult.nL = report.nL\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f" * q{n+1}) Total"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")",\n last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL)\n\n # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n # print(" ")\n # print("="*n)\n # print("Final evaluation")\n # print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n print("")\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f" * {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.normpath(os.path.join(output_dir, token))\n\n\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single unmodified file: ")\n print(">", token)\n # print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n def test_add(self):\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n self.assertEqualC(add(-100, 5))\n\n @hide\n def test_add_hidden(self):\n # This is a hidden test. The @hide-decorator will allow unitgrade to remove the test.\n # See the output in the student directory for more information.\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n\nclass AutomaticPass(UTestCase):\n def test_student_passed(self):\n self.assertEqual(2,2)\n\n @hide\n def test_hidden_fail(self):\n self.assertEqual(2,3)\n\nimport cs103\nclass Report3(Report):\n title = "CS 101 Report 3"\n questions = [(Week1, 20), (AutomaticPass, 10)] # Include a single question for 10 credits.\n pack_imports = [cs103]' +report1_payload = '80049589000000000000007d94288c055765656b31947d942868018c08746573745f6164649486948c066173736572749486947d94284b014aa1ffffff4b004b047568018c0f746573745f6164645f68696464656e948694680586947d944b004b04738c0474696d6594473fda6e8700000000758c0d4175746f6d6174696350617373947d94680c473fb8d5140000000073752e' +name="Report3" + +report = source_instantiate(name, report1_source, report1_payload) +output_dir = os.path.dirname(__file__) +gather_upload_to_campusnet(report, output_dir) \ No newline at end of file diff --git a/docker_images/unitgrade-docker/tmp/cs103/report3_grade.py b/docker_images/unitgrade-docker/tmp/cs103/report3_grade.py new file mode 100644 index 0000000..85573c9 --- /dev/null +++ b/docker_images/unitgrade-docker/tmp/cs103/report3_grade.py @@ -0,0 +1,340 @@ +""" +Example student code. This file is automatically generated from the files in the instructor-directory +""" +import numpy as np +from tabulate import tabulate +from datetime import datetime +import pyfiglet +import unittest +import inspect +import os +import argparse +import time + +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: +To run all tests in a report: + +> python assignment1_dp.py + +To run only question 2 or question 2.1 + +> python assignment1_dp.py -q 2 +> python assignment1_dp.py -q 2.1 + +Note this scripts does not grade your report. To grade your report, use: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('-q', nargs='?', type=str, default=None, help='Only evaluate this question (e.g.: -q 2)') +parser.add_argument('--showexpected', action="store_true", help='Show the expected/desired result') +parser.add_argument('--showcomputed', action="store_true", help='Show the answer your code computes') +parser.add_argument('--unmute', action="store_true", help='Show result of print(...) commands in code') +parser.add_argument('--passall', action="store_true", help='Automatically pass all tests. Useful when debugging.') + +def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False): + args = parser.parse_args() + if question is None and args.q is not None: + question = args.q + if "." in question: + question, qitem = [int(v) for v in question.split(".")] + else: + question = int(question) + + if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file: + raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation") + + if unmute is None: + unmute = args.unmute + if passall is None: + passall = args.passall + + results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute, + show_tol_err=show_tol_err) + + + if question is None: + print("Provisional evaluation") + tabulate(table_data) + table = table_data + print(tabulate(table)) + print(" ") + + fr = inspect.getouterframes(inspect.currentframe())[1].filename + gfile = os.path.basename(fr)[:-3] + "_grade.py" + if os.path.exists(gfile): + print("Note your results have not yet been registered. \nTo register your results, please run the file:") + print(">>>", gfile) + print("In the same manner as you ran this file.") + + + return results + + +def upack(q): + # h = zip([(i['w'], i['possible'], i['obtained']) for i in q.values()]) + h =[(i['w'], i['possible'], i['obtained']) for i in q.values()] + h = np.asarray(h) + return h[:,0], h[:,1], h[:,2], + +class UnitgradeTextRunner(unittest.TextTestRunner): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + +class SequentialTestLoader(unittest.TestLoader): + def getTestCaseNames(self, testCaseClass): + test_names = super().getTestCaseNames(testCaseClass) + # testcase_methods = list(testCaseClass.__dict__.keys()) + ls = [] + for C in testCaseClass.mro(): + if issubclass(C, unittest.TestCase): + ls = list(C.__dict__.keys()) + ls + testcase_methods = ls + test_names.sort(key=testcase_methods.index) + return test_names + +def evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False, + show_progress_bar=True, + show_tol_err=False, + big_header=True): + + now = datetime.now() + if big_header: + ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom") + b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) + else: + b = "Unitgrade" + dt_string = now.strftime("%d/%m/%Y %H:%M:%S") + print(b + " v" + __version__ + ", started: " + dt_string+ "\n") + # print("Started: " + dt_string) + s = report.title + if hasattr(report, "version") and report.version is not None: + s += " version " + report.version + print(s, "(use --help for options)" if show_help_flag else "") + # print(f"Loaded answers from: ", report.computed_answers_file, "\n") + table_data = [] + t_start = time.time() + score = {} + loader = SequentialTestLoader() + + for n, (q, w) in enumerate(report.questions): + if question is not None and n+1 != question: + continue + suite = loader.loadTestsFromTestCase(q) + qtitle = q.question_title() if hasattr(q, 'question_title') else q.__qualname__ + q_title_print = "Question %i: %s"%(n+1, qtitle) + print(q_title_print, end="") + q.possible = 0 + q.obtained = 0 + q_ = {} # Gather score in this class. + UTextResult.q_title_print = q_title_print # Hacky + UTextResult.show_progress_bar = show_progress_bar # Hacky. + UTextResult.number = n + UTextResult.nL = report.nL + + res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) + + possible = res.testsRun + obtained = len(res.successes) + + assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun + + obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 + score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} + q.obtained = obtained + q.possible = possible + + s1 = f" * q{n+1}) Total" + s2 = f" {q.obtained}/{w}" + print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 ) + print(" ") + table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"]) + + ws, possible, obtained = upack(score) + possible = int( msum(possible) ) + obtained = int( msum(obtained) ) # Cast to python int + report.possible = possible + report.obtained = obtained + now = datetime.now() + dt_string = now.strftime("%H:%M:%S") + + dt = int(time.time()-t_start) + minutes = dt//60 + seconds = dt - minutes*60 + plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") + + dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", + last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) + + # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total") + + table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) + results = {'total': (obtained, possible), 'details': score} + return results, table_data + + +from tabulate import tabulate +from datetime import datetime +import inspect +import json +import os +import bz2 +import pickle +import os + +def bzwrite(json_str, token): # to get around obfuscation issues + with getattr(bz2, 'open')(token, "wt") as f: + f.write(json_str) + +def gather_imports(imp): + resources = {} + m = imp + # for m in pack_imports: + # print(f"*** {m.__name__}") + f = m.__file__ + # dn = os.path.dirname(f) + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = str(__import__(m.__name__.split('.')[0]).__path__) + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: + top_package = os.path.dirname(m.__file__) + module_import = True + else: + top_package = __import__(m.__name__.split('.')[0]).__path__._path[0] + module_import = False + + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = os.path.dirname(top_package) + import zipfile + # import strea + # zipfile.ZipFile + import io + # file_like_object = io.BytesIO(my_zip_data) + zip_buffer = io.BytesIO() + with zipfile.ZipFile(zip_buffer, 'w') as zip: + # zip.write() + for root, dirs, files in os.walk(top_package): + for file in files: + if file.endswith(".py"): + fpath = os.path.join(root, file) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) + zip.write(fpath, v) + + resources['zipfile'] = zip_buffer.getvalue() + resources['top_package'] = top_package + resources['module_import'] = module_import + return resources, top_package + + if f.endswith("__init__.py"): + for root, dirs, files in os.walk(os.path.dirname(f)): + for file in files: + if file.endswith(".py"): + # print(file) + # print() + v = os.path.relpath(os.path.join(root, file), top_package) + with open(os.path.join(root, file), 'r') as ff: + resources[v] = ff.read() + else: + v = os.path.relpath(f, top_package) + with open(f, 'r') as ff: + resources[v] = ff.read() + return resources + +import argparse +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Use this script to get the score of your report. Example: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('--noprogress', action="store_true", help='Disable progress bars') +parser.add_argument('--autolab', action="store_true", help='Show Autolab results') + +def gather_upload_to_campusnet(report, output_dir=None): + n = report.nL + args = parser.parse_args() + results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, + show_progress_bar=not args.noprogress, + big_header=not args.autolab) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) + # also load the source code of missing files... + + sources = {} + print("") + if not args.autolab: + if len(report.individual_imports) > 0: + print("By uploading the .token file, you verify the files:") + for m in report.individual_imports: + print(">", m.__file__) + print("Are created/modified individually by you in agreement with DTUs exam rules") + report.pack_imports += report.individual_imports + + if len(report.pack_imports) > 0: + print("Including files in upload...") + for k, m in enumerate(report.pack_imports): + nimp, top_package = gather_imports(m) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import + nimp['name'] = m.__name__ + sources[k] = nimp + # if len([k for k in nimp if k not in sources]) > 0: + print(f" * {m.__name__}") + # sources = {**sources, **nimp} + results['sources'] = sources + + if output_dir is None: + output_dir = os.getcwd() + + payload_out_base = report.__class__.__name__ + "_handin" + + obtain, possible = results['total'] + vstring = "_v"+report.version if report.version is not None else "" + + token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) + token = os.path.normpath(os.path.join(output_dir, token)) + + + with open(token, 'wb') as f: + pickle.dump(results, f) + + if not args.autolab: + print(" ") + print("To get credit for your results, please upload the single unmodified file: ") + print(">", token) + # print("To campusnet without any modifications.") + + # print("Now time for some autolab fun") + +def source_instantiate(name, report1_source, payload): + eval("exec")(report1_source, globals()) + pl = pickle.loads(bytes.fromhex(payload)) + report = eval(name)(payload=pl, strict=True) + # report.set_payload(pl) + return report + + + +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n"""\nimport numpy as np\nimport sys\nimport re\nimport threading\nimport tqdm\nimport pickle\nimport os\nfrom io import StringIO\nimport io\nfrom unittest.runner import _WritelnDecorator\nfrom typing import Any\nimport inspect\nimport textwrap\nimport colorama\nfrom colorama import Fore\nfrom functools import _make_key, RLock\nfrom collections import namedtuple\nimport unittest\nimport time\n\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ncolorama.init(autoreset=True) # auto resets your settings after every output\n\ndef gprint(s):\n print(f"{Fore.GREEN}{s}")\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\n\ndef setup_dir_by_class(C, base_dir):\n name = C.__class__.__name__\n return base_dir, name\n\n\nclass Logger(object):\n def __init__(self, buffer):\n assert False\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\n# @classmethod\n# class OrderedClassMembers(type):\n# def __prepare__(self, name, bases):\n# assert False\n# return collections.OrderedDict()\n#\n# def __new__(self, name, bases, classdict):\n# ks = list(classdict.keys())\n# for b in bases:\n# ks += b.__ordered__\n# classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n# return type.__new__(self, name, bases, classdict)\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 120 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q, _) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q, _) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n loader = unittest.TestLoader()\n for q, _ in self.questions:\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self, with_coverage=False):\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = True\n q._report = self\n\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n # print(self.questions)\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n print("q is", q())\n q()._cache_put(\'time\', q.time) # = q.time\n report_cache[q.__qualname__] = q._cache2\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\': True}\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = False\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct + 1)\n if i > 0 and l.find("|", i + 1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = r\'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar", show_progress_bar=True, file=None):\n if file == None:\n file = sys.stdout\n self.file = file\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.01\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n self.pbar = None\n\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar = None\n\n self.file.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=self.file, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\')\n\n for _ in range(self.n - 1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\ndef dprint(first, last, nL, extra = "", file=None, dotsym=\'.\', color=\'white\'):\n if file == None:\n file = sys.stdout\n\n # ss = self.item_title_print\n # state = "PASS" if success else "FAILED"\n dot_parts = (dotsym * max(0, nL - len(last) - len(first)))\n # if self.show_progress_bar or True:\n print(first + dot_parts, end="", file=file)\n # else:\n # print(dot_parts, end="", file=self.cc.file)\n last += extra\n # if tsecs >= 0.5:\n # state += " (" + str(tsecs) + " seconds)"\n print(last, file=file)\n\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n cc = None\n\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n self.successes.append(test)\n self.cc_terminate()\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n self.cc.file.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="", file=self.cc.file)\n else:\n print(dot_parts, end="", file=self.cc.file)\n\n if tsecs >= 0.5:\n state += " (" + str(tsecs) + " seconds)"\n print(state, file=self.cc.file)\n\n def startTest(self, test):\n # j =self.testsRun\n self.testsRun += 1\n # item_title = self.getDescription(test)\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n self.item_title_print = " * q%i.%i) %s" % (UTextResult.number + 1, self.testsRun, item_title)\n estimated_time = 10\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar, file=sys.stdout)\n else:\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n self._stdout = sys.stdout\n sys.stdout = io.StringIO()\n\n def stopTest(self, test):\n sys.stdout = self._stdout\n super().stopTest(test)\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass is None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n print(" " * max(0, self.nL - len(self.cc.title)) + (" (" + str(q_time) + " seconds)" if q_time >= 0.5 else ""))\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n stream = io.StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)))\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n\n return wrapper\n\n\ndef get_hints(ss):\n if ss == None:\n return None\n try:\n ss = textwrap.dedent(ss)\n ss = ss.replace(\'\'\'"""\'\'\', "").strip()\n hints = ["hints:", ]\n j = np.argmax([ss.lower().find(h) for h in hints])\n h = hints[j]\n ss = ss[ss.find(h) + len(h) + 1:]\n ss = "\\n".join([l for l in ss.split("\\n") if not l.strip().startswith(":")])\n ss = textwrap.dedent(ss)\n ss = ss.strip()\n return ss\n except Exception as e:\n print("bad hints", ss, e)\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n _with_coverage = False\n _report = None # The report used. This is very, very hacky and should always be None. Don\'t rely on it!\n\n def capture(self):\n if hasattr(self, \'_stdout\') and self._stdout is not None:\n file = self._stdout\n else:\n # self._stdout = sys.stdout\n # sys._stdout = io.StringIO()\n file = sys.stdout\n return Capturing2(stdout=file)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ is not None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n if self._with_coverage:\n if not hasattr(self._report, \'covcache\'):\n self._report.covcache = {}\n import coverage\n self.cov = coverage.Coverage()\n self.cov.start()\n self.setUp()\n\n def _callTearDown(self):\n self.tearDown()\n if self._with_coverage:\n from pathlib import Path\n from snipper import snipper\n self.cov.stop()\n data = self.cov.get_data()\n base, _, _ = self._report._import_base_relative()\n for file in data.measured_files():\n file = os.path.normpath(file)\n root = Path(base)\n child = Path(file)\n if root in child.parents:\n with open(child, \'r\') as f:\n s = f.read()\n lines = s.splitlines()\n garb = \'GARBAGE\'\n\n lines2 = snipper.censor_code(lines, keep=True)\n assert len(lines) == len(lines2)\n\n for l in data.contexts_by_lineno(file):\n if lines2[l].strip() == garb:\n if self.cache_id() not in self._report.covcache:\n self._report.covcache[self.cache_id()] = {}\n\n rel = os.path.relpath(child, root)\n cc = self._report.covcache[self.cache_id()]\n j = 0\n for j in range(l, -1, -1):\n if "def" in lines2[j] or "class" in lines2[j]:\n break\n from snipper.snipper import gcoms\n fun = lines2[j]\n comments, _ = gcoms("\\n".join(lines2[j:l]))\n if rel not in cc:\n cc[rel] = {}\n cc[rel][fun] = (l, "\\n".join(comments))\n self._cache_put((self.cache_id(), \'coverage\'), self._report.covcache)\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd is None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n sd = self.shortDescriptionStandard()\n title = self._cache_get((self.cache_id(), \'title\'), sd)\n return title if title is not None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome is None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc is not None:\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard())\n\n self._cache2[(self.cache_id(), \'assert\')] = {}\n res = testMethod()\n elapsed = time.time() - t\n self._get_outcome()[self.cache_id()] = res\n self._cache_put((self.cache_id(), "time"), elapsed)\n\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return c, m\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n # sys.stdout = self._stdout\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n self.__class__._cache[\n key] = {} # A new dict. We manually insert it because we have to use that the dict is mutable.\n cache = self._cache_get(key)\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, f"Key {id} not found in cache; framework files missing. Please run deploy()")\n\n # The order of these calls is important. If the method assert fails, we should still store the correct result in cache.\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n assert_fun(first, _expected, *args, **kwargs)\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache is not None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n if os.path.exists(cfile):\n try:\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n except Exception as e:\n print("Bad cache", cfile)\n print(e)\n else:\n print("Warning! data file not found", cfile)\n\n def _feedErrorsToResult(self, result, errors):\n """ Use this to show hints on test failure. """\n if not isinstance(result, UTextResult):\n er = [e for e, v in errors if v != None]\n\n if len(er) > 0:\n hints = []\n key = (self.cache_id(), \'coverage\')\n if self._cache_contains(key):\n CC = self._cache_get(key)\n for id in CC:\n if id == self.cache_id():\n cl, m = id\n gprint(f"> An error occured while solving: {cl}.{m}. The files/methods you need to edit are:") # For the test {id} in {file} you should edit:")\n for file in CC[id]:\n rec = CC[id][file]\n gprint(f"> * {file}")\n for l in rec:\n _, comments = CC[id][file][l]\n hint = get_hints(comments)\n\n if hint != None:\n hints.append(hint)\n gprint(f"> - {l}")\n\n er = er[0]\n doc = er._testMethodDoc\n if doc is not None:\n hint = get_hints(er._testMethodDoc)\n if hint is not None:\n hints = [hint] + hints\n if len(hints) > 0:\n gprint("> Hints:")\n gprint(textwrap.indent("\\n".join(hints), "> "))\n\n super()._feedErrorsToResult(result, errors)\n\n def startTestRun(self):\n # print("asdfsdaf 11", file=sys.stderr)\n super().startTestRun()\n # print("asdfsdaf")\n\n def _callTestMethod(self, method):\n # print("asdfsdaf")\n super()._callTestMethod(method)\n\n\ndef hide(func):\n return func\n\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n# 817\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\nimport inspect\nimport os\nimport argparse\nimport time\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print(b + " v" + __version__ + ", started: " + dt_string+ "\\n")\n # print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print(s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n UTextResult.nL = report.nL\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f" * q{n+1}) Total"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")",\n last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL)\n\n # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n # print(" ")\n # print("="*n)\n # print("Final evaluation")\n # print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n print("")\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f" * {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.normpath(os.path.join(output_dir, token))\n\n\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single unmodified file: ")\n print(">", token)\n # print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n def test_add(self):\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n self.assertEqualC(add(-100, 5))\n\n\nclass AutomaticPass(UTestCase):\n def test_student_passed(self):\n self.assertEqual(2,2)\n\n\nimport cs103\nclass Report3(Report):\n title = "CS 101 Report 3"\n questions = [(Week1, 20), (AutomaticPass, 10)] # Include a single question for 10 credits.\n pack_imports = [cs103]' +report1_payload = '80049568000000000000007d94288c055765656b31947d942868018c08746573745f6164649486948c066173736572749486947d94284b014aa1ffffff4b004b04758c0474696d6594473fb1eb1c00000000758c0d4175746f6d6174696350617373947d946808473fa78d300000000073752e' +name="Report3" + +report = source_instantiate(name, report1_source, report1_payload) +output_dir = os.path.dirname(__file__) +gather_upload_to_campusnet(report, output_dir) diff --git a/examples/02471/instructor/02471/report1.py b/examples/02471/instructor/02471/report1.py index 1ed131f..96bb952 100644 --- a/examples/02471/instructor/02471/report1.py +++ b/examples/02471/instructor/02471/report1.py @@ -114,7 +114,7 @@ if __name__ == "__main__": # from week02 import Week_2_sol import importnb - file = "week02/week2.ipynb" + file = "../../../example_jupyter/instructor/cs105/week2.ipynb" file2 = 'week02/Week_2_sol.ipynb' m = importnb.Notebook.load(file) # importnb.Notebook.l diff --git a/examples/02631/instructor/programs/.coverage b/examples/02631/instructor/programs/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..11b2ec620c9b142ae8ee79c7a6f5afa297121530 GIT binary patch literal 53248 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 literal 0 HcmV?d00001 diff --git a/examples/02631/instructor/programs/__pycache__/looping.cpython-38.pyc b/examples/02631/instructor/programs/__pycache__/looping.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fa302e5a102c505ba4b62aa5eab3205ca2683298 GIT binary patch literal 2019 zcmWIL<>g{vU|@KAPd`zBoq^#oh=YuI7#J8F7#J9elNcBnQW#Pga~Pr+!8B78QwmcG za|=Tha|%leYYRgZOA1>GdkaGpYYImSXA46VTPk}BS2j~oTq;K@YbxUc&J^wxo)q2` zz7+lxffT_Mp>&Rgj48q?BF)UrjEoE^jKK_=qAx+V`z5o0C@5xSU|?WlU|?_txx<Zt zfuV+>hB=$1hzF#jgkb^WLIxLxSdChi6s8)+6s8)68YXE52?h}cafTWO5r$frdJ#s3 zLc2nd8kQ6$afV<9P3EeHTnY*b3eJf+$)!1oC8-J}nYpP7rFkWpISNUM$t9^pnTZPN zMfv3=849Tt$*HL+3VsU3C5c5PnR)37X+`<D3W*8@`2`@&nfZAN`DqGy25_BvV8b&q z^Gb@Xz)X-`S_;YedBvG2sYMET21ufbIRzPs3U&$*F{^^aqQqQ<JOhYEC>N{-MZ_D0 z=Lh8#rIwTy<v}<_j0_A6FF}MR(=E=-lGLJ-{QR8aTdc|XrFkWqthbo*3~sT4O}@qG zeT&iW7Guduh9Xd+_!aJK6%$&VT2vfUQksz(<C33Tnwy$eQXEs7msyftl$eqlUr>}; zmROP+lUk9OTac4l9AjW)W^5RfnO9s=RGM6pUldbNl%HOdm|Gl^lb>IZnU}6tP<e|F z7P#)<um#0+F&`+hc{mtZ7<m{a7&#c(7>ht@FB#-?kQ4}m6mq~4!VCrm22kYJFf3%O z1+kb)m}?lC85c69Fp4pxFl95<GJ@nw7#6V9Fw`*eGe|Nl1c`uoAa**_LdIIA5{4|+ z5{3nAHK63p&<f(Sr!h$~)PPwj3@Hq)Od!4l!$L+-g70Mlo6TIpQNvurn9bD8SkzTG zp@zAZu}}e&{J=?z#qT8(0|P@5h|pxb#gbT&S**!<iz%<*7JFiGVo_0I<t?_vqQt!P z)LYCssd=|pa#IuYKtk!cnR&OE6O)Q>vFD|hgY?~EPRz;CWQpP~N=?g2O)g2yOHPeq z@-)1~m>b3HX&4WVEC|8Pz`$^eKRKtgxFog6F)uNvvN*F?0+ek8K;g&8#VE$4!f3(B z!^i{*U`YlBhGb@t3>1Udpxg$ci>06e+|JO>n8uXCn8MV;QN!Q@4q{O5@k5ClkWWEc zg+aDyFfcHrGt@A|D%3J|Fw`)zFfcP@GZe{mFlI9pvE;EZG8D4ZFivDDWC@0(HL&wF znQk%Z8QfwlTFG>aD<{7^wa6*IG%w{AS7|{3lnqu_1okGEO-^QUNpgN}ft@PIPEe9% zV_;%nV&G!nVW?6`ODzJW*2I#`{JfyVl2kpLoc!d(oMJmYghoxKB7RUbfgG*L9L1BH znpj*~l$rukUnC2XkpmGRql;ufEU?W2=te7p><0w^15=R#C`^$PEhsEO0Ry6o{h(nA zNwnz<wTvZ9phWA!5UUfzRLj)KP{Q2Kkj97{+Rcn9%-KvuVl|8lSUMOMGBPq0ih&Y# z4dX&kiOEuBr{IAoWV94Q60=hk5*2dt^RpEaOB6~nQWc=tQz0`C%1tdw&MZ!alu)>% z5#$$eG)jQ7QVnA>V+lhCLo;JGQxR(PX)@ko&de*(WGVtBnp=z&D;aMwmLVdmNS=X# z!4MQmpmH1(O9Bj4Dn+Te`DLk|d7wDVNyQ#RFG0bk$qI=kP$Is?nVMIcn_84ul3FAL zO73il1qG>jDVof;m@-qMm@`w1ZZT#;tOmskB(Vsf+oc6^45)x%VBun9VH9ALV&q{g z0*T(@h>y=r%*>0APoFh&=FFLrCJyOm&zw1P#>dzJA~N%%eY!Ci7&$-~n~fdP(?Gy2 z4yM?|Asv#bOdMW<(%4H-X4B-l#hO=|TTlt{56ExeQmzOj2~Ik2C*&pOrpCwLVl61j w%qzLY3NA7rz5=I4Fab`D95%W6DWy57cA!KDE=D;RIhZ&^IJi03Ie0i&0HrYEG5`Po literal 0 HcmV?d00001 diff --git a/examples/02631/instructor/programs/__pycache__/report1intro.cpython-38.pyc b/examples/02631/instructor/programs/__pycache__/report1intro.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ddadca6243eeebd021c63d8d875e9092033e1190 GIT binary patch literal 7198 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 D4<0BP literal 0 HcmV?d00001 diff --git a/examples/02631/instructor/programs/deploy.py b/examples/02631/instructor/programs/deploy.py new file mode 100644 index 0000000..6099792 --- /dev/null +++ b/examples/02631/instructor/programs/deploy.py @@ -0,0 +1,62 @@ +from report1intro import Report1Flat +from unitgrade_private2.hidden_create_files import setup_grade_file_report +from snipper import snip_dir + +if __name__ == "__main__": + setup_grade_file_report(Report1Flat, minify=False, obfuscate=False, execute=False, with_coverage=True) + + # from unitgrade_private2.hidden_gather_upload import gather_upload_to_campusnet + # gather_upload_to_campusnet((Report1Flat())) + + # Deploy the files using snipper: https://gitlab.compute.dtu.dk/tuhe/snipper + snip_dir.snip_dir(source_dir="", dest_dir="../../students/programs", clean_destination_dir=True, exclude=['__pycache__', '*.token', 'deploy.py']) + + import os + os.system("python report1intro_grade.py") + + +""" +from coverage import CoverageData +import coverage +cov2 = coverage.Coverage() + + def setUp(self): + import trace + self.cov = cov2 + self.cov.start() + + + + # self.tracer.start() + + # using obj_to_trace + + def tearDown(self) -> None: + + self.cov.stop() + print() + + data = CoverageData() + + # data.measured_files() + # data.lines() + data = self.cov.get_data() + # data. + for file in data.measured_files(): + print(file) + print(data.lines(file)) + print(data.arcs(file)) + print( data.contexts_by_lineno(file)) + + # print(data[file]) + + + +- Idea: Measure coverage in setup/teardown. This gives a handful of covered lines. +- During setup, supply a dicionary to UTestCase of files, along with the lines that are removed. +- When running setup: Take the coverage report, and compare against files. Write functions/lines encountered to the cache dictionary. Rquires you to +- inspect the functions that are edited to figure out what is removed. This can probably be done by going upwars towards the first sensible class or function definition (which has not been removed). +- Supply a dictionary to UTestCase of files, along with the lines edited. Allow UTestCase to write this information to the + cache dictionary (i.e. lines removed). Then use this information when displaying helpful hints later. + +""" \ No newline at end of file diff --git a/examples/02631/instructor/programs/looping.py b/examples/02631/instructor/programs/looping.py new file mode 100644 index 0000000..59a1485 --- /dev/null +++ b/examples/02631/instructor/programs/looping.py @@ -0,0 +1,64 @@ +import numpy as np +import itertools + +def bacteriaGrowth(n0, alpha, K, N): #!f + """ + Calculate time until bacteria growth exceed N starting from a population of n0 bacteria. + hints: + * consider n0 + * alpha > 0 + :param n0: + :param alpha: + :param K: + :param N: + :return: + """ + if n0 > N: + return 0 + for t in itertools.count(): + n0 = (1 + alpha * (1-n0 / K) ) * n0 + if n0 > N: + break + return t+1 + +def clusterAnalysis(reflectance): + reflectance = np.asarray(reflectance) + I1 = np.arange(len(reflectance)) % 2 == 1 + while True: + m = np.asarray( [np.mean( reflectance[~I1] ), np.mean( reflectance[I1] ) ] ) + I1_ = np.argmin( np.abs( reflectance[:, np.newaxis] - m[np.newaxis, :] ), axis=1) == 1 + if all(I1_ == I1): + break + I1 = I1_ + return I1 + 1 + +def fermentationRate(measuredRate, lowerBound, upperBound): + # Insert your code here + return np.mean( [r for r in measuredRate if lowerBound < r < upperBound] ) + + + + +def removeIncomplete(id): + """ Hints: + * Take a look at the example in the exercise. + """ + id = np.asarray(id) + id2 = [] + for i, v in enumerate(id): + if len( [x for x in id if int(x) == int(v) ] ) == 3: + id2.append(v) + return np.asarray(id2) + + +if __name__ == "__main__": + # I = clusterAnalysis([1.7, 1.6, 1.3, 1.3, 2.8, 1.4, 2.8, 2.6, 1.6, 2.7]) + # print(I) + + print(fermentationRate(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 15, 25)) + + + # print(removeIncomplete(np.array([1.3, 2.2, 2.3, 4.2, 5.1, 3.2, 5.3, 3.3, 2.1, 1.1, 5.2, 3.1]))) + + # Problem 1: Write a function which add two numbers + # clusterAnalysis([2, 1, 2, 4, 5]) \ No newline at end of file diff --git a/examples/02631/instructor/programs/report1intro.py b/examples/02631/instructor/programs/report1intro.py new file mode 100644 index 0000000..10b1898 --- /dev/null +++ b/examples/02631/instructor/programs/report1intro.py @@ -0,0 +1,139 @@ +from src.unitgrade2.unitgrade2 import Report, UTestCase, cache +from src.unitgrade2 import evaluate_report_student +import numpy as np +import looping +from looping import bacteriaGrowth, clusterAnalysis, removeIncomplete, fermentationRate + +def trlist(x): + s = str(list(x)) + if len(s) > 30: + s = s[:30] + "...]" + return s + +class Bacteria(UTestCase): + """ Bacteria growth rates """ + + def stest(self, n0, alpha, K, N): + g = bacteriaGrowth(n0=n0, alpha=alpha, K=K, N=N) + self.title = f"bacteriaGrowth({n0}, {alpha}, {K}, {N}) = {g} ?" + self.assertEqualC(g) + + def test_growth1(self): + """ Hints: + * Make sure to frobulate the frobulator. + """ + self.stest(100, 0.4, 1000, 500) + + def test_growth2(self): + self.stest(10, 0.4, 1000, 500) + + def test_growth3(self): + self.stest(100, 1.4, 1000, 500) + + def test_growth4(self): + self.stest(100, 0.0004, 1000, 500) + + def test_growth5(self): + """ + hints: + * What happens when n0 > N? (in this case return t=0) """ + self.stest(100, 0.4, 1000, 99) + +class ClusterAnalysis(UTestCase): + """ Test the cluster analysis method """ + + def stest(self, n, seed): + np.random.seed(seed) + x = np.round(np.random.rand(n), 1) + I = clusterAnalysis(x) + self.title = f"clusterAnalysis({list(x)}) = {list(I)} ?" + self.assertEqualC(list(I)) + + def test_cluster1(self): + """ Hints: + * Make sure to frobulate the frobulator. + * Just try harder + """ + self.stest(3, 10) + + def test_cluster2(self): + self.stest(4, 146) + + def test_cluster3(self): + self.stest(5, 12) + + def test_cluster4(self): + """ + Cluster analysis for tied lists + Hints: + * It may be that an observations has the same distance to the two clusters. Where do you assign it in this case? + """ + x = np.array([10.0, 12.0, 10.0, 12.0, 9.0, 11.0, 11.0, 13.0]) + self.assertEqualC(list(clusterAnalysis(x) ) ) + + +class RemoveIncomplete(UTestCase): + """ Remove incomplete IDs """ + + def stest(self, x): + I = list( removeIncomplete(x) ) + self.title = f"removeId({trlist(x)}) = {trlist(I)} ?" + self.assertEqualC(I) + + @cache + def rseq(self, max, n): + np.random.seed(42) + return np.random.randint(max, size=(n,) ) + (np.random.randint(2, size=(n,) )+1)/10 + + def test_incomplete1(self): + self.stest( np.array([1.3, 2.2, 2.3, 4.2, 5.1, 3.2, 5.3, 3.3, 2.1, 1.1, 5.2, 3.1]) ) + + def test_incomplete2(self): + self.stest( np.array([1.1, 1.2, 1.3, 2.1, 2.2, 2.3]) ) + + def test_incomplete3(self): + self.stest(np.array([5.1, 5.2, 4.1, 4.3, 4.2, 8.1, 8.2, 8.3]) ) + + def test_incomplete4(self): + self.stest(np.array([1.1, 1.3, 2.1, 2.2, 3.1, 3.3, 4.1, 4.2, 4.3]) ) + + def test_incomplete5(self): + self.stest(self.rseq(10, 40)) + + +class FermentationRate(UTestCase): + """ Test the fermentation rate question """ + + def stest(self, x, lower, upper): + I = fermentationRate(x, lower, upper) + s = trlist(x) + self.title = f"fermentationRate({s}, {lower}, {upper}) = {I:.3f} ?" + self.assertEqualC(I) + + @cache + def rseq(self, max, n): + np.random.seed(42) + return np.random.randint(max, size=(n,) ) + (np.random.randint(3, size=(n,) )+1)/n + + def test_rate1(self): + self.stest(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 15, 25) + + def test_rate2(self): + self.stest(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 1, 200) + + def test_rate3(self): + self.stest(np.array([1.75]), 1, 2) + + def test_rate4(self): + self.stest(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 18.2, 20) + + +class Report1Flat(Report): + title = "Week 4: Looping" + questions = [(ClusterAnalysis, 10), (RemoveIncomplete, 10), (Bacteria, 10), (FermentationRate, 10),] + pack_imports = [looping] + +if __name__ == "__main__": + # Uncomment to simply run everything as a unittest: + # unittest.main(verbosity=2) + evaluate_report_student(Report1Flat()) diff --git a/examples/02631/instructor/programs/report1intro_grade.py b/examples/02631/instructor/programs/report1intro_grade.py new file mode 100644 index 0000000..4381d55 --- /dev/null +++ b/examples/02631/instructor/programs/report1intro_grade.py @@ -0,0 +1,337 @@ + +import numpy as np +from tabulate import tabulate +from datetime import datetime +import pyfiglet +import unittest +import inspect +import os +import argparse +import time + +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: +To run all tests in a report: + +> python assignment1_dp.py + +To run only question 2 or question 2.1 + +> python assignment1_dp.py -q 2 +> python assignment1_dp.py -q 2.1 + +Note this scripts does not grade your report. To grade your report, use: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('-q', nargs='?', type=str, default=None, help='Only evaluate this question (e.g.: -q 2)') +parser.add_argument('--showexpected', action="store_true", help='Show the expected/desired result') +parser.add_argument('--showcomputed', action="store_true", help='Show the answer your code computes') +parser.add_argument('--unmute', action="store_true", help='Show result of print(...) commands in code') +parser.add_argument('--passall', action="store_true", help='Automatically pass all tests. Useful when debugging.') + +def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False): + args = parser.parse_args() + if question is None and args.q is not None: + question = args.q + if "." in question: + question, qitem = [int(v) for v in question.split(".")] + else: + question = int(question) + + if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file: + raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation") + + if unmute is None: + unmute = args.unmute + if passall is None: + passall = args.passall + + results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute, + show_tol_err=show_tol_err) + + + if question is None: + print("Provisional evaluation") + tabulate(table_data) + table = table_data + print(tabulate(table)) + print(" ") + + fr = inspect.getouterframes(inspect.currentframe())[1].filename + gfile = os.path.basename(fr)[:-3] + "_grade.py" + if os.path.exists(gfile): + print("Note your results have not yet been registered. \nTo register your results, please run the file:") + print(">>>", gfile) + print("In the same manner as you ran this file.") + + + return results + + +def upack(q): + # h = zip([(i['w'], i['possible'], i['obtained']) for i in q.values()]) + h =[(i['w'], i['possible'], i['obtained']) for i in q.values()] + h = np.asarray(h) + return h[:,0], h[:,1], h[:,2], + +class UnitgradeTextRunner(unittest.TextTestRunner): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + +class SequentialTestLoader(unittest.TestLoader): + def getTestCaseNames(self, testCaseClass): + test_names = super().getTestCaseNames(testCaseClass) + # testcase_methods = list(testCaseClass.__dict__.keys()) + ls = [] + for C in testCaseClass.mro(): + if issubclass(C, unittest.TestCase): + ls = list(C.__dict__.keys()) + ls + testcase_methods = ls + test_names.sort(key=testcase_methods.index) + return test_names + +def evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False, + show_progress_bar=True, + show_tol_err=False, + big_header=True): + + now = datetime.now() + if big_header: + ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom") + b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) + else: + b = "Unitgrade" + dt_string = now.strftime("%d/%m/%Y %H:%M:%S") + print(b + " v" + __version__ + ", started: " + dt_string+ "\n") + # print("Started: " + dt_string) + s = report.title + if hasattr(report, "version") and report.version is not None: + s += " version " + report.version + print(s, "(use --help for options)" if show_help_flag else "") + # print(f"Loaded answers from: ", report.computed_answers_file, "\n") + table_data = [] + # nL = + t_start = time.time() + score = {} + loader = SequentialTestLoader() + + for n, (q, w) in enumerate(report.questions): + if question is not None and n+1 != question: + continue + suite = loader.loadTestsFromTestCase(q) + qtitle = q.question_title() if hasattr(q, 'question_title') else q.__qualname__ + q_title_print = "Question %i: %s"%(n+1, qtitle) + print(q_title_print, end="") + q.possible = 0 + q.obtained = 0 + q_ = {} # Gather score in this class. + UTextResult.q_title_print = q_title_print # Hacky + UTextResult.show_progress_bar = show_progress_bar # Hacky. + UTextResult.number = n + UTextResult.nL = report.nL + + res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) + + possible = res.testsRun + obtained = len(res.successes) + + assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun + + obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 + score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} + q.obtained = obtained + q.possible = possible + + s1 = f"Question {n+1} total" + s2 = f" {q.obtained}/{w}" + print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 ) + print(" ") + table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"]) + + ws, possible, obtained = upack(score) + possible = int( msum(possible) ) + obtained = int( msum(obtained) ) # Cast to python int + report.possible = possible + report.obtained = obtained + now = datetime.now() + dt_string = now.strftime("%H:%M:%S") + + dt = int(time.time()-t_start) + minutes = dt//60 + seconds = dt - minutes*60 + plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") + + dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", + last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) + + # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total") + + table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) + results = {'total': (obtained, possible), 'details': score} + return results, table_data + + +from tabulate import tabulate +from datetime import datetime +import inspect +import json +import os +import bz2 +import pickle +import os + +def bzwrite(json_str, token): # to get around obfuscation issues + with getattr(bz2, 'open')(token, "wt") as f: + f.write(json_str) + +def gather_imports(imp): + resources = {} + m = imp + # for m in pack_imports: + # print(f"*** {m.__name__}") + f = m.__file__ + # dn = os.path.dirname(f) + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = str(__import__(m.__name__.split('.')[0]).__path__) + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: + top_package = os.path.dirname(m.__file__) + module_import = True + else: + top_package = __import__(m.__name__.split('.')[0]).__path__._path[0] + module_import = False + + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = os.path.dirname(top_package) + import zipfile + # import strea + # zipfile.ZipFile + import io + # file_like_object = io.BytesIO(my_zip_data) + zip_buffer = io.BytesIO() + with zipfile.ZipFile(zip_buffer, 'w') as zip: + # zip.write() + for root, dirs, files in os.walk(top_package): + for file in files: + if file.endswith(".py"): + fpath = os.path.join(root, file) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) + zip.write(fpath, v) + + resources['zipfile'] = zip_buffer.getvalue() + resources['top_package'] = top_package + resources['module_import'] = module_import + return resources, top_package + + if f.endswith("__init__.py"): + for root, dirs, files in os.walk(os.path.dirname(f)): + for file in files: + if file.endswith(".py"): + # print(file) + # print() + v = os.path.relpath(os.path.join(root, file), top_package) + with open(os.path.join(root, file), 'r') as ff: + resources[v] = ff.read() + else: + v = os.path.relpath(f, top_package) + with open(f, 'r') as ff: + resources[v] = ff.read() + return resources + +import argparse +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Use this script to get the score of your report. Example: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('--noprogress', action="store_true", help='Disable progress bars') +parser.add_argument('--autolab', action="store_true", help='Show Autolab results') + +def gather_upload_to_campusnet(report, output_dir=None): + n = report.nL + args = parser.parse_args() + results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, + show_progress_bar=not args.noprogress, + big_header=not args.autolab) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) + # also load the source code of missing files... + + sources = {} + print("") + if not args.autolab: + if len(report.individual_imports) > 0: + print("By uploading the .token file, you verify the files:") + for m in report.individual_imports: + print(">", m.__file__) + print("Are created/modified individually by you in agreement with DTUs exam rules") + report.pack_imports += report.individual_imports + + if len(report.pack_imports) > 0: + print("Including files in upload...") + for k, m in enumerate(report.pack_imports): + nimp, top_package = gather_imports(m) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import + nimp['name'] = m.__name__ + sources[k] = nimp + # if len([k for k in nimp if k not in sources]) > 0: + print(f" * {m.__name__}") + # sources = {**sources, **nimp} + results['sources'] = sources + + if output_dir is None: + output_dir = os.getcwd() + + payload_out_base = report.__class__.__name__ + "_handin" + + obtain, possible = results['total'] + vstring = "_v"+report.version if report.version is not None else "" + + token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) + token = os.path.join(output_dir, token) + with open(token, 'wb') as f: + pickle.dump(results, f) + + if not args.autolab: + print(" ") + print("To get credit for your results, please upload the single unmodified file: ") + print(">", token) + # print("To campusnet without any modifications.") + + # print("Now time for some autolab fun") + +def source_instantiate(name, report1_source, payload): + eval("exec")(report1_source, globals()) + pl = pickle.loads(bytes.fromhex(payload)) + report = eval(name)(payload=pl, strict=True) + # report.set_payload(pl) + return report + + + +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n"""\nimport numpy as np\nimport sys\nimport re\nimport threading\nimport tqdm\nimport pickle\nimport os\nfrom io import StringIO\nimport io\nfrom unittest.runner import _WritelnDecorator\nfrom typing import Any\nimport inspect\nimport textwrap\nimport colorama\nfrom colorama import Fore\nfrom functools import _make_key, RLock\nfrom collections import namedtuple\nimport unittest\nimport time\n\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ncolorama.init(autoreset=True) # auto resets your settings after every output\n\ndef gprint(s):\n print(f"{Fore.GREEN}{s}")\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\n\ndef setup_dir_by_class(C, base_dir):\n name = C.__class__.__name__\n return base_dir, name\n\n\nclass Logger(object):\n def __init__(self, buffer):\n assert False\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\n# @classmethod\n# class OrderedClassMembers(type):\n# def __prepare__(self, name, bases):\n# assert False\n# return collections.OrderedDict()\n#\n# def __new__(self, name, bases, classdict):\n# ks = list(classdict.keys())\n# for b in bases:\n# ks += b.__ordered__\n# classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n# return type.__new__(self, name, bases, classdict)\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 120 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q, _) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q, _) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n loader = unittest.TestLoader()\n for q, _ in self.questions:\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self, with_coverage=False):\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = True\n q._report = self\n\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\': True}\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = False\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct + 1)\n if i > 0 and l.find("|", i + 1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = r\'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar", show_progress_bar=True, file=None):\n if file == None:\n file = sys.stdout\n self.file = file\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.01\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n self.pbar = None\n\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar = None\n\n self.file.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=self.file, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\')\n\n for _ in range(self.n - 1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\ndef dprint(first, last, nL, extra = "", file=None, dotsym=\'.\', color=\'white\'):\n if file == None:\n file = sys.stdout\n\n # ss = self.item_title_print\n # state = "PASS" if success else "FAILED"\n dot_parts = (dotsym * max(0, nL - len(last) - len(first)))\n # if self.show_progress_bar or True:\n print(first + dot_parts, end="", file=file)\n # else:\n # print(dot_parts, end="", file=self.cc.file)\n last += extra\n # if tsecs >= 0.5:\n # state += " (" + str(tsecs) + " seconds)"\n print(last, file=file)\n\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n cc = None\n\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n self.successes.append(test)\n self.cc_terminate()\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n self.cc.file.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="", file=self.cc.file)\n else:\n print(dot_parts, end="", file=self.cc.file)\n\n if tsecs >= 0.5:\n state += " (" + str(tsecs) + " seconds)"\n print(state, file=self.cc.file)\n\n def startTest(self, test):\n # j =self.testsRun\n self.testsRun += 1\n # item_title = self.getDescription(test)\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n self.item_title_print = " * q%i.%i) %s" % (UTextResult.number + 1, self.testsRun, item_title)\n estimated_time = 10\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar, file=sys.stdout)\n else:\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n self._stdout = sys.stdout\n sys.stdout = io.StringIO()\n\n def stopTest(self, test):\n sys.stdout = self._stdout\n super().stopTest(test)\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass is None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n print(" " * max(0, self.nL - len(self.cc.title)) + (" (" + str(q_time) + " seconds)" if q_time >= 0.5 else ""))\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n stream = io.StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)))\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n\n return wrapper\n\n\ndef get_hints(ss):\n if ss == None:\n return None\n try:\n ss = textwrap.dedent(ss)\n ss = ss.replace(\'\'\'"""\'\'\', "").strip()\n hints = ["hints:", ]\n j = np.argmax([ss.lower().find(h) for h in hints])\n h = hints[j]\n ss = ss[ss.find(h) + len(h) + 1:]\n ss = "\\n".join([l for l in ss.split("\\n") if not l.strip().startswith(":")])\n ss = textwrap.dedent(ss)\n ss = ss.strip()\n return ss\n except Exception as e:\n print("bad hints", ss, e)\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n _with_coverage = False\n _report = None # The report used. This is very, very hacky and should always be None. Don\'t rely on it!\n\n def capture(self):\n return Capturing2(stdout=self._stdout)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ is not None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n if self._with_coverage:\n if not hasattr(self._report, \'covcache\'):\n self._report.covcache = {}\n import coverage\n self.cov = coverage.Coverage()\n self.cov.start()\n self.setUp()\n\n def _callTearDown(self):\n self.tearDown()\n if self._with_coverage:\n from pathlib import Path\n from snipper import snipper\n self.cov.stop()\n data = self.cov.get_data()\n base, _, _ = self._report._import_base_relative()\n for file in data.measured_files():\n file = os.path.normpath(file)\n root = Path(base)\n child = Path(file)\n if root in child.parents:\n with open(child, \'r\') as f:\n s = f.read()\n lines = s.splitlines()\n garb = \'GARBAGE\'\n\n lines2 = snipper.censor_code(lines, keep=True)\n assert len(lines) == len(lines2)\n\n for l in data.contexts_by_lineno(file):\n if lines2[l].strip() == garb:\n if self.cache_id() not in self._report.covcache:\n self._report.covcache[self.cache_id()] = {}\n\n rel = os.path.relpath(child, root)\n cc = self._report.covcache[self.cache_id()]\n j = 0\n for j in range(l, -1, -1):\n if "def" in lines2[j] or "class" in lines2[j]:\n break\n from snipper.snipper import gcoms\n fun = lines2[j]\n comments, _ = gcoms("\\n".join(lines2[j:l]))\n if rel not in cc:\n cc[rel] = {}\n cc[rel][fun] = (l, "\\n".join(comments))\n self._cache_put((self.cache_id(), \'coverage\'), self._report.covcache)\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd is None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n sd = self.shortDescriptionStandard()\n title = self._cache_get((self.cache_id(), \'title\'), sd)\n return title if title is not None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome is None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc is not None:\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard())\n\n self._cache2[(self.cache_id(), \'assert\')] = {}\n res = testMethod()\n elapsed = time.time() - t\n self._get_outcome()[self.cache_id()] = res\n self._cache_put((self.cache_id(), "time"), elapsed)\n\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return c, m\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n # sys.stdout = self._stdout\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n self.__class__._cache[\n key] = {} # A new dict. We manually insert it because we have to use that the dict is mutable.\n cache = self._cache_get(key)\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, f"Key {id} not found in cache; framework files missing. Please run deploy()")\n\n # The order of these calls is important. If the method assert fails, we should still store the correct result in cache.\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n assert_fun(first, _expected, *args, **kwargs)\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache is not None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n if os.path.exists(cfile):\n try:\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n except Exception as e:\n print("Bad cache", cfile)\n print(e)\n else:\n print("Warning! data file not found", cfile)\n\n def _feedErrorsToResult(self, result, errors):\n """ Use this to show hints on test failure. """\n if not isinstance(result, UTextResult):\n er = [e for e, v in errors if v != None]\n\n if len(er) > 0:\n hints = []\n key = (self.cache_id(), \'coverage\')\n if self._cache_contains(key):\n CC = self._cache_get(key)\n for id in CC:\n if id == self.cache_id():\n cl, m = id\n gprint(f"> An error occured while solving: {cl}.{m}. The files/methods you need to edit are:") # For the test {id} in {file} you should edit:")\n for file in CC[id]:\n rec = CC[id][file]\n gprint(f"> * {file}")\n for l in rec:\n _, comments = CC[id][file][l]\n hint = get_hints(comments)\n\n if hint != None:\n hints.append(hint)\n gprint(f"> - {l}")\n\n er = er[0]\n doc = er._testMethodDoc\n if doc is not None:\n hint = get_hints(er._testMethodDoc)\n if hint is not None:\n hints = [hint] + hints\n if len(hints) > 0:\n gprint("> Hints:")\n gprint(textwrap.indent("\\n".join(hints), "> "))\n\n super()._feedErrorsToResult(result, errors)\n\n def startTestRun(self):\n # print("asdfsdaf 11", file=sys.stderr)\n super().startTestRun()\n # print("asdfsdaf")\n\n def _callTestMethod(self, method):\n # print("asdfsdaf")\n super()._callTestMethod(method)\n\n\ndef hide(func):\n return func\n\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n# 817\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\nimport inspect\nimport os\nimport argparse\nimport time\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print(b + " v" + __version__ + ", started: " + dt_string+ "\\n")\n # print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print(s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n # nL =\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n UTextResult.nL = report.nL\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"Question {n+1} total"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")",\n last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL)\n\n # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n # print(" ")\n # print("="*n)\n # print("Final evaluation")\n # print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n print("")\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f" * {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single unmodified file: ")\n print(">", token)\n # print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\nimport numpy as np\nimport looping\nfrom looping import bacteriaGrowth, clusterAnalysis, removeIncomplete, fermentationRate\n\ndef trlist(x):\n s = str(list(x))\n if len(s) > 30:\n s = s[:30] + "...]"\n return s\n\nclass Bacteria(UTestCase):\n """ Bacteria growth rates """\n\n def stest(self, n0, alpha, K, N):\n g = bacteriaGrowth(n0=n0, alpha=alpha, K=K, N=N)\n self.title = f"bacteriaGrowth({n0}, {alpha}, {K}, {N}) = {g} ?"\n self.assertEqualC(g)\n\n def test_growth1(self):\n """ Hints:\n * Make sure to frobulate the frobulator.\n """\n self.stest(100, 0.4, 1000, 500)\n\n def test_growth2(self):\n self.stest(10, 0.4, 1000, 500)\n\n def test_growth3(self):\n self.stest(100, 1.4, 1000, 500)\n\n def test_growth4(self):\n self.stest(100, 0.0004, 1000, 500)\n\n def test_growth5(self):\n """\n hints:\n * What happens when n0 > N? (in this case return t=0) """\n self.stest(100, 0.4, 1000, 99)\n\nclass ClusterAnalysis(UTestCase):\n """ Test the cluster analysis method """\n\n def stest(self, n, seed):\n np.random.seed(seed)\n x = np.round(np.random.rand(n), 1)\n I = clusterAnalysis(x)\n self.title = f"clusterAnalysis({list(x)}) = {list(I)} ?"\n self.assertEqualC(list(I))\n\n def test_cluster1(self):\n """ Hints:\n * Make sure to frobulate the frobulator.\n * Just try harder\n """\n self.stest(3, 10)\n\n def test_cluster2(self):\n self.stest(4, 146)\n\n def test_cluster3(self):\n self.stest(5, 12)\n\n def test_cluster4(self):\n """\n Cluster analysis for tied lists\n Hints:\n * It may be that an observations has the same distance to the two clusters. Where do you assign it in this case?\n """\n x = np.array([10.0, 12.0, 10.0, 12.0, 9.0, 11.0, 11.0, 13.0])\n self.assertEqualC(list(clusterAnalysis(x) ) )\n\n\nclass RemoveIncomplete(UTestCase):\n """ Remove incomplete IDs """\n\n def stest(self, x):\n I = list( removeIncomplete(x) )\n self.title = f"removeId({trlist(x)}) = {trlist(I)} ?"\n self.assertEqualC(I)\n\n @cache\n def rseq(self, max, n):\n np.random.seed(42)\n return np.random.randint(max, size=(n,) ) + (np.random.randint(2, size=(n,) )+1)/10\n\n def test_incomplete1(self):\n self.stest( np.array([1.3, 2.2, 2.3, 4.2, 5.1, 3.2, 5.3, 3.3, 2.1, 1.1, 5.2, 3.1]) )\n\n def test_incomplete2(self):\n self.stest( np.array([1.1, 1.2, 1.3, 2.1, 2.2, 2.3]) )\n\n def test_incomplete3(self):\n self.stest(np.array([5.1, 5.2, 4.1, 4.3, 4.2, 8.1, 8.2, 8.3]) )\n\n def test_incomplete4(self):\n self.stest(np.array([1.1, 1.3, 2.1, 2.2, 3.1, 3.3, 4.1, 4.2, 4.3]) )\n\n def test_incomplete5(self):\n self.stest(self.rseq(10, 40))\n\n\nclass FermentationRate(UTestCase):\n """ Test the fermentation rate question """\n\n def stest(self, x, lower, upper):\n I = fermentationRate(x, lower, upper)\n s = trlist(x)\n self.title = f"fermentationRate({s}, {lower}, {upper}) = {I:.3f} ?"\n self.assertEqualC(I)\n\n @cache\n def rseq(self, max, n):\n np.random.seed(42)\n return np.random.randint(max, size=(n,) ) + (np.random.randint(3, size=(n,) )+1)/n\n\n def test_rate1(self):\n self.stest(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 15, 25)\n\n def test_rate2(self):\n self.stest(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 1, 200)\n\n def test_rate3(self):\n self.stest(np.array([1.75]), 1, 2)\n\n def test_rate4(self):\n self.stest(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 18.2, 20)\n\n\nclass Report1Flat(Report):\n title = "Week 4: Looping"\n questions = [(ClusterAnalysis, 10), (RemoveIncomplete, 10), (Bacteria, 10), (FermentationRate, 10),]\n pack_imports = [looping]' +report1_payload = '80049592150000000000007d94288c0f436c7573746572416e616c79736973947d94288c0f436c7573746572416e616c79736973948c0d746573745f636c7573746572319486948c057469746c659486948c2e636c7573746572416e616c79736973285b302e382c20302e302c20302e365d29203d205b312c20322c20315d203f946803680486948c066173736572749486947d944b005d94288c156e756d70792e636f72652e6d756c74696172726179948c067363616c61729493948c056e756d7079948c0564747970659493948c02693494898887945294284b038c013c944e4e4e4affffffff4affffffff4b007494624304010000009486945294681068164304020000009486945294681068164304010000009486945294657368038c0d746573745f636c757374657232948694680686948c36636c7573746572416e616c79736973285b302e352c20302e362c20302e332c20302e335d29203d205b322c20322c20312c20315d203f94680368228694680a86947d944b005d9428681068164304020000009486945294681068164304020000009486945294681068164304010000009486945294681068164304010000009486945294657368038c0d746573745f636c757374657233948694680686948c3e636c7573746572416e616c79736973285b302e322c20302e372c20302e332c20302e352c20302e305d29203d205b312c20322c20312c20322c20315d203f94680368368694680a86947d944b005d9428681068164304010000009486945294681068164304020000009486945294681068164304010000009486945294681068164304020000009486945294681068164304010000009486945294657368038c0d746573745f636c757374657234948694680a86947d944b005d942868106816430401000000948694529468106816430402000000948694529468106816430401000000948694529468106816430402000000948694529468106816430401000000948694529468106816430401000000948694529468106816430401000000948694529468106816430402000000948694529465738c0474696d6594473fdf6c8500000000758c1052656d6f7665496e636f6d706c657465947d94288c1052656d6f7665496e636f6d706c657465948c10746573745f696e636f6d706c657465319486948c057469746c659486948c5372656d6f76654964285b312e332c20322e322c20322e332c20342e322c20352e312c20332e322c2e2e2e5d29203d205b322e322c20322e332c20352e312c20332e322c20352e332c20332e332c2e2e2e5d203f94686d686e86948c066173736572749486947d944b005d9428681068138c02663894898887945294284b0368174e4e4e4affffffff4affffffff4b0074946243089a9999999999014094869452946810687a4308666666666666024094869452946810687a4308666666666666144094869452946810687a43089a9999999999094094869452946810687a4308333333333333154094869452946810687a43086666666666660a4094869452946810687a4308cdcccccccccc004094869452946810687a4308cdcccccccccc144094869452946810687a4308cdcccccccccc084094869452946573686d8c10746573745f696e636f6d706c65746532948694687086948c4b72656d6f76654964285b312e312c20312e322c20312e332c20322e312c20322e322c20322e335d29203d205b312e312c20312e322c20312e332c20322e312c20322e322c20322e335d203f94686d68978694687486947d944b005d94286810687a43089a9999999999f13f94869452946810687a4308333333333333f33f94869452946810687a4308cdccccccccccf43f94869452946810687a4308cdcccccccccc004094869452946810687a43089a9999999999014094869452946810687a4308666666666666024094869452946573686d8c10746573745f696e636f6d706c65746533948694687086948c4f72656d6f76654964285b352e312c20352e322c20342e312c20342e332c20342e322c20382e312c2e2e2e5d29203d205b342e312c20342e332c20342e322c20382e312c20382e322c20382e335d203f94686d68b18694687486947d944b005d94286810687a4308666666666666104094869452946810687a4308333333333333114094869452946810687a4308cdcccccccccc104094869452946810687a4308333333333333204094869452946810687a4308666666666666204094869452946810687a43089a9999999999204094869452946573686d8c10746573745f696e636f6d706c65746534948694687086948c4072656d6f76654964285b312e312c20312e332c20322e312c20322e322c20332e312c20332e332c2e2e2e5d29203d205b342e312c20342e322c20342e335d203f94686d68cb8694687486947d944b005d94286810687a4308666666666666104094869452946810687a4308cdcccccccccc104094869452946810687a4308333333333333114094869452946573686d8c10746573745f696e636f6d706c657465359486948c06406361636865948c0472736571948c0966756e63746f6f6c73948c0a5f486173686564536571949394298194284b0a4b28654e7d948c096861736876616c7565948a0884d8ef03874d7f467386946287948694680e8c0c5f7265636f6e73747275637494939468118c076e6461727261799493944b0085944301629487945294284b014b28859468138c02663894898887945294284b0368174e4e4e4affffffff4affffffff4b0074946289424001000066666666666618409a99999999990940cdcccccccccc1c40cdcccccccccc1040cdcccccccccc184033333333333322409a999999999901406666666666661840cdcccccccccc1c40cdcccccccccc10409a999999999909406666666666661c40cdcccccccccc1c40cdcccccccccc0040cdcccccccccc14406666666666661040333333333333f33f6666666666661c406666666666661440333333333333f33f66666666666610409a9999999999c93f6666666666662240cdcccccccccc144066666666666620409a9999999999c93f66666666666622409a99999999990140cdcccccccccc18409a9999999999094066666666666620409a999999999901406666666666661040cdcccccccccc0040cdcccccccccc1840cdcccccccccc10406666666666662040cdcccccccccc1840333333333333f33f9a9999999999094094749462686d68dc8694687086948c5372656d6f76654964285b362e312c20332e322c20372e322c20342e322c20362e322c20392e312c2e2e2e5d29203d205b392e312c20352e322c20312e322c20352e312c20312e322c20392e322c2e2e2e5d203f94686d68dc8694687486947d944b005d94286810687a4308333333333333224094869452946810687a4308cdcccccccccc144094869452946810687a4308333333333333f33f94869452946810687a4308666666666666144094869452946810687a4308333333333333f33f94869452946810687a4308666666666666224094869452946810687a4308cdcccccccccc144094869452946810687a4308666666666666204094869452946810687a4308666666666666224094869452946810687a4308666666666666204094869452946810687a4308666666666666204094869452946810687a4308333333333333f33f94869452946573686a473fcf9dc400000000758c084261637465726961947d94288c084261637465726961948c0c746573745f67726f777468319486948c057469746c659486948c29626163746572696147726f777468283130302c20302e342c20313030302c2035303029203d2037203f946a250100006a2601000086948c066173736572749486947d944b004b07736a250100006a2601000086948c08636f7665726167659486947d94286a250100006a2601000086947d948c0a6c6f6f70696e672e7079947d948c2564656620626163746572696147726f777468286e302c20616c7068612c204b2c204e293a20944b158ce72222220a2020202043616c63756c6174652074696d6520756e74696c2062616374657269612067726f77746820657863656564204e207374617274696e672066726f6d206120706f70756c6174696f6e206f66206e302062616374657269612e0a2020202068696e74733a0a20202020202020202a20636f6e7369646572206e300a20202020202020202a20616c706861203e20300a202020203a706172616d206e303a0a202020203a706172616d20616c7068613a0a202020203a706172616d204b3a0a202020203a706172616d204e3a0a202020203a72657475726e3a0a2020202022222294869473736a250100008c0c746573745f67726f777468329486947d948c0a6c6f6f70696e672e7079947d948c2564656620626163746572696147726f777468286e302c20616c7068612c204b2c204e293a20944b158ce72222220a2020202043616c63756c6174652074696d6520756e74696c2062616374657269612067726f77746820657863656564204e207374617274696e672066726f6d206120706f70756c6174696f6e206f66206e302062616374657269612e0a2020202068696e74733a0a20202020202020202a20636f6e7369646572206e300a20202020202020202a20616c706861203e20300a202020203a706172616d206e303a0a202020203a706172616d20616c7068613a0a202020203a706172616d204b3a0a202020203a706172616d204e3a0a202020203a72657475726e3a0a2020202022222294869473736a250100008c0c746573745f67726f777468339486947d948c0a6c6f6f70696e672e7079947d948c2564656620626163746572696147726f777468286e302c20616c7068612c204b2c204e293a20944b158ce72222220a2020202043616c63756c6174652074696d6520756e74696c2062616374657269612067726f77746820657863656564204e207374617274696e672066726f6d206120706f70756c6174696f6e206f66206e302062616374657269612e0a2020202068696e74733a0a20202020202020202a20636f6e7369646572206e300a20202020202020202a20616c706861203e20300a202020203a706172616d206e303a0a202020203a706172616d20616c7068613a0a202020203a706172616d204b3a0a202020203a706172616d204e3a0a202020203a72657475726e3a0a2020202022222294869473736a250100008c0c746573745f67726f777468349486947d948c0a6c6f6f70696e672e7079947d948c2564656620626163746572696147726f777468286e302c20616c7068612c204b2c204e293a20944b158ce72222220a2020202043616c63756c6174652074696d6520756e74696c2062616374657269612067726f77746820657863656564204e207374617274696e672066726f6d206120706f70756c6174696f6e206f66206e302062616374657269612e0a2020202068696e74733a0a20202020202020202a20636f6e7369646572206e300a20202020202020202a20616c706861203e20300a202020203a706172616d206e303a0a202020203a706172616d20616c7068613a0a202020203a706172616d204b3a0a202020203a706172616d204e3a0a202020203a72657475726e3a0a2020202022222294869473736a250100008c0c746573745f67726f777468359486947d948c0a6c6f6f70696e672e7079947d948c2564656620626163746572696147726f777468286e302c20616c7068612c204b2c204e293a20944b118ce72222220a2020202043616c63756c6174652074696d6520756e74696c2062616374657269612067726f77746820657863656564204e207374617274696e672066726f6d206120706f70756c6174696f6e206f66206e302062616374657269612e0a2020202068696e74733a0a20202020202020202a20636f6e7369646572206e300a20202020202020202a20616c706861203e20300a202020203a706172616d206e303a0a202020203a706172616d20616c7068613a0a202020203a706172616d204b3a0a202020203a706172616d204e3a0a202020203a72657475726e3a0a202020202222229486947373756a250100006a3a01000086946a2801000086948c29626163746572696147726f7774682831302c20302e342c20313030302c2035303029203d203134203f946a250100006a3a01000086946a2c01000086947d944b004b0e736a250100006a3a01000086946a3001000086946a320100006a250100006a4201000086946a2801000086948c29626163746572696147726f777468283130302c20312e342c20313030302c2035303029203d2033203f946a250100006a4201000086946a2c01000086947d944b004b03736a250100006a4201000086946a3001000086946a320100006a250100006a4a01000086946a2801000086948c2f626163746572696147726f777468283130302c20302e303030342c20313030302c2035303029203d2035343934203f946a250100006a4a01000086946a2c01000086947d944b004d7615736a250100006a4a01000086946a3001000086946a320100006a250100006a5201000086946a2801000086948c28626163746572696147726f777468283130302c20302e342c20313030302c20393929203d2030203f946a250100006a5201000086946a2c01000086947d944b004b00736a250100006a5201000086946a3001000086946a32010000686a473fcf9d9a00000000758c104665726d656e746174696f6e52617465947d94288c104665726d656e746174696f6e52617465948c0a746573745f72617465319486948c057469746c659486948c476665726d656e746174696f6e52617465285b32302e312c2031392e332c20312e312c2031382e322c2031392e372c202e2e2e5d2c2031352c20323529203d2031392e363030203f946a7c0100006a7d01000086948c066173736572749486947d944b006810687a43089a999999999933409486945294736a7c0100008c0a746573745f72617465329486946a7f01000086948c476665726d656e746174696f6e52617465285b32302e312c2031392e332c20312e312c2031382e322c2031392e372c202e2e2e5d2c20312c2032303029203d2032392e393735203f946a7c0100006a8901000086946a8301000086947d944b006810687a43089899999999f93d409486945294736a7c0100008c0a746573745f72617465339486946a7f01000086948c286665726d656e746174696f6e52617465285b312e37355d2c20312c203229203d20312e373530203f946a7c0100006a9301000086946a8301000086947d944b006810687a4308000000000000fc3f9486945294736a7c0100008c0a746573745f72617465349486946a7f01000086948c496665726d656e746174696f6e52617465285b32302e312c2031392e332c20312e312c2031382e322c2031392e372c202e2e2e5d2c2031382e322c20323029203d2031392e353030203f946a7c0100006a9d01000086946a8301000086947d944b006810687a43080000000000803340948694529473686a473fc74c0a0000000075752e' +name="Report1Flat" + +report = source_instantiate(name, report1_source, report1_payload) +output_dir = os.path.dirname(__file__) +gather_upload_to_campusnet(report, output_dir) \ No newline at end of file diff --git a/examples/02631/instructor/programs/unitgrade/Bacteria.pkl b/examples/02631/instructor/programs/unitgrade/Bacteria.pkl new file mode 100644 index 0000000000000000000000000000000000000000..b246df45c63387d95bc69cc26deba7770d8249c5 GIT binary patch literal 2050 zcmZo*nfjfb0Ss!VX!LM6B_@}o7G)+*>ES6!EiQ>qFUl`3$uOMKHl>HPB(o$Z6~xj^ zf~s-{%W4=J80aV%=$Ys!7#e_>rUnL@3bqR73ieYn7&DmKru48S78j=$l}u@yQai<) z!JEApD$bFdUzS>wm<}?wc8Uf>w024lS5AI@L1tdMUP0xQ+9^G%DXD1+XqM&~=qM!S z6l5goD0u5A_-R@xOz{@&d9I|S#HFC1px~UClU$mUSdywxl9`*TP?}egnFBXM0pcx% z)QaTP)D#6jh2oOLqLR$KbcM8{{9J`Zg@XJ7kmk(%Jcax;g**eePCc;U8JT${#a3V@ z$Sy5~<ovwi%#_q3g**c!QLytA>=YnkRt1SgiMa}S1`v%<E?5nUh&Kw)56UY_Eh#O^ zgK(6Tl%}*zDK5@nM2RIMaPW?Xoq_^1Y)J?^<I%7qJKdR#h8@{qXG&4n368Att~7%& zL#%B|220zN9?UujTNPwz0;z*Qx;UVf4<D%V0SWN5P08TPV9b!iX{3RUf+0q=W(+YB zqzh&wGssAg0L(}=sFC_;l@_$7G%zqguPjYXETL`%>4h5XTP6xJ86*NTSr2Nm26mI- c##&l}s$c_%TS1zjMtU=VOauvlO)S*|044x_qW}N^ literal 0 HcmV?d00001 diff --git a/examples/02631/instructor/programs/unitgrade/ClusterAnalysis.pkl b/examples/02631/instructor/programs/unitgrade/ClusterAnalysis.pkl new file mode 100644 index 0000000000000000000000000000000000000000..36635371f6e86d5f841a3827696a5909e766532b GIT binary patch literal 763 zcmZo*nfif=0Ss!VX!P(q=ad$gq!u~mCFWEXXBJQC;VnrmE{RWu2pdjmo6^Htl39|I z3S#L&g<+~Sq7C#cbQBEq3_yfgtfqpkLbRcdf{~7bVXT7vlnllUrnV_PY>CCisYNAI z+NRV_@n(pfqR}IoSDIT;sh6Bzl&Y6onp2XQSX7i)Ii-iKI5{yVv1rQVDLt%UB~yA> zQ%WieQb8h2nI=;@JKCoNP0{dX?qRf<;^*h*_5c6>|6szKp=3&uGYcaF1H+WIDM3>* z_%cMCSwLPz;=*N9i!&Hey=pY2ZAu19+ms$NES@w4`wv7Ig9(UFjUYZncvhipN(Kkq zmsnkZ%Rvyuc-&_UbDtd+_Zfj)Wey_1?gKm50OCk!bU>U6jT93s?u1(naS48R;*!M- aRuho3(Zhr)c0t^UW)vD1?#|TWQau35`|pDQ literal 0 HcmV?d00001 diff --git a/examples/02631/instructor/programs/unitgrade/FermentationRate.pkl b/examples/02631/instructor/programs/unitgrade/FermentationRate.pkl new file mode 100644 index 0000000000000000000000000000000000000000..9f5cea13c96d67ff2704de398cd66b78a2db463b GIT binary patch literal 618 zcmZo*nHtZ;00y;FG<pQwQj2m^^GXs+GV}9-5=&C2^l+7=7MH{qC6=TbPHCIc!&;JA zl9LKzxu>D2)rdAS&@<FgFtpS&)=@A7GcELtzyjtv3VM2au{sKdraB5nrkV=2AZ2C- z1`765G8i+M+NSidB^DQ_7L`nCn^HT)o1sTEuQa!yQZG5bC{-`FG^Zppv8X7qa!L<d zadKi#V$qbzQ+imzN~ZL%rj%3`q=H14(k!NQcC=3knxf&&+{0)y#m~>r>;M1%|G|Ve zL&=mRXO3AjLBQByO52p6Da9F#NM1CW(l#Z7rEN+NnV!>8FfuRzdCtgE&(ho!;yKB- zDH$9PUuE!S2s?Ak0NMZ37MlZ%VGhv13|&J#b5l?V>L?h2tN?ML!KaLF12{q%{@7#j jq6y3fPfQz#i9c|R8i74&XsKrki$PtiPH2FIa;Y8w7Y@sc literal 0 HcmV?d00001 diff --git a/examples/02631/instructor/programs/unitgrade/RemoveIncomplete.pkl b/examples/02631/instructor/programs/unitgrade/RemoveIncomplete.pkl new file mode 100644 index 0000000000000000000000000000000000000000..30edf2db7bc0e41f0ada9c1c7443ac1d487a6d9a GIT binary patch literal 1444 zcmZo*nL3k|0Ss!VX!HmKrRL_BrF!Ni=jRsWq?V*k=@BSNEiQ@Ago_wXX`9l+T9R3k zlL}%57eRESXha+88S5w*=^23tV;uz(5X)50P)EU7&qzm4PcK$e!B!y}p#UzYU<%S{ ztY-{XrC>iLgE51tZAuSYVsUY5QOT6HDYa9)8Dghs^oZt_<`z`yCFd8V>gAT^lw>9r z6(v?q>0v8QPRvOxnlgDx4=Y&7lpfZUlFEWqkO)(n#gxvD_9;PAG`yL67;UEb`T2SM z|Ns9#nDAyOnUdtpF>59WFgi?Wn-Vl7gD*qGnIjDhm@tJzkcA)`Igy2o!9WyQ2y9du z7qZaVGa$f#DTHD?L=^`_RcdhtBSwT6O=+8w!O}LR#~V2+40RL?K@k9r6hly?Kw<_G z4ThkI!KDHc1x9UCGC1I24+#^9Z$H{21ryk_#-EXeAddcmDTLx%h*`LU9IJ1QVZQZ8 z_!gXyOhLXi0r|=Vl$yY)&qB`-oTR`B4~v|F1xS;Ho^dS1$39qm42d}bNCZFv0pepp zWFd%;v8Yl&Rs}H&MF<iIP$8^7Hi7xr0pVk4?1A$eENYEGeurfuWFH%W@*>QOX~cOE zr+=Wn#_Cd2m`jlg1v7}NjC2&tL17J!7Bdjb5;<-_90hO;f|DdTLcuIcXwd<QpK>gY zheU=Faxg<2kCKfbs!$RsBr=FoMaX!FS<qO7Boc@aibjYK7F8%hC{9f+F4Y47-%{y- literal 0 HcmV?d00001 diff --git a/examples/02631/students/programs/.coverage b/examples/02631/students/programs/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..11b2ec620c9b142ae8ee79c7a6f5afa297121530 GIT binary patch literal 53248 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 literal 0 HcmV?d00001 diff --git a/examples/02631/students/programs/__pycache__/looping.cpython-38.pyc b/examples/02631/students/programs/__pycache__/looping.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fa302e5a102c505ba4b62aa5eab3205ca2683298 GIT binary patch literal 2019 zcmWIL<>g{vU|@KAPd`zBoq^#oh=YuI7#J8F7#J9elNcBnQW#Pga~Pr+!8B78QwmcG za|=Tha|%leYYRgZOA1>GdkaGpYYImSXA46VTPk}BS2j~oTq;K@YbxUc&J^wxo)q2` zz7+lxffT_Mp>&Rgj48q?BF)UrjEoE^jKK_=qAx+V`z5o0C@5xSU|?WlU|?_txx<Zt zfuV+>hB=$1hzF#jgkb^WLIxLxSdChi6s8)+6s8)68YXE52?h}cafTWO5r$frdJ#s3 zLc2nd8kQ6$afV<9P3EeHTnY*b3eJf+$)!1oC8-J}nYpP7rFkWpISNUM$t9^pnTZPN zMfv3=849Tt$*HL+3VsU3C5c5PnR)37X+`<D3W*8@`2`@&nfZAN`DqGy25_BvV8b&q z^Gb@Xz)X-`S_;YedBvG2sYMET21ufbIRzPs3U&$*F{^^aqQqQ<JOhYEC>N{-MZ_D0 z=Lh8#rIwTy<v}<_j0_A6FF}MR(=E=-lGLJ-{QR8aTdc|XrFkWqthbo*3~sT4O}@qG zeT&iW7Guduh9Xd+_!aJK6%$&VT2vfUQksz(<C33Tnwy$eQXEs7msyftl$eqlUr>}; zmROP+lUk9OTac4l9AjW)W^5RfnO9s=RGM6pUldbNl%HOdm|Gl^lb>IZnU}6tP<e|F z7P#)<um#0+F&`+hc{mtZ7<m{a7&#c(7>ht@FB#-?kQ4}m6mq~4!VCrm22kYJFf3%O z1+kb)m}?lC85c69Fp4pxFl95<GJ@nw7#6V9Fw`*eGe|Nl1c`uoAa**_LdIIA5{4|+ z5{3nAHK63p&<f(Sr!h$~)PPwj3@Hq)Od!4l!$L+-g70Mlo6TIpQNvurn9bD8SkzTG zp@zAZu}}e&{J=?z#qT8(0|P@5h|pxb#gbT&S**!<iz%<*7JFiGVo_0I<t?_vqQt!P z)LYCssd=|pa#IuYKtk!cnR&OE6O)Q>vFD|hgY?~EPRz;CWQpP~N=?g2O)g2yOHPeq z@-)1~m>b3HX&4WVEC|8Pz`$^eKRKtgxFog6F)uNvvN*F?0+ek8K;g&8#VE$4!f3(B z!^i{*U`YlBhGb@t3>1Udpxg$ci>06e+|JO>n8uXCn8MV;QN!Q@4q{O5@k5ClkWWEc zg+aDyFfcHrGt@A|D%3J|Fw`)zFfcP@GZe{mFlI9pvE;EZG8D4ZFivDDWC@0(HL&wF znQk%Z8QfwlTFG>aD<{7^wa6*IG%w{AS7|{3lnqu_1okGEO-^QUNpgN}ft@PIPEe9% zV_;%nV&G!nVW?6`ODzJW*2I#`{JfyVl2kpLoc!d(oMJmYghoxKB7RUbfgG*L9L1BH znpj*~l$rukUnC2XkpmGRql;ufEU?W2=te7p><0w^15=R#C`^$PEhsEO0Ry6o{h(nA zNwnz<wTvZ9phWA!5UUfzRLj)KP{Q2Kkj97{+Rcn9%-KvuVl|8lSUMOMGBPq0ih&Y# z4dX&kiOEuBr{IAoWV94Q60=hk5*2dt^RpEaOB6~nQWc=tQz0`C%1tdw&MZ!alu)>% z5#$$eG)jQ7QVnA>V+lhCLo;JGQxR(PX)@ko&de*(WGVtBnp=z&D;aMwmLVdmNS=X# z!4MQmpmH1(O9Bj4Dn+Te`DLk|d7wDVNyQ#RFG0bk$qI=kP$Is?nVMIcn_84ul3FAL zO73il1qG>jDVof;m@-qMm@`w1ZZT#;tOmskB(Vsf+oc6^45)x%VBun9VH9ALV&q{g z0*T(@h>y=r%*>0APoFh&=FFLrCJyOm&zw1P#>dzJA~N%%eY!Ci7&$-~n~fdP(?Gy2 z4yM?|Asv#bOdMW<(%4H-X4B-l#hO=|TTlt{56ExeQmzOj2~Ik2C*&pOrpCwLVl61j w%qzLY3NA7rz5=I4Fab`D95%W6DWy57cA!KDE=D;RIhZ&^IJi03Ie0i&0HrYEG5`Po literal 0 HcmV?d00001 diff --git a/examples/02631/students/programs/__pycache__/report1intro.cpython-38.pyc b/examples/02631/students/programs/__pycache__/report1intro.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ddadca6243eeebd021c63d8d875e9092033e1190 GIT binary patch literal 7198 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 D4<0BP literal 0 HcmV?d00001 diff --git a/examples/02631/students/programs/looping.py b/examples/02631/students/programs/looping.py new file mode 100644 index 0000000..34517b1 --- /dev/null +++ b/examples/02631/students/programs/looping.py @@ -0,0 +1,62 @@ +""" +Example student code. This file is automatically generated from the files in the instructor-directory +""" +import numpy as np +import itertools + +def bacteriaGrowth(n0, alpha, K, N): + """ + Calculate time until bacteria growth exceed N starting from a population of n0 bacteria. + hints: + * consider n0 + * alpha > 0 + :param n0: + :param alpha: + :param K: + :param N: + :return: + """ + # TODO: 7 lines missing. + raise NotImplementedError("Implement function body") + +def clusterAnalysis(reflectance): + reflectance = np.asarray(reflectance) + I1 = np.arange(len(reflectance)) % 2 == 1 + while True: + m = np.asarray( [np.mean( reflectance[~I1] ), np.mean( reflectance[I1] ) ] ) + I1_ = np.argmin( np.abs( reflectance[:, np.newaxis] - m[np.newaxis, :] ), axis=1) == 1 + if all(I1_ == I1): + break + I1 = I1_ + return I1 + 1 + +def fermentationRate(measuredRate, lowerBound, upperBound): + # Insert your code here + return np.mean( [r for r in measuredRate if lowerBound < r < upperBound] ) + + + + +def removeIncomplete(id): + """ Hints: + * Take a look at the example in the exercise. + """ + id = np.asarray(id) + id2 = [] + for i, v in enumerate(id): + if len( [x for x in id if int(x) == int(v) ] ) == 3: + id2.append(v) + return np.asarray(id2) + + +if __name__ == "__main__": + # I = clusterAnalysis([1.7, 1.6, 1.3, 1.3, 2.8, 1.4, 2.8, 2.6, 1.6, 2.7]) + # print(I) + + print(fermentationRate(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 15, 25)) + + + # print(removeIncomplete(np.array([1.3, 2.2, 2.3, 4.2, 5.1, 3.2, 5.3, 3.3, 2.1, 1.1, 5.2, 3.1]))) + + # Problem 1: Write a function which add two numbers + # clusterAnalysis([2, 1, 2, 4, 5]) diff --git a/examples/02631/students/programs/report1intro.py b/examples/02631/students/programs/report1intro.py new file mode 100644 index 0000000..587129b --- /dev/null +++ b/examples/02631/students/programs/report1intro.py @@ -0,0 +1,142 @@ +""" +Example student code. This file is automatically generated from the files in the instructor-directory +""" +from src.unitgrade2.unitgrade2 import Report, UTestCase, cache +from src.unitgrade2 import evaluate_report_student +import numpy as np +import looping +from looping import bacteriaGrowth, clusterAnalysis, removeIncomplete, fermentationRate + +def trlist(x): + s = str(list(x)) + if len(s) > 30: + s = s[:30] + "...]" + return s + +class Bacteria(UTestCase): + """ Bacteria growth rates """ + + def stest(self, n0, alpha, K, N): + g = bacteriaGrowth(n0=n0, alpha=alpha, K=K, N=N) + self.title = f"bacteriaGrowth({n0}, {alpha}, {K}, {N}) = {g} ?" + self.assertEqualC(g) + + def test_growth1(self): + """ Hints: + * Make sure to frobulate the frobulator. + """ + self.stest(100, 0.4, 1000, 500) + + def test_growth2(self): + self.stest(10, 0.4, 1000, 500) + + def test_growth3(self): + self.stest(100, 1.4, 1000, 500) + + def test_growth4(self): + self.stest(100, 0.0004, 1000, 500) + + def test_growth5(self): + """ + hints: + * What happens when n0 > N? (in this case return t=0) """ + self.stest(100, 0.4, 1000, 99) + +class ClusterAnalysis(UTestCase): + """ Test the cluster analysis method """ + + def stest(self, n, seed): + np.random.seed(seed) + x = np.round(np.random.rand(n), 1) + I = clusterAnalysis(x) + self.title = f"clusterAnalysis({list(x)}) = {list(I)} ?" + self.assertEqualC(list(I)) + + def test_cluster1(self): + """ Hints: + * Make sure to frobulate the frobulator. + * Just try harder + """ + self.stest(3, 10) + + def test_cluster2(self): + self.stest(4, 146) + + def test_cluster3(self): + self.stest(5, 12) + + def test_cluster4(self): + """ + Cluster analysis for tied lists + Hints: + * It may be that an observations has the same distance to the two clusters. Where do you assign it in this case? + """ + x = np.array([10.0, 12.0, 10.0, 12.0, 9.0, 11.0, 11.0, 13.0]) + self.assertEqualC(list(clusterAnalysis(x) ) ) + + +class RemoveIncomplete(UTestCase): + """ Remove incomplete IDs """ + + def stest(self, x): + I = list( removeIncomplete(x) ) + self.title = f"removeId({trlist(x)}) = {trlist(I)} ?" + self.assertEqualC(I) + + @cache + def rseq(self, max, n): + np.random.seed(42) + return np.random.randint(max, size=(n,) ) + (np.random.randint(2, size=(n,) )+1)/10 + + def test_incomplete1(self): + self.stest( np.array([1.3, 2.2, 2.3, 4.2, 5.1, 3.2, 5.3, 3.3, 2.1, 1.1, 5.2, 3.1]) ) + + def test_incomplete2(self): + self.stest( np.array([1.1, 1.2, 1.3, 2.1, 2.2, 2.3]) ) + + def test_incomplete3(self): + self.stest(np.array([5.1, 5.2, 4.1, 4.3, 4.2, 8.1, 8.2, 8.3]) ) + + def test_incomplete4(self): + self.stest(np.array([1.1, 1.3, 2.1, 2.2, 3.1, 3.3, 4.1, 4.2, 4.3]) ) + + def test_incomplete5(self): + self.stest(self.rseq(10, 40)) + + +class FermentationRate(UTestCase): + """ Test the fermentation rate question """ + + def stest(self, x, lower, upper): + I = fermentationRate(x, lower, upper) + s = trlist(x) + self.title = f"fermentationRate({s}, {lower}, {upper}) = {I:.3f} ?" + self.assertEqualC(I) + + @cache + def rseq(self, max, n): + np.random.seed(42) + return np.random.randint(max, size=(n,) ) + (np.random.randint(3, size=(n,) )+1)/n + + def test_rate1(self): + self.stest(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 15, 25) + + def test_rate2(self): + self.stest(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 1, 200) + + def test_rate3(self): + self.stest(np.array([1.75]), 1, 2) + + def test_rate4(self): + self.stest(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 18.2, 20) + + +class Report1Flat(Report): + title = "Week 4: Looping" + questions = [(ClusterAnalysis, 10), (RemoveIncomplete, 10), (Bacteria, 10), (FermentationRate, 10),] + pack_imports = [looping] + +if __name__ == "__main__": + # Uncomment to simply run everything as a unittest: + # unittest.main(verbosity=2) + evaluate_report_student(Report1Flat()) diff --git a/examples/02631/students/programs/report1intro_grade.py b/examples/02631/students/programs/report1intro_grade.py new file mode 100644 index 0000000..e7ffde8 --- /dev/null +++ b/examples/02631/students/programs/report1intro_grade.py @@ -0,0 +1,339 @@ +""" +Example student code. This file is automatically generated from the files in the instructor-directory +""" +import numpy as np +from tabulate import tabulate +from datetime import datetime +import pyfiglet +import unittest +import inspect +import os +import argparse +import time + +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: +To run all tests in a report: + +> python assignment1_dp.py + +To run only question 2 or question 2.1 + +> python assignment1_dp.py -q 2 +> python assignment1_dp.py -q 2.1 + +Note this scripts does not grade your report. To grade your report, use: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('-q', nargs='?', type=str, default=None, help='Only evaluate this question (e.g.: -q 2)') +parser.add_argument('--showexpected', action="store_true", help='Show the expected/desired result') +parser.add_argument('--showcomputed', action="store_true", help='Show the answer your code computes') +parser.add_argument('--unmute', action="store_true", help='Show result of print(...) commands in code') +parser.add_argument('--passall', action="store_true", help='Automatically pass all tests. Useful when debugging.') + +def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False): + args = parser.parse_args() + if question is None and args.q is not None: + question = args.q + if "." in question: + question, qitem = [int(v) for v in question.split(".")] + else: + question = int(question) + + if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file: + raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation") + + if unmute is None: + unmute = args.unmute + if passall is None: + passall = args.passall + + results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute, + show_tol_err=show_tol_err) + + + if question is None: + print("Provisional evaluation") + tabulate(table_data) + table = table_data + print(tabulate(table)) + print(" ") + + fr = inspect.getouterframes(inspect.currentframe())[1].filename + gfile = os.path.basename(fr)[:-3] + "_grade.py" + if os.path.exists(gfile): + print("Note your results have not yet been registered. \nTo register your results, please run the file:") + print(">>>", gfile) + print("In the same manner as you ran this file.") + + + return results + + +def upack(q): + # h = zip([(i['w'], i['possible'], i['obtained']) for i in q.values()]) + h =[(i['w'], i['possible'], i['obtained']) for i in q.values()] + h = np.asarray(h) + return h[:,0], h[:,1], h[:,2], + +class UnitgradeTextRunner(unittest.TextTestRunner): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + +class SequentialTestLoader(unittest.TestLoader): + def getTestCaseNames(self, testCaseClass): + test_names = super().getTestCaseNames(testCaseClass) + # testcase_methods = list(testCaseClass.__dict__.keys()) + ls = [] + for C in testCaseClass.mro(): + if issubclass(C, unittest.TestCase): + ls = list(C.__dict__.keys()) + ls + testcase_methods = ls + test_names.sort(key=testcase_methods.index) + return test_names + +def evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False, + show_progress_bar=True, + show_tol_err=False, + big_header=True): + + now = datetime.now() + if big_header: + ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom") + b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) + else: + b = "Unitgrade" + dt_string = now.strftime("%d/%m/%Y %H:%M:%S") + print(b + " v" + __version__ + ", started: " + dt_string+ "\n") + # print("Started: " + dt_string) + s = report.title + if hasattr(report, "version") and report.version is not None: + s += " version " + report.version + print(s, "(use --help for options)" if show_help_flag else "") + # print(f"Loaded answers from: ", report.computed_answers_file, "\n") + table_data = [] + # nL = + t_start = time.time() + score = {} + loader = SequentialTestLoader() + + for n, (q, w) in enumerate(report.questions): + if question is not None and n+1 != question: + continue + suite = loader.loadTestsFromTestCase(q) + qtitle = q.question_title() if hasattr(q, 'question_title') else q.__qualname__ + q_title_print = "Question %i: %s"%(n+1, qtitle) + print(q_title_print, end="") + q.possible = 0 + q.obtained = 0 + q_ = {} # Gather score in this class. + UTextResult.q_title_print = q_title_print # Hacky + UTextResult.show_progress_bar = show_progress_bar # Hacky. + UTextResult.number = n + UTextResult.nL = report.nL + + res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) + + possible = res.testsRun + obtained = len(res.successes) + + assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun + + obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 + score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} + q.obtained = obtained + q.possible = possible + + s1 = f"Question {n+1} total" + s2 = f" {q.obtained}/{w}" + print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 ) + print(" ") + table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"]) + + ws, possible, obtained = upack(score) + possible = int( msum(possible) ) + obtained = int( msum(obtained) ) # Cast to python int + report.possible = possible + report.obtained = obtained + now = datetime.now() + dt_string = now.strftime("%H:%M:%S") + + dt = int(time.time()-t_start) + minutes = dt//60 + seconds = dt - minutes*60 + plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") + + dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", + last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) + + # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total") + + table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) + results = {'total': (obtained, possible), 'details': score} + return results, table_data + + +from tabulate import tabulate +from datetime import datetime +import inspect +import json +import os +import bz2 +import pickle +import os + +def bzwrite(json_str, token): # to get around obfuscation issues + with getattr(bz2, 'open')(token, "wt") as f: + f.write(json_str) + +def gather_imports(imp): + resources = {} + m = imp + # for m in pack_imports: + # print(f"*** {m.__name__}") + f = m.__file__ + # dn = os.path.dirname(f) + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = str(__import__(m.__name__.split('.')[0]).__path__) + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: + top_package = os.path.dirname(m.__file__) + module_import = True + else: + top_package = __import__(m.__name__.split('.')[0]).__path__._path[0] + module_import = False + + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = os.path.dirname(top_package) + import zipfile + # import strea + # zipfile.ZipFile + import io + # file_like_object = io.BytesIO(my_zip_data) + zip_buffer = io.BytesIO() + with zipfile.ZipFile(zip_buffer, 'w') as zip: + # zip.write() + for root, dirs, files in os.walk(top_package): + for file in files: + if file.endswith(".py"): + fpath = os.path.join(root, file) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) + zip.write(fpath, v) + + resources['zipfile'] = zip_buffer.getvalue() + resources['top_package'] = top_package + resources['module_import'] = module_import + return resources, top_package + + if f.endswith("__init__.py"): + for root, dirs, files in os.walk(os.path.dirname(f)): + for file in files: + if file.endswith(".py"): + # print(file) + # print() + v = os.path.relpath(os.path.join(root, file), top_package) + with open(os.path.join(root, file), 'r') as ff: + resources[v] = ff.read() + else: + v = os.path.relpath(f, top_package) + with open(f, 'r') as ff: + resources[v] = ff.read() + return resources + +import argparse +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Use this script to get the score of your report. Example: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('--noprogress', action="store_true", help='Disable progress bars') +parser.add_argument('--autolab', action="store_true", help='Show Autolab results') + +def gather_upload_to_campusnet(report, output_dir=None): + n = report.nL + args = parser.parse_args() + results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, + show_progress_bar=not args.noprogress, + big_header=not args.autolab) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) + # also load the source code of missing files... + + sources = {} + print("") + if not args.autolab: + if len(report.individual_imports) > 0: + print("By uploading the .token file, you verify the files:") + for m in report.individual_imports: + print(">", m.__file__) + print("Are created/modified individually by you in agreement with DTUs exam rules") + report.pack_imports += report.individual_imports + + if len(report.pack_imports) > 0: + print("Including files in upload...") + for k, m in enumerate(report.pack_imports): + nimp, top_package = gather_imports(m) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import + nimp['name'] = m.__name__ + sources[k] = nimp + # if len([k for k in nimp if k not in sources]) > 0: + print(f" * {m.__name__}") + # sources = {**sources, **nimp} + results['sources'] = sources + + if output_dir is None: + output_dir = os.getcwd() + + payload_out_base = report.__class__.__name__ + "_handin" + + obtain, possible = results['total'] + vstring = "_v"+report.version if report.version is not None else "" + + token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) + token = os.path.join(output_dir, token) + with open(token, 'wb') as f: + pickle.dump(results, f) + + if not args.autolab: + print(" ") + print("To get credit for your results, please upload the single unmodified file: ") + print(">", token) + # print("To campusnet without any modifications.") + + # print("Now time for some autolab fun") + +def source_instantiate(name, report1_source, payload): + eval("exec")(report1_source, globals()) + pl = pickle.loads(bytes.fromhex(payload)) + report = eval(name)(payload=pl, strict=True) + # report.set_payload(pl) + return report + + + +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n"""\nimport numpy as np\nimport sys\nimport re\nimport threading\nimport tqdm\nimport pickle\nimport os\nfrom io import StringIO\nimport io\nfrom unittest.runner import _WritelnDecorator\nfrom typing import Any\nimport inspect\nimport textwrap\nimport colorama\nfrom colorama import Fore\nfrom functools import _make_key, RLock\nfrom collections import namedtuple\nimport unittest\nimport time\n\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ncolorama.init(autoreset=True) # auto resets your settings after every output\n\ndef gprint(s):\n print(f"{Fore.GREEN}{s}")\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\n\ndef setup_dir_by_class(C, base_dir):\n name = C.__class__.__name__\n return base_dir, name\n\n\nclass Logger(object):\n def __init__(self, buffer):\n assert False\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\n# @classmethod\n# class OrderedClassMembers(type):\n# def __prepare__(self, name, bases):\n# assert False\n# return collections.OrderedDict()\n#\n# def __new__(self, name, bases, classdict):\n# ks = list(classdict.keys())\n# for b in bases:\n# ks += b.__ordered__\n# classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n# return type.__new__(self, name, bases, classdict)\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 120 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q, _) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q, _) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n loader = unittest.TestLoader()\n for q, _ in self.questions:\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self, with_coverage=False):\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = True\n q._report = self\n\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\': True}\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = False\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct + 1)\n if i > 0 and l.find("|", i + 1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = r\'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar", show_progress_bar=True, file=None):\n if file == None:\n file = sys.stdout\n self.file = file\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.01\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n self.pbar = None\n\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar = None\n\n self.file.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=self.file, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\')\n\n for _ in range(self.n - 1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\ndef dprint(first, last, nL, extra = "", file=None, dotsym=\'.\', color=\'white\'):\n if file == None:\n file = sys.stdout\n\n # ss = self.item_title_print\n # state = "PASS" if success else "FAILED"\n dot_parts = (dotsym * max(0, nL - len(last) - len(first)))\n # if self.show_progress_bar or True:\n print(first + dot_parts, end="", file=file)\n # else:\n # print(dot_parts, end="", file=self.cc.file)\n last += extra\n # if tsecs >= 0.5:\n # state += " (" + str(tsecs) + " seconds)"\n print(last, file=file)\n\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n cc = None\n\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n self.successes.append(test)\n self.cc_terminate()\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n self.cc.file.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="", file=self.cc.file)\n else:\n print(dot_parts, end="", file=self.cc.file)\n\n if tsecs >= 0.5:\n state += " (" + str(tsecs) + " seconds)"\n print(state, file=self.cc.file)\n\n def startTest(self, test):\n # j =self.testsRun\n self.testsRun += 1\n # item_title = self.getDescription(test)\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n self.item_title_print = " * q%i.%i) %s" % (UTextResult.number + 1, self.testsRun, item_title)\n estimated_time = 10\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar, file=sys.stdout)\n else:\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n self._stdout = sys.stdout\n sys.stdout = io.StringIO()\n\n def stopTest(self, test):\n sys.stdout = self._stdout\n super().stopTest(test)\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass is None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n print(" " * max(0, self.nL - len(self.cc.title)) + (" (" + str(q_time) + " seconds)" if q_time >= 0.5 else ""))\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n stream = io.StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)))\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n\n return wrapper\n\n\ndef get_hints(ss):\n if ss == None:\n return None\n try:\n ss = textwrap.dedent(ss)\n ss = ss.replace(\'\'\'"""\'\'\', "").strip()\n hints = ["hints:", ]\n j = np.argmax([ss.lower().find(h) for h in hints])\n h = hints[j]\n ss = ss[ss.find(h) + len(h) + 1:]\n ss = "\\n".join([l for l in ss.split("\\n") if not l.strip().startswith(":")])\n ss = textwrap.dedent(ss)\n ss = ss.strip()\n return ss\n except Exception as e:\n print("bad hints", ss, e)\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n _with_coverage = False\n _report = None # The report used. This is very, very hacky and should always be None. Don\'t rely on it!\n\n def capture(self):\n return Capturing2(stdout=self._stdout)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ is not None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n if self._with_coverage:\n if not hasattr(self._report, \'covcache\'):\n self._report.covcache = {}\n import coverage\n self.cov = coverage.Coverage()\n self.cov.start()\n self.setUp()\n\n def _callTearDown(self):\n self.tearDown()\n if self._with_coverage:\n from pathlib import Path\n from snipper import snipper\n self.cov.stop()\n data = self.cov.get_data()\n base, _, _ = self._report._import_base_relative()\n for file in data.measured_files():\n file = os.path.normpath(file)\n root = Path(base)\n child = Path(file)\n if root in child.parents:\n with open(child, \'r\') as f:\n s = f.read()\n lines = s.splitlines()\n garb = \'GARBAGE\'\n\n lines2 = snipper.censor_code(lines, keep=True)\n assert len(lines) == len(lines2)\n\n for l in data.contexts_by_lineno(file):\n if lines2[l].strip() == garb:\n if self.cache_id() not in self._report.covcache:\n self._report.covcache[self.cache_id()] = {}\n\n rel = os.path.relpath(child, root)\n cc = self._report.covcache[self.cache_id()]\n j = 0\n for j in range(l, -1, -1):\n if "def" in lines2[j] or "class" in lines2[j]:\n break\n from snipper.snipper import gcoms\n fun = lines2[j]\n comments, _ = gcoms("\\n".join(lines2[j:l]))\n if rel not in cc:\n cc[rel] = {}\n cc[rel][fun] = (l, "\\n".join(comments))\n self._cache_put((self.cache_id(), \'coverage\'), self._report.covcache)\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd is None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n sd = self.shortDescriptionStandard()\n title = self._cache_get((self.cache_id(), \'title\'), sd)\n return title if title is not None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome is None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc is not None:\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard())\n\n self._cache2[(self.cache_id(), \'assert\')] = {}\n res = testMethod()\n elapsed = time.time() - t\n self._get_outcome()[self.cache_id()] = res\n self._cache_put((self.cache_id(), "time"), elapsed)\n\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return c, m\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n # sys.stdout = self._stdout\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n self.__class__._cache[\n key] = {} # A new dict. We manually insert it because we have to use that the dict is mutable.\n cache = self._cache_get(key)\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, f"Key {id} not found in cache; framework files missing. Please run deploy()")\n\n # The order of these calls is important. If the method assert fails, we should still store the correct result in cache.\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n assert_fun(first, _expected, *args, **kwargs)\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache is not None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n if os.path.exists(cfile):\n try:\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n except Exception as e:\n print("Bad cache", cfile)\n print(e)\n else:\n print("Warning! data file not found", cfile)\n\n def _feedErrorsToResult(self, result, errors):\n """ Use this to show hints on test failure. """\n if not isinstance(result, UTextResult):\n er = [e for e, v in errors if v != None]\n\n if len(er) > 0:\n hints = []\n key = (self.cache_id(), \'coverage\')\n if self._cache_contains(key):\n CC = self._cache_get(key)\n for id in CC:\n if id == self.cache_id():\n cl, m = id\n gprint(f"> An error occured while solving: {cl}.{m}. The files/methods you need to edit are:") # For the test {id} in {file} you should edit:")\n for file in CC[id]:\n rec = CC[id][file]\n gprint(f"> * {file}")\n for l in rec:\n _, comments = CC[id][file][l]\n hint = get_hints(comments)\n\n if hint != None:\n hints.append(hint)\n gprint(f"> - {l}")\n\n er = er[0]\n doc = er._testMethodDoc\n if doc is not None:\n hint = get_hints(er._testMethodDoc)\n if hint is not None:\n hints = [hint] + hints\n if len(hints) > 0:\n gprint("> Hints:")\n gprint(textwrap.indent("\\n".join(hints), "> "))\n\n super()._feedErrorsToResult(result, errors)\n\n def startTestRun(self):\n # print("asdfsdaf 11", file=sys.stderr)\n super().startTestRun()\n # print("asdfsdaf")\n\n def _callTestMethod(self, method):\n # print("asdfsdaf")\n super()._callTestMethod(method)\n\n\ndef hide(func):\n return func\n\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n# 817\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\nimport inspect\nimport os\nimport argparse\nimport time\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print(b + " v" + __version__ + ", started: " + dt_string+ "\\n")\n # print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print(s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n # nL =\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n UTextResult.nL = report.nL\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"Question {n+1} total"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")",\n last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL)\n\n # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n # print(" ")\n # print("="*n)\n # print("Final evaluation")\n # print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n print("")\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f" * {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single unmodified file: ")\n print(">", token)\n # print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\nimport numpy as np\nimport looping\nfrom looping import bacteriaGrowth, clusterAnalysis, removeIncomplete, fermentationRate\n\ndef trlist(x):\n s = str(list(x))\n if len(s) > 30:\n s = s[:30] + "...]"\n return s\n\nclass Bacteria(UTestCase):\n """ Bacteria growth rates """\n\n def stest(self, n0, alpha, K, N):\n g = bacteriaGrowth(n0=n0, alpha=alpha, K=K, N=N)\n self.title = f"bacteriaGrowth({n0}, {alpha}, {K}, {N}) = {g} ?"\n self.assertEqualC(g)\n\n def test_growth1(self):\n """ Hints:\n * Make sure to frobulate the frobulator.\n """\n self.stest(100, 0.4, 1000, 500)\n\n def test_growth2(self):\n self.stest(10, 0.4, 1000, 500)\n\n def test_growth3(self):\n self.stest(100, 1.4, 1000, 500)\n\n def test_growth4(self):\n self.stest(100, 0.0004, 1000, 500)\n\n def test_growth5(self):\n """\n hints:\n * What happens when n0 > N? (in this case return t=0) """\n self.stest(100, 0.4, 1000, 99)\n\nclass ClusterAnalysis(UTestCase):\n """ Test the cluster analysis method """\n\n def stest(self, n, seed):\n np.random.seed(seed)\n x = np.round(np.random.rand(n), 1)\n I = clusterAnalysis(x)\n self.title = f"clusterAnalysis({list(x)}) = {list(I)} ?"\n self.assertEqualC(list(I))\n\n def test_cluster1(self):\n """ Hints:\n * Make sure to frobulate the frobulator.\n * Just try harder\n """\n self.stest(3, 10)\n\n def test_cluster2(self):\n self.stest(4, 146)\n\n def test_cluster3(self):\n self.stest(5, 12)\n\n def test_cluster4(self):\n """\n Cluster analysis for tied lists\n Hints:\n * It may be that an observations has the same distance to the two clusters. Where do you assign it in this case?\n """\n x = np.array([10.0, 12.0, 10.0, 12.0, 9.0, 11.0, 11.0, 13.0])\n self.assertEqualC(list(clusterAnalysis(x) ) )\n\n\nclass RemoveIncomplete(UTestCase):\n """ Remove incomplete IDs """\n\n def stest(self, x):\n I = list( removeIncomplete(x) )\n self.title = f"removeId({trlist(x)}) = {trlist(I)} ?"\n self.assertEqualC(I)\n\n @cache\n def rseq(self, max, n):\n np.random.seed(42)\n return np.random.randint(max, size=(n,) ) + (np.random.randint(2, size=(n,) )+1)/10\n\n def test_incomplete1(self):\n self.stest( np.array([1.3, 2.2, 2.3, 4.2, 5.1, 3.2, 5.3, 3.3, 2.1, 1.1, 5.2, 3.1]) )\n\n def test_incomplete2(self):\n self.stest( np.array([1.1, 1.2, 1.3, 2.1, 2.2, 2.3]) )\n\n def test_incomplete3(self):\n self.stest(np.array([5.1, 5.2, 4.1, 4.3, 4.2, 8.1, 8.2, 8.3]) )\n\n def test_incomplete4(self):\n self.stest(np.array([1.1, 1.3, 2.1, 2.2, 3.1, 3.3, 4.1, 4.2, 4.3]) )\n\n def test_incomplete5(self):\n self.stest(self.rseq(10, 40))\n\n\nclass FermentationRate(UTestCase):\n """ Test the fermentation rate question """\n\n def stest(self, x, lower, upper):\n I = fermentationRate(x, lower, upper)\n s = trlist(x)\n self.title = f"fermentationRate({s}, {lower}, {upper}) = {I:.3f} ?"\n self.assertEqualC(I)\n\n @cache\n def rseq(self, max, n):\n np.random.seed(42)\n return np.random.randint(max, size=(n,) ) + (np.random.randint(3, size=(n,) )+1)/n\n\n def test_rate1(self):\n self.stest(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 15, 25)\n\n def test_rate2(self):\n self.stest(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 1, 200)\n\n def test_rate3(self):\n self.stest(np.array([1.75]), 1, 2)\n\n def test_rate4(self):\n self.stest(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 18.2, 20)\n\n\nclass Report1Flat(Report):\n title = "Week 4: Looping"\n questions = [(ClusterAnalysis, 10), (RemoveIncomplete, 10), (Bacteria, 10), (FermentationRate, 10),]\n pack_imports = [looping]' +report1_payload = '80049592150000000000007d94288c0f436c7573746572416e616c79736973947d94288c0f436c7573746572416e616c79736973948c0d746573745f636c7573746572319486948c057469746c659486948c2e636c7573746572416e616c79736973285b302e382c20302e302c20302e365d29203d205b312c20322c20315d203f946803680486948c066173736572749486947d944b005d94288c156e756d70792e636f72652e6d756c74696172726179948c067363616c61729493948c056e756d7079948c0564747970659493948c02693494898887945294284b038c013c944e4e4e4affffffff4affffffff4b007494624304010000009486945294681068164304020000009486945294681068164304010000009486945294657368038c0d746573745f636c757374657232948694680686948c36636c7573746572416e616c79736973285b302e352c20302e362c20302e332c20302e335d29203d205b322c20322c20312c20315d203f94680368228694680a86947d944b005d9428681068164304020000009486945294681068164304020000009486945294681068164304010000009486945294681068164304010000009486945294657368038c0d746573745f636c757374657233948694680686948c3e636c7573746572416e616c79736973285b302e322c20302e372c20302e332c20302e352c20302e305d29203d205b312c20322c20312c20322c20315d203f94680368368694680a86947d944b005d9428681068164304010000009486945294681068164304020000009486945294681068164304010000009486945294681068164304020000009486945294681068164304010000009486945294657368038c0d746573745f636c757374657234948694680a86947d944b005d942868106816430401000000948694529468106816430402000000948694529468106816430401000000948694529468106816430402000000948694529468106816430401000000948694529468106816430401000000948694529468106816430401000000948694529468106816430402000000948694529465738c0474696d6594473fdf6c8500000000758c1052656d6f7665496e636f6d706c657465947d94288c1052656d6f7665496e636f6d706c657465948c10746573745f696e636f6d706c657465319486948c057469746c659486948c5372656d6f76654964285b312e332c20322e322c20322e332c20342e322c20352e312c20332e322c2e2e2e5d29203d205b322e322c20322e332c20352e312c20332e322c20352e332c20332e332c2e2e2e5d203f94686d686e86948c066173736572749486947d944b005d9428681068138c02663894898887945294284b0368174e4e4e4affffffff4affffffff4b0074946243089a9999999999014094869452946810687a4308666666666666024094869452946810687a4308666666666666144094869452946810687a43089a9999999999094094869452946810687a4308333333333333154094869452946810687a43086666666666660a4094869452946810687a4308cdcccccccccc004094869452946810687a4308cdcccccccccc144094869452946810687a4308cdcccccccccc084094869452946573686d8c10746573745f696e636f6d706c65746532948694687086948c4b72656d6f76654964285b312e312c20312e322c20312e332c20322e312c20322e322c20322e335d29203d205b312e312c20312e322c20312e332c20322e312c20322e322c20322e335d203f94686d68978694687486947d944b005d94286810687a43089a9999999999f13f94869452946810687a4308333333333333f33f94869452946810687a4308cdccccccccccf43f94869452946810687a4308cdcccccccccc004094869452946810687a43089a9999999999014094869452946810687a4308666666666666024094869452946573686d8c10746573745f696e636f6d706c65746533948694687086948c4f72656d6f76654964285b352e312c20352e322c20342e312c20342e332c20342e322c20382e312c2e2e2e5d29203d205b342e312c20342e332c20342e322c20382e312c20382e322c20382e335d203f94686d68b18694687486947d944b005d94286810687a4308666666666666104094869452946810687a4308333333333333114094869452946810687a4308cdcccccccccc104094869452946810687a4308333333333333204094869452946810687a4308666666666666204094869452946810687a43089a9999999999204094869452946573686d8c10746573745f696e636f6d706c65746534948694687086948c4072656d6f76654964285b312e312c20312e332c20322e312c20322e322c20332e312c20332e332c2e2e2e5d29203d205b342e312c20342e322c20342e335d203f94686d68cb8694687486947d944b005d94286810687a4308666666666666104094869452946810687a4308cdcccccccccc104094869452946810687a4308333333333333114094869452946573686d8c10746573745f696e636f6d706c657465359486948c06406361636865948c0472736571948c0966756e63746f6f6c73948c0a5f486173686564536571949394298194284b0a4b28654e7d948c096861736876616c7565948a0884d8ef03874d7f467386946287948694680e8c0c5f7265636f6e73747275637494939468118c076e6461727261799493944b0085944301629487945294284b014b28859468138c02663894898887945294284b0368174e4e4e4affffffff4affffffff4b0074946289424001000066666666666618409a99999999990940cdcccccccccc1c40cdcccccccccc1040cdcccccccccc184033333333333322409a999999999901406666666666661840cdcccccccccc1c40cdcccccccccc10409a999999999909406666666666661c40cdcccccccccc1c40cdcccccccccc0040cdcccccccccc14406666666666661040333333333333f33f6666666666661c406666666666661440333333333333f33f66666666666610409a9999999999c93f6666666666662240cdcccccccccc144066666666666620409a9999999999c93f66666666666622409a99999999990140cdcccccccccc18409a9999999999094066666666666620409a999999999901406666666666661040cdcccccccccc0040cdcccccccccc1840cdcccccccccc10406666666666662040cdcccccccccc1840333333333333f33f9a9999999999094094749462686d68dc8694687086948c5372656d6f76654964285b362e312c20332e322c20372e322c20342e322c20362e322c20392e312c2e2e2e5d29203d205b392e312c20352e322c20312e322c20352e312c20312e322c20392e322c2e2e2e5d203f94686d68dc8694687486947d944b005d94286810687a4308333333333333224094869452946810687a4308cdcccccccccc144094869452946810687a4308333333333333f33f94869452946810687a4308666666666666144094869452946810687a4308333333333333f33f94869452946810687a4308666666666666224094869452946810687a4308cdcccccccccc144094869452946810687a4308666666666666204094869452946810687a4308666666666666224094869452946810687a4308666666666666204094869452946810687a4308666666666666204094869452946810687a4308333333333333f33f94869452946573686a473fcf9dc400000000758c084261637465726961947d94288c084261637465726961948c0c746573745f67726f777468319486948c057469746c659486948c29626163746572696147726f777468283130302c20302e342c20313030302c2035303029203d2037203f946a250100006a2601000086948c066173736572749486947d944b004b07736a250100006a2601000086948c08636f7665726167659486947d94286a250100006a2601000086947d948c0a6c6f6f70696e672e7079947d948c2564656620626163746572696147726f777468286e302c20616c7068612c204b2c204e293a20944b158ce72222220a2020202043616c63756c6174652074696d6520756e74696c2062616374657269612067726f77746820657863656564204e207374617274696e672066726f6d206120706f70756c6174696f6e206f66206e302062616374657269612e0a2020202068696e74733a0a20202020202020202a20636f6e7369646572206e300a20202020202020202a20616c706861203e20300a202020203a706172616d206e303a0a202020203a706172616d20616c7068613a0a202020203a706172616d204b3a0a202020203a706172616d204e3a0a202020203a72657475726e3a0a2020202022222294869473736a250100008c0c746573745f67726f777468329486947d948c0a6c6f6f70696e672e7079947d948c2564656620626163746572696147726f777468286e302c20616c7068612c204b2c204e293a20944b158ce72222220a2020202043616c63756c6174652074696d6520756e74696c2062616374657269612067726f77746820657863656564204e207374617274696e672066726f6d206120706f70756c6174696f6e206f66206e302062616374657269612e0a2020202068696e74733a0a20202020202020202a20636f6e7369646572206e300a20202020202020202a20616c706861203e20300a202020203a706172616d206e303a0a202020203a706172616d20616c7068613a0a202020203a706172616d204b3a0a202020203a706172616d204e3a0a202020203a72657475726e3a0a2020202022222294869473736a250100008c0c746573745f67726f777468339486947d948c0a6c6f6f70696e672e7079947d948c2564656620626163746572696147726f777468286e302c20616c7068612c204b2c204e293a20944b158ce72222220a2020202043616c63756c6174652074696d6520756e74696c2062616374657269612067726f77746820657863656564204e207374617274696e672066726f6d206120706f70756c6174696f6e206f66206e302062616374657269612e0a2020202068696e74733a0a20202020202020202a20636f6e7369646572206e300a20202020202020202a20616c706861203e20300a202020203a706172616d206e303a0a202020203a706172616d20616c7068613a0a202020203a706172616d204b3a0a202020203a706172616d204e3a0a202020203a72657475726e3a0a2020202022222294869473736a250100008c0c746573745f67726f777468349486947d948c0a6c6f6f70696e672e7079947d948c2564656620626163746572696147726f777468286e302c20616c7068612c204b2c204e293a20944b158ce72222220a2020202043616c63756c6174652074696d6520756e74696c2062616374657269612067726f77746820657863656564204e207374617274696e672066726f6d206120706f70756c6174696f6e206f66206e302062616374657269612e0a2020202068696e74733a0a20202020202020202a20636f6e7369646572206e300a20202020202020202a20616c706861203e20300a202020203a706172616d206e303a0a202020203a706172616d20616c7068613a0a202020203a706172616d204b3a0a202020203a706172616d204e3a0a202020203a72657475726e3a0a2020202022222294869473736a250100008c0c746573745f67726f777468359486947d948c0a6c6f6f70696e672e7079947d948c2564656620626163746572696147726f777468286e302c20616c7068612c204b2c204e293a20944b118ce72222220a2020202043616c63756c6174652074696d6520756e74696c2062616374657269612067726f77746820657863656564204e207374617274696e672066726f6d206120706f70756c6174696f6e206f66206e302062616374657269612e0a2020202068696e74733a0a20202020202020202a20636f6e7369646572206e300a20202020202020202a20616c706861203e20300a202020203a706172616d206e303a0a202020203a706172616d20616c7068613a0a202020203a706172616d204b3a0a202020203a706172616d204e3a0a202020203a72657475726e3a0a202020202222229486947373756a250100006a3a01000086946a2801000086948c29626163746572696147726f7774682831302c20302e342c20313030302c2035303029203d203134203f946a250100006a3a01000086946a2c01000086947d944b004b0e736a250100006a3a01000086946a3001000086946a320100006a250100006a4201000086946a2801000086948c29626163746572696147726f777468283130302c20312e342c20313030302c2035303029203d2033203f946a250100006a4201000086946a2c01000086947d944b004b03736a250100006a4201000086946a3001000086946a320100006a250100006a4a01000086946a2801000086948c2f626163746572696147726f777468283130302c20302e303030342c20313030302c2035303029203d2035343934203f946a250100006a4a01000086946a2c01000086947d944b004d7615736a250100006a4a01000086946a3001000086946a320100006a250100006a5201000086946a2801000086948c28626163746572696147726f777468283130302c20302e342c20313030302c20393929203d2030203f946a250100006a5201000086946a2c01000086947d944b004b00736a250100006a5201000086946a3001000086946a32010000686a473fcf9d9a00000000758c104665726d656e746174696f6e52617465947d94288c104665726d656e746174696f6e52617465948c0a746573745f72617465319486948c057469746c659486948c476665726d656e746174696f6e52617465285b32302e312c2031392e332c20312e312c2031382e322c2031392e372c202e2e2e5d2c2031352c20323529203d2031392e363030203f946a7c0100006a7d01000086948c066173736572749486947d944b006810687a43089a999999999933409486945294736a7c0100008c0a746573745f72617465329486946a7f01000086948c476665726d656e746174696f6e52617465285b32302e312c2031392e332c20312e312c2031382e322c2031392e372c202e2e2e5d2c20312c2032303029203d2032392e393735203f946a7c0100006a8901000086946a8301000086947d944b006810687a43089899999999f93d409486945294736a7c0100008c0a746573745f72617465339486946a7f01000086948c286665726d656e746174696f6e52617465285b312e37355d2c20312c203229203d20312e373530203f946a7c0100006a9301000086946a8301000086947d944b006810687a4308000000000000fc3f9486945294736a7c0100008c0a746573745f72617465349486946a7f01000086948c496665726d656e746174696f6e52617465285b32302e312c2031392e332c20312e312c2031382e322c2031392e372c202e2e2e5d2c2031382e322c20323029203d2031392e353030203f946a7c0100006a9d01000086946a8301000086947d944b006810687a43080000000000803340948694529473686a473fc74c0a0000000075752e' +name="Report1Flat" + +report = source_instantiate(name, report1_source, report1_payload) +output_dir = os.path.dirname(__file__) +gather_upload_to_campusnet(report, output_dir) diff --git a/examples/02631/students/programs/unitgrade/Bacteria.pkl b/examples/02631/students/programs/unitgrade/Bacteria.pkl new file mode 100644 index 0000000000000000000000000000000000000000..b246df45c63387d95bc69cc26deba7770d8249c5 GIT binary patch literal 2050 zcmZo*nfjfb0Ss!VX!LM6B_@}o7G)+*>ES6!EiQ>qFUl`3$uOMKHl>HPB(o$Z6~xj^ zf~s-{%W4=J80aV%=$Ys!7#e_>rUnL@3bqR73ieYn7&DmKru48S78j=$l}u@yQai<) z!JEApD$bFdUzS>wm<}?wc8Uf>w024lS5AI@L1tdMUP0xQ+9^G%DXD1+XqM&~=qM!S z6l5goD0u5A_-R@xOz{@&d9I|S#HFC1px~UClU$mUSdywxl9`*TP?}egnFBXM0pcx% z)QaTP)D#6jh2oOLqLR$KbcM8{{9J`Zg@XJ7kmk(%Jcax;g**eePCc;U8JT${#a3V@ z$Sy5~<ovwi%#_q3g**c!QLytA>=YnkRt1SgiMa}S1`v%<E?5nUh&Kw)56UY_Eh#O^ zgK(6Tl%}*zDK5@nM2RIMaPW?Xoq_^1Y)J?^<I%7qJKdR#h8@{qXG&4n368Att~7%& zL#%B|220zN9?UujTNPwz0;z*Qx;UVf4<D%V0SWN5P08TPV9b!iX{3RUf+0q=W(+YB zqzh&wGssAg0L(}=sFC_;l@_$7G%zqguPjYXETL`%>4h5XTP6xJ86*NTSr2Nm26mI- c##&l}s$c_%TS1zjMtU=VOauvlO)S*|044x_qW}N^ literal 0 HcmV?d00001 diff --git a/examples/02631/students/programs/unitgrade/ClusterAnalysis.pkl b/examples/02631/students/programs/unitgrade/ClusterAnalysis.pkl new file mode 100644 index 0000000000000000000000000000000000000000..36635371f6e86d5f841a3827696a5909e766532b GIT binary patch literal 763 zcmZo*nfif=0Ss!VX!P(q=ad$gq!u~mCFWEXXBJQC;VnrmE{RWu2pdjmo6^Htl39|I z3S#L&g<+~Sq7C#cbQBEq3_yfgtfqpkLbRcdf{~7bVXT7vlnllUrnV_PY>CCisYNAI z+NRV_@n(pfqR}IoSDIT;sh6Bzl&Y6onp2XQSX7i)Ii-iKI5{yVv1rQVDLt%UB~yA> zQ%WieQb8h2nI=;@JKCoNP0{dX?qRf<;^*h*_5c6>|6szKp=3&uGYcaF1H+WIDM3>* z_%cMCSwLPz;=*N9i!&Hey=pY2ZAu19+ms$NES@w4`wv7Ig9(UFjUYZncvhipN(Kkq zmsnkZ%Rvyuc-&_UbDtd+_Zfj)Wey_1?gKm50OCk!bU>U6jT93s?u1(naS48R;*!M- aRuho3(Zhr)c0t^UW)vD1?#|TWQau35`|pDQ literal 0 HcmV?d00001 diff --git a/examples/02631/students/programs/unitgrade/FermentationRate.pkl b/examples/02631/students/programs/unitgrade/FermentationRate.pkl new file mode 100644 index 0000000000000000000000000000000000000000..9f5cea13c96d67ff2704de398cd66b78a2db463b GIT binary patch literal 618 zcmZo*nHtZ;00y;FG<pQwQj2m^^GXs+GV}9-5=&C2^l+7=7MH{qC6=TbPHCIc!&;JA zl9LKzxu>D2)rdAS&@<FgFtpS&)=@A7GcELtzyjtv3VM2au{sKdraB5nrkV=2AZ2C- z1`765G8i+M+NSidB^DQ_7L`nCn^HT)o1sTEuQa!yQZG5bC{-`FG^Zppv8X7qa!L<d zadKi#V$qbzQ+imzN~ZL%rj%3`q=H14(k!NQcC=3knxf&&+{0)y#m~>r>;M1%|G|Ve zL&=mRXO3AjLBQByO52p6Da9F#NM1CW(l#Z7rEN+NnV!>8FfuRzdCtgE&(ho!;yKB- zDH$9PUuE!S2s?Ak0NMZ37MlZ%VGhv13|&J#b5l?V>L?h2tN?ML!KaLF12{q%{@7#j jq6y3fPfQz#i9c|R8i74&XsKrki$PtiPH2FIa;Y8w7Y@sc literal 0 HcmV?d00001 diff --git a/examples/02631/students/programs/unitgrade/RemoveIncomplete.pkl b/examples/02631/students/programs/unitgrade/RemoveIncomplete.pkl new file mode 100644 index 0000000000000000000000000000000000000000..30edf2db7bc0e41f0ada9c1c7443ac1d487a6d9a GIT binary patch literal 1444 zcmZo*nL3k|0Ss!VX!HmKrRL_BrF!Ni=jRsWq?V*k=@BSNEiQ@Ago_wXX`9l+T9R3k zlL}%57eRESXha+88S5w*=^23tV;uz(5X)50P)EU7&qzm4PcK$e!B!y}p#UzYU<%S{ ztY-{XrC>iLgE51tZAuSYVsUY5QOT6HDYa9)8Dghs^oZt_<`z`yCFd8V>gAT^lw>9r z6(v?q>0v8QPRvOxnlgDx4=Y&7lpfZUlFEWqkO)(n#gxvD_9;PAG`yL67;UEb`T2SM z|Ns9#nDAyOnUdtpF>59WFgi?Wn-Vl7gD*qGnIjDhm@tJzkcA)`Igy2o!9WyQ2y9du z7qZaVGa$f#DTHD?L=^`_RcdhtBSwT6O=+8w!O}LR#~V2+40RL?K@k9r6hly?Kw<_G z4ThkI!KDHc1x9UCGC1I24+#^9Z$H{21ryk_#-EXeAddcmDTLx%h*`LU9IJ1QVZQZ8 z_!gXyOhLXi0r|=Vl$yY)&qB`-oTR`B4~v|F1xS;Ho^dS1$39qm42d}bNCZFv0pepp zWFd%;v8Yl&Rs}H&MF<iIP$8^7Hi7xr0pVk4?1A$eENYEGeurfuWFH%W@*>QOX~cOE zr+=Wn#_Cd2m`jlg1v7}NjC2&tL17J!7Bdjb5;<-_90hO;f|DdTLcuIcXwd<QpK>gY zheU=Faxg<2kCKfbs!$RsBr=FoMaX!FS<qO7Boc@aibjYK7F8%hC{9f+F4Y47-%{y- literal 0 HcmV?d00001 diff --git a/examples/autolab_example/autolab_example.py b/examples/autolab_example/autolab_example.py index 6cdf58a..a4bdf62 100644 --- a/examples/autolab_example/autolab_example.py +++ b/examples/autolab_example/autolab_example.py @@ -1,9 +1,9 @@ import os -from autolab.autolab import deploy_assignment +from unitgrade_private2.autolab.autolab import deploy_assignment if __name__ == "__main__": wdir = os.getcwd() - args = [('example_simplest', 'cs101', 'report1_grade.py', 'report1_grade.py'), + args = [('example_simplest', 'programs', 'report1_grade.py', 'report1_grade.py'), ('example_framework', 'cs102', 'report2_grade.py', 'report2_grade.py'), ('example_docker', 'cs103', 'report3_complete_grade.py', 'report3_grade.py'), ] diff --git a/examples/autolab_example/tmp/cs101/cs101.yml b/examples/autolab_example/tmp/cs101/cs101.yml index 7631a7f..6dc13d8 100644 --- a/examples/autolab_example/tmp/cs101/cs101.yml +++ b/examples/autolab_example/tmp/cs101/cs101.yml @@ -1,14 +1,14 @@ --- general: - name: cs101 + name: programs description: '' display_name: CS 101 Report 1 handin_filename: Report1_handin.token handin_directory: handin max_grace_days: 0 - handout: cs101-handout.tar - writeup: writeup/cs101.html + handout: programs-handout.tar + writeup: writeup/programs.html max_submissions: -1 disable_handins: false max_size: 2 diff --git a/examples/autolab_example/tmp/cs101/src/driver_python.py b/examples/autolab_example/tmp/cs101/src/driver_python.py index 9b3e081..074cc75 100644 --- a/examples/autolab_example/tmp/cs101/src/driver_python.py +++ b/examples/autolab_example/tmp/cs101/src/driver_python.py @@ -25,7 +25,7 @@ def pfiles(): student_token_file = 'Report1_handin.token' instructor_grade_script = 'report1_grade.py' -grade_file_relative_destination = "cs101\report1_grade.py" +grade_file_relative_destination = "programs\report1_grade.py" with open(student_token_file, 'rb') as f: results = pickle.load(f) sources = results['sources'][0] @@ -55,8 +55,8 @@ def rcom(cm): start = time.time() rcom(command) # pfiles() -# for f in glob.glob(host_tmp_dir + "/cs101/*"): -# print("cs101/", f) +# for f in glob.glob(host_tmp_dir + "/programs/*"): +# print("programs/", f) # print("---") ls = glob.glob(token) # print(ls) diff --git a/examples/autolab_example/tmp/cs101/src/report1_grade.py b/examples/autolab_example/tmp/cs101/src/report1_grade.py index 8972ab5..fbbeabf 100644 --- a/examples/autolab_example/tmp/cs101/src/report1_grade.py +++ b/examples/autolab_example/tmp/cs101/src/report1_grade.py @@ -453,7 +453,7 @@ def source_instantiate(name, report1_source, payload): -report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n\n"""\n# from . import cache_read\nimport unittest\nimport numpy as np\nimport sys\nfrom io import StringIO\nimport collections\nimport re\nimport threading\nimport tqdm\nimport time\nimport pickle\nimport itertools\nimport os\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\ndef setup_dir_by_class(C,base_dir):\n name = C.__class__.__name__\n # base_dir = os.path.join(base_dir, name)\n # if not os.path.isdir(base_dir):\n # os.makedirs(base_dir)\n return base_dir, name\n\nclass Hidden:\n def hide(self):\n return True\n\nclass Logger(object):\n def __init__(self, buffer):\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\nclass Capturing(list):\n def __init__(self, *args, unmute=False, **kwargs):\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass QItem(unittest.TestCase):\n title = None\n testfun = None\n tol = 0\n estimated_time = 0.42\n _precomputed_payload = None\n _computed_answer = None # Internal helper to later get results.\n weight = 1 # the weight of the question.\n\n def __init__(self, question=None, *args, **kwargs):\n if self.tol > 0 and self.testfun is None:\n self.testfun = self.assertL2Relative\n elif self.testfun is None:\n self.testfun = self.assertEqual\n\n self.name = self.__class__.__name__\n # self._correct_answer_payload = correct_answer_payload\n self.question = question\n\n super().__init__(*args, **kwargs)\n if self.title is None:\n self.title = self.name\n\n def _safe_get_title(self):\n if self._precomputed_title is not None:\n return self._precomputed_title\n return self.title\n\n def assertNorm(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed).flat- np.asarray(expected)).flat )\n nrm = np.sqrt(np.sum( diff ** 2))\n\n self.error_computed = nrm\n\n if nrm > tol:\n print(f"Not equal within tolerance {tol}; norm of difference was {nrm}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def assertL2(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n self.error_computed = np.max(diff)\n\n if np.max(diff) > tol:\n print(f"Not equal within tolerance {tol=}; deviation was {np.max(diff)=}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol=}, {np.max(diff)=}")\n\n def assertL2Relative(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n diff = diff / (1e-8 + np.abs( (np.asarray(computed) + np.asarray(expected)) ) )\n self.error_computed = np.max(np.abs(diff))\n if np.sum(diff > tol) > 0:\n print(f"Not equal within tolerance {tol}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def precomputed_payload(self):\n return self._precomputed_payload\n\n def precompute_payload(self):\n # Pre-compute resources to include in tests (useful for getting around rng).\n pass\n\n def compute_answer(self, unmute=False):\n raise NotImplementedError("test code here")\n\n def test(self, computed, expected):\n self.testfun(computed, expected)\n\n def get_points(self, verbose=False, show_expected=False, show_computed=False,unmute=False, passall=False, silent=False, **kwargs):\n possible = 1\n computed = None\n def show_computed_(computed):\n print(">>> Your output:")\n print(computed)\n\n def show_expected_(expected):\n print(">>> Expected output (note: may have been processed; read text script):")\n print(expected)\n\n correct = self._correct_answer_payload\n try:\n if unmute: # Required to not mix together print stuff.\n print("")\n computed = self.compute_answer(unmute=unmute)\n except Exception as e:\n if not passall:\n if not silent:\n print("\\n=================================================================================")\n print(f"When trying to run test class \'{self.name}\' your code threw an error:", e)\n show_expected_(correct)\n import traceback\n print(traceback.format_exc())\n print("=================================================================================")\n return (0, possible)\n\n if self._computed_answer is None:\n self._computed_answer = computed\n\n if show_expected or show_computed:\n print("\\n")\n if show_expected:\n show_expected_(correct)\n if show_computed:\n show_computed_(computed)\n try:\n if not passall:\n self.test(computed=computed, expected=correct)\n except Exception as e:\n if not silent:\n print("\\n=================================================================================")\n print(f"Test output from test class \'{self.name}\' does not match expected result. Test error:")\n print(e)\n show_computed_(computed)\n show_expected_(correct)\n return (0, possible)\n return (1, possible)\n\n def score(self):\n try:\n self.test()\n except Exception as e:\n return 0\n return 1\n\nclass QPrintItem(QItem):\n def compute_answer_print(self):\n """\n Generate output which is to be tested. By default, both text written to the terminal using print(...) as well as return values\n are send to process_output (see compute_answer below). In other words, the text generated is:\n\n res = compute_Answer_print()\n txt = (any terminal output generated above)\n numbers = (any numbers found in terminal-output txt)\n\n self.test(process_output(res, txt, numbers), <expected result>)\n\n :return: Optional values for comparison\n """\n raise Exception("Generate output here. The output is passed to self.process_output")\n\n def process_output(self, res, txt, numbers):\n return res\n\n def compute_answer(self, unmute=False):\n with Capturing(unmute=unmute) as output:\n res = self.compute_answer_print()\n s = "\\n".join(output)\n s = rm_progress_bar(s) # Remove progress bar.\n numbers = extract_numbers(s)\n self._computed_answer = (res, s, numbers)\n return self.process_output(res, s, numbers)\n\nclass OrderedClassMembers(type):\n @classmethod\n def __prepare__(self, name, bases):\n return collections.OrderedDict()\n def __new__(self, name, bases, classdict):\n ks = list(classdict.keys())\n for b in bases:\n ks += b.__ordered__\n classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n return type.__new__(self, name, bases, classdict)\n\nclass QuestionGroup(metaclass=OrderedClassMembers):\n title = "Untitled question"\n partially_scored = False\n t_init = 0 # Time spend on initialization (placeholder; set this externally).\n estimated_time = 0.42\n has_called_init_ = False\n _name = None\n _items = None\n\n @property\n def items(self):\n if self._items == None:\n self._items = []\n members = [gt for gt in [getattr(self, gt) for gt in self.__ordered__ if gt not in ["__classcell__", "__init__"]] if inspect.isclass(gt) and issubclass(gt, QItem)]\n for I in members:\n self._items.append( I(question=self))\n return self._items\n\n @items.setter\n def items(self, value):\n self._items = value\n\n @property\n def name(self):\n if self._name == None:\n self._name = self.__class__.__name__\n return self._name #\n\n @name.setter\n def name(self, val):\n self._name = val\n\n def init(self):\n # Can be used to set resources relevant for this question instance.\n pass\n\n def init_all_item_questions(self):\n for item in self.items:\n if not item.question.has_called_init_:\n item.question.init()\n item.question.has_called_init_ = True\n\n\nclass Report():\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 80 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q,_) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n root_dir = self.pack_imports[0].__path__._path[0]\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q,_) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n # else:\n # if os.path.isfile(self.computed_answers_file):\n # self.set_payload(cache_read(self.computed_answers_file), strict=strict)\n # else:\n # s = f"> Warning: The pre-computed answer file, {os.path.abspath(self.computed_answers_file)} is missing. The framework will NOT work as intended. Reasons may be a broken local installation."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n import unittest\n loader = unittest.TestLoader()\n for q,_ in self.questions:\n import time\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self):\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\':True}\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n # for item in q.items:\n # if q.name not in payloads or item.name not in payloads[q.name]:\n # s = f"> Broken resource dictionary submitted to unitgrade for question {q.name} and subquestion {item.name}. Framework will not work."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n # else:\n # item._correct_answer_payload = payloads[q.name][item.name][\'payload\']\n # item.estimated_time = payloads[q.name][item.name].get("time", 1)\n # q.estimated_time = payloads[q.name].get("time", 1)\n # if "precomputed" in payloads[q.name][item.name]: # Consider removing later.\n # item._precomputed_payload = payloads[q.name][item.name][\'precomputed\']\n # try:\n # if "title" in payloads[q.name][item.name]: # can perhaps be removed later.\n # item.title = payloads[q.name][item.name][\'title\']\n # except Exception as e: # Cannot set attribute error. The title is a function (and probably should not be).\n # pass\n # # print("bad", e)\n # self.payloads = payloads\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct+1)\n if i > 0 and l.find("|", i+1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = \'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar",show_progress_bar=True):\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.1\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n\n # self.pbar = tqdm.tqdm(total=self.n)\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if hasattr(self, \'pbar\') and self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar=None\n\n sys.stdout.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=sys.stdout, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\') # , unit_scale=dt, unit=\'seconds\'):\n\n for _ in range(self.n-1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\n\n\nfrom unittest.suite import _isnotsuite\n\nclass MySuite(unittest.suite.TestSuite): # Not sure we need this one anymore.\n pass\n\ndef instance_call_stack(instance):\n s = "-".join(map(lambda x: x.__name__, instance.__class__.mro()))\n return s\n\ndef get_class_that_defined_method(meth):\n for cls in inspect.getmro(meth.im_class):\n if meth.__name__ in cls.__dict__:\n return cls\n return None\n\ndef caller_name(skip=2):\n """Get a name of a caller in the format module.class.method\n\n `skip` specifies how many levels of stack to skip while getting caller\n name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.\n\n An empty string is returned if skipped levels exceed stack height\n """\n stack = inspect.stack()\n start = 0 + skip\n if len(stack) < start + 1:\n return \'\'\n parentframe = stack[start][0]\n\n name = []\n module = inspect.getmodule(parentframe)\n # `modname` can be None when frame is executed directly in console\n # TODO(techtonik): consider using __main__\n if module:\n name.append(module.__name__)\n # detect classname\n if \'self\' in parentframe.f_locals:\n # I don\'t know any way to detect call from the object method\n # XXX: there seems to be no way to detect static method call - it will\n # be just a function call\n name.append(parentframe.f_locals[\'self\'].__class__.__name__)\n codename = parentframe.f_code.co_name\n if codename != \'<module>\': # top level usually\n name.append( codename ) # function or a method\n\n ## Avoid circular refs and frame leaks\n # https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack\n del parentframe, stack\n\n return ".".join(name)\n\ndef get_class_from_frame(fr):\n import inspect\n args, _, _, value_dict = inspect.getargvalues(fr)\n # we check the first parameter for the frame function is\n # named \'self\'\n if len(args) and args[0] == \'self\':\n # in that case, \'self\' will be referenced in value_dict\n instance = value_dict.get(\'self\', None)\n if instance:\n # return its class\n # isinstance(instance, Testing) # is the actual class instance.\n\n return getattr(instance, \'__class__\', None)\n # return None otherwise\n return None\n\nfrom typing import Any\nimport inspect, gc\n\ndef giveupthefunc():\n frame = inspect.currentframe()\n code = frame.f_code\n globs = frame.f_globals\n functype = type(lambda: 0)\n funcs = []\n for func in gc.get_referrers(code):\n if type(func) is functype:\n if getattr(func, "__code__", None) is code:\n if getattr(func, "__globals__", None) is globs:\n funcs.append(func)\n if len(funcs) > 1:\n return None\n return funcs[0] if funcs else None\n\n\nfrom collections import defaultdict\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n # if self.dots or self.showAll:\n # self.stream.writeln()\n # if hasattr(self, \'cc\'):\n # self.cc.terminate()\n # self.cc_terminate(success=False)\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n # if self.showAll:\n # self.stream.writeln("FAIL")\n # elif self.dots:\n # self.stream.write(\'F\')\n # self.stream.flush()\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n # super().addSuccess(test)\n self.successes.append(test)\n # super().addSuccess(test)\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n self.cc_terminate()\n\n\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n sys.stdout.flush()\n ss = self.item_title_print\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(ss))), end="")\n # current = 1\n # possible = 1\n # current == possible\n ss = "PASS" if success else "FAILED"\n if tsecs >= 0.1:\n ss += " (" + str(tsecs) + " seconds)"\n print(ss)\n\n\n def startTest(self, test):\n # super().startTest(test)\n j =self.testsRun\n self.testsRun += 1\n # print("Starting the test...")\n # show_progress_bar = True\n n = UTextResult.number\n\n item_title = self.getDescription(test)\n item_title = item_title.split("\\n")[0]\n\n item_title = test.shortDescription() # Better for printing (get from cache).\n # test.countTestCases()\n self.item_title_print = "*** q%i.%i) %s" % (n + 1, j + 1, item_title)\n estimated_time = 10\n nL = 80\n #\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar)\n else:\n print(self.item_title_print + (\'.\' * max(0, nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 2\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n # q_title_print = "some printed title..."\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass == None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n print(self.cc.title, end="")\n # start = 10\n # q_time = np.round(time.time() - start, 2)\n nL = 80\n print(" " * max(0, nL - len(self.cc.title)) + (\n " (" + str(q_time) + " seconds)" if q_time >= 0.1 else "")) # if q.name in report.payloads else "")\n # print("=" * nL)\n\nfrom unittest.runner import _WritelnDecorator\nfrom io import StringIO\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n from io import StringIO\n stream = StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\ndef wrapper(foo):\n def magic(self):\n s = "-".join(map(lambda x: x.__name__, self.__class__.mro()))\n # print(s)\n foo(self)\n magic.__doc__ = foo.__doc__\n return magic\n\nfrom functools import update_wrapper, _make_key, RLock\nfrom collections import namedtuple\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)) )\n # key = (self.cache_id(), \'@cache\')\n # if self._cache_contains[key]\n\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n return wrapper\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n\n @classmethod\n def question_title(cls):\n return cls.__doc__.splitlines()[0].strip() if cls.__doc__ != None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd == None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n # self._testMethodDoc.strip().splitlines()[0].strip()\n sd = self.shortDescriptionStandard()\n title = self._cache_get( (self.cache_id(), \'title\'), sd )\n return title if title != None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n # def _callSetUp(self):\n # # Always run before method is called.\n # print("asdf")\n # pass\n # @classmethod\n # def setUpClass(cls):\n # # self._cache_put((self.cache_id(), \'title\'), value)\n # cls.reset()\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome == None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc != None:\n # Ensure the cache is eventually updated with the right docstring.\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard() )\n # Fix temp cache here (for using the @cache decorator)\n self._cache2[ (self.cache_id(), \'assert\') ] = {}\n\n res = testMethod()\n elapsed = time.time() - t\n # self._cache_put( (self.cache_id(), \'title\'), self.shortDescription() )\n\n self._get_outcome()[self.cache_id()] = res\n self._cache_put( (self.cache_id(), "time"), elapsed)\n\n # This is my base test class. So what is new about it?\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return (c,m)\n\n # def unique_cache_id(self):\n # k0 = self.cache_id()\n # # key = ()\n # i = 0\n # for i in itertools.count():\n # # key = k0 + (i,)\n # if i not in self._cache_get( (k0, \'assert\') ):\n # break\n # return i\n # return key\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n # self.cache_indexes = defaultdict(lambda: 0)\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n #\n # def _cache2_contains(self, key):\n # print("Is this needed?")\n # self._ensure_cache_exists()\n # return key in self.__class__._cache2\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n cache = self._cache_get(key, {})\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, first)\n assert_fun(first, _expected, *args, **kwargs)\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__) ) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache != None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n # print("Loading cache from", cfile)\n if os.path.exists(cfile):\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n else:\n print("Warning! data file not found", cfile)\n\ndef hide(func):\n return func\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n # (*)We can be somewhat "hygienic", but newDecorator still isn\'t signature-preserving, i.e. you will not be able to get a runtime list of parameters. For that, you need hackish libraries...but in this case, the only argument is func, so it\'s not a big issue\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\n\nimport inspect\nimport os\nimport argparse\nimport sys\nimport time\nimport threading # don\'t import Thread bc. of minify issue.\nimport tqdm # don\'t do from tqdm import tqdm because of minify-issue\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n # try: # For registering stats.\n # import unitgrade_private\n # import irlc.lectures\n # import xlwings\n # from openpyxl import Workbook\n # import pandas as pd\n # from collections import defaultdict\n # dd = defaultdict(lambda: [])\n # error_computed = []\n # for k1, (q, _) in enumerate(report.questions):\n # for k2, item in enumerate(q.items):\n # dd[\'question_index\'].append(k1)\n # dd[\'item_index\'].append(k2)\n # dd[\'question\'].append(q.name)\n # dd[\'item\'].append(item.name)\n # dd[\'tol\'].append(0 if not hasattr(item, \'tol\') else item.tol)\n # error_computed.append(0 if not hasattr(item, \'error_computed\') else item.error_computed)\n #\n # qstats = report.wdir + "/" + report.name + ".xlsx"\n #\n # if os.path.isfile(qstats):\n # d_read = pd.read_excel(qstats).to_dict()\n # else:\n # d_read = dict()\n #\n # for k in range(1000):\n # key = \'run_\'+str(k)\n # if key in d_read:\n # dd[key] = list(d_read[\'run_0\'].values())\n # else:\n # dd[key] = error_computed\n # break\n #\n # workbook = Workbook()\n # worksheet = workbook.active\n # for col, key in enumerate(dd.keys()):\n # worksheet.cell(row=1, column=col+1).value = key\n # for row, item in enumerate(dd[key]):\n # worksheet.cell(row=row+2, column=col+1).value = item\n #\n # workbook.save(qstats)\n # workbook.close()\n #\n # except ModuleNotFoundError as e:\n # s = 234\n # pass\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n print(b + " v" + __version__)\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print("Evaluating " + s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n nL = 80\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n # q = q()\n # q_hidden = False\n # q_hidden = issubclass(q.__class__, Hidden)\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n # unittest.Te\n # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_]\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n # res = UTextTestRunner(verbosity=2, resultclass=unittest.TextTestResult).run(suite)\n z = 234\n # for j, item in enumerate(q.items):\n # if qitem is not None and question is not None and j+1 != qitem:\n # continue\n #\n # if q_with_outstanding_init is not None: # check for None bc. this must be called to set titles.\n # # if not item.question.has_called_init_:\n # start = time.time()\n #\n # cc = None\n # if show_progress_bar:\n # total_estimated_time = q.estimated_time # Use this. The time is estimated for the q itself. # sum( [q2.estimated_time for q2 in q_with_outstanding_init] )\n # cc = ActiveProgress(t=total_estimated_time, title=q_title_print)\n # from unitgrade import Capturing # DON\'T REMOVE THIS LINE\n # with eval(\'Capturing\')(unmute=unmute): # Clunky import syntax is required bc. of minify issue.\n # try:\n # for q2 in q_with_outstanding_init:\n # q2.init()\n # q2.has_called_init_ = True\n #\n # # item.question.init() # Initialize the question. Useful for sharing resources.\n # except Exception as e:\n # if not passall:\n # if not silent:\n # print(" ")\n # print("="*30)\n # print(f"When initializing question {q.title} the initialization code threw an error")\n # print(e)\n # print("The remaining parts of this question will likely fail.")\n # print("="*30)\n #\n # if show_progress_bar:\n # cc.terminate()\n # sys.stdout.flush()\n # print(q_title_print, end="")\n #\n # q_time =np.round( time.time()-start, 2)\n #\n # print(" "* max(0,nL - len(q_title_print) ) + (" (" + str(q_time) + " seconds)" if q_time >= 0.1 else "") ) # if q.name in report.payloads else "")\n # print("=" * nL)\n # q_with_outstanding_init = None\n #\n # # item.question = q # Set the parent question instance for later reference.\n # item_title_print = ss = "*** q%i.%i) %s"%(n+1, j+1, item.title)\n #\n # if show_progress_bar:\n # cc = ActiveProgress(t=item.estimated_time, title=item_title_print)\n # else:\n # print(item_title_print + ( \'.\'*max(0, nL-4-len(ss)) ), end="")\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n # if show_progress_bar:\n # cc.terminate()\n # sys.stdout.flush()\n # print(item_title_print + (\'.\' * max(0, nL - 4 - len(ss))), end="")\n #\n # if not hidden:\n # ss = "PASS" if current == possible else "*** FAILED"\n # if tsecs >= 0.1:\n # ss += " ("+ str(tsecs) + " seconds)"\n # print(ss)\n\n # ws, possible, obtained = upack(q_)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n # possible = int(ws @ possible)\n # obtained = int(ws @ obtained)\n # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"*** Question q{n+1}"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n if m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package))\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n print(" ")\n print("="*n)\n print("Final evaluation")\n print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f"*** {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single file: ")\n print(">", token)\n print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\nfrom cs101.homework1 import reverse_list, add\nimport unittest\n\nclass Week1(unittest.TestCase):\n def test_add(self):\n self.assertEqual(add(2,2), 4)\n self.assertEqual(add(-100, 5), -95)\n\n def test_reverse(self):\n self.assertEqual(reverse_list([1,2,3]), [3,2,1])\n # print("Bad output\\n\\n")\n\n\nimport cs101\nclass Report1(Report):\n title = "CS 101 Report 1"\n questions = [(Week1, 10)] # Include a single question for 10 credits.\n pack_imports = [cs101]' +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n\n"""\n# from . import cache_read\nimport unittest\nimport numpy as np\nimport sys\nfrom io import StringIO\nimport collections\nimport re\nimport threading\nimport tqdm\nimport time\nimport pickle\nimport itertools\nimport os\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\ndef setup_dir_by_class(C,base_dir):\n name = C.__class__.__name__\n # base_dir = os.path.join(base_dir, name)\n # if not os.path.isdir(base_dir):\n # os.makedirs(base_dir)\n return base_dir, name\n\nclass Hidden:\n def hide(self):\n return True\n\nclass Logger(object):\n def __init__(self, buffer):\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\nclass Capturing(list):\n def __init__(self, *args, unmute=False, **kwargs):\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass QItem(unittest.TestCase):\n title = None\n testfun = None\n tol = 0\n estimated_time = 0.42\n _precomputed_payload = None\n _computed_answer = None # Internal helper to later get results.\n weight = 1 # the weight of the question.\n\n def __init__(self, question=None, *args, **kwargs):\n if self.tol > 0 and self.testfun is None:\n self.testfun = self.assertL2Relative\n elif self.testfun is None:\n self.testfun = self.assertEqual\n\n self.name = self.__class__.__name__\n # self._correct_answer_payload = correct_answer_payload\n self.question = question\n\n super().__init__(*args, **kwargs)\n if self.title is None:\n self.title = self.name\n\n def _safe_get_title(self):\n if self._precomputed_title is not None:\n return self._precomputed_title\n return self.title\n\n def assertNorm(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed).flat- np.asarray(expected)).flat )\n nrm = np.sqrt(np.sum( diff ** 2))\n\n self.error_computed = nrm\n\n if nrm > tol:\n print(f"Not equal within tolerance {tol}; norm of difference was {nrm}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def assertL2(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n self.error_computed = np.max(diff)\n\n if np.max(diff) > tol:\n print(f"Not equal within tolerance {tol=}; deviation was {np.max(diff)=}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol=}, {np.max(diff)=}")\n\n def assertL2Relative(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n diff = diff / (1e-8 + np.abs( (np.asarray(computed) + np.asarray(expected)) ) )\n self.error_computed = np.max(np.abs(diff))\n if np.sum(diff > tol) > 0:\n print(f"Not equal within tolerance {tol}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def precomputed_payload(self):\n return self._precomputed_payload\n\n def precompute_payload(self):\n # Pre-compute resources to include in tests (useful for getting around rng).\n pass\n\n def compute_answer(self, unmute=False):\n raise NotImplementedError("test code here")\n\n def test(self, computed, expected):\n self.testfun(computed, expected)\n\n def get_points(self, verbose=False, show_expected=False, show_computed=False,unmute=False, passall=False, silent=False, **kwargs):\n possible = 1\n computed = None\n def show_computed_(computed):\n print(">>> Your output:")\n print(computed)\n\n def show_expected_(expected):\n print(">>> Expected output (note: may have been processed; read text script):")\n print(expected)\n\n correct = self._correct_answer_payload\n try:\n if unmute: # Required to not mix together print stuff.\n print("")\n computed = self.compute_answer(unmute=unmute)\n except Exception as e:\n if not passall:\n if not silent:\n print("\\n=================================================================================")\n print(f"When trying to run test class \'{self.name}\' your code threw an error:", e)\n show_expected_(correct)\n import traceback\n print(traceback.format_exc())\n print("=================================================================================")\n return (0, possible)\n\n if self._computed_answer is None:\n self._computed_answer = computed\n\n if show_expected or show_computed:\n print("\\n")\n if show_expected:\n show_expected_(correct)\n if show_computed:\n show_computed_(computed)\n try:\n if not passall:\n self.test(computed=computed, expected=correct)\n except Exception as e:\n if not silent:\n print("\\n=================================================================================")\n print(f"Test output from test class \'{self.name}\' does not match expected result. Test error:")\n print(e)\n show_computed_(computed)\n show_expected_(correct)\n return (0, possible)\n return (1, possible)\n\n def score(self):\n try:\n self.test()\n except Exception as e:\n return 0\n return 1\n\nclass QPrintItem(QItem):\n def compute_answer_print(self):\n """\n Generate output which is to be tested. By default, both text written to the terminal using print(...) as well as return values\n are send to process_output (see compute_answer below). In other words, the text generated is:\n\n res = compute_Answer_print()\n txt = (any terminal output generated above)\n numbers = (any numbers found in terminal-output txt)\n\n self.test(process_output(res, txt, numbers), <expected result>)\n\n :return: Optional values for comparison\n """\n raise Exception("Generate output here. The output is passed to self.process_output")\n\n def process_output(self, res, txt, numbers):\n return res\n\n def compute_answer(self, unmute=False):\n with Capturing(unmute=unmute) as output:\n res = self.compute_answer_print()\n s = "\\n".join(output)\n s = rm_progress_bar(s) # Remove progress bar.\n numbers = extract_numbers(s)\n self._computed_answer = (res, s, numbers)\n return self.process_output(res, s, numbers)\n\nclass OrderedClassMembers(type):\n @classmethod\n def __prepare__(self, name, bases):\n return collections.OrderedDict()\n def __new__(self, name, bases, classdict):\n ks = list(classdict.keys())\n for b in bases:\n ks += b.__ordered__\n classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n return type.__new__(self, name, bases, classdict)\n\nclass QuestionGroup(metaclass=OrderedClassMembers):\n title = "Untitled question"\n partially_scored = False\n t_init = 0 # Time spend on initialization (placeholder; set this externally).\n estimated_time = 0.42\n has_called_init_ = False\n _name = None\n _items = None\n\n @property\n def items(self):\n if self._items == None:\n self._items = []\n members = [gt for gt in [getattr(self, gt) for gt in self.__ordered__ if gt not in ["__classcell__", "__init__"]] if inspect.isclass(gt) and issubclass(gt, QItem)]\n for I in members:\n self._items.append( I(question=self))\n return self._items\n\n @items.setter\n def items(self, value):\n self._items = value\n\n @property\n def name(self):\n if self._name == None:\n self._name = self.__class__.__name__\n return self._name #\n\n @name.setter\n def name(self, val):\n self._name = val\n\n def init(self):\n # Can be used to set resources relevant for this question instance.\n pass\n\n def init_all_item_questions(self):\n for item in self.items:\n if not item.question.has_called_init_:\n item.question.init()\n item.question.has_called_init_ = True\n\n\nclass Report():\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 80 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q,_) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n root_dir = self.pack_imports[0].__path__._path[0]\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q,_) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n # else:\n # if os.path.isfile(self.computed_answers_file):\n # self.set_payload(cache_read(self.computed_answers_file), strict=strict)\n # else:\n # s = f"> Warning: The pre-computed answer file, {os.path.abspath(self.computed_answers_file)} is missing. The framework will NOT work as intended. Reasons may be a broken local installation."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n import unittest\n loader = unittest.TestLoader()\n for q,_ in self.questions:\n import time\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self):\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\':True}\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n # for item in q.items:\n # if q.name not in payloads or item.name not in payloads[q.name]:\n # s = f"> Broken resource dictionary submitted to unitgrade for question {q.name} and subquestion {item.name}. Framework will not work."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n # else:\n # item._correct_answer_payload = payloads[q.name][item.name][\'payload\']\n # item.estimated_time = payloads[q.name][item.name].get("time", 1)\n # q.estimated_time = payloads[q.name].get("time", 1)\n # if "precomputed" in payloads[q.name][item.name]: # Consider removing later.\n # item._precomputed_payload = payloads[q.name][item.name][\'precomputed\']\n # try:\n # if "title" in payloads[q.name][item.name]: # can perhaps be removed later.\n # item.title = payloads[q.name][item.name][\'title\']\n # except Exception as e: # Cannot set attribute error. The title is a function (and probably should not be).\n # pass\n # # print("bad", e)\n # self.payloads = payloads\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct+1)\n if i > 0 and l.find("|", i+1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = \'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar",show_progress_bar=True):\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.1\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n\n # self.pbar = tqdm.tqdm(total=self.n)\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if hasattr(self, \'pbar\') and self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar=None\n\n sys.stdout.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=sys.stdout, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\') # , unit_scale=dt, unit=\'seconds\'):\n\n for _ in range(self.n-1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\n\n\nfrom unittest.suite import _isnotsuite\n\nclass MySuite(unittest.suite.TestSuite): # Not sure we need this one anymore.\n pass\n\ndef instance_call_stack(instance):\n s = "-".join(map(lambda x: x.__name__, instance.__class__.mro()))\n return s\n\ndef get_class_that_defined_method(meth):\n for cls in inspect.getmro(meth.im_class):\n if meth.__name__ in cls.__dict__:\n return cls\n return None\n\ndef caller_name(skip=2):\n """Get a name of a caller in the format module.class.method\n\n `skip` specifies how many levels of stack to skip while getting caller\n name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.\n\n An empty string is returned if skipped levels exceed stack height\n """\n stack = inspect.stack()\n start = 0 + skip\n if len(stack) < start + 1:\n return \'\'\n parentframe = stack[start][0]\n\n name = []\n module = inspect.getmodule(parentframe)\n # `modname` can be None when frame is executed directly in console\n # TODO(techtonik): consider using __main__\n if module:\n name.append(module.__name__)\n # detect classname\n if \'self\' in parentframe.f_locals:\n # I don\'t know any way to detect call from the object method\n # XXX: there seems to be no way to detect static method call - it will\n # be just a function call\n name.append(parentframe.f_locals[\'self\'].__class__.__name__)\n codename = parentframe.f_code.co_name\n if codename != \'<module>\': # top level usually\n name.append( codename ) # function or a method\n\n ## Avoid circular refs and frame leaks\n # https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack\n del parentframe, stack\n\n return ".".join(name)\n\ndef get_class_from_frame(fr):\n import inspect\n args, _, _, value_dict = inspect.getargvalues(fr)\n # we check the first parameter for the frame function is\n # named \'self\'\n if len(args) and args[0] == \'self\':\n # in that case, \'self\' will be referenced in value_dict\n instance = value_dict.get(\'self\', None)\n if instance:\n # return its class\n # isinstance(instance, Testing) # is the actual class instance.\n\n return getattr(instance, \'__class__\', None)\n # return None otherwise\n return None\n\nfrom typing import Any\nimport inspect, gc\n\ndef giveupthefunc():\n frame = inspect.currentframe()\n code = frame.f_code\n globs = frame.f_globals\n functype = type(lambda: 0)\n funcs = []\n for func in gc.get_referrers(code):\n if type(func) is functype:\n if getattr(func, "__code__", None) is code:\n if getattr(func, "__globals__", None) is globs:\n funcs.append(func)\n if len(funcs) > 1:\n return None\n return funcs[0] if funcs else None\n\n\nfrom collections import defaultdict\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n # if self.dots or self.showAll:\n # self.stream.writeln()\n # if hasattr(self, \'cc\'):\n # self.cc.terminate()\n # self.cc_terminate(success=False)\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n # if self.showAll:\n # self.stream.writeln("FAIL")\n # elif self.dots:\n # self.stream.write(\'F\')\n # self.stream.flush()\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n # super().addSuccess(test)\n self.successes.append(test)\n # super().addSuccess(test)\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n self.cc_terminate()\n\n\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n sys.stdout.flush()\n ss = self.item_title_print\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(ss))), end="")\n # current = 1\n # possible = 1\n # current == possible\n ss = "PASS" if success else "FAILED"\n if tsecs >= 0.1:\n ss += " (" + str(tsecs) + " seconds)"\n print(ss)\n\n\n def startTest(self, test):\n # super().startTest(test)\n j =self.testsRun\n self.testsRun += 1\n # print("Starting the test...")\n # show_progress_bar = True\n n = UTextResult.number\n\n item_title = self.getDescription(test)\n item_title = item_title.split("\\n")[0]\n\n item_title = test.shortDescription() # Better for printing (get from cache).\n # test.countTestCases()\n self.item_title_print = "*** q%i.%i) %s" % (n + 1, j + 1, item_title)\n estimated_time = 10\n nL = 80\n #\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar)\n else:\n print(self.item_title_print + (\'.\' * max(0, nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 2\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n # q_title_print = "some printed title..."\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass == None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n print(self.cc.title, end="")\n # start = 10\n # q_time = np.round(time.time() - start, 2)\n nL = 80\n print(" " * max(0, nL - len(self.cc.title)) + (\n " (" + str(q_time) + " seconds)" if q_time >= 0.1 else "")) # if q.name in report.payloads else "")\n # print("=" * nL)\n\nfrom unittest.runner import _WritelnDecorator\nfrom io import StringIO\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n from io import StringIO\n stream = StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\ndef wrapper(foo):\n def magic(self):\n s = "-".join(map(lambda x: x.__name__, self.__class__.mro()))\n # print(s)\n foo(self)\n magic.__doc__ = foo.__doc__\n return magic\n\nfrom functools import update_wrapper, _make_key, RLock\nfrom collections import namedtuple\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)) )\n # key = (self.cache_id(), \'@cache\')\n # if self._cache_contains[key]\n\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n return wrapper\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n\n @classmethod\n def question_title(cls):\n return cls.__doc__.splitlines()[0].strip() if cls.__doc__ != None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd == None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n # self._testMethodDoc.strip().splitlines()[0].strip()\n sd = self.shortDescriptionStandard()\n title = self._cache_get( (self.cache_id(), \'title\'), sd )\n return title if title != None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n # def _callSetUp(self):\n # # Always run before method is called.\n # print("asdf")\n # pass\n # @classmethod\n # def setUpClass(cls):\n # # self._cache_put((self.cache_id(), \'title\'), value)\n # cls.reset()\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome == None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc != None:\n # Ensure the cache is eventually updated with the right docstring.\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard() )\n # Fix temp cache here (for using the @cache decorator)\n self._cache2[ (self.cache_id(), \'assert\') ] = {}\n\n res = testMethod()\n elapsed = time.time() - t\n # self._cache_put( (self.cache_id(), \'title\'), self.shortDescription() )\n\n self._get_outcome()[self.cache_id()] = res\n self._cache_put( (self.cache_id(), "time"), elapsed)\n\n # This is my base test class. So what is new about it?\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return (c,m)\n\n # def unique_cache_id(self):\n # k0 = self.cache_id()\n # # key = ()\n # i = 0\n # for i in itertools.count():\n # # key = k0 + (i,)\n # if i not in self._cache_get( (k0, \'assert\') ):\n # break\n # return i\n # return key\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n # self.cache_indexes = defaultdict(lambda: 0)\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n #\n # def _cache2_contains(self, key):\n # print("Is this needed?")\n # self._ensure_cache_exists()\n # return key in self.__class__._cache2\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n cache = self._cache_get(key, {})\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, first)\n assert_fun(first, _expected, *args, **kwargs)\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__) ) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache != None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n # print("Loading cache from", cfile)\n if os.path.exists(cfile):\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n else:\n print("Warning! data file not found", cfile)\n\ndef hide(func):\n return func\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n # (*)We can be somewhat "hygienic", but newDecorator still isn\'t signature-preserving, i.e. you will not be able to get a runtime list of parameters. For that, you need hackish libraries...but in this case, the only argument is func, so it\'s not a big issue\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\n\nimport inspect\nimport os\nimport argparse\nimport sys\nimport time\nimport threading # don\'t import Thread bc. of minify issue.\nimport tqdm # don\'t do from tqdm import tqdm because of minify-issue\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n # try: # For registering stats.\n # import unitgrade_private\n # import irlc.lectures\n # import xlwings\n # from openpyxl import Workbook\n # import pandas as pd\n # from collections import defaultdict\n # dd = defaultdict(lambda: [])\n # error_computed = []\n # for k1, (q, _) in enumerate(report.questions):\n # for k2, item in enumerate(q.items):\n # dd[\'question_index\'].append(k1)\n # dd[\'item_index\'].append(k2)\n # dd[\'question\'].append(q.name)\n # dd[\'item\'].append(item.name)\n # dd[\'tol\'].append(0 if not hasattr(item, \'tol\') else item.tol)\n # error_computed.append(0 if not hasattr(item, \'error_computed\') else item.error_computed)\n #\n # qstats = report.wdir + "/" + report.name + ".xlsx"\n #\n # if os.path.isfile(qstats):\n # d_read = pd.read_excel(qstats).to_dict()\n # else:\n # d_read = dict()\n #\n # for k in range(1000):\n # key = \'run_\'+str(k)\n # if key in d_read:\n # dd[key] = list(d_read[\'run_0\'].values())\n # else:\n # dd[key] = error_computed\n # break\n #\n # workbook = Workbook()\n # worksheet = workbook.active\n # for col, key in enumerate(dd.keys()):\n # worksheet.cell(row=1, column=col+1).value = key\n # for row, item in enumerate(dd[key]):\n # worksheet.cell(row=row+2, column=col+1).value = item\n #\n # workbook.save(qstats)\n # workbook.close()\n #\n # except ModuleNotFoundError as e:\n # s = 234\n # pass\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n print(b + " v" + __version__)\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print("Evaluating " + s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n nL = 80\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n # q = q()\n # q_hidden = False\n # q_hidden = issubclass(q.__class__, Hidden)\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n # unittest.Te\n # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_]\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n # res = UTextTestRunner(verbosity=2, resultclass=unittest.TextTestResult).run(suite)\n z = 234\n # for j, item in enumerate(q.items):\n # if qitem is not None and question is not None and j+1 != qitem:\n # continue\n #\n # if q_with_outstanding_init is not None: # check for None bc. this must be called to set titles.\n # # if not item.question.has_called_init_:\n # start = time.time()\n #\n # cc = None\n # if show_progress_bar:\n # total_estimated_time = q.estimated_time # Use this. The time is estimated for the q itself. # sum( [q2.estimated_time for q2 in q_with_outstanding_init] )\n # cc = ActiveProgress(t=total_estimated_time, title=q_title_print)\n # from unitgrade import Capturing # DON\'T REMOVE THIS LINE\n # with eval(\'Capturing\')(unmute=unmute): # Clunky import syntax is required bc. of minify issue.\n # try:\n # for q2 in q_with_outstanding_init:\n # q2.init()\n # q2.has_called_init_ = True\n #\n # # item.question.init() # Initialize the question. Useful for sharing resources.\n # except Exception as e:\n # if not passall:\n # if not silent:\n # print(" ")\n # print("="*30)\n # print(f"When initializing question {q.title} the initialization code threw an error")\n # print(e)\n # print("The remaining parts of this question will likely fail.")\n # print("="*30)\n #\n # if show_progress_bar:\n # cc.terminate()\n # sys.stdout.flush()\n # print(q_title_print, end="")\n #\n # q_time =np.round( time.time()-start, 2)\n #\n # print(" "* max(0,nL - len(q_title_print) ) + (" (" + str(q_time) + " seconds)" if q_time >= 0.1 else "") ) # if q.name in report.payloads else "")\n # print("=" * nL)\n # q_with_outstanding_init = None\n #\n # # item.question = q # Set the parent question instance for later reference.\n # item_title_print = ss = "*** q%i.%i) %s"%(n+1, j+1, item.title)\n #\n # if show_progress_bar:\n # cc = ActiveProgress(t=item.estimated_time, title=item_title_print)\n # else:\n # print(item_title_print + ( \'.\'*max(0, nL-4-len(ss)) ), end="")\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n # if show_progress_bar:\n # cc.terminate()\n # sys.stdout.flush()\n # print(item_title_print + (\'.\' * max(0, nL - 4 - len(ss))), end="")\n #\n # if not hidden:\n # ss = "PASS" if current == possible else "*** FAILED"\n # if tsecs >= 0.1:\n # ss += " ("+ str(tsecs) + " seconds)"\n # print(ss)\n\n # ws, possible, obtained = upack(q_)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n # possible = int(ws @ possible)\n # obtained = int(ws @ obtained)\n # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"*** Question q{n+1}"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n if m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package))\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n print(" ")\n print("="*n)\n print("Final evaluation")\n print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f"*** {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single file: ")\n print(">", token)\n print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\nfrom programs.homework1 import reverse_list, add\nimport unittest\n\nclass Week1(unittest.TestCase):\n def test_add(self):\n self.assertEqual(add(2,2), 4)\n self.assertEqual(add(-100, 5), -95)\n\n def test_reverse(self):\n self.assertEqual(reverse_list([1,2,3]), [3,2,1])\n # print("Bad output\\n\\n")\n\n\nimport programs\nclass Report1(Report):\n title = "CS 101 Report 1"\n questions = [(Week1, 10)] # Include a single question for 10 credits.\n pack_imports = [programs]' report1_payload = '8004953f000000000000007d948c055765656b31947d948c2c6e6f20636163686520736565205f73657475705f616e737765727320696e20756e69746772616465322e7079948873732e' name="Report1" diff --git a/examples/autolab_example/tmp/cs102/src/driver_python.py b/examples/autolab_example/tmp/cs102/src/driver_python.py index 092842a..80da44e 100644 --- a/examples/autolab_example/tmp/cs102/src/driver_python.py +++ b/examples/autolab_example/tmp/cs102/src/driver_python.py @@ -55,8 +55,8 @@ def rcom(cm): start = time.time() rcom(command) # pfiles() -# for f in glob.glob(host_tmp_dir + "/cs101/*"): -# print("cs101/", f) +# for f in glob.glob(host_tmp_dir + "/programs/*"): +# print("programs/", f) # print("---") ls = glob.glob(token) # print(ls) diff --git a/examples/autolab_example/tmp/cs103/src/driver_python.py b/examples/autolab_example/tmp/cs103/src/driver_python.py index ed9bd8b..34e6b0b 100644 --- a/examples/autolab_example/tmp/cs103/src/driver_python.py +++ b/examples/autolab_example/tmp/cs103/src/driver_python.py @@ -55,8 +55,8 @@ def rcom(cm): start = time.time() rcom(command) # pfiles() -# for f in glob.glob(host_tmp_dir + "/cs101/*"): -# print("cs101/", f) +# for f in glob.glob(host_tmp_dir + "/programs/*"): +# print("programs/", f) # print("---") ls = glob.glob(token) # print(ls) diff --git a/examples/example_docker/instructor/cs103/.coverage b/examples/example_docker/instructor/cs103/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..f386b2198168450113494de5b3b3bd99d653d108 GIT binary patch literal 53248 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 Z7!85Z5Eu=C(GVC7fzc2c4FSR-0004q+NS^j literal 0 HcmV?d00001 diff --git a/examples/example_docker/instructor/cs103/Report3_handin_25_of_30.token b/examples/example_docker/instructor/cs103/Report3_handin_25_of_30.token new file mode 100644 index 0000000000000000000000000000000000000000..af7c703d2692f8bf15644581b9cdf6688b4b9c3b GIT binary patch literal 117485 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 UH)Kj`4+|($O@S#aEiTmq064_Sm;e9( literal 0 HcmV?d00001 diff --git a/examples/example_docker/instructor/cs103/Report3_handin_30_of_30.token b/examples/example_docker/instructor/cs103/Report3_handin_30_of_30.token new file mode 100644 index 0000000000000000000000000000000000000000..0ddcac53870efc12081817272c4b694c4c34fe9e GIT binary patch literal 117467 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 Sl++#;P!^g3lP@hU)dK(kdBY6= literal 0 HcmV?d00001 diff --git a/examples/example_docker/instructor/cs103/__pycache__/homework1.cpython-38.pyc b/examples/example_docker/instructor/cs103/__pycache__/homework1.cpython-38.pyc index 4beaff64aef655b294b4513aa4842f7eada9c024..c2ad43883cce209ac9cd30ec4db9f197297be5f1 100644 GIT binary patch delta 20 bcmX@ec94xbl$V!_fq{WRdWykDZZl>8EsX=` delta 20 bcmX@ec94xbl$V!_fq{YH<6)hR+-A%GG;IXv diff --git a/examples/example_docker/instructor/cs103/__pycache__/report3.cpython-38.pyc b/examples/example_docker/instructor/cs103/__pycache__/report3.cpython-38.pyc index da19a02cc1c508d3eaafcf35b0b34e9d58501d7e..78157d3ccd77777a3e6bdc5d8ef57fa286b7b497 100644 GIT binary patch delta 25 gcmaFC^@58pl$V!_fq{WRf388I7xPBGNLEH>08T#zHvj+t delta 25 gcmaFC^@58pl$V!_fq{WR>7P#Gai)!Yk*ti209Azs0RR91 diff --git a/examples/example_docker/instructor/cs103/__pycache__/report3_complete.cpython-38.pyc b/examples/example_docker/instructor/cs103/__pycache__/report3_complete.cpython-38.pyc index 921af5c7208e7aa4d953fa1e4551029aeb05c182..ca3fa7b43ad2a579965093501504022367ac79d6 100644 GIT binary patch delta 53 zcmcb@dy<zol$V!_fq{X+Zm~h)+Ks#hY^;*SMag=T?buZL_&}`Eyv&mHqQsQc$qj75 GjLZOZLk^|@ delta 64 zcmX@fdxe)bl$V!_fq{X6_n%Ip=|)}yHb&9OPHd{OvZZ;MCFw<pDXB(!aAtf)YED6F MQSszbwqQm^04-D!>Hq)$ diff --git a/examples/example_docker/instructor/cs103/deploy.py b/examples/example_docker/instructor/cs103/deploy.py index 9379f59..e1350f9 100644 --- a/examples/example_docker/instructor/cs103/deploy.py +++ b/examples/example_docker/instructor/cs103/deploy.py @@ -4,25 +4,24 @@ from unitgrade_private2.hidden_create_files import setup_grade_file_report from unitgrade_private2.hidden_gather_upload import gather_upload_to_campusnet from unitgrade_private2.deployment import remove_hidden_methods from unitgrade_private2.docker_helpers import docker_run_token_file -import shutil import os import glob import pickle from snipper.snip_dir import snip_dir +wd = os.path.dirname(__file__) def deploy_student_files(): setup_grade_file_report(Report3, minify=False, obfuscate=False, execute=False) - # Report3.reset() fout, ReportWithoutHidden = remove_hidden_methods(Report3, outfile="report3.py") setup_grade_file_report(ReportWithoutHidden, minify=False, obfuscate=False, execute=False) - sdir = "../../students/cs103" - snip_dir(source_dir="../cs103", dest_dir=sdir, clean_destination_dir=True, exclude=['__pycache__', '*.token', 'deploy.py', 'report3_complete*.py']) + sdir = wd+"/../../students/cs103" + snip_dir(source_dir=wd+"/../cs103", dest_dir=sdir, clean_destination_dir=True, exclude=['__pycache__', '*.token', 'deploy.py', 'report3_complete*.py']) return sdir def run_student_code_on_docker(Dockerfile, student_token_file): token = docker_run_token_file(Dockerfile_location=Dockerfile, - host_tmp_dir=os.path.dirname(Dockerfile) + "/tmp", + host_tmp_dir=os.path.dirname(Dockerfile) + "/home", student_token_file=student_token_file, instructor_grade_script="report3_complete_grade.py") with open(token, 'rb') as f: @@ -32,17 +31,14 @@ def run_student_code_on_docker(Dockerfile, student_token_file): if __name__ == "__main__": # Step 1: Deploy the students files and return the directory they were written to student_directory = deploy_student_files() - # import sys - # sys.exit() - # student_directory = "../../students/cs103" + # Step 2: Simulate that the student run their report script and generate a .token file. os.system("cd ../../students && python -m cs103.report3_grade") student_token_file = glob.glob(student_directory + "/*.token")[0] - # Step 3: Compile the Docker image (obviously you will only do this once; add your packages to requirements.txt). - Dockerfile = os.path.dirname(__file__) + "/../unitgrade-docker/Dockerfile" - os.system("cd ../unitgrade-docker && docker build --tag unitgrade-docker .") + Dockerfile = os.path.dirname(__file__) + "/../../../../docker_images/unitgrade-docker/Dockerfile" + os.system(f"cd {os.path.dirname(Dockerfile)} && docker build --tag unitgrade-docker .") # Step 4: Test the students .token file and get the results-token-file. Compare the contents with the students_token_file: checked_token = run_student_code_on_docker(Dockerfile, student_token_file) diff --git a/examples/example_docker/instructor/cs103/report3.py b/examples/example_docker/instructor/cs103/report3.py index 6dfbe04..3bdc6e6 100644 --- a/examples/example_docker/instructor/cs103/report3.py +++ b/examples/example_docker/instructor/cs103/report3.py @@ -1,5 +1,5 @@ -from unitgrade2.unitgrade2 import UTestCase, Report, hide -from unitgrade2.unitgrade_helpers2 import evaluate_report_student +from src.unitgrade2.unitgrade2 import UTestCase, Report +from src.unitgrade2 import evaluate_report_student class Week1(UTestCase): """ The first question for week 1. """ @@ -21,4 +21,6 @@ class Report3(Report): pack_imports = [cs103] if __name__ == "__main__": + # from unitgrade_private2.hidden_gather_upload import gather_upload_to_campusnet + # gather_upload_to_campusnet(Report3()) evaluate_report_student(Report3()) \ No newline at end of file diff --git a/examples/example_docker/instructor/cs103/report3_complete.py b/examples/example_docker/instructor/cs103/report3_complete.py index 4e72f82..dd85bd8 100644 --- a/examples/example_docker/instructor/cs103/report3_complete.py +++ b/examples/example_docker/instructor/cs103/report3_complete.py @@ -1,5 +1,5 @@ from unitgrade2.unitgrade2 import UTestCase, Report, hide -from unitgrade2.unitgrade_helpers2 import evaluate_report_student +from unitgrade2 import evaluate_report_student class Week1(UTestCase): """ The first question for week 1. """ @@ -30,4 +30,6 @@ class Report3(Report): pack_imports = [cs103] if __name__ == "__main__": + # from unitgrade_private2.hidden_gather_upload import gather_upload_to_campusnet + # gather_upload_to_campusnet(Report3()) evaluate_report_student(Report3()) diff --git a/examples/example_docker/instructor/cs103/report3_complete_grade.py b/examples/example_docker/instructor/cs103/report3_complete_grade.py index b053e48..8ea5f2e 100644 --- a/examples/example_docker/instructor/cs103/report3_complete_grade.py +++ b/examples/example_docker/instructor/cs103/report3_complete_grade.py @@ -4,15 +4,10 @@ from tabulate import tabulate from datetime import datetime import pyfiglet import unittest -# from unitgrade2.unitgrade2 import MySuite - import inspect import os import argparse -import sys import time -import threading # don't import Thread bc. of minify issue. -import tqdm # don't do from tqdm import tqdm because of minify-issue parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: To run all tests in a report: @@ -113,24 +108,20 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) else: b = "Unitgrade" - print(b + " v" + __version__) dt_string = now.strftime("%d/%m/%Y %H:%M:%S") - print("Started: " + dt_string) + print(b + " v" + __version__ + ", started: " + dt_string+ "\n") + # print("Started: " + dt_string) s = report.title if hasattr(report, "version") and report.version is not None: s += " version " + report.version - print("Evaluating " + s, "(use --help for options)" if show_help_flag else "") + print(s, "(use --help for options)" if show_help_flag else "") # print(f"Loaded answers from: ", report.computed_answers_file, "\n") table_data = [] - nL = 80 t_start = time.time() score = {} loader = SequentialTestLoader() for n, (q, w) in enumerate(report.questions): - # q = q() - # q_hidden = False - # q_hidden = issubclass(q.__class__, Hidden) if question is not None and n+1 != question: continue suite = loader.loadTestsFromTestCase(q) @@ -140,11 +131,10 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa q.possible = 0 q.obtained = 0 q_ = {} # Gather score in this class. - # unittest.Te - # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_] UTextResult.q_title_print = q_title_print # Hacky UTextResult.show_progress_bar = show_progress_bar # Hacky. UTextResult.number = n + UTextResult.nL = report.nL res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) @@ -153,20 +143,16 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun - # possible = int(ws @ possible) - # obtained = int(ws @ obtained) - # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0 - obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} q.obtained = obtained q.possible = possible - s1 = f"*** Question q{n+1}" + s1 = f" * q{n+1}) Total" s2 = f" {q.obtained}/{w}" - print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 ) + print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 ) print(" ") - table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"]) + table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"]) ws, possible, obtained = upack(score) possible = int( msum(possible) ) @@ -181,15 +167,16 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa seconds = dt - minutes*60 plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") - print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")") + dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", + last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) + + # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total") table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) results = {'total': (obtained, possible), 'details': score} return results, table_data - - from tabulate import tabulate from datetime import datetime import inspect @@ -212,7 +199,8 @@ def gather_imports(imp): # dn = os.path.dirname(f) # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) # top_package = str(__import__(m.__name__.split('.')[0]).__path__) - if m.__class__.__name__ == 'module' and False: + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: top_package = os.path.dirname(m.__file__) module_import = True else: @@ -233,7 +221,7 @@ def gather_imports(imp): for file in files: if file.endswith(".py"): fpath = os.path.join(root, file) - v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package)) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) zip.write(fpath, v) resources['zipfile'] = zip_buffer.getvalue() @@ -277,14 +265,14 @@ def gather_upload_to_campusnet(report, output_dir=None): results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, show_progress_bar=not args.noprogress, big_header=not args.autolab) - print(" ") - print("="*n) - print("Final evaluation") - print(tabulate(table_data)) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) # also load the source code of missing files... sources = {} - + print("") if not args.autolab: if len(report.individual_imports) > 0: print("By uploading the .token file, you verify the files:") @@ -297,12 +285,15 @@ def gather_upload_to_campusnet(report, output_dir=None): print("Including files in upload...") for k, m in enumerate(report.pack_imports): nimp, top_package = gather_imports(m) - report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import nimp['name'] = m.__name__ sources[k] = nimp # if len([k for k in nimp if k not in sources]) > 0: - print(f"*** {m.__name__}") + print(f" * {m.__name__}") # sources = {**sources, **nimp} results['sources'] = sources @@ -315,15 +306,17 @@ def gather_upload_to_campusnet(report, output_dir=None): vstring = "_v"+report.version if report.version is not None else "" token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) - token = os.path.join(output_dir, token) + token = os.path.normpath(os.path.join(output_dir, token)) + + with open(token, 'wb') as f: pickle.dump(results, f) if not args.autolab: print(" ") - print("To get credit for your results, please upload the single file: ") + print("To get credit for your results, please upload the single unmodified file: ") print(">", token) - print("To campusnet without any modifications.") + # print("To campusnet without any modifications.") # print("Now time for some autolab fun") @@ -336,8 +329,8 @@ def source_instantiate(name, report1_source, payload): -report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n\n"""\n# from . import cache_read\nimport unittest\nimport numpy as np\nimport sys\nfrom io import StringIO\nimport collections\nimport re\nimport threading\nimport tqdm\nimport time\nimport pickle\nimport itertools\nimport os\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\ndef setup_dir_by_class(C,base_dir):\n name = C.__class__.__name__\n # base_dir = os.path.join(base_dir, name)\n # if not os.path.isdir(base_dir):\n # os.makedirs(base_dir)\n return base_dir, name\n\nclass Hidden:\n def hide(self):\n return True\n\nclass Logger(object):\n def __init__(self, buffer):\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\nclass QItem(unittest.TestCase):\n title = None\n testfun = None\n tol = 0\n estimated_time = 0.42\n _precomputed_payload = None\n _computed_answer = None # Internal helper to later get results.\n weight = 1 # the weight of the question.\n\n def __init__(self, question=None, *args, **kwargs):\n if self.tol > 0 and self.testfun is None:\n self.testfun = self.assertL2Relative\n elif self.testfun is None:\n self.testfun = self.assertEqual\n\n self.name = self.__class__.__name__\n # self._correct_answer_payload = correct_answer_payload\n self.question = question\n\n super().__init__(*args, **kwargs)\n if self.title is None:\n self.title = self.name\n\n def _safe_get_title(self):\n if self._precomputed_title is not None:\n return self._precomputed_title\n return self.title\n\n def assertNorm(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed).flat- np.asarray(expected)).flat )\n nrm = np.sqrt(np.sum( diff ** 2))\n\n self.error_computed = nrm\n\n if nrm > tol:\n print(f"Not equal within tolerance {tol}; norm of difference was {nrm}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def assertL2(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n self.error_computed = np.max(diff)\n\n if np.max(diff) > tol:\n print(f"Not equal within tolerance {tol=}; deviation was {np.max(diff)=}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol=}, {np.max(diff)=}")\n\n def assertL2Relative(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n diff = diff / (1e-8 + np.abs( (np.asarray(computed) + np.asarray(expected)) ) )\n self.error_computed = np.max(np.abs(diff))\n if np.sum(diff > tol) > 0:\n print(f"Not equal within tolerance {tol}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def precomputed_payload(self):\n return self._precomputed_payload\n\n def precompute_payload(self):\n # Pre-compute resources to include in tests (useful for getting around rng).\n pass\n\n def compute_answer(self, unmute=False):\n raise NotImplementedError("test code here")\n\n def test(self, computed, expected):\n self.testfun(computed, expected)\n\n def get_points(self, verbose=False, show_expected=False, show_computed=False,unmute=False, passall=False, silent=False, **kwargs):\n possible = 1\n computed = None\n def show_computed_(computed):\n print(">>> Your output:")\n print(computed)\n\n def show_expected_(expected):\n print(">>> Expected output (note: may have been processed; read text script):")\n print(expected)\n\n correct = self._correct_answer_payload\n try:\n if unmute: # Required to not mix together print stuff.\n print("")\n computed = self.compute_answer(unmute=unmute)\n except Exception as e:\n if not passall:\n if not silent:\n print("\\n=================================================================================")\n print(f"When trying to run test class \'{self.name}\' your code threw an error:", e)\n show_expected_(correct)\n import traceback\n print(traceback.format_exc())\n print("=================================================================================")\n return (0, possible)\n\n if self._computed_answer is None:\n self._computed_answer = computed\n\n if show_expected or show_computed:\n print("\\n")\n if show_expected:\n show_expected_(correct)\n if show_computed:\n show_computed_(computed)\n try:\n if not passall:\n self.test(computed=computed, expected=correct)\n except Exception as e:\n if not silent:\n print("\\n=================================================================================")\n print(f"Test output from test class \'{self.name}\' does not match expected result. Test error:")\n print(e)\n show_computed_(computed)\n show_expected_(correct)\n return (0, possible)\n return (1, possible)\n\n def score(self):\n try:\n self.test()\n except Exception as e:\n return 0\n return 1\n\nclass QPrintItem(QItem):\n def compute_answer_print(self):\n """\n Generate output which is to be tested. By default, both text written to the terminal using print(...) as well as return values\n are send to process_output (see compute_answer below). In other words, the text generated is:\n\n res = compute_Answer_print()\n txt = (any terminal output generated above)\n numbers = (any numbers found in terminal-output txt)\n\n self.test(process_output(res, txt, numbers), <expected result>)\n\n :return: Optional values for comparison\n """\n raise Exception("Generate output here. The output is passed to self.process_output")\n\n def process_output(self, res, txt, numbers):\n return res\n\n def compute_answer(self, unmute=False):\n with Capturing(unmute=unmute) as output:\n res = self.compute_answer_print()\n s = "\\n".join(output)\n s = rm_progress_bar(s) # Remove progress bar.\n numbers = extract_numbers(s)\n self._computed_answer = (res, s, numbers)\n return self.process_output(res, s, numbers)\n\nclass OrderedClassMembers(type):\n @classmethod\n def __prepare__(self, name, bases):\n return collections.OrderedDict()\n def __new__(self, name, bases, classdict):\n ks = list(classdict.keys())\n for b in bases:\n ks += b.__ordered__\n classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n return type.__new__(self, name, bases, classdict)\n\nclass QuestionGroup(metaclass=OrderedClassMembers):\n title = "Untitled question"\n partially_scored = False\n t_init = 0 # Time spend on initialization (placeholder; set this externally).\n estimated_time = 0.42\n has_called_init_ = False\n _name = None\n _items = None\n\n @property\n def items(self):\n if self._items == None:\n self._items = []\n members = [gt for gt in [getattr(self, gt) for gt in self.__ordered__ if gt not in ["__classcell__", "__init__"]] if inspect.isclass(gt) and issubclass(gt, QItem)]\n for I in members:\n self._items.append( I(question=self))\n return self._items\n\n @items.setter\n def items(self, value):\n self._items = value\n\n @property\n def name(self):\n if self._name == None:\n self._name = self.__class__.__name__\n return self._name #\n\n @name.setter\n def name(self, val):\n self._name = val\n\n def init(self):\n # Can be used to set resources relevant for this question instance.\n pass\n\n def init_all_item_questions(self):\n for item in self.items:\n if not item.question.has_called_init_:\n item.question.init()\n item.question.has_called_init_ = True\n\n\nclass Report():\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 80 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q,_) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n root_dir = self.pack_imports[0].__path__._path[0]\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q,_) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n # else:\n # if os.path.isfile(self.computed_answers_file):\n # self.set_payload(cache_read(self.computed_answers_file), strict=strict)\n # else:\n # s = f"> Warning: The pre-computed answer file, {os.path.abspath(self.computed_answers_file)} is missing. The framework will NOT work as intended. Reasons may be a broken local installation."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n import unittest\n loader = unittest.TestLoader()\n for q,_ in self.questions:\n import time\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self):\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\':True}\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct+1)\n if i > 0 and l.find("|", i+1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = \'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar",show_progress_bar=True):\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.1\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n\n # self.pbar = tqdm.tqdm(total=self.n)\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if hasattr(self, \'pbar\') and self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar=None\n\n sys.stdout.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=sys.stdout, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\') # , unit_scale=dt, unit=\'seconds\'):\n\n for _ in range(self.n-1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\n\n\nfrom unittest.suite import _isnotsuite\n\n# class MySuite(unittest.suite.TestSuite): # Not sure we need this one anymore.\n# raise Exception("no suite")\n# pass\n\ndef instance_call_stack(instance):\n s = "-".join(map(lambda x: x.__name__, instance.__class__.mro()))\n return s\n\ndef get_class_that_defined_method(meth):\n for cls in inspect.getmro(meth.im_class):\n if meth.__name__ in cls.__dict__:\n return cls\n return None\n\ndef caller_name(skip=2):\n """Get a name of a caller in the format module.class.method\n\n `skip` specifies how many levels of stack to skip while getting caller\n name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.\n\n An empty string is returned if skipped levels exceed stack height\n """\n stack = inspect.stack()\n start = 0 + skip\n if len(stack) < start + 1:\n return \'\'\n parentframe = stack[start][0]\n\n name = []\n module = inspect.getmodule(parentframe)\n # `modname` can be None when frame is executed directly in console\n # TODO(techtonik): consider using __main__\n if module:\n name.append(module.__name__)\n # detect classname\n if \'self\' in parentframe.f_locals:\n # I don\'t know any way to detect call from the object method\n # XXX: there seems to be no way to detect static method call - it will\n # be just a function call\n name.append(parentframe.f_locals[\'self\'].__class__.__name__)\n codename = parentframe.f_code.co_name\n if codename != \'<module>\': # top level usually\n name.append( codename ) # function or a method\n\n ## Avoid circular refs and frame leaks\n # https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack\n del parentframe, stack\n\n return ".".join(name)\n\ndef get_class_from_frame(fr):\n import inspect\n args, _, _, value_dict = inspect.getargvalues(fr)\n # we check the first parameter for the frame function is\n # named \'self\'\n if len(args) and args[0] == \'self\':\n # in that case, \'self\' will be referenced in value_dict\n instance = value_dict.get(\'self\', None)\n if instance:\n # return its class\n # isinstance(instance, Testing) # is the actual class instance.\n\n return getattr(instance, \'__class__\', None)\n # return None otherwise\n return None\n\nfrom typing import Any\nimport inspect, gc\n\ndef giveupthefunc():\n frame = inspect.currentframe()\n code = frame.f_code\n globs = frame.f_globals\n functype = type(lambda: 0)\n funcs = []\n for func in gc.get_referrers(code):\n if type(func) is functype:\n if getattr(func, "__code__", None) is code:\n if getattr(func, "__globals__", None) is globs:\n funcs.append(func)\n if len(funcs) > 1:\n return None\n return funcs[0] if funcs else None\n\n\nfrom collections import defaultdict\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n # if self.dots or self.showAll:\n # self.stream.writeln()\n # if hasattr(self, \'cc\'):\n # self.cc.terminate()\n # self.cc_terminate(success=False)\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n # if self.showAll:\n # self.stream.writeln("FAIL")\n # elif self.dots:\n # self.stream.write(\'F\')\n # self.stream.flush()\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n # super().addSuccess(test)\n self.successes.append(test)\n # super().addSuccess(test)\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n self.cc_terminate()\n\n\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n sys.stdout.flush()\n ss = self.item_title_print\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(ss))), end="")\n # current = 1\n # possible = 1\n # current == possible\n ss = "PASS" if success else "FAILED"\n if tsecs >= 0.1:\n ss += " (" + str(tsecs) + " seconds)"\n print(ss)\n\n\n def startTest(self, test):\n # super().startTest(test)\n j =self.testsRun\n self.testsRun += 1\n # print("Starting the test...")\n # show_progress_bar = True\n n = UTextResult.number\n\n item_title = self.getDescription(test)\n # item_title = item_title.split("\\n")[0]\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n # test.countTestCases()\n self.item_title_print = "*** q%i.%i) %s" % (n + 1, j + 1, item_title)\n estimated_time = 10\n nL = 80\n #\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar)\n else:\n print(self.item_title_print + (\'.\' * max(0, nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n # q_title_print = "some printed title..."\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass == None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n print(self.cc.title, end="")\n # start = 10\n # q_time = np.round(time.time() - start, 2)\n nL = 80\n print(" " * max(0, nL - len(self.cc.title)) + (\n " (" + str(q_time) + " seconds)" if q_time >= 0.1 else "")) # if q.name in report.payloads else "")\n # print("=" * nL)\n\nfrom unittest.runner import _WritelnDecorator\nfrom io import StringIO\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n from io import StringIO\n stream = StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\ndef wrapper(foo):\n def magic(self):\n s = "-".join(map(lambda x: x.__name__, self.__class__.mro()))\n # print(s)\n foo(self)\n magic.__doc__ = foo.__doc__\n return magic\n\nfrom functools import update_wrapper, _make_key, RLock\nfrom collections import namedtuple\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)) )\n # key = (self.cache_id(), \'@cache\')\n # if self._cache_contains[key]\n\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n return wrapper\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n\n def capture(self):\n return Capturing2(stdout=self._stdout)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ != None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n self._stdout = sys.stdout\n import io\n sys.stdout = io.StringIO()\n super().setUp()\n # print("Setting up...")\n\n def _callTearDown(self):\n sys.stdout = self._stdout\n super().tearDown()\n # print("asdfsfd")\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd == None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n # self._testMethodDoc.strip().splitlines()[0].strip()\n sd = self.shortDescriptionStandard()\n title = self._cache_get( (self.cache_id(), \'title\'), sd )\n return title if title != None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome == None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc != None:\n # Ensure the cache is eventually updated with the right docstring.\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard() )\n # Fix temp cache here (for using the @cache decorator)\n self._cache2[ (self.cache_id(), \'assert\') ] = {}\n\n res = testMethod()\n elapsed = time.time() - t\n # self._cache_put( (self.cache_id(), \'title\'), self.shortDescription() )\n\n self._get_outcome()[self.cache_id()] = res\n self._cache_put( (self.cache_id(), "time"), elapsed)\n\n # This is my base test class. So what is new about it?\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return (c,m)\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n # self.cache_indexes = defaultdict(lambda: 0)\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n cache = self._cache_get(key, {})\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, first)\n assert_fun(first, _expected, *args, **kwargs)\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__) ) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache != None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n # print("Loading cache from", cfile)\n if os.path.exists(cfile):\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n else:\n print("Warning! data file not found", cfile)\n\ndef hide(func):\n return func\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n # (*)We can be somewhat "hygienic", but newDecorator still isn\'t signature-preserving, i.e. you will not be able to get a runtime list of parameters. For that, you need hackish libraries...but in this case, the only argument is func, so it\'s not a big issue\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\n# from unitgrade2.unitgrade2 import MySuite\n\nimport inspect\nimport os\nimport argparse\nimport sys\nimport time\nimport threading # don\'t import Thread bc. of minify issue.\nimport tqdm # don\'t do from tqdm import tqdm because of minify-issue\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n print(b + " v" + __version__)\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print("Evaluating " + s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n nL = 80\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n # q = q()\n # q_hidden = False\n # q_hidden = issubclass(q.__class__, Hidden)\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n # unittest.Te\n # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_]\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n # possible = int(ws @ possible)\n # obtained = int(ws @ obtained)\n # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"*** Question q{n+1}"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n if m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package))\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n print(" ")\n print("="*n)\n print("Final evaluation")\n print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f"*** {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single file: ")\n print(">", token)\n print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n def test_add(self):\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n self.assertEqualC(add(-100, 5))\n\n @hide\n def test_add_hidden(self):\n # This is a hidden test. The @hide-decorator will allow unitgrade to remove the test.\n # See the output in the student directory for more information.\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n\nclass AutomaticPass(UTestCase):\n def test_student_passed(self):\n self.assertEqual(2,2)\n\n @hide\n def test_hidden_fail(self):\n self.assertEqual(2,3)\n\nimport cs103\nclass Report3(Report):\n title = "CS 101 Report 3"\n questions = [(Week1, 20), (AutomaticPass, 10)] # Include a single question for 10 credits.\n pack_imports = [cs103]' -report1_payload = '80049586000000000000007d94288c055765656b31947d94288c055765656b31948c08746573745f6164649486948c066173736572749486947d94284b014aa1ffffff4b004b04756803680486948c0474696d659486944700000000000000008c0474696d6594473f60628000000000758c0d4175746f6d6174696350617373947d94680c473f689d000000000073752e' +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n"""\nimport numpy as np\nimport sys\nimport re\nimport threading\nimport tqdm\nimport pickle\nimport os\nfrom io import StringIO\nimport io\nfrom unittest.runner import _WritelnDecorator\nfrom typing import Any\nimport inspect\nimport textwrap\nimport colorama\nfrom colorama import Fore\nfrom functools import _make_key, RLock\nfrom collections import namedtuple\nimport unittest\nimport time\n\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ncolorama.init(autoreset=True) # auto resets your settings after every output\n\ndef gprint(s):\n print(f"{Fore.GREEN}{s}")\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\n\ndef setup_dir_by_class(C, base_dir):\n name = C.__class__.__name__\n return base_dir, name\n\n\nclass Logger(object):\n def __init__(self, buffer):\n assert False\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\n# @classmethod\n# class OrderedClassMembers(type):\n# def __prepare__(self, name, bases):\n# assert False\n# return collections.OrderedDict()\n#\n# def __new__(self, name, bases, classdict):\n# ks = list(classdict.keys())\n# for b in bases:\n# ks += b.__ordered__\n# classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n# return type.__new__(self, name, bases, classdict)\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 120 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q, _) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q, _) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n loader = unittest.TestLoader()\n for q, _ in self.questions:\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self, with_coverage=False):\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = True\n q._report = self\n\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n # print(self.questions)\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n print("q is", q())\n q()._cache_put(\'time\', q.time) # = q.time\n report_cache[q.__qualname__] = q._cache2\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\': True}\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = False\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct + 1)\n if i > 0 and l.find("|", i + 1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = r\'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar", show_progress_bar=True, file=None):\n if file == None:\n file = sys.stdout\n self.file = file\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.01\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n self.pbar = None\n\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar = None\n\n self.file.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=self.file, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\')\n\n for _ in range(self.n - 1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\ndef dprint(first, last, nL, extra = "", file=None, dotsym=\'.\', color=\'white\'):\n if file == None:\n file = sys.stdout\n\n # ss = self.item_title_print\n # state = "PASS" if success else "FAILED"\n dot_parts = (dotsym * max(0, nL - len(last) - len(first)))\n # if self.show_progress_bar or True:\n print(first + dot_parts, end="", file=file)\n # else:\n # print(dot_parts, end="", file=self.cc.file)\n last += extra\n # if tsecs >= 0.5:\n # state += " (" + str(tsecs) + " seconds)"\n print(last, file=file)\n\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n cc = None\n\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n self.successes.append(test)\n self.cc_terminate()\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n self.cc.file.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="", file=self.cc.file)\n else:\n print(dot_parts, end="", file=self.cc.file)\n\n if tsecs >= 0.5:\n state += " (" + str(tsecs) + " seconds)"\n print(state, file=self.cc.file)\n\n def startTest(self, test):\n # j =self.testsRun\n self.testsRun += 1\n # item_title = self.getDescription(test)\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n self.item_title_print = " * q%i.%i) %s" % (UTextResult.number + 1, self.testsRun, item_title)\n estimated_time = 10\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar, file=sys.stdout)\n else:\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n self._stdout = sys.stdout\n sys.stdout = io.StringIO()\n\n def stopTest(self, test):\n sys.stdout = self._stdout\n super().stopTest(test)\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass is None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n print(" " * max(0, self.nL - len(self.cc.title)) + (" (" + str(q_time) + " seconds)" if q_time >= 0.5 else ""))\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n stream = io.StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)))\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n\n return wrapper\n\n\ndef get_hints(ss):\n if ss == None:\n return None\n try:\n ss = textwrap.dedent(ss)\n ss = ss.replace(\'\'\'"""\'\'\', "").strip()\n hints = ["hints:", ]\n j = np.argmax([ss.lower().find(h) for h in hints])\n h = hints[j]\n ss = ss[ss.find(h) + len(h) + 1:]\n ss = "\\n".join([l for l in ss.split("\\n") if not l.strip().startswith(":")])\n ss = textwrap.dedent(ss)\n ss = ss.strip()\n return ss\n except Exception as e:\n print("bad hints", ss, e)\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n _with_coverage = False\n _report = None # The report used. This is very, very hacky and should always be None. Don\'t rely on it!\n\n def capture(self):\n if hasattr(self, \'_stdout\') and self._stdout is not None:\n file = self._stdout\n else:\n # self._stdout = sys.stdout\n # sys._stdout = io.StringIO()\n file = sys.stdout\n return Capturing2(stdout=file)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ is not None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n if self._with_coverage:\n if not hasattr(self._report, \'covcache\'):\n self._report.covcache = {}\n import coverage\n self.cov = coverage.Coverage()\n self.cov.start()\n self.setUp()\n\n def _callTearDown(self):\n self.tearDown()\n if self._with_coverage:\n from pathlib import Path\n from snipper import snipper\n self.cov.stop()\n data = self.cov.get_data()\n base, _, _ = self._report._import_base_relative()\n for file in data.measured_files():\n file = os.path.normpath(file)\n root = Path(base)\n child = Path(file)\n if root in child.parents:\n with open(child, \'r\') as f:\n s = f.read()\n lines = s.splitlines()\n garb = \'GARBAGE\'\n\n lines2 = snipper.censor_code(lines, keep=True)\n assert len(lines) == len(lines2)\n\n for l in data.contexts_by_lineno(file):\n if lines2[l].strip() == garb:\n if self.cache_id() not in self._report.covcache:\n self._report.covcache[self.cache_id()] = {}\n\n rel = os.path.relpath(child, root)\n cc = self._report.covcache[self.cache_id()]\n j = 0\n for j in range(l, -1, -1):\n if "def" in lines2[j] or "class" in lines2[j]:\n break\n from snipper.snipper import gcoms\n fun = lines2[j]\n comments, _ = gcoms("\\n".join(lines2[j:l]))\n if rel not in cc:\n cc[rel] = {}\n cc[rel][fun] = (l, "\\n".join(comments))\n self._cache_put((self.cache_id(), \'coverage\'), self._report.covcache)\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd is None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n sd = self.shortDescriptionStandard()\n title = self._cache_get((self.cache_id(), \'title\'), sd)\n return title if title is not None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome is None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc is not None:\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard())\n\n self._cache2[(self.cache_id(), \'assert\')] = {}\n res = testMethod()\n elapsed = time.time() - t\n self._get_outcome()[self.cache_id()] = res\n self._cache_put((self.cache_id(), "time"), elapsed)\n\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return c, m\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n # sys.stdout = self._stdout\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n self.__class__._cache[\n key] = {} # A new dict. We manually insert it because we have to use that the dict is mutable.\n cache = self._cache_get(key)\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, f"Key {id} not found in cache; framework files missing. Please run deploy()")\n\n # The order of these calls is important. If the method assert fails, we should still store the correct result in cache.\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n assert_fun(first, _expected, *args, **kwargs)\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache is not None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n if os.path.exists(cfile):\n try:\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n except Exception as e:\n print("Bad cache", cfile)\n print(e)\n else:\n print("Warning! data file not found", cfile)\n\n def _feedErrorsToResult(self, result, errors):\n """ Use this to show hints on test failure. """\n if not isinstance(result, UTextResult):\n er = [e for e, v in errors if v != None]\n\n if len(er) > 0:\n hints = []\n key = (self.cache_id(), \'coverage\')\n if self._cache_contains(key):\n CC = self._cache_get(key)\n for id in CC:\n if id == self.cache_id():\n cl, m = id\n gprint(f"> An error occured while solving: {cl}.{m}. The files/methods you need to edit are:") # For the test {id} in {file} you should edit:")\n for file in CC[id]:\n rec = CC[id][file]\n gprint(f"> * {file}")\n for l in rec:\n _, comments = CC[id][file][l]\n hint = get_hints(comments)\n\n if hint != None:\n hints.append(hint)\n gprint(f"> - {l}")\n\n er = er[0]\n doc = er._testMethodDoc\n if doc is not None:\n hint = get_hints(er._testMethodDoc)\n if hint is not None:\n hints = [hint] + hints\n if len(hints) > 0:\n gprint("> Hints:")\n gprint(textwrap.indent("\\n".join(hints), "> "))\n\n super()._feedErrorsToResult(result, errors)\n\n def startTestRun(self):\n # print("asdfsdaf 11", file=sys.stderr)\n super().startTestRun()\n # print("asdfsdaf")\n\n def _callTestMethod(self, method):\n # print("asdfsdaf")\n super()._callTestMethod(method)\n\n\ndef hide(func):\n return func\n\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n# 817\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\nimport inspect\nimport os\nimport argparse\nimport time\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print(b + " v" + __version__ + ", started: " + dt_string+ "\\n")\n # print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print(s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n UTextResult.nL = report.nL\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f" * q{n+1}) Total"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")",\n last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL)\n\n # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n # print(" ")\n # print("="*n)\n # print("Final evaluation")\n # print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n print("")\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f" * {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.normpath(os.path.join(output_dir, token))\n\n\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single unmodified file: ")\n print(">", token)\n # print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n def test_add(self):\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n self.assertEqualC(add(-100, 5))\n\n @hide\n def test_add_hidden(self):\n # This is a hidden test. The @hide-decorator will allow unitgrade to remove the test.\n # See the output in the student directory for more information.\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n\nclass AutomaticPass(UTestCase):\n def test_student_passed(self):\n self.assertEqual(2,2)\n\n @hide\n def test_hidden_fail(self):\n self.assertEqual(2,3)\n\nimport cs103\nclass Report3(Report):\n title = "CS 101 Report 3"\n questions = [(Week1, 20), (AutomaticPass, 10)] # Include a single question for 10 credits.\n pack_imports = [cs103]' +report1_payload = '80049589000000000000007d94288c055765656b31947d942868018c08746573745f6164649486948c066173736572749486947d94284b014aa1ffffff4b004b047568018c0f746573745f6164645f68696464656e948694680586947d944b004b04738c0474696d6594473fe3b8a400000000758c0d4175746f6d6174696350617373947d94680c473fc45a520000000073752e' name="Report3" report = source_instantiate(name, report1_source, report1_payload) diff --git a/examples/example_docker/instructor/cs103/report3_grade.py b/examples/example_docker/instructor/cs103/report3_grade.py index 06bc99f..3b6b512 100644 --- a/examples/example_docker/instructor/cs103/report3_grade.py +++ b/examples/example_docker/instructor/cs103/report3_grade.py @@ -4,15 +4,10 @@ from tabulate import tabulate from datetime import datetime import pyfiglet import unittest -# from unitgrade2.unitgrade2 import MySuite - import inspect import os import argparse -import sys import time -import threading # don't import Thread bc. of minify issue. -import tqdm # don't do from tqdm import tqdm because of minify-issue parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: To run all tests in a report: @@ -113,24 +108,20 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) else: b = "Unitgrade" - print(b + " v" + __version__) dt_string = now.strftime("%d/%m/%Y %H:%M:%S") - print("Started: " + dt_string) + print(b + " v" + __version__ + ", started: " + dt_string+ "\n") + # print("Started: " + dt_string) s = report.title if hasattr(report, "version") and report.version is not None: s += " version " + report.version - print("Evaluating " + s, "(use --help for options)" if show_help_flag else "") + print(s, "(use --help for options)" if show_help_flag else "") # print(f"Loaded answers from: ", report.computed_answers_file, "\n") table_data = [] - nL = 80 t_start = time.time() score = {} loader = SequentialTestLoader() for n, (q, w) in enumerate(report.questions): - # q = q() - # q_hidden = False - # q_hidden = issubclass(q.__class__, Hidden) if question is not None and n+1 != question: continue suite = loader.loadTestsFromTestCase(q) @@ -140,11 +131,10 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa q.possible = 0 q.obtained = 0 q_ = {} # Gather score in this class. - # unittest.Te - # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_] UTextResult.q_title_print = q_title_print # Hacky UTextResult.show_progress_bar = show_progress_bar # Hacky. UTextResult.number = n + UTextResult.nL = report.nL res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) @@ -153,20 +143,16 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun - # possible = int(ws @ possible) - # obtained = int(ws @ obtained) - # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0 - obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} q.obtained = obtained q.possible = possible - s1 = f"*** Question q{n+1}" + s1 = f" * q{n+1}) Total" s2 = f" {q.obtained}/{w}" - print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 ) + print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 ) print(" ") - table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"]) + table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"]) ws, possible, obtained = upack(score) possible = int( msum(possible) ) @@ -181,15 +167,16 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa seconds = dt - minutes*60 plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") - print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")") + dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", + last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) + + # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total") table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) results = {'total': (obtained, possible), 'details': score} return results, table_data - - from tabulate import tabulate from datetime import datetime import inspect @@ -212,7 +199,8 @@ def gather_imports(imp): # dn = os.path.dirname(f) # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) # top_package = str(__import__(m.__name__.split('.')[0]).__path__) - if m.__class__.__name__ == 'module' and False: + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: top_package = os.path.dirname(m.__file__) module_import = True else: @@ -233,7 +221,7 @@ def gather_imports(imp): for file in files: if file.endswith(".py"): fpath = os.path.join(root, file) - v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package)) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) zip.write(fpath, v) resources['zipfile'] = zip_buffer.getvalue() @@ -277,14 +265,14 @@ def gather_upload_to_campusnet(report, output_dir=None): results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, show_progress_bar=not args.noprogress, big_header=not args.autolab) - print(" ") - print("="*n) - print("Final evaluation") - print(tabulate(table_data)) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) # also load the source code of missing files... sources = {} - + print("") if not args.autolab: if len(report.individual_imports) > 0: print("By uploading the .token file, you verify the files:") @@ -297,12 +285,15 @@ def gather_upload_to_campusnet(report, output_dir=None): print("Including files in upload...") for k, m in enumerate(report.pack_imports): nimp, top_package = gather_imports(m) - report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import nimp['name'] = m.__name__ sources[k] = nimp # if len([k for k in nimp if k not in sources]) > 0: - print(f"*** {m.__name__}") + print(f" * {m.__name__}") # sources = {**sources, **nimp} results['sources'] = sources @@ -315,15 +306,17 @@ def gather_upload_to_campusnet(report, output_dir=None): vstring = "_v"+report.version if report.version is not None else "" token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) - token = os.path.join(output_dir, token) + token = os.path.normpath(os.path.join(output_dir, token)) + + with open(token, 'wb') as f: pickle.dump(results, f) if not args.autolab: print(" ") - print("To get credit for your results, please upload the single file: ") + print("To get credit for your results, please upload the single unmodified file: ") print(">", token) - print("To campusnet without any modifications.") + # print("To campusnet without any modifications.") # print("Now time for some autolab fun") @@ -336,8 +329,8 @@ def source_instantiate(name, report1_source, payload): -report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n\n"""\n# from . import cache_read\nimport unittest\nimport numpy as np\nimport sys\nfrom io import StringIO\nimport collections\nimport re\nimport threading\nimport tqdm\nimport time\nimport pickle\nimport itertools\nimport os\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\ndef setup_dir_by_class(C,base_dir):\n name = C.__class__.__name__\n # base_dir = os.path.join(base_dir, name)\n # if not os.path.isdir(base_dir):\n # os.makedirs(base_dir)\n return base_dir, name\n\nclass Hidden:\n def hide(self):\n return True\n\nclass Logger(object):\n def __init__(self, buffer):\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\nclass QItem(unittest.TestCase):\n title = None\n testfun = None\n tol = 0\n estimated_time = 0.42\n _precomputed_payload = None\n _computed_answer = None # Internal helper to later get results.\n weight = 1 # the weight of the question.\n\n def __init__(self, question=None, *args, **kwargs):\n if self.tol > 0 and self.testfun is None:\n self.testfun = self.assertL2Relative\n elif self.testfun is None:\n self.testfun = self.assertEqual\n\n self.name = self.__class__.__name__\n # self._correct_answer_payload = correct_answer_payload\n self.question = question\n\n super().__init__(*args, **kwargs)\n if self.title is None:\n self.title = self.name\n\n def _safe_get_title(self):\n if self._precomputed_title is not None:\n return self._precomputed_title\n return self.title\n\n def assertNorm(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed).flat- np.asarray(expected)).flat )\n nrm = np.sqrt(np.sum( diff ** 2))\n\n self.error_computed = nrm\n\n if nrm > tol:\n print(f"Not equal within tolerance {tol}; norm of difference was {nrm}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def assertL2(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n self.error_computed = np.max(diff)\n\n if np.max(diff) > tol:\n print(f"Not equal within tolerance {tol=}; deviation was {np.max(diff)=}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol=}, {np.max(diff)=}")\n\n def assertL2Relative(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n diff = diff / (1e-8 + np.abs( (np.asarray(computed) + np.asarray(expected)) ) )\n self.error_computed = np.max(np.abs(diff))\n if np.sum(diff > tol) > 0:\n print(f"Not equal within tolerance {tol}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def precomputed_payload(self):\n return self._precomputed_payload\n\n def precompute_payload(self):\n # Pre-compute resources to include in tests (useful for getting around rng).\n pass\n\n def compute_answer(self, unmute=False):\n raise NotImplementedError("test code here")\n\n def test(self, computed, expected):\n self.testfun(computed, expected)\n\n def get_points(self, verbose=False, show_expected=False, show_computed=False,unmute=False, passall=False, silent=False, **kwargs):\n possible = 1\n computed = None\n def show_computed_(computed):\n print(">>> Your output:")\n print(computed)\n\n def show_expected_(expected):\n print(">>> Expected output (note: may have been processed; read text script):")\n print(expected)\n\n correct = self._correct_answer_payload\n try:\n if unmute: # Required to not mix together print stuff.\n print("")\n computed = self.compute_answer(unmute=unmute)\n except Exception as e:\n if not passall:\n if not silent:\n print("\\n=================================================================================")\n print(f"When trying to run test class \'{self.name}\' your code threw an error:", e)\n show_expected_(correct)\n import traceback\n print(traceback.format_exc())\n print("=================================================================================")\n return (0, possible)\n\n if self._computed_answer is None:\n self._computed_answer = computed\n\n if show_expected or show_computed:\n print("\\n")\n if show_expected:\n show_expected_(correct)\n if show_computed:\n show_computed_(computed)\n try:\n if not passall:\n self.test(computed=computed, expected=correct)\n except Exception as e:\n if not silent:\n print("\\n=================================================================================")\n print(f"Test output from test class \'{self.name}\' does not match expected result. Test error:")\n print(e)\n show_computed_(computed)\n show_expected_(correct)\n return (0, possible)\n return (1, possible)\n\n def score(self):\n try:\n self.test()\n except Exception as e:\n return 0\n return 1\n\nclass QPrintItem(QItem):\n def compute_answer_print(self):\n """\n Generate output which is to be tested. By default, both text written to the terminal using print(...) as well as return values\n are send to process_output (see compute_answer below). In other words, the text generated is:\n\n res = compute_Answer_print()\n txt = (any terminal output generated above)\n numbers = (any numbers found in terminal-output txt)\n\n self.test(process_output(res, txt, numbers), <expected result>)\n\n :return: Optional values for comparison\n """\n raise Exception("Generate output here. The output is passed to self.process_output")\n\n def process_output(self, res, txt, numbers):\n return res\n\n def compute_answer(self, unmute=False):\n with Capturing(unmute=unmute) as output:\n res = self.compute_answer_print()\n s = "\\n".join(output)\n s = rm_progress_bar(s) # Remove progress bar.\n numbers = extract_numbers(s)\n self._computed_answer = (res, s, numbers)\n return self.process_output(res, s, numbers)\n\nclass OrderedClassMembers(type):\n @classmethod\n def __prepare__(self, name, bases):\n return collections.OrderedDict()\n def __new__(self, name, bases, classdict):\n ks = list(classdict.keys())\n for b in bases:\n ks += b.__ordered__\n classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n return type.__new__(self, name, bases, classdict)\n\nclass QuestionGroup(metaclass=OrderedClassMembers):\n title = "Untitled question"\n partially_scored = False\n t_init = 0 # Time spend on initialization (placeholder; set this externally).\n estimated_time = 0.42\n has_called_init_ = False\n _name = None\n _items = None\n\n @property\n def items(self):\n if self._items == None:\n self._items = []\n members = [gt for gt in [getattr(self, gt) for gt in self.__ordered__ if gt not in ["__classcell__", "__init__"]] if inspect.isclass(gt) and issubclass(gt, QItem)]\n for I in members:\n self._items.append( I(question=self))\n return self._items\n\n @items.setter\n def items(self, value):\n self._items = value\n\n @property\n def name(self):\n if self._name == None:\n self._name = self.__class__.__name__\n return self._name #\n\n @name.setter\n def name(self, val):\n self._name = val\n\n def init(self):\n # Can be used to set resources relevant for this question instance.\n pass\n\n def init_all_item_questions(self):\n for item in self.items:\n if not item.question.has_called_init_:\n item.question.init()\n item.question.has_called_init_ = True\n\n\nclass Report():\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 80 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q,_) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n root_dir = self.pack_imports[0].__path__._path[0]\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q,_) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n # else:\n # if os.path.isfile(self.computed_answers_file):\n # self.set_payload(cache_read(self.computed_answers_file), strict=strict)\n # else:\n # s = f"> Warning: The pre-computed answer file, {os.path.abspath(self.computed_answers_file)} is missing. The framework will NOT work as intended. Reasons may be a broken local installation."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n import unittest\n loader = unittest.TestLoader()\n for q,_ in self.questions:\n import time\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self):\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\':True}\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct+1)\n if i > 0 and l.find("|", i+1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = \'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar",show_progress_bar=True):\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.1\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n\n # self.pbar = tqdm.tqdm(total=self.n)\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if hasattr(self, \'pbar\') and self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar=None\n\n sys.stdout.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=sys.stdout, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\') # , unit_scale=dt, unit=\'seconds\'):\n\n for _ in range(self.n-1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\n\n\nfrom unittest.suite import _isnotsuite\n\n# class MySuite(unittest.suite.TestSuite): # Not sure we need this one anymore.\n# raise Exception("no suite")\n# pass\n\ndef instance_call_stack(instance):\n s = "-".join(map(lambda x: x.__name__, instance.__class__.mro()))\n return s\n\ndef get_class_that_defined_method(meth):\n for cls in inspect.getmro(meth.im_class):\n if meth.__name__ in cls.__dict__:\n return cls\n return None\n\ndef caller_name(skip=2):\n """Get a name of a caller in the format module.class.method\n\n `skip` specifies how many levels of stack to skip while getting caller\n name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.\n\n An empty string is returned if skipped levels exceed stack height\n """\n stack = inspect.stack()\n start = 0 + skip\n if len(stack) < start + 1:\n return \'\'\n parentframe = stack[start][0]\n\n name = []\n module = inspect.getmodule(parentframe)\n # `modname` can be None when frame is executed directly in console\n # TODO(techtonik): consider using __main__\n if module:\n name.append(module.__name__)\n # detect classname\n if \'self\' in parentframe.f_locals:\n # I don\'t know any way to detect call from the object method\n # XXX: there seems to be no way to detect static method call - it will\n # be just a function call\n name.append(parentframe.f_locals[\'self\'].__class__.__name__)\n codename = parentframe.f_code.co_name\n if codename != \'<module>\': # top level usually\n name.append( codename ) # function or a method\n\n ## Avoid circular refs and frame leaks\n # https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack\n del parentframe, stack\n\n return ".".join(name)\n\ndef get_class_from_frame(fr):\n import inspect\n args, _, _, value_dict = inspect.getargvalues(fr)\n # we check the first parameter for the frame function is\n # named \'self\'\n if len(args) and args[0] == \'self\':\n # in that case, \'self\' will be referenced in value_dict\n instance = value_dict.get(\'self\', None)\n if instance:\n # return its class\n # isinstance(instance, Testing) # is the actual class instance.\n\n return getattr(instance, \'__class__\', None)\n # return None otherwise\n return None\n\nfrom typing import Any\nimport inspect, gc\n\ndef giveupthefunc():\n frame = inspect.currentframe()\n code = frame.f_code\n globs = frame.f_globals\n functype = type(lambda: 0)\n funcs = []\n for func in gc.get_referrers(code):\n if type(func) is functype:\n if getattr(func, "__code__", None) is code:\n if getattr(func, "__globals__", None) is globs:\n funcs.append(func)\n if len(funcs) > 1:\n return None\n return funcs[0] if funcs else None\n\n\nfrom collections import defaultdict\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n # if self.dots or self.showAll:\n # self.stream.writeln()\n # if hasattr(self, \'cc\'):\n # self.cc.terminate()\n # self.cc_terminate(success=False)\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n # if self.showAll:\n # self.stream.writeln("FAIL")\n # elif self.dots:\n # self.stream.write(\'F\')\n # self.stream.flush()\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n # super().addSuccess(test)\n self.successes.append(test)\n # super().addSuccess(test)\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n self.cc_terminate()\n\n\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n sys.stdout.flush()\n ss = self.item_title_print\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(ss))), end="")\n # current = 1\n # possible = 1\n # current == possible\n ss = "PASS" if success else "FAILED"\n if tsecs >= 0.1:\n ss += " (" + str(tsecs) + " seconds)"\n print(ss)\n\n\n def startTest(self, test):\n # super().startTest(test)\n j =self.testsRun\n self.testsRun += 1\n # print("Starting the test...")\n # show_progress_bar = True\n n = UTextResult.number\n\n item_title = self.getDescription(test)\n # item_title = item_title.split("\\n")[0]\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n # test.countTestCases()\n self.item_title_print = "*** q%i.%i) %s" % (n + 1, j + 1, item_title)\n estimated_time = 10\n nL = 80\n #\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar)\n else:\n print(self.item_title_print + (\'.\' * max(0, nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n # q_title_print = "some printed title..."\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass == None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n print(self.cc.title, end="")\n # start = 10\n # q_time = np.round(time.time() - start, 2)\n nL = 80\n print(" " * max(0, nL - len(self.cc.title)) + (\n " (" + str(q_time) + " seconds)" if q_time >= 0.1 else "")) # if q.name in report.payloads else "")\n # print("=" * nL)\n\nfrom unittest.runner import _WritelnDecorator\nfrom io import StringIO\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n from io import StringIO\n stream = StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\ndef wrapper(foo):\n def magic(self):\n s = "-".join(map(lambda x: x.__name__, self.__class__.mro()))\n # print(s)\n foo(self)\n magic.__doc__ = foo.__doc__\n return magic\n\nfrom functools import update_wrapper, _make_key, RLock\nfrom collections import namedtuple\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)) )\n # key = (self.cache_id(), \'@cache\')\n # if self._cache_contains[key]\n\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n return wrapper\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n\n def capture(self):\n return Capturing2(stdout=self._stdout)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ != None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n self._stdout = sys.stdout\n import io\n sys.stdout = io.StringIO()\n super().setUp()\n # print("Setting up...")\n\n def _callTearDown(self):\n sys.stdout = self._stdout\n super().tearDown()\n # print("asdfsfd")\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd == None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n # self._testMethodDoc.strip().splitlines()[0].strip()\n sd = self.shortDescriptionStandard()\n title = self._cache_get( (self.cache_id(), \'title\'), sd )\n return title if title != None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome == None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc != None:\n # Ensure the cache is eventually updated with the right docstring.\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard() )\n # Fix temp cache here (for using the @cache decorator)\n self._cache2[ (self.cache_id(), \'assert\') ] = {}\n\n res = testMethod()\n elapsed = time.time() - t\n # self._cache_put( (self.cache_id(), \'title\'), self.shortDescription() )\n\n self._get_outcome()[self.cache_id()] = res\n self._cache_put( (self.cache_id(), "time"), elapsed)\n\n # This is my base test class. So what is new about it?\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return (c,m)\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n # self.cache_indexes = defaultdict(lambda: 0)\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n cache = self._cache_get(key, {})\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, first)\n assert_fun(first, _expected, *args, **kwargs)\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__) ) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache != None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n # print("Loading cache from", cfile)\n if os.path.exists(cfile):\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n else:\n print("Warning! data file not found", cfile)\n\ndef hide(func):\n return func\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n # (*)We can be somewhat "hygienic", but newDecorator still isn\'t signature-preserving, i.e. you will not be able to get a runtime list of parameters. For that, you need hackish libraries...but in this case, the only argument is func, so it\'s not a big issue\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\n# from unitgrade2.unitgrade2 import MySuite\n\nimport inspect\nimport os\nimport argparse\nimport sys\nimport time\nimport threading # don\'t import Thread bc. of minify issue.\nimport tqdm # don\'t do from tqdm import tqdm because of minify-issue\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n print(b + " v" + __version__)\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print("Evaluating " + s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n nL = 80\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n # q = q()\n # q_hidden = False\n # q_hidden = issubclass(q.__class__, Hidden)\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n # unittest.Te\n # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_]\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n # possible = int(ws @ possible)\n # obtained = int(ws @ obtained)\n # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"*** Question q{n+1}"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n if m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package))\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n print(" ")\n print("="*n)\n print("Final evaluation")\n print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f"*** {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single file: ")\n print(">", token)\n print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n def test_add(self):\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n self.assertEqualC(add(-100, 5))\n\n\nclass AutomaticPass(UTestCase):\n def test_student_passed(self):\n self.assertEqual(2,2)\n\n\nimport cs103\nclass Report3(Report):\n title = "CS 101 Report 3"\n questions = [(Week1, 20), (AutomaticPass, 10)] # Include a single question for 10 credits.\n pack_imports = [cs103]' -report1_payload = '80049525010000000000007d94288c055765656b31947d94288c055765656b31948c08746573745f6164649486948c066173736572749486947d94284b014aa1ffffff4b004b04756803680486948c0474696d65948694473f506a000000000068038c0f746573745f6164645f68696464656e948694680686947d944b004b04736803680c8694680a86944700000000000000008c0474696d6594473f926de000000000758c0d4175746f6d6174696350617373947d94288c0d4175746f6d6174696350617373948c10746573745f68696464656e5f6661696c9486948c066173736572749486947d9468158c13746573745f73747564656e745f706173736564948694681886947d946815681b86948c0474696d659486944700000000000000006812473f9894100000000075752e' +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n"""\nimport numpy as np\nimport sys\nimport re\nimport threading\nimport tqdm\nimport pickle\nimport os\nfrom io import StringIO\nimport io\nfrom unittest.runner import _WritelnDecorator\nfrom typing import Any\nimport inspect\nimport textwrap\nimport colorama\nfrom colorama import Fore\nfrom functools import _make_key, RLock\nfrom collections import namedtuple\nimport unittest\nimport time\n\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ncolorama.init(autoreset=True) # auto resets your settings after every output\n\ndef gprint(s):\n print(f"{Fore.GREEN}{s}")\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\n\ndef setup_dir_by_class(C, base_dir):\n name = C.__class__.__name__\n return base_dir, name\n\n\nclass Logger(object):\n def __init__(self, buffer):\n assert False\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\n# @classmethod\n# class OrderedClassMembers(type):\n# def __prepare__(self, name, bases):\n# assert False\n# return collections.OrderedDict()\n#\n# def __new__(self, name, bases, classdict):\n# ks = list(classdict.keys())\n# for b in bases:\n# ks += b.__ordered__\n# classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n# return type.__new__(self, name, bases, classdict)\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 120 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q, _) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q, _) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n loader = unittest.TestLoader()\n for q, _ in self.questions:\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self, with_coverage=False):\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = True\n q._report = self\n\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n # print(self.questions)\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n print("q is", q())\n q()._cache_put(\'time\', q.time) # = q.time\n report_cache[q.__qualname__] = q._cache2\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\': True}\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = False\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct + 1)\n if i > 0 and l.find("|", i + 1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = r\'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar", show_progress_bar=True, file=None):\n if file == None:\n file = sys.stdout\n self.file = file\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.01\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n self.pbar = None\n\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar = None\n\n self.file.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=self.file, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\')\n\n for _ in range(self.n - 1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\ndef dprint(first, last, nL, extra = "", file=None, dotsym=\'.\', color=\'white\'):\n if file == None:\n file = sys.stdout\n\n # ss = self.item_title_print\n # state = "PASS" if success else "FAILED"\n dot_parts = (dotsym * max(0, nL - len(last) - len(first)))\n # if self.show_progress_bar or True:\n print(first + dot_parts, end="", file=file)\n # else:\n # print(dot_parts, end="", file=self.cc.file)\n last += extra\n # if tsecs >= 0.5:\n # state += " (" + str(tsecs) + " seconds)"\n print(last, file=file)\n\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n cc = None\n\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n self.successes.append(test)\n self.cc_terminate()\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n self.cc.file.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="", file=self.cc.file)\n else:\n print(dot_parts, end="", file=self.cc.file)\n\n if tsecs >= 0.5:\n state += " (" + str(tsecs) + " seconds)"\n print(state, file=self.cc.file)\n\n def startTest(self, test):\n # j =self.testsRun\n self.testsRun += 1\n # item_title = self.getDescription(test)\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n self.item_title_print = " * q%i.%i) %s" % (UTextResult.number + 1, self.testsRun, item_title)\n estimated_time = 10\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar, file=sys.stdout)\n else:\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n self._stdout = sys.stdout\n sys.stdout = io.StringIO()\n\n def stopTest(self, test):\n sys.stdout = self._stdout\n super().stopTest(test)\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass is None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n print(" " * max(0, self.nL - len(self.cc.title)) + (" (" + str(q_time) + " seconds)" if q_time >= 0.5 else ""))\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n stream = io.StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)))\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n\n return wrapper\n\n\ndef get_hints(ss):\n if ss == None:\n return None\n try:\n ss = textwrap.dedent(ss)\n ss = ss.replace(\'\'\'"""\'\'\', "").strip()\n hints = ["hints:", ]\n j = np.argmax([ss.lower().find(h) for h in hints])\n h = hints[j]\n ss = ss[ss.find(h) + len(h) + 1:]\n ss = "\\n".join([l for l in ss.split("\\n") if not l.strip().startswith(":")])\n ss = textwrap.dedent(ss)\n ss = ss.strip()\n return ss\n except Exception as e:\n print("bad hints", ss, e)\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n _with_coverage = False\n _report = None # The report used. This is very, very hacky and should always be None. Don\'t rely on it!\n\n def capture(self):\n if hasattr(self, \'_stdout\') and self._stdout is not None:\n file = self._stdout\n else:\n # self._stdout = sys.stdout\n # sys._stdout = io.StringIO()\n file = sys.stdout\n return Capturing2(stdout=file)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ is not None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n if self._with_coverage:\n if not hasattr(self._report, \'covcache\'):\n self._report.covcache = {}\n import coverage\n self.cov = coverage.Coverage()\n self.cov.start()\n self.setUp()\n\n def _callTearDown(self):\n self.tearDown()\n if self._with_coverage:\n from pathlib import Path\n from snipper import snipper\n self.cov.stop()\n data = self.cov.get_data()\n base, _, _ = self._report._import_base_relative()\n for file in data.measured_files():\n file = os.path.normpath(file)\n root = Path(base)\n child = Path(file)\n if root in child.parents:\n with open(child, \'r\') as f:\n s = f.read()\n lines = s.splitlines()\n garb = \'GARBAGE\'\n\n lines2 = snipper.censor_code(lines, keep=True)\n assert len(lines) == len(lines2)\n\n for l in data.contexts_by_lineno(file):\n if lines2[l].strip() == garb:\n if self.cache_id() not in self._report.covcache:\n self._report.covcache[self.cache_id()] = {}\n\n rel = os.path.relpath(child, root)\n cc = self._report.covcache[self.cache_id()]\n j = 0\n for j in range(l, -1, -1):\n if "def" in lines2[j] or "class" in lines2[j]:\n break\n from snipper.snipper import gcoms\n fun = lines2[j]\n comments, _ = gcoms("\\n".join(lines2[j:l]))\n if rel not in cc:\n cc[rel] = {}\n cc[rel][fun] = (l, "\\n".join(comments))\n self._cache_put((self.cache_id(), \'coverage\'), self._report.covcache)\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd is None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n sd = self.shortDescriptionStandard()\n title = self._cache_get((self.cache_id(), \'title\'), sd)\n return title if title is not None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome is None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc is not None:\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard())\n\n self._cache2[(self.cache_id(), \'assert\')] = {}\n res = testMethod()\n elapsed = time.time() - t\n self._get_outcome()[self.cache_id()] = res\n self._cache_put((self.cache_id(), "time"), elapsed)\n\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return c, m\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n # sys.stdout = self._stdout\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n self.__class__._cache[\n key] = {} # A new dict. We manually insert it because we have to use that the dict is mutable.\n cache = self._cache_get(key)\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, f"Key {id} not found in cache; framework files missing. Please run deploy()")\n\n # The order of these calls is important. If the method assert fails, we should still store the correct result in cache.\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n assert_fun(first, _expected, *args, **kwargs)\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache is not None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n if os.path.exists(cfile):\n try:\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n except Exception as e:\n print("Bad cache", cfile)\n print(e)\n else:\n print("Warning! data file not found", cfile)\n\n def _feedErrorsToResult(self, result, errors):\n """ Use this to show hints on test failure. """\n if not isinstance(result, UTextResult):\n er = [e for e, v in errors if v != None]\n\n if len(er) > 0:\n hints = []\n key = (self.cache_id(), \'coverage\')\n if self._cache_contains(key):\n CC = self._cache_get(key)\n for id in CC:\n if id == self.cache_id():\n cl, m = id\n gprint(f"> An error occured while solving: {cl}.{m}. The files/methods you need to edit are:") # For the test {id} in {file} you should edit:")\n for file in CC[id]:\n rec = CC[id][file]\n gprint(f"> * {file}")\n for l in rec:\n _, comments = CC[id][file][l]\n hint = get_hints(comments)\n\n if hint != None:\n hints.append(hint)\n gprint(f"> - {l}")\n\n er = er[0]\n doc = er._testMethodDoc\n if doc is not None:\n hint = get_hints(er._testMethodDoc)\n if hint is not None:\n hints = [hint] + hints\n if len(hints) > 0:\n gprint("> Hints:")\n gprint(textwrap.indent("\\n".join(hints), "> "))\n\n super()._feedErrorsToResult(result, errors)\n\n def startTestRun(self):\n # print("asdfsdaf 11", file=sys.stderr)\n super().startTestRun()\n # print("asdfsdaf")\n\n def _callTestMethod(self, method):\n # print("asdfsdaf")\n super()._callTestMethod(method)\n\n\ndef hide(func):\n return func\n\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n# 817\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\nimport inspect\nimport os\nimport argparse\nimport time\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print(b + " v" + __version__ + ", started: " + dt_string+ "\\n")\n # print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print(s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n UTextResult.nL = report.nL\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f" * q{n+1}) Total"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")",\n last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL)\n\n # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n # print(" ")\n # print("="*n)\n # print("Final evaluation")\n # print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n print("")\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f" * {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.normpath(os.path.join(output_dir, token))\n\n\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single unmodified file: ")\n print(">", token)\n # print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n def test_add(self):\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n self.assertEqualC(add(-100, 5))\n\n\nclass AutomaticPass(UTestCase):\n def test_student_passed(self):\n self.assertEqual(2,2)\n\n\nimport cs103\nclass Report3(Report):\n title = "CS 101 Report 3"\n questions = [(Week1, 20), (AutomaticPass, 10)] # Include a single question for 10 credits.\n pack_imports = [cs103]' +report1_payload = '80049568000000000000007d94288c055765656b31947d942868018c08746573745f6164649486948c066173736572749486947d94284b014aa1ffffff4b004b04758c0474696d6594473fb71ac800000000758c0d4175746f6d6174696350617373947d946808473fb127100000000073752e' name="Report3" report = source_instantiate(name, report1_source, report1_payload) diff --git a/examples/example_docker/instructor/cs103/unitgrade/AutomaticPass.pkl b/examples/example_docker/instructor/cs103/unitgrade/AutomaticPass.pkl index 2a722e2b9c8264b76eca73fec2c7dd84eb0e02d3..9b6ff7ac689837f86e1b0e393993ec7acbb784e8 100644 GIT binary patch literal 4 LcmZo*@zVnU0@?uq literal 93 zcmZo*nHt0Z0ku;!dUzd6OY(CQOEQxK5{rwc^az)v7MH{qmz1WY=9R=3Bo-H^rc7y@ m(!&N~6_reBn^HR^gE51tZAuSINoH>9l(s4E5YreKO7#HeD<BB~ diff --git a/examples/example_docker/instructor/cs103/unitgrade/Week1.pkl b/examples/example_docker/instructor/cs103/unitgrade/Week1.pkl index fe27b785553c86fe6975853b9990eed439d2d5bc..20eb565b4b7903e4aef2d3d44e08726a2b0e14ed 100644 GIT binary patch delta 22 ZcmWHy<85G>YRmuuwNobY=`$7U0RS=m1aSZW delta 47 ucmcBu=WAe@>cap5wNo@E^6E=vFlI2dP3d7N$;?fi(l*5%BFVr|ss{iFA`0yQ diff --git a/examples/example_docker/instructor/unitgrade-docker/tmp/cs103/Report3_handin_5_of_30.token b/examples/example_docker/instructor/unitgrade-docker/tmp/cs103/Report3_handin_5_of_30.token deleted file mode 100644 index 675c59014e1063147604cc9ab25520eb3d1bbb5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128198 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ⅈC-(A#PtCqWfJ4m?0>%Yo!U80zepm^@o*1N5K( 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 diff --git a/examples/example_docker/instructor/unitgrade-docker/tmp/cs103/report3_complete_grade.py b/examples/example_docker/instructor/unitgrade-docker/tmp/cs103/report3_complete_grade.py deleted file mode 100644 index b053e48..0000000 --- a/examples/example_docker/instructor/unitgrade-docker/tmp/cs103/report3_complete_grade.py +++ /dev/null @@ -1,345 +0,0 @@ - -import numpy as np -from tabulate import tabulate -from datetime import datetime -import pyfiglet -import unittest -# from unitgrade2.unitgrade2 import MySuite - -import inspect -import os -import argparse -import sys -import time -import threading # don't import Thread bc. of minify issue. -import tqdm # don't do from tqdm import tqdm because of minify-issue - -parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: -To run all tests in a report: - -> python assignment1_dp.py - -To run only question 2 or question 2.1 - -> python assignment1_dp.py -q 2 -> python assignment1_dp.py -q 2.1 - -Note this scripts does not grade your report. To grade your report, use: - -> python report1_grade.py - -Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. -For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: - -> python -m course_package.report1 - -see https://docs.python.org/3.9/using/cmdline.html -""", formatter_class=argparse.RawTextHelpFormatter) -parser.add_argument('-q', nargs='?', type=str, default=None, help='Only evaluate this question (e.g.: -q 2)') -parser.add_argument('--showexpected', action="store_true", help='Show the expected/desired result') -parser.add_argument('--showcomputed', action="store_true", help='Show the answer your code computes') -parser.add_argument('--unmute', action="store_true", help='Show result of print(...) commands in code') -parser.add_argument('--passall', action="store_true", help='Automatically pass all tests. Useful when debugging.') - -def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False): - args = parser.parse_args() - if question is None and args.q is not None: - question = args.q - if "." in question: - question, qitem = [int(v) for v in question.split(".")] - else: - question = int(question) - - if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file: - raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation") - - if unmute is None: - unmute = args.unmute - if passall is None: - passall = args.passall - - results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute, - show_tol_err=show_tol_err) - - - if question is None: - print("Provisional evaluation") - tabulate(table_data) - table = table_data - print(tabulate(table)) - print(" ") - - fr = inspect.getouterframes(inspect.currentframe())[1].filename - gfile = os.path.basename(fr)[:-3] + "_grade.py" - if os.path.exists(gfile): - print("Note your results have not yet been registered. \nTo register your results, please run the file:") - print(">>>", gfile) - print("In the same manner as you ran this file.") - - - return results - - -def upack(q): - # h = zip([(i['w'], i['possible'], i['obtained']) for i in q.values()]) - h =[(i['w'], i['possible'], i['obtained']) for i in q.values()] - h = np.asarray(h) - return h[:,0], h[:,1], h[:,2], - -class UnitgradeTextRunner(unittest.TextTestRunner): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - -class SequentialTestLoader(unittest.TestLoader): - def getTestCaseNames(self, testCaseClass): - test_names = super().getTestCaseNames(testCaseClass) - # testcase_methods = list(testCaseClass.__dict__.keys()) - ls = [] - for C in testCaseClass.mro(): - if issubclass(C, unittest.TestCase): - ls = list(C.__dict__.keys()) + ls - testcase_methods = ls - test_names.sort(key=testcase_methods.index) - return test_names - -def evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False, - show_progress_bar=True, - show_tol_err=False, - big_header=True): - - now = datetime.now() - if big_header: - ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom") - b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) - else: - b = "Unitgrade" - print(b + " v" + __version__) - dt_string = now.strftime("%d/%m/%Y %H:%M:%S") - print("Started: " + dt_string) - s = report.title - if hasattr(report, "version") and report.version is not None: - s += " version " + report.version - print("Evaluating " + s, "(use --help for options)" if show_help_flag else "") - # print(f"Loaded answers from: ", report.computed_answers_file, "\n") - table_data = [] - nL = 80 - t_start = time.time() - score = {} - loader = SequentialTestLoader() - - for n, (q, w) in enumerate(report.questions): - # q = q() - # q_hidden = False - # q_hidden = issubclass(q.__class__, Hidden) - if question is not None and n+1 != question: - continue - suite = loader.loadTestsFromTestCase(q) - qtitle = q.question_title() if hasattr(q, 'question_title') else q.__qualname__ - q_title_print = "Question %i: %s"%(n+1, qtitle) - print(q_title_print, end="") - q.possible = 0 - q.obtained = 0 - q_ = {} # Gather score in this class. - # unittest.Te - # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_] - UTextResult.q_title_print = q_title_print # Hacky - UTextResult.show_progress_bar = show_progress_bar # Hacky. - UTextResult.number = n - - res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) - - possible = res.testsRun - obtained = len(res.successes) - - assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun - - # possible = int(ws @ possible) - # obtained = int(ws @ obtained) - # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0 - - obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 - score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} - q.obtained = obtained - q.possible = possible - - s1 = f"*** Question q{n+1}" - s2 = f" {q.obtained}/{w}" - print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 ) - print(" ") - table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"]) - - ws, possible, obtained = upack(score) - possible = int( msum(possible) ) - obtained = int( msum(obtained) ) # Cast to python int - report.possible = possible - report.obtained = obtained - now = datetime.now() - dt_string = now.strftime("%H:%M:%S") - - dt = int(time.time()-t_start) - minutes = dt//60 - seconds = dt - minutes*60 - plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") - - print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")") - - table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) - results = {'total': (obtained, possible), 'details': score} - return results, table_data - - - - -from tabulate import tabulate -from datetime import datetime -import inspect -import json -import os -import bz2 -import pickle -import os - -def bzwrite(json_str, token): # to get around obfuscation issues - with getattr(bz2, 'open')(token, "wt") as f: - f.write(json_str) - -def gather_imports(imp): - resources = {} - m = imp - # for m in pack_imports: - # print(f"*** {m.__name__}") - f = m.__file__ - # dn = os.path.dirname(f) - # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) - # top_package = str(__import__(m.__name__.split('.')[0]).__path__) - if m.__class__.__name__ == 'module' and False: - top_package = os.path.dirname(m.__file__) - module_import = True - else: - top_package = __import__(m.__name__.split('.')[0]).__path__._path[0] - module_import = False - - # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) - # top_package = os.path.dirname(top_package) - import zipfile - # import strea - # zipfile.ZipFile - import io - # file_like_object = io.BytesIO(my_zip_data) - zip_buffer = io.BytesIO() - with zipfile.ZipFile(zip_buffer, 'w') as zip: - # zip.write() - for root, dirs, files in os.walk(top_package): - for file in files: - if file.endswith(".py"): - fpath = os.path.join(root, file) - v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package)) - zip.write(fpath, v) - - resources['zipfile'] = zip_buffer.getvalue() - resources['top_package'] = top_package - resources['module_import'] = module_import - return resources, top_package - - if f.endswith("__init__.py"): - for root, dirs, files in os.walk(os.path.dirname(f)): - for file in files: - if file.endswith(".py"): - # print(file) - # print() - v = os.path.relpath(os.path.join(root, file), top_package) - with open(os.path.join(root, file), 'r') as ff: - resources[v] = ff.read() - else: - v = os.path.relpath(f, top_package) - with open(f, 'r') as ff: - resources[v] = ff.read() - return resources - -import argparse -parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Use this script to get the score of your report. Example: - -> python report1_grade.py - -Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. -For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: - -> python -m course_package.report1 - -see https://docs.python.org/3.9/using/cmdline.html -""", formatter_class=argparse.RawTextHelpFormatter) -parser.add_argument('--noprogress', action="store_true", help='Disable progress bars') -parser.add_argument('--autolab', action="store_true", help='Show Autolab results') - -def gather_upload_to_campusnet(report, output_dir=None): - n = report.nL - args = parser.parse_args() - results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, - show_progress_bar=not args.noprogress, - big_header=not args.autolab) - print(" ") - print("="*n) - print("Final evaluation") - print(tabulate(table_data)) - # also load the source code of missing files... - - sources = {} - - if not args.autolab: - if len(report.individual_imports) > 0: - print("By uploading the .token file, you verify the files:") - for m in report.individual_imports: - print(">", m.__file__) - print("Are created/modified individually by you in agreement with DTUs exam rules") - report.pack_imports += report.individual_imports - - if len(report.pack_imports) > 0: - print("Including files in upload...") - for k, m in enumerate(report.pack_imports): - nimp, top_package = gather_imports(m) - report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) - nimp['report_relative_location'] = report_relative_location - nimp['name'] = m.__name__ - sources[k] = nimp - # if len([k for k in nimp if k not in sources]) > 0: - print(f"*** {m.__name__}") - # sources = {**sources, **nimp} - results['sources'] = sources - - if output_dir is None: - output_dir = os.getcwd() - - payload_out_base = report.__class__.__name__ + "_handin" - - obtain, possible = results['total'] - vstring = "_v"+report.version if report.version is not None else "" - - token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) - token = os.path.join(output_dir, token) - with open(token, 'wb') as f: - pickle.dump(results, f) - - if not args.autolab: - print(" ") - print("To get credit for your results, please upload the single file: ") - print(">", token) - print("To campusnet without any modifications.") - - # print("Now time for some autolab fun") - -def source_instantiate(name, report1_source, payload): - eval("exec")(report1_source, globals()) - pl = pickle.loads(bytes.fromhex(payload)) - report = eval(name)(payload=pl, strict=True) - # report.set_payload(pl) - return report - - - -report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n\n"""\n# from . import cache_read\nimport unittest\nimport numpy as np\nimport sys\nfrom io import StringIO\nimport collections\nimport re\nimport threading\nimport tqdm\nimport time\nimport pickle\nimport itertools\nimport os\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\ndef setup_dir_by_class(C,base_dir):\n name = C.__class__.__name__\n # base_dir = os.path.join(base_dir, name)\n # if not os.path.isdir(base_dir):\n # os.makedirs(base_dir)\n return base_dir, name\n\nclass Hidden:\n def hide(self):\n return True\n\nclass Logger(object):\n def __init__(self, buffer):\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\nclass QItem(unittest.TestCase):\n title = None\n testfun = None\n tol = 0\n estimated_time = 0.42\n _precomputed_payload = None\n _computed_answer = None # Internal helper to later get results.\n weight = 1 # the weight of the question.\n\n def __init__(self, question=None, *args, **kwargs):\n if self.tol > 0 and self.testfun is None:\n self.testfun = self.assertL2Relative\n elif self.testfun is None:\n self.testfun = self.assertEqual\n\n self.name = self.__class__.__name__\n # self._correct_answer_payload = correct_answer_payload\n self.question = question\n\n super().__init__(*args, **kwargs)\n if self.title is None:\n self.title = self.name\n\n def _safe_get_title(self):\n if self._precomputed_title is not None:\n return self._precomputed_title\n return self.title\n\n def assertNorm(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed).flat- np.asarray(expected)).flat )\n nrm = np.sqrt(np.sum( diff ** 2))\n\n self.error_computed = nrm\n\n if nrm > tol:\n print(f"Not equal within tolerance {tol}; norm of difference was {nrm}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def assertL2(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n self.error_computed = np.max(diff)\n\n if np.max(diff) > tol:\n print(f"Not equal within tolerance {tol=}; deviation was {np.max(diff)=}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol=}, {np.max(diff)=}")\n\n def assertL2Relative(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n diff = diff / (1e-8 + np.abs( (np.asarray(computed) + np.asarray(expected)) ) )\n self.error_computed = np.max(np.abs(diff))\n if np.sum(diff > tol) > 0:\n print(f"Not equal within tolerance {tol}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def precomputed_payload(self):\n return self._precomputed_payload\n\n def precompute_payload(self):\n # Pre-compute resources to include in tests (useful for getting around rng).\n pass\n\n def compute_answer(self, unmute=False):\n raise NotImplementedError("test code here")\n\n def test(self, computed, expected):\n self.testfun(computed, expected)\n\n def get_points(self, verbose=False, show_expected=False, show_computed=False,unmute=False, passall=False, silent=False, **kwargs):\n possible = 1\n computed = None\n def show_computed_(computed):\n print(">>> Your output:")\n print(computed)\n\n def show_expected_(expected):\n print(">>> Expected output (note: may have been processed; read text script):")\n print(expected)\n\n correct = self._correct_answer_payload\n try:\n if unmute: # Required to not mix together print stuff.\n print("")\n computed = self.compute_answer(unmute=unmute)\n except Exception as e:\n if not passall:\n if not silent:\n print("\\n=================================================================================")\n print(f"When trying to run test class \'{self.name}\' your code threw an error:", e)\n show_expected_(correct)\n import traceback\n print(traceback.format_exc())\n print("=================================================================================")\n return (0, possible)\n\n if self._computed_answer is None:\n self._computed_answer = computed\n\n if show_expected or show_computed:\n print("\\n")\n if show_expected:\n show_expected_(correct)\n if show_computed:\n show_computed_(computed)\n try:\n if not passall:\n self.test(computed=computed, expected=correct)\n except Exception as e:\n if not silent:\n print("\\n=================================================================================")\n print(f"Test output from test class \'{self.name}\' does not match expected result. Test error:")\n print(e)\n show_computed_(computed)\n show_expected_(correct)\n return (0, possible)\n return (1, possible)\n\n def score(self):\n try:\n self.test()\n except Exception as e:\n return 0\n return 1\n\nclass QPrintItem(QItem):\n def compute_answer_print(self):\n """\n Generate output which is to be tested. By default, both text written to the terminal using print(...) as well as return values\n are send to process_output (see compute_answer below). In other words, the text generated is:\n\n res = compute_Answer_print()\n txt = (any terminal output generated above)\n numbers = (any numbers found in terminal-output txt)\n\n self.test(process_output(res, txt, numbers), <expected result>)\n\n :return: Optional values for comparison\n """\n raise Exception("Generate output here. The output is passed to self.process_output")\n\n def process_output(self, res, txt, numbers):\n return res\n\n def compute_answer(self, unmute=False):\n with Capturing(unmute=unmute) as output:\n res = self.compute_answer_print()\n s = "\\n".join(output)\n s = rm_progress_bar(s) # Remove progress bar.\n numbers = extract_numbers(s)\n self._computed_answer = (res, s, numbers)\n return self.process_output(res, s, numbers)\n\nclass OrderedClassMembers(type):\n @classmethod\n def __prepare__(self, name, bases):\n return collections.OrderedDict()\n def __new__(self, name, bases, classdict):\n ks = list(classdict.keys())\n for b in bases:\n ks += b.__ordered__\n classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n return type.__new__(self, name, bases, classdict)\n\nclass QuestionGroup(metaclass=OrderedClassMembers):\n title = "Untitled question"\n partially_scored = False\n t_init = 0 # Time spend on initialization (placeholder; set this externally).\n estimated_time = 0.42\n has_called_init_ = False\n _name = None\n _items = None\n\n @property\n def items(self):\n if self._items == None:\n self._items = []\n members = [gt for gt in [getattr(self, gt) for gt in self.__ordered__ if gt not in ["__classcell__", "__init__"]] if inspect.isclass(gt) and issubclass(gt, QItem)]\n for I in members:\n self._items.append( I(question=self))\n return self._items\n\n @items.setter\n def items(self, value):\n self._items = value\n\n @property\n def name(self):\n if self._name == None:\n self._name = self.__class__.__name__\n return self._name #\n\n @name.setter\n def name(self, val):\n self._name = val\n\n def init(self):\n # Can be used to set resources relevant for this question instance.\n pass\n\n def init_all_item_questions(self):\n for item in self.items:\n if not item.question.has_called_init_:\n item.question.init()\n item.question.has_called_init_ = True\n\n\nclass Report():\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 80 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q,_) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n root_dir = self.pack_imports[0].__path__._path[0]\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q,_) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n # else:\n # if os.path.isfile(self.computed_answers_file):\n # self.set_payload(cache_read(self.computed_answers_file), strict=strict)\n # else:\n # s = f"> Warning: The pre-computed answer file, {os.path.abspath(self.computed_answers_file)} is missing. The framework will NOT work as intended. Reasons may be a broken local installation."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n import unittest\n loader = unittest.TestLoader()\n for q,_ in self.questions:\n import time\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self):\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\':True}\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct+1)\n if i > 0 and l.find("|", i+1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = \'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar",show_progress_bar=True):\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.1\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n\n # self.pbar = tqdm.tqdm(total=self.n)\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if hasattr(self, \'pbar\') and self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar=None\n\n sys.stdout.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=sys.stdout, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\') # , unit_scale=dt, unit=\'seconds\'):\n\n for _ in range(self.n-1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\n\n\nfrom unittest.suite import _isnotsuite\n\n# class MySuite(unittest.suite.TestSuite): # Not sure we need this one anymore.\n# raise Exception("no suite")\n# pass\n\ndef instance_call_stack(instance):\n s = "-".join(map(lambda x: x.__name__, instance.__class__.mro()))\n return s\n\ndef get_class_that_defined_method(meth):\n for cls in inspect.getmro(meth.im_class):\n if meth.__name__ in cls.__dict__:\n return cls\n return None\n\ndef caller_name(skip=2):\n """Get a name of a caller in the format module.class.method\n\n `skip` specifies how many levels of stack to skip while getting caller\n name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.\n\n An empty string is returned if skipped levels exceed stack height\n """\n stack = inspect.stack()\n start = 0 + skip\n if len(stack) < start + 1:\n return \'\'\n parentframe = stack[start][0]\n\n name = []\n module = inspect.getmodule(parentframe)\n # `modname` can be None when frame is executed directly in console\n # TODO(techtonik): consider using __main__\n if module:\n name.append(module.__name__)\n # detect classname\n if \'self\' in parentframe.f_locals:\n # I don\'t know any way to detect call from the object method\n # XXX: there seems to be no way to detect static method call - it will\n # be just a function call\n name.append(parentframe.f_locals[\'self\'].__class__.__name__)\n codename = parentframe.f_code.co_name\n if codename != \'<module>\': # top level usually\n name.append( codename ) # function or a method\n\n ## Avoid circular refs and frame leaks\n # https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack\n del parentframe, stack\n\n return ".".join(name)\n\ndef get_class_from_frame(fr):\n import inspect\n args, _, _, value_dict = inspect.getargvalues(fr)\n # we check the first parameter for the frame function is\n # named \'self\'\n if len(args) and args[0] == \'self\':\n # in that case, \'self\' will be referenced in value_dict\n instance = value_dict.get(\'self\', None)\n if instance:\n # return its class\n # isinstance(instance, Testing) # is the actual class instance.\n\n return getattr(instance, \'__class__\', None)\n # return None otherwise\n return None\n\nfrom typing import Any\nimport inspect, gc\n\ndef giveupthefunc():\n frame = inspect.currentframe()\n code = frame.f_code\n globs = frame.f_globals\n functype = type(lambda: 0)\n funcs = []\n for func in gc.get_referrers(code):\n if type(func) is functype:\n if getattr(func, "__code__", None) is code:\n if getattr(func, "__globals__", None) is globs:\n funcs.append(func)\n if len(funcs) > 1:\n return None\n return funcs[0] if funcs else None\n\n\nfrom collections import defaultdict\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n # if self.dots or self.showAll:\n # self.stream.writeln()\n # if hasattr(self, \'cc\'):\n # self.cc.terminate()\n # self.cc_terminate(success=False)\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n # if self.showAll:\n # self.stream.writeln("FAIL")\n # elif self.dots:\n # self.stream.write(\'F\')\n # self.stream.flush()\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n # super().addSuccess(test)\n self.successes.append(test)\n # super().addSuccess(test)\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n self.cc_terminate()\n\n\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n sys.stdout.flush()\n ss = self.item_title_print\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(ss))), end="")\n # current = 1\n # possible = 1\n # current == possible\n ss = "PASS" if success else "FAILED"\n if tsecs >= 0.1:\n ss += " (" + str(tsecs) + " seconds)"\n print(ss)\n\n\n def startTest(self, test):\n # super().startTest(test)\n j =self.testsRun\n self.testsRun += 1\n # print("Starting the test...")\n # show_progress_bar = True\n n = UTextResult.number\n\n item_title = self.getDescription(test)\n # item_title = item_title.split("\\n")[0]\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n # test.countTestCases()\n self.item_title_print = "*** q%i.%i) %s" % (n + 1, j + 1, item_title)\n estimated_time = 10\n nL = 80\n #\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar)\n else:\n print(self.item_title_print + (\'.\' * max(0, nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n # q_title_print = "some printed title..."\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass == None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n print(self.cc.title, end="")\n # start = 10\n # q_time = np.round(time.time() - start, 2)\n nL = 80\n print(" " * max(0, nL - len(self.cc.title)) + (\n " (" + str(q_time) + " seconds)" if q_time >= 0.1 else "")) # if q.name in report.payloads else "")\n # print("=" * nL)\n\nfrom unittest.runner import _WritelnDecorator\nfrom io import StringIO\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n from io import StringIO\n stream = StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\ndef wrapper(foo):\n def magic(self):\n s = "-".join(map(lambda x: x.__name__, self.__class__.mro()))\n # print(s)\n foo(self)\n magic.__doc__ = foo.__doc__\n return magic\n\nfrom functools import update_wrapper, _make_key, RLock\nfrom collections import namedtuple\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)) )\n # key = (self.cache_id(), \'@cache\')\n # if self._cache_contains[key]\n\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n return wrapper\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n\n def capture(self):\n return Capturing2(stdout=self._stdout)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ != None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n self._stdout = sys.stdout\n import io\n sys.stdout = io.StringIO()\n super().setUp()\n # print("Setting up...")\n\n def _callTearDown(self):\n sys.stdout = self._stdout\n super().tearDown()\n # print("asdfsfd")\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd == None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n # self._testMethodDoc.strip().splitlines()[0].strip()\n sd = self.shortDescriptionStandard()\n title = self._cache_get( (self.cache_id(), \'title\'), sd )\n return title if title != None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome == None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc != None:\n # Ensure the cache is eventually updated with the right docstring.\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard() )\n # Fix temp cache here (for using the @cache decorator)\n self._cache2[ (self.cache_id(), \'assert\') ] = {}\n\n res = testMethod()\n elapsed = time.time() - t\n # self._cache_put( (self.cache_id(), \'title\'), self.shortDescription() )\n\n self._get_outcome()[self.cache_id()] = res\n self._cache_put( (self.cache_id(), "time"), elapsed)\n\n # This is my base test class. So what is new about it?\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return (c,m)\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n # self.cache_indexes = defaultdict(lambda: 0)\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n cache = self._cache_get(key, {})\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, first)\n assert_fun(first, _expected, *args, **kwargs)\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__) ) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache != None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n # print("Loading cache from", cfile)\n if os.path.exists(cfile):\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n else:\n print("Warning! data file not found", cfile)\n\ndef hide(func):\n return func\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n # (*)We can be somewhat "hygienic", but newDecorator still isn\'t signature-preserving, i.e. you will not be able to get a runtime list of parameters. For that, you need hackish libraries...but in this case, the only argument is func, so it\'s not a big issue\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\n# from unitgrade2.unitgrade2 import MySuite\n\nimport inspect\nimport os\nimport argparse\nimport sys\nimport time\nimport threading # don\'t import Thread bc. of minify issue.\nimport tqdm # don\'t do from tqdm import tqdm because of minify-issue\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n print(b + " v" + __version__)\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print("Evaluating " + s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n nL = 80\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n # q = q()\n # q_hidden = False\n # q_hidden = issubclass(q.__class__, Hidden)\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n # unittest.Te\n # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_]\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n # possible = int(ws @ possible)\n # obtained = int(ws @ obtained)\n # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"*** Question q{n+1}"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n if m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package))\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n print(" ")\n print("="*n)\n print("Final evaluation")\n print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f"*** {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single file: ")\n print(">", token)\n print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n def test_add(self):\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n self.assertEqualC(add(-100, 5))\n\n @hide\n def test_add_hidden(self):\n # This is a hidden test. The @hide-decorator will allow unitgrade to remove the test.\n # See the output in the student directory for more information.\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n\nclass AutomaticPass(UTestCase):\n def test_student_passed(self):\n self.assertEqual(2,2)\n\n @hide\n def test_hidden_fail(self):\n self.assertEqual(2,3)\n\nimport cs103\nclass Report3(Report):\n title = "CS 101 Report 3"\n questions = [(Week1, 20), (AutomaticPass, 10)] # Include a single question for 10 credits.\n pack_imports = [cs103]' -report1_payload = '80049586000000000000007d94288c055765656b31947d94288c055765656b31948c08746573745f6164649486948c066173736572749486947d94284b014aa1ffffff4b004b04756803680486948c0474696d659486944700000000000000008c0474696d6594473f60628000000000758c0d4175746f6d6174696350617373947d94680c473f689d000000000073752e' -name="Report3" - -report = source_instantiate(name, report1_source, report1_payload) -output_dir = os.path.dirname(__file__) -gather_upload_to_campusnet(report, output_dir) \ No newline at end of file diff --git a/examples/example_docker/instructor/unitgrade-docker/tmp/cs103/report3_grade.py b/examples/example_docker/instructor/unitgrade-docker/tmp/cs103/report3_grade.py deleted file mode 100644 index ecff9f7..0000000 --- a/examples/example_docker/instructor/unitgrade-docker/tmp/cs103/report3_grade.py +++ /dev/null @@ -1,347 +0,0 @@ -""" -Example student code. This file is automatically generated from the files in the instructor-directory -""" -import numpy as np -from tabulate import tabulate -from datetime import datetime -import pyfiglet -import unittest -# from unitgrade2.unitgrade2 import MySuite - -import inspect -import os -import argparse -import sys -import time -import threading # don't import Thread bc. of minify issue. -import tqdm # don't do from tqdm import tqdm because of minify-issue - -parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: -To run all tests in a report: - -> python assignment1_dp.py - -To run only question 2 or question 2.1 - -> python assignment1_dp.py -q 2 -> python assignment1_dp.py -q 2.1 - -Note this scripts does not grade your report. To grade your report, use: - -> python report1_grade.py - -Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. -For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: - -> python -m course_package.report1 - -see https://docs.python.org/3.9/using/cmdline.html -""", formatter_class=argparse.RawTextHelpFormatter) -parser.add_argument('-q', nargs='?', type=str, default=None, help='Only evaluate this question (e.g.: -q 2)') -parser.add_argument('--showexpected', action="store_true", help='Show the expected/desired result') -parser.add_argument('--showcomputed', action="store_true", help='Show the answer your code computes') -parser.add_argument('--unmute', action="store_true", help='Show result of print(...) commands in code') -parser.add_argument('--passall', action="store_true", help='Automatically pass all tests. Useful when debugging.') - -def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False): - args = parser.parse_args() - if question is None and args.q is not None: - question = args.q - if "." in question: - question, qitem = [int(v) for v in question.split(".")] - else: - question = int(question) - - if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file: - raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation") - - if unmute is None: - unmute = args.unmute - if passall is None: - passall = args.passall - - results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute, - show_tol_err=show_tol_err) - - - if question is None: - print("Provisional evaluation") - tabulate(table_data) - table = table_data - print(tabulate(table)) - print(" ") - - fr = inspect.getouterframes(inspect.currentframe())[1].filename - gfile = os.path.basename(fr)[:-3] + "_grade.py" - if os.path.exists(gfile): - print("Note your results have not yet been registered. \nTo register your results, please run the file:") - print(">>>", gfile) - print("In the same manner as you ran this file.") - - - return results - - -def upack(q): - # h = zip([(i['w'], i['possible'], i['obtained']) for i in q.values()]) - h =[(i['w'], i['possible'], i['obtained']) for i in q.values()] - h = np.asarray(h) - return h[:,0], h[:,1], h[:,2], - -class UnitgradeTextRunner(unittest.TextTestRunner): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - -class SequentialTestLoader(unittest.TestLoader): - def getTestCaseNames(self, testCaseClass): - test_names = super().getTestCaseNames(testCaseClass) - # testcase_methods = list(testCaseClass.__dict__.keys()) - ls = [] - for C in testCaseClass.mro(): - if issubclass(C, unittest.TestCase): - ls = list(C.__dict__.keys()) + ls - testcase_methods = ls - test_names.sort(key=testcase_methods.index) - return test_names - -def evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False, - show_progress_bar=True, - show_tol_err=False, - big_header=True): - - now = datetime.now() - if big_header: - ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom") - b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) - else: - b = "Unitgrade" - print(b + " v" + __version__) - dt_string = now.strftime("%d/%m/%Y %H:%M:%S") - print("Started: " + dt_string) - s = report.title - if hasattr(report, "version") and report.version is not None: - s += " version " + report.version - print("Evaluating " + s, "(use --help for options)" if show_help_flag else "") - # print(f"Loaded answers from: ", report.computed_answers_file, "\n") - table_data = [] - nL = 80 - t_start = time.time() - score = {} - loader = SequentialTestLoader() - - for n, (q, w) in enumerate(report.questions): - # q = q() - # q_hidden = False - # q_hidden = issubclass(q.__class__, Hidden) - if question is not None and n+1 != question: - continue - suite = loader.loadTestsFromTestCase(q) - qtitle = q.question_title() if hasattr(q, 'question_title') else q.__qualname__ - q_title_print = "Question %i: %s"%(n+1, qtitle) - print(q_title_print, end="") - q.possible = 0 - q.obtained = 0 - q_ = {} # Gather score in this class. - # unittest.Te - # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_] - UTextResult.q_title_print = q_title_print # Hacky - UTextResult.show_progress_bar = show_progress_bar # Hacky. - UTextResult.number = n - - res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) - - possible = res.testsRun - obtained = len(res.successes) - - assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun - - # possible = int(ws @ possible) - # obtained = int(ws @ obtained) - # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0 - - obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 - score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} - q.obtained = obtained - q.possible = possible - - s1 = f"*** Question q{n+1}" - s2 = f" {q.obtained}/{w}" - print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 ) - print(" ") - table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"]) - - ws, possible, obtained = upack(score) - possible = int( msum(possible) ) - obtained = int( msum(obtained) ) # Cast to python int - report.possible = possible - report.obtained = obtained - now = datetime.now() - dt_string = now.strftime("%H:%M:%S") - - dt = int(time.time()-t_start) - minutes = dt//60 - seconds = dt - minutes*60 - plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") - - print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")") - - table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) - results = {'total': (obtained, possible), 'details': score} - return results, table_data - - - - -from tabulate import tabulate -from datetime import datetime -import inspect -import json -import os -import bz2 -import pickle -import os - -def bzwrite(json_str, token): # to get around obfuscation issues - with getattr(bz2, 'open')(token, "wt") as f: - f.write(json_str) - -def gather_imports(imp): - resources = {} - m = imp - # for m in pack_imports: - # print(f"*** {m.__name__}") - f = m.__file__ - # dn = os.path.dirname(f) - # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) - # top_package = str(__import__(m.__name__.split('.')[0]).__path__) - if m.__class__.__name__ == 'module' and False: - top_package = os.path.dirname(m.__file__) - module_import = True - else: - top_package = __import__(m.__name__.split('.')[0]).__path__._path[0] - module_import = False - - # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) - # top_package = os.path.dirname(top_package) - import zipfile - # import strea - # zipfile.ZipFile - import io - # file_like_object = io.BytesIO(my_zip_data) - zip_buffer = io.BytesIO() - with zipfile.ZipFile(zip_buffer, 'w') as zip: - # zip.write() - for root, dirs, files in os.walk(top_package): - for file in files: - if file.endswith(".py"): - fpath = os.path.join(root, file) - v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package)) - zip.write(fpath, v) - - resources['zipfile'] = zip_buffer.getvalue() - resources['top_package'] = top_package - resources['module_import'] = module_import - return resources, top_package - - if f.endswith("__init__.py"): - for root, dirs, files in os.walk(os.path.dirname(f)): - for file in files: - if file.endswith(".py"): - # print(file) - # print() - v = os.path.relpath(os.path.join(root, file), top_package) - with open(os.path.join(root, file), 'r') as ff: - resources[v] = ff.read() - else: - v = os.path.relpath(f, top_package) - with open(f, 'r') as ff: - resources[v] = ff.read() - return resources - -import argparse -parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Use this script to get the score of your report. Example: - -> python report1_grade.py - -Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. -For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: - -> python -m course_package.report1 - -see https://docs.python.org/3.9/using/cmdline.html -""", formatter_class=argparse.RawTextHelpFormatter) -parser.add_argument('--noprogress', action="store_true", help='Disable progress bars') -parser.add_argument('--autolab', action="store_true", help='Show Autolab results') - -def gather_upload_to_campusnet(report, output_dir=None): - n = report.nL - args = parser.parse_args() - results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, - show_progress_bar=not args.noprogress, - big_header=not args.autolab) - print(" ") - print("="*n) - print("Final evaluation") - print(tabulate(table_data)) - # also load the source code of missing files... - - sources = {} - - if not args.autolab: - if len(report.individual_imports) > 0: - print("By uploading the .token file, you verify the files:") - for m in report.individual_imports: - print(">", m.__file__) - print("Are created/modified individually by you in agreement with DTUs exam rules") - report.pack_imports += report.individual_imports - - if len(report.pack_imports) > 0: - print("Including files in upload...") - for k, m in enumerate(report.pack_imports): - nimp, top_package = gather_imports(m) - report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) - nimp['report_relative_location'] = report_relative_location - nimp['name'] = m.__name__ - sources[k] = nimp - # if len([k for k in nimp if k not in sources]) > 0: - print(f"*** {m.__name__}") - # sources = {**sources, **nimp} - results['sources'] = sources - - if output_dir is None: - output_dir = os.getcwd() - - payload_out_base = report.__class__.__name__ + "_handin" - - obtain, possible = results['total'] - vstring = "_v"+report.version if report.version is not None else "" - - token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) - token = os.path.join(output_dir, token) - with open(token, 'wb') as f: - pickle.dump(results, f) - - if not args.autolab: - print(" ") - print("To get credit for your results, please upload the single file: ") - print(">", token) - print("To campusnet without any modifications.") - - # print("Now time for some autolab fun") - -def source_instantiate(name, report1_source, payload): - eval("exec")(report1_source, globals()) - pl = pickle.loads(bytes.fromhex(payload)) - report = eval(name)(payload=pl, strict=True) - # report.set_payload(pl) - return report - - - -report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n\n"""\n# from . import cache_read\nimport unittest\nimport numpy as np\nimport sys\nfrom io import StringIO\nimport collections\nimport re\nimport threading\nimport tqdm\nimport time\nimport pickle\nimport itertools\nimport os\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\ndef setup_dir_by_class(C,base_dir):\n name = C.__class__.__name__\n # base_dir = os.path.join(base_dir, name)\n # if not os.path.isdir(base_dir):\n # os.makedirs(base_dir)\n return base_dir, name\n\nclass Hidden:\n def hide(self):\n return True\n\nclass Logger(object):\n def __init__(self, buffer):\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\nclass QItem(unittest.TestCase):\n title = None\n testfun = None\n tol = 0\n estimated_time = 0.42\n _precomputed_payload = None\n _computed_answer = None # Internal helper to later get results.\n weight = 1 # the weight of the question.\n\n def __init__(self, question=None, *args, **kwargs):\n if self.tol > 0 and self.testfun is None:\n self.testfun = self.assertL2Relative\n elif self.testfun is None:\n self.testfun = self.assertEqual\n\n self.name = self.__class__.__name__\n # self._correct_answer_payload = correct_answer_payload\n self.question = question\n\n super().__init__(*args, **kwargs)\n if self.title is None:\n self.title = self.name\n\n def _safe_get_title(self):\n if self._precomputed_title is not None:\n return self._precomputed_title\n return self.title\n\n def assertNorm(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed).flat- np.asarray(expected)).flat )\n nrm = np.sqrt(np.sum( diff ** 2))\n\n self.error_computed = nrm\n\n if nrm > tol:\n print(f"Not equal within tolerance {tol}; norm of difference was {nrm}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def assertL2(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n self.error_computed = np.max(diff)\n\n if np.max(diff) > tol:\n print(f"Not equal within tolerance {tol=}; deviation was {np.max(diff)=}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol=}, {np.max(diff)=}")\n\n def assertL2Relative(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n diff = diff / (1e-8 + np.abs( (np.asarray(computed) + np.asarray(expected)) ) )\n self.error_computed = np.max(np.abs(diff))\n if np.sum(diff > tol) > 0:\n print(f"Not equal within tolerance {tol}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def precomputed_payload(self):\n return self._precomputed_payload\n\n def precompute_payload(self):\n # Pre-compute resources to include in tests (useful for getting around rng).\n pass\n\n def compute_answer(self, unmute=False):\n raise NotImplementedError("test code here")\n\n def test(self, computed, expected):\n self.testfun(computed, expected)\n\n def get_points(self, verbose=False, show_expected=False, show_computed=False,unmute=False, passall=False, silent=False, **kwargs):\n possible = 1\n computed = None\n def show_computed_(computed):\n print(">>> Your output:")\n print(computed)\n\n def show_expected_(expected):\n print(">>> Expected output (note: may have been processed; read text script):")\n print(expected)\n\n correct = self._correct_answer_payload\n try:\n if unmute: # Required to not mix together print stuff.\n print("")\n computed = self.compute_answer(unmute=unmute)\n except Exception as e:\n if not passall:\n if not silent:\n print("\\n=================================================================================")\n print(f"When trying to run test class \'{self.name}\' your code threw an error:", e)\n show_expected_(correct)\n import traceback\n print(traceback.format_exc())\n print("=================================================================================")\n return (0, possible)\n\n if self._computed_answer is None:\n self._computed_answer = computed\n\n if show_expected or show_computed:\n print("\\n")\n if show_expected:\n show_expected_(correct)\n if show_computed:\n show_computed_(computed)\n try:\n if not passall:\n self.test(computed=computed, expected=correct)\n except Exception as e:\n if not silent:\n print("\\n=================================================================================")\n print(f"Test output from test class \'{self.name}\' does not match expected result. Test error:")\n print(e)\n show_computed_(computed)\n show_expected_(correct)\n return (0, possible)\n return (1, possible)\n\n def score(self):\n try:\n self.test()\n except Exception as e:\n return 0\n return 1\n\nclass QPrintItem(QItem):\n def compute_answer_print(self):\n """\n Generate output which is to be tested. By default, both text written to the terminal using print(...) as well as return values\n are send to process_output (see compute_answer below). In other words, the text generated is:\n\n res = compute_Answer_print()\n txt = (any terminal output generated above)\n numbers = (any numbers found in terminal-output txt)\n\n self.test(process_output(res, txt, numbers), <expected result>)\n\n :return: Optional values for comparison\n """\n raise Exception("Generate output here. The output is passed to self.process_output")\n\n def process_output(self, res, txt, numbers):\n return res\n\n def compute_answer(self, unmute=False):\n with Capturing(unmute=unmute) as output:\n res = self.compute_answer_print()\n s = "\\n".join(output)\n s = rm_progress_bar(s) # Remove progress bar.\n numbers = extract_numbers(s)\n self._computed_answer = (res, s, numbers)\n return self.process_output(res, s, numbers)\n\nclass OrderedClassMembers(type):\n @classmethod\n def __prepare__(self, name, bases):\n return collections.OrderedDict()\n def __new__(self, name, bases, classdict):\n ks = list(classdict.keys())\n for b in bases:\n ks += b.__ordered__\n classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n return type.__new__(self, name, bases, classdict)\n\nclass QuestionGroup(metaclass=OrderedClassMembers):\n title = "Untitled question"\n partially_scored = False\n t_init = 0 # Time spend on initialization (placeholder; set this externally).\n estimated_time = 0.42\n has_called_init_ = False\n _name = None\n _items = None\n\n @property\n def items(self):\n if self._items == None:\n self._items = []\n members = [gt for gt in [getattr(self, gt) for gt in self.__ordered__ if gt not in ["__classcell__", "__init__"]] if inspect.isclass(gt) and issubclass(gt, QItem)]\n for I in members:\n self._items.append( I(question=self))\n return self._items\n\n @items.setter\n def items(self, value):\n self._items = value\n\n @property\n def name(self):\n if self._name == None:\n self._name = self.__class__.__name__\n return self._name #\n\n @name.setter\n def name(self, val):\n self._name = val\n\n def init(self):\n # Can be used to set resources relevant for this question instance.\n pass\n\n def init_all_item_questions(self):\n for item in self.items:\n if not item.question.has_called_init_:\n item.question.init()\n item.question.has_called_init_ = True\n\n\nclass Report():\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 80 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q,_) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n root_dir = self.pack_imports[0].__path__._path[0]\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q,_) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n # else:\n # if os.path.isfile(self.computed_answers_file):\n # self.set_payload(cache_read(self.computed_answers_file), strict=strict)\n # else:\n # s = f"> Warning: The pre-computed answer file, {os.path.abspath(self.computed_answers_file)} is missing. The framework will NOT work as intended. Reasons may be a broken local installation."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n import unittest\n loader = unittest.TestLoader()\n for q,_ in self.questions:\n import time\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self):\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\':True}\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct+1)\n if i > 0 and l.find("|", i+1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = \'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar",show_progress_bar=True):\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.1\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n\n # self.pbar = tqdm.tqdm(total=self.n)\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if hasattr(self, \'pbar\') and self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar=None\n\n sys.stdout.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=sys.stdout, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\') # , unit_scale=dt, unit=\'seconds\'):\n\n for _ in range(self.n-1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\n\n\nfrom unittest.suite import _isnotsuite\n\n# class MySuite(unittest.suite.TestSuite): # Not sure we need this one anymore.\n# raise Exception("no suite")\n# pass\n\ndef instance_call_stack(instance):\n s = "-".join(map(lambda x: x.__name__, instance.__class__.mro()))\n return s\n\ndef get_class_that_defined_method(meth):\n for cls in inspect.getmro(meth.im_class):\n if meth.__name__ in cls.__dict__:\n return cls\n return None\n\ndef caller_name(skip=2):\n """Get a name of a caller in the format module.class.method\n\n `skip` specifies how many levels of stack to skip while getting caller\n name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.\n\n An empty string is returned if skipped levels exceed stack height\n """\n stack = inspect.stack()\n start = 0 + skip\n if len(stack) < start + 1:\n return \'\'\n parentframe = stack[start][0]\n\n name = []\n module = inspect.getmodule(parentframe)\n # `modname` can be None when frame is executed directly in console\n # TODO(techtonik): consider using __main__\n if module:\n name.append(module.__name__)\n # detect classname\n if \'self\' in parentframe.f_locals:\n # I don\'t know any way to detect call from the object method\n # XXX: there seems to be no way to detect static method call - it will\n # be just a function call\n name.append(parentframe.f_locals[\'self\'].__class__.__name__)\n codename = parentframe.f_code.co_name\n if codename != \'<module>\': # top level usually\n name.append( codename ) # function or a method\n\n ## Avoid circular refs and frame leaks\n # https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack\n del parentframe, stack\n\n return ".".join(name)\n\ndef get_class_from_frame(fr):\n import inspect\n args, _, _, value_dict = inspect.getargvalues(fr)\n # we check the first parameter for the frame function is\n # named \'self\'\n if len(args) and args[0] == \'self\':\n # in that case, \'self\' will be referenced in value_dict\n instance = value_dict.get(\'self\', None)\n if instance:\n # return its class\n # isinstance(instance, Testing) # is the actual class instance.\n\n return getattr(instance, \'__class__\', None)\n # return None otherwise\n return None\n\nfrom typing import Any\nimport inspect, gc\n\ndef giveupthefunc():\n frame = inspect.currentframe()\n code = frame.f_code\n globs = frame.f_globals\n functype = type(lambda: 0)\n funcs = []\n for func in gc.get_referrers(code):\n if type(func) is functype:\n if getattr(func, "__code__", None) is code:\n if getattr(func, "__globals__", None) is globs:\n funcs.append(func)\n if len(funcs) > 1:\n return None\n return funcs[0] if funcs else None\n\n\nfrom collections import defaultdict\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n # if self.dots or self.showAll:\n # self.stream.writeln()\n # if hasattr(self, \'cc\'):\n # self.cc.terminate()\n # self.cc_terminate(success=False)\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n # if self.showAll:\n # self.stream.writeln("FAIL")\n # elif self.dots:\n # self.stream.write(\'F\')\n # self.stream.flush()\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n # super().addSuccess(test)\n self.successes.append(test)\n # super().addSuccess(test)\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n self.cc_terminate()\n\n\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n sys.stdout.flush()\n ss = self.item_title_print\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(ss))), end="")\n # current = 1\n # possible = 1\n # current == possible\n ss = "PASS" if success else "FAILED"\n if tsecs >= 0.1:\n ss += " (" + str(tsecs) + " seconds)"\n print(ss)\n\n\n def startTest(self, test):\n # super().startTest(test)\n j =self.testsRun\n self.testsRun += 1\n # print("Starting the test...")\n # show_progress_bar = True\n n = UTextResult.number\n\n item_title = self.getDescription(test)\n # item_title = item_title.split("\\n")[0]\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n # test.countTestCases()\n self.item_title_print = "*** q%i.%i) %s" % (n + 1, j + 1, item_title)\n estimated_time = 10\n nL = 80\n #\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar)\n else:\n print(self.item_title_print + (\'.\' * max(0, nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n # q_title_print = "some printed title..."\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass == None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n print(self.cc.title, end="")\n # start = 10\n # q_time = np.round(time.time() - start, 2)\n nL = 80\n print(" " * max(0, nL - len(self.cc.title)) + (\n " (" + str(q_time) + " seconds)" if q_time >= 0.1 else "")) # if q.name in report.payloads else "")\n # print("=" * nL)\n\nfrom unittest.runner import _WritelnDecorator\nfrom io import StringIO\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n from io import StringIO\n stream = StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\ndef wrapper(foo):\n def magic(self):\n s = "-".join(map(lambda x: x.__name__, self.__class__.mro()))\n # print(s)\n foo(self)\n magic.__doc__ = foo.__doc__\n return magic\n\nfrom functools import update_wrapper, _make_key, RLock\nfrom collections import namedtuple\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)) )\n # key = (self.cache_id(), \'@cache\')\n # if self._cache_contains[key]\n\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n return wrapper\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n\n def capture(self):\n return Capturing2(stdout=self._stdout)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ != None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n self._stdout = sys.stdout\n import io\n sys.stdout = io.StringIO()\n super().setUp()\n # print("Setting up...")\n\n def _callTearDown(self):\n sys.stdout = self._stdout\n super().tearDown()\n # print("asdfsfd")\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd == None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n # self._testMethodDoc.strip().splitlines()[0].strip()\n sd = self.shortDescriptionStandard()\n title = self._cache_get( (self.cache_id(), \'title\'), sd )\n return title if title != None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome == None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc != None:\n # Ensure the cache is eventually updated with the right docstring.\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard() )\n # Fix temp cache here (for using the @cache decorator)\n self._cache2[ (self.cache_id(), \'assert\') ] = {}\n\n res = testMethod()\n elapsed = time.time() - t\n # self._cache_put( (self.cache_id(), \'title\'), self.shortDescription() )\n\n self._get_outcome()[self.cache_id()] = res\n self._cache_put( (self.cache_id(), "time"), elapsed)\n\n # This is my base test class. So what is new about it?\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return (c,m)\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n # self.cache_indexes = defaultdict(lambda: 0)\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n cache = self._cache_get(key, {})\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, first)\n assert_fun(first, _expected, *args, **kwargs)\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__) ) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache != None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n # print("Loading cache from", cfile)\n if os.path.exists(cfile):\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n else:\n print("Warning! data file not found", cfile)\n\ndef hide(func):\n return func\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n # (*)We can be somewhat "hygienic", but newDecorator still isn\'t signature-preserving, i.e. you will not be able to get a runtime list of parameters. For that, you need hackish libraries...but in this case, the only argument is func, so it\'s not a big issue\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\n# from unitgrade2.unitgrade2 import MySuite\n\nimport inspect\nimport os\nimport argparse\nimport sys\nimport time\nimport threading # don\'t import Thread bc. of minify issue.\nimport tqdm # don\'t do from tqdm import tqdm because of minify-issue\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n print(b + " v" + __version__)\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print("Evaluating " + s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n nL = 80\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n # q = q()\n # q_hidden = False\n # q_hidden = issubclass(q.__class__, Hidden)\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n # unittest.Te\n # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_]\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n # possible = int(ws @ possible)\n # obtained = int(ws @ obtained)\n # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"*** Question q{n+1}"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n if m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package))\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n print(" ")\n print("="*n)\n print("Final evaluation")\n print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f"*** {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single file: ")\n print(">", token)\n print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n def test_add(self):\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n self.assertEqualC(add(-100, 5))\n\n\nclass AutomaticPass(UTestCase):\n def test_student_passed(self):\n self.assertEqual(2,2)\n\n\nimport cs103\nclass Report3(Report):\n title = "CS 101 Report 3"\n questions = [(Week1, 20), (AutomaticPass, 10)] # Include a single question for 10 credits.\n pack_imports = [cs103]' -report1_payload = '80049525010000000000007d94288c055765656b31947d94288c055765656b31948c08746573745f6164649486948c066173736572749486947d94284b014aa1ffffff4b004b04756803680486948c0474696d65948694473f506a000000000068038c0f746573745f6164645f68696464656e948694680686947d944b004b04736803680c8694680a86944700000000000000008c0474696d6594473f926de000000000758c0d4175746f6d6174696350617373947d94288c0d4175746f6d6174696350617373948c10746573745f68696464656e5f6661696c9486948c066173736572749486947d9468158c13746573745f73747564656e745f706173736564948694681886947d946815681b86948c0474696d659486944700000000000000006812473f9894100000000075752e' -name="Report3" - -report = source_instantiate(name, report1_source, report1_payload) -output_dir = os.path.dirname(__file__) -gather_upload_to_campusnet(report, output_dir) diff --git a/examples/example_docker/run_all_docker.py b/examples/example_docker/run_all_docker.py new file mode 100644 index 0000000..15e9ea7 --- /dev/null +++ b/examples/example_docker/run_all_docker.py @@ -0,0 +1,57 @@ +from unitgrade_private2.docker_helpers import docker_run_token_file +import os +import glob +import pickle +import time + +""" Run all examples on docker. """ + +if __name__ == "__main__": + # Step 0: Compile our two docker images. + from unitgrade_private2.docker_helpers import compile_docker_image + + docker_files = [f"{os.getcwd()}/../../docker_images/unitgrade-docker/Dockerfile"] + docker_tags = [] + for f in docker_files: + tag = compile_docker_image(f) + docker_tags.append( (f, tag) ) + + EX_BASE = f"{os.getcwd()}/../" # Base of examples. + + runs = [ + ("example_flat", "programs", "programs/report1flat_grade.py", "programs", "programs/report1flat_grade.py",), + ("example_simplest", "", "cs101/report1_grade.py", "", "cs101/report1_grade.py", ), + ("example_framework", "", "cs102/report2_grade.py", "", "cs102/report2_grade.py", ), + ("example_docker", "", "cs103/report3_complete_grade.py", "", "cs103/report3_grade.py",), + ] + rs = [] + + def p2mod(file, base): + return ".".join(os.path.normpath(os.path.relpath(file, base)).split(os.sep))[:-3] + start = time.time() + for ex, ibase, ig, sbase, sg in runs: + ibase = f"{EX_BASE}/{ex}/instructor/{ibase}" + ig = f"{EX_BASE}/{ex}/instructor/{ig}" + sbase = f"{EX_BASE}/{ex}/students/{sbase}" + sg = f"{EX_BASE}/{ex}/students/{sg}" + + # Uncomment to run example deployment scripts: + # os.system(f"cd {ibase} && python -m {p2mod(os.path.dirname(ig) + '/deploy.py', ibase)}") + + os.system(f"cd {sbase} && python -m { p2mod(sg, sbase) }") + stoken = glob.glob(f"{os.path.dirname(sg)}/*.token")[0] + + Dockerfile, tag = docker_tags[0] # Get first docker file. + token = docker_run_token_file(Dockerfile_location=Dockerfile, + host_tmp_dir=os.path.dirname(Dockerfile) + "/tmp", + student_token_file=stoken, + instructor_grade_script=ig) + with open(token, 'rb') as f: + iresults = pickle.load(f) + with open(stoken, 'rb') as f: + sresults = pickle.load(f) + rs.append( (ex, sresults, iresults) ) + + for ex, sresults, iresults in rs: + print( f"[{ex}]", "Student's score was:", sresults['total'], "score using my eval script", iresults['total']) + print("Total elapsed time", time.time()-start, "seconds") \ No newline at end of file diff --git a/examples/example_docker/students/cs103/.coverage b/examples/example_docker/students/cs103/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..f386b2198168450113494de5b3b3bd99d653d108 GIT binary patch literal 53248 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 Z7!85Z5Eu=C(GVC7fzc2c4FSR-0004q+NS^j literal 0 HcmV?d00001 diff --git a/examples/example_docker/students/cs103/Report3_handin_10_of_30.token b/examples/example_docker/students/cs103/Report3_handin_10_of_30.token index 7231343c1882f4366cfe40348680c8d947889798..3880b47087c30a84d5471426ba613e6421ea54fe 100644 GIT binary patch delta 8228 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 E0BT<w$p8QV delta 15750 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 V171BbrH3`S*wDauN@;Pa9stCDjCTM4 diff --git a/examples/example_docker/students/cs103/__pycache__/homework1.cpython-38.pyc b/examples/example_docker/students/cs103/__pycache__/homework1.cpython-38.pyc index 5c552a81fc42feaf0654da24d93270b73dc806af..d82e790ad8cee1871f1919af140451af558d6b85 100644 GIT binary patch delta 20 bcmaFB{(zl3l$V!_fq{X+V6MSN?yJlIG*SfQ delta 20 bcmaFB{(zl3l$V!_fq{WR`Jc{4?yJlIHYo)( diff --git a/examples/example_docker/students/cs103/__pycache__/homework1.cpython-39.pyc b/examples/example_docker/students/cs103/__pycache__/homework1.cpython-39.pyc index 6dfacc25f48700797f5286eae416edaeaf0e2b5b..0e7a0c627f56abdd78d2ba3ec05f9d13ea233030 100644 GIT binary patch delta 20 bcmX@ec94xbk(ZZ?fq{YHakBPCZZl>8GI|7s delta 20 bcmX@ec94xbk(ZZ?fq{V`+D&sKw;3}4E(!zQ diff --git a/examples/example_docker/students/cs103/__pycache__/report3.cpython-38.pyc b/examples/example_docker/students/cs103/__pycache__/report3.cpython-38.pyc index da19a02cc1c508d3eaafcf35b0b34e9d58501d7e..78157d3ccd77777a3e6bdc5d8ef57fa286b7b497 100644 GIT binary patch delta 25 gcmaFC^@58pl$V!_fq{WRf388I7xPBGNLEH>08T#zHvj+t delta 25 gcmaFC^@58pl$V!_fq{WR>7P#Gai)!Yk*ti209Azs0RR91 diff --git a/examples/example_docker/students/cs103/__pycache__/report3_complete.cpython-38.pyc b/examples/example_docker/students/cs103/__pycache__/report3_complete.cpython-38.pyc index 921af5c7208e7aa4d953fa1e4551029aeb05c182..41e62d06e5af5bb771d3c92d6afde5e8a0d36380 100644 GIT binary patch delta 24 fcmcb@dxe)bl$V!_fq{YHP>^ooj*Yy>*ch1sPU{9S delta 24 fcmcb@dxe)bl$V!_fq{X6_n%Ip=|<jTY>bQmN<0Ok diff --git a/examples/example_docker/students/cs103/__pycache__/report3_complete.cpython-39.pyc b/examples/example_docker/students/cs103/__pycache__/report3_complete.cpython-39.pyc index 2f18f8be0976727ab41dd1114924b8483c6d3aac..3e87b71662ab4e431da5c9b952594f5411205f72 100644 GIT binary patch delta 948 zcmcb|`Gl7*k(ZZ?fq{Xc>7P!b2+Kr18OFSc+Qts<3@NNBY%L5aY^jXR%u(zq3@Pk6 z98nzZ3@IEboGlC~oT*IB%u$@FTq#`HOhs9#tf`#M49$#;3@Plv44T{<`*au?Z%y9K zD9gsrz`*dUZ1O!u1EIvylKh;+r1;c|#N2|MRDC_Y$-GRP1$Y@47>Yp3AcVl=3rv~v zf*?T_5FrF25Yk|lz~l&Kx%y;~%aJiN0|SEt0|P^`0s{j>Dnk@w3PTiA3S$aW3qurh zDoYA;3QG$^Gh-BM3TrTfCfhAu$I_Dg+{BX1<bcHD;$%i7vp{T+_0AyU1Q-|?Y8VzU zq%fv1E@Wb4NMQ(O&}6FjE8=8eU|7j`i#xHnIJKz6wXifXXC-41AF7K%x^D@Wq!yRN z7nhW#q~?{x7l1UR2!qUK0$IvfrHJAbJuIp*oL7&{eN1roF+-g95)>#!5+El@fe3K; zfeEm?1i&_BWTvE~=EbKaX6A^&9mP~7hs99{rS&;}nruZNPZWuOOcn(Z(jWrlup(Iy z3v3aB09jci&cMI`iqv8@1_lNWMh<2sMzEM4?)X$giBE7`GNp+#rLv|lr!l27MX{x_ zr!z)zq_6}tXtGY;&MaU55|pg{G&#}ScZ;<ovm__A2xK+b(p#K`rK!awnfZCew^);l z4GoNM@f0K`XUAve7UUO|6hqP^nh)d{7#Khvh5CSnQGl@sBzB7<K0Y@wGcP`#S(E$L zWIq;F##@t1STsFAUM^Aw1qetP#7zh@!7NbF+~Tmw%}*)KNwotdzhY225@F<F65tcy L;9wMB<zNH=sMEQn delta 416 zcmaFDdykVZk(ZZ?fq{V`+D$Vtgn1&L3}f6xZDWHJh7{Hu_9%9Dh7`6G_7;W|_Ee^3 z<|vL-&J>PprlP1+)>MvWhGs@ah7{Id22IY5^K=*)qb8qblx5>%U|{%_Joz1?!DK_G zO#(a&3=BmeF$lpw`7=|dyZ}g$1w;sf2!u44#Xq^4Sx&|;8RRZx%*4RJ;K0DZP^>Vy zkyTV6oiU0vl`Wkyiamunm_d_e@)}n8dOuB$A|a5O!XQF~fq~%`Ye{BFPHGVw0|P@8 zXJKh-aY<%=Uhyr~<YGeu<0zhj#N_Pw%-n+fqLN~W8^MkM6Z{}U#26SDK&BOgOyFQ- zVdP>g0*T$?h>y=r%*>0A*W`*4EzQd;NiUil!Y0QUHMy8g(@_$p7vdhUc?bez@+}UV d-29Z%oK!nTkgq@?CBn$VB*4ePD8S0W2ms_4PRalP diff --git a/examples/example_docker/instructor/unitgrade-docker/tmp/cs103/__pycache__/report3_complete_grade.cpython-38.pyc b/examples/example_docker/students/cs103/__pycache__/report3_complete_grade.cpython-39.pyc similarity index 90% rename from examples/example_docker/instructor/unitgrade-docker/tmp/cs103/__pycache__/report3_complete_grade.cpython-38.pyc rename to examples/example_docker/students/cs103/__pycache__/report3_complete_grade.cpython-39.pyc index 8bcaebe92988bf2a57647836e6aabc65f30fdadc..5efad03805d904ffdc7a11abb12cf5d179ea47c5 100644 GIT binary patch delta 2103 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 delta 1936 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 diff --git a/examples/example_docker/students/cs103/__pycache__/report3_grade.cpython-38.pyc b/examples/example_docker/students/cs103/__pycache__/report3_grade.cpython-38.pyc index 7433fca776754f5b6910141e9adc509cbf13a6f5..fe08941631eb3353b6394ba8fca90bb65c4db357 100644 GIT binary patch delta 10563 zcmexxgn3UlGhZk#FBby?1B1a_gG9F*6ZvEqFHF=9uTSO7;!a^rVN2!A;z?oeWsc%a z<;~(t;Yi_3<xgQs;Y#7|Wo2Xli}9rJrtqck_cBBI0x7&Hf=GO!6y6kJB)&)rZ;B{{ zpJJ3Eo+8o07$uM*nIhG~5GCl&kRqKT)1uChB9qFNCDhCuCG5_SBAX)D!jK}D%9bV4 z%p4_}Vw|FoqS(S1B{tcCQMFz?N<4)zg(-(4iUUkbL`i^Y$tX!M4H8#MQEp*~l1foY zQEg#}k_L-&MsY&DsfOlF^%UL|4TuxLUerwCP0@nzQ%q8{Q*>GwqhwNaQ}kLGqGYvF z^rPfb45H*y45JiMWm6SW<x=IFnWGd_IkJ=%D5t6{WQ<Zxl}S}hRo%?T^pA~EZ!<4v z5Hq9Z<^&!cM#iAYy}Y@M0h538-ewG+e4VeFF>G@%e>Wpz^yK>j#X3<83=G9g3=9k$ zj76>t3=AR3yrAfUVhsic26hGp24|2er^$7K)(SPe3)oUP7Bbcfl(5(Er*MihlyHbM zq;QEd)Ch<(q_9lBDX3r1UL#n;Q8X!qH;*ZWubGLFA)TRCsDx($&q9V;;Y@~FkrLi! zhFZ}Qz8Z!s{$@rOh6#+ZeKAb6Vlhm$;<XYrj42G+oJDtP#8UXh8EP1_nTzfe?x+!6 zAW$Q|kg-;>gsVn8MWC6f=tqtC0>On0c`P;JSwgi^DS{9k^)*syOkxbR(k1LQ(kXl? zLcL6k3@O4j;_<>@QY4e1R;EU}L^NKkhO0z0Uc5%EL^NKah9OI`L^NKqL@Gt3L^MUT zmuZ1?jpRbcTG<-eESXxl5)dud%vdWAV}WV;W=3%aFiQc%s*%mAm&s<Dz*sz?M6rpn zMlMA-n`HuH(Yg|)8i^Wt5rz~o5r!H$X@+LTJf;-kTKO6Yka&$ujZ}*G9M&3<8ZjG& z8u=RWc##@8u#+VqzM9RDA~~12Rv|^IMm$SoHbaVZjl^t*xh%DcCG0f{DKcQ^%ho6~ zGuBIh+@sjcD8V4YAkI*uD9+H#$jDH_o+1UJYlIdkr^vz$m7B|qVjy!m(?Ui@hQb{s zDm5a_j4{l$O0{ye@+C?osx@-WjLnR-a$vSRm@Qu;SHlo5Tq9q@5HC`~uz+zPLv1}T zIF-nQQ;8BJ-fM&<7@8Stm1~q!6r>nxRce%LR8kZ~7$g{KRZ|pGlv)^S)k@S-l)<S; zMVz5Vtwtq9wV6qrAw><$Q%z9^^EAX6Qus>LYZ$UbK*60NEzSVu34?f`Fi6pK0u>fu zUuxl23h`x)e2rWhQ;kT9_8g{K^&0gWp){so22GvG3q|@E^ClaJ+Di+nrs%8Y>Z?X7 zsCrnb`dX<5S1~D+RdMSm6i@CDm7TmvRC@9y(JE~g1uccbDo#xW1%;6OlEj=MNd^Xn zUyS-ioD2*MMRE)b3{~u!5UI%}Vug%xlOH}570OA>O-f0$TgAY@@EMfrsuU+Xh)Rn? zM5_eACMgu;XXcd@D<qaowi1`o(`1ffP0K7QE{S5vNh~h8#gyk0#R^ua$#jdg1Y~y< zdrE3aVrEXUrpDw#aZ|Zl9K|I?X(gGtsYU7x3=C1+@$qG;Ma7x<dGYZ@(UXseyD^4O z=9aK#ElOr!V3_PCk;NaymQqlZnO9O2&%nT-sWExGL<MIY$S3+B6*iK+^=Y6|p#tP+ zRt`oMMm|O<CKg66Mj0kHMiE9nMj<8^MvyoglLVs(BNHPRqXeS>BM&1RBO4<Nm}Fz* zU=m_tW8`AeVp3uh0f{jQFbXlMF>)|!F!3?+Kvi=vaxn=oaxn5RaxiHyN-?nfXJb`k zoa`dCuzo+t4J;rxFbXjiH83zRBr}3|P%Oy6zyJz<Z~?ZBk%1wTp_Z|Rp_ZwJDT|?& zxrDKXDTT3_smKh>Vrph8HY;JuVqU-kl3&PJ%aX!e%bKT9!dk<W#kPPwg=HaQ3acbT zGh-G<7H0~p<m3ct7Dl$osnUw|><gLti)uMbm=^HWuq<SBVTetLVXoz><*wza;c;Pz z4X6d>3Vu-8Su_P~i9iik4evsxT0SsO5KKbFYFLXef%z;Tv+|f~SQawX^4D-K5USy; z;a|wa$WS<?uq~Xylp&BIh#`WZhGX(yX?8(Q8-^N|8paeZ8-^Oz8pagv$)Ylf`Wy=x zComSNfXzd-1#D`G2v`Pca|-X|`7$zc6Br9sCNLI?<r#n_1#6fgPACznVFoKvRO0iy z#StH$mYI_p9}i*`B$i~v$A>_YJ17#|CSQ|rWG`w2B^rgv8nViKjv%Hyh-d;OqDWas zfm#LzhFcuzsU>BJIi;yZRg+iA%8S;6l(c||RuIt)BDyBulZ_EN42o|jQ0y|YFtRYS zv5HLgk&|T9n4Bi3z&K-byId#}p9pfK@0k2b!MHw?p_Z+LErkVK{4r;-7XK?@uVKjI zXl6`d&1Na;C}B%s1IsaFakwzV3e>Wfu%)oquxD|CYOlh$61Ef$h&&5J78gjTIRg_z zJQvs$E~qL0O4w33L8?l)YZ$V)K=oQ|NDNagM{PZO2~Q1sGh+->EoUuP4Z{N7g$yyw zprldDTf<Ys363z{8qO4M8-^P8EWR4f6doIf8qONF8V(zV61EgxkmziNxlADA3s=;z z)v%=S*)V`BN{$JPMG__a3j`K|({`3%79XhEtmUg=NfD4_h+(SbudfxTVaO5!)v6O1 zi`-HKK|FDWJf;+uTEP@>nyL|~5lmwd1J%>~S;7lMN<gG&4P%YaLZ*d`wL&FgH9{%E z%}o75wZbLh3nXe-YJ@@Q5Gf5ZXR%ITEZ$PWmLdXjW(ix0D3s=9uMq+TWC}wtgQnQz z(@H8VVW0%Sp?okoO1dbuAit<2J~uz5G$%E_xF9t-Gc7YYu_QA;?-om5Vs2^`vx1hw zE%xI4(xT+l;wbj`jKsW@%)Fu$P*KJdUsffoS{$#M8K0jPubQb^tXGnsotk%xsXPf> z1Xp=Y7ErMiEzQf#PsvQnOifV$rCBQlP06C<$u%mvlh>(8)`Jph6i-27a&~-XF335> zMcJTKHv?paC{!#yDX};;z9=;(u_UuBwFsP=6+!8jEj_g)xjd!F0i-1qL@0rTIr8$0 zazWXmC><nL1rlQ`$V|@8Nxj9AQkq+!DN>{Z66A8oFD)r3Es0ObEGjAksnrD$dLRN+ z+!oCNu_l3(GG<4y<Ynd-6oInGEynnwSs<y_$pxx9Y;_=J{p5!ag(q)Sm5$_KWMC-v z1LqYsaOug$$o7wm6I3d*G4e5TFmi!O0VWVzh>?epkC_833M!R3n79}@n0T0Y7^Rp5 z7{!=*7{!=CvK)*>ODF5Awb(8IxrQ|-KQX2F7Hd*vNow&e_Ozn>+>F!;P1d5>AXz?0 z%o@f+Vy<X8C@`4{a*A}oZe&&$mUIQVkpt{T0Y(8v0j8q$3=9m3PiJq|SC3?6R#H-$ zyj@3rGNZ1ZASB?pAkm|cU(7Yxpj~3}I~{hW%>2piZ6=e~6)8;STgl;3l3GzxUX)k> zQ<|KglV6mWo5%%no&t=g05!-hzbF+VlUACST#}!kQw)`w9N%dw165U;mswJhT3iA% z6I7LQO`hBtR1Y&xFEcN*L?f}ZB)=%NIJLw!q^LAiQ$ayl0VJdV7Asb$%r7lcC{8UY z$;?YHR!B@MNi9-HElVw`RDdJ^F0Pc+G=+3<#j8=QX~m_Wpr8QZr72Z|oT}#@<m&2I zTU}hMq{+pVTRAyFH+k|QT`5L|$#-=1C)ewVPd=k(&y!eOoLW?(;Fg$EoH{v9Uu?33 z{-?>D1|K&6Fvw?|oMv=-vaxaP<n_iM!L+=owV<+sLvl`HadB>HNk)DOm$Jg-$-atA z$_kT@`f5ljgWLhe$OeNHz4g^(Q3f%@{PaPpUHw#K5USJiixiR+GV>HbIj7hPq^{9V z0i<rBUkZp~^|$8b0^6$)1TF?92ZYqI7#bNUOui7}&8V<hI+TZT^6@4CCFHyrZ4j%Y zpdKF&%3JaA>d<hSEFb1H`Bt~kWc4s5c2FLVkLTJP5tht2c~AH;W`%go$s1x7_zU&& zd=zXIic@paK&;7|B8n#KMrTjn5Uo3TU98e%u^5rbnlXDPe~z&?(@`kTEXjyZ&M!+X zN=#3+1qUb8Hc)6}rlBf;#Mk5nu?Ca<n^`UN;!(6K*eZY$1vpMYx?#9bFCLOnpw8l& zY!W+1Tp1egV22i#rWS+B!(z?J`k^wD@5ee$-tWR@t5m3vS*)a^P^h7)iD|5cre1t< zVsb`md_if6hB_$8s_Q5e>VX)V3d)lW;v5-`CKtvPse;{A3pWA3x5P2M1rC?VFXEPM z?v0mWWLD5tn7k@MlT$%k!BA7dPQhTZW3%w&w+Z2sof9J%i#E?rEMT0>l^i~KQll^z zC`fd`Me<}uOJU};%$&&yLE<c+_?!H(U7Q~jPauwhEksdeaw4O_<Xg#UlO5AVSpD+z zQn@zgrMzOCT$GkI`F#e55ZoK!kOOI*{57qP17^<Tn)Hszni=x!3c3o0npO&%eKMFC z>p|HL(%#hoH?4IPKuv2Mg*+b}h180YqC^E-1tlejJ8eOh>L{e-mlRj#+N$fR>p%(> zTlMmc%#u`Ca?DHvJ6OTiRso~|k~Tnb4ssGGsw<22ic3=ROG~)8xIyB|3dO|=whEI4 znnZcPo={dOE=ep&oxGt|-618vB)%ZAs05@!17fv;mO^e~g@yshl?u8FIjMOXAV+IL z*<gQYLKBAaWa&nYdT2=k>N_ZCgFFB+Qb!>*FU1z_J&^k#$w^ruHK#Zg;&NDeggXRH z7dW(&ll2g~L5@+-wuOWr$V18s$_klj3MIv<$;AqGwh9J%rZBTX{)c#$sZK*2;!co$ zXoy1$o&2H1YqDjj8&`6&0whqmCMy_7OrBJ_Wb*krtdlj$1vaylXE9E`SI#q8snQlK z_`8&I@`OsU$=o%fla*?wbA!?W)R~hNTSX^duaQP18H6)7)GAAW+zb){W4ON+Y$r?C zN=k!*8|-3Oco!GL0(<h=6vfH8HDZ$|#Ib<#>EyeO5~^_h_`+>+VVp8ZhZ9k%xlkO4 z7P6B!x(Ty@lMhp!xdey-4NH`Wnru_2J-NNkY;t%#@8qxsrOBQRypt!_OH5u>e{S-O z21#>Jr&cvnPc>6hLA6*(K~+H`G$gg6Bq+7GG^a!_uQWF)wFs2Ybs!}wsE#ZSD$SdG zp}}bK)f7o^FesR_P7Y|y7KAwfp71AM=$4u6Z^t(IZll^{ty<y9f=xD)z5BH$uQcS9 z&dk>fE-A{)OZW8G(1dsuluL_C@(U(=*GYlYOy-*)%Lft&Ni8nX0NJg%*{8{!aq`LL z#gnsJTqpBeh)#aj!a4a|OUC4@De9Box^PXlX;q&5PK$l==~kY}TD4r_;E05#ijvHd zoK#Is1#Jb5$t-OKJf`3bprE9rsmV3@Y+Ev0aY<2XV(#RL{fd(}7zj*mY5%nONv9Yi zqvqs*?sGQa_$W#(DJ{xV0JT60Qj55tReEYkd<Li!uTc!E)-%(<`5lswt+*ynD3Pcy zDXN6z2vCv(mj<xLj9yA=N@`vSNITdTxPszhy`t2DoW$f*4Rv*OB_$<wb#)z(i}i|2 ziZTl{AUPSN9BjXWtwOXCm}RA;qYw)>D@(yvA+NqbFR>^cRH#H37whHZm!}qKXzHb9 z=A~$4Xexjz^9)dB4%QxvWUzv*0$40M3t=GG^~J>?gJ1?|gUhH4Q1BXBp{Z1g$y3s+ z&&tot(}>OiTa^Q{s<>FMxF9F9L<1zR2`WAE@=FwQV1CmpE&-K^plVh_$x2BR$x&eY zi3vTVVB!Y_lL9mXi;KA?fACP2gcL?fNr@>CPb%ps6c_6#q-st!=&_!h)$^zxqzj{} z2iJ+<;6>E#pn3q*Bn-(&Re)CcrNyZ!dI}*KnZ*j3#R{ONpbnT+$Vg1iu2e|OOHnA! z$S=)FQAo@wPpm9fNJ<4)CwdAl`FZLk3Pq_o9F+?Bc?y{&ij%#26(=8W;+F+Ajv!Tw zx+cU3Q2LIC2u*$%AtekdP(gJNviQYrX-4JAGKNx<|Fv_%YNX0yz4*!9{qh_jPl9w! z{@5-yIj2_=QbSMdUCNeOtdN&qve}|fno$gtsKI7}q6FS>n!F)cbn@Nle1Z^9>nNxv z=a+$-CX)j~MJ8WZz$OBb(SwOA*eX=lPW~7zk^*adfrAp{2oQ#pVfkeWwhAzLJ!fbu z4JpMVD}^K#BuTIt#i=Es1saoY_G?Z~xXV&slA2iLl3$*Oqzt={5V;g&2e@MaYK-P& zCP6z73IT~F87OWBD=N;*EGS4Vf+>P>Vd}ss2~v<I=a=b$N;V{yfNV-hECDq|z-|XA zNlz__2Z^C612wmG6ykLh;^C1D4PdNYS(t8hkQE9F;OqyAYfugZnXZ?cnpj*~l$rt_ z?kU!o92hE`1Xf_HkYB6^^0^+Yud4wPN0bdv6BUZ`^Gg(L!J(o7avi3M<c!Rm6qpjQ zA(KBAi->|XX67k?rS%FDi&FDSiYGTXi%qUwz|8^Dqobf+G<o6z$;lH+IJiOWqcpvu z)Wj5x$se7?L~}CpQbA(Sas<o;%Lt|?79}azDyX|V201ypyQ*_dZgdva1uHNDg&HKL z^paEait~%&lk-zjHNcWO3fZZt1>pW2$cD)~4{(ctB{V@zJy2}|<{4>nO*SkMQ^_w< z0GDLoa7@n60~JZd@ky2OAXRz!;NSv<C&=|OnQ00j;bNocoLE>H2Qn4p*2w|qq$W=& zVbO!7NN@&<&rH$K1m!<aT!E7Xw7AiOW~IrEal(@WE(?HinHn?^K&4Wn!5R?e#DcRO z*W|!+pul8NEK1E$K*VrSY7Qu-HNY{U1CCK}08A+1&<3~2z^0*Dk7)<YbXHK6V*ukx zfNGa4kSB@~^U_l_aQ4M7+jHUXCfbTiWwlu}aDlt5~rfs_@ikYA*r1nxhgih~2o zU^5SEQc-GRHp~)5<UFVc&3v%Dn4X-UTMSdlpH`ZuU<-?ktXP=1R&suBE+_>;vKCkm zxNgz|S6wi7S>?oP!oo%%GYwQq!{R788SJ(RB^*4-$<alrIgmhyS)c|Lj7}@f1Id5_ z0#x@w%!k<yHyjkJlmAWSn%po+SP@nfs%t`9W9V@)*?&UfWPS^=$;T#yOpci7I=Nwz z3{!E+<b4wrV5RtjiRz4#`7NX-ubas|`Tr!&$)=MhPd+}`X7Zy5xygU0a)Y!?o;*cf z0@@@4<!?y5gOia_^yCS%q}f0&udbb}JJoIS(W%ap8zu=&mYya)Id@tPYqE|)E~wcq zKl$IZ(#<QUKV)K5o*ZDTJh^LD=j2rZT$9(&<gbs0m53nY!8kj$5@IN*{&ZBxOD$JO z$xJTMQwUF0$W6>EP0Y!uRLIN&k9uU5C?usOCzckcDwL-xWF(fQDwO0afVd?Yi6sgp z8L42spb9s)v?MVpCshw_AJ`X@C1#6FHk!SqUdcPPQlUCCr4}4}Y5ApjDWK#BR%ES^ zR+N~VTAp8&4esa_E97Pt7iZ?B>nQ}}q$U=pDioFGDWs$p<m6XsXo5y-z#dTmc>vS^ z$S+DsEmFu&1G%R-RUtVs2Q=~o8+=O4E74Q%^90K(K*nSgpkps-potwFkk_CsgW{6R zoE(K>(8v?WFoop&qN3DfP}>01NQXOEZ?fW?)X8sVn@yhE&N;b%&dJS@bN@5)LaJ|Y zqrysIa>D#mlfxINal%`olM5GEf{LBVs}^`q{@|fL8QQ54QF2O5Q2>X8l8!<$xWML` z{Ln>gvb+a3co?SMk$p1XLUnMZmXeyL5TBNsn&MhilwVXFk`L)XXn>NA4rp|&G^a#I zAr&l%G{B;yq@)m9oT^X)Y9E#4gPKC+kX9I|$pdQeD}X|yv?x_iK}kso9*dc2;0Vhs z&de(gDoM;sPSq#^_gHikkh(8O7J;HBwFuNWP6gL~sX7W}pn!w82-NN@Q&5C7)nmE1 zCMT8%ho=^S$2L$~0w8n1O<|Bxh$b#b7=*J^D-~=Nz`oW42OYThhO~#kLrm(Lph|DD z&wTF5+1)ae6GB-)VbA33Jo#g{!sOr1JUpOsG83E%ot;4?iFjt3LS_o6k^qGz!c=g% zX|S1xD>+9;Ay>gxAu|P5#?(Ux7}J#O6ddy)p{|ghoD8br70NR*b5a$G^K;5F^U|#p zs*`hS^{R7gL2cAjNIKVtBuDUQrb1q7Y6>WKrKV(-C?po8S}B1CJ>Bw)z)2AtMc@pS znWs<<N+h)kmHhdopqK<TYC+npCVy}i2UUMUAisgsfI{BcIXW{X7UaXp6H2%QKs|nl zP&7ydtU%@vMe>V+f)>;cu!#nnA)y7Xev48e<*vbI9@%)%fDNP=SFlw;HZ?j2Vz9wx z9%)cCD%dK3T38@vu?D>Gg;bCRn|U}h)4-`wL2>fN77=jHSa4ZPSOF|pte03&kP7NL zW#*NDD}#W`0?{bWRZvjSRjAGZ59NZA2Qmhy#?+!{1I$#Ek`D@v)FQojQ1JSK2W(yP zlR+tmIVC?C)W8S3WkLxHxT}Za3oP24#n_=9LDI@02KF^59fKIL3fd4~foz#vP{P6m z5-kR20Z?m90O~O%I|UC=KNlRSu!II`^MLxpdYO6PPAsBAgBYTtpk$|@prE9QT&a{6 zfP0_VN)1?EhU8ADrj*n)(0Ex<31~1Ns5DQbIJG_}4JnVqMzE9;i&N5yQxek@3=Ne~ z$0SmViVzLcVyFwieN3342x$!CU`0I0A@QK%JY;hIWC34DZo%gcC4`|cJ0bd0i%TG} zs{u8HYqIe|<;kH7Uv9QtB*wHkaw!KhtFnTHq50&*6-AR)8Zeh+=5F4<Qbus{-(41y zAMO$Z^|e7AR!}!NHN{Fn2{i6k5)bP0=A~;ZfFc57x$<Pa-K!_R+&z2pggq^r%l39N zPHx>lMG2N7kxEU_sJSh)`>L1cGg;!m@5%cPeiQ<Y6jkSG8`i>CKukV#$c`P_<eYTa zm$wiq4PL9TIp**|MtS5hSI`hESPE&qgZ|NAAsu+JuAl%OmbIO{^r(R@c-RxkA<$$1 zi<UGc=ltA)oYa!k6wty6{i8wbdeGGujYoHGwmBBe6azLGl=Kse6H7{pG;%>5XK)_} zG^+saIfI59GEk&JZD5eJ6{sTj1Wy)b=A|nnDimje>;spPRtmY33+IY#UVS2%SqfBp zfQ*4oa6)I4KqIS@&M9pcIdhm@73OFq1!!7;3)m`Y<-ua;+huu1<;fyfl6gVV0v(r7 z(%f8rC4q@KUPoc_Mr8>X9R*Z-;30&o=LZf1kby9)3^r);`)f<_P0dbTct(7(*>$1G zQ?5^DRM@O}V-uq`yblQL6oMPYkf|-u+@_8~3215-l+7o9y1AVTF(*5D#Vs99xThxv zs;F;fx^2kJ6ZOo(z`(@P)Xc)bV6xLa4Hh#C1H;Kx_xvO*k_}AEP0TFKQp`*(O-#&< z(~`^$6O%0_SKQZ=FtaeQ01Fu!nHw6wfVuJHDG$^c)h6$GV9Q#R1)6~td1%ELKiU7G zy<ZJz@)R_w30_4~1e*9QS_%>aO-L5415I7>L(;Ngd_iI*XyOw*%L$&rEdtH=7PW(v zH-d;olkYrK;|9%7voM3!t?*11ex#=#09w!jnM4JXOdKp6W*m$hTrA(%xIvSjT(%sX LETDxAOdyp2L8pMq delta 17866 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 JpM?pe5&-@WOrZb( diff --git a/examples/example_docker/students/cs103/report3.py b/examples/example_docker/students/cs103/report3.py index c97b5a4..f83bb53 100644 --- a/examples/example_docker/students/cs103/report3.py +++ b/examples/example_docker/students/cs103/report3.py @@ -1,8 +1,8 @@ """ Example student code. This file is automatically generated from the files in the instructor-directory """ -from unitgrade2.unitgrade2 import UTestCase, Report, hide -from unitgrade2.unitgrade_helpers2 import evaluate_report_student +from src.unitgrade2.unitgrade2 import UTestCase, Report +from src.unitgrade2 import evaluate_report_student class Week1(UTestCase): """ The first question for week 1. """ @@ -24,4 +24,6 @@ class Report3(Report): pack_imports = [cs103] if __name__ == "__main__": + # from unitgrade_private2.hidden_gather_upload import gather_upload_to_campusnet + # gather_upload_to_campusnet(Report3()) evaluate_report_student(Report3()) diff --git a/examples/example_docker/students/cs103/report3_grade.py b/examples/example_docker/students/cs103/report3_grade.py index ecff9f7..3c64c04 100644 --- a/examples/example_docker/students/cs103/report3_grade.py +++ b/examples/example_docker/students/cs103/report3_grade.py @@ -6,15 +6,10 @@ from tabulate import tabulate from datetime import datetime import pyfiglet import unittest -# from unitgrade2.unitgrade2 import MySuite - import inspect import os import argparse -import sys import time -import threading # don't import Thread bc. of minify issue. -import tqdm # don't do from tqdm import tqdm because of minify-issue parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: To run all tests in a report: @@ -115,24 +110,20 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) else: b = "Unitgrade" - print(b + " v" + __version__) dt_string = now.strftime("%d/%m/%Y %H:%M:%S") - print("Started: " + dt_string) + print(b + " v" + __version__ + ", started: " + dt_string+ "\n") + # print("Started: " + dt_string) s = report.title if hasattr(report, "version") and report.version is not None: s += " version " + report.version - print("Evaluating " + s, "(use --help for options)" if show_help_flag else "") + print(s, "(use --help for options)" if show_help_flag else "") # print(f"Loaded answers from: ", report.computed_answers_file, "\n") table_data = [] - nL = 80 t_start = time.time() score = {} loader = SequentialTestLoader() for n, (q, w) in enumerate(report.questions): - # q = q() - # q_hidden = False - # q_hidden = issubclass(q.__class__, Hidden) if question is not None and n+1 != question: continue suite = loader.loadTestsFromTestCase(q) @@ -142,11 +133,10 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa q.possible = 0 q.obtained = 0 q_ = {} # Gather score in this class. - # unittest.Te - # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_] UTextResult.q_title_print = q_title_print # Hacky UTextResult.show_progress_bar = show_progress_bar # Hacky. UTextResult.number = n + UTextResult.nL = report.nL res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) @@ -155,20 +145,16 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun - # possible = int(ws @ possible) - # obtained = int(ws @ obtained) - # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0 - obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} q.obtained = obtained q.possible = possible - s1 = f"*** Question q{n+1}" + s1 = f" * q{n+1}) Total" s2 = f" {q.obtained}/{w}" - print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 ) + print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 ) print(" ") - table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"]) + table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"]) ws, possible, obtained = upack(score) possible = int( msum(possible) ) @@ -183,15 +169,16 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa seconds = dt - minutes*60 plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") - print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")") + dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", + last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) + + # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total") table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) results = {'total': (obtained, possible), 'details': score} return results, table_data - - from tabulate import tabulate from datetime import datetime import inspect @@ -214,7 +201,8 @@ def gather_imports(imp): # dn = os.path.dirname(f) # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) # top_package = str(__import__(m.__name__.split('.')[0]).__path__) - if m.__class__.__name__ == 'module' and False: + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: top_package = os.path.dirname(m.__file__) module_import = True else: @@ -235,7 +223,7 @@ def gather_imports(imp): for file in files: if file.endswith(".py"): fpath = os.path.join(root, file) - v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package)) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) zip.write(fpath, v) resources['zipfile'] = zip_buffer.getvalue() @@ -279,14 +267,14 @@ def gather_upload_to_campusnet(report, output_dir=None): results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, show_progress_bar=not args.noprogress, big_header=not args.autolab) - print(" ") - print("="*n) - print("Final evaluation") - print(tabulate(table_data)) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) # also load the source code of missing files... sources = {} - + print("") if not args.autolab: if len(report.individual_imports) > 0: print("By uploading the .token file, you verify the files:") @@ -299,12 +287,15 @@ def gather_upload_to_campusnet(report, output_dir=None): print("Including files in upload...") for k, m in enumerate(report.pack_imports): nimp, top_package = gather_imports(m) - report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import nimp['name'] = m.__name__ sources[k] = nimp # if len([k for k in nimp if k not in sources]) > 0: - print(f"*** {m.__name__}") + print(f" * {m.__name__}") # sources = {**sources, **nimp} results['sources'] = sources @@ -317,15 +308,17 @@ def gather_upload_to_campusnet(report, output_dir=None): vstring = "_v"+report.version if report.version is not None else "" token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) - token = os.path.join(output_dir, token) + token = os.path.normpath(os.path.join(output_dir, token)) + + with open(token, 'wb') as f: pickle.dump(results, f) if not args.autolab: print(" ") - print("To get credit for your results, please upload the single file: ") + print("To get credit for your results, please upload the single unmodified file: ") print(">", token) - print("To campusnet without any modifications.") + # print("To campusnet without any modifications.") # print("Now time for some autolab fun") @@ -338,8 +331,8 @@ def source_instantiate(name, report1_source, payload): -report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n\n"""\n# from . import cache_read\nimport unittest\nimport numpy as np\nimport sys\nfrom io import StringIO\nimport collections\nimport re\nimport threading\nimport tqdm\nimport time\nimport pickle\nimport itertools\nimport os\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\ndef setup_dir_by_class(C,base_dir):\n name = C.__class__.__name__\n # base_dir = os.path.join(base_dir, name)\n # if not os.path.isdir(base_dir):\n # os.makedirs(base_dir)\n return base_dir, name\n\nclass Hidden:\n def hide(self):\n return True\n\nclass Logger(object):\n def __init__(self, buffer):\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\nclass QItem(unittest.TestCase):\n title = None\n testfun = None\n tol = 0\n estimated_time = 0.42\n _precomputed_payload = None\n _computed_answer = None # Internal helper to later get results.\n weight = 1 # the weight of the question.\n\n def __init__(self, question=None, *args, **kwargs):\n if self.tol > 0 and self.testfun is None:\n self.testfun = self.assertL2Relative\n elif self.testfun is None:\n self.testfun = self.assertEqual\n\n self.name = self.__class__.__name__\n # self._correct_answer_payload = correct_answer_payload\n self.question = question\n\n super().__init__(*args, **kwargs)\n if self.title is None:\n self.title = self.name\n\n def _safe_get_title(self):\n if self._precomputed_title is not None:\n return self._precomputed_title\n return self.title\n\n def assertNorm(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed).flat- np.asarray(expected)).flat )\n nrm = np.sqrt(np.sum( diff ** 2))\n\n self.error_computed = nrm\n\n if nrm > tol:\n print(f"Not equal within tolerance {tol}; norm of difference was {nrm}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def assertL2(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n self.error_computed = np.max(diff)\n\n if np.max(diff) > tol:\n print(f"Not equal within tolerance {tol=}; deviation was {np.max(diff)=}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol=}, {np.max(diff)=}")\n\n def assertL2Relative(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n diff = diff / (1e-8 + np.abs( (np.asarray(computed) + np.asarray(expected)) ) )\n self.error_computed = np.max(np.abs(diff))\n if np.sum(diff > tol) > 0:\n print(f"Not equal within tolerance {tol}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def precomputed_payload(self):\n return self._precomputed_payload\n\n def precompute_payload(self):\n # Pre-compute resources to include in tests (useful for getting around rng).\n pass\n\n def compute_answer(self, unmute=False):\n raise NotImplementedError("test code here")\n\n def test(self, computed, expected):\n self.testfun(computed, expected)\n\n def get_points(self, verbose=False, show_expected=False, show_computed=False,unmute=False, passall=False, silent=False, **kwargs):\n possible = 1\n computed = None\n def show_computed_(computed):\n print(">>> Your output:")\n print(computed)\n\n def show_expected_(expected):\n print(">>> Expected output (note: may have been processed; read text script):")\n print(expected)\n\n correct = self._correct_answer_payload\n try:\n if unmute: # Required to not mix together print stuff.\n print("")\n computed = self.compute_answer(unmute=unmute)\n except Exception as e:\n if not passall:\n if not silent:\n print("\\n=================================================================================")\n print(f"When trying to run test class \'{self.name}\' your code threw an error:", e)\n show_expected_(correct)\n import traceback\n print(traceback.format_exc())\n print("=================================================================================")\n return (0, possible)\n\n if self._computed_answer is None:\n self._computed_answer = computed\n\n if show_expected or show_computed:\n print("\\n")\n if show_expected:\n show_expected_(correct)\n if show_computed:\n show_computed_(computed)\n try:\n if not passall:\n self.test(computed=computed, expected=correct)\n except Exception as e:\n if not silent:\n print("\\n=================================================================================")\n print(f"Test output from test class \'{self.name}\' does not match expected result. Test error:")\n print(e)\n show_computed_(computed)\n show_expected_(correct)\n return (0, possible)\n return (1, possible)\n\n def score(self):\n try:\n self.test()\n except Exception as e:\n return 0\n return 1\n\nclass QPrintItem(QItem):\n def compute_answer_print(self):\n """\n Generate output which is to be tested. By default, both text written to the terminal using print(...) as well as return values\n are send to process_output (see compute_answer below). In other words, the text generated is:\n\n res = compute_Answer_print()\n txt = (any terminal output generated above)\n numbers = (any numbers found in terminal-output txt)\n\n self.test(process_output(res, txt, numbers), <expected result>)\n\n :return: Optional values for comparison\n """\n raise Exception("Generate output here. The output is passed to self.process_output")\n\n def process_output(self, res, txt, numbers):\n return res\n\n def compute_answer(self, unmute=False):\n with Capturing(unmute=unmute) as output:\n res = self.compute_answer_print()\n s = "\\n".join(output)\n s = rm_progress_bar(s) # Remove progress bar.\n numbers = extract_numbers(s)\n self._computed_answer = (res, s, numbers)\n return self.process_output(res, s, numbers)\n\nclass OrderedClassMembers(type):\n @classmethod\n def __prepare__(self, name, bases):\n return collections.OrderedDict()\n def __new__(self, name, bases, classdict):\n ks = list(classdict.keys())\n for b in bases:\n ks += b.__ordered__\n classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n return type.__new__(self, name, bases, classdict)\n\nclass QuestionGroup(metaclass=OrderedClassMembers):\n title = "Untitled question"\n partially_scored = False\n t_init = 0 # Time spend on initialization (placeholder; set this externally).\n estimated_time = 0.42\n has_called_init_ = False\n _name = None\n _items = None\n\n @property\n def items(self):\n if self._items == None:\n self._items = []\n members = [gt for gt in [getattr(self, gt) for gt in self.__ordered__ if gt not in ["__classcell__", "__init__"]] if inspect.isclass(gt) and issubclass(gt, QItem)]\n for I in members:\n self._items.append( I(question=self))\n return self._items\n\n @items.setter\n def items(self, value):\n self._items = value\n\n @property\n def name(self):\n if self._name == None:\n self._name = self.__class__.__name__\n return self._name #\n\n @name.setter\n def name(self, val):\n self._name = val\n\n def init(self):\n # Can be used to set resources relevant for this question instance.\n pass\n\n def init_all_item_questions(self):\n for item in self.items:\n if not item.question.has_called_init_:\n item.question.init()\n item.question.has_called_init_ = True\n\n\nclass Report():\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 80 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q,_) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n root_dir = self.pack_imports[0].__path__._path[0]\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q,_) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n # else:\n # if os.path.isfile(self.computed_answers_file):\n # self.set_payload(cache_read(self.computed_answers_file), strict=strict)\n # else:\n # s = f"> Warning: The pre-computed answer file, {os.path.abspath(self.computed_answers_file)} is missing. The framework will NOT work as intended. Reasons may be a broken local installation."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n import unittest\n loader = unittest.TestLoader()\n for q,_ in self.questions:\n import time\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self):\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\':True}\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct+1)\n if i > 0 and l.find("|", i+1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = \'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar",show_progress_bar=True):\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.1\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n\n # self.pbar = tqdm.tqdm(total=self.n)\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if hasattr(self, \'pbar\') and self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar=None\n\n sys.stdout.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=sys.stdout, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\') # , unit_scale=dt, unit=\'seconds\'):\n\n for _ in range(self.n-1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\n\n\nfrom unittest.suite import _isnotsuite\n\n# class MySuite(unittest.suite.TestSuite): # Not sure we need this one anymore.\n# raise Exception("no suite")\n# pass\n\ndef instance_call_stack(instance):\n s = "-".join(map(lambda x: x.__name__, instance.__class__.mro()))\n return s\n\ndef get_class_that_defined_method(meth):\n for cls in inspect.getmro(meth.im_class):\n if meth.__name__ in cls.__dict__:\n return cls\n return None\n\ndef caller_name(skip=2):\n """Get a name of a caller in the format module.class.method\n\n `skip` specifies how many levels of stack to skip while getting caller\n name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.\n\n An empty string is returned if skipped levels exceed stack height\n """\n stack = inspect.stack()\n start = 0 + skip\n if len(stack) < start + 1:\n return \'\'\n parentframe = stack[start][0]\n\n name = []\n module = inspect.getmodule(parentframe)\n # `modname` can be None when frame is executed directly in console\n # TODO(techtonik): consider using __main__\n if module:\n name.append(module.__name__)\n # detect classname\n if \'self\' in parentframe.f_locals:\n # I don\'t know any way to detect call from the object method\n # XXX: there seems to be no way to detect static method call - it will\n # be just a function call\n name.append(parentframe.f_locals[\'self\'].__class__.__name__)\n codename = parentframe.f_code.co_name\n if codename != \'<module>\': # top level usually\n name.append( codename ) # function or a method\n\n ## Avoid circular refs and frame leaks\n # https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack\n del parentframe, stack\n\n return ".".join(name)\n\ndef get_class_from_frame(fr):\n import inspect\n args, _, _, value_dict = inspect.getargvalues(fr)\n # we check the first parameter for the frame function is\n # named \'self\'\n if len(args) and args[0] == \'self\':\n # in that case, \'self\' will be referenced in value_dict\n instance = value_dict.get(\'self\', None)\n if instance:\n # return its class\n # isinstance(instance, Testing) # is the actual class instance.\n\n return getattr(instance, \'__class__\', None)\n # return None otherwise\n return None\n\nfrom typing import Any\nimport inspect, gc\n\ndef giveupthefunc():\n frame = inspect.currentframe()\n code = frame.f_code\n globs = frame.f_globals\n functype = type(lambda: 0)\n funcs = []\n for func in gc.get_referrers(code):\n if type(func) is functype:\n if getattr(func, "__code__", None) is code:\n if getattr(func, "__globals__", None) is globs:\n funcs.append(func)\n if len(funcs) > 1:\n return None\n return funcs[0] if funcs else None\n\n\nfrom collections import defaultdict\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n # if self.dots or self.showAll:\n # self.stream.writeln()\n # if hasattr(self, \'cc\'):\n # self.cc.terminate()\n # self.cc_terminate(success=False)\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n # if self.showAll:\n # self.stream.writeln("FAIL")\n # elif self.dots:\n # self.stream.write(\'F\')\n # self.stream.flush()\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n # super().addSuccess(test)\n self.successes.append(test)\n # super().addSuccess(test)\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n self.cc_terminate()\n\n\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n sys.stdout.flush()\n ss = self.item_title_print\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(ss))), end="")\n # current = 1\n # possible = 1\n # current == possible\n ss = "PASS" if success else "FAILED"\n if tsecs >= 0.1:\n ss += " (" + str(tsecs) + " seconds)"\n print(ss)\n\n\n def startTest(self, test):\n # super().startTest(test)\n j =self.testsRun\n self.testsRun += 1\n # print("Starting the test...")\n # show_progress_bar = True\n n = UTextResult.number\n\n item_title = self.getDescription(test)\n # item_title = item_title.split("\\n")[0]\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n # test.countTestCases()\n self.item_title_print = "*** q%i.%i) %s" % (n + 1, j + 1, item_title)\n estimated_time = 10\n nL = 80\n #\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar)\n else:\n print(self.item_title_print + (\'.\' * max(0, nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n # q_title_print = "some printed title..."\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass == None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n print(self.cc.title, end="")\n # start = 10\n # q_time = np.round(time.time() - start, 2)\n nL = 80\n print(" " * max(0, nL - len(self.cc.title)) + (\n " (" + str(q_time) + " seconds)" if q_time >= 0.1 else "")) # if q.name in report.payloads else "")\n # print("=" * nL)\n\nfrom unittest.runner import _WritelnDecorator\nfrom io import StringIO\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n from io import StringIO\n stream = StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\ndef wrapper(foo):\n def magic(self):\n s = "-".join(map(lambda x: x.__name__, self.__class__.mro()))\n # print(s)\n foo(self)\n magic.__doc__ = foo.__doc__\n return magic\n\nfrom functools import update_wrapper, _make_key, RLock\nfrom collections import namedtuple\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)) )\n # key = (self.cache_id(), \'@cache\')\n # if self._cache_contains[key]\n\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n return wrapper\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n\n def capture(self):\n return Capturing2(stdout=self._stdout)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ != None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n self._stdout = sys.stdout\n import io\n sys.stdout = io.StringIO()\n super().setUp()\n # print("Setting up...")\n\n def _callTearDown(self):\n sys.stdout = self._stdout\n super().tearDown()\n # print("asdfsfd")\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd == None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n # self._testMethodDoc.strip().splitlines()[0].strip()\n sd = self.shortDescriptionStandard()\n title = self._cache_get( (self.cache_id(), \'title\'), sd )\n return title if title != None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome == None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc != None:\n # Ensure the cache is eventually updated with the right docstring.\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard() )\n # Fix temp cache here (for using the @cache decorator)\n self._cache2[ (self.cache_id(), \'assert\') ] = {}\n\n res = testMethod()\n elapsed = time.time() - t\n # self._cache_put( (self.cache_id(), \'title\'), self.shortDescription() )\n\n self._get_outcome()[self.cache_id()] = res\n self._cache_put( (self.cache_id(), "time"), elapsed)\n\n # This is my base test class. So what is new about it?\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return (c,m)\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n # self.cache_indexes = defaultdict(lambda: 0)\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n cache = self._cache_get(key, {})\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, first)\n assert_fun(first, _expected, *args, **kwargs)\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__) ) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache != None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n # print("Loading cache from", cfile)\n if os.path.exists(cfile):\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n else:\n print("Warning! data file not found", cfile)\n\ndef hide(func):\n return func\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n # (*)We can be somewhat "hygienic", but newDecorator still isn\'t signature-preserving, i.e. you will not be able to get a runtime list of parameters. For that, you need hackish libraries...but in this case, the only argument is func, so it\'s not a big issue\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\n# from unitgrade2.unitgrade2 import MySuite\n\nimport inspect\nimport os\nimport argparse\nimport sys\nimport time\nimport threading # don\'t import Thread bc. of minify issue.\nimport tqdm # don\'t do from tqdm import tqdm because of minify-issue\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n print(b + " v" + __version__)\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print("Evaluating " + s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n nL = 80\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n # q = q()\n # q_hidden = False\n # q_hidden = issubclass(q.__class__, Hidden)\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n # unittest.Te\n # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_]\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n # possible = int(ws @ possible)\n # obtained = int(ws @ obtained)\n # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"*** Question q{n+1}"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n if m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package))\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n print(" ")\n print("="*n)\n print("Final evaluation")\n print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f"*** {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single file: ")\n print(">", token)\n print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n def test_add(self):\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n self.assertEqualC(add(-100, 5))\n\n\nclass AutomaticPass(UTestCase):\n def test_student_passed(self):\n self.assertEqual(2,2)\n\n\nimport cs103\nclass Report3(Report):\n title = "CS 101 Report 3"\n questions = [(Week1, 20), (AutomaticPass, 10)] # Include a single question for 10 credits.\n pack_imports = [cs103]' -report1_payload = '80049525010000000000007d94288c055765656b31947d94288c055765656b31948c08746573745f6164649486948c066173736572749486947d94284b014aa1ffffff4b004b04756803680486948c0474696d65948694473f506a000000000068038c0f746573745f6164645f68696464656e948694680686947d944b004b04736803680c8694680a86944700000000000000008c0474696d6594473f926de000000000758c0d4175746f6d6174696350617373947d94288c0d4175746f6d6174696350617373948c10746573745f68696464656e5f6661696c9486948c066173736572749486947d9468158c13746573745f73747564656e745f706173736564948694681886947d946815681b86948c0474696d659486944700000000000000006812473f9894100000000075752e' +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n"""\nimport numpy as np\nimport sys\nimport re\nimport threading\nimport tqdm\nimport pickle\nimport os\nfrom io import StringIO\nimport io\nfrom unittest.runner import _WritelnDecorator\nfrom typing import Any\nimport inspect\nimport textwrap\nimport colorama\nfrom colorama import Fore\nfrom functools import _make_key, RLock\nfrom collections import namedtuple\nimport unittest\nimport time\n\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ncolorama.init(autoreset=True) # auto resets your settings after every output\n\ndef gprint(s):\n print(f"{Fore.GREEN}{s}")\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\n\ndef setup_dir_by_class(C, base_dir):\n name = C.__class__.__name__\n return base_dir, name\n\n\nclass Logger(object):\n def __init__(self, buffer):\n assert False\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\n# @classmethod\n# class OrderedClassMembers(type):\n# def __prepare__(self, name, bases):\n# assert False\n# return collections.OrderedDict()\n#\n# def __new__(self, name, bases, classdict):\n# ks = list(classdict.keys())\n# for b in bases:\n# ks += b.__ordered__\n# classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n# return type.__new__(self, name, bases, classdict)\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 120 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q, _) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q, _) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n loader = unittest.TestLoader()\n for q, _ in self.questions:\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self, with_coverage=False):\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = True\n q._report = self\n\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n # print(self.questions)\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n print("q is", q())\n q()._cache_put(\'time\', q.time) # = q.time\n report_cache[q.__qualname__] = q._cache2\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\': True}\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = False\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct + 1)\n if i > 0 and l.find("|", i + 1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = r\'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar", show_progress_bar=True, file=None):\n if file == None:\n file = sys.stdout\n self.file = file\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.01\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n self.pbar = None\n\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar = None\n\n self.file.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=self.file, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\')\n\n for _ in range(self.n - 1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\ndef dprint(first, last, nL, extra = "", file=None, dotsym=\'.\', color=\'white\'):\n if file == None:\n file = sys.stdout\n\n # ss = self.item_title_print\n # state = "PASS" if success else "FAILED"\n dot_parts = (dotsym * max(0, nL - len(last) - len(first)))\n # if self.show_progress_bar or True:\n print(first + dot_parts, end="", file=file)\n # else:\n # print(dot_parts, end="", file=self.cc.file)\n last += extra\n # if tsecs >= 0.5:\n # state += " (" + str(tsecs) + " seconds)"\n print(last, file=file)\n\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n cc = None\n\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n self.successes.append(test)\n self.cc_terminate()\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n self.cc.file.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="", file=self.cc.file)\n else:\n print(dot_parts, end="", file=self.cc.file)\n\n if tsecs >= 0.5:\n state += " (" + str(tsecs) + " seconds)"\n print(state, file=self.cc.file)\n\n def startTest(self, test):\n # j =self.testsRun\n self.testsRun += 1\n # item_title = self.getDescription(test)\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n self.item_title_print = " * q%i.%i) %s" % (UTextResult.number + 1, self.testsRun, item_title)\n estimated_time = 10\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar, file=sys.stdout)\n else:\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n self._stdout = sys.stdout\n sys.stdout = io.StringIO()\n\n def stopTest(self, test):\n sys.stdout = self._stdout\n super().stopTest(test)\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass is None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n print(" " * max(0, self.nL - len(self.cc.title)) + (" (" + str(q_time) + " seconds)" if q_time >= 0.5 else ""))\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n stream = io.StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)))\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n\n return wrapper\n\n\ndef get_hints(ss):\n if ss == None:\n return None\n try:\n ss = textwrap.dedent(ss)\n ss = ss.replace(\'\'\'"""\'\'\', "").strip()\n hints = ["hints:", ]\n j = np.argmax([ss.lower().find(h) for h in hints])\n h = hints[j]\n ss = ss[ss.find(h) + len(h) + 1:]\n ss = "\\n".join([l for l in ss.split("\\n") if not l.strip().startswith(":")])\n ss = textwrap.dedent(ss)\n ss = ss.strip()\n return ss\n except Exception as e:\n print("bad hints", ss, e)\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n _with_coverage = False\n _report = None # The report used. This is very, very hacky and should always be None. Don\'t rely on it!\n\n def capture(self):\n if hasattr(self, \'_stdout\') and self._stdout is not None:\n file = self._stdout\n else:\n # self._stdout = sys.stdout\n # sys._stdout = io.StringIO()\n file = sys.stdout\n return Capturing2(stdout=file)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ is not None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n if self._with_coverage:\n if not hasattr(self._report, \'covcache\'):\n self._report.covcache = {}\n import coverage\n self.cov = coverage.Coverage()\n self.cov.start()\n self.setUp()\n\n def _callTearDown(self):\n self.tearDown()\n if self._with_coverage:\n from pathlib import Path\n from snipper import snipper\n self.cov.stop()\n data = self.cov.get_data()\n base, _, _ = self._report._import_base_relative()\n for file in data.measured_files():\n file = os.path.normpath(file)\n root = Path(base)\n child = Path(file)\n if root in child.parents:\n with open(child, \'r\') as f:\n s = f.read()\n lines = s.splitlines()\n garb = \'GARBAGE\'\n\n lines2 = snipper.censor_code(lines, keep=True)\n assert len(lines) == len(lines2)\n\n for l in data.contexts_by_lineno(file):\n if lines2[l].strip() == garb:\n if self.cache_id() not in self._report.covcache:\n self._report.covcache[self.cache_id()] = {}\n\n rel = os.path.relpath(child, root)\n cc = self._report.covcache[self.cache_id()]\n j = 0\n for j in range(l, -1, -1):\n if "def" in lines2[j] or "class" in lines2[j]:\n break\n from snipper.snipper import gcoms\n fun = lines2[j]\n comments, _ = gcoms("\\n".join(lines2[j:l]))\n if rel not in cc:\n cc[rel] = {}\n cc[rel][fun] = (l, "\\n".join(comments))\n self._cache_put((self.cache_id(), \'coverage\'), self._report.covcache)\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd is None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n sd = self.shortDescriptionStandard()\n title = self._cache_get((self.cache_id(), \'title\'), sd)\n return title if title is not None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome is None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc is not None:\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard())\n\n self._cache2[(self.cache_id(), \'assert\')] = {}\n res = testMethod()\n elapsed = time.time() - t\n self._get_outcome()[self.cache_id()] = res\n self._cache_put((self.cache_id(), "time"), elapsed)\n\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return c, m\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n # sys.stdout = self._stdout\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n self.__class__._cache[\n key] = {} # A new dict. We manually insert it because we have to use that the dict is mutable.\n cache = self._cache_get(key)\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, f"Key {id} not found in cache; framework files missing. Please run deploy()")\n\n # The order of these calls is important. If the method assert fails, we should still store the correct result in cache.\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n assert_fun(first, _expected, *args, **kwargs)\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache is not None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n if os.path.exists(cfile):\n try:\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n except Exception as e:\n print("Bad cache", cfile)\n print(e)\n else:\n print("Warning! data file not found", cfile)\n\n def _feedErrorsToResult(self, result, errors):\n """ Use this to show hints on test failure. """\n if not isinstance(result, UTextResult):\n er = [e for e, v in errors if v != None]\n\n if len(er) > 0:\n hints = []\n key = (self.cache_id(), \'coverage\')\n if self._cache_contains(key):\n CC = self._cache_get(key)\n for id in CC:\n if id == self.cache_id():\n cl, m = id\n gprint(f"> An error occured while solving: {cl}.{m}. The files/methods you need to edit are:") # For the test {id} in {file} you should edit:")\n for file in CC[id]:\n rec = CC[id][file]\n gprint(f"> * {file}")\n for l in rec:\n _, comments = CC[id][file][l]\n hint = get_hints(comments)\n\n if hint != None:\n hints.append(hint)\n gprint(f"> - {l}")\n\n er = er[0]\n doc = er._testMethodDoc\n if doc is not None:\n hint = get_hints(er._testMethodDoc)\n if hint is not None:\n hints = [hint] + hints\n if len(hints) > 0:\n gprint("> Hints:")\n gprint(textwrap.indent("\\n".join(hints), "> "))\n\n super()._feedErrorsToResult(result, errors)\n\n def startTestRun(self):\n # print("asdfsdaf 11", file=sys.stderr)\n super().startTestRun()\n # print("asdfsdaf")\n\n def _callTestMethod(self, method):\n # print("asdfsdaf")\n super()._callTestMethod(method)\n\n\ndef hide(func):\n return func\n\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n# 817\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\nimport inspect\nimport os\nimport argparse\nimport time\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print(b + " v" + __version__ + ", started: " + dt_string+ "\\n")\n # print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print(s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n UTextResult.nL = report.nL\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f" * q{n+1}) Total"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")",\n last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL)\n\n # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n # print(" ")\n # print("="*n)\n # print("Final evaluation")\n # print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n print("")\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f" * {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.normpath(os.path.join(output_dir, token))\n\n\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single unmodified file: ")\n print(">", token)\n # print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n def test_add(self):\n from cs103.homework1 import add\n self.assertEqualC(add(2,2))\n self.assertEqualC(add(-100, 5))\n\n\nclass AutomaticPass(UTestCase):\n def test_student_passed(self):\n self.assertEqual(2,2)\n\n\nimport cs103\nclass Report3(Report):\n title = "CS 101 Report 3"\n questions = [(Week1, 20), (AutomaticPass, 10)] # Include a single question for 10 credits.\n pack_imports = [cs103]' +report1_payload = '80049568000000000000007d94288c055765656b31947d942868018c08746573745f6164649486948c066173736572749486947d94284b014aa1ffffff4b004b04758c0474696d6594473fb71ac800000000758c0d4175746f6d6174696350617373947d946808473fb127100000000073752e' name="Report3" report = source_instantiate(name, report1_source, report1_payload) diff --git a/examples/example_docker/students/cs103/unitgrade/AutomaticPass.pkl b/examples/example_docker/students/cs103/unitgrade/AutomaticPass.pkl index 2a722e2b9c8264b76eca73fec2c7dd84eb0e02d3..9b6ff7ac689837f86e1b0e393993ec7acbb784e8 100644 GIT binary patch literal 4 LcmZo*@zVnU0@?uq literal 93 zcmZo*nHt0Z0ku;!dUzd6OY(CQOEQxK5{rwc^az)v7MH{qmz1WY=9R=3Bo-H^rc7y@ m(!&N~6_reBn^HR^gE51tZAuSINoH>9l(s4E5YreKO7#HeD<BB~ diff --git a/examples/example_docker/students/cs103/unitgrade/Week1.pkl b/examples/example_docker/students/cs103/unitgrade/Week1.pkl index fe27b785553c86fe6975853b9990eed439d2d5bc..20eb565b4b7903e4aef2d3d44e08726a2b0e14ed 100644 GIT binary patch delta 22 ZcmWHy<85G>YRmuuwNobY=`$7U0RS=m1aSZW delta 47 ucmcBu=WAe@>cap5wNo@E^6E=vFlI2dP3d7N$;?fi(l*5%BFVr|ss{iFA`0yQ diff --git a/examples/example_flat/instructor/cs101flat/Report1Flat_handin_10_of_10.token b/examples/example_flat/instructor/cs101flat/Report1Flat_handin_10_of_10.token new file mode 100644 index 0000000000000000000000000000000000000000..6207232bba6714f3ffac3686c93f17dac4be1cb1 GIT binary patch literal 65761 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 V5_?!csd!2cCp?a(loprj0RUJp^)mnf literal 0 HcmV?d00001 diff --git a/examples/example_flat/instructor/cs101flat/__pycache__/deploy.cpython-38.pyc b/examples/example_flat/instructor/cs101flat/__pycache__/deploy.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c91ca3192aaec26f60abe00c1109cb32983e52a GIT binary patch literal 581 zcmWIL<>g{vU|=}nr<>Ts#K7<v#6iaF3=9ko3=9m#5ey6rDGVu$ISjdsQH+crHd78$ zE^`z!n9ZESlFJ&!%E*w)mco+FR1}iRoXVKO3WjXGEQ}1PtXZrn>?s^6oGDx>+$lWi z%qhGe@f5yb22K8#AUib~Z*d2u7UUO|7`o*omS{5G5-(0IDJ_UkFG@^FjZe$WNsTW8 z%WE>;;wa9`EQn9ZEV{)JAD^3;nHL}LrpX+|mYbQEnN}IanV*zaTAZ9%k{ZRHT9KMu zT9SH;p-MtePhU?@zqq6{B{i?4SU<Vg(7-S)C$S{t7I%DnL1l7caz<)=d=<NvUP*p- zYF-s*N@_t)ex+VPr6x-hS8;x6QF1EC(kPCU)Z!8_OEftrH8C$9#Lvu2EXmBz0}HdK zRwU<?rlk65vPW@4Tn)CVh>3xLAxf<@FS7*Vt%9P=vc!^9BfX5wl$6xG_~fG0#1e>a zi;I{+irGP4El4dYVqsul0C_hrF*h|nekDT@Cj$e7_!Z}D6%$&VT2vfUQksz(<C33T znhWw`42DH9sTGO21v#n3Fh)Em0Ae!pic5-0lS}f8V&I_?1NZ$c4x8Nkl+v73J4R4q R0HqloMjl2UCJsgcHURWnuU!BD literal 0 HcmV?d00001 diff --git a/examples/example_flat/instructor/cs101flat/__pycache__/homework1.cpython-38.pyc b/examples/example_flat/instructor/cs101flat/__pycache__/homework1.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ad952b7db7aa4adbd8a113e6d74f318bd77ce47 GIT binary patch literal 835 zcmWIL<>g{vU|`VW&`b1VVqka-;vi#o1_lNP1_p*=HwFfV6owSW7KSK>6s8pB7KSLs zRHhV`Y^EZoROS@cRK^sx6t-rj*$i`;ni&}xQkhfOQyEg&QaDmLQ@GMuni-p!7#UKy zgBdh=k{OY#0I``F7#N&Ew(u}8FqANqFxD_MGd42@GiWkaE#gv8P*8BsEKAK(NL0wl zEG|({&#eT}>Iy}vC8b4q#W3;Y{Ji4K;*!j~bcOshg_4X^h2q5ARE5->)ZEm(l46C- zJcXjvvecsDRE7Mal++?U1y{XvJ+S#u@pzEA8qtP2Mmol^nhM1k`K382P-_&TjdhH4 z3}Y2E5@9xKLNseK-C_Z0yu|@ECPkAmiVfn(l?+87-~CE-wu%WYPAw{qDJjiJjd96O zE(JR(rZg|JB)upxB{jaFD6=fFBsC_rA~ClhC$$*Hh)>H&EQ!g?D=sN2O)kkVib*au zG%y4UX5{Clmgg5`8|oEQ-r|AU4)z2nSc{oJ;mlgZ%D}*o3}%D)3=9k)HVA{moP&XZ zp@yM`QJeuB-bv7~E-BAf$SciFN-Zi@NJvajNX$!7NJvUhD9OkyR!A$&OD@UG&x3_f zab|8oP9-d$N-|P2ixi4WbFIK(Yp0NylA@8QlccF&tB|OjqyP>PO{QCniMJS&z#Is{ z!oa|Ai#ahR1>_`%<C%*<>{}f1@wthadGYa8;*t5KMG8f!#icnVpjb9i&{i-~uvK^o z@=ujm5IBB8DIigyI5#mT2b^MFf}-FhC>UOX611Nt3ph;RX6Gg5rpCwLVl61j%qxMo h1*8MQ0=tUCCO1E&G$+*#9IhZYaj<i+axjT70sv2E*y8{I literal 0 HcmV?d00001 diff --git a/examples/example_flat/instructor/cs101flat/__pycache__/report1flat.cpython-38.pyc b/examples/example_flat/instructor/cs101flat/__pycache__/report1flat.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b843499aeb6a5314ec477e4a8ff9439e6927c59 GIT binary patch literal 1204 zcmWIL<>g{vU|`^O*G>Gy#K7<v#6iX^3=9ko3=9m#1q=)fDGVu$ISjdsQH+crHd78$ zE^`z!n9ZESlFJ&!n#&f&2IjNmut%}GGo-Mlu(dFxu%)tRaWpeWai%b&u;j2rvA8p& zu%~deFr;v#GBz_uaiwynaAq?V<)kvFay2tFgUn|MX3*q%337`j<1MzJ)Pnq?5>3Wi z;;ChcIi-musqsZ%q4?sG(v;M^5>2LCJVmKxsYS)9@j02rCAXLpQ&RkrLHdy~6U;v{ z3=9mZ3{i|J3{gxej44bl3{lJ}%qc7_3{fm8ticSLY`0j$Q&Y1IlNpipgV?ML3=Gbo zAkko8V5nhOz)-@N!kEI?%#^~kkckl{!kog=%#^|k5=mhI8Rz#BWcN!@fM(A5|Ns9> zkU5zP|NsB5$#jc5vA8(3sKm9fG%=?LB(ak57E5tzPTER_B3=dthF{6fRxzQ)sYS&x zC8ZguF)sPZrManjCB-qNd6^~YMTse?@dZVhWr-!JF{u@axdl0?#V|&ET25j~OlDqj zNl|HXNq$jGa<QR-Ay^O+gkZK_LFFxulGNgo_{5YHc98c#E@fa8Vyxl=hl3tW5+gv^ zU;(0l93V_7%<0U{j48}1Oeu`%%<zC<@p}mh#UhX$FPRw_7&MuRKxwcDWM2_C0|Ugz zU~d=kF)%RP;sM(Xja-m|Vo*pjFjfg6ISeZ8r^$MYBR)PaF*h|n{uWn!d~SY9X%2|Z z6CV!>U5HE(Kgb>-5CIBah-F|F$i^a&k3hH><R%VA4i>No?!*N0I3h7cF{iSmFr_i2 zGe)tdvZXUdv8ONxGib8h;)bMRL${p7k}7`ZU<E@1Lj{PKg5gV0Aoyu=6bXY|B?2Nu zLB3}#$t=l91v?;$v#>O^xFj<_ulN>cMt*K;d45s0VH8h6VsdtTW-cg)6eD>B;U6&u z1_l98oPs>X!N|hM#aILq0|ixXVrE`^ye3bSC_E_{=^>a!ps<dTMF=3|;xkfn3Q~)T zjfz0gSR@H@GAO{n2{?)aqy-dnQ5+$u#U;*(#Sl+`Erxg!>_7wo3i?|dHjo&z10|AT WkfV7Rc^Cy4IhcevIT!_4IhX;a(-RH= literal 0 HcmV?d00001 diff --git a/examples/example_flat/instructor/cs101flat/deploy.py b/examples/example_flat/instructor/cs101flat/deploy.py new file mode 100644 index 0000000..be04b5a --- /dev/null +++ b/examples/example_flat/instructor/cs101flat/deploy.py @@ -0,0 +1,15 @@ +from report1flat import Report1Flat +from unitgrade_private2.hidden_create_files import setup_grade_file_report +from snipper import snip_dir + +if __name__ == "__main__": + setup_grade_file_report(Report1Flat, minify=False, obfuscate=False, execute=False) + + # from unitgrade_private2.hidden_gather_upload import gather_upload_to_campusnet + # gather_upload_to_campusnet((Report1Flat())) + + # Deploy the files using snipper: https://gitlab.compute.dtu.dk/tuhe/snipper + snip_dir.snip_dir(source_dir="", dest_dir="../../students/cs101flat", clean_destination_dir=True, exclude=['__pycache__', '*.token', 'deploy.py']) + + + diff --git a/examples/example_flat/instructor/cs101flat/homework1.py b/examples/example_flat/instructor/cs101flat/homework1.py new file mode 100644 index 0000000..286b79f --- /dev/null +++ b/examples/example_flat/instructor/cs101flat/homework1.py @@ -0,0 +1,16 @@ +def reverse_list(mylist): #!f + """ + Given a list 'mylist' returns a list consisting of the same elements in reverse order. E.g. + reverse_list([1,2,3]) should return [3,2,1] (as a list). + """ + return list(reversed(mylist)) + +def add(a,b): #!f + """ Given two numbers `a` and `b` this function should simply return their sum: + > add(a,b) = a+b """ + return a+b + +if __name__ == "__main__": + # Problem 1: Write a function which add two numbers + print(f"Your result of 2 + 2 = {add(2,2)}") + print(f"Reversing a small list", reverse_list([2,3,5,7])) diff --git a/examples/example_flat/instructor/cs101flat/report1flat.py b/examples/example_flat/instructor/cs101flat/report1flat.py new file mode 100644 index 0000000..9ede035 --- /dev/null +++ b/examples/example_flat/instructor/cs101flat/report1flat.py @@ -0,0 +1,24 @@ +from src.unitgrade2.unitgrade2 import Report +from src.unitgrade2 import evaluate_report_student +from homework1 import reverse_list, add +import unittest + +class Week1(unittest.TestCase): + def test_add(self): + self.assertEqual(add(2,2), 4) + self.assertEqual(add(-100, 5), -95) + + def test_reverse(self): + self.assertEqual(reverse_list([1,2,3]), [3,2,1]) + + +import homework1 +class Report1Flat(Report): + title = "CS 101 Report 1" + questions = [(Week1, 10)] # Include a single question for 10 credits. + pack_imports = [homework1] + +if __name__ == "__main__": + # Uncomment to simply run everything as a unittest: + # unittest.main(verbosity=2) + evaluate_report_student(Report1Flat()) diff --git a/examples/example_flat/instructor/cs101flat/report1flat_grade.py b/examples/example_flat/instructor/cs101flat/report1flat_grade.py new file mode 100644 index 0000000..48b0980 --- /dev/null +++ b/examples/example_flat/instructor/cs101flat/report1flat_grade.py @@ -0,0 +1,349 @@ + +import numpy as np +from tabulate import tabulate +from datetime import datetime +import pyfiglet +import unittest +# from unitgrade2.unitgrade2 import MySuite + +import inspect +import os +import argparse +import sys +import time +import threading # don't import Thread bc. of minify issue. +import tqdm # don't do from tqdm import tqdm because of minify-issue + +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: +To run all tests in a report: + +> python assignment1_dp.py + +To run only question 2 or question 2.1 + +> python assignment1_dp.py -q 2 +> python assignment1_dp.py -q 2.1 + +Note this scripts does not grade your report. To grade your report, use: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('-q', nargs='?', type=str, default=None, help='Only evaluate this question (e.g.: -q 2)') +parser.add_argument('--showexpected', action="store_true", help='Show the expected/desired result') +parser.add_argument('--showcomputed', action="store_true", help='Show the answer your code computes') +parser.add_argument('--unmute', action="store_true", help='Show result of print(...) commands in code') +parser.add_argument('--passall', action="store_true", help='Automatically pass all tests. Useful when debugging.') + +def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False): + args = parser.parse_args() + if question is None and args.q is not None: + question = args.q + if "." in question: + question, qitem = [int(v) for v in question.split(".")] + else: + question = int(question) + + if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file: + raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation") + + if unmute is None: + unmute = args.unmute + if passall is None: + passall = args.passall + + results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute, + show_tol_err=show_tol_err) + + + if question is None: + print("Provisional evaluation") + tabulate(table_data) + table = table_data + print(tabulate(table)) + print(" ") + + fr = inspect.getouterframes(inspect.currentframe())[1].filename + gfile = os.path.basename(fr)[:-3] + "_grade.py" + if os.path.exists(gfile): + print("Note your results have not yet been registered. \nTo register your results, please run the file:") + print(">>>", gfile) + print("In the same manner as you ran this file.") + + + return results + + +def upack(q): + # h = zip([(i['w'], i['possible'], i['obtained']) for i in q.values()]) + h =[(i['w'], i['possible'], i['obtained']) for i in q.values()] + h = np.asarray(h) + return h[:,0], h[:,1], h[:,2], + +class UnitgradeTextRunner(unittest.TextTestRunner): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + +class SequentialTestLoader(unittest.TestLoader): + def getTestCaseNames(self, testCaseClass): + test_names = super().getTestCaseNames(testCaseClass) + # testcase_methods = list(testCaseClass.__dict__.keys()) + ls = [] + for C in testCaseClass.mro(): + if issubclass(C, unittest.TestCase): + ls = list(C.__dict__.keys()) + ls + testcase_methods = ls + test_names.sort(key=testcase_methods.index) + return test_names + +def evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False, + show_progress_bar=True, + show_tol_err=False, + big_header=True): + + now = datetime.now() + if big_header: + ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom") + b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) + else: + b = "Unitgrade" + print(b + " v" + __version__) + dt_string = now.strftime("%d/%m/%Y %H:%M:%S") + print("Started: " + dt_string) + s = report.title + if hasattr(report, "version") and report.version is not None: + s += " version " + report.version + print("Evaluating " + s, "(use --help for options)" if show_help_flag else "") + # print(f"Loaded answers from: ", report.computed_answers_file, "\n") + table_data = [] + nL = 80 + t_start = time.time() + score = {} + loader = SequentialTestLoader() + + for n, (q, w) in enumerate(report.questions): + # q = q() + # q_hidden = False + # q_hidden = issubclass(q.__class__, Hidden) + if question is not None and n+1 != question: + continue + suite = loader.loadTestsFromTestCase(q) + qtitle = q.question_title() if hasattr(q, 'question_title') else q.__qualname__ + q_title_print = "Question %i: %s"%(n+1, qtitle) + print(q_title_print, end="") + q.possible = 0 + q.obtained = 0 + q_ = {} # Gather score in this class. + # unittest.Te + # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_] + UTextResult.q_title_print = q_title_print # Hacky + UTextResult.show_progress_bar = show_progress_bar # Hacky. + UTextResult.number = n + + res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) + + possible = res.testsRun + obtained = len(res.successes) + + assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun + + # possible = int(ws @ possible) + # obtained = int(ws @ obtained) + # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0 + + obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 + score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} + q.obtained = obtained + q.possible = possible + + s1 = f"*** Question q{n+1}" + s2 = f" {q.obtained}/{w}" + print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 ) + print(" ") + table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"]) + + ws, possible, obtained = upack(score) + possible = int( msum(possible) ) + obtained = int( msum(obtained) ) # Cast to python int + report.possible = possible + report.obtained = obtained + now = datetime.now() + dt_string = now.strftime("%H:%M:%S") + + dt = int(time.time()-t_start) + minutes = dt//60 + seconds = dt - minutes*60 + plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") + + print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")") + + table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) + results = {'total': (obtained, possible), 'details': score} + return results, table_data + + + + +from tabulate import tabulate +from datetime import datetime +import inspect +import json +import os +import bz2 +import pickle +import os + +def bzwrite(json_str, token): # to get around obfuscation issues + with getattr(bz2, 'open')(token, "wt") as f: + f.write(json_str) + +def gather_imports(imp): + resources = {} + m = imp + # for m in pack_imports: + # print(f"*** {m.__name__}") + f = m.__file__ + # dn = os.path.dirname(f) + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = str(__import__(m.__name__.split('.')[0]).__path__) + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: + top_package = os.path.dirname(m.__file__) + module_import = True + else: + top_package = __import__(m.__name__.split('.')[0]).__path__._path[0] + module_import = False + + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = os.path.dirname(top_package) + import zipfile + # import strea + # zipfile.ZipFile + import io + # file_like_object = io.BytesIO(my_zip_data) + zip_buffer = io.BytesIO() + with zipfile.ZipFile(zip_buffer, 'w') as zip: + # zip.write() + for root, dirs, files in os.walk(top_package): + for file in files: + if file.endswith(".py"): + fpath = os.path.join(root, file) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) + zip.write(fpath, v) + + resources['zipfile'] = zip_buffer.getvalue() + resources['top_package'] = top_package + resources['module_import'] = module_import + return resources, top_package + + if f.endswith("__init__.py"): + for root, dirs, files in os.walk(os.path.dirname(f)): + for file in files: + if file.endswith(".py"): + # print(file) + # print() + v = os.path.relpath(os.path.join(root, file), top_package) + with open(os.path.join(root, file), 'r') as ff: + resources[v] = ff.read() + else: + v = os.path.relpath(f, top_package) + with open(f, 'r') as ff: + resources[v] = ff.read() + return resources + +import argparse +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Use this script to get the score of your report. Example: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('--noprogress', action="store_true", help='Disable progress bars') +parser.add_argument('--autolab', action="store_true", help='Show Autolab results') + +def gather_upload_to_campusnet(report, output_dir=None): + n = report.nL + args = parser.parse_args() + results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, + show_progress_bar=not args.noprogress, + big_header=not args.autolab) + print(" ") + print("="*n) + print("Final evaluation") + print(tabulate(table_data)) + # also load the source code of missing files... + + sources = {} + + if not args.autolab: + if len(report.individual_imports) > 0: + print("By uploading the .token file, you verify the files:") + for m in report.individual_imports: + print(">", m.__file__) + print("Are created/modified individually by you in agreement with DTUs exam rules") + report.pack_imports += report.individual_imports + + if len(report.pack_imports) > 0: + print("Including files in upload...") + for k, m in enumerate(report.pack_imports): + nimp, top_package = gather_imports(m) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import + nimp['name'] = m.__name__ + sources[k] = nimp + # if len([k for k in nimp if k not in sources]) > 0: + print(f"*** {m.__name__}") + # sources = {**sources, **nimp} + results['sources'] = sources + + if output_dir is None: + output_dir = os.getcwd() + + payload_out_base = report.__class__.__name__ + "_handin" + + obtain, possible = results['total'] + vstring = "_v"+report.version if report.version is not None else "" + + token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) + token = os.path.join(output_dir, token) + with open(token, 'wb') as f: + pickle.dump(results, f) + + if not args.autolab: + print(" ") + print("To get credit for your results, please upload the single file: ") + print(">", token) + print("To campusnet without any modifications.") + + # print("Now time for some autolab fun") + +def source_instantiate(name, report1_source, payload): + eval("exec")(report1_source, globals()) + pl = pickle.loads(bytes.fromhex(payload)) + report = eval(name)(payload=pl, strict=True) + # report.set_payload(pl) + return report + + + +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n\n"""\n# from . import cache_read\nimport unittest\nimport numpy as np\nimport sys\nimport collections\nimport re\nimport threading\nimport tqdm\nimport time\nimport pickle\nimport os\nfrom io import StringIO\nfrom unittest.runner import _WritelnDecorator\nimport inspect\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\ndef setup_dir_by_class(C,base_dir):\n name = C.__class__.__name__\n # base_dir = os.path.join(base_dir, name)\n # if not os.path.isdir(base_dir):\n # os.makedirs(base_dir)\n return base_dir, name\n\nclass Hidden:\n def hide(self):\n return True\n\nclass Logger(object):\n def __init__(self, buffer):\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\nclass QItem(unittest.TestCase):\n title = None\n testfun = None\n tol = 0\n estimated_time = 0.42\n _precomputed_payload = None\n _computed_answer = None # Internal helper to later get results.\n weight = 1 # the weight of the question.\n\n def __init__(self, question=None, *args, **kwargs):\n if self.tol > 0 and self.testfun is None:\n self.testfun = self.assertL2Relative\n elif self.testfun is None:\n self.testfun = self.assertEqual\n\n self.name = self.__class__.__name__\n # self._correct_answer_payload = correct_answer_payload\n self.question = question\n\n super().__init__(*args, **kwargs)\n if self.title is None:\n self.title = self.name\n\n def _safe_get_title(self):\n if self._precomputed_title is not None:\n return self._precomputed_title\n return self.title\n\n def assertNorm(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed).flat- np.asarray(expected)).flat )\n nrm = np.sqrt(np.sum( diff ** 2))\n\n self.error_computed = nrm\n\n if nrm > tol:\n print(f"Not equal within tolerance {tol}; norm of difference was {nrm}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def assertL2(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n self.error_computed = np.max(diff)\n\n if np.max(diff) > tol:\n print(f"Not equal within tolerance {tol=}; deviation was {np.max(diff)=}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol=}, {np.max(diff)=}")\n\n def assertL2Relative(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n diff = diff / (1e-8 + np.abs( (np.asarray(computed) + np.asarray(expected)) ) )\n self.error_computed = np.max(np.abs(diff))\n if np.sum(diff > tol) > 0:\n print(f"Not equal within tolerance {tol}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def precomputed_payload(self):\n return self._precomputed_payload\n\n def precompute_payload(self):\n # Pre-compute resources to include in tests (useful for getting around rng).\n pass\n\n def compute_answer(self, unmute=False):\n raise NotImplementedError("test code here")\n\n def test(self, computed, expected):\n self.testfun(computed, expected)\n\n def get_points(self, verbose=False, show_expected=False, show_computed=False,unmute=False, passall=False, silent=False, **kwargs):\n possible = 1\n computed = None\n def show_computed_(computed):\n print(">>> Your output:")\n print(computed)\n\n def show_expected_(expected):\n print(">>> Expected output (note: may have been processed; read text script):")\n print(expected)\n\n correct = self._correct_answer_payload\n try:\n if unmute: # Required to not mix together print stuff.\n print("")\n computed = self.compute_answer(unmute=unmute)\n except Exception as e:\n if not passall:\n if not silent:\n print("\\n=================================================================================")\n print(f"When trying to run test class \'{self.name}\' your code threw an error:", e)\n show_expected_(correct)\n import traceback\n print(traceback.format_exc())\n print("=================================================================================")\n return (0, possible)\n\n if self._computed_answer is None:\n self._computed_answer = computed\n\n if show_expected or show_computed:\n print("\\n")\n if show_expected:\n show_expected_(correct)\n if show_computed:\n show_computed_(computed)\n try:\n if not passall:\n self.test(computed=computed, expected=correct)\n except Exception as e:\n if not silent:\n print("\\n=================================================================================")\n print(f"Test output from test class \'{self.name}\' does not match expected result. Test error:")\n print(e)\n show_computed_(computed)\n show_expected_(correct)\n return (0, possible)\n return (1, possible)\n\n def score(self):\n try:\n self.test()\n except Exception as e:\n return 0\n return 1\n\nclass QPrintItem(QItem):\n def compute_answer_print(self):\n """\n Generate output which is to be tested. By default, both text written to the terminal using print(...) as well as return values\n are send to process_output (see compute_answer below). In other words, the text generated is:\n\n res = compute_Answer_print()\n txt = (any terminal output generated above)\n numbers = (any numbers found in terminal-output txt)\n\n self.test(process_output(res, txt, numbers), <expected result>)\n\n :return: Optional values for comparison\n """\n raise Exception("Generate output here. The output is passed to self.process_output")\n\n def process_output(self, res, txt, numbers):\n return res\n\n def compute_answer(self, unmute=False):\n with Capturing(unmute=unmute) as output:\n res = self.compute_answer_print()\n s = "\\n".join(output)\n s = rm_progress_bar(s) # Remove progress bar.\n numbers = extract_numbers(s)\n self._computed_answer = (res, s, numbers)\n return self.process_output(res, s, numbers)\n\nclass OrderedClassMembers(type):\n @classmethod\n def __prepare__(self, name, bases):\n return collections.OrderedDict()\n def __new__(self, name, bases, classdict):\n ks = list(classdict.keys())\n for b in bases:\n ks += b.__ordered__\n classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n return type.__new__(self, name, bases, classdict)\n\nclass QuestionGroup(metaclass=OrderedClassMembers):\n title = "Untitled question"\n partially_scored = False\n t_init = 0 # Time spend on initialization (placeholder; set this externally).\n estimated_time = 0.42\n has_called_init_ = False\n _name = None\n _items = None\n\n @property\n def items(self):\n if self._items == None:\n self._items = []\n members = [gt for gt in [getattr(self, gt) for gt in self.__ordered__ if gt not in ["__classcell__", "__init__"]] if inspect.isclass(gt) and issubclass(gt, QItem)]\n for I in members:\n self._items.append( I(question=self))\n return self._items\n\n @items.setter\n def items(self, value):\n self._items = value\n\n @property\n def name(self):\n if self._name == None:\n self._name = self.__class__.__name__\n return self._name #\n\n @name.setter\n def name(self, val):\n self._name = val\n\n def init(self):\n # Can be used to set resources relevant for this question instance.\n pass\n\n def init_all_item_questions(self):\n for item in self.items:\n if not item.question.has_called_init_:\n item.question.init()\n item.question.has_called_init_ = True\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 80 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q,_) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q,_) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n # else:\n # if os.path.isfile(self.computed_answers_file):\n # self.set_payload(cache_read(self.computed_answers_file), strict=strict)\n # else:\n # s = f"> Warning: The pre-computed answer file, {os.path.abspath(self.computed_answers_file)} is missing. The framework will NOT work as intended. Reasons may be a broken local installation."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n import unittest\n loader = unittest.TestLoader()\n for q,_ in self.questions:\n import time\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self):\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\':True}\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct+1)\n if i > 0 and l.find("|", i+1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = \'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar",show_progress_bar=True):\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.1\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n\n # self.pbar = tqdm.tqdm(total=self.n)\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if hasattr(self, \'pbar\') and self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar=None\n\n sys.stdout.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=sys.stdout, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\') # , unit_scale=dt, unit=\'seconds\'):\n\n for _ in range(self.n-1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\n\n# class MySuite(unittest.suite.TestSuite): # Not sure we need this one anymore.\n# raise Exception("no suite")\n# pass\n\ndef instance_call_stack(instance):\n s = "-".join(map(lambda x: x.__name__, instance.__class__.mro()))\n return s\n\ndef get_class_that_defined_method(meth):\n for cls in inspect.getmro(meth.im_class):\n if meth.__name__ in cls.__dict__:\n return cls\n return None\n\ndef caller_name(skip=2):\n """Get a name of a caller in the format module.class.method\n\n `skip` specifies how many levels of stack to skip while getting caller\n name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.\n\n An empty string is returned if skipped levels exceed stack height\n """\n stack = inspect.stack()\n start = 0 + skip\n if len(stack) < start + 1:\n return \'\'\n parentframe = stack[start][0]\n\n name = []\n module = inspect.getmodule(parentframe)\n # `modname` can be None when frame is executed directly in console\n # TODO(techtonik): consider using __main__\n if module:\n name.append(module.__name__)\n # detect classname\n if \'self\' in parentframe.f_locals:\n # I don\'t know any way to detect call from the object method\n # XXX: there seems to be no way to detect static method call - it will\n # be just a function call\n name.append(parentframe.f_locals[\'self\'].__class__.__name__)\n codename = parentframe.f_code.co_name\n if codename != \'<module>\': # top level usually\n name.append( codename ) # function or a method\n\n ## Avoid circular refs and frame leaks\n # https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack\n del parentframe, stack\n\n return ".".join(name)\n\ndef get_class_from_frame(fr):\n\n args, _, _, value_dict = inspect.getargvalues(fr)\n # we check the first parameter for the frame function is\n # named \'self\'\n if len(args) and args[0] == \'self\':\n # in that case, \'self\' will be referenced in value_dict\n instance = value_dict.get(\'self\', None)\n if instance:\n # return its class\n # isinstance(instance, Testing) # is the actual class instance.\n\n return getattr(instance, \'__class__\', None)\n # return None otherwise\n return None\n\nfrom typing import Any\nimport inspect, gc\n\ndef giveupthefunc():\n frame = inspect.currentframe()\n code = frame.f_code\n globs = frame.f_globals\n functype = type(lambda: 0)\n funcs = []\n for func in gc.get_referrers(code):\n if type(func) is functype:\n if getattr(func, "__code__", None) is code:\n if getattr(func, "__globals__", None) is globs:\n funcs.append(func)\n if len(funcs) > 1:\n return None\n return funcs[0] if funcs else None\n\n\nfrom collections import defaultdict\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n # if self.dots or self.showAll:\n # self.stream.writeln()\n # if hasattr(self, \'cc\'):\n # self.cc.terminate()\n # self.cc_terminate(success=False)\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n # if self.showAll:\n # self.stream.writeln("FAIL")\n # elif self.dots:\n # self.stream.write(\'F\')\n # self.stream.flush()\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n # super().addSuccess(test)\n self.successes.append(test)\n # super().addSuccess(test)\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n self.cc_terminate()\n\n\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n sys.stdout.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="")\n else:\n print( dot_parts, end="")\n\n if tsecs >= 0.1:\n state += " (" + str(tsecs) + " seconds)"\n print(state)\n\n\n def startTest(self, test):\n # super().startTest(test)\n j =self.testsRun\n self.testsRun += 1\n # print("Starting the test...")\n # show_progress_bar = True\n n = UTextResult.number\n\n item_title = self.getDescription(test)\n # item_title = item_title.split("\\n")[0]\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n # test.countTestCases()\n self.item_title_print = "*** q%i.%i) %s" % (n + 1, j + 1, item_title)\n estimated_time = 10\n nL = 80\n #\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar)\n else:\n print(self.item_title_print + (\'.\' * max(0, nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n # q_title_print = "some printed title..."\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass == None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n # start = 10\n # q_time = np.round(time.time() - start, 2)\n nL = 80\n print(" " * max(0, nL - len(self.cc.title)) + (\n " (" + str(q_time) + " seconds)" if q_time >= 0.1 else "")) # if q.name in report.payloads else "")\n # print("=" * nL)\n\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n from io import StringIO\n stream = StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n# def wrapper(foo):\n# def magic(self):\n# # s = "-".join(map(lambda x: x.__name__, self.__class__.mro()))\n# foo(self)\n# magic.__doc__ = foo.__doc__\n# return magic\n\nfrom functools import update_wrapper, _make_key, RLock\nfrom collections import namedtuple\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)) )\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n return wrapper\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n\n def capture(self):\n return Capturing2(stdout=self._stdout)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ != None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n self._stdout = sys.stdout\n import io\n sys.stdout = io.StringIO()\n super().setUp()\n # print("Setting up...")\n\n def _callTearDown(self):\n sys.stdout = self._stdout\n super().tearDown()\n # print("asdfsfd")\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd == None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n # self._testMethodDoc.strip().splitlines()[0].strip()\n sd = self.shortDescriptionStandard()\n title = self._cache_get( (self.cache_id(), \'title\'), sd )\n return title if title != None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome == None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc != None:\n # Ensure the cache is eventually updated with the right docstring.\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard() )\n # Fix temp cache here (for using the @cache decorator)\n self._cache2[ (self.cache_id(), \'assert\') ] = {}\n\n res = testMethod()\n elapsed = time.time() - t\n # self._cache_put( (self.cache_id(), \'title\'), self.shortDescription() )\n\n self._get_outcome()[self.cache_id()] = res\n self._cache_put( (self.cache_id(), "time"), elapsed)\n\n # This is my base test class. So what is new about it?\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return (c,m)\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n # self.cache_indexes = defaultdict(lambda: 0)\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n cache = self._cache_get(key, {})\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, first)\n assert_fun(first, _expected, *args, **kwargs)\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__) ) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache != None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n # print("Loading cache from", cfile)\n if os.path.exists(cfile):\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n else:\n print("Warning! data file not found", cfile)\n\ndef hide(func):\n return func\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n # (*)We can be somewhat "hygienic", but newDecorator still isn\'t signature-preserving, i.e. you will not be able to get a runtime list of parameters. For that, you need hackish libraries...but in this case, the only argument is func, so it\'s not a big issue\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\n# from unitgrade2.unitgrade2 import MySuite\n\nimport inspect\nimport os\nimport argparse\nimport sys\nimport time\nimport threading # don\'t import Thread bc. of minify issue.\nimport tqdm # don\'t do from tqdm import tqdm because of minify-issue\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n print(b + " v" + __version__)\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print("Evaluating " + s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n nL = 80\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n # q = q()\n # q_hidden = False\n # q_hidden = issubclass(q.__class__, Hidden)\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n # unittest.Te\n # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_]\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n # possible = int(ws @ possible)\n # obtained = int(ws @ obtained)\n # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"*** Question q{n+1}"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n print(" ")\n print("="*n)\n print("Final evaluation")\n print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f"*** {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single file: ")\n print(">", token)\n print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\nfrom homework1 import reverse_list, add\nimport unittest\n\nclass Week1(unittest.TestCase):\n def test_add(self):\n self.assertEqual(add(2,2), 4)\n self.assertEqual(add(-100, 5), -95)\n\n def test_reverse(self):\n self.assertEqual(reverse_list([1,2,3]), [3,2,1])\n\n\nimport homework1\nclass Report1Flat(Report):\n title = "CS 101 Report 1"\n questions = [(Week1, 10)] # Include a single question for 10 credits.\n pack_imports = [homework1]' +report1_payload = '8004953f000000000000007d948c055765656b31947d948c2c6e6f20636163686520736565205f73657475705f616e737765727320696e20756e69746772616465322e7079948873732e' +name="Report1Flat" + +report = source_instantiate(name, report1_source, report1_payload) +output_dir = os.path.dirname(__file__) +gather_upload_to_campusnet(report, output_dir) \ No newline at end of file diff --git a/examples/example_flat/students/cs101flat/Report1Flat_handin_0_of_10.token b/examples/example_flat/students/cs101flat/Report1Flat_handin_0_of_10.token new file mode 100644 index 0000000000000000000000000000000000000000..c9122d00c8fff99b66be2f551819687826d1b9c0 GIT binary patch literal 65468 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 DVzir6 literal 0 HcmV?d00001 diff --git a/examples/example_flat/students/cs101flat/__pycache__/deploy.cpython-38.pyc b/examples/example_flat/students/cs101flat/__pycache__/deploy.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c91ca3192aaec26f60abe00c1109cb32983e52a GIT binary patch literal 581 zcmWIL<>g{vU|=}nr<>Ts#K7<v#6iaF3=9ko3=9m#5ey6rDGVu$ISjdsQH+crHd78$ zE^`z!n9ZESlFJ&!%E*w)mco+FR1}iRoXVKO3WjXGEQ}1PtXZrn>?s^6oGDx>+$lWi z%qhGe@f5yb22K8#AUib~Z*d2u7UUO|7`o*omS{5G5-(0IDJ_UkFG@^FjZe$WNsTW8 z%WE>;;wa9`EQn9ZEV{)JAD^3;nHL}LrpX+|mYbQEnN}IanV*zaTAZ9%k{ZRHT9KMu zT9SH;p-MtePhU?@zqq6{B{i?4SU<Vg(7-S)C$S{t7I%DnL1l7caz<)=d=<NvUP*p- zYF-s*N@_t)ex+VPr6x-hS8;x6QF1EC(kPCU)Z!8_OEftrH8C$9#Lvu2EXmBz0}HdK zRwU<?rlk65vPW@4Tn)CVh>3xLAxf<@FS7*Vt%9P=vc!^9BfX5wl$6xG_~fG0#1e>a zi;I{+irGP4El4dYVqsul0C_hrF*h|nekDT@Cj$e7_!Z}D6%$&VT2vfUQksz(<C33T znhWw`42DH9sTGO21v#n3Fh)Em0Ae!pic5-0lS}f8V&I_?1NZ$c4x8Nkl+v73J4R4q R0HqloMjl2UCJsgcHURWnuU!BD literal 0 HcmV?d00001 diff --git a/examples/example_flat/students/cs101flat/__pycache__/homework1.cpython-38.pyc b/examples/example_flat/students/cs101flat/__pycache__/homework1.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f019923d47a404593f992813d90c89b3e2da2546 GIT binary patch literal 994 zcmWIL<>g{vU|{$tpqFUI%)sy%#6iaF3=9ko3=9m#UJMKjDGX5zDU2yhEeuhNDa<J> zEeugismv*?*-S-lsVpgMsZ1&CDeTQmvl-?xH8V0Yq_U)Nq%x+kr*NilrEsUSG&43c zF*2m^1T$#zR;6>fRwU*Y<fJMTmz1WY=9MTU=clCVDTHKX7AvG>=A<fQ7Aqu{mgMIq zmSiR;=Hyf=q^IVk7A2OXrYNKp<>xAtWTb)>7As`tfjOCZ#U(|h$tC$kx+$4OsUSur zS281#e?V*|1_lOake7HE7#K<zQW%>Vn;27=f*CZKs}^x7C@3hnXO^YrDI_Z7WEPhw zsOMIKXmy36)RNMoykeMma(-TMW^qYoUb;el8rb^c#N1Sc)ST4Z)Vva~n~GA)Qj3aH z74nNxQj7EyT=mlRz~)26<3Z+XL>uZD=@`dqDimkrm*%8Etx<?J)-lpCj8)J`gxRPG z(fmu?6BKG7OBB*d^O8$4^Yavv@>44PRx;ia_RBAUOQ)u|78T_eX);E!LHw|ip@@Zn zf#FxYvsFxJacWU<Oi5`*YK%*Maw*8T;+WFB%#!q?#FW(df}+f_#FEsQR7e~Z!x-^t zIf*4P&=@O@NiH@tFa!%_<maZA=NDxg>J?Pp;z9Ny$jV|+pfj))u`w_(B!k(|NCB}y z7*~WOK_jE2JYONNG&d==s8}H(F+m|QFGV3CDM6tGoLXSP01d+8%-n*UN?1siWTa*m zDHNCHT7d)KP9ZTRMI%usNmIdAAyGR?p$L>H{E9%X&}6#Bn0SjZ2^>rif|Y@R;TCgZ zN(v~oK)k_H#0e7Qh>y=r%*>0AuM&^UFD+6iN-ZwUDFG!0BL!^*BL!Q9mmo8%#Dc)d z0F+P?6^e5cb8^55>m@S-1H(&@@h?G{%ukc`7JGbrN`7*DJUE!(mgFVorpCwLVl61j p%qxL73KXOe7D)ds4x8Nkl+v73JCIL`K^dBbnS+Caje|*q5diVO60iUO literal 0 HcmV?d00001 diff --git a/examples/example_flat/students/cs101flat/__pycache__/report1flat.cpython-38.pyc b/examples/example_flat/students/cs101flat/__pycache__/report1flat.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b843499aeb6a5314ec477e4a8ff9439e6927c59 GIT binary patch literal 1204 zcmWIL<>g{vU|`^O*G>Gy#K7<v#6iX^3=9ko3=9m#1q=)fDGVu$ISjdsQH+crHd78$ zE^`z!n9ZESlFJ&!n#&f&2IjNmut%}GGo-Mlu(dFxu%)tRaWpeWai%b&u;j2rvA8p& zu%~deFr;v#GBz_uaiwynaAq?V<)kvFay2tFgUn|MX3*q%337`j<1MzJ)Pnq?5>3Wi z;;ChcIi-musqsZ%q4?sG(v;M^5>2LCJVmKxsYS)9@j02rCAXLpQ&RkrLHdy~6U;v{ z3=9mZ3{i|J3{gxej44bl3{lJ}%qc7_3{fm8ticSLY`0j$Q&Y1IlNpipgV?ML3=Gbo zAkko8V5nhOz)-@N!kEI?%#^~kkckl{!kog=%#^|k5=mhI8Rz#BWcN!@fM(A5|Ns9> zkU5zP|NsB5$#jc5vA8(3sKm9fG%=?LB(ak57E5tzPTER_B3=dthF{6fRxzQ)sYS&x zC8ZguF)sPZrManjCB-qNd6^~YMTse?@dZVhWr-!JF{u@axdl0?#V|&ET25j~OlDqj zNl|HXNq$jGa<QR-Ay^O+gkZK_LFFxulGNgo_{5YHc98c#E@fa8Vyxl=hl3tW5+gv^ zU;(0l93V_7%<0U{j48}1Oeu`%%<zC<@p}mh#UhX$FPRw_7&MuRKxwcDWM2_C0|Ugz zU~d=kF)%RP;sM(Xja-m|Vo*pjFjfg6ISeZ8r^$MYBR)PaF*h|n{uWn!d~SY9X%2|Z z6CV!>U5HE(Kgb>-5CIBah-F|F$i^a&k3hH><R%VA4i>No?!*N0I3h7cF{iSmFr_i2 zGe)tdvZXUdv8ONxGib8h;)bMRL${p7k}7`ZU<E@1Lj{PKg5gV0Aoyu=6bXY|B?2Nu zLB3}#$t=l91v?;$v#>O^xFj<_ulN>cMt*K;d45s0VH8h6VsdtTW-cg)6eD>B;U6&u z1_l98oPs>X!N|hM#aILq0|ixXVrE`^ye3bSC_E_{=^>a!ps<dTMF=3|;xkfn3Q~)T zjfz0gSR@H@GAO{n2{?)aqy-dnQ5+$u#U;*(#Sl+`Erxg!>_7wo3i?|dHjo&z10|AT WkfV7Rc^Cy4IhcevIT!_4IhX;a(-RH= literal 0 HcmV?d00001 diff --git a/examples/example_flat/students/cs101flat/__pycache__/report1flat_grade.cpython-38.pyc b/examples/example_flat/students/cs101flat/__pycache__/report1flat_grade.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..01435181f7f83079961ed2105a4d343c2511a9d0 GIT binary patch literal 57893 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 za�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 A*Z=?k literal 0 HcmV?d00001 diff --git a/examples/example_flat/students/cs101flat/homework1.py b/examples/example_flat/students/cs101flat/homework1.py new file mode 100644 index 0000000..3543f1b --- /dev/null +++ b/examples/example_flat/students/cs101flat/homework1.py @@ -0,0 +1,21 @@ +""" +Example student code. This file is automatically generated from the files in the instructor-directory +""" +def reverse_list(mylist): + """ + Given a list 'mylist' returns a list consisting of the same elements in reverse order. E.g. + reverse_list([1,2,3]) should return [3,2,1] (as a list). + """ + # TODO: 1 lines missing. + raise NotImplementedError("Implement function body") + +def add(a,b): + """ Given two numbers `a` and `b` this function should simply return their sum: + > add(a,b) = a+b """ + # TODO: 1 lines missing. + raise NotImplementedError("Implement function body") + +if __name__ == "__main__": + # Problem 1: Write a function which add two numbers + print(f"Your result of 2 + 2 = {add(2,2)}") + print(f"Reversing a small list", reverse_list([2,3,5,7])) diff --git a/examples/example_flat/students/cs101flat/report1flat.py b/examples/example_flat/students/cs101flat/report1flat.py new file mode 100644 index 0000000..4a268f7 --- /dev/null +++ b/examples/example_flat/students/cs101flat/report1flat.py @@ -0,0 +1,27 @@ +""" +Example student code. This file is automatically generated from the files in the instructor-directory +""" +from src.unitgrade2.unitgrade2 import Report +from src.unitgrade2 import evaluate_report_student +from homework1 import reverse_list, add +import unittest + +class Week1(unittest.TestCase): + def test_add(self): + self.assertEqual(add(2,2), 4) + self.assertEqual(add(-100, 5), -95) + + def test_reverse(self): + self.assertEqual(reverse_list([1,2,3]), [3,2,1]) + + +import homework1 +class Report1Flat(Report): + title = "CS 101 Report 1" + questions = [(Week1, 10)] # Include a single question for 10 credits. + pack_imports = [homework1] + +if __name__ == "__main__": + # Uncomment to simply run everything as a unittest: + # unittest.main(verbosity=2) + evaluate_report_student(Report1Flat()) diff --git a/examples/example_flat/students/cs101flat/report1flat_grade.py b/examples/example_flat/students/cs101flat/report1flat_grade.py new file mode 100644 index 0000000..65db51b --- /dev/null +++ b/examples/example_flat/students/cs101flat/report1flat_grade.py @@ -0,0 +1,351 @@ +""" +Example student code. This file is automatically generated from the files in the instructor-directory +""" +import numpy as np +from tabulate import tabulate +from datetime import datetime +import pyfiglet +import unittest +# from unitgrade2.unitgrade2 import MySuite + +import inspect +import os +import argparse +import sys +import time +import threading # don't import Thread bc. of minify issue. +import tqdm # don't do from tqdm import tqdm because of minify-issue + +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: +To run all tests in a report: + +> python assignment1_dp.py + +To run only question 2 or question 2.1 + +> python assignment1_dp.py -q 2 +> python assignment1_dp.py -q 2.1 + +Note this scripts does not grade your report. To grade your report, use: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('-q', nargs='?', type=str, default=None, help='Only evaluate this question (e.g.: -q 2)') +parser.add_argument('--showexpected', action="store_true", help='Show the expected/desired result') +parser.add_argument('--showcomputed', action="store_true", help='Show the answer your code computes') +parser.add_argument('--unmute', action="store_true", help='Show result of print(...) commands in code') +parser.add_argument('--passall', action="store_true", help='Automatically pass all tests. Useful when debugging.') + +def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False): + args = parser.parse_args() + if question is None and args.q is not None: + question = args.q + if "." in question: + question, qitem = [int(v) for v in question.split(".")] + else: + question = int(question) + + if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file: + raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation") + + if unmute is None: + unmute = args.unmute + if passall is None: + passall = args.passall + + results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute, + show_tol_err=show_tol_err) + + + if question is None: + print("Provisional evaluation") + tabulate(table_data) + table = table_data + print(tabulate(table)) + print(" ") + + fr = inspect.getouterframes(inspect.currentframe())[1].filename + gfile = os.path.basename(fr)[:-3] + "_grade.py" + if os.path.exists(gfile): + print("Note your results have not yet been registered. \nTo register your results, please run the file:") + print(">>>", gfile) + print("In the same manner as you ran this file.") + + + return results + + +def upack(q): + # h = zip([(i['w'], i['possible'], i['obtained']) for i in q.values()]) + h =[(i['w'], i['possible'], i['obtained']) for i in q.values()] + h = np.asarray(h) + return h[:,0], h[:,1], h[:,2], + +class UnitgradeTextRunner(unittest.TextTestRunner): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + +class SequentialTestLoader(unittest.TestLoader): + def getTestCaseNames(self, testCaseClass): + test_names = super().getTestCaseNames(testCaseClass) + # testcase_methods = list(testCaseClass.__dict__.keys()) + ls = [] + for C in testCaseClass.mro(): + if issubclass(C, unittest.TestCase): + ls = list(C.__dict__.keys()) + ls + testcase_methods = ls + test_names.sort(key=testcase_methods.index) + return test_names + +def evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False, + show_progress_bar=True, + show_tol_err=False, + big_header=True): + + now = datetime.now() + if big_header: + ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom") + b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) + else: + b = "Unitgrade" + print(b + " v" + __version__) + dt_string = now.strftime("%d/%m/%Y %H:%M:%S") + print("Started: " + dt_string) + s = report.title + if hasattr(report, "version") and report.version is not None: + s += " version " + report.version + print("Evaluating " + s, "(use --help for options)" if show_help_flag else "") + # print(f"Loaded answers from: ", report.computed_answers_file, "\n") + table_data = [] + nL = 80 + t_start = time.time() + score = {} + loader = SequentialTestLoader() + + for n, (q, w) in enumerate(report.questions): + # q = q() + # q_hidden = False + # q_hidden = issubclass(q.__class__, Hidden) + if question is not None and n+1 != question: + continue + suite = loader.loadTestsFromTestCase(q) + qtitle = q.question_title() if hasattr(q, 'question_title') else q.__qualname__ + q_title_print = "Question %i: %s"%(n+1, qtitle) + print(q_title_print, end="") + q.possible = 0 + q.obtained = 0 + q_ = {} # Gather score in this class. + # unittest.Te + # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_] + UTextResult.q_title_print = q_title_print # Hacky + UTextResult.show_progress_bar = show_progress_bar # Hacky. + UTextResult.number = n + + res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) + + possible = res.testsRun + obtained = len(res.successes) + + assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun + + # possible = int(ws @ possible) + # obtained = int(ws @ obtained) + # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0 + + obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 + score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} + q.obtained = obtained + q.possible = possible + + s1 = f"*** Question q{n+1}" + s2 = f" {q.obtained}/{w}" + print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 ) + print(" ") + table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"]) + + ws, possible, obtained = upack(score) + possible = int( msum(possible) ) + obtained = int( msum(obtained) ) # Cast to python int + report.possible = possible + report.obtained = obtained + now = datetime.now() + dt_string = now.strftime("%H:%M:%S") + + dt = int(time.time()-t_start) + minutes = dt//60 + seconds = dt - minutes*60 + plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") + + print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")") + + table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) + results = {'total': (obtained, possible), 'details': score} + return results, table_data + + + + +from tabulate import tabulate +from datetime import datetime +import inspect +import json +import os +import bz2 +import pickle +import os + +def bzwrite(json_str, token): # to get around obfuscation issues + with getattr(bz2, 'open')(token, "wt") as f: + f.write(json_str) + +def gather_imports(imp): + resources = {} + m = imp + # for m in pack_imports: + # print(f"*** {m.__name__}") + f = m.__file__ + # dn = os.path.dirname(f) + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = str(__import__(m.__name__.split('.')[0]).__path__) + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: + top_package = os.path.dirname(m.__file__) + module_import = True + else: + top_package = __import__(m.__name__.split('.')[0]).__path__._path[0] + module_import = False + + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = os.path.dirname(top_package) + import zipfile + # import strea + # zipfile.ZipFile + import io + # file_like_object = io.BytesIO(my_zip_data) + zip_buffer = io.BytesIO() + with zipfile.ZipFile(zip_buffer, 'w') as zip: + # zip.write() + for root, dirs, files in os.walk(top_package): + for file in files: + if file.endswith(".py"): + fpath = os.path.join(root, file) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) + zip.write(fpath, v) + + resources['zipfile'] = zip_buffer.getvalue() + resources['top_package'] = top_package + resources['module_import'] = module_import + return resources, top_package + + if f.endswith("__init__.py"): + for root, dirs, files in os.walk(os.path.dirname(f)): + for file in files: + if file.endswith(".py"): + # print(file) + # print() + v = os.path.relpath(os.path.join(root, file), top_package) + with open(os.path.join(root, file), 'r') as ff: + resources[v] = ff.read() + else: + v = os.path.relpath(f, top_package) + with open(f, 'r') as ff: + resources[v] = ff.read() + return resources + +import argparse +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Use this script to get the score of your report. Example: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('--noprogress', action="store_true", help='Disable progress bars') +parser.add_argument('--autolab', action="store_true", help='Show Autolab results') + +def gather_upload_to_campusnet(report, output_dir=None): + n = report.nL + args = parser.parse_args() + results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, + show_progress_bar=not args.noprogress, + big_header=not args.autolab) + print(" ") + print("="*n) + print("Final evaluation") + print(tabulate(table_data)) + # also load the source code of missing files... + + sources = {} + + if not args.autolab: + if len(report.individual_imports) > 0: + print("By uploading the .token file, you verify the files:") + for m in report.individual_imports: + print(">", m.__file__) + print("Are created/modified individually by you in agreement with DTUs exam rules") + report.pack_imports += report.individual_imports + + if len(report.pack_imports) > 0: + print("Including files in upload...") + for k, m in enumerate(report.pack_imports): + nimp, top_package = gather_imports(m) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import + nimp['name'] = m.__name__ + sources[k] = nimp + # if len([k for k in nimp if k not in sources]) > 0: + print(f"*** {m.__name__}") + # sources = {**sources, **nimp} + results['sources'] = sources + + if output_dir is None: + output_dir = os.getcwd() + + payload_out_base = report.__class__.__name__ + "_handin" + + obtain, possible = results['total'] + vstring = "_v"+report.version if report.version is not None else "" + + token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) + token = os.path.join(output_dir, token) + with open(token, 'wb') as f: + pickle.dump(results, f) + + if not args.autolab: + print(" ") + print("To get credit for your results, please upload the single file: ") + print(">", token) + print("To campusnet without any modifications.") + + # print("Now time for some autolab fun") + +def source_instantiate(name, report1_source, payload): + eval("exec")(report1_source, globals()) + pl = pickle.loads(bytes.fromhex(payload)) + report = eval(name)(payload=pl, strict=True) + # report.set_payload(pl) + return report + + + +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n\n"""\n# from . import cache_read\nimport unittest\nimport numpy as np\nimport sys\nimport collections\nimport re\nimport threading\nimport tqdm\nimport time\nimport pickle\nimport os\nfrom io import StringIO\nfrom unittest.runner import _WritelnDecorator\nimport inspect\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\ndef setup_dir_by_class(C,base_dir):\n name = C.__class__.__name__\n # base_dir = os.path.join(base_dir, name)\n # if not os.path.isdir(base_dir):\n # os.makedirs(base_dir)\n return base_dir, name\n\nclass Hidden:\n def hide(self):\n return True\n\nclass Logger(object):\n def __init__(self, buffer):\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\nclass QItem(unittest.TestCase):\n title = None\n testfun = None\n tol = 0\n estimated_time = 0.42\n _precomputed_payload = None\n _computed_answer = None # Internal helper to later get results.\n weight = 1 # the weight of the question.\n\n def __init__(self, question=None, *args, **kwargs):\n if self.tol > 0 and self.testfun is None:\n self.testfun = self.assertL2Relative\n elif self.testfun is None:\n self.testfun = self.assertEqual\n\n self.name = self.__class__.__name__\n # self._correct_answer_payload = correct_answer_payload\n self.question = question\n\n super().__init__(*args, **kwargs)\n if self.title is None:\n self.title = self.name\n\n def _safe_get_title(self):\n if self._precomputed_title is not None:\n return self._precomputed_title\n return self.title\n\n def assertNorm(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed).flat- np.asarray(expected)).flat )\n nrm = np.sqrt(np.sum( diff ** 2))\n\n self.error_computed = nrm\n\n if nrm > tol:\n print(f"Not equal within tolerance {tol}; norm of difference was {nrm}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def assertL2(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n self.error_computed = np.max(diff)\n\n if np.max(diff) > tol:\n print(f"Not equal within tolerance {tol=}; deviation was {np.max(diff)=}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol=}, {np.max(diff)=}")\n\n def assertL2Relative(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n diff = diff / (1e-8 + np.abs( (np.asarray(computed) + np.asarray(expected)) ) )\n self.error_computed = np.max(np.abs(diff))\n if np.sum(diff > tol) > 0:\n print(f"Not equal within tolerance {tol}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def precomputed_payload(self):\n return self._precomputed_payload\n\n def precompute_payload(self):\n # Pre-compute resources to include in tests (useful for getting around rng).\n pass\n\n def compute_answer(self, unmute=False):\n raise NotImplementedError("test code here")\n\n def test(self, computed, expected):\n self.testfun(computed, expected)\n\n def get_points(self, verbose=False, show_expected=False, show_computed=False,unmute=False, passall=False, silent=False, **kwargs):\n possible = 1\n computed = None\n def show_computed_(computed):\n print(">>> Your output:")\n print(computed)\n\n def show_expected_(expected):\n print(">>> Expected output (note: may have been processed; read text script):")\n print(expected)\n\n correct = self._correct_answer_payload\n try:\n if unmute: # Required to not mix together print stuff.\n print("")\n computed = self.compute_answer(unmute=unmute)\n except Exception as e:\n if not passall:\n if not silent:\n print("\\n=================================================================================")\n print(f"When trying to run test class \'{self.name}\' your code threw an error:", e)\n show_expected_(correct)\n import traceback\n print(traceback.format_exc())\n print("=================================================================================")\n return (0, possible)\n\n if self._computed_answer is None:\n self._computed_answer = computed\n\n if show_expected or show_computed:\n print("\\n")\n if show_expected:\n show_expected_(correct)\n if show_computed:\n show_computed_(computed)\n try:\n if not passall:\n self.test(computed=computed, expected=correct)\n except Exception as e:\n if not silent:\n print("\\n=================================================================================")\n print(f"Test output from test class \'{self.name}\' does not match expected result. Test error:")\n print(e)\n show_computed_(computed)\n show_expected_(correct)\n return (0, possible)\n return (1, possible)\n\n def score(self):\n try:\n self.test()\n except Exception as e:\n return 0\n return 1\n\nclass QPrintItem(QItem):\n def compute_answer_print(self):\n """\n Generate output which is to be tested. By default, both text written to the terminal using print(...) as well as return values\n are send to process_output (see compute_answer below). In other words, the text generated is:\n\n res = compute_Answer_print()\n txt = (any terminal output generated above)\n numbers = (any numbers found in terminal-output txt)\n\n self.test(process_output(res, txt, numbers), <expected result>)\n\n :return: Optional values for comparison\n """\n raise Exception("Generate output here. The output is passed to self.process_output")\n\n def process_output(self, res, txt, numbers):\n return res\n\n def compute_answer(self, unmute=False):\n with Capturing(unmute=unmute) as output:\n res = self.compute_answer_print()\n s = "\\n".join(output)\n s = rm_progress_bar(s) # Remove progress bar.\n numbers = extract_numbers(s)\n self._computed_answer = (res, s, numbers)\n return self.process_output(res, s, numbers)\n\nclass OrderedClassMembers(type):\n @classmethod\n def __prepare__(self, name, bases):\n return collections.OrderedDict()\n def __new__(self, name, bases, classdict):\n ks = list(classdict.keys())\n for b in bases:\n ks += b.__ordered__\n classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n return type.__new__(self, name, bases, classdict)\n\nclass QuestionGroup(metaclass=OrderedClassMembers):\n title = "Untitled question"\n partially_scored = False\n t_init = 0 # Time spend on initialization (placeholder; set this externally).\n estimated_time = 0.42\n has_called_init_ = False\n _name = None\n _items = None\n\n @property\n def items(self):\n if self._items == None:\n self._items = []\n members = [gt for gt in [getattr(self, gt) for gt in self.__ordered__ if gt not in ["__classcell__", "__init__"]] if inspect.isclass(gt) and issubclass(gt, QItem)]\n for I in members:\n self._items.append( I(question=self))\n return self._items\n\n @items.setter\n def items(self, value):\n self._items = value\n\n @property\n def name(self):\n if self._name == None:\n self._name = self.__class__.__name__\n return self._name #\n\n @name.setter\n def name(self, val):\n self._name = val\n\n def init(self):\n # Can be used to set resources relevant for this question instance.\n pass\n\n def init_all_item_questions(self):\n for item in self.items:\n if not item.question.has_called_init_:\n item.question.init()\n item.question.has_called_init_ = True\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 80 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q,_) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q,_) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n # else:\n # if os.path.isfile(self.computed_answers_file):\n # self.set_payload(cache_read(self.computed_answers_file), strict=strict)\n # else:\n # s = f"> Warning: The pre-computed answer file, {os.path.abspath(self.computed_answers_file)} is missing. The framework will NOT work as intended. Reasons may be a broken local installation."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n import unittest\n loader = unittest.TestLoader()\n for q,_ in self.questions:\n import time\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self):\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\':True}\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct+1)\n if i > 0 and l.find("|", i+1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = \'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar",show_progress_bar=True):\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.1\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n\n # self.pbar = tqdm.tqdm(total=self.n)\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if hasattr(self, \'pbar\') and self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar=None\n\n sys.stdout.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=sys.stdout, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\') # , unit_scale=dt, unit=\'seconds\'):\n\n for _ in range(self.n-1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\n\n# class MySuite(unittest.suite.TestSuite): # Not sure we need this one anymore.\n# raise Exception("no suite")\n# pass\n\ndef instance_call_stack(instance):\n s = "-".join(map(lambda x: x.__name__, instance.__class__.mro()))\n return s\n\ndef get_class_that_defined_method(meth):\n for cls in inspect.getmro(meth.im_class):\n if meth.__name__ in cls.__dict__:\n return cls\n return None\n\ndef caller_name(skip=2):\n """Get a name of a caller in the format module.class.method\n\n `skip` specifies how many levels of stack to skip while getting caller\n name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.\n\n An empty string is returned if skipped levels exceed stack height\n """\n stack = inspect.stack()\n start = 0 + skip\n if len(stack) < start + 1:\n return \'\'\n parentframe = stack[start][0]\n\n name = []\n module = inspect.getmodule(parentframe)\n # `modname` can be None when frame is executed directly in console\n # TODO(techtonik): consider using __main__\n if module:\n name.append(module.__name__)\n # detect classname\n if \'self\' in parentframe.f_locals:\n # I don\'t know any way to detect call from the object method\n # XXX: there seems to be no way to detect static method call - it will\n # be just a function call\n name.append(parentframe.f_locals[\'self\'].__class__.__name__)\n codename = parentframe.f_code.co_name\n if codename != \'<module>\': # top level usually\n name.append( codename ) # function or a method\n\n ## Avoid circular refs and frame leaks\n # https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack\n del parentframe, stack\n\n return ".".join(name)\n\ndef get_class_from_frame(fr):\n\n args, _, _, value_dict = inspect.getargvalues(fr)\n # we check the first parameter for the frame function is\n # named \'self\'\n if len(args) and args[0] == \'self\':\n # in that case, \'self\' will be referenced in value_dict\n instance = value_dict.get(\'self\', None)\n if instance:\n # return its class\n # isinstance(instance, Testing) # is the actual class instance.\n\n return getattr(instance, \'__class__\', None)\n # return None otherwise\n return None\n\nfrom typing import Any\nimport inspect, gc\n\ndef giveupthefunc():\n frame = inspect.currentframe()\n code = frame.f_code\n globs = frame.f_globals\n functype = type(lambda: 0)\n funcs = []\n for func in gc.get_referrers(code):\n if type(func) is functype:\n if getattr(func, "__code__", None) is code:\n if getattr(func, "__globals__", None) is globs:\n funcs.append(func)\n if len(funcs) > 1:\n return None\n return funcs[0] if funcs else None\n\n\nfrom collections import defaultdict\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n # if self.dots or self.showAll:\n # self.stream.writeln()\n # if hasattr(self, \'cc\'):\n # self.cc.terminate()\n # self.cc_terminate(success=False)\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n # if self.showAll:\n # self.stream.writeln("FAIL")\n # elif self.dots:\n # self.stream.write(\'F\')\n # self.stream.flush()\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n # super().addSuccess(test)\n self.successes.append(test)\n # super().addSuccess(test)\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n self.cc_terminate()\n\n\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n sys.stdout.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="")\n else:\n print( dot_parts, end="")\n\n if tsecs >= 0.1:\n state += " (" + str(tsecs) + " seconds)"\n print(state)\n\n\n def startTest(self, test):\n # super().startTest(test)\n j =self.testsRun\n self.testsRun += 1\n # print("Starting the test...")\n # show_progress_bar = True\n n = UTextResult.number\n\n item_title = self.getDescription(test)\n # item_title = item_title.split("\\n")[0]\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n # test.countTestCases()\n self.item_title_print = "*** q%i.%i) %s" % (n + 1, j + 1, item_title)\n estimated_time = 10\n nL = 80\n #\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar)\n else:\n print(self.item_title_print + (\'.\' * max(0, nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n # q_title_print = "some printed title..."\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass == None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n # start = 10\n # q_time = np.round(time.time() - start, 2)\n nL = 80\n print(" " * max(0, nL - len(self.cc.title)) + (\n " (" + str(q_time) + " seconds)" if q_time >= 0.1 else "")) # if q.name in report.payloads else "")\n # print("=" * nL)\n\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n from io import StringIO\n stream = StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n# def wrapper(foo):\n# def magic(self):\n# # s = "-".join(map(lambda x: x.__name__, self.__class__.mro()))\n# foo(self)\n# magic.__doc__ = foo.__doc__\n# return magic\n\nfrom functools import update_wrapper, _make_key, RLock\nfrom collections import namedtuple\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)) )\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n return wrapper\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n\n def capture(self):\n return Capturing2(stdout=self._stdout)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ != None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n self._stdout = sys.stdout\n import io\n sys.stdout = io.StringIO()\n super().setUp()\n # print("Setting up...")\n\n def _callTearDown(self):\n sys.stdout = self._stdout\n super().tearDown()\n # print("asdfsfd")\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd == None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n # self._testMethodDoc.strip().splitlines()[0].strip()\n sd = self.shortDescriptionStandard()\n title = self._cache_get( (self.cache_id(), \'title\'), sd )\n return title if title != None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome == None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc != None:\n # Ensure the cache is eventually updated with the right docstring.\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard() )\n # Fix temp cache here (for using the @cache decorator)\n self._cache2[ (self.cache_id(), \'assert\') ] = {}\n\n res = testMethod()\n elapsed = time.time() - t\n # self._cache_put( (self.cache_id(), \'title\'), self.shortDescription() )\n\n self._get_outcome()[self.cache_id()] = res\n self._cache_put( (self.cache_id(), "time"), elapsed)\n\n # This is my base test class. So what is new about it?\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return (c,m)\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n # self.cache_indexes = defaultdict(lambda: 0)\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n cache = self._cache_get(key, {})\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, first)\n assert_fun(first, _expected, *args, **kwargs)\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__) ) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache != None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n # print("Loading cache from", cfile)\n if os.path.exists(cfile):\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n else:\n print("Warning! data file not found", cfile)\n\ndef hide(func):\n return func\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n # (*)We can be somewhat "hygienic", but newDecorator still isn\'t signature-preserving, i.e. you will not be able to get a runtime list of parameters. For that, you need hackish libraries...but in this case, the only argument is func, so it\'s not a big issue\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\n# from unitgrade2.unitgrade2 import MySuite\n\nimport inspect\nimport os\nimport argparse\nimport sys\nimport time\nimport threading # don\'t import Thread bc. of minify issue.\nimport tqdm # don\'t do from tqdm import tqdm because of minify-issue\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n print(b + " v" + __version__)\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print("Evaluating " + s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n nL = 80\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n # q = q()\n # q_hidden = False\n # q_hidden = issubclass(q.__class__, Hidden)\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n # unittest.Te\n # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_]\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n # possible = int(ws @ possible)\n # obtained = int(ws @ obtained)\n # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"*** Question q{n+1}"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n print(" ")\n print("="*n)\n print("Final evaluation")\n print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f"*** {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single file: ")\n print(">", token)\n print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\nfrom homework1 import reverse_list, add\nimport unittest\n\nclass Week1(unittest.TestCase):\n def test_add(self):\n self.assertEqual(add(2,2), 4)\n self.assertEqual(add(-100, 5), -95)\n\n def test_reverse(self):\n self.assertEqual(reverse_list([1,2,3]), [3,2,1])\n\n\nimport homework1\nclass Report1Flat(Report):\n title = "CS 101 Report 1"\n questions = [(Week1, 10)] # Include a single question for 10 credits.\n pack_imports = [homework1]' +report1_payload = '8004953f000000000000007d948c055765656b31947d948c2c6e6f20636163686520736565205f73657475705f616e737765727320696e20756e69746772616465322e7079948873732e' +name="Report1Flat" + +report = source_instantiate(name, report1_source, report1_payload) +output_dir = os.path.dirname(__file__) +gather_upload_to_campusnet(report, output_dir) diff --git a/examples/example_framework/instructor/cs102/.coverage b/examples/example_framework/instructor/cs102/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..a93b4d7e94a84f8ad080beeafaa98b3784e23e91 GIT binary patch literal 53248 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 literal 0 HcmV?d00001 diff --git a/examples/example_framework/instructor/cs102/Report2_handin_10_of_18.token b/examples/example_framework/instructor/cs102/Report2_handin_10_of_18.token deleted file mode 100644 index 3dc602acc93517ba2a3211a3f926b2dfe0cf8c90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80985 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 diff --git a/examples/example_framework/instructor/cs102/Report2_handin_13_of_18.token b/examples/example_framework/instructor/cs102/Report2_handin_13_of_18.token new file mode 100644 index 0000000000000000000000000000000000000000..303223eab586229ad1fd65b48a7b96322ded0274 GIT binary patch literal 60507 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 literal 0 HcmV?d00001 diff --git a/examples/example_framework/instructor/cs102/Report2_handin_18_of_18.token b/examples/example_framework/instructor/cs102/Report2_handin_18_of_18.token index 01a473c0bc4e8c0b536bb6c2b01d901ca33c5689..fe0c87cc2b34d3770b07ece28291836a66a6d7ed 100644 GIT binary patch delta 9214 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 GrFsAsXa{Zp delta 27029 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ɔ?;uAatN`&KAq}?37u>Q< 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 DHviow diff --git a/examples/example_framework/instructor/cs102/Report2_handin_28_of_28.token b/examples/example_framework/instructor/cs102/Report2_handin_28_of_28.token deleted file mode 100644 index 4fe9c89fea3b77998c9201c910a729cc1eb16d89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81409 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 Pu|K7U6=F_laj6~vkj0d4 diff --git a/examples/example_framework/instructor/cs102/Report2_handin_5_of_18.token b/examples/example_framework/instructor/cs102/Report2_handin_5_of_18.token deleted file mode 100644 index e22d430bac7ac0c21c0931fd87cd6871fa95b8c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78385 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�A_XIL50PCb6s{jpD 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 E0PrUs$N&HU diff --git a/examples/example_framework/instructor/cs102/Report2_handin_5_of_28.token b/examples/example_framework/instructor/cs102/Report2_handin_5_of_28.token deleted file mode 100644 index cb4ed5d6abfec70f738f205440eba8f73c909f7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80005 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≺a010Hgc8PqF?doD}zA!A~wUH}#U5X(Tx6jctK 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 GrFsDPGTn0k diff --git a/examples/example_framework/instructor/cs102/__pycache__/deploy.cpython-38.pyc b/examples/example_framework/instructor/cs102/__pycache__/deploy.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d9b529d6cae3250756694b95ca75ce15b33bf86 GIT binary patch literal 760 zcmWIL<>g{vU|@J4te5D?#K7<v#6iaF3=9ko3=9m#9SjT%DGVu$ISjdsQH+crHd78$ zE^`z!n9ZESlFJ&!3TCt9u;sExv4h#HIUG?OsT^6H3%F9b7cxfir1GY)Wiu7Eq_U<m zHZwFcGBTtxr!uCngCR#R3nN1+dn!)~r#M3@PYRbfLkf2aPYQ1eUpjLNKS(x(HJCwD z;3de7nvA#DgHj9fi%N_%8E=Ufr<Rl!#HSY}rliKFW#**D7lGw88E;9YCzfQS7R8qq z<m4x&#Fym9Cnx3>loscumS{5G;wa9`EQn9ZEb_a>5g(tMn3)$J@21Hd#g?0ymzh=> z#hIU!R$82#SdtpWo?4NbTw0P^#i_5Sr=MJGXkb(&3S#Q%=@*xjrljVT6hnkVZgI!Q z7gQ!ECTFC^$5*jy>6PSXr{-00rlc0+<X7qyRBEzBaTVv67A2>G?2h6{Ni8k`vqY0~ zQWNvyLHx|T#FEVXJg_i(YDIERX-cXlUlcFcW<7}ijEa~T7#O0|O7k*HAf7KM$}CGP zNj1{T$V^E|&5KViN=+<*gh_D`Gf1&IZpA1;Rm1{PEdUCjg47~C5Djum5i0`&!!4%# z;#({Qi6t4g*i$l#@)C1XLE#4u`S>WN@)QscBpM&TlA(x~fdNANig&h(2`x@7Dvl{B z%}9-L$xklL1%+D-hMQtiD-v@Ha#D+7jQF&o#N5>K{G#lb%)H`~qSEA&{Gu3eV8p<~ k^%jRsZhlH>PO2RvDBTJ%Ffi~iLLdht4-*F?7oz|p0DCy)i2wiq literal 0 HcmV?d00001 diff --git a/examples/example_framework/instructor/cs102/__pycache__/homework1.cpython-38.pyc b/examples/example_framework/instructor/cs102/__pycache__/homework1.cpython-38.pyc index aca3c8b22c11ae4d9d824d7ba252b4194f5db12f..d67337369ba5bf909f1eb07c3dda178779750fb2 100644 GIT binary patch delta 20 bcmX@Yc7%;Pl$V!_fq{XcM_6wow<R+GE(`<S delta 20 bcmX@Yc7%;Pl$V!_fq{YH<6)hR+?LD$H01>G diff --git a/examples/example_framework/instructor/cs102/__pycache__/report2_grade.cpython-38.pyc b/examples/example_framework/instructor/cs102/__pycache__/report2_grade.cpython-38.pyc index 5fb59bc9a735c474aff24e25cd7c318cc0869f8d..42fb3a4a526346eae8494f38ac29d254b5f30b83 100644 GIT binary patch delta 3461 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 H`TtS?CbZ_| delta 7469 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 diff --git a/examples/example_framework/instructor/cs102/deploy.py b/examples/example_framework/instructor/cs102/deploy.py index c585908..4e47e5e 100644 --- a/examples/example_framework/instructor/cs102/deploy.py +++ b/examples/example_framework/instructor/cs102/deploy.py @@ -2,8 +2,10 @@ from cs102.report2 import Report2 from unitgrade_private2.hidden_create_files import setup_grade_file_report from unitgrade_private2.hidden_gather_upload import gather_upload_to_campusnet from snipper.snip_dir import snip_dir -if __name__ == "__main__": +import os +wd = os.path.dirname(__file__) +if __name__ == "__main__": gather_upload_to_campusnet(Report2()) setup_grade_file_report(Report2, minify=False, obfuscate=False, execute=False) - snip_dir(source_dir="../cs102", dest_dir="../../students/cs102", clean_destination_dir=True, exclude=['__pycache__', '*.token', 'deploy.py']) + snip_dir(source_dir=wd+"/../cs102", dest_dir=wd+"/../../students/cs102", clean_destination_dir=True, exclude=['__pycache__', '*.token', 'deploy.py']) diff --git a/examples/example_framework/instructor/cs102/report2.py b/examples/example_framework/instructor/cs102/report2.py index 381cdb1..c7be1cd 100644 --- a/examples/example_framework/instructor/cs102/report2.py +++ b/examples/example_framework/instructor/cs102/report2.py @@ -1,6 +1,7 @@ -from unitgrade2.unitgrade2 import Report -from unitgrade2.unitgrade_helpers2 import evaluate_report_student -from unitgrade2.unitgrade2 import UTestCase, cache, hide +from src.unitgrade2.unitgrade2 import Report +from src.unitgrade2 import evaluate_report_student +from src.unitgrade2.unitgrade2 import UTestCase, cache + class Week1(UTestCase): """ The first question for week 1. """ @@ -8,9 +9,6 @@ class Week1(UTestCase): """ Docstring for this method """ from cs102.homework1 import add self.assertEqualC(add(2,2)) - with self.capture() as out: - print("hello world 42") - self.assertEqual(out.numbers[0], 42) self.assertEqualC(add(-100, 5)) def test_reverse(self): @@ -18,6 +16,12 @@ class Week1(UTestCase): from cs102.homework1 import reverse_list self.assertEqualC(reverse_list([1,2,3])) + def test_output_capture(self): + with self.capture() as out: + print("hello world 42") # Genereate some output (i.e. in a homework script) + self.assertEqual(out.numbers[0], 42) # out.numbers is a list of all numbers generated + self.assertEqual(out.output, "hello world 42") # you can also access the raw output. + class Question2(UTestCase): """ Second problem """ diff --git a/examples/example_framework/instructor/cs102/report2_grade.py b/examples/example_framework/instructor/cs102/report2_grade.py index 503237e..eeb50ec 100644 --- a/examples/example_framework/instructor/cs102/report2_grade.py +++ b/examples/example_framework/instructor/cs102/report2_grade.py @@ -4,14 +4,10 @@ from tabulate import tabulate from datetime import datetime import pyfiglet import unittest - import inspect import os import argparse -import sys import time -import threading # don't import Thread bc. of minify issue. -import tqdm # don't do from tqdm import tqdm because of minify-issue parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: To run all tests in a report: @@ -61,53 +57,6 @@ def evaluate_report_student(report, question=None, qitem=None, unmute=None, pass show_tol_err=show_tol_err) - # try: # For registering stats. - # import unitgrade_private - # import irlc.lectures - # import xlwings - # from openpyxl import Workbook - # import pandas as pd - # from collections import defaultdict - # dd = defaultdict(lambda: []) - # error_computed = [] - # for k1, (q, _) in enumerate(report.questions): - # for k2, item in enumerate(q.items): - # dd['question_index'].append(k1) - # dd['item_index'].append(k2) - # dd['question'].append(q.name) - # dd['item'].append(item.name) - # dd['tol'].append(0 if not hasattr(item, 'tol') else item.tol) - # error_computed.append(0 if not hasattr(item, 'error_computed') else item.error_computed) - # - # qstats = report.wdir + "/" + report.name + ".xlsx" - # - # if os.path.isfile(qstats): - # d_read = pd.read_excel(qstats).to_dict() - # else: - # d_read = dict() - # - # for k in range(1000): - # key = 'run_'+str(k) - # if key in d_read: - # dd[key] = list(d_read['run_0'].values()) - # else: - # dd[key] = error_computed - # break - # - # workbook = Workbook() - # worksheet = workbook.active - # for col, key in enumerate(dd.keys()): - # worksheet.cell(row=1, column=col+1).value = key - # for row, item in enumerate(dd[key]): - # worksheet.cell(row=row+2, column=col+1).value = item - # - # workbook.save(qstats) - # workbook.close() - # - # except ModuleNotFoundError as e: - # s = 234 - # pass - if question is None: print("Provisional evaluation") tabulate(table_data) @@ -159,24 +108,20 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) else: b = "Unitgrade" - print(b + " v" + __version__) dt_string = now.strftime("%d/%m/%Y %H:%M:%S") - print("Started: " + dt_string) + print(b + " v" + __version__ + ", started: " + dt_string+ "\n") + # print("Started: " + dt_string) s = report.title if hasattr(report, "version") and report.version is not None: s += " version " + report.version - print("Evaluating " + s, "(use --help for options)" if show_help_flag else "") + print(s, "(use --help for options)" if show_help_flag else "") # print(f"Loaded answers from: ", report.computed_answers_file, "\n") table_data = [] - nL = 80 t_start = time.time() score = {} loader = SequentialTestLoader() for n, (q, w) in enumerate(report.questions): - # q = q() - # q_hidden = False - # q_hidden = issubclass(q.__class__, Hidden) if question is not None and n+1 != question: continue suite = loader.loadTestsFromTestCase(q) @@ -186,104 +131,28 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa q.possible = 0 q.obtained = 0 q_ = {} # Gather score in this class. - # unittest.Te - # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_] UTextResult.q_title_print = q_title_print # Hacky UTextResult.show_progress_bar = show_progress_bar # Hacky. UTextResult.number = n + UTextResult.nL = report.nL res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) - # res = UTextTestRunner(verbosity=2, resultclass=unittest.TextTestResult).run(suite) - z = 234 - # for j, item in enumerate(q.items): - # if qitem is not None and question is not None and j+1 != qitem: - # continue - # - # if q_with_outstanding_init is not None: # check for None bc. this must be called to set titles. - # # if not item.question.has_called_init_: - # start = time.time() - # - # cc = None - # if show_progress_bar: - # total_estimated_time = q.estimated_time # Use this. The time is estimated for the q itself. # sum( [q2.estimated_time for q2 in q_with_outstanding_init] ) - # cc = ActiveProgress(t=total_estimated_time, title=q_title_print) - # from unitgrade import Capturing # DON'T REMOVE THIS LINE - # with eval('Capturing')(unmute=unmute): # Clunky import syntax is required bc. of minify issue. - # try: - # for q2 in q_with_outstanding_init: - # q2.init() - # q2.has_called_init_ = True - # - # # item.question.init() # Initialize the question. Useful for sharing resources. - # except Exception as e: - # if not passall: - # if not silent: - # print(" ") - # print("="*30) - # print(f"When initializing question {q.title} the initialization code threw an error") - # print(e) - # print("The remaining parts of this question will likely fail.") - # print("="*30) - # - # if show_progress_bar: - # cc.terminate() - # sys.stdout.flush() - # print(q_title_print, end="") - # - # q_time =np.round( time.time()-start, 2) - # - # print(" "* max(0,nL - len(q_title_print) ) + (" (" + str(q_time) + " seconds)" if q_time >= 0.1 else "") ) # if q.name in report.payloads else "") - # print("=" * nL) - # q_with_outstanding_init = None - # - # # item.question = q # Set the parent question instance for later reference. - # item_title_print = ss = "*** q%i.%i) %s"%(n+1, j+1, item.title) - # - # if show_progress_bar: - # cc = ActiveProgress(t=item.estimated_time, title=item_title_print) - # else: - # print(item_title_print + ( '.'*max(0, nL-4-len(ss)) ), end="") - # hidden = issubclass(item.__class__, Hidden) - # # if not hidden: - # # print(ss, end="") - # # sys.stdout.flush() - # start = time.time() - # - # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent) - # q_[j] = {'w': item.weight, 'possible': possible, 'obtained': current, 'hidden': hidden, 'computed': str(item._computed_answer), 'title': item.title} - # tsecs = np.round(time.time()-start, 2) - # if show_progress_bar: - # cc.terminate() - # sys.stdout.flush() - # print(item_title_print + ('.' * max(0, nL - 4 - len(ss))), end="") - # - # if not hidden: - # ss = "PASS" if current == possible else "*** FAILED" - # if tsecs >= 0.1: - # ss += " ("+ str(tsecs) + " seconds)" - # print(ss) - - # ws, possible, obtained = upack(q_) possible = res.testsRun obtained = len(res.successes) assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun - # possible = int(ws @ possible) - # obtained = int(ws @ obtained) - # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0 - obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} q.obtained = obtained q.possible = possible - s1 = f"*** Question q{n+1}" + s1 = f"Question {n+1} total" s2 = f" {q.obtained}/{w}" - print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 ) + print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 ) print(" ") - table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"]) + table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"]) ws, possible, obtained = upack(score) possible = int( msum(possible) ) @@ -298,15 +167,16 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa seconds = dt - minutes*60 plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") - print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")") + dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", + last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) + + # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total") table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) results = {'total': (obtained, possible), 'details': score} return results, table_data - - from tabulate import tabulate from datetime import datetime import inspect @@ -329,7 +199,8 @@ def gather_imports(imp): # dn = os.path.dirname(f) # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) # top_package = str(__import__(m.__name__.split('.')[0]).__path__) - if m.__class__.__name__ == 'module' and False: + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: top_package = os.path.dirname(m.__file__) module_import = True else: @@ -350,7 +221,7 @@ def gather_imports(imp): for file in files: if file.endswith(".py"): fpath = os.path.join(root, file) - v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package)) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) zip.write(fpath, v) resources['zipfile'] = zip_buffer.getvalue() @@ -394,14 +265,14 @@ def gather_upload_to_campusnet(report, output_dir=None): results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, show_progress_bar=not args.noprogress, big_header=not args.autolab) - print(" ") - print("="*n) - print("Final evaluation") - print(tabulate(table_data)) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) # also load the source code of missing files... sources = {} - + print("") if not args.autolab: if len(report.individual_imports) > 0: print("By uploading the .token file, you verify the files:") @@ -414,12 +285,15 @@ def gather_upload_to_campusnet(report, output_dir=None): print("Including files in upload...") for k, m in enumerate(report.pack_imports): nimp, top_package = gather_imports(m) - report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import nimp['name'] = m.__name__ sources[k] = nimp # if len([k for k in nimp if k not in sources]) > 0: - print(f"*** {m.__name__}") + print(f" * {m.__name__}") # sources = {**sources, **nimp} results['sources'] = sources @@ -438,9 +312,9 @@ def gather_upload_to_campusnet(report, output_dir=None): if not args.autolab: print(" ") - print("To get credit for your results, please upload the single file: ") + print("To get credit for your results, please upload the single unmodified file: ") print(">", token) - print("To campusnet without any modifications.") + # print("To campusnet without any modifications.") # print("Now time for some autolab fun") @@ -453,8 +327,8 @@ def source_instantiate(name, report1_source, payload): -report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n\n"""\n# from . import cache_read\nimport unittest\nimport numpy as np\nimport sys\nfrom io import StringIO\nimport collections\nimport re\nimport threading\nimport tqdm\nimport time\nimport pickle\nimport itertools\nimport os\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\ndef setup_dir_by_class(C,base_dir):\n name = C.__class__.__name__\n # base_dir = os.path.join(base_dir, name)\n # if not os.path.isdir(base_dir):\n # os.makedirs(base_dir)\n return base_dir, name\n\nclass Hidden:\n def hide(self):\n return True\n\nclass Logger(object):\n def __init__(self, buffer):\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\nclass Capturing(list):\n def __init__(self, *args, unmute=False, **kwargs):\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass QItem(unittest.TestCase):\n title = None\n testfun = None\n tol = 0\n estimated_time = 0.42\n _precomputed_payload = None\n _computed_answer = None # Internal helper to later get results.\n weight = 1 # the weight of the question.\n\n def __init__(self, question=None, *args, **kwargs):\n if self.tol > 0 and self.testfun is None:\n self.testfun = self.assertL2Relative\n elif self.testfun is None:\n self.testfun = self.assertEqual\n\n self.name = self.__class__.__name__\n # self._correct_answer_payload = correct_answer_payload\n self.question = question\n\n super().__init__(*args, **kwargs)\n if self.title is None:\n self.title = self.name\n\n def _safe_get_title(self):\n if self._precomputed_title is not None:\n return self._precomputed_title\n return self.title\n\n def assertNorm(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed).flat- np.asarray(expected)).flat )\n nrm = np.sqrt(np.sum( diff ** 2))\n\n self.error_computed = nrm\n\n if nrm > tol:\n print(f"Not equal within tolerance {tol}; norm of difference was {nrm}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def assertL2(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n self.error_computed = np.max(diff)\n\n if np.max(diff) > tol:\n print(f"Not equal within tolerance {tol=}; deviation was {np.max(diff)=}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol=}, {np.max(diff)=}")\n\n def assertL2Relative(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n diff = diff / (1e-8 + np.abs( (np.asarray(computed) + np.asarray(expected)) ) )\n self.error_computed = np.max(np.abs(diff))\n if np.sum(diff > tol) > 0:\n print(f"Not equal within tolerance {tol}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def precomputed_payload(self):\n return self._precomputed_payload\n\n def precompute_payload(self):\n # Pre-compute resources to include in tests (useful for getting around rng).\n pass\n\n def compute_answer(self, unmute=False):\n raise NotImplementedError("test code here")\n\n def test(self, computed, expected):\n self.testfun(computed, expected)\n\n def get_points(self, verbose=False, show_expected=False, show_computed=False,unmute=False, passall=False, silent=False, **kwargs):\n possible = 1\n computed = None\n def show_computed_(computed):\n print(">>> Your output:")\n print(computed)\n\n def show_expected_(expected):\n print(">>> Expected output (note: may have been processed; read text script):")\n print(expected)\n\n correct = self._correct_answer_payload\n try:\n if unmute: # Required to not mix together print stuff.\n print("")\n computed = self.compute_answer(unmute=unmute)\n except Exception as e:\n if not passall:\n if not silent:\n print("\\n=================================================================================")\n print(f"When trying to run test class \'{self.name}\' your code threw an error:", e)\n show_expected_(correct)\n import traceback\n print(traceback.format_exc())\n print("=================================================================================")\n return (0, possible)\n\n if self._computed_answer is None:\n self._computed_answer = computed\n\n if show_expected or show_computed:\n print("\\n")\n if show_expected:\n show_expected_(correct)\n if show_computed:\n show_computed_(computed)\n try:\n if not passall:\n self.test(computed=computed, expected=correct)\n except Exception as e:\n if not silent:\n print("\\n=================================================================================")\n print(f"Test output from test class \'{self.name}\' does not match expected result. Test error:")\n print(e)\n show_computed_(computed)\n show_expected_(correct)\n return (0, possible)\n return (1, possible)\n\n def score(self):\n try:\n self.test()\n except Exception as e:\n return 0\n return 1\n\nclass QPrintItem(QItem):\n def compute_answer_print(self):\n """\n Generate output which is to be tested. By default, both text written to the terminal using print(...) as well as return values\n are send to process_output (see compute_answer below). In other words, the text generated is:\n\n res = compute_Answer_print()\n txt = (any terminal output generated above)\n numbers = (any numbers found in terminal-output txt)\n\n self.test(process_output(res, txt, numbers), <expected result>)\n\n :return: Optional values for comparison\n """\n raise Exception("Generate output here. The output is passed to self.process_output")\n\n def process_output(self, res, txt, numbers):\n return res\n\n def compute_answer(self, unmute=False):\n with Capturing(unmute=unmute) as output:\n res = self.compute_answer_print()\n s = "\\n".join(output)\n s = rm_progress_bar(s) # Remove progress bar.\n numbers = extract_numbers(s)\n self._computed_answer = (res, s, numbers)\n return self.process_output(res, s, numbers)\n\nclass OrderedClassMembers(type):\n @classmethod\n def __prepare__(self, name, bases):\n return collections.OrderedDict()\n def __new__(self, name, bases, classdict):\n ks = list(classdict.keys())\n for b in bases:\n ks += b.__ordered__\n classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n return type.__new__(self, name, bases, classdict)\n\nclass QuestionGroup(metaclass=OrderedClassMembers):\n title = "Untitled question"\n partially_scored = False\n t_init = 0 # Time spend on initialization (placeholder; set this externally).\n estimated_time = 0.42\n has_called_init_ = False\n _name = None\n _items = None\n\n @property\n def items(self):\n if self._items == None:\n self._items = []\n members = [gt for gt in [getattr(self, gt) for gt in self.__ordered__ if gt not in ["__classcell__", "__init__"]] if inspect.isclass(gt) and issubclass(gt, QItem)]\n for I in members:\n self._items.append( I(question=self))\n return self._items\n\n @items.setter\n def items(self, value):\n self._items = value\n\n @property\n def name(self):\n if self._name == None:\n self._name = self.__class__.__name__\n return self._name #\n\n @name.setter\n def name(self, val):\n self._name = val\n\n def init(self):\n # Can be used to set resources relevant for this question instance.\n pass\n\n def init_all_item_questions(self):\n for item in self.items:\n if not item.question.has_called_init_:\n item.question.init()\n item.question.has_called_init_ = True\n\n\nclass Report():\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 80 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q,_) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n root_dir = self.pack_imports[0].__path__._path[0]\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q,_) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n # else:\n # if os.path.isfile(self.computed_answers_file):\n # self.set_payload(cache_read(self.computed_answers_file), strict=strict)\n # else:\n # s = f"> Warning: The pre-computed answer file, {os.path.abspath(self.computed_answers_file)} is missing. The framework will NOT work as intended. Reasons may be a broken local installation."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n import unittest\n loader = unittest.TestLoader()\n for q,_ in self.questions:\n import time\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self):\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\':True}\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n # for item in q.items:\n # if q.name not in payloads or item.name not in payloads[q.name]:\n # s = f"> Broken resource dictionary submitted to unitgrade for question {q.name} and subquestion {item.name}. Framework will not work."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n # else:\n # item._correct_answer_payload = payloads[q.name][item.name][\'payload\']\n # item.estimated_time = payloads[q.name][item.name].get("time", 1)\n # q.estimated_time = payloads[q.name].get("time", 1)\n # if "precomputed" in payloads[q.name][item.name]: # Consider removing later.\n # item._precomputed_payload = payloads[q.name][item.name][\'precomputed\']\n # try:\n # if "title" in payloads[q.name][item.name]: # can perhaps be removed later.\n # item.title = payloads[q.name][item.name][\'title\']\n # except Exception as e: # Cannot set attribute error. The title is a function (and probably should not be).\n # pass\n # # print("bad", e)\n # self.payloads = payloads\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct+1)\n if i > 0 and l.find("|", i+1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = \'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar",show_progress_bar=True):\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.1\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n\n # self.pbar = tqdm.tqdm(total=self.n)\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if hasattr(self, \'pbar\') and self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar=None\n\n sys.stdout.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=sys.stdout, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\') # , unit_scale=dt, unit=\'seconds\'):\n\n for _ in range(self.n-1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\n\n\nfrom unittest.suite import _isnotsuite\n\nclass MySuite(unittest.suite.TestSuite): # Not sure we need this one anymore.\n pass\n\ndef instance_call_stack(instance):\n s = "-".join(map(lambda x: x.__name__, instance.__class__.mro()))\n return s\n\ndef get_class_that_defined_method(meth):\n for cls in inspect.getmro(meth.im_class):\n if meth.__name__ in cls.__dict__:\n return cls\n return None\n\ndef caller_name(skip=2):\n """Get a name of a caller in the format module.class.method\n\n `skip` specifies how many levels of stack to skip while getting caller\n name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.\n\n An empty string is returned if skipped levels exceed stack height\n """\n stack = inspect.stack()\n start = 0 + skip\n if len(stack) < start + 1:\n return \'\'\n parentframe = stack[start][0]\n\n name = []\n module = inspect.getmodule(parentframe)\n # `modname` can be None when frame is executed directly in console\n # TODO(techtonik): consider using __main__\n if module:\n name.append(module.__name__)\n # detect classname\n if \'self\' in parentframe.f_locals:\n # I don\'t know any way to detect call from the object method\n # XXX: there seems to be no way to detect static method call - it will\n # be just a function call\n name.append(parentframe.f_locals[\'self\'].__class__.__name__)\n codename = parentframe.f_code.co_name\n if codename != \'<module>\': # top level usually\n name.append( codename ) # function or a method\n\n ## Avoid circular refs and frame leaks\n # https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack\n del parentframe, stack\n\n return ".".join(name)\n\ndef get_class_from_frame(fr):\n import inspect\n args, _, _, value_dict = inspect.getargvalues(fr)\n # we check the first parameter for the frame function is\n # named \'self\'\n if len(args) and args[0] == \'self\':\n # in that case, \'self\' will be referenced in value_dict\n instance = value_dict.get(\'self\', None)\n if instance:\n # return its class\n # isinstance(instance, Testing) # is the actual class instance.\n\n return getattr(instance, \'__class__\', None)\n # return None otherwise\n return None\n\nfrom typing import Any\nimport inspect, gc\n\ndef giveupthefunc():\n frame = inspect.currentframe()\n code = frame.f_code\n globs = frame.f_globals\n functype = type(lambda: 0)\n funcs = []\n for func in gc.get_referrers(code):\n if type(func) is functype:\n if getattr(func, "__code__", None) is code:\n if getattr(func, "__globals__", None) is globs:\n funcs.append(func)\n if len(funcs) > 1:\n return None\n return funcs[0] if funcs else None\n\n\nfrom collections import defaultdict\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n # if self.dots or self.showAll:\n # self.stream.writeln()\n # if hasattr(self, \'cc\'):\n # self.cc.terminate()\n # self.cc_terminate(success=False)\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n # if self.showAll:\n # self.stream.writeln("FAIL")\n # elif self.dots:\n # self.stream.write(\'F\')\n # self.stream.flush()\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n # super().addSuccess(test)\n self.successes.append(test)\n # super().addSuccess(test)\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n self.cc_terminate()\n\n\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n sys.stdout.flush()\n ss = self.item_title_print\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(ss))), end="")\n # current = 1\n # possible = 1\n # current == possible\n ss = "PASS" if success else "FAILED"\n if tsecs >= 0.1:\n ss += " (" + str(tsecs) + " seconds)"\n print(ss)\n\n\n def startTest(self, test):\n # super().startTest(test)\n j =self.testsRun\n self.testsRun += 1\n # print("Starting the test...")\n # show_progress_bar = True\n n = UTextResult.number\n\n item_title = self.getDescription(test)\n item_title = item_title.split("\\n")[0]\n\n item_title = test.shortDescription() # Better for printing (get from cache).\n # test.countTestCases()\n self.item_title_print = "*** q%i.%i) %s" % (n + 1, j + 1, item_title)\n estimated_time = 10\n nL = 80\n #\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar)\n else:\n print(self.item_title_print + (\'.\' * max(0, nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 2\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n # q_title_print = "some printed title..."\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass == None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n print(self.cc.title, end="")\n # start = 10\n # q_time = np.round(time.time() - start, 2)\n nL = 80\n print(" " * max(0, nL - len(self.cc.title)) + (\n " (" + str(q_time) + " seconds)" if q_time >= 0.1 else "")) # if q.name in report.payloads else "")\n # print("=" * nL)\n\nfrom unittest.runner import _WritelnDecorator\nfrom io import StringIO\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n from io import StringIO\n stream = StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\ndef wrapper(foo):\n def magic(self):\n s = "-".join(map(lambda x: x.__name__, self.__class__.mro()))\n # print(s)\n foo(self)\n magic.__doc__ = foo.__doc__\n return magic\n\nfrom functools import update_wrapper, _make_key, RLock\nfrom collections import namedtuple\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)) )\n # key = (self.cache_id(), \'@cache\')\n # if self._cache_contains[key]\n\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n return wrapper\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n\n @classmethod\n def question_title(cls):\n return cls.__doc__.splitlines()[0].strip() if cls.__doc__ != None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd == None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n # self._testMethodDoc.strip().splitlines()[0].strip()\n sd = self.shortDescriptionStandard()\n title = self._cache_get( (self.cache_id(), \'title\'), sd )\n return title if title != None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n # def _callSetUp(self):\n # # Always run before method is called.\n # print("asdf")\n # pass\n # @classmethod\n # def setUpClass(cls):\n # # self._cache_put((self.cache_id(), \'title\'), value)\n # cls.reset()\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome == None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n if self._testMethodDoc != None:\n # Ensure the cache is eventually updated with the right docstring.\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard() )\n # Fix temp cache here (for using the @cache decorator)\n self._cache2[ (self.cache_id(), \'assert\') ] = {}\n\n res = testMethod()\n elapsed = time.time() - t\n # self._cache_put( (self.cache_id(), \'title\'), self.shortDescription() )\n\n self._get_outcome()[self.cache_id()] = res\n self._cache_put( (self.cache_id(), "time"), elapsed)\n\n # This is my base test class. So what is new about it?\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return (c,m)\n\n # def unique_cache_id(self):\n # k0 = self.cache_id()\n # # key = ()\n # i = 0\n # for i in itertools.count():\n # # key = k0 + (i,)\n # if i not in self._cache_get( (k0, \'assert\') ):\n # break\n # return i\n # return key\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n # self.cache_indexes = defaultdict(lambda: 0)\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n #\n # def _cache2_contains(self, key):\n # print("Is this needed?")\n # self._ensure_cache_exists()\n # return key in self.__class__._cache2\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n cache = self._cache_get(key, {})\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, first)\n assert_fun(first, _expected, *args, **kwargs)\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__) ) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache != None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n # print("Loading cache from", cfile)\n if os.path.exists(cfile):\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n else:\n print("Warning! data file not found", cfile)\n\ndef hide(func):\n return func\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n # (*)We can be somewhat "hygienic", but newDecorator still isn\'t signature-preserving, i.e. you will not be able to get a runtime list of parameters. For that, you need hackish libraries...but in this case, the only argument is func, so it\'s not a big issue\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\n\nimport inspect\nimport os\nimport argparse\nimport sys\nimport time\nimport threading # don\'t import Thread bc. of minify issue.\nimport tqdm # don\'t do from tqdm import tqdm because of minify-issue\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n # try: # For registering stats.\n # import unitgrade_private\n # import irlc.lectures\n # import xlwings\n # from openpyxl import Workbook\n # import pandas as pd\n # from collections import defaultdict\n # dd = defaultdict(lambda: [])\n # error_computed = []\n # for k1, (q, _) in enumerate(report.questions):\n # for k2, item in enumerate(q.items):\n # dd[\'question_index\'].append(k1)\n # dd[\'item_index\'].append(k2)\n # dd[\'question\'].append(q.name)\n # dd[\'item\'].append(item.name)\n # dd[\'tol\'].append(0 if not hasattr(item, \'tol\') else item.tol)\n # error_computed.append(0 if not hasattr(item, \'error_computed\') else item.error_computed)\n #\n # qstats = report.wdir + "/" + report.name + ".xlsx"\n #\n # if os.path.isfile(qstats):\n # d_read = pd.read_excel(qstats).to_dict()\n # else:\n # d_read = dict()\n #\n # for k in range(1000):\n # key = \'run_\'+str(k)\n # if key in d_read:\n # dd[key] = list(d_read[\'run_0\'].values())\n # else:\n # dd[key] = error_computed\n # break\n #\n # workbook = Workbook()\n # worksheet = workbook.active\n # for col, key in enumerate(dd.keys()):\n # worksheet.cell(row=1, column=col+1).value = key\n # for row, item in enumerate(dd[key]):\n # worksheet.cell(row=row+2, column=col+1).value = item\n #\n # workbook.save(qstats)\n # workbook.close()\n #\n # except ModuleNotFoundError as e:\n # s = 234\n # pass\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n print(b + " v" + __version__)\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print("Evaluating " + s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n nL = 80\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n # q = q()\n # q_hidden = False\n # q_hidden = issubclass(q.__class__, Hidden)\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n # unittest.Te\n # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_]\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n # res = UTextTestRunner(verbosity=2, resultclass=unittest.TextTestResult).run(suite)\n z = 234\n # for j, item in enumerate(q.items):\n # if qitem is not None and question is not None and j+1 != qitem:\n # continue\n #\n # if q_with_outstanding_init is not None: # check for None bc. this must be called to set titles.\n # # if not item.question.has_called_init_:\n # start = time.time()\n #\n # cc = None\n # if show_progress_bar:\n # total_estimated_time = q.estimated_time # Use this. The time is estimated for the q itself. # sum( [q2.estimated_time for q2 in q_with_outstanding_init] )\n # cc = ActiveProgress(t=total_estimated_time, title=q_title_print)\n # from unitgrade import Capturing # DON\'T REMOVE THIS LINE\n # with eval(\'Capturing\')(unmute=unmute): # Clunky import syntax is required bc. of minify issue.\n # try:\n # for q2 in q_with_outstanding_init:\n # q2.init()\n # q2.has_called_init_ = True\n #\n # # item.question.init() # Initialize the question. Useful for sharing resources.\n # except Exception as e:\n # if not passall:\n # if not silent:\n # print(" ")\n # print("="*30)\n # print(f"When initializing question {q.title} the initialization code threw an error")\n # print(e)\n # print("The remaining parts of this question will likely fail.")\n # print("="*30)\n #\n # if show_progress_bar:\n # cc.terminate()\n # sys.stdout.flush()\n # print(q_title_print, end="")\n #\n # q_time =np.round( time.time()-start, 2)\n #\n # print(" "* max(0,nL - len(q_title_print) ) + (" (" + str(q_time) + " seconds)" if q_time >= 0.1 else "") ) # if q.name in report.payloads else "")\n # print("=" * nL)\n # q_with_outstanding_init = None\n #\n # # item.question = q # Set the parent question instance for later reference.\n # item_title_print = ss = "*** q%i.%i) %s"%(n+1, j+1, item.title)\n #\n # if show_progress_bar:\n # cc = ActiveProgress(t=item.estimated_time, title=item_title_print)\n # else:\n # print(item_title_print + ( \'.\'*max(0, nL-4-len(ss)) ), end="")\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n # if show_progress_bar:\n # cc.terminate()\n # sys.stdout.flush()\n # print(item_title_print + (\'.\' * max(0, nL - 4 - len(ss))), end="")\n #\n # if not hidden:\n # ss = "PASS" if current == possible else "*** FAILED"\n # if tsecs >= 0.1:\n # ss += " ("+ str(tsecs) + " seconds)"\n # print(ss)\n\n # ws, possible, obtained = upack(q_)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n # possible = int(ws @ possible)\n # obtained = int(ws @ obtained)\n # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"*** Question q{n+1}"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n if m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package))\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n print(" ")\n print("="*n)\n print("Final evaluation")\n print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f"*** {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single file: ")\n print(">", token)\n print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\nimport random\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n\n def test_add(self):\n """ Docstring for this method """\n from cs102.homework1 import add\n self.assertEqualC(add(2,2))\n self.assertEqualC(add(-100, 5))\n\n def test_reverse(self):\n """ Reverse a list """ # Add a title to the test.\n from cs102.homework1 import reverse_list\n self.assertEqualC(reverse_list([1,2,3]))\n\n\nclass Question2(UTestCase):\n """ Second problem """\n @cache\n def my_reversal(self, ls):\n # The \'@cache\' decorator ensures the function is not run on the *students* computer\n # Instead the code is run on the teachers computer and the result is passed on with the\n # other pre-computed results -- i.e. this function will run regardless of how the student happens to have\n # implemented reverse_list.\n from cs102.homework1 import reverse_list\n return reverse_list(ls)\n\n def test_reverse_tricky(self):\n ls = [2,4,8]\n self.title = f"Reversing a small list containing {ls=}"\n ls2 = self.my_reversal( tuple(ls) ) # This will always produce the right result.\n ls3 = self.my_reversal( tuple([1,2,3]) ) # Also works; the cache respects input arguments.\n self.assertEqualC(self.my_reversal( tuple(ls2) )) # This will actually test the students code.\n\n\nimport cs102\nclass Report2(Report):\n title = "CS 101 Report 2"\n questions = [(Week1, 10), (Question2, 8) ] # Include a single question for 10 credits.\n pack_imports = [cs102]' -report1_payload = '8004959a010000000000007d94288c055765656b31947d94288c055765656b31948c08746573745f6164649486948c057469746c659486948c19446f63737472696e6720666f722074686973206d6574686f64946803680486948c066173736572749486947d94284b004b044b014aa1ffffff756803680486948c0474696d6594869447000000000000000068038c0c746573745f72657665727365948694680686948c0e526576657273652061206c69737494680368108694680a86947d944b005d94284b034b024b016573680368108694680e86944700000000000000008c0474696d6594470000000000000000758c095175657374696f6e32947d94288c095175657374696f6e32948c13746573745f726576657273655f747269636b799486948c066173736572749486947d944b005d94284b024b044b086573681d681e86948c057469746c659486948c2e526576657273696e67206120736d616c6c206c69737420636f6e7461696e696e67206c733d5b322c20342c20385d94681d681e86948c0474696d65948694470000000000000000681a473f5066000000000075752e' +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n"""\nimport numpy as np\nimport sys\nimport re\nimport threading\nimport tqdm\nimport pickle\nimport os\nfrom io import StringIO\nimport io\nfrom unittest.runner import _WritelnDecorator\nfrom typing import Any\nimport inspect\nimport textwrap\nimport colorama\nfrom colorama import Fore\nfrom functools import _make_key, RLock\nfrom collections import namedtuple\nimport unittest\nimport time\n\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ncolorama.init(autoreset=True) # auto resets your settings after every output\n\ndef gprint(s):\n print(f"{Fore.GREEN}{s}")\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\n\ndef setup_dir_by_class(C, base_dir):\n name = C.__class__.__name__\n return base_dir, name\n\n\nclass Logger(object):\n def __init__(self, buffer):\n assert False\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\n# @classmethod\n# class OrderedClassMembers(type):\n# def __prepare__(self, name, bases):\n# assert False\n# return collections.OrderedDict()\n#\n# def __new__(self, name, bases, classdict):\n# ks = list(classdict.keys())\n# for b in bases:\n# ks += b.__ordered__\n# classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n# return type.__new__(self, name, bases, classdict)\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 120 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q, _) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q, _) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n loader = unittest.TestLoader()\n for q, _ in self.questions:\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self, with_coverage=False):\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = True\n q._report = self\n\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\': True}\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = False\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct + 1)\n if i > 0 and l.find("|", i + 1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = r\'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar", show_progress_bar=True, file=None):\n if file == None:\n file = sys.stdout\n self.file = file\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.01\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n self.pbar = None\n\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar = None\n\n self.file.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=self.file, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\')\n\n for _ in range(self.n - 1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\ndef dprint(first, last, nL, extra = "", file=None, dotsym=\'.\', color=\'white\'):\n if file == None:\n file = sys.stdout\n\n # ss = self.item_title_print\n # state = "PASS" if success else "FAILED"\n dot_parts = (dotsym * max(0, nL - len(last) - len(first)))\n # if self.show_progress_bar or True:\n print(first + dot_parts, end="", file=file)\n # else:\n # print(dot_parts, end="", file=self.cc.file)\n last += extra\n # if tsecs >= 0.5:\n # state += " (" + str(tsecs) + " seconds)"\n print(last, file=file)\n\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n cc = None\n\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n self.successes.append(test)\n self.cc_terminate()\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n self.cc.file.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="", file=self.cc.file)\n else:\n print(dot_parts, end="", file=self.cc.file)\n\n if tsecs >= 0.5:\n state += " (" + str(tsecs) + " seconds)"\n print(state, file=self.cc.file)\n\n def startTest(self, test):\n # j =self.testsRun\n self.testsRun += 1\n # item_title = self.getDescription(test)\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n self.item_title_print = " * q%i.%i) %s" % (UTextResult.number + 1, self.testsRun, item_title)\n estimated_time = 10\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar, file=sys.stdout)\n else:\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n self._stdout = sys.stdout\n sys.stdout = io.StringIO()\n\n def stopTest(self, test):\n sys.stdout = self._stdout\n super().stopTest(test)\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass is None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n print(" " * max(0, self.nL - len(self.cc.title)) + (" (" + str(q_time) + " seconds)" if q_time >= 0.5 else ""))\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n stream = io.StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)))\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n\n return wrapper\n\n\ndef get_hints(ss):\n if ss == None:\n return None\n try:\n ss = textwrap.dedent(ss)\n ss = ss.replace(\'\'\'"""\'\'\', "").strip()\n hints = ["hints:", ]\n j = np.argmax([ss.lower().find(h) for h in hints])\n h = hints[j]\n ss = ss[ss.find(h) + len(h) + 1:]\n ss = "\\n".join([l for l in ss.split("\\n") if not l.strip().startswith(":")])\n ss = textwrap.dedent(ss)\n ss = ss.strip()\n return ss\n except Exception as e:\n print("bad hints", ss, e)\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n _with_coverage = False\n _report = None # The report used. This is very, very hacky and should always be None. Don\'t rely on it!\n\n def capture(self):\n if hasattr(self, \'_stdout\') and self._stdout is not None:\n file = self._stdout\n else:\n file = sys.stdout\n return Capturing2(stdout=file)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ is not None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n if self._with_coverage:\n if not hasattr(self._report, \'covcache\'):\n self._report.covcache = {}\n import coverage\n self.cov = coverage.Coverage()\n self.cov.start()\n self.setUp()\n\n def _callTearDown(self):\n self.tearDown()\n if self._with_coverage:\n from pathlib import Path\n from snipper import snipper\n self.cov.stop()\n data = self.cov.get_data()\n base, _, _ = self._report._import_base_relative()\n for file in data.measured_files():\n file = os.path.normpath(file)\n root = Path(base)\n child = Path(file)\n if root in child.parents:\n with open(child, \'r\') as f:\n s = f.read()\n lines = s.splitlines()\n garb = \'GARBAGE\'\n\n lines2 = snipper.censor_code(lines, keep=True)\n assert len(lines) == len(lines2)\n\n for l in data.contexts_by_lineno(file):\n if lines2[l].strip() == garb:\n if self.cache_id() not in self._report.covcache:\n self._report.covcache[self.cache_id()] = {}\n\n rel = os.path.relpath(child, root)\n cc = self._report.covcache[self.cache_id()]\n j = 0\n for j in range(l, -1, -1):\n if "def" in lines2[j] or "class" in lines2[j]:\n break\n from snipper.snipper import gcoms\n fun = lines2[j]\n comments, _ = gcoms("\\n".join(lines2[j:l]))\n if rel not in cc:\n cc[rel] = {}\n cc[rel][fun] = (l, "\\n".join(comments))\n self._cache_put((self.cache_id(), \'coverage\'), self._report.covcache)\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd is None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n sd = self.shortDescriptionStandard()\n title = self._cache_get((self.cache_id(), \'title\'), sd)\n return title if title is not None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome is None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc is not None:\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard())\n\n self._cache2[(self.cache_id(), \'assert\')] = {}\n res = testMethod()\n elapsed = time.time() - t\n self._get_outcome()[self.cache_id()] = res\n self._cache_put((self.cache_id(), "time"), elapsed)\n\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return c, m\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n # sys.stdout = self._stdout\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n self.__class__._cache[\n key] = {} # A new dict. We manually insert it because we have to use that the dict is mutable.\n cache = self._cache_get(key)\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, f"Key {id} not found in cache; framework files missing. Please run deploy()")\n\n # The order of these calls is important. If the method assert fails, we should still store the correct result in cache.\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n assert_fun(first, _expected, *args, **kwargs)\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache is not None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n if os.path.exists(cfile):\n try:\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n except Exception as e:\n print("Bad cache", cfile)\n print(e)\n else:\n print("Warning! data file not found", cfile)\n\n def _feedErrorsToResult(self, result, errors):\n """ Use this to show hints on test failure. """\n if not isinstance(result, UTextResult):\n er = [e for e, v in errors if v != None]\n\n if len(er) > 0:\n hints = []\n key = (self.cache_id(), \'coverage\')\n if self._cache_contains(key):\n CC = self._cache_get(key)\n for id in CC:\n if id == self.cache_id():\n cl, m = id\n gprint(f"> An error occured while solving: {cl}.{m}. The files/methods you need to edit are:") # For the test {id} in {file} you should edit:")\n for file in CC[id]:\n rec = CC[id][file]\n gprint(f"> * {file}")\n for l in rec:\n _, comments = CC[id][file][l]\n hint = get_hints(comments)\n\n if hint != None:\n hints.append(hint)\n gprint(f"> - {l}")\n\n er = er[0]\n doc = er._testMethodDoc\n if doc is not None:\n hint = get_hints(er._testMethodDoc)\n if hint is not None:\n hints = [hint] + hints\n if len(hints) > 0:\n gprint("> Hints:")\n gprint(textwrap.indent("\\n".join(hints), "> "))\n\n super()._feedErrorsToResult(result, errors)\n\n def startTestRun(self):\n # print("asdfsdaf 11", file=sys.stderr)\n super().startTestRun()\n # print("asdfsdaf")\n\n def _callTestMethod(self, method):\n # print("asdfsdaf")\n super()._callTestMethod(method)\n\n\ndef hide(func):\n return func\n\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n# 817\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\nimport inspect\nimport os\nimport argparse\nimport time\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print(b + " v" + __version__ + ", started: " + dt_string+ "\\n")\n # print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print(s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n UTextResult.nL = report.nL\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"Question {n+1} total"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")",\n last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL)\n\n # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n # print(" ")\n # print("="*n)\n # print("Final evaluation")\n # print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n print("")\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f" * {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single unmodified file: ")\n print(">", token)\n # print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n def test_add(self):\n """ Docstring for this method """\n from cs102.homework1 import add\n self.assertEqualC(add(2,2))\n with self.capture() as out:\n print("hello world 42")\n self.assertEqual(out.numbers[0], 42)\n self.assertEqualC(add(-100, 5))\n\n def test_reverse(self):\n """ Reverse a list """ # Add a title to the test.\n from cs102.homework1 import reverse_list\n self.assertEqualC(reverse_list([1,2,3]))\n\n\nclass Question2(UTestCase):\n """ Second problem """\n @cache\n def my_reversal(self, ls):\n # The \'@cache\' decorator ensures the function is not run on the *students* computer\n # Instead the code is run on the teachers computer and the result is passed on with the\n # other pre-computed results -- i.e. this function will run regardless of how the student happens to have\n # implemented reverse_list.\n from cs102.homework1 import reverse_list\n return reverse_list(ls)\n\n def test_reverse_tricky(self):\n ls = [2,4,8]\n self.title = f"Reversing a small list containing {ls=}" # Titles can be set like this at any point in the function body.\n ls2 = self.my_reversal( tuple(ls) ) # This will always produce the right result.\n ls3 = self.my_reversal( tuple([1,2,3]) ) # Also works; the cache respects input arguments.\n self.assertEqualC(self.my_reversal( tuple(ls2) )) # This will actually test the students code.\n\n\nimport cs102\nclass Report2(Report):\n title = "CS 101 Report 2"\n questions = [(Week1, 10), (Question2, 8) ]\n pack_imports = [cs102]' +report1_payload = '80049510010000000000007d94288c055765656b31947d94288c055765656b31948c08746573745f6164649486948c066173736572749486947d94284b004b044b014aa1ffffff7568038c0c746573745f72657665727365948694680686947d944b005d94284b034b024b0165738c0474696d6594473fe6a7e700000000758c095175657374696f6e32947d94288c095175657374696f6e32948c13746573745f726576657273655f747269636b799486948c057469746c659486948c2e526576657273696e67206120736d616c6c206c69737420636f6e7461696e696e67206c733d5b322c20342c20385d946811681286948c066173736572749486947d944b005d94284b024b044b086573680e473facac280000000075752e' name="Report2" report = source_instantiate(name, report1_source, report1_payload) diff --git a/examples/example_framework/instructor/cs102/unitgrade/Question2.pkl b/examples/example_framework/instructor/cs102/unitgrade/Question2.pkl index 634a7fbbe4bad27f24b2d894ef3c0b37c4f5dd94..b950c49faa2b91b7675ee266d63a13fe3652e3cc 100644 GIT binary patch delta 119 zcmZo*e!;}jz%n&<B8&dS;0<CN8JrnBnvGMu8NHdjncJr%`qfU!5ST2<sO$rm;_zni zW`ap|aqR0^R{wxw2Ul_1l%)14ZBt^WXm~Suvv_l)W-w+jwN2?^ODrx<Eh?GPHl=oo LH$#R>aj6~v?r|tR delta 140 zcmaFC)WFQrz%sRTB8$E(TVio>YEj9Qwkfq!ycuGrXm~Suvv_l)7H2SKFikYrEXJF` zmm#RxIK`XMo5`EGeM+KV?UW4Bi9eNH;4&QEEZ$5onJ$ifJ<IAJaO~hJZkv+SKBa9+ Z20O^CwkbU<C7HRYQ`)AuGeAM99sr;8Ev*0m diff --git a/examples/example_framework/instructor/cs102/unitgrade/Week1.pkl b/examples/example_framework/instructor/cs102/unitgrade/Week1.pkl index 7912698f036128bb2a8b616c2a66c58ac9e774e5..6b4ee502e9c67e2ff3aba1b11c4911bb88195e34 100644 GIT binary patch delta 35 rcmcc4n99<?GBs)<i^9YJ>50vvg0)k;8Dghscr$x5c{8RKm+Aok$2|&n delta 146 zcmYej&dAchGWE<v76nb#lFX8v)G2LKdL&))lZ#7=GV{_E((;QGN-{Ew6>?KcGV)WV zWH4qhO;nT<%V27o(!)}cnG4e3&Hx1yZN=?-_<~Z)Qj3aH6%rM4GK)(<TKL+gWN@@i gsh#4@5IaS~o7tPmn=!RGgE0dn!P7PcZa}FX06nQK!~g&Q diff --git a/examples/example_framework/students/cs102/.coverage b/examples/example_framework/students/cs102/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..a93b4d7e94a84f8ad080beeafaa98b3784e23e91 GIT binary patch literal 53248 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 literal 0 HcmV?d00001 diff --git a/examples/example_framework/students/cs102/Report2_handin_0_of_18.token b/examples/example_framework/students/cs102/Report2_handin_0_of_18.token deleted file mode 100644 index 63734376c0eae1c4df3121a0c656e90452e80cba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79868 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 aM*t#a1oy#|9u`odnbN}wF|D+?R1W}G5%E6& diff --git a/examples/example_framework/students/cs102/__pycache__/deploy.cpython-38.pyc b/examples/example_framework/students/cs102/__pycache__/deploy.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d9b529d6cae3250756694b95ca75ce15b33bf86 GIT binary patch literal 760 zcmWIL<>g{vU|@J4te5D?#K7<v#6iaF3=9ko3=9m#9SjT%DGVu$ISjdsQH+crHd78$ zE^`z!n9ZESlFJ&!3TCt9u;sExv4h#HIUG?OsT^6H3%F9b7cxfir1GY)Wiu7Eq_U<m zHZwFcGBTtxr!uCngCR#R3nN1+dn!)~r#M3@PYRbfLkf2aPYQ1eUpjLNKS(x(HJCwD z;3de7nvA#DgHj9fi%N_%8E=Ufr<Rl!#HSY}rliKFW#**D7lGw88E;9YCzfQS7R8qq z<m4x&#Fym9Cnx3>loscumS{5G;wa9`EQn9ZEb_a>5g(tMn3)$J@21Hd#g?0ymzh=> z#hIU!R$82#SdtpWo?4NbTw0P^#i_5Sr=MJGXkb(&3S#Q%=@*xjrljVT6hnkVZgI!Q z7gQ!ECTFC^$5*jy>6PSXr{-00rlc0+<X7qyRBEzBaTVv67A2>G?2h6{Ni8k`vqY0~ zQWNvyLHx|T#FEVXJg_i(YDIERX-cXlUlcFcW<7}ijEa~T7#O0|O7k*HAf7KM$}CGP zNj1{T$V^E|&5KViN=+<*gh_D`Gf1&IZpA1;Rm1{PEdUCjg47~C5Djum5i0`&!!4%# z;#({Qi6t4g*i$l#@)C1XLE#4u`S>WN@)QscBpM&TlA(x~fdNANig&h(2`x@7Dvl{B z%}9-L$xklL1%+D-hMQtiD-v@Ha#D+7jQF&o#N5>K{G#lb%)H`~qSEA&{Gu3eV8p<~ k^%jRsZhlH>PO2RvDBTJ%Ffi~iLLdht4-*F?7oz|p0DCy)i2wiq literal 0 HcmV?d00001 diff --git a/examples/example_framework/students/cs102/__pycache__/homework1.cpython-38.pyc b/examples/example_framework/students/cs102/__pycache__/homework1.cpython-38.pyc index a099ef9f65bf987d85152d20d4dad941ae1d27bc..d67337369ba5bf909f1eb07c3dda178779750fb2 100644 GIT binary patch delta 398 zcmaFNeuRxLl$V!_fq{XcM_4b>k7**GOuZWe149Z!3S$dH6hjJA3UdoX6k{q=3QIOq zky9#j3TrB33R?<WGt+E_xlGNBj0~yFDeS2XDQqbmDV!->=`78R%}k68Dcr#fnmoyj zAhV$u#Aaq-U~pz&U?}EcU|=X=C}FH&Xl86?3}(<|tXjmSprD{IS&VV6swUGdmYmGu zl3N@_sb#4}#i=Qpj8Sa4l_0T|3`HPQekD!*&gjaOnO9s=RGM6pUo_c;NznpiM==w~ zLe?Ty1_p*?FdM{YU|;~TK^W`+4h9B>8ipE1aR!j(lNU0j>Vk~bWV*$ec#AO!YzKs3 zVPIgm#hjRu0@4C8oVkc&vL$nfHw)O3B9O!_j`;Yz#N5>Q_*<+6MVWae5X~SnAuO;l a95%W6DWy57c3_8s^l`9ruyQboFaiLWSxc?} delta 535 zcmX@Y_L!Y7l$V!_fq{X+d$&%a8S_LwnR+h<28I-dD25cq6s8u2D8>}#6qXi-D5g~A z6xM8}BDYkQ6t+~R6!sMMW~SK;bD5eM85vSpQaDl>Q`l2DQ@B#N(^;Aso0%9HQh0(H zG<mDixm+s}a|?1(6^ctrQ&RIv6q55(QuP!<GBS%5(lT>W6*7wz5=%?+a}!H4lj{?6 zaw-+lQ}a@b5=&B36w-?Fa}`Q5Qo$;V6*BX{oXou9lA_Y&lKdjwl+2=35TlYSnGxg@ zC<d{a7#J9w85kIfc^DWNN*GcYn;DxJQ<#DoG?}XwaVaP$D7a^qrRFIlD&%Aqmnf*` zR_0_*+}-W+OWYIW-dvDd(@OJ_OEUBG6q52&D*aY6-V*l9FM&&^rnnXr<rir(MzMi3 z7niJLC}Lq?VEC0VIh)ay1M1=8$-5X8c|qdEASW@f6|qfz!6++)>8#1ZOsU2or~4Iw zRBAHaVobcnm;^QxLa;J0Fx+BJOi2N0h8V|E#5s8>Q-~nzE%x~Ml>FrQ_#!q228JTe q$&AdR1|Vx8TEQ%^eh!=5{FKt1R6CGW#h?Vh!py<J!N$QP!UzCL*@fHy diff --git a/examples/example_framework/students/cs102/__pycache__/report2.cpython-38.pyc b/examples/example_framework/students/cs102/__pycache__/report2.cpython-38.pyc index d06f59685aba1b0e7100778b3122129ce8573306..e88ea2f06cb46d8605dea67d9293e601ee149b83 100644 GIT binary patch delta 1025 zcmZn=_$t5~%FD~ez`(%3@k%Fg(?nib#;S?h`abRqDJ&_hEet8FsVvRRQS9yvDQqe1 zEet8_U_M6*LkdR@XB4M9LkedKR|`W5S1MyOa}-x9cM5klQ&Cweb1GLeLo*{ILkh>l zbsiJHm+>((Ffed3FfceXFfbIyO|E8Ca%5h}(9c)PRKk+N0#X{zV9F545X2C{P{Xi* zwT3B+Ern5%A%%4z6C+53sfH2ch-RjRU}J+BG&v`4VANBp;>$?Q$;nqJ&o9bJQ7|!j zsl~v+kU8i7|Nk#p85kJ+G}$M=W>j&!#h#p4P*PfydW*H7C^N6*7I$KCacWVCYhh_( z&Mo%5(%hufqGC<vTP($?IcY^43=9mnnDa|ZRx%WcFfcIuDxR#$WE_`RT9TiWm=vE{ zk(gVMld7+$rw?VurxhjUrk3XyW$S0=6_*s1CYR(F=_eN(8W`ypr55BDl^E$2RNmq! zNi8mkPfST+2ZaL<DCn7Z7zG%)7=;*xCeLN+w*ZM1NiZ-l6!9`JFlaIr34z#P0gyw$ zt|<})IgbZyR#9qMYEf~jz~mI>Kw~kGT4@l0Pzz>(G!%jC1cga48z{yZSr|Dui`XX@ zu*ptlWofb%hgb)<Uy~VZ^)05HVu*Xd27w8XX}7p@E8`)SC+0{@KEiT3P7-9X6v!Bs zTdXCSB{``@N+5@_mXsFcq(c0|0yeZrk%56BiaDp)2t*h|JcTe?7};m>B}JLZ*_Dcu z-?Ewp$$+eo1rc%}LLNkbyacwWNFBrl8-pM~ZY@$^U|;}+O))55IT%?OnHae^z@mPW zUo%Th?q&0-R{^IM<7`R6%BfLJq9_7H45;YH>+seqQk{R&eaz;weZ>&W_K_1;ueO zl25=+0Nbv?z`y`XmGE#EU@QWO-QtLk&rQtCi;vgjxg{f7nwMFUUX+-UYNUr?7BPWB zaB>N|Q2;ppz^*9L200331jMyqs}Kap(pwxhx%nxjIjMGxpp+)Sz`(%6$ipbYBp@gt L%)uzY%E1T#{|@D7 delta 899 zcmew=&>+AY%FD~ez`($uyIUvGd?K$bW79-!{g4!f6qX$JD0X*-6xI~B7KRkIRF-Du zC=Pdq6!sL37KRiKFrO2wjw_1GogsxYg{y@jg)5b@nK_C(l_!Neo2jTSl{uBWnW34H zks*a;;xZ3Lzlrb4_?Q?N7+4t?7@Qdx7>W%h7cnXcGu1Gru%xgyGc5!uWeaA|WS@MM zQI9us&j0`aUxF0-X);e%Wl|B-WV*#toSKtX#KFM8u#%yOkAZ>VSHk2jCS%vsip1Q4 zoYeTVqQu<P^8BLgn9RK5lA_Y&lKi5W<YGeuqnM)9g8ZTqBfWykTO1{+#U=5HDJh&F zXRv|7fQg4uV6rrGzXdk~14EGzh~QyhV9;bL;svq60wDXqmKO0dFfiQW0h>~kT9#T= zoXS7>5p$rS07$JUh(M?Xvp^b(*cliYK$aDQ2o6RTCJwgADJ%<Z1tAu}ZPsK4TY8Hr zrx@ZAun}MaWX>(_+{$=}wTU_6lbu*k#|eY16ag8-a*MSjvm__ANCxCY){@eKoYW$) zPguZ)7D+QOFhnuu6dQpEV~BSUMhheRDZZpAGda6bVRAN`X`mR$3ULr20U{(p1SoRA z78NOixL{)t1jwyLQVa|Xpa3Zb#Tf@93nLRF7YA5$@+Y>mdN~FL20u;CA~e(GLB@fC z3#>AVv#>O^xFj<_ulN=#I5MMn3KEmE<1=$XQCf`T39#*8i<Lm30gGOc#R7~)AhBB< z@$tEdnR)T?n!HgmqNRD6CFw<pDXB(!2xbu|nMO_i%x)AF#a5J<my(|gjy$kyic~<3 x0~rNzHP}i70kZrShfQvNN@-529V5sEpv1$&$ipbY$iXDQFCfgpD8S0W2mqR}xm5rF diff --git a/examples/example_framework/students/cs102/__pycache__/report2_grade.cpython-38.pyc b/examples/example_framework/students/cs102/__pycache__/report2_grade.cpython-38.pyc index cb391caf0c1bf2175df62b4c8789fee08d1961e3..42fb3a4a526346eae8494f38ac29d254b5f30b83 100644 GIT binary patch delta 4043 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 Uv2lZ%3|tl*oGhS56cb1%0L&;{pa1{> delta 8117 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 diff --git a/examples/example_framework/students/cs102/report2.py b/examples/example_framework/students/cs102/report2.py index d84e9c4..e4d6b02 100644 --- a/examples/example_framework/students/cs102/report2.py +++ b/examples/example_framework/students/cs102/report2.py @@ -1,18 +1,20 @@ """ Example student code. This file is automatically generated from the files in the instructor-directory """ -from unitgrade2.unitgrade2 import Report -from unitgrade2.unitgrade_helpers2 import evaluate_report_student -from unitgrade2.unitgrade2 import UTestCase, cache, hide -import random +from src.unitgrade2.unitgrade2 import Report +from src.unitgrade2 import evaluate_report_student +from src.unitgrade2.unitgrade2 import UTestCase, cache + class Week1(UTestCase): """ The first question for week 1. """ - def test_add(self): """ Docstring for this method """ from cs102.homework1 import add self.assertEqualC(add(2,2)) + with self.capture() as out: + print("hello world 42") + self.assertEqual(out.numbers[0], 42) self.assertEqualC(add(-100, 5)) def test_reverse(self): @@ -34,7 +36,7 @@ class Question2(UTestCase): def test_reverse_tricky(self): ls = [2,4,8] - self.title = f"Reversing a small list containing {ls=}" + self.title = f"Reversing a small list containing {ls=}" # Titles can be set like this at any point in the function body. ls2 = self.my_reversal( tuple(ls) ) # This will always produce the right result. ls3 = self.my_reversal( tuple([1,2,3]) ) # Also works; the cache respects input arguments. self.assertEqualC(self.my_reversal( tuple(ls2) )) # This will actually test the students code. @@ -43,7 +45,7 @@ class Question2(UTestCase): import cs102 class Report2(Report): title = "CS 101 Report 2" - questions = [(Week1, 10), (Question2, 8) ] # Include a single question for 10 credits. + questions = [(Week1, 10), (Question2, 8) ] pack_imports = [cs102] if __name__ == "__main__": diff --git a/examples/example_framework/students/cs102/report2_grade.py b/examples/example_framework/students/cs102/report2_grade.py index 1f9a885..e6318de 100644 --- a/examples/example_framework/students/cs102/report2_grade.py +++ b/examples/example_framework/students/cs102/report2_grade.py @@ -6,14 +6,10 @@ from tabulate import tabulate from datetime import datetime import pyfiglet import unittest - import inspect import os import argparse -import sys import time -import threading # don't import Thread bc. of minify issue. -import tqdm # don't do from tqdm import tqdm because of minify-issue parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: To run all tests in a report: @@ -63,53 +59,6 @@ def evaluate_report_student(report, question=None, qitem=None, unmute=None, pass show_tol_err=show_tol_err) - # try: # For registering stats. - # import unitgrade_private - # import irlc.lectures - # import xlwings - # from openpyxl import Workbook - # import pandas as pd - # from collections import defaultdict - # dd = defaultdict(lambda: []) - # error_computed = [] - # for k1, (q, _) in enumerate(report.questions): - # for k2, item in enumerate(q.items): - # dd['question_index'].append(k1) - # dd['item_index'].append(k2) - # dd['question'].append(q.name) - # dd['item'].append(item.name) - # dd['tol'].append(0 if not hasattr(item, 'tol') else item.tol) - # error_computed.append(0 if not hasattr(item, 'error_computed') else item.error_computed) - # - # qstats = report.wdir + "/" + report.name + ".xlsx" - # - # if os.path.isfile(qstats): - # d_read = pd.read_excel(qstats).to_dict() - # else: - # d_read = dict() - # - # for k in range(1000): - # key = 'run_'+str(k) - # if key in d_read: - # dd[key] = list(d_read['run_0'].values()) - # else: - # dd[key] = error_computed - # break - # - # workbook = Workbook() - # worksheet = workbook.active - # for col, key in enumerate(dd.keys()): - # worksheet.cell(row=1, column=col+1).value = key - # for row, item in enumerate(dd[key]): - # worksheet.cell(row=row+2, column=col+1).value = item - # - # workbook.save(qstats) - # workbook.close() - # - # except ModuleNotFoundError as e: - # s = 234 - # pass - if question is None: print("Provisional evaluation") tabulate(table_data) @@ -161,24 +110,20 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) else: b = "Unitgrade" - print(b + " v" + __version__) dt_string = now.strftime("%d/%m/%Y %H:%M:%S") - print("Started: " + dt_string) + print(b + " v" + __version__ + ", started: " + dt_string+ "\n") + # print("Started: " + dt_string) s = report.title if hasattr(report, "version") and report.version is not None: s += " version " + report.version - print("Evaluating " + s, "(use --help for options)" if show_help_flag else "") + print(s, "(use --help for options)" if show_help_flag else "") # print(f"Loaded answers from: ", report.computed_answers_file, "\n") table_data = [] - nL = 80 t_start = time.time() score = {} loader = SequentialTestLoader() for n, (q, w) in enumerate(report.questions): - # q = q() - # q_hidden = False - # q_hidden = issubclass(q.__class__, Hidden) if question is not None and n+1 != question: continue suite = loader.loadTestsFromTestCase(q) @@ -188,104 +133,28 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa q.possible = 0 q.obtained = 0 q_ = {} # Gather score in this class. - # unittest.Te - # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_] UTextResult.q_title_print = q_title_print # Hacky UTextResult.show_progress_bar = show_progress_bar # Hacky. UTextResult.number = n + UTextResult.nL = report.nL res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) - # res = UTextTestRunner(verbosity=2, resultclass=unittest.TextTestResult).run(suite) - z = 234 - # for j, item in enumerate(q.items): - # if qitem is not None and question is not None and j+1 != qitem: - # continue - # - # if q_with_outstanding_init is not None: # check for None bc. this must be called to set titles. - # # if not item.question.has_called_init_: - # start = time.time() - # - # cc = None - # if show_progress_bar: - # total_estimated_time = q.estimated_time # Use this. The time is estimated for the q itself. # sum( [q2.estimated_time for q2 in q_with_outstanding_init] ) - # cc = ActiveProgress(t=total_estimated_time, title=q_title_print) - # from unitgrade import Capturing # DON'T REMOVE THIS LINE - # with eval('Capturing')(unmute=unmute): # Clunky import syntax is required bc. of minify issue. - # try: - # for q2 in q_with_outstanding_init: - # q2.init() - # q2.has_called_init_ = True - # - # # item.question.init() # Initialize the question. Useful for sharing resources. - # except Exception as e: - # if not passall: - # if not silent: - # print(" ") - # print("="*30) - # print(f"When initializing question {q.title} the initialization code threw an error") - # print(e) - # print("The remaining parts of this question will likely fail.") - # print("="*30) - # - # if show_progress_bar: - # cc.terminate() - # sys.stdout.flush() - # print(q_title_print, end="") - # - # q_time =np.round( time.time()-start, 2) - # - # print(" "* max(0,nL - len(q_title_print) ) + (" (" + str(q_time) + " seconds)" if q_time >= 0.1 else "") ) # if q.name in report.payloads else "") - # print("=" * nL) - # q_with_outstanding_init = None - # - # # item.question = q # Set the parent question instance for later reference. - # item_title_print = ss = "*** q%i.%i) %s"%(n+1, j+1, item.title) - # - # if show_progress_bar: - # cc = ActiveProgress(t=item.estimated_time, title=item_title_print) - # else: - # print(item_title_print + ( '.'*max(0, nL-4-len(ss)) ), end="") - # hidden = issubclass(item.__class__, Hidden) - # # if not hidden: - # # print(ss, end="") - # # sys.stdout.flush() - # start = time.time() - # - # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent) - # q_[j] = {'w': item.weight, 'possible': possible, 'obtained': current, 'hidden': hidden, 'computed': str(item._computed_answer), 'title': item.title} - # tsecs = np.round(time.time()-start, 2) - # if show_progress_bar: - # cc.terminate() - # sys.stdout.flush() - # print(item_title_print + ('.' * max(0, nL - 4 - len(ss))), end="") - # - # if not hidden: - # ss = "PASS" if current == possible else "*** FAILED" - # if tsecs >= 0.1: - # ss += " ("+ str(tsecs) + " seconds)" - # print(ss) - - # ws, possible, obtained = upack(q_) possible = res.testsRun obtained = len(res.successes) assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun - # possible = int(ws @ possible) - # obtained = int(ws @ obtained) - # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0 - obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} q.obtained = obtained q.possible = possible - s1 = f"*** Question q{n+1}" + s1 = f"Question {n+1} total" s2 = f" {q.obtained}/{w}" - print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 ) + print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 ) print(" ") - table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"]) + table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"]) ws, possible, obtained = upack(score) possible = int( msum(possible) ) @@ -300,15 +169,16 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa seconds = dt - minutes*60 plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") - print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")") + dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", + last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) + + # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total") table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) results = {'total': (obtained, possible), 'details': score} return results, table_data - - from tabulate import tabulate from datetime import datetime import inspect @@ -331,7 +201,8 @@ def gather_imports(imp): # dn = os.path.dirname(f) # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) # top_package = str(__import__(m.__name__.split('.')[0]).__path__) - if m.__class__.__name__ == 'module' and False: + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: top_package = os.path.dirname(m.__file__) module_import = True else: @@ -352,7 +223,7 @@ def gather_imports(imp): for file in files: if file.endswith(".py"): fpath = os.path.join(root, file) - v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package)) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) zip.write(fpath, v) resources['zipfile'] = zip_buffer.getvalue() @@ -396,14 +267,14 @@ def gather_upload_to_campusnet(report, output_dir=None): results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, show_progress_bar=not args.noprogress, big_header=not args.autolab) - print(" ") - print("="*n) - print("Final evaluation") - print(tabulate(table_data)) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) # also load the source code of missing files... sources = {} - + print("") if not args.autolab: if len(report.individual_imports) > 0: print("By uploading the .token file, you verify the files:") @@ -416,12 +287,15 @@ def gather_upload_to_campusnet(report, output_dir=None): print("Including files in upload...") for k, m in enumerate(report.pack_imports): nimp, top_package = gather_imports(m) - report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import nimp['name'] = m.__name__ sources[k] = nimp # if len([k for k in nimp if k not in sources]) > 0: - print(f"*** {m.__name__}") + print(f" * {m.__name__}") # sources = {**sources, **nimp} results['sources'] = sources @@ -440,9 +314,9 @@ def gather_upload_to_campusnet(report, output_dir=None): if not args.autolab: print(" ") - print("To get credit for your results, please upload the single file: ") + print("To get credit for your results, please upload the single unmodified file: ") print(">", token) - print("To campusnet without any modifications.") + # print("To campusnet without any modifications.") # print("Now time for some autolab fun") @@ -455,8 +329,8 @@ def source_instantiate(name, report1_source, payload): -report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n\n"""\n# from . import cache_read\nimport unittest\nimport numpy as np\nimport sys\nfrom io import StringIO\nimport collections\nimport re\nimport threading\nimport tqdm\nimport time\nimport pickle\nimport itertools\nimport os\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\ndef setup_dir_by_class(C,base_dir):\n name = C.__class__.__name__\n # base_dir = os.path.join(base_dir, name)\n # if not os.path.isdir(base_dir):\n # os.makedirs(base_dir)\n return base_dir, name\n\nclass Hidden:\n def hide(self):\n return True\n\nclass Logger(object):\n def __init__(self, buffer):\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\nclass Capturing(list):\n def __init__(self, *args, unmute=False, **kwargs):\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass QItem(unittest.TestCase):\n title = None\n testfun = None\n tol = 0\n estimated_time = 0.42\n _precomputed_payload = None\n _computed_answer = None # Internal helper to later get results.\n weight = 1 # the weight of the question.\n\n def __init__(self, question=None, *args, **kwargs):\n if self.tol > 0 and self.testfun is None:\n self.testfun = self.assertL2Relative\n elif self.testfun is None:\n self.testfun = self.assertEqual\n\n self.name = self.__class__.__name__\n # self._correct_answer_payload = correct_answer_payload\n self.question = question\n\n super().__init__(*args, **kwargs)\n if self.title is None:\n self.title = self.name\n\n def _safe_get_title(self):\n if self._precomputed_title is not None:\n return self._precomputed_title\n return self.title\n\n def assertNorm(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed).flat- np.asarray(expected)).flat )\n nrm = np.sqrt(np.sum( diff ** 2))\n\n self.error_computed = nrm\n\n if nrm > tol:\n print(f"Not equal within tolerance {tol}; norm of difference was {nrm}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def assertL2(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n self.error_computed = np.max(diff)\n\n if np.max(diff) > tol:\n print(f"Not equal within tolerance {tol=}; deviation was {np.max(diff)=}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol=}, {np.max(diff)=}")\n\n def assertL2Relative(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n diff = diff / (1e-8 + np.abs( (np.asarray(computed) + np.asarray(expected)) ) )\n self.error_computed = np.max(np.abs(diff))\n if np.sum(diff > tol) > 0:\n print(f"Not equal within tolerance {tol}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def precomputed_payload(self):\n return self._precomputed_payload\n\n def precompute_payload(self):\n # Pre-compute resources to include in tests (useful for getting around rng).\n pass\n\n def compute_answer(self, unmute=False):\n raise NotImplementedError("test code here")\n\n def test(self, computed, expected):\n self.testfun(computed, expected)\n\n def get_points(self, verbose=False, show_expected=False, show_computed=False,unmute=False, passall=False, silent=False, **kwargs):\n possible = 1\n computed = None\n def show_computed_(computed):\n print(">>> Your output:")\n print(computed)\n\n def show_expected_(expected):\n print(">>> Expected output (note: may have been processed; read text script):")\n print(expected)\n\n correct = self._correct_answer_payload\n try:\n if unmute: # Required to not mix together print stuff.\n print("")\n computed = self.compute_answer(unmute=unmute)\n except Exception as e:\n if not passall:\n if not silent:\n print("\\n=================================================================================")\n print(f"When trying to run test class \'{self.name}\' your code threw an error:", e)\n show_expected_(correct)\n import traceback\n print(traceback.format_exc())\n print("=================================================================================")\n return (0, possible)\n\n if self._computed_answer is None:\n self._computed_answer = computed\n\n if show_expected or show_computed:\n print("\\n")\n if show_expected:\n show_expected_(correct)\n if show_computed:\n show_computed_(computed)\n try:\n if not passall:\n self.test(computed=computed, expected=correct)\n except Exception as e:\n if not silent:\n print("\\n=================================================================================")\n print(f"Test output from test class \'{self.name}\' does not match expected result. Test error:")\n print(e)\n show_computed_(computed)\n show_expected_(correct)\n return (0, possible)\n return (1, possible)\n\n def score(self):\n try:\n self.test()\n except Exception as e:\n return 0\n return 1\n\nclass QPrintItem(QItem):\n def compute_answer_print(self):\n """\n Generate output which is to be tested. By default, both text written to the terminal using print(...) as well as return values\n are send to process_output (see compute_answer below). In other words, the text generated is:\n\n res = compute_Answer_print()\n txt = (any terminal output generated above)\n numbers = (any numbers found in terminal-output txt)\n\n self.test(process_output(res, txt, numbers), <expected result>)\n\n :return: Optional values for comparison\n """\n raise Exception("Generate output here. The output is passed to self.process_output")\n\n def process_output(self, res, txt, numbers):\n return res\n\n def compute_answer(self, unmute=False):\n with Capturing(unmute=unmute) as output:\n res = self.compute_answer_print()\n s = "\\n".join(output)\n s = rm_progress_bar(s) # Remove progress bar.\n numbers = extract_numbers(s)\n self._computed_answer = (res, s, numbers)\n return self.process_output(res, s, numbers)\n\nclass OrderedClassMembers(type):\n @classmethod\n def __prepare__(self, name, bases):\n return collections.OrderedDict()\n def __new__(self, name, bases, classdict):\n ks = list(classdict.keys())\n for b in bases:\n ks += b.__ordered__\n classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n return type.__new__(self, name, bases, classdict)\n\nclass QuestionGroup(metaclass=OrderedClassMembers):\n title = "Untitled question"\n partially_scored = False\n t_init = 0 # Time spend on initialization (placeholder; set this externally).\n estimated_time = 0.42\n has_called_init_ = False\n _name = None\n _items = None\n\n @property\n def items(self):\n if self._items == None:\n self._items = []\n members = [gt for gt in [getattr(self, gt) for gt in self.__ordered__ if gt not in ["__classcell__", "__init__"]] if inspect.isclass(gt) and issubclass(gt, QItem)]\n for I in members:\n self._items.append( I(question=self))\n return self._items\n\n @items.setter\n def items(self, value):\n self._items = value\n\n @property\n def name(self):\n if self._name == None:\n self._name = self.__class__.__name__\n return self._name #\n\n @name.setter\n def name(self, val):\n self._name = val\n\n def init(self):\n # Can be used to set resources relevant for this question instance.\n pass\n\n def init_all_item_questions(self):\n for item in self.items:\n if not item.question.has_called_init_:\n item.question.init()\n item.question.has_called_init_ = True\n\n\nclass Report():\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 80 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q,_) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n root_dir = self.pack_imports[0].__path__._path[0]\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q,_) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n # else:\n # if os.path.isfile(self.computed_answers_file):\n # self.set_payload(cache_read(self.computed_answers_file), strict=strict)\n # else:\n # s = f"> Warning: The pre-computed answer file, {os.path.abspath(self.computed_answers_file)} is missing. The framework will NOT work as intended. Reasons may be a broken local installation."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n import unittest\n loader = unittest.TestLoader()\n for q,_ in self.questions:\n import time\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self):\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\':True}\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n # for item in q.items:\n # if q.name not in payloads or item.name not in payloads[q.name]:\n # s = f"> Broken resource dictionary submitted to unitgrade for question {q.name} and subquestion {item.name}. Framework will not work."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n # else:\n # item._correct_answer_payload = payloads[q.name][item.name][\'payload\']\n # item.estimated_time = payloads[q.name][item.name].get("time", 1)\n # q.estimated_time = payloads[q.name].get("time", 1)\n # if "precomputed" in payloads[q.name][item.name]: # Consider removing later.\n # item._precomputed_payload = payloads[q.name][item.name][\'precomputed\']\n # try:\n # if "title" in payloads[q.name][item.name]: # can perhaps be removed later.\n # item.title = payloads[q.name][item.name][\'title\']\n # except Exception as e: # Cannot set attribute error. The title is a function (and probably should not be).\n # pass\n # # print("bad", e)\n # self.payloads = payloads\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct+1)\n if i > 0 and l.find("|", i+1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = \'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar",show_progress_bar=True):\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.1\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n\n # self.pbar = tqdm.tqdm(total=self.n)\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if hasattr(self, \'pbar\') and self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar=None\n\n sys.stdout.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=sys.stdout, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\') # , unit_scale=dt, unit=\'seconds\'):\n\n for _ in range(self.n-1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\n\n\nfrom unittest.suite import _isnotsuite\n\nclass MySuite(unittest.suite.TestSuite): # Not sure we need this one anymore.\n pass\n\ndef instance_call_stack(instance):\n s = "-".join(map(lambda x: x.__name__, instance.__class__.mro()))\n return s\n\ndef get_class_that_defined_method(meth):\n for cls in inspect.getmro(meth.im_class):\n if meth.__name__ in cls.__dict__:\n return cls\n return None\n\ndef caller_name(skip=2):\n """Get a name of a caller in the format module.class.method\n\n `skip` specifies how many levels of stack to skip while getting caller\n name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.\n\n An empty string is returned if skipped levels exceed stack height\n """\n stack = inspect.stack()\n start = 0 + skip\n if len(stack) < start + 1:\n return \'\'\n parentframe = stack[start][0]\n\n name = []\n module = inspect.getmodule(parentframe)\n # `modname` can be None when frame is executed directly in console\n # TODO(techtonik): consider using __main__\n if module:\n name.append(module.__name__)\n # detect classname\n if \'self\' in parentframe.f_locals:\n # I don\'t know any way to detect call from the object method\n # XXX: there seems to be no way to detect static method call - it will\n # be just a function call\n name.append(parentframe.f_locals[\'self\'].__class__.__name__)\n codename = parentframe.f_code.co_name\n if codename != \'<module>\': # top level usually\n name.append( codename ) # function or a method\n\n ## Avoid circular refs and frame leaks\n # https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack\n del parentframe, stack\n\n return ".".join(name)\n\ndef get_class_from_frame(fr):\n import inspect\n args, _, _, value_dict = inspect.getargvalues(fr)\n # we check the first parameter for the frame function is\n # named \'self\'\n if len(args) and args[0] == \'self\':\n # in that case, \'self\' will be referenced in value_dict\n instance = value_dict.get(\'self\', None)\n if instance:\n # return its class\n # isinstance(instance, Testing) # is the actual class instance.\n\n return getattr(instance, \'__class__\', None)\n # return None otherwise\n return None\n\nfrom typing import Any\nimport inspect, gc\n\ndef giveupthefunc():\n frame = inspect.currentframe()\n code = frame.f_code\n globs = frame.f_globals\n functype = type(lambda: 0)\n funcs = []\n for func in gc.get_referrers(code):\n if type(func) is functype:\n if getattr(func, "__code__", None) is code:\n if getattr(func, "__globals__", None) is globs:\n funcs.append(func)\n if len(funcs) > 1:\n return None\n return funcs[0] if funcs else None\n\n\nfrom collections import defaultdict\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n # if self.dots or self.showAll:\n # self.stream.writeln()\n # if hasattr(self, \'cc\'):\n # self.cc.terminate()\n # self.cc_terminate(success=False)\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n # if self.showAll:\n # self.stream.writeln("FAIL")\n # elif self.dots:\n # self.stream.write(\'F\')\n # self.stream.flush()\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n # super().addSuccess(test)\n self.successes.append(test)\n # super().addSuccess(test)\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n self.cc_terminate()\n\n\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n sys.stdout.flush()\n ss = self.item_title_print\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(ss))), end="")\n # current = 1\n # possible = 1\n # current == possible\n ss = "PASS" if success else "FAILED"\n if tsecs >= 0.1:\n ss += " (" + str(tsecs) + " seconds)"\n print(ss)\n\n\n def startTest(self, test):\n # super().startTest(test)\n j =self.testsRun\n self.testsRun += 1\n # print("Starting the test...")\n # show_progress_bar = True\n n = UTextResult.number\n\n item_title = self.getDescription(test)\n item_title = item_title.split("\\n")[0]\n\n item_title = test.shortDescription() # Better for printing (get from cache).\n # test.countTestCases()\n self.item_title_print = "*** q%i.%i) %s" % (n + 1, j + 1, item_title)\n estimated_time = 10\n nL = 80\n #\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar)\n else:\n print(self.item_title_print + (\'.\' * max(0, nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 2\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n # q_title_print = "some printed title..."\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass == None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n print(self.cc.title, end="")\n # start = 10\n # q_time = np.round(time.time() - start, 2)\n nL = 80\n print(" " * max(0, nL - len(self.cc.title)) + (\n " (" + str(q_time) + " seconds)" if q_time >= 0.1 else "")) # if q.name in report.payloads else "")\n # print("=" * nL)\n\nfrom unittest.runner import _WritelnDecorator\nfrom io import StringIO\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n from io import StringIO\n stream = StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\ndef wrapper(foo):\n def magic(self):\n s = "-".join(map(lambda x: x.__name__, self.__class__.mro()))\n # print(s)\n foo(self)\n magic.__doc__ = foo.__doc__\n return magic\n\nfrom functools import update_wrapper, _make_key, RLock\nfrom collections import namedtuple\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)) )\n # key = (self.cache_id(), \'@cache\')\n # if self._cache_contains[key]\n\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n return wrapper\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n\n @classmethod\n def question_title(cls):\n return cls.__doc__.splitlines()[0].strip() if cls.__doc__ != None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd == None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n # self._testMethodDoc.strip().splitlines()[0].strip()\n sd = self.shortDescriptionStandard()\n title = self._cache_get( (self.cache_id(), \'title\'), sd )\n return title if title != None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n # def _callSetUp(self):\n # # Always run before method is called.\n # print("asdf")\n # pass\n # @classmethod\n # def setUpClass(cls):\n # # self._cache_put((self.cache_id(), \'title\'), value)\n # cls.reset()\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome == None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n if self._testMethodDoc != None:\n # Ensure the cache is eventually updated with the right docstring.\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard() )\n # Fix temp cache here (for using the @cache decorator)\n self._cache2[ (self.cache_id(), \'assert\') ] = {}\n\n res = testMethod()\n elapsed = time.time() - t\n # self._cache_put( (self.cache_id(), \'title\'), self.shortDescription() )\n\n self._get_outcome()[self.cache_id()] = res\n self._cache_put( (self.cache_id(), "time"), elapsed)\n\n # This is my base test class. So what is new about it?\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return (c,m)\n\n # def unique_cache_id(self):\n # k0 = self.cache_id()\n # # key = ()\n # i = 0\n # for i in itertools.count():\n # # key = k0 + (i,)\n # if i not in self._cache_get( (k0, \'assert\') ):\n # break\n # return i\n # return key\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n # self.cache_indexes = defaultdict(lambda: 0)\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n #\n # def _cache2_contains(self, key):\n # print("Is this needed?")\n # self._ensure_cache_exists()\n # return key in self.__class__._cache2\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n cache = self._cache_get(key, {})\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, first)\n assert_fun(first, _expected, *args, **kwargs)\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__) ) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache != None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n # print("Loading cache from", cfile)\n if os.path.exists(cfile):\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n else:\n print("Warning! data file not found", cfile)\n\ndef hide(func):\n return func\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n # (*)We can be somewhat "hygienic", but newDecorator still isn\'t signature-preserving, i.e. you will not be able to get a runtime list of parameters. For that, you need hackish libraries...but in this case, the only argument is func, so it\'s not a big issue\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\n\nimport inspect\nimport os\nimport argparse\nimport sys\nimport time\nimport threading # don\'t import Thread bc. of minify issue.\nimport tqdm # don\'t do from tqdm import tqdm because of minify-issue\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n # try: # For registering stats.\n # import unitgrade_private\n # import irlc.lectures\n # import xlwings\n # from openpyxl import Workbook\n # import pandas as pd\n # from collections import defaultdict\n # dd = defaultdict(lambda: [])\n # error_computed = []\n # for k1, (q, _) in enumerate(report.questions):\n # for k2, item in enumerate(q.items):\n # dd[\'question_index\'].append(k1)\n # dd[\'item_index\'].append(k2)\n # dd[\'question\'].append(q.name)\n # dd[\'item\'].append(item.name)\n # dd[\'tol\'].append(0 if not hasattr(item, \'tol\') else item.tol)\n # error_computed.append(0 if not hasattr(item, \'error_computed\') else item.error_computed)\n #\n # qstats = report.wdir + "/" + report.name + ".xlsx"\n #\n # if os.path.isfile(qstats):\n # d_read = pd.read_excel(qstats).to_dict()\n # else:\n # d_read = dict()\n #\n # for k in range(1000):\n # key = \'run_\'+str(k)\n # if key in d_read:\n # dd[key] = list(d_read[\'run_0\'].values())\n # else:\n # dd[key] = error_computed\n # break\n #\n # workbook = Workbook()\n # worksheet = workbook.active\n # for col, key in enumerate(dd.keys()):\n # worksheet.cell(row=1, column=col+1).value = key\n # for row, item in enumerate(dd[key]):\n # worksheet.cell(row=row+2, column=col+1).value = item\n #\n # workbook.save(qstats)\n # workbook.close()\n #\n # except ModuleNotFoundError as e:\n # s = 234\n # pass\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n print(b + " v" + __version__)\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print("Evaluating " + s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n nL = 80\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n # q = q()\n # q_hidden = False\n # q_hidden = issubclass(q.__class__, Hidden)\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n # unittest.Te\n # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_]\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n # res = UTextTestRunner(verbosity=2, resultclass=unittest.TextTestResult).run(suite)\n z = 234\n # for j, item in enumerate(q.items):\n # if qitem is not None and question is not None and j+1 != qitem:\n # continue\n #\n # if q_with_outstanding_init is not None: # check for None bc. this must be called to set titles.\n # # if not item.question.has_called_init_:\n # start = time.time()\n #\n # cc = None\n # if show_progress_bar:\n # total_estimated_time = q.estimated_time # Use this. The time is estimated for the q itself. # sum( [q2.estimated_time for q2 in q_with_outstanding_init] )\n # cc = ActiveProgress(t=total_estimated_time, title=q_title_print)\n # from unitgrade import Capturing # DON\'T REMOVE THIS LINE\n # with eval(\'Capturing\')(unmute=unmute): # Clunky import syntax is required bc. of minify issue.\n # try:\n # for q2 in q_with_outstanding_init:\n # q2.init()\n # q2.has_called_init_ = True\n #\n # # item.question.init() # Initialize the question. Useful for sharing resources.\n # except Exception as e:\n # if not passall:\n # if not silent:\n # print(" ")\n # print("="*30)\n # print(f"When initializing question {q.title} the initialization code threw an error")\n # print(e)\n # print("The remaining parts of this question will likely fail.")\n # print("="*30)\n #\n # if show_progress_bar:\n # cc.terminate()\n # sys.stdout.flush()\n # print(q_title_print, end="")\n #\n # q_time =np.round( time.time()-start, 2)\n #\n # print(" "* max(0,nL - len(q_title_print) ) + (" (" + str(q_time) + " seconds)" if q_time >= 0.1 else "") ) # if q.name in report.payloads else "")\n # print("=" * nL)\n # q_with_outstanding_init = None\n #\n # # item.question = q # Set the parent question instance for later reference.\n # item_title_print = ss = "*** q%i.%i) %s"%(n+1, j+1, item.title)\n #\n # if show_progress_bar:\n # cc = ActiveProgress(t=item.estimated_time, title=item_title_print)\n # else:\n # print(item_title_print + ( \'.\'*max(0, nL-4-len(ss)) ), end="")\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n # if show_progress_bar:\n # cc.terminate()\n # sys.stdout.flush()\n # print(item_title_print + (\'.\' * max(0, nL - 4 - len(ss))), end="")\n #\n # if not hidden:\n # ss = "PASS" if current == possible else "*** FAILED"\n # if tsecs >= 0.1:\n # ss += " ("+ str(tsecs) + " seconds)"\n # print(ss)\n\n # ws, possible, obtained = upack(q_)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n # possible = int(ws @ possible)\n # obtained = int(ws @ obtained)\n # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"*** Question q{n+1}"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n if m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package))\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n print(" ")\n print("="*n)\n print("Final evaluation")\n print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f"*** {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single file: ")\n print(">", token)\n print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\nimport random\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n\n def test_add(self):\n """ Docstring for this method """\n from cs102.homework1 import add\n self.assertEqualC(add(2,2))\n self.assertEqualC(add(-100, 5))\n\n def test_reverse(self):\n """ Reverse a list """ # Add a title to the test.\n from cs102.homework1 import reverse_list\n self.assertEqualC(reverse_list([1,2,3]))\n\n\nclass Question2(UTestCase):\n """ Second problem """\n @cache\n def my_reversal(self, ls):\n # The \'@cache\' decorator ensures the function is not run on the *students* computer\n # Instead the code is run on the teachers computer and the result is passed on with the\n # other pre-computed results -- i.e. this function will run regardless of how the student happens to have\n # implemented reverse_list.\n from cs102.homework1 import reverse_list\n return reverse_list(ls)\n\n def test_reverse_tricky(self):\n ls = [2,4,8]\n self.title = f"Reversing a small list containing {ls=}"\n ls2 = self.my_reversal( tuple(ls) ) # This will always produce the right result.\n ls3 = self.my_reversal( tuple([1,2,3]) ) # Also works; the cache respects input arguments.\n self.assertEqualC(self.my_reversal( tuple(ls2) )) # This will actually test the students code.\n\n\nimport cs102\nclass Report2(Report):\n title = "CS 101 Report 2"\n questions = [(Week1, 10), (Question2, 8) ] # Include a single question for 10 credits.\n pack_imports = [cs102]' -report1_payload = '8004959a010000000000007d94288c055765656b31947d94288c055765656b31948c08746573745f6164649486948c057469746c659486948c19446f63737472696e6720666f722074686973206d6574686f64946803680486948c066173736572749486947d94284b004b044b014aa1ffffff756803680486948c0474696d6594869447000000000000000068038c0c746573745f72657665727365948694680686948c0e526576657273652061206c69737494680368108694680a86947d944b005d94284b034b024b016573680368108694680e86944700000000000000008c0474696d6594470000000000000000758c095175657374696f6e32947d94288c095175657374696f6e32948c13746573745f726576657273655f747269636b799486948c066173736572749486947d944b005d94284b024b044b086573681d681e86948c057469746c659486948c2e526576657273696e67206120736d616c6c206c69737420636f6e7461696e696e67206c733d5b322c20342c20385d94681d681e86948c0474696d65948694470000000000000000681a473f5066000000000075752e' +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n"""\nimport numpy as np\nimport sys\nimport re\nimport threading\nimport tqdm\nimport pickle\nimport os\nfrom io import StringIO\nimport io\nfrom unittest.runner import _WritelnDecorator\nfrom typing import Any\nimport inspect\nimport textwrap\nimport colorama\nfrom colorama import Fore\nfrom functools import _make_key, RLock\nfrom collections import namedtuple\nimport unittest\nimport time\n\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ncolorama.init(autoreset=True) # auto resets your settings after every output\n\ndef gprint(s):\n print(f"{Fore.GREEN}{s}")\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\n\ndef setup_dir_by_class(C, base_dir):\n name = C.__class__.__name__\n return base_dir, name\n\n\nclass Logger(object):\n def __init__(self, buffer):\n assert False\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\n# @classmethod\n# class OrderedClassMembers(type):\n# def __prepare__(self, name, bases):\n# assert False\n# return collections.OrderedDict()\n#\n# def __new__(self, name, bases, classdict):\n# ks = list(classdict.keys())\n# for b in bases:\n# ks += b.__ordered__\n# classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n# return type.__new__(self, name, bases, classdict)\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 120 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q, _) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q, _) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n loader = unittest.TestLoader()\n for q, _ in self.questions:\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self, with_coverage=False):\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = True\n q._report = self\n\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\': True}\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = False\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct + 1)\n if i > 0 and l.find("|", i + 1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = r\'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar", show_progress_bar=True, file=None):\n if file == None:\n file = sys.stdout\n self.file = file\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.01\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n self.pbar = None\n\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar = None\n\n self.file.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=self.file, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\')\n\n for _ in range(self.n - 1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\ndef dprint(first, last, nL, extra = "", file=None, dotsym=\'.\', color=\'white\'):\n if file == None:\n file = sys.stdout\n\n # ss = self.item_title_print\n # state = "PASS" if success else "FAILED"\n dot_parts = (dotsym * max(0, nL - len(last) - len(first)))\n # if self.show_progress_bar or True:\n print(first + dot_parts, end="", file=file)\n # else:\n # print(dot_parts, end="", file=self.cc.file)\n last += extra\n # if tsecs >= 0.5:\n # state += " (" + str(tsecs) + " seconds)"\n print(last, file=file)\n\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n cc = None\n\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n self.successes.append(test)\n self.cc_terminate()\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n self.cc.file.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="", file=self.cc.file)\n else:\n print(dot_parts, end="", file=self.cc.file)\n\n if tsecs >= 0.5:\n state += " (" + str(tsecs) + " seconds)"\n print(state, file=self.cc.file)\n\n def startTest(self, test):\n # j =self.testsRun\n self.testsRun += 1\n # item_title = self.getDescription(test)\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n self.item_title_print = " * q%i.%i) %s" % (UTextResult.number + 1, self.testsRun, item_title)\n estimated_time = 10\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar, file=sys.stdout)\n else:\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n self._stdout = sys.stdout\n sys.stdout = io.StringIO()\n\n def stopTest(self, test):\n sys.stdout = self._stdout\n super().stopTest(test)\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass is None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n print(" " * max(0, self.nL - len(self.cc.title)) + (" (" + str(q_time) + " seconds)" if q_time >= 0.5 else ""))\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n stream = io.StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)))\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n\n return wrapper\n\n\ndef get_hints(ss):\n if ss == None:\n return None\n try:\n ss = textwrap.dedent(ss)\n ss = ss.replace(\'\'\'"""\'\'\', "").strip()\n hints = ["hints:", ]\n j = np.argmax([ss.lower().find(h) for h in hints])\n h = hints[j]\n ss = ss[ss.find(h) + len(h) + 1:]\n ss = "\\n".join([l for l in ss.split("\\n") if not l.strip().startswith(":")])\n ss = textwrap.dedent(ss)\n ss = ss.strip()\n return ss\n except Exception as e:\n print("bad hints", ss, e)\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n _with_coverage = False\n _report = None # The report used. This is very, very hacky and should always be None. Don\'t rely on it!\n\n def capture(self):\n if hasattr(self, \'_stdout\') and self._stdout is not None:\n file = self._stdout\n else:\n file = sys.stdout\n return Capturing2(stdout=file)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ is not None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n if self._with_coverage:\n if not hasattr(self._report, \'covcache\'):\n self._report.covcache = {}\n import coverage\n self.cov = coverage.Coverage()\n self.cov.start()\n self.setUp()\n\n def _callTearDown(self):\n self.tearDown()\n if self._with_coverage:\n from pathlib import Path\n from snipper import snipper\n self.cov.stop()\n data = self.cov.get_data()\n base, _, _ = self._report._import_base_relative()\n for file in data.measured_files():\n file = os.path.normpath(file)\n root = Path(base)\n child = Path(file)\n if root in child.parents:\n with open(child, \'r\') as f:\n s = f.read()\n lines = s.splitlines()\n garb = \'GARBAGE\'\n\n lines2 = snipper.censor_code(lines, keep=True)\n assert len(lines) == len(lines2)\n\n for l in data.contexts_by_lineno(file):\n if lines2[l].strip() == garb:\n if self.cache_id() not in self._report.covcache:\n self._report.covcache[self.cache_id()] = {}\n\n rel = os.path.relpath(child, root)\n cc = self._report.covcache[self.cache_id()]\n j = 0\n for j in range(l, -1, -1):\n if "def" in lines2[j] or "class" in lines2[j]:\n break\n from snipper.snipper import gcoms\n fun = lines2[j]\n comments, _ = gcoms("\\n".join(lines2[j:l]))\n if rel not in cc:\n cc[rel] = {}\n cc[rel][fun] = (l, "\\n".join(comments))\n self._cache_put((self.cache_id(), \'coverage\'), self._report.covcache)\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd is None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n sd = self.shortDescriptionStandard()\n title = self._cache_get((self.cache_id(), \'title\'), sd)\n return title if title is not None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome is None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc is not None:\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard())\n\n self._cache2[(self.cache_id(), \'assert\')] = {}\n res = testMethod()\n elapsed = time.time() - t\n self._get_outcome()[self.cache_id()] = res\n self._cache_put((self.cache_id(), "time"), elapsed)\n\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return c, m\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n # sys.stdout = self._stdout\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n self.__class__._cache[\n key] = {} # A new dict. We manually insert it because we have to use that the dict is mutable.\n cache = self._cache_get(key)\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, f"Key {id} not found in cache; framework files missing. Please run deploy()")\n\n # The order of these calls is important. If the method assert fails, we should still store the correct result in cache.\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n assert_fun(first, _expected, *args, **kwargs)\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache is not None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n if os.path.exists(cfile):\n try:\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n except Exception as e:\n print("Bad cache", cfile)\n print(e)\n else:\n print("Warning! data file not found", cfile)\n\n def _feedErrorsToResult(self, result, errors):\n """ Use this to show hints on test failure. """\n if not isinstance(result, UTextResult):\n er = [e for e, v in errors if v != None]\n\n if len(er) > 0:\n hints = []\n key = (self.cache_id(), \'coverage\')\n if self._cache_contains(key):\n CC = self._cache_get(key)\n for id in CC:\n if id == self.cache_id():\n cl, m = id\n gprint(f"> An error occured while solving: {cl}.{m}. The files/methods you need to edit are:") # For the test {id} in {file} you should edit:")\n for file in CC[id]:\n rec = CC[id][file]\n gprint(f"> * {file}")\n for l in rec:\n _, comments = CC[id][file][l]\n hint = get_hints(comments)\n\n if hint != None:\n hints.append(hint)\n gprint(f"> - {l}")\n\n er = er[0]\n doc = er._testMethodDoc\n if doc is not None:\n hint = get_hints(er._testMethodDoc)\n if hint is not None:\n hints = [hint] + hints\n if len(hints) > 0:\n gprint("> Hints:")\n gprint(textwrap.indent("\\n".join(hints), "> "))\n\n super()._feedErrorsToResult(result, errors)\n\n def startTestRun(self):\n # print("asdfsdaf 11", file=sys.stderr)\n super().startTestRun()\n # print("asdfsdaf")\n\n def _callTestMethod(self, method):\n # print("asdfsdaf")\n super()._callTestMethod(method)\n\n\ndef hide(func):\n return func\n\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n# 817\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\nimport inspect\nimport os\nimport argparse\nimport time\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print(b + " v" + __version__ + ", started: " + dt_string+ "\\n")\n # print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print(s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n UTextResult.nL = report.nL\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"Question {n+1} total"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")",\n last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL)\n\n # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n # print(" ")\n # print("="*n)\n # print("Final evaluation")\n # print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n print("")\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f" * {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single unmodified file: ")\n print(">", token)\n # print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\n\nclass Week1(UTestCase):\n """ The first question for week 1. """\n def test_add(self):\n """ Docstring for this method """\n from cs102.homework1 import add\n self.assertEqualC(add(2,2))\n with self.capture() as out:\n print("hello world 42")\n self.assertEqual(out.numbers[0], 42)\n self.assertEqualC(add(-100, 5))\n\n def test_reverse(self):\n """ Reverse a list """ # Add a title to the test.\n from cs102.homework1 import reverse_list\n self.assertEqualC(reverse_list([1,2,3]))\n\n\nclass Question2(UTestCase):\n """ Second problem """\n @cache\n def my_reversal(self, ls):\n # The \'@cache\' decorator ensures the function is not run on the *students* computer\n # Instead the code is run on the teachers computer and the result is passed on with the\n # other pre-computed results -- i.e. this function will run regardless of how the student happens to have\n # implemented reverse_list.\n from cs102.homework1 import reverse_list\n return reverse_list(ls)\n\n def test_reverse_tricky(self):\n ls = [2,4,8]\n self.title = f"Reversing a small list containing {ls=}" # Titles can be set like this at any point in the function body.\n ls2 = self.my_reversal( tuple(ls) ) # This will always produce the right result.\n ls3 = self.my_reversal( tuple([1,2,3]) ) # Also works; the cache respects input arguments.\n self.assertEqualC(self.my_reversal( tuple(ls2) )) # This will actually test the students code.\n\n\nimport cs102\nclass Report2(Report):\n title = "CS 101 Report 2"\n questions = [(Week1, 10), (Question2, 8) ]\n pack_imports = [cs102]' +report1_payload = '80049510010000000000007d94288c055765656b31947d94288c055765656b31948c08746573745f6164649486948c066173736572749486947d94284b004b044b014aa1ffffff7568038c0c746573745f72657665727365948694680686947d944b005d94284b034b024b0165738c0474696d6594473fe6a7e700000000758c095175657374696f6e32947d94288c095175657374696f6e32948c13746573745f726576657273655f747269636b799486948c057469746c659486948c2e526576657273696e67206120736d616c6c206c69737420636f6e7461696e696e67206c733d5b322c20342c20385d946811681286948c066173736572749486947d944b005d94284b024b044b086573680e473facac280000000075752e' name="Report2" report = source_instantiate(name, report1_source, report1_payload) diff --git a/examples/example_framework/students/cs102/unitgrade/Question2.pkl b/examples/example_framework/students/cs102/unitgrade/Question2.pkl index 634a7fbbe4bad27f24b2d894ef3c0b37c4f5dd94..b950c49faa2b91b7675ee266d63a13fe3652e3cc 100644 GIT binary patch delta 119 zcmZo*e!;}jz%n&<B8&dS;0<CN8JrnBnvGMu8NHdjncJr%`qfU!5ST2<sO$rm;_zni zW`ap|aqR0^R{wxw2Ul_1l%)14ZBt^WXm~Suvv_l)W-w+jwN2?^ODrx<Eh?GPHl=oo LH$#R>aj6~v?r|tR delta 140 zcmaFC)WFQrz%sRTB8$E(TVio>YEj9Qwkfq!ycuGrXm~Suvv_l)7H2SKFikYrEXJF` zmm#RxIK`XMo5`EGeM+KV?UW4Bi9eNH;4&QEEZ$5onJ$ifJ<IAJaO~hJZkv+SKBa9+ Z20O^CwkbU<C7HRYQ`)AuGeAM99sr;8Ev*0m diff --git a/examples/example_framework/students/cs102/unitgrade/Week1.pkl b/examples/example_framework/students/cs102/unitgrade/Week1.pkl index 7912698f036128bb2a8b616c2a66c58ac9e774e5..6b4ee502e9c67e2ff3aba1b11c4911bb88195e34 100644 GIT binary patch delta 35 rcmcc4n99<?GBs)<i^9YJ>50vvg0)k;8Dghscr$x5c{8RKm+Aok$2|&n delta 146 zcmYej&dAchGWE<v76nb#lFX8v)G2LKdL&))lZ#7=GV{_E((;QGN-{Ew6>?KcGV)WV zWH4qhO;nT<%V27o(!)}cnG4e3&Hx1yZN=?-_<~Z)Qj3aH6%rM4GK)(<TKL+gWN@@i gsh#4@5IaS~o7tPmn=!RGgE0dn!P7PcZa}FX06nQK!~g&Q diff --git a/examples/example_jupyter/instructor/cs105/.coverage b/examples/example_jupyter/instructor/cs105/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..572452543e2092b38f99ec9afedd779fdc155f29 GIT binary patch literal 53248 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 literal 0 HcmV?d00001 diff --git a/examples/example_jupyter/instructor/cs105/Report1Jupyter_handin_18_of_18.token b/examples/example_jupyter/instructor/cs105/Report1Jupyter_handin_18_of_18.token new file mode 100644 index 0000000000000000000000000000000000000000..cfb09b6ef919a3d4c721651cb236201490461fc4 GIT binary patch literal 58736 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 Da6eMK literal 0 HcmV?d00001 diff --git a/examples/example_jupyter/instructor/cs105/__pycache__/homework1.cpython-38.pyc b/examples/example_jupyter/instructor/cs105/__pycache__/homework1.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..405a1c978c8bf2114f9b470bfd5312fb8f02188f GIT binary patch literal 187 zcmWIL<>g{vU|{$cp_?eqz`*br#6iZ43=9ko3=9m#EDQ_`DGb33nv8xc8Hzx{2;x_Q zvsFxJacWU<Oi5`*YK%*Ma%paAUP*CGX<lYYdQoCZYJ5RaW?5oMYD{WHVs1fBYB7uv zpH*5=S&~{5lbKgsQdF8;l3x^)Tx@7y8k3Qqn_8Y<lx?V2P<e~PCO1E&G$+*#Wb<c` F3juJhGy4Dl literal 0 HcmV?d00001 diff --git a/examples/example_jupyter/instructor/cs105/__pycache__/report5.cpython-38.pyc b/examples/example_jupyter/instructor/cs105/__pycache__/report5.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c80ebb8aac0fa657aacc6cb4b52f65783b2e1ec8 GIT binary patch literal 2312 zcmWIL<>g{vU|={t-5_xjD+9x05C<8vFfcGUFfcF_Ph((UNMT4}%wfo7jAG1Xieh2} z@tJa%b6KKTz-;Cm)+p8#h7{%;wkWm~h7^_@hFtb2_FRrA4n~F))+kPQh7`6G_7;W| z_Ee^3<|r<Ah7^tz&K8CgPB5R_ogsxQg}a3zg*%nInK_EbogsxMg|~$vg*TP4nK_C# zl`n-ao2h70DoZMFGea{YBSQ*vFoP!lOOT5-nQpNKr55BDmE7VC4M{C7aZW5w)nvRS zo?4ceQ<_+k8easKjV~@KO-aow@zZ3w#pRq>P*PfynU`*KixbZN#a*77nr)<)Sx}jm zlngQ(8N)+FkAZ<9l_82Tg&~S5l{tkmg=r3B3Udle3rjO&6iW(g3R??96l)553P%e= z6k7^oFoP!NE!ObV)NDh)m5jI8ic(8Ti}I2gkqig1IT;ujoIxS3!oa{#!qCjnFT%)B z!kERhfVqTaA!7|gJZm_EDMKJb5JLn*3PUi%N=83Twjx#r28LT4nYo|<$V<A#;g?^M znv|cPeTyY0KQZMNOIl`5>Mf?cq?L@fn3HpgS27d{GcYjxigUJ#2`x@7Dvl{B%}9-L z$xklLP0cGQjw#K{EJ-g)Oi7I|D9S8LEJ=+?tw_u*$Vn}RG2*jI3o1)ei()eKic5-0 zlS}f8Vv>su4NPMo0d1;RP<e~1IJG3Sz&R(exESQIVo>NYFmW(e34jAa4<U^a5Nxo3 zFkoO{s9{(D4TKcN6vl;2DNGBQ7*R!;7BZzUgG51rqRHa-l8J$V;U&nzmmCZX44N#r zxD$(uQ;SMm3riDoia=sT0-#W1&8<u;&0EQMi={X<Ck-4f5JChb%TbbAToRv{lEMdy z6p(QYjAD#cd=U4;Br#kMQV5PlkRdSFXEB4^4$3US44O=SRlFIgIXU?X<@rT9DGElK zEJczG3=BvvV=hZ9Dv}0?fg%gZQ9NLKi&D!{i;7c)Kn??WmVvQK2+3JcX-)Q99P#ma ziMgrq@wd3*<8$*<N^?MLp7?lBNI+${lR=S`n_7~QpHd_WvQrjB$b$%k?O+zjOJG-m z!mt=ba4<443b1gnfJJd<86%`DlfoFqoWdBzlFEvfb=XqbA(^L{F^U76ZP;&d`8mO| zv|kY?0|Ns_c!LDN;jISCHe#@BQ^SyjHRBX<gOUX#>lE>T{K*R<K=FKwBM+8;nB()3 ziUdItV84S2Z~%b92ozt4G%bp7CBB3W%H?3Ef<mr_A&Vi4F_;137^YjC@$uk@h>tG< z`2<_+gHx>>Jkpt}c#-VQOVZ>lQUEzu5kx3~2vraPHcc7C0y+E^M?q13L26M+CD=4b z(gOPpOn^NIDuBd5)_}r>gOP=i1suyvj71<Bj93PxKyXzdfga0DDa<VlQ7kDe!3>(L zw>Se!Q;SP7^Ye^~*s;YVJl})N0p*+;hAhSfOrTV<kZB>P&}Rr{&}8;20=caSq)3xF ziU%ocJ43uA1@bGF%u@tP*B~DhgIo;CGjIp#fwNU&N{XK*E7m{+X~h~=nhXpKps)rf z22fZPfkZIEh#6L$f?@+!d4O_4DoYAe8dEB33UeA$I#U!|DtkI8^+Q684^pogdO-@@ zDt_l+1w#W<1&FYM=}S-@4h{)FjL5&mT9R3klM2q5U^hi^7DB_n_!ehIer{@ceo?kz z6i-27a&|nV{wT)kMJ)yf26a$!0NDoeA{!&ye-`E<kT|G>$xY16i;vgjj}nE~9Y%Tx zW)Uc~i$DPoC5sS6D2UHU%_&GNDmE$t6`e)8px6T?JaElc1WFV|{2(z<rU55Fuy4S& wL!t_ta1jJ33E$$dfs}T3pgdO$N-zS9Jd7NS9E<`?EQ|tt0z3k&0_+^j050p;!~g&Q literal 0 HcmV?d00001 diff --git a/examples/example_jupyter/instructor/cs105/__pycache__/week2.cpython-38.pyc b/examples/example_jupyter/instructor/cs105/__pycache__/week2.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d81c0675c7056e7e11ab8b96d289c407997be566 GIT binary patch literal 466 zcmWIL<>g{vU|?{asFQe(fq~&Mh=Yuo85kHG7#J9e?HCvsQW&BbQW&EcQ<zeiTNt94 zQkha%QdparQrN^9qL@=zQW={W85vSpQkg+?3VRf53P%cO3qurJ3Rf_LCU=#fvVv<x zYEg1#ajJrmo{^p*R~2tYYEDkRLV12sPKtt2G82;JAT|g)gY4&EU|^_Ws9_Xg2xib^ z^jpcG$#jb`@fKrJ5hDWw1DLqQU7nhnZKRi3P??u>i#4}0tu#*tq!(le17i^@0|UcL z5c?&_S1&;xfBA}mfgu@W0?b|z8zc?3l#hXdp@gA^A%(G-DTPTKWHqy25gW)HkUC9> znVO8Z7*oJ5fDkMU3=AOab4zoyk*w!oU|{gmWWU88AD@z+93Ov+xh$~=?5JC8`K2WV zr6spm3yL!HN^UV$6@e5(Oaoa2VS&x%u*uC&Da}c>V+R=xav+Nc3nK?JA0r1N2PX?7 E09j2^v;Y7A literal 0 HcmV?d00001 diff --git a/examples/example_jupyter/instructor/cs105/deploy.py b/examples/example_jupyter/instructor/cs105/deploy.py new file mode 100644 index 0000000..8acafa9 --- /dev/null +++ b/examples/example_jupyter/instructor/cs105/deploy.py @@ -0,0 +1,15 @@ +from report5 import Report1Jupyter +from unitgrade_private2.hidden_create_files import setup_grade_file_report +from snipper import snip_dir + +if __name__ == "__main__": + setup_grade_file_report(Report1Jupyter, minify=False, obfuscate=False, execute=False) + + # from unitgrade_private2.hidden_gather_upload import gather_upload_to_campusnet + # gather_upload_to_campusnet((Report1Flat())) + + # Deploy the files using snipper: https://gitlab.compute.dtu.dk/tuhe/snipper + snip_dir.snip_dir(source_dir="", dest_dir="../../students/cs105", clean_destination_dir=True, exclude=['__pycache__', '*.token', 'deploy.py']) + + + diff --git a/examples/example_jupyter/instructor/cs105/homework1.py b/examples/example_jupyter/instructor/cs105/homework1.py new file mode 100644 index 0000000..b01dcab --- /dev/null +++ b/examples/example_jupyter/instructor/cs105/homework1.py @@ -0,0 +1 @@ +# This file is blank. diff --git a/examples/example_jupyter/instructor/cs105/report5.py b/examples/example_jupyter/instructor/cs105/report5.py new file mode 100644 index 0000000..38e9fbd --- /dev/null +++ b/examples/example_jupyter/instructor/cs105/report5.py @@ -0,0 +1,49 @@ +from src.unitgrade2.unitgrade2 import Report, UTestCase +from src.unitgrade2 import evaluate_report_student +import homework1 +import importnb +from src.unitgrade2.unitgrade2 import Capturing2 + +file = 'week2.ipynb' +class Week1(UTestCase): + @classmethod + def setUpClass(cls) -> None: + with Capturing2(): + cls.nb = importnb.Notebook.load(file) + + def test_add(self): + self.assertEqual(Week1.nb.myfun(2,2), 4) + self.assertEqual(Week1.nb.myfun(2,4), 8) + + def test_reverse(self): + self.assertEqual(Week1.nb.var, "hello world 2") + +# Nicer: Automatically load the notebook. +class NBTestCase(UTestCase): + notebook = None + _nb = None + @classmethod + def setUpClass(cls) -> None: + with Capturing2(): + cls._nb = importnb.Notebook.load(cls.notebook) + + @property + def nb(self): + return self.__class__._nb + +class Question2(NBTestCase): + notebook = "week2.ipynb" + def test_add(self): + self.assertEqualC(self.nb.myfun(2,8)) + +class Report1Jupyter(Report): + title = "CS 105 Report 5" + questions = [(Week1, 10), + (Question2, 8) + ] # Include a single question for 10 credits. + pack_imports = [homework1] + +if __name__ == "__main__": + # Uncomment to simply run everything as a unittest: + # unittest.main(verbosity=2) + evaluate_report_student(Report1Jupyter()) diff --git a/examples/example_jupyter/instructor/cs105/report5_grade.py b/examples/example_jupyter/instructor/cs105/report5_grade.py new file mode 100644 index 0000000..3e65178 --- /dev/null +++ b/examples/example_jupyter/instructor/cs105/report5_grade.py @@ -0,0 +1,336 @@ + +import numpy as np +from tabulate import tabulate +from datetime import datetime +import pyfiglet +import unittest +import inspect +import os +import argparse +import time + +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: +To run all tests in a report: + +> python assignment1_dp.py + +To run only question 2 or question 2.1 + +> python assignment1_dp.py -q 2 +> python assignment1_dp.py -q 2.1 + +Note this scripts does not grade your report. To grade your report, use: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('-q', nargs='?', type=str, default=None, help='Only evaluate this question (e.g.: -q 2)') +parser.add_argument('--showexpected', action="store_true", help='Show the expected/desired result') +parser.add_argument('--showcomputed', action="store_true", help='Show the answer your code computes') +parser.add_argument('--unmute', action="store_true", help='Show result of print(...) commands in code') +parser.add_argument('--passall', action="store_true", help='Automatically pass all tests. Useful when debugging.') + +def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False): + args = parser.parse_args() + if question is None and args.q is not None: + question = args.q + if "." in question: + question, qitem = [int(v) for v in question.split(".")] + else: + question = int(question) + + if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file: + raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation") + + if unmute is None: + unmute = args.unmute + if passall is None: + passall = args.passall + + results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute, + show_tol_err=show_tol_err) + + + if question is None: + print("Provisional evaluation") + tabulate(table_data) + table = table_data + print(tabulate(table)) + print(" ") + + fr = inspect.getouterframes(inspect.currentframe())[1].filename + gfile = os.path.basename(fr)[:-3] + "_grade.py" + if os.path.exists(gfile): + print("Note your results have not yet been registered. \nTo register your results, please run the file:") + print(">>>", gfile) + print("In the same manner as you ran this file.") + + + return results + + +def upack(q): + # h = zip([(i['w'], i['possible'], i['obtained']) for i in q.values()]) + h =[(i['w'], i['possible'], i['obtained']) for i in q.values()] + h = np.asarray(h) + return h[:,0], h[:,1], h[:,2], + +class UnitgradeTextRunner(unittest.TextTestRunner): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + +class SequentialTestLoader(unittest.TestLoader): + def getTestCaseNames(self, testCaseClass): + test_names = super().getTestCaseNames(testCaseClass) + # testcase_methods = list(testCaseClass.__dict__.keys()) + ls = [] + for C in testCaseClass.mro(): + if issubclass(C, unittest.TestCase): + ls = list(C.__dict__.keys()) + ls + testcase_methods = ls + test_names.sort(key=testcase_methods.index) + return test_names + +def evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False, + show_progress_bar=True, + show_tol_err=False, + big_header=True): + + now = datetime.now() + if big_header: + ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom") + b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) + else: + b = "Unitgrade" + dt_string = now.strftime("%d/%m/%Y %H:%M:%S") + print(b + " v" + __version__ + ", started: " + dt_string+ "\n") + # print("Started: " + dt_string) + s = report.title + if hasattr(report, "version") and report.version is not None: + s += " version " + report.version + print(s, "(use --help for options)" if show_help_flag else "") + # print(f"Loaded answers from: ", report.computed_answers_file, "\n") + table_data = [] + t_start = time.time() + score = {} + loader = SequentialTestLoader() + + for n, (q, w) in enumerate(report.questions): + if question is not None and n+1 != question: + continue + suite = loader.loadTestsFromTestCase(q) + qtitle = q.question_title() if hasattr(q, 'question_title') else q.__qualname__ + q_title_print = "Question %i: %s"%(n+1, qtitle) + print(q_title_print, end="") + q.possible = 0 + q.obtained = 0 + q_ = {} # Gather score in this class. + UTextResult.q_title_print = q_title_print # Hacky + UTextResult.show_progress_bar = show_progress_bar # Hacky. + UTextResult.number = n + UTextResult.nL = report.nL + + res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) + + possible = res.testsRun + obtained = len(res.successes) + + assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun + + obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 + score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} + q.obtained = obtained + q.possible = possible + + s1 = f" * q{n+1}) Total" + s2 = f" {q.obtained}/{w}" + print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 ) + print(" ") + table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"]) + + ws, possible, obtained = upack(score) + possible = int( msum(possible) ) + obtained = int( msum(obtained) ) # Cast to python int + report.possible = possible + report.obtained = obtained + now = datetime.now() + dt_string = now.strftime("%H:%M:%S") + + dt = int(time.time()-t_start) + minutes = dt//60 + seconds = dt - minutes*60 + plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") + + dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", + last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) + + # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total") + + table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) + results = {'total': (obtained, possible), 'details': score} + return results, table_data + + +from tabulate import tabulate +from datetime import datetime +import inspect +import json +import os +import bz2 +import pickle +import os + +def bzwrite(json_str, token): # to get around obfuscation issues + with getattr(bz2, 'open')(token, "wt") as f: + f.write(json_str) + +def gather_imports(imp): + resources = {} + m = imp + # for m in pack_imports: + # print(f"*** {m.__name__}") + f = m.__file__ + # dn = os.path.dirname(f) + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = str(__import__(m.__name__.split('.')[0]).__path__) + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: + top_package = os.path.dirname(m.__file__) + module_import = True + else: + top_package = __import__(m.__name__.split('.')[0]).__path__._path[0] + module_import = False + + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = os.path.dirname(top_package) + import zipfile + # import strea + # zipfile.ZipFile + import io + # file_like_object = io.BytesIO(my_zip_data) + zip_buffer = io.BytesIO() + with zipfile.ZipFile(zip_buffer, 'w') as zip: + # zip.write() + for root, dirs, files in os.walk(top_package): + for file in files: + if file.endswith(".py"): + fpath = os.path.join(root, file) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) + zip.write(fpath, v) + + resources['zipfile'] = zip_buffer.getvalue() + resources['top_package'] = top_package + resources['module_import'] = module_import + return resources, top_package + + if f.endswith("__init__.py"): + for root, dirs, files in os.walk(os.path.dirname(f)): + for file in files: + if file.endswith(".py"): + # print(file) + # print() + v = os.path.relpath(os.path.join(root, file), top_package) + with open(os.path.join(root, file), 'r') as ff: + resources[v] = ff.read() + else: + v = os.path.relpath(f, top_package) + with open(f, 'r') as ff: + resources[v] = ff.read() + return resources + +import argparse +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Use this script to get the score of your report. Example: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('--noprogress', action="store_true", help='Disable progress bars') +parser.add_argument('--autolab', action="store_true", help='Show Autolab results') + +def gather_upload_to_campusnet(report, output_dir=None): + n = report.nL + args = parser.parse_args() + results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, + show_progress_bar=not args.noprogress, + big_header=not args.autolab) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) + # also load the source code of missing files... + + sources = {} + print("") + if not args.autolab: + if len(report.individual_imports) > 0: + print("By uploading the .token file, you verify the files:") + for m in report.individual_imports: + print(">", m.__file__) + print("Are created/modified individually by you in agreement with DTUs exam rules") + report.pack_imports += report.individual_imports + + if len(report.pack_imports) > 0: + print("Including files in upload...") + for k, m in enumerate(report.pack_imports): + nimp, top_package = gather_imports(m) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import + nimp['name'] = m.__name__ + sources[k] = nimp + # if len([k for k in nimp if k not in sources]) > 0: + print(f" * {m.__name__}") + # sources = {**sources, **nimp} + results['sources'] = sources + + if output_dir is None: + output_dir = os.getcwd() + + payload_out_base = report.__class__.__name__ + "_handin" + + obtain, possible = results['total'] + vstring = "_v"+report.version if report.version is not None else "" + + token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) + token = os.path.join(output_dir, token) + with open(token, 'wb') as f: + pickle.dump(results, f) + + if not args.autolab: + print(" ") + print("To get credit for your results, please upload the single unmodified file: ") + print(">", token) + # print("To campusnet without any modifications.") + + # print("Now time for some autolab fun") + +def source_instantiate(name, report1_source, payload): + eval("exec")(report1_source, globals()) + pl = pickle.loads(bytes.fromhex(payload)) + report = eval(name)(payload=pl, strict=True) + # report.set_payload(pl) + return report + + + +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n"""\nimport numpy as np\nimport sys\nimport re\nimport threading\nimport tqdm\nimport pickle\nimport os\nfrom io import StringIO\nimport io\nfrom unittest.runner import _WritelnDecorator\nfrom typing import Any\nimport inspect\nimport textwrap\nimport colorama\nfrom colorama import Fore\nfrom functools import _make_key, RLock\nfrom collections import namedtuple\nimport unittest\nimport time\n\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ncolorama.init(autoreset=True) # auto resets your settings after every output\n\ndef gprint(s):\n print(f"{Fore.GREEN}{s}")\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\n\ndef setup_dir_by_class(C, base_dir):\n name = C.__class__.__name__\n return base_dir, name\n\n\nclass Logger(object):\n def __init__(self, buffer):\n assert False\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\n# @classmethod\n# class OrderedClassMembers(type):\n# def __prepare__(self, name, bases):\n# assert False\n# return collections.OrderedDict()\n#\n# def __new__(self, name, bases, classdict):\n# ks = list(classdict.keys())\n# for b in bases:\n# ks += b.__ordered__\n# classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n# return type.__new__(self, name, bases, classdict)\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 120 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q, _) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q, _) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n loader = unittest.TestLoader()\n for q, _ in self.questions:\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self, with_coverage=False):\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = True\n q._report = self\n\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n # print(self.questions)\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n print("q is", q())\n q()._cache_put(\'time\', q.time) # = q.time\n report_cache[q.__qualname__] = q._cache2\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\': True}\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = False\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct + 1)\n if i > 0 and l.find("|", i + 1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = r\'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar", show_progress_bar=True, file=None):\n if file == None:\n file = sys.stdout\n self.file = file\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.01\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n self.pbar = None\n\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar = None\n\n self.file.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=self.file, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\')\n\n for _ in range(self.n - 1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\ndef dprint(first, last, nL, extra = "", file=None, dotsym=\'.\', color=\'white\'):\n if file == None:\n file = sys.stdout\n\n # ss = self.item_title_print\n # state = "PASS" if success else "FAILED"\n dot_parts = (dotsym * max(0, nL - len(last) - len(first)))\n # if self.show_progress_bar or True:\n print(first + dot_parts, end="", file=file)\n # else:\n # print(dot_parts, end="", file=self.cc.file)\n last += extra\n # if tsecs >= 0.5:\n # state += " (" + str(tsecs) + " seconds)"\n print(last, file=file)\n\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n cc = None\n\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n self.successes.append(test)\n self.cc_terminate()\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n self.cc.file.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="", file=self.cc.file)\n else:\n print(dot_parts, end="", file=self.cc.file)\n\n if tsecs >= 0.5:\n state += " (" + str(tsecs) + " seconds)"\n print(state, file=self.cc.file)\n\n def startTest(self, test):\n # j =self.testsRun\n self.testsRun += 1\n # item_title = self.getDescription(test)\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n self.item_title_print = " * q%i.%i) %s" % (UTextResult.number + 1, self.testsRun, item_title)\n estimated_time = 10\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar, file=sys.stdout)\n else:\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n self._stdout = sys.stdout\n sys.stdout = io.StringIO()\n\n def stopTest(self, test):\n sys.stdout = self._stdout\n super().stopTest(test)\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass is None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n print(" " * max(0, self.nL - len(self.cc.title)) + (" (" + str(q_time) + " seconds)" if q_time >= 0.5 else ""))\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n stream = io.StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)))\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n\n return wrapper\n\n\ndef get_hints(ss):\n if ss == None:\n return None\n try:\n ss = textwrap.dedent(ss)\n ss = ss.replace(\'\'\'"""\'\'\', "").strip()\n hints = ["hints:", ]\n j = np.argmax([ss.lower().find(h) for h in hints])\n h = hints[j]\n ss = ss[ss.find(h) + len(h) + 1:]\n ss = "\\n".join([l for l in ss.split("\\n") if not l.strip().startswith(":")])\n ss = textwrap.dedent(ss)\n ss = ss.strip()\n return ss\n except Exception as e:\n print("bad hints", ss, e)\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n _with_coverage = False\n _report = None # The report used. This is very, very hacky and should always be None. Don\'t rely on it!\n\n def capture(self):\n if hasattr(self, \'_stdout\') and self._stdout is not None:\n file = self._stdout\n else:\n # self._stdout = sys.stdout\n # sys._stdout = io.StringIO()\n file = sys.stdout\n return Capturing2(stdout=file)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ is not None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n if self._with_coverage:\n if not hasattr(self._report, \'covcache\'):\n self._report.covcache = {}\n import coverage\n self.cov = coverage.Coverage()\n self.cov.start()\n self.setUp()\n\n def _callTearDown(self):\n self.tearDown()\n if self._with_coverage:\n from pathlib import Path\n from snipper import snipper\n self.cov.stop()\n data = self.cov.get_data()\n base, _, _ = self._report._import_base_relative()\n for file in data.measured_files():\n file = os.path.normpath(file)\n root = Path(base)\n child = Path(file)\n if root in child.parents:\n with open(child, \'r\') as f:\n s = f.read()\n lines = s.splitlines()\n garb = \'GARBAGE\'\n\n lines2 = snipper.censor_code(lines, keep=True)\n assert len(lines) == len(lines2)\n\n for l in data.contexts_by_lineno(file):\n if lines2[l].strip() == garb:\n if self.cache_id() not in self._report.covcache:\n self._report.covcache[self.cache_id()] = {}\n\n rel = os.path.relpath(child, root)\n cc = self._report.covcache[self.cache_id()]\n j = 0\n for j in range(l, -1, -1):\n if "def" in lines2[j] or "class" in lines2[j]:\n break\n from snipper.snipper import gcoms\n fun = lines2[j]\n comments, _ = gcoms("\\n".join(lines2[j:l]))\n if rel not in cc:\n cc[rel] = {}\n cc[rel][fun] = (l, "\\n".join(comments))\n self._cache_put((self.cache_id(), \'coverage\'), self._report.covcache)\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd is None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n sd = self.shortDescriptionStandard()\n title = self._cache_get((self.cache_id(), \'title\'), sd)\n return title if title is not None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome is None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc is not None:\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard())\n\n self._cache2[(self.cache_id(), \'assert\')] = {}\n res = testMethod()\n elapsed = time.time() - t\n self._get_outcome()[self.cache_id()] = res\n self._cache_put((self.cache_id(), "time"), elapsed)\n\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return c, m\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n # sys.stdout = self._stdout\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n self.__class__._cache[\n key] = {} # A new dict. We manually insert it because we have to use that the dict is mutable.\n cache = self._cache_get(key)\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, f"Key {id} not found in cache; framework files missing. Please run deploy()")\n\n # The order of these calls is important. If the method assert fails, we should still store the correct result in cache.\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n assert_fun(first, _expected, *args, **kwargs)\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache is not None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n if os.path.exists(cfile):\n try:\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n except Exception as e:\n print("Bad cache", cfile)\n print(e)\n else:\n print("Warning! data file not found", cfile)\n\n def _feedErrorsToResult(self, result, errors):\n """ Use this to show hints on test failure. """\n if not isinstance(result, UTextResult):\n er = [e for e, v in errors if v != None]\n\n if len(er) > 0:\n hints = []\n key = (self.cache_id(), \'coverage\')\n if self._cache_contains(key):\n CC = self._cache_get(key)\n for id in CC:\n if id == self.cache_id():\n cl, m = id\n gprint(f"> An error occured while solving: {cl}.{m}. The files/methods you need to edit are:") # For the test {id} in {file} you should edit:")\n for file in CC[id]:\n rec = CC[id][file]\n gprint(f"> * {file}")\n for l in rec:\n _, comments = CC[id][file][l]\n hint = get_hints(comments)\n\n if hint != None:\n hints.append(hint)\n gprint(f"> - {l}")\n\n er = er[0]\n doc = er._testMethodDoc\n if doc is not None:\n hint = get_hints(er._testMethodDoc)\n if hint is not None:\n hints = [hint] + hints\n if len(hints) > 0:\n gprint("> Hints:")\n gprint(textwrap.indent("\\n".join(hints), "> "))\n\n super()._feedErrorsToResult(result, errors)\n\n def startTestRun(self):\n # print("asdfsdaf 11", file=sys.stderr)\n super().startTestRun()\n # print("asdfsdaf")\n\n def _callTestMethod(self, method):\n # print("asdfsdaf")\n super()._callTestMethod(method)\n\n\ndef hide(func):\n return func\n\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n# 817\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\nimport inspect\nimport os\nimport argparse\nimport time\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print(b + " v" + __version__ + ", started: " + dt_string+ "\\n")\n # print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print(s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n UTextResult.nL = report.nL\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f" * q{n+1}) Total"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")",\n last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL)\n\n # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n # print(" ")\n # print("="*n)\n # print("Final evaluation")\n # print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n print("")\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f" * {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single unmodified file: ")\n print(">", token)\n # print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\nimport homework1\nimport importnb\n\nfile = \'week2.ipynb\'\nclass Week1(UTestCase):\n @classmethod\n def setUpClass(cls) -> None:\n with Capturing2():\n cls.nb = importnb.Notebook.load(file)\n\n def test_add(self):\n self.assertEqual(Week1.nb.myfun(2,2), 4)\n self.assertEqual(Week1.nb.myfun(2,4), 8)\n\n def test_reverse(self):\n self.assertEqual(Week1.nb.var, "hello world 2")\n\n# Nicer: Automatically load the notebook.\nclass NBTestCase(UTestCase):\n notebook = None\n _nb = None\n @classmethod\n def setUpClass(cls) -> None:\n with Capturing2():\n cls._nb = importnb.Notebook.load(cls.notebook)\n\n @property\n def nb(self):\n return self.__class__._nb\n\nclass Question2(NBTestCase):\n notebook = "week2.ipynb"\n def test_add(self):\n self.assertEqualC(self.nb.myfun(2,8))\n\nclass Report1Jupyter(Report):\n title = "CS 105 Report 5"\n questions = [(Week1, 10),\n (Question2, 8)\n ] # Include a single question for 10 credits.\n pack_imports = [homework1]' +report1_payload = '8004955c000000000000007d94288c055765656b31947d948c0474696d6594473fed915600000000738c095175657374696f6e32947d942868048c08746573745f6164649486948c066173736572749486947d944b004b10736803473fcc28f40000000075752e' +name="Report1Jupyter" + +report = source_instantiate(name, report1_source, report1_payload) +output_dir = os.path.dirname(__file__) +gather_upload_to_campusnet(report, output_dir) \ No newline at end of file diff --git a/examples/example_jupyter/instructor/cs105/unitgrade/Question2.pkl b/examples/example_jupyter/instructor/cs105/unitgrade/Question2.pkl new file mode 100644 index 0000000000000000000000000000000000000000..b08cef102cf660c34ffd24ee8633b7254630acac GIT binary patch literal 58 zcmZo*nX1nK0ku<lI0H*li%T-|^NgnSaFnDLm&7Ngq)cg>(!-WmT%1}|GNo-w?G$eY JZ-L@sJpdXr6f^(; literal 0 HcmV?d00001 diff --git a/examples/example_jupyter/instructor/cs105/unitgrade/Week1.pkl b/examples/example_jupyter/instructor/cs105/unitgrade/Week1.pkl new file mode 100644 index 0000000..9b6ff7a --- /dev/null +++ b/examples/example_jupyter/instructor/cs105/unitgrade/Week1.pkl @@ -0,0 +1 @@ +�N. \ No newline at end of file diff --git a/examples/02471/instructor/02471/week02/week2.ipynb b/examples/example_jupyter/instructor/cs105/week2.ipynb similarity index 100% rename from examples/02471/instructor/02471/week02/week2.ipynb rename to examples/example_jupyter/instructor/cs105/week2.ipynb diff --git a/examples/example_jupyter/students/cs105/.coverage b/examples/example_jupyter/students/cs105/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..572452543e2092b38f99ec9afedd779fdc155f29 GIT binary patch literal 53248 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 literal 0 HcmV?d00001 diff --git a/examples/example_jupyter/students/cs105/__pycache__/homework1.cpython-38.pyc b/examples/example_jupyter/students/cs105/__pycache__/homework1.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..405a1c978c8bf2114f9b470bfd5312fb8f02188f GIT binary patch literal 187 zcmWIL<>g{vU|{$cp_?eqz`*br#6iZ43=9ko3=9m#EDQ_`DGb33nv8xc8Hzx{2;x_Q zvsFxJacWU<Oi5`*YK%*Ma%paAUP*CGX<lYYdQoCZYJ5RaW?5oMYD{WHVs1fBYB7uv zpH*5=S&~{5lbKgsQdF8;l3x^)Tx@7y8k3Qqn_8Y<lx?V2P<e~PCO1E&G$+*#Wb<c` F3juJhGy4Dl literal 0 HcmV?d00001 diff --git a/examples/example_jupyter/students/cs105/__pycache__/report5.cpython-38.pyc b/examples/example_jupyter/students/cs105/__pycache__/report5.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c80ebb8aac0fa657aacc6cb4b52f65783b2e1ec8 GIT binary patch literal 2312 zcmWIL<>g{vU|={t-5_xjD+9x05C<8vFfcGUFfcF_Ph((UNMT4}%wfo7jAG1Xieh2} z@tJa%b6KKTz-;Cm)+p8#h7{%;wkWm~h7^_@hFtb2_FRrA4n~F))+kPQh7`6G_7;W| z_Ee^3<|r<Ah7^tz&K8CgPB5R_ogsxQg}a3zg*%nInK_EbogsxMg|~$vg*TP4nK_C# zl`n-ao2h70DoZMFGea{YBSQ*vFoP!lOOT5-nQpNKr55BDmE7VC4M{C7aZW5w)nvRS zo?4ceQ<_+k8easKjV~@KO-aow@zZ3w#pRq>P*PfynU`*KixbZN#a*77nr)<)Sx}jm zlngQ(8N)+FkAZ<9l_82Tg&~S5l{tkmg=r3B3Udle3rjO&6iW(g3R??96l)553P%e= z6k7^oFoP!NE!ObV)NDh)m5jI8ic(8Ti}I2gkqig1IT;ujoIxS3!oa{#!qCjnFT%)B z!kERhfVqTaA!7|gJZm_EDMKJb5JLn*3PUi%N=83Twjx#r28LT4nYo|<$V<A#;g?^M znv|cPeTyY0KQZMNOIl`5>Mf?cq?L@fn3HpgS27d{GcYjxigUJ#2`x@7Dvl{B%}9-L z$xklLP0cGQjw#K{EJ-g)Oi7I|D9S8LEJ=+?tw_u*$Vn}RG2*jI3o1)ei()eKic5-0 zlS}f8Vv>su4NPMo0d1;RP<e~1IJG3Sz&R(exESQIVo>NYFmW(e34jAa4<U^a5Nxo3 zFkoO{s9{(D4TKcN6vl;2DNGBQ7*R!;7BZzUgG51rqRHa-l8J$V;U&nzmmCZX44N#r zxD$(uQ;SMm3riDoia=sT0-#W1&8<u;&0EQMi={X<Ck-4f5JChb%TbbAToRv{lEMdy z6p(QYjAD#cd=U4;Br#kMQV5PlkRdSFXEB4^4$3US44O=SRlFIgIXU?X<@rT9DGElK zEJczG3=BvvV=hZ9Dv}0?fg%gZQ9NLKi&D!{i;7c)Kn??WmVvQK2+3JcX-)Q99P#ma ziMgrq@wd3*<8$*<N^?MLp7?lBNI+${lR=S`n_7~QpHd_WvQrjB$b$%k?O+zjOJG-m z!mt=ba4<443b1gnfJJd<86%`DlfoFqoWdBzlFEvfb=XqbA(^L{F^U76ZP;&d`8mO| zv|kY?0|Ns_c!LDN;jISCHe#@BQ^SyjHRBX<gOUX#>lE>T{K*R<K=FKwBM+8;nB()3 ziUdItV84S2Z~%b92ozt4G%bp7CBB3W%H?3Ef<mr_A&Vi4F_;137^YjC@$uk@h>tG< z`2<_+gHx>>Jkpt}c#-VQOVZ>lQUEzu5kx3~2vraPHcc7C0y+E^M?q13L26M+CD=4b z(gOPpOn^NIDuBd5)_}r>gOP=i1suyvj71<Bj93PxKyXzdfga0DDa<VlQ7kDe!3>(L zw>Se!Q;SP7^Ye^~*s;YVJl})N0p*+;hAhSfOrTV<kZB>P&}Rr{&}8;20=caSq)3xF ziU%ocJ43uA1@bGF%u@tP*B~DhgIo;CGjIp#fwNU&N{XK*E7m{+X~h~=nhXpKps)rf z22fZPfkZIEh#6L$f?@+!d4O_4DoYAe8dEB33UeA$I#U!|DtkI8^+Q684^pogdO-@@ zDt_l+1w#W<1&FYM=}S-@4h{)FjL5&mT9R3klM2q5U^hi^7DB_n_!ehIer{@ceo?kz z6i-27a&|nV{wT)kMJ)yf26a$!0NDoeA{!&ye-`E<kT|G>$xY16i;vgjj}nE~9Y%Tx zW)Uc~i$DPoC5sS6D2UHU%_&GNDmE$t6`e)8px6T?JaElc1WFV|{2(z<rU55Fuy4S& wL!t_ta1jJ33E$$dfs}T3pgdO$N-zS9Jd7NS9E<`?EQ|tt0z3k&0_+^j050p;!~g&Q literal 0 HcmV?d00001 diff --git a/examples/example_jupyter/students/cs105/__pycache__/week2.cpython-38.pyc b/examples/example_jupyter/students/cs105/__pycache__/week2.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d81c0675c7056e7e11ab8b96d289c407997be566 GIT binary patch literal 466 zcmWIL<>g{vU|?{asFQe(fq~&Mh=Yuo85kHG7#J9e?HCvsQW&BbQW&EcQ<zeiTNt94 zQkha%QdparQrN^9qL@=zQW={W85vSpQkg+?3VRf53P%cO3qurJ3Rf_LCU=#fvVv<x zYEg1#ajJrmo{^p*R~2tYYEDkRLV12sPKtt2G82;JAT|g)gY4&EU|^_Ws9_Xg2xib^ z^jpcG$#jb`@fKrJ5hDWw1DLqQU7nhnZKRi3P??u>i#4}0tu#*tq!(le17i^@0|UcL z5c?&_S1&;xfBA}mfgu@W0?b|z8zc?3l#hXdp@gA^A%(G-DTPTKWHqy25gW)HkUC9> znVO8Z7*oJ5fDkMU3=AOab4zoyk*w!oU|{gmWWU88AD@z+93Ov+xh$~=?5JC8`K2WV zr6spm3yL!HN^UV$6@e5(Oaoa2VS&x%u*uC&Da}c>V+R=xav+Nc3nK?JA0r1N2PX?7 E09j2^v;Y7A literal 0 HcmV?d00001 diff --git a/examples/example_jupyter/students/cs105/homework1.py b/examples/example_jupyter/students/cs105/homework1.py new file mode 100644 index 0000000..80562c2 --- /dev/null +++ b/examples/example_jupyter/students/cs105/homework1.py @@ -0,0 +1,4 @@ +""" +Example student code. This file is automatically generated from the files in the instructor-directory +""" +# This file is blank. diff --git a/examples/example_jupyter/students/cs105/report5.py b/examples/example_jupyter/students/cs105/report5.py new file mode 100644 index 0000000..e91cba1 --- /dev/null +++ b/examples/example_jupyter/students/cs105/report5.py @@ -0,0 +1,52 @@ +""" +Example student code. This file is automatically generated from the files in the instructor-directory +""" +from src.unitgrade2.unitgrade2 import Report, UTestCase +from src.unitgrade2 import evaluate_report_student +import homework1 +import importnb +from src.unitgrade2.unitgrade2 import Capturing2 + +file = 'week2.ipynb' +class Week1(UTestCase): + @classmethod + def setUpClass(cls) -> None: + with Capturing2(): + cls.nb = importnb.Notebook.load(file) + + def test_add(self): + self.assertEqual(Week1.nb.myfun(2,2), 4) + self.assertEqual(Week1.nb.myfun(2,4), 8) + + def test_reverse(self): + self.assertEqual(Week1.nb.var, "hello world 2") + +# Nicer: Automatically load the notebook. +class NBTestCase(UTestCase): + notebook = None + _nb = None + @classmethod + def setUpClass(cls) -> None: + with Capturing2(): + cls._nb = importnb.Notebook.load(cls.notebook) + + @property + def nb(self): + return self.__class__._nb + +class Question2(NBTestCase): + notebook = "week2.ipynb" + def test_add(self): + self.assertEqualC(self.nb.myfun(2,8)) + +class Report1Jupyter(Report): + title = "CS 105 Report 5" + questions = [(Week1, 10), + (Question2, 8) + ] # Include a single question for 10 credits. + pack_imports = [homework1] + +if __name__ == "__main__": + # Uncomment to simply run everything as a unittest: + # unittest.main(verbosity=2) + evaluate_report_student(Report1Jupyter()) diff --git a/examples/example_jupyter/students/cs105/report5_grade.py b/examples/example_jupyter/students/cs105/report5_grade.py new file mode 100644 index 0000000..b059908 --- /dev/null +++ b/examples/example_jupyter/students/cs105/report5_grade.py @@ -0,0 +1,338 @@ +""" +Example student code. This file is automatically generated from the files in the instructor-directory +""" +import numpy as np +from tabulate import tabulate +from datetime import datetime +import pyfiglet +import unittest +import inspect +import os +import argparse +import time + +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: +To run all tests in a report: + +> python assignment1_dp.py + +To run only question 2 or question 2.1 + +> python assignment1_dp.py -q 2 +> python assignment1_dp.py -q 2.1 + +Note this scripts does not grade your report. To grade your report, use: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('-q', nargs='?', type=str, default=None, help='Only evaluate this question (e.g.: -q 2)') +parser.add_argument('--showexpected', action="store_true", help='Show the expected/desired result') +parser.add_argument('--showcomputed', action="store_true", help='Show the answer your code computes') +parser.add_argument('--unmute', action="store_true", help='Show result of print(...) commands in code') +parser.add_argument('--passall', action="store_true", help='Automatically pass all tests. Useful when debugging.') + +def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False): + args = parser.parse_args() + if question is None and args.q is not None: + question = args.q + if "." in question: + question, qitem = [int(v) for v in question.split(".")] + else: + question = int(question) + + if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file: + raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation") + + if unmute is None: + unmute = args.unmute + if passall is None: + passall = args.passall + + results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute, + show_tol_err=show_tol_err) + + + if question is None: + print("Provisional evaluation") + tabulate(table_data) + table = table_data + print(tabulate(table)) + print(" ") + + fr = inspect.getouterframes(inspect.currentframe())[1].filename + gfile = os.path.basename(fr)[:-3] + "_grade.py" + if os.path.exists(gfile): + print("Note your results have not yet been registered. \nTo register your results, please run the file:") + print(">>>", gfile) + print("In the same manner as you ran this file.") + + + return results + + +def upack(q): + # h = zip([(i['w'], i['possible'], i['obtained']) for i in q.values()]) + h =[(i['w'], i['possible'], i['obtained']) for i in q.values()] + h = np.asarray(h) + return h[:,0], h[:,1], h[:,2], + +class UnitgradeTextRunner(unittest.TextTestRunner): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + +class SequentialTestLoader(unittest.TestLoader): + def getTestCaseNames(self, testCaseClass): + test_names = super().getTestCaseNames(testCaseClass) + # testcase_methods = list(testCaseClass.__dict__.keys()) + ls = [] + for C in testCaseClass.mro(): + if issubclass(C, unittest.TestCase): + ls = list(C.__dict__.keys()) + ls + testcase_methods = ls + test_names.sort(key=testcase_methods.index) + return test_names + +def evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False, + show_progress_bar=True, + show_tol_err=False, + big_header=True): + + now = datetime.now() + if big_header: + ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom") + b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) + else: + b = "Unitgrade" + dt_string = now.strftime("%d/%m/%Y %H:%M:%S") + print(b + " v" + __version__ + ", started: " + dt_string+ "\n") + # print("Started: " + dt_string) + s = report.title + if hasattr(report, "version") and report.version is not None: + s += " version " + report.version + print(s, "(use --help for options)" if show_help_flag else "") + # print(f"Loaded answers from: ", report.computed_answers_file, "\n") + table_data = [] + t_start = time.time() + score = {} + loader = SequentialTestLoader() + + for n, (q, w) in enumerate(report.questions): + if question is not None and n+1 != question: + continue + suite = loader.loadTestsFromTestCase(q) + qtitle = q.question_title() if hasattr(q, 'question_title') else q.__qualname__ + q_title_print = "Question %i: %s"%(n+1, qtitle) + print(q_title_print, end="") + q.possible = 0 + q.obtained = 0 + q_ = {} # Gather score in this class. + UTextResult.q_title_print = q_title_print # Hacky + UTextResult.show_progress_bar = show_progress_bar # Hacky. + UTextResult.number = n + UTextResult.nL = report.nL + + res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) + + possible = res.testsRun + obtained = len(res.successes) + + assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun + + obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 + score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} + q.obtained = obtained + q.possible = possible + + s1 = f" * q{n+1}) Total" + s2 = f" {q.obtained}/{w}" + print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 ) + print(" ") + table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"]) + + ws, possible, obtained = upack(score) + possible = int( msum(possible) ) + obtained = int( msum(obtained) ) # Cast to python int + report.possible = possible + report.obtained = obtained + now = datetime.now() + dt_string = now.strftime("%H:%M:%S") + + dt = int(time.time()-t_start) + minutes = dt//60 + seconds = dt - minutes*60 + plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") + + dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", + last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) + + # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total") + + table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) + results = {'total': (obtained, possible), 'details': score} + return results, table_data + + +from tabulate import tabulate +from datetime import datetime +import inspect +import json +import os +import bz2 +import pickle +import os + +def bzwrite(json_str, token): # to get around obfuscation issues + with getattr(bz2, 'open')(token, "wt") as f: + f.write(json_str) + +def gather_imports(imp): + resources = {} + m = imp + # for m in pack_imports: + # print(f"*** {m.__name__}") + f = m.__file__ + # dn = os.path.dirname(f) + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = str(__import__(m.__name__.split('.')[0]).__path__) + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: + top_package = os.path.dirname(m.__file__) + module_import = True + else: + top_package = __import__(m.__name__.split('.')[0]).__path__._path[0] + module_import = False + + # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) + # top_package = os.path.dirname(top_package) + import zipfile + # import strea + # zipfile.ZipFile + import io + # file_like_object = io.BytesIO(my_zip_data) + zip_buffer = io.BytesIO() + with zipfile.ZipFile(zip_buffer, 'w') as zip: + # zip.write() + for root, dirs, files in os.walk(top_package): + for file in files: + if file.endswith(".py"): + fpath = os.path.join(root, file) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) + zip.write(fpath, v) + + resources['zipfile'] = zip_buffer.getvalue() + resources['top_package'] = top_package + resources['module_import'] = module_import + return resources, top_package + + if f.endswith("__init__.py"): + for root, dirs, files in os.walk(os.path.dirname(f)): + for file in files: + if file.endswith(".py"): + # print(file) + # print() + v = os.path.relpath(os.path.join(root, file), top_package) + with open(os.path.join(root, file), 'r') as ff: + resources[v] = ff.read() + else: + v = os.path.relpath(f, top_package) + with open(f, 'r') as ff: + resources[v] = ff.read() + return resources + +import argparse +parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Use this script to get the score of your report. Example: + +> python report1_grade.py + +Finally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful. +For instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to 'Documents/` and run: + +> python -m course_package.report1 + +see https://docs.python.org/3.9/using/cmdline.html +""", formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('--noprogress', action="store_true", help='Disable progress bars') +parser.add_argument('--autolab', action="store_true", help='Show Autolab results') + +def gather_upload_to_campusnet(report, output_dir=None): + n = report.nL + args = parser.parse_args() + results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, + show_progress_bar=not args.noprogress, + big_header=not args.autolab) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) + # also load the source code of missing files... + + sources = {} + print("") + if not args.autolab: + if len(report.individual_imports) > 0: + print("By uploading the .token file, you verify the files:") + for m in report.individual_imports: + print(">", m.__file__) + print("Are created/modified individually by you in agreement with DTUs exam rules") + report.pack_imports += report.individual_imports + + if len(report.pack_imports) > 0: + print("Including files in upload...") + for k, m in enumerate(report.pack_imports): + nimp, top_package = gather_imports(m) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import + nimp['name'] = m.__name__ + sources[k] = nimp + # if len([k for k in nimp if k not in sources]) > 0: + print(f" * {m.__name__}") + # sources = {**sources, **nimp} + results['sources'] = sources + + if output_dir is None: + output_dir = os.getcwd() + + payload_out_base = report.__class__.__name__ + "_handin" + + obtain, possible = results['total'] + vstring = "_v"+report.version if report.version is not None else "" + + token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) + token = os.path.join(output_dir, token) + with open(token, 'wb') as f: + pickle.dump(results, f) + + if not args.autolab: + print(" ") + print("To get credit for your results, please upload the single unmodified file: ") + print(">", token) + # print("To campusnet without any modifications.") + + # print("Now time for some autolab fun") + +def source_instantiate(name, report1_source, payload): + eval("exec")(report1_source, globals()) + pl = pickle.loads(bytes.fromhex(payload)) + report = eval(name)(payload=pl, strict=True) + # report.set_payload(pl) + return report + + + +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n"""\nimport numpy as np\nimport sys\nimport re\nimport threading\nimport tqdm\nimport pickle\nimport os\nfrom io import StringIO\nimport io\nfrom unittest.runner import _WritelnDecorator\nfrom typing import Any\nimport inspect\nimport textwrap\nimport colorama\nfrom colorama import Fore\nfrom functools import _make_key, RLock\nfrom collections import namedtuple\nimport unittest\nimport time\n\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ncolorama.init(autoreset=True) # auto resets your settings after every output\n\ndef gprint(s):\n print(f"{Fore.GREEN}{s}")\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\n\ndef setup_dir_by_class(C, base_dir):\n name = C.__class__.__name__\n return base_dir, name\n\n\nclass Logger(object):\n def __init__(self, buffer):\n assert False\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\n# @classmethod\n# class OrderedClassMembers(type):\n# def __prepare__(self, name, bases):\n# assert False\n# return collections.OrderedDict()\n#\n# def __new__(self, name, bases, classdict):\n# ks = list(classdict.keys())\n# for b in bases:\n# ks += b.__ordered__\n# classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n# return type.__new__(self, name, bases, classdict)\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 120 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q, _) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q, _) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n loader = unittest.TestLoader()\n for q, _ in self.questions:\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self, with_coverage=False):\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = True\n q._report = self\n\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n # print(self.questions)\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n print("q is", q())\n q()._cache_put(\'time\', q.time) # = q.time\n report_cache[q.__qualname__] = q._cache2\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\': True}\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = False\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct + 1)\n if i > 0 and l.find("|", i + 1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = r\'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar", show_progress_bar=True, file=None):\n if file == None:\n file = sys.stdout\n self.file = file\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.01\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n self.pbar = None\n\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar = None\n\n self.file.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=self.file, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\')\n\n for _ in range(self.n - 1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\ndef dprint(first, last, nL, extra = "", file=None, dotsym=\'.\', color=\'white\'):\n if file == None:\n file = sys.stdout\n\n # ss = self.item_title_print\n # state = "PASS" if success else "FAILED"\n dot_parts = (dotsym * max(0, nL - len(last) - len(first)))\n # if self.show_progress_bar or True:\n print(first + dot_parts, end="", file=file)\n # else:\n # print(dot_parts, end="", file=self.cc.file)\n last += extra\n # if tsecs >= 0.5:\n # state += " (" + str(tsecs) + " seconds)"\n print(last, file=file)\n\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n cc = None\n\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n self.successes.append(test)\n self.cc_terminate()\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n self.cc.file.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="", file=self.cc.file)\n else:\n print(dot_parts, end="", file=self.cc.file)\n\n if tsecs >= 0.5:\n state += " (" + str(tsecs) + " seconds)"\n print(state, file=self.cc.file)\n\n def startTest(self, test):\n # j =self.testsRun\n self.testsRun += 1\n # item_title = self.getDescription(test)\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n self.item_title_print = " * q%i.%i) %s" % (UTextResult.number + 1, self.testsRun, item_title)\n estimated_time = 10\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar, file=sys.stdout)\n else:\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n self._stdout = sys.stdout\n sys.stdout = io.StringIO()\n\n def stopTest(self, test):\n sys.stdout = self._stdout\n super().stopTest(test)\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass is None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n print(" " * max(0, self.nL - len(self.cc.title)) + (" (" + str(q_time) + " seconds)" if q_time >= 0.5 else ""))\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n stream = io.StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)))\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n\n return wrapper\n\n\ndef get_hints(ss):\n if ss == None:\n return None\n try:\n ss = textwrap.dedent(ss)\n ss = ss.replace(\'\'\'"""\'\'\', "").strip()\n hints = ["hints:", ]\n j = np.argmax([ss.lower().find(h) for h in hints])\n h = hints[j]\n ss = ss[ss.find(h) + len(h) + 1:]\n ss = "\\n".join([l for l in ss.split("\\n") if not l.strip().startswith(":")])\n ss = textwrap.dedent(ss)\n ss = ss.strip()\n return ss\n except Exception as e:\n print("bad hints", ss, e)\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n _with_coverage = False\n _report = None # The report used. This is very, very hacky and should always be None. Don\'t rely on it!\n\n def capture(self):\n if hasattr(self, \'_stdout\') and self._stdout is not None:\n file = self._stdout\n else:\n # self._stdout = sys.stdout\n # sys._stdout = io.StringIO()\n file = sys.stdout\n return Capturing2(stdout=file)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ is not None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n if self._with_coverage:\n if not hasattr(self._report, \'covcache\'):\n self._report.covcache = {}\n import coverage\n self.cov = coverage.Coverage()\n self.cov.start()\n self.setUp()\n\n def _callTearDown(self):\n self.tearDown()\n if self._with_coverage:\n from pathlib import Path\n from snipper import snipper\n self.cov.stop()\n data = self.cov.get_data()\n base, _, _ = self._report._import_base_relative()\n for file in data.measured_files():\n file = os.path.normpath(file)\n root = Path(base)\n child = Path(file)\n if root in child.parents:\n with open(child, \'r\') as f:\n s = f.read()\n lines = s.splitlines()\n garb = \'GARBAGE\'\n\n lines2 = snipper.censor_code(lines, keep=True)\n assert len(lines) == len(lines2)\n\n for l in data.contexts_by_lineno(file):\n if lines2[l].strip() == garb:\n if self.cache_id() not in self._report.covcache:\n self._report.covcache[self.cache_id()] = {}\n\n rel = os.path.relpath(child, root)\n cc = self._report.covcache[self.cache_id()]\n j = 0\n for j in range(l, -1, -1):\n if "def" in lines2[j] or "class" in lines2[j]:\n break\n from snipper.snipper import gcoms\n fun = lines2[j]\n comments, _ = gcoms("\\n".join(lines2[j:l]))\n if rel not in cc:\n cc[rel] = {}\n cc[rel][fun] = (l, "\\n".join(comments))\n self._cache_put((self.cache_id(), \'coverage\'), self._report.covcache)\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd is None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n sd = self.shortDescriptionStandard()\n title = self._cache_get((self.cache_id(), \'title\'), sd)\n return title if title is not None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome is None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc is not None:\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard())\n\n self._cache2[(self.cache_id(), \'assert\')] = {}\n res = testMethod()\n elapsed = time.time() - t\n self._get_outcome()[self.cache_id()] = res\n self._cache_put((self.cache_id(), "time"), elapsed)\n\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return c, m\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n # sys.stdout = self._stdout\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n self.__class__._cache[\n key] = {} # A new dict. We manually insert it because we have to use that the dict is mutable.\n cache = self._cache_get(key)\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, f"Key {id} not found in cache; framework files missing. Please run deploy()")\n\n # The order of these calls is important. If the method assert fails, we should still store the correct result in cache.\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n assert_fun(first, _expected, *args, **kwargs)\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache is not None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n if os.path.exists(cfile):\n try:\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n except Exception as e:\n print("Bad cache", cfile)\n print(e)\n else:\n print("Warning! data file not found", cfile)\n\n def _feedErrorsToResult(self, result, errors):\n """ Use this to show hints on test failure. """\n if not isinstance(result, UTextResult):\n er = [e for e, v in errors if v != None]\n\n if len(er) > 0:\n hints = []\n key = (self.cache_id(), \'coverage\')\n if self._cache_contains(key):\n CC = self._cache_get(key)\n for id in CC:\n if id == self.cache_id():\n cl, m = id\n gprint(f"> An error occured while solving: {cl}.{m}. The files/methods you need to edit are:") # For the test {id} in {file} you should edit:")\n for file in CC[id]:\n rec = CC[id][file]\n gprint(f"> * {file}")\n for l in rec:\n _, comments = CC[id][file][l]\n hint = get_hints(comments)\n\n if hint != None:\n hints.append(hint)\n gprint(f"> - {l}")\n\n er = er[0]\n doc = er._testMethodDoc\n if doc is not None:\n hint = get_hints(er._testMethodDoc)\n if hint is not None:\n hints = [hint] + hints\n if len(hints) > 0:\n gprint("> Hints:")\n gprint(textwrap.indent("\\n".join(hints), "> "))\n\n super()._feedErrorsToResult(result, errors)\n\n def startTestRun(self):\n # print("asdfsdaf 11", file=sys.stderr)\n super().startTestRun()\n # print("asdfsdaf")\n\n def _callTestMethod(self, method):\n # print("asdfsdaf")\n super()._callTestMethod(method)\n\n\ndef hide(func):\n return func\n\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n# 817\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\nimport inspect\nimport os\nimport argparse\nimport time\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print(b + " v" + __version__ + ", started: " + dt_string+ "\\n")\n # print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print(s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n UTextResult.nL = report.nL\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f" * q{n+1}) Total"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")",\n last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL)\n\n # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n # print(" ")\n # print("="*n)\n # print("Final evaluation")\n # print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n print("")\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f" * {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single unmodified file: ")\n print(">", token)\n # print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\nimport homework1\nimport importnb\n\nfile = \'week2.ipynb\'\nclass Week1(UTestCase):\n @classmethod\n def setUpClass(cls) -> None:\n with Capturing2():\n cls.nb = importnb.Notebook.load(file)\n\n def test_add(self):\n self.assertEqual(Week1.nb.myfun(2,2), 4)\n self.assertEqual(Week1.nb.myfun(2,4), 8)\n\n def test_reverse(self):\n self.assertEqual(Week1.nb.var, "hello world 2")\n\n# Nicer: Automatically load the notebook.\nclass NBTestCase(UTestCase):\n notebook = None\n _nb = None\n @classmethod\n def setUpClass(cls) -> None:\n with Capturing2():\n cls._nb = importnb.Notebook.load(cls.notebook)\n\n @property\n def nb(self):\n return self.__class__._nb\n\nclass Question2(NBTestCase):\n notebook = "week2.ipynb"\n def test_add(self):\n self.assertEqualC(self.nb.myfun(2,8))\n\nclass Report1Jupyter(Report):\n title = "CS 105 Report 5"\n questions = [(Week1, 10),\n (Question2, 8)\n ] # Include a single question for 10 credits.\n pack_imports = [homework1]' +report1_payload = '8004955c000000000000007d94288c055765656b31947d948c0474696d6594473fed915600000000738c095175657374696f6e32947d942868048c08746573745f6164649486948c066173736572749486947d944b004b10736803473fcc28f40000000075752e' +name="Report1Jupyter" + +report = source_instantiate(name, report1_source, report1_payload) +output_dir = os.path.dirname(__file__) +gather_upload_to_campusnet(report, output_dir) diff --git a/examples/example_jupyter/students/cs105/unitgrade/Question2.pkl b/examples/example_jupyter/students/cs105/unitgrade/Question2.pkl new file mode 100644 index 0000000000000000000000000000000000000000..b08cef102cf660c34ffd24ee8633b7254630acac GIT binary patch literal 58 zcmZo*nX1nK0ku<lI0H*li%T-|^NgnSaFnDLm&7Ngq)cg>(!-WmT%1}|GNo-w?G$eY JZ-L@sJpdXr6f^(; literal 0 HcmV?d00001 diff --git a/examples/example_jupyter/students/cs105/unitgrade/Week1.pkl b/examples/example_jupyter/students/cs105/unitgrade/Week1.pkl new file mode 100644 index 0000000..9b6ff7a --- /dev/null +++ b/examples/example_jupyter/students/cs105/unitgrade/Week1.pkl @@ -0,0 +1 @@ +�N. \ No newline at end of file diff --git a/examples/example_jupyter/students/cs105/week2.ipynb b/examples/example_jupyter/students/cs105/week2.ipynb new file mode 100644 index 0000000..6bc411a --- /dev/null +++ b/examples/example_jupyter/students/cs105/week2.ipynb @@ -0,0 +1,69 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Exercise 2.2.1" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hello world\n", + "6\n" + ] + } + ], + "source": [ + "var = \"hello world 2\"\n", + "def myfun(a,b):\n", + " return a*b\n", + "\n", + "output = myfun(2,3) + 10\n", + "print(var)\n", + "print(output)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "z = 234 \n", + "def mymul(d):\n", + " return myfun(d,2)+1" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/example_simplest/instructor/cs101/Report1_handin_10_of_10.token b/examples/example_simplest/instructor/cs101/Report1_handin_10_of_10.token index 5d34d0fd91b71eecb9ff050591857b09f382421b..1884f587f74133902500c7b2e2ea1095e067afd8 100644 GIT binary patch delta 8583 zcmdmbhh@Q0X4VFlsZ$?KWNna<f5^ZP;LXe;!T<snB9wx4{SNG#%*?<rnR((uDYoQd zLjyy-iM#t%bQJQ7dAZ6{6l@jpi}eZ;OEUCQGK=yOb5k|q<I^&8Qsd(_Cwnm#vgs%! zRb>`T-pW`fSDvD+q_3x^4>m<fM<FG(xFkL$v&a@KG1-%80t-mz<Qq&${JdNW$Y8P+ zvxSWgnxKNZo*u|NJw5%@ip1Q4oYeTVoWv6S%)H`~qSEA&{33`|Ai>Eg%xco*Dcb7# z8Tq-X<@rU~hI$2+>N*NwyOp#z&thK2%6~CJDY&!pv`W)828O0<43qsiCr*CEY0O-b znY(#8*Co!);nM5bC+B_FousU!rJ${#q@z$=l2}xdnqsA(q@b;kQW9TWQk0pOuC1UH zlc%H!aj^2_1>fZ-S1C`J9RI^&a*#^e<fAH`lYjoy*?eC$gK@Ht2G8W1>fMtxM1=Bu z6l@iWQVa5nO7!x4c)57FCa?SBH~E(NrpeZp-^2q;Q;SP7^Yav{^Rx|X6-x3;5_6Pz zxu&n-V^o}+VI{*3)jItpAEV6V1y;tqg<zGM3Lzk+n;%(SV3bcOD9X$$(MZcIDlSp5 zRZs#;DHP;q=9LsHB$iCq6l4q*Mh)tm#Nra$=?euJ4fJ&sP~E5rawI6Glog=nDmmwa zqM{@<1!`IluO7sHP<ZfiO>Y!r44#~Cn?AXL({;1FoeEPdB$zVO6fzQv6H7{pG;(zm z)M3d;T~i@3FGV3QzXVMhl<4B))itdY6qFS_b3uWYnU}7Rs8E~<a)1ITEm<k#PBy$G zvN_%%gIP);GYxD^ZhlH>PHKE6Sc^hxPI2mFU4Qw>4k9d@?>WC?SBH5;Nde?yWX~zt zDrx1xWA&}K0;BR|9-nl6kQ#{hl|WjiGb%DlZ0_|5W@3uhnS9?*Qc_0&)lzsYPu|cZ zCI$~gkU21{Jo&!g0cl8##zS=%7o;X<re!84f>Occd>4tyz5Xj06*ij(OlIOL&CAVC z$xO>kO__W$NRLr@dXGAz;b!&VQ*6viN=lQJqm(8WMHvbdWF}|lq{ieyy`MkXDN30s zGk<dMPP55XOBJX03o&w<l%!Ualouryz?CKE=j0b9<|fAEr4{ApD!{l3P*dFUi&7`c z?2(x4vd2sgDps18SyGZ(Tmm<vBr`WPCU0`Zp3r)je!a}R%o2^n(vtk5)Z)|<+mNEt zR83HPf`k;nV#NxT`K3h)#i=DFnR)5O3W;eYsYME@WvNA#3i+ia1*Ii1c`<n@sc8!7 zkSHkDw2H|Cg(8HTrc@1bot}G;tE*pab#bkdX3XR(Q8|<CqNN!XCPzjaPG*deoE#kE z#FJQDoLW?(;Fg$EoVvL?Muc(l+*k|Y;*ym7(h^(0{Jc~hh0?s-(vsB4Jh?Lb#i==I zdhrlh1zUy53*$s5|B20+oF3<(0!nJA^0o>f1Hh?G0V1<`VVoG_<Zba6CwC@vi8&`0 zl$3&s4kHaXQ`2fPPl6UlPG(+eF~|m|1T{s7*FfGYsQ~BSn3y~zy{!DqJPoiq&B-y9 zD)J>2C7KA8VEa-lN>cMuAc`huB*;(hOlS%OnFGRL6_C(WuvI9jC_&SlSDKrYS_E<g zlm!a&m^@_#hvb~Z;^N%Yl8pQm5P$N!N+l*`g~>`)no`Ojlff7{V8N=qtF%~^!K~6M z1CaWqRjRTG^=bJ<3P}o?c?wC1#i_+sV1<vX6hR7Es<S~<ezh%MOdi;Bg&=T&Ir(`* z3yYzVfx_h6Mt??y&3hW97$;j^7MT33iC;`dAx1qu9#Ue&sKXM-<a15Vlb1ggo_wuI zi33zz#K*_vZDw!IW}IBoa)MbQUURZxk1{W~!~=&=o{z%h+}5(mU)%F0*L3Jj*6&fC z+@>YWs4)3Q$AQU5JDvD+6v{J8GUAi-%TkLH(<d|L2#aT?p@^kIQ|DyG9>d8E`7Gjv zdhy75QWb0!K-plj;$&e#y?96gpkND5G%<ObIlJ~Ts)H5OBI1aE6%tsi04MUvi50?= z?|0AId}^W!BeR0G!sPdpv{)3h6%40uRALkXId8J@<haTGlcO1nHouyDhf!9cI3vG2 zzMv>Sy(qP~I6f({2%Kqkz{Tz43n?OyI$SVQ9Kx6tC&LX2S_NB(ihEO284V^oPs@}7 zrwj!Ju*dWYk`jv)AbBJvZ}aVGzZoZgnvp957ee?I91$QBCs)mE;sE7AFo$(k_vG!f z6xkJY6$~}46gJ<P#mg8EDix6HI~@g3RR*fVbQDr6N{SLe6`&Ht?a&OKl3!9>nQI%P zt{0=O11aZhW7NwtGD}hs2`4iR;u*nAVJVR9py;VA)`OG<psWG%oATs;>lN9RK`n%m z)XDcZXgH?im&6w&7L|bVkp{#f1ucc##0m`qked{A6>?JZG(b+>Ec4w5BFBFDp;( zn;|v%-e&%KXwe3)!?hLQ=ISV<=B3yw!F&XA8cZv=<bftVSOtemA2^7Ull74FgB+!x zZ3_vt7(;ORsthXSN{UmHixuo_6%6!D;l_i!UtE${k_ytIq@basF#Q}iqk;s)*&wr^ zVGgxn^7-X{lVw(Ta3&{1V|+4SlJw;A6)Pu;uM*q*V`Vwx<eRH_Cu^>;o7}xdb+Yz) z@yTxMXHEXUjg1xT%E>R+8wn%Q2;Bb3_cy2npo+sitzfI50gh%w9DqHZ=K~FVun%A{ zQCtj<hRKENEkIdg`aLE_w#m91Bvj#MqD27MJXk<Z4(wJ1Yf&dkc}(79#*H$JrjrZv zh55ivK?zKi$q5^|B|u>e4N6qI@;B;D-nY?Wa`GnI$(J`tOn$Y=h&MA$A+sbkH@+mZ zBqw#UV5Yc>vVvQFkpiruR!A#KgtUzn$}>`nQWeruOI%WmlZ!G7K<y2M+{8+SqSTVo zqC9Y2rUwbv$<doECfjb7G*{44C{)eVQ_a*=P%TzcP*u<f4N0vi2}&(4%_-4?)El6p zMF*1OK~00=pwhg_rJGGASFSgh{2+;S@~_R=LNNcpGnD4!xld&$zt07Qwe{rvTkI#d z#pz65nZP5PnXeZNZf|?~YiPpq9VpWmm*f{rZrCV2c}0S<SaE4VYLSMf9!M}GwYWqB zWVPnxKU)eJ4JTh{l$_kY^~B`A|LrGRZ!w+xYn#zz?d@*Uom3fxCO2=dm|VGDa=JA; zBlqN*d=7B*O=jGoGI>G-yP$#+TFRNcV28})U_%35Q*drmP*MWr+{v?drt=h+6s0ES zD%dJy=Id?N*`>y~`Sc!H#>tWAttQv(n_~_MN@(DM8eRpdMIiq`8#bvW@fo1@xkfR( zag&**P+UA&FjIJP@p8fXlA=mz<^v~vPyqvL!s(@?rljVTfV4tvfvYGk)+<Ua$Vp62 z)re6CK_w+65Ctmul$1c>Rg_r(3RjR05C*$J!B!z!3Cyxm(ou*7n^_N%Rmf7XRmdyQ zODswU6=~7M#d<mU<=~J{%gjsB$k0>()$19cdL67k7FsreG-oK-Du4x}vmi!-1Qm)w z9w{yc83i*!8(c<aXo6Cb6-+6}ez004L~AHI2W(vq$hzWUz2btL%n}W-ye6pJ0`(_y z^gz9}0&vm*70aM%SVPH5NfX&+U~>rsCJ(3r0G0os0GrIXTbQ{xCU3Grv5F+5#8XO2 zOo4b0)MG5xQApLCthL{Ma>o9rAg3!}GyuT0GdO4!5G??315!a*AtWOe+DTL>Ely3* zQwYh(ELO-YRsgk$b-<)TMq+YyCAi~MoRMFelcJEAQ=V8^tdNun$-4?J`FSzwB??8d zsX3Jj`FRSNC5oT~2MTsj21*7uM^h(%s1ccLbD2jT+<=1CZ_pkYxWIw7e<#mtl@<mS z=AeK8)yXjNxld&+VB+u$4e=Prsv<~n0&npeX+Z3-1?ibA*d#wW^MIV9l9G}_5JY83 zMyf&~tQ!n2$P_@Go7w|%lUE;-69dI;a!#>ce0)lNa(p}}Q1bFiHXlB~#mE((oS2gn zoLUlEF!@7`6fY>UVePck$&L+TlYP$c@j{wgAm1kEmrdT^C2bDzCPZ8>IlnA9F*yU& zMyp0@e}TLL?e#!geyNi`Y!eaBFH^8pfQjfiLmGvt8k65|6IIAB(*utkAZIYJUBw_L zYG_XWcvP4*vB)LAeDZ;r;#?)EV0NB{=48Pu!cHizN6Pvjmw<a{ptf#KW)iG}7LZty zfe3bxs>$+<63hjuMIeVpf*q5OlG#COQW8r*O(d|hlJm>-(o;*~L1M^m2B`xz({&W$ zbrj;^@ed6!Q2&-ub9$UOqm&3pr(SMqVsU9vY6^Ies90mVqB5h1G$^)_Mw0UKi*iBk z*GS9EnH*3e!e5l159;fHJfV@4SPT*qNY2R20S!Na1)&PXGSd{mYBTc`!18(piAAY- z(+?^#ih}z0lYhB#ae*a3F<dnHVz~5V2N4c#P%)FHSCpEVqA~rWB%`<}xGM$n3#8lt zb0Jb9>4`;23bqO{>h6v~PLA%bG3qgSlMR=P>3|g(fi&f178Ilw=_RM;73UYlC+DZ6 zLb_c#3fZZt1)%ow^beAZ5=zi+5xCI@>f>oD*xG`NH;7T15Q7y#r6#x-0|#Joejcba zDvnR8j0dU9%h#CfcvWQbf_x5;7iBWj6hOkoM$tL3u<{LLF38=JC+3TTJgf(*JwVwM zoS5S?Q#3TeBO{r4kh&RK=IB9_`{c$YVv`+2_;kSu0ZlSB8Z3%1DOSN&0hBpFfdaCO zxhOSfa-o|rH#Fcu5d;opkfb)a#RoQlpt&#uSwR(z0gNXB3Z5)bDPNSBm!7JTqobf} z2*Ti`HQ7N#KsPf@K`A9QO$nq38a7$63i(9}O5hG8syH-^1Xz-aQWGa1EC2_XA~;VK zLn9VS!}3;oa(-?x%sKpNrFjaruqeoig^6n?=jZ08=9LsfG6`4@q&h<Cyc<PlS>?oP z!b3*@l%7*_U~!Y240Rk2PjYf}QECn(s9{#9Lj|MLO7lQ6pr8O(cn}+44nP<`dCfk7 z$pXhklwn0wjJhVYb%q`pnv)M6Oo23t%n!v*zI@1S@~0zmOvNda8)u444mixqmRSsP z=H&RpS0<YtO@b($dq!k(|547#$BxdJ9DB@m^88l$$@7l0O@4bsd9vPdd1g=w?>{aM za>L}^$2r+RuCA_~eD=8K<eU>OlLd|oO+I`=k_A*DO<s6Xbh6>eFqUK;g<Np&V1u0C z<aej?xgf<FSk3=4qMJ9Lp1?Hu?Kv+-<;lwvR3^{ZD>2#seA#5lGm`bu5T&3J3x=~( zD<PJG2Av!g@>0tcQZkcE^c2EV6><~vN)vN(Dit#Gz~e%hB??KY$%&=KsS4$(3K@xI zsR||epek6QBqOmz0aP`C^nxm#+|rW7q?}Y}6B%S#a$@r26Xzu+|KG<w`ThB|^-A8U zl?v6FDYc+bQ%K7%%}W6#PjEJ}Mrw(JipFAv+|1(Q%)E3xg@ByY#Nt$iqS8Etl+=Qp z{7MZ?C3wphWDlfykYALNTBMMl269w!ssgCy1Gf+$l~iJ0Nr0Y$Cs<Y?7d&>O04<Bs z5;Jp(bwK`uwi}8|GIMeiia`T(Aj1@r^NWg7lS@EjbEP>Ya3||czHuRQ^5P5TlN~Q! z-z<4akck(PAi&KHD}~8MSI$jtkrA5ga8-2j&LSaD{|QtYPmaH;1}e5Dw_f#~?#{%h zGP!pNxS7o^;FOr601gf%9ff3Q0S{?_2bA!Dhi%^HvQM_Vz&`o&RV8pU3X}%o(^6AY zT#JhGi;6??A$<=GQ1PMzb+L{@Dp*nzsnP_sFG7n`6-q$usFHl}5C^2W25K3BdJ77m za49WH)dRIQ5UB_{pqyEpnUhysl9-pAs!;^)?&v5W^?i`70!2|OXrv=L72G^c)lnz| z1tP>zpyo@Nf+A!bBo@{l1L*-_P|2Q}r;%C&ADm$UHJK+H)QU`=vR8&DMjerlCkK`Y zGi9bt&N{CNsu`G^ohQHCr#StOBqI+GsKCqwr#5G2Xo1248l}oi0o4<rz|sSUJE&fO z6{0*`$vHX-;0{a*th}j*4!EW%*(o^YLBdrbKRFpxy(^SwWagwQ6zAuZW#*+@DO4xt z)aq5|)`A+Vso=qrVtq);1CQ@2<fW#jfO1o6N@j^dVo|D<5_lLG)I9~|D{vfub4zBP zLNzE))GAa8<d=e?3e>s<X|qy-mz&db#2I<moSmaHQ(~b$<Pj=L1r07h1fxMlz{+MG zF(i*DC}=?~fEx!1F>noBlnN_?1!Utv!$XiVUBOlX+1%(Hn9%|<pcqoH1$CvNT{w6# z45{5g6%;3E>LW9+L_u+~V67-9z9u_}hzNqT7V9Mz6oAHUK^&O>1QJjjte~Kvt5BT- z8raT5q)kwY0by`zOD&2vfOVEYf>4~24+@ghBE5J}0Q-VV0+;;cn7qk>dcw>p`N^Pm zJ;*)a4j+m)Ff@T3#}4%gOdlv~OMsjXYGFl#7_kc4kgNr<gdNoQ$;ix`?x@Hp2C_*I z>NO=h1rJb{7aFTZP+?Hh2h=~-%gh7!YLV(Ukm<#mItog53JMBJnwpa*CW|UzDGgvb z6q4mMC+BYztq&^A1D6uWSrs-)rj%Hml2)9On5JN8sDwH=ky=!Q)Q3fkGb9$3fCd^s zrXtB>7zi)9C;vDq9RSG=cwGR}0F62Ym~G&04ahb~^l3niiOHKRcuixn`Lz$5Rj(^D zP3GIfzB%lc6f>)`f`y^^<bu0Z(;I~t#V0f0li`3&%*5nPUc;rk`RTo#oRhyk5uWa; z%E-NW&HMSF2~W$(f#1cn3C(zdraU1D5H#hvescVe`IC>Th)!1cS+e=(&uNU4Ilxn$ zs*~ORq)pNg;R6q>Lput2K9g7d31N!Kn|#k)etHBCWB>ALyo`(@*ycQ^ujFHNK%VUM zN1f>0?#|Ch^h_tj!O&0z4Nc;j>4X)>uxLzE!kFn~ht70PuNGuHvfWvTF`sGjm8XIM zxuEU_Y(g^z+J6R(Kp;(cqE1kNnj`q<G`F7<VJyHmsX2Y8G9!3abMifB;q4g`j2}>^ zHX$B|`35qz2^E|C)?11l#^&*nnZ8AV(VG_(M$qvLCC%;s6&Q1go!^879moV22F-6q zPd}#2m@)mnG9$YPwkgi(uO%2Ir%S6au3}W!eq4odCh{!j^muhfJx1m2J?f0hn6`&& zF^Vwpp)OgNUa7<A%Xnb=aUDi`COfw29J-8h3TSiu3}T?A4p6+UF@$rvn=YdU6W4?3 zmAZ`6Q^mlG6=EQX)G$81C^02fub^^D55Ae%DX~*DdRW0ar}RLF6rg&ir1r3YR!K~O K$(I(F>Hz?298FLF delta 27054 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 PN)Idef~6^?#ie=x%)J)u diff --git a/examples/example_simplest/instructor/cs101/__pycache__/deploy.cpython-38.pyc b/examples/example_simplest/instructor/cs101/__pycache__/deploy.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e14e2ac2aed2a082d17689ac69828e53aeba409 GIT binary patch literal 911 zcmWIL<>g{vU|^^b)=Qks%)sy%#6iaF3=9ko3=9m#3m6y}QW#Pga~N_NqZk=MY^EHh zT;?cdFq=7tC6_gd70hPIVT)o*VMt-gVUJ=@WzXVRz?sUmkTHrol_!NYo2h6{Dsw7h z3L6x%_p&lFq_SqQrgEonh%=;er*Mihq;REhr|_ikrZcDT^|CNBr0}~i#L7hRrm`*I zOXW@x0IL%eXGrB0XIRL@$WU09!V=7&DfANLc1^}x>_Mpo`9&p$nvA!^i&INV3*ys@ z5>ry+(=u~X<BPy@nvAzNit{oH;!`q<{BCi?$LA(y=EcXmX|hDI<!0t(rd399<|n0< z7AGf`q(-r)R-`7EmZV0pBvoY=RB`I->FFmI8yXl^iGrAVdiupBr75X-CB+cokXzjG z@dcI1iOCtM@$pscT6!h<*{OL|oGGaVIr)`(1(ljCQC!9OrA5i9AQwb&q@)&?fLWr+ zIjM<x@gRO?USdgRejZquJ+&e^r!*y1lex-5PY-HoYDHphK~8FXT2W$dYI%N9wti+_ zaY<2Wa!Gy>*da#x8Tk+yL%o8^DpL%dDf!9SsYU48j8V0jV`xjuNi0FvWC#{Sv5yzU zoL`Lkn*33`;BeD}#DZZF69WT7lv-(CW(g#U3W_qz5=&Bz^fEG2Qd0BclZ#RlOCT{< zT*M4g%npjSg4CiS77&lEIHR;AGv^jle(^1qg2a-HTkI*BMR|$2sh~&&M|gY`Q+W!A z2NI2szr~n#izPX~pmHTc5kCV1g!mQbY!wq)oLW>IQ&O6d8sn0mT$&4t&ln7+#Xv*7 z7{-V%&IH9maY+m!^kcxjjDbh@Ee@O9{FKt1R69mcb`oM>VBlc{K><b{CJsg+W(8&r FW&j@s6|(>U literal 0 HcmV?d00001 diff --git a/examples/example_simplest/instructor/cs101/__pycache__/report1_grade.cpython-38.pyc b/examples/example_simplest/instructor/cs101/__pycache__/report1_grade.cpython-38.pyc index 3b0930bdcddcda845eef8dfac07d6ab732a2473f..aed06f7cf667b51f32903bf52b3c17138bda2777 100644 GIT binary patch delta 2862 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 R1*rgGCJq)3v&r&5r2z5IKuG`q delta 7319 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 diff --git a/examples/example_simplest/instructor/cs101/deploy.py b/examples/example_simplest/instructor/cs101/deploy.py index 3e9682d..b51f79c 100644 --- a/examples/example_simplest/instructor/cs101/deploy.py +++ b/examples/example_simplest/instructor/cs101/deploy.py @@ -1,16 +1,19 @@ -from report1 import Report1 +from cs101.report1 import Report1 from unitgrade_private2.hidden_create_files import setup_grade_file_report from snipper import snip_dir -import shutil +import shutil, os +wd = os.path.dirname(__file__) if __name__ == "__main__": - setup_grade_file_report(Report1, minify=False, obfuscate=False, execute=False) + setup_grade_file_report(Report1, minify=False, obfuscate=False, execute=False, bzip=False) # Deploy the files using snipper: https://gitlab.compute.dtu.dk/tuhe/snipper - snip_dir.snip_dir(source_dir="../cs101", dest_dir="../../students/cs101", clean_destination_dir=True, exclude=['__pycache__', '*.token', 'deploy.py']) + snip_dir.snip_dir(source_dir=wd+"/../cs101", dest_dir=wd+"/../../students/cs101", clean_destination_dir=True, exclude=['__pycache__', '*.token', 'deploy.py']) - # For my own sake, copy the homework to the other examples. - for f in ['../../../example_framework/instructor/cs102/homework1.py', '../../../example_docker/instructor/cs103/homework1.py']: - shutil.copy('homework1.py', f) + # For my own sake, copy the homework file to the other examples. + for f in ['../../../example_framework/instructor/cs102/homework1.py', + '../../../example_docker/instructor/cs103/homework1.py', + '../../../example_flat/instructor/cs101flat/homework1.py']: + shutil.copy(wd+'/homework1.py', wd+"/"+f) diff --git a/examples/example_simplest/instructor/cs101/report1.py b/examples/example_simplest/instructor/cs101/report1.py index e00853f..447cedb 100644 --- a/examples/example_simplest/instructor/cs101/report1.py +++ b/examples/example_simplest/instructor/cs101/report1.py @@ -1,5 +1,5 @@ -from unitgrade2.unitgrade2 import Report -from unitgrade2.unitgrade_helpers2 import evaluate_report_student +from src.unitgrade2.unitgrade2 import Report +from src.unitgrade2 import evaluate_report_student from cs101.homework1 import reverse_list, add import unittest diff --git a/examples/example_simplest/instructor/cs101/report1_grade.py b/examples/example_simplest/instructor/cs101/report1_grade.py index 8972ab5..7f2feaa 100644 --- a/examples/example_simplest/instructor/cs101/report1_grade.py +++ b/examples/example_simplest/instructor/cs101/report1_grade.py @@ -4,14 +4,10 @@ from tabulate import tabulate from datetime import datetime import pyfiglet import unittest - import inspect import os import argparse -import sys import time -import threading # don't import Thread bc. of minify issue. -import tqdm # don't do from tqdm import tqdm because of minify-issue parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: To run all tests in a report: @@ -61,53 +57,6 @@ def evaluate_report_student(report, question=None, qitem=None, unmute=None, pass show_tol_err=show_tol_err) - # try: # For registering stats. - # import unitgrade_private - # import irlc.lectures - # import xlwings - # from openpyxl import Workbook - # import pandas as pd - # from collections import defaultdict - # dd = defaultdict(lambda: []) - # error_computed = [] - # for k1, (q, _) in enumerate(report.questions): - # for k2, item in enumerate(q.items): - # dd['question_index'].append(k1) - # dd['item_index'].append(k2) - # dd['question'].append(q.name) - # dd['item'].append(item.name) - # dd['tol'].append(0 if not hasattr(item, 'tol') else item.tol) - # error_computed.append(0 if not hasattr(item, 'error_computed') else item.error_computed) - # - # qstats = report.wdir + "/" + report.name + ".xlsx" - # - # if os.path.isfile(qstats): - # d_read = pd.read_excel(qstats).to_dict() - # else: - # d_read = dict() - # - # for k in range(1000): - # key = 'run_'+str(k) - # if key in d_read: - # dd[key] = list(d_read['run_0'].values()) - # else: - # dd[key] = error_computed - # break - # - # workbook = Workbook() - # worksheet = workbook.active - # for col, key in enumerate(dd.keys()): - # worksheet.cell(row=1, column=col+1).value = key - # for row, item in enumerate(dd[key]): - # worksheet.cell(row=row+2, column=col+1).value = item - # - # workbook.save(qstats) - # workbook.close() - # - # except ModuleNotFoundError as e: - # s = 234 - # pass - if question is None: print("Provisional evaluation") tabulate(table_data) @@ -159,24 +108,20 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) else: b = "Unitgrade" - print(b + " v" + __version__) dt_string = now.strftime("%d/%m/%Y %H:%M:%S") - print("Started: " + dt_string) + print(b + " v" + __version__ + ", started: " + dt_string+ "\n") + # print("Started: " + dt_string) s = report.title if hasattr(report, "version") and report.version is not None: s += " version " + report.version - print("Evaluating " + s, "(use --help for options)" if show_help_flag else "") + print(s, "(use --help for options)" if show_help_flag else "") # print(f"Loaded answers from: ", report.computed_answers_file, "\n") table_data = [] - nL = 80 t_start = time.time() score = {} loader = SequentialTestLoader() for n, (q, w) in enumerate(report.questions): - # q = q() - # q_hidden = False - # q_hidden = issubclass(q.__class__, Hidden) if question is not None and n+1 != question: continue suite = loader.loadTestsFromTestCase(q) @@ -186,104 +131,28 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa q.possible = 0 q.obtained = 0 q_ = {} # Gather score in this class. - # unittest.Te - # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_] UTextResult.q_title_print = q_title_print # Hacky UTextResult.show_progress_bar = show_progress_bar # Hacky. UTextResult.number = n + UTextResult.nL = report.nL res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) - # res = UTextTestRunner(verbosity=2, resultclass=unittest.TextTestResult).run(suite) - z = 234 - # for j, item in enumerate(q.items): - # if qitem is not None and question is not None and j+1 != qitem: - # continue - # - # if q_with_outstanding_init is not None: # check for None bc. this must be called to set titles. - # # if not item.question.has_called_init_: - # start = time.time() - # - # cc = None - # if show_progress_bar: - # total_estimated_time = q.estimated_time # Use this. The time is estimated for the q itself. # sum( [q2.estimated_time for q2 in q_with_outstanding_init] ) - # cc = ActiveProgress(t=total_estimated_time, title=q_title_print) - # from unitgrade import Capturing # DON'T REMOVE THIS LINE - # with eval('Capturing')(unmute=unmute): # Clunky import syntax is required bc. of minify issue. - # try: - # for q2 in q_with_outstanding_init: - # q2.init() - # q2.has_called_init_ = True - # - # # item.question.init() # Initialize the question. Useful for sharing resources. - # except Exception as e: - # if not passall: - # if not silent: - # print(" ") - # print("="*30) - # print(f"When initializing question {q.title} the initialization code threw an error") - # print(e) - # print("The remaining parts of this question will likely fail.") - # print("="*30) - # - # if show_progress_bar: - # cc.terminate() - # sys.stdout.flush() - # print(q_title_print, end="") - # - # q_time =np.round( time.time()-start, 2) - # - # print(" "* max(0,nL - len(q_title_print) ) + (" (" + str(q_time) + " seconds)" if q_time >= 0.1 else "") ) # if q.name in report.payloads else "") - # print("=" * nL) - # q_with_outstanding_init = None - # - # # item.question = q # Set the parent question instance for later reference. - # item_title_print = ss = "*** q%i.%i) %s"%(n+1, j+1, item.title) - # - # if show_progress_bar: - # cc = ActiveProgress(t=item.estimated_time, title=item_title_print) - # else: - # print(item_title_print + ( '.'*max(0, nL-4-len(ss)) ), end="") - # hidden = issubclass(item.__class__, Hidden) - # # if not hidden: - # # print(ss, end="") - # # sys.stdout.flush() - # start = time.time() - # - # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent) - # q_[j] = {'w': item.weight, 'possible': possible, 'obtained': current, 'hidden': hidden, 'computed': str(item._computed_answer), 'title': item.title} - # tsecs = np.round(time.time()-start, 2) - # if show_progress_bar: - # cc.terminate() - # sys.stdout.flush() - # print(item_title_print + ('.' * max(0, nL - 4 - len(ss))), end="") - # - # if not hidden: - # ss = "PASS" if current == possible else "*** FAILED" - # if tsecs >= 0.1: - # ss += " ("+ str(tsecs) + " seconds)" - # print(ss) - - # ws, possible, obtained = upack(q_) possible = res.testsRun obtained = len(res.successes) assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun - # possible = int(ws @ possible) - # obtained = int(ws @ obtained) - # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0 - obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} q.obtained = obtained q.possible = possible - s1 = f"*** Question q{n+1}" + s1 = f"Question {n+1} total" s2 = f" {q.obtained}/{w}" - print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 ) + print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 ) print(" ") - table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"]) + table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"]) ws, possible, obtained = upack(score) possible = int( msum(possible) ) @@ -298,15 +167,16 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa seconds = dt - minutes*60 plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") - print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")") + dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", + last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) + + # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total") table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) results = {'total': (obtained, possible), 'details': score} return results, table_data - - from tabulate import tabulate from datetime import datetime import inspect @@ -329,7 +199,8 @@ def gather_imports(imp): # dn = os.path.dirname(f) # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) # top_package = str(__import__(m.__name__.split('.')[0]).__path__) - if m.__class__.__name__ == 'module' and False: + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: top_package = os.path.dirname(m.__file__) module_import = True else: @@ -350,7 +221,7 @@ def gather_imports(imp): for file in files: if file.endswith(".py"): fpath = os.path.join(root, file) - v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package)) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) zip.write(fpath, v) resources['zipfile'] = zip_buffer.getvalue() @@ -394,14 +265,14 @@ def gather_upload_to_campusnet(report, output_dir=None): results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, show_progress_bar=not args.noprogress, big_header=not args.autolab) - print(" ") - print("="*n) - print("Final evaluation") - print(tabulate(table_data)) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) # also load the source code of missing files... sources = {} - + print("") if not args.autolab: if len(report.individual_imports) > 0: print("By uploading the .token file, you verify the files:") @@ -414,12 +285,15 @@ def gather_upload_to_campusnet(report, output_dir=None): print("Including files in upload...") for k, m in enumerate(report.pack_imports): nimp, top_package = gather_imports(m) - report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import nimp['name'] = m.__name__ sources[k] = nimp # if len([k for k in nimp if k not in sources]) > 0: - print(f"*** {m.__name__}") + print(f" * {m.__name__}") # sources = {**sources, **nimp} results['sources'] = sources @@ -438,9 +312,9 @@ def gather_upload_to_campusnet(report, output_dir=None): if not args.autolab: print(" ") - print("To get credit for your results, please upload the single file: ") + print("To get credit for your results, please upload the single unmodified file: ") print(">", token) - print("To campusnet without any modifications.") + # print("To campusnet without any modifications.") # print("Now time for some autolab fun") @@ -453,7 +327,7 @@ def source_instantiate(name, report1_source, payload): -report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n\n"""\n# from . import cache_read\nimport unittest\nimport numpy as np\nimport sys\nfrom io import StringIO\nimport collections\nimport re\nimport threading\nimport tqdm\nimport time\nimport pickle\nimport itertools\nimport os\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\ndef setup_dir_by_class(C,base_dir):\n name = C.__class__.__name__\n # base_dir = os.path.join(base_dir, name)\n # if not os.path.isdir(base_dir):\n # os.makedirs(base_dir)\n return base_dir, name\n\nclass Hidden:\n def hide(self):\n return True\n\nclass Logger(object):\n def __init__(self, buffer):\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\nclass Capturing(list):\n def __init__(self, *args, unmute=False, **kwargs):\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass QItem(unittest.TestCase):\n title = None\n testfun = None\n tol = 0\n estimated_time = 0.42\n _precomputed_payload = None\n _computed_answer = None # Internal helper to later get results.\n weight = 1 # the weight of the question.\n\n def __init__(self, question=None, *args, **kwargs):\n if self.tol > 0 and self.testfun is None:\n self.testfun = self.assertL2Relative\n elif self.testfun is None:\n self.testfun = self.assertEqual\n\n self.name = self.__class__.__name__\n # self._correct_answer_payload = correct_answer_payload\n self.question = question\n\n super().__init__(*args, **kwargs)\n if self.title is None:\n self.title = self.name\n\n def _safe_get_title(self):\n if self._precomputed_title is not None:\n return self._precomputed_title\n return self.title\n\n def assertNorm(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed).flat- np.asarray(expected)).flat )\n nrm = np.sqrt(np.sum( diff ** 2))\n\n self.error_computed = nrm\n\n if nrm > tol:\n print(f"Not equal within tolerance {tol}; norm of difference was {nrm}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def assertL2(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n self.error_computed = np.max(diff)\n\n if np.max(diff) > tol:\n print(f"Not equal within tolerance {tol=}; deviation was {np.max(diff)=}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol=}, {np.max(diff)=}")\n\n def assertL2Relative(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n diff = diff / (1e-8 + np.abs( (np.asarray(computed) + np.asarray(expected)) ) )\n self.error_computed = np.max(np.abs(diff))\n if np.sum(diff > tol) > 0:\n print(f"Not equal within tolerance {tol}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def precomputed_payload(self):\n return self._precomputed_payload\n\n def precompute_payload(self):\n # Pre-compute resources to include in tests (useful for getting around rng).\n pass\n\n def compute_answer(self, unmute=False):\n raise NotImplementedError("test code here")\n\n def test(self, computed, expected):\n self.testfun(computed, expected)\n\n def get_points(self, verbose=False, show_expected=False, show_computed=False,unmute=False, passall=False, silent=False, **kwargs):\n possible = 1\n computed = None\n def show_computed_(computed):\n print(">>> Your output:")\n print(computed)\n\n def show_expected_(expected):\n print(">>> Expected output (note: may have been processed; read text script):")\n print(expected)\n\n correct = self._correct_answer_payload\n try:\n if unmute: # Required to not mix together print stuff.\n print("")\n computed = self.compute_answer(unmute=unmute)\n except Exception as e:\n if not passall:\n if not silent:\n print("\\n=================================================================================")\n print(f"When trying to run test class \'{self.name}\' your code threw an error:", e)\n show_expected_(correct)\n import traceback\n print(traceback.format_exc())\n print("=================================================================================")\n return (0, possible)\n\n if self._computed_answer is None:\n self._computed_answer = computed\n\n if show_expected or show_computed:\n print("\\n")\n if show_expected:\n show_expected_(correct)\n if show_computed:\n show_computed_(computed)\n try:\n if not passall:\n self.test(computed=computed, expected=correct)\n except Exception as e:\n if not silent:\n print("\\n=================================================================================")\n print(f"Test output from test class \'{self.name}\' does not match expected result. Test error:")\n print(e)\n show_computed_(computed)\n show_expected_(correct)\n return (0, possible)\n return (1, possible)\n\n def score(self):\n try:\n self.test()\n except Exception as e:\n return 0\n return 1\n\nclass QPrintItem(QItem):\n def compute_answer_print(self):\n """\n Generate output which is to be tested. By default, both text written to the terminal using print(...) as well as return values\n are send to process_output (see compute_answer below). In other words, the text generated is:\n\n res = compute_Answer_print()\n txt = (any terminal output generated above)\n numbers = (any numbers found in terminal-output txt)\n\n self.test(process_output(res, txt, numbers), <expected result>)\n\n :return: Optional values for comparison\n """\n raise Exception("Generate output here. The output is passed to self.process_output")\n\n def process_output(self, res, txt, numbers):\n return res\n\n def compute_answer(self, unmute=False):\n with Capturing(unmute=unmute) as output:\n res = self.compute_answer_print()\n s = "\\n".join(output)\n s = rm_progress_bar(s) # Remove progress bar.\n numbers = extract_numbers(s)\n self._computed_answer = (res, s, numbers)\n return self.process_output(res, s, numbers)\n\nclass OrderedClassMembers(type):\n @classmethod\n def __prepare__(self, name, bases):\n return collections.OrderedDict()\n def __new__(self, name, bases, classdict):\n ks = list(classdict.keys())\n for b in bases:\n ks += b.__ordered__\n classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n return type.__new__(self, name, bases, classdict)\n\nclass QuestionGroup(metaclass=OrderedClassMembers):\n title = "Untitled question"\n partially_scored = False\n t_init = 0 # Time spend on initialization (placeholder; set this externally).\n estimated_time = 0.42\n has_called_init_ = False\n _name = None\n _items = None\n\n @property\n def items(self):\n if self._items == None:\n self._items = []\n members = [gt for gt in [getattr(self, gt) for gt in self.__ordered__ if gt not in ["__classcell__", "__init__"]] if inspect.isclass(gt) and issubclass(gt, QItem)]\n for I in members:\n self._items.append( I(question=self))\n return self._items\n\n @items.setter\n def items(self, value):\n self._items = value\n\n @property\n def name(self):\n if self._name == None:\n self._name = self.__class__.__name__\n return self._name #\n\n @name.setter\n def name(self, val):\n self._name = val\n\n def init(self):\n # Can be used to set resources relevant for this question instance.\n pass\n\n def init_all_item_questions(self):\n for item in self.items:\n if not item.question.has_called_init_:\n item.question.init()\n item.question.has_called_init_ = True\n\n\nclass Report():\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 80 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q,_) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n root_dir = self.pack_imports[0].__path__._path[0]\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q,_) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n # else:\n # if os.path.isfile(self.computed_answers_file):\n # self.set_payload(cache_read(self.computed_answers_file), strict=strict)\n # else:\n # s = f"> Warning: The pre-computed answer file, {os.path.abspath(self.computed_answers_file)} is missing. The framework will NOT work as intended. Reasons may be a broken local installation."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n import unittest\n loader = unittest.TestLoader()\n for q,_ in self.questions:\n import time\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self):\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\':True}\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n # for item in q.items:\n # if q.name not in payloads or item.name not in payloads[q.name]:\n # s = f"> Broken resource dictionary submitted to unitgrade for question {q.name} and subquestion {item.name}. Framework will not work."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n # else:\n # item._correct_answer_payload = payloads[q.name][item.name][\'payload\']\n # item.estimated_time = payloads[q.name][item.name].get("time", 1)\n # q.estimated_time = payloads[q.name].get("time", 1)\n # if "precomputed" in payloads[q.name][item.name]: # Consider removing later.\n # item._precomputed_payload = payloads[q.name][item.name][\'precomputed\']\n # try:\n # if "title" in payloads[q.name][item.name]: # can perhaps be removed later.\n # item.title = payloads[q.name][item.name][\'title\']\n # except Exception as e: # Cannot set attribute error. The title is a function (and probably should not be).\n # pass\n # # print("bad", e)\n # self.payloads = payloads\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct+1)\n if i > 0 and l.find("|", i+1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = \'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar",show_progress_bar=True):\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.1\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n\n # self.pbar = tqdm.tqdm(total=self.n)\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if hasattr(self, \'pbar\') and self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar=None\n\n sys.stdout.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=sys.stdout, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\') # , unit_scale=dt, unit=\'seconds\'):\n\n for _ in range(self.n-1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\n\n\nfrom unittest.suite import _isnotsuite\n\nclass MySuite(unittest.suite.TestSuite): # Not sure we need this one anymore.\n pass\n\ndef instance_call_stack(instance):\n s = "-".join(map(lambda x: x.__name__, instance.__class__.mro()))\n return s\n\ndef get_class_that_defined_method(meth):\n for cls in inspect.getmro(meth.im_class):\n if meth.__name__ in cls.__dict__:\n return cls\n return None\n\ndef caller_name(skip=2):\n """Get a name of a caller in the format module.class.method\n\n `skip` specifies how many levels of stack to skip while getting caller\n name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.\n\n An empty string is returned if skipped levels exceed stack height\n """\n stack = inspect.stack()\n start = 0 + skip\n if len(stack) < start + 1:\n return \'\'\n parentframe = stack[start][0]\n\n name = []\n module = inspect.getmodule(parentframe)\n # `modname` can be None when frame is executed directly in console\n # TODO(techtonik): consider using __main__\n if module:\n name.append(module.__name__)\n # detect classname\n if \'self\' in parentframe.f_locals:\n # I don\'t know any way to detect call from the object method\n # XXX: there seems to be no way to detect static method call - it will\n # be just a function call\n name.append(parentframe.f_locals[\'self\'].__class__.__name__)\n codename = parentframe.f_code.co_name\n if codename != \'<module>\': # top level usually\n name.append( codename ) # function or a method\n\n ## Avoid circular refs and frame leaks\n # https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack\n del parentframe, stack\n\n return ".".join(name)\n\ndef get_class_from_frame(fr):\n import inspect\n args, _, _, value_dict = inspect.getargvalues(fr)\n # we check the first parameter for the frame function is\n # named \'self\'\n if len(args) and args[0] == \'self\':\n # in that case, \'self\' will be referenced in value_dict\n instance = value_dict.get(\'self\', None)\n if instance:\n # return its class\n # isinstance(instance, Testing) # is the actual class instance.\n\n return getattr(instance, \'__class__\', None)\n # return None otherwise\n return None\n\nfrom typing import Any\nimport inspect, gc\n\ndef giveupthefunc():\n frame = inspect.currentframe()\n code = frame.f_code\n globs = frame.f_globals\n functype = type(lambda: 0)\n funcs = []\n for func in gc.get_referrers(code):\n if type(func) is functype:\n if getattr(func, "__code__", None) is code:\n if getattr(func, "__globals__", None) is globs:\n funcs.append(func)\n if len(funcs) > 1:\n return None\n return funcs[0] if funcs else None\n\n\nfrom collections import defaultdict\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n # if self.dots or self.showAll:\n # self.stream.writeln()\n # if hasattr(self, \'cc\'):\n # self.cc.terminate()\n # self.cc_terminate(success=False)\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n # if self.showAll:\n # self.stream.writeln("FAIL")\n # elif self.dots:\n # self.stream.write(\'F\')\n # self.stream.flush()\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n # super().addSuccess(test)\n self.successes.append(test)\n # super().addSuccess(test)\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n self.cc_terminate()\n\n\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n sys.stdout.flush()\n ss = self.item_title_print\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(ss))), end="")\n # current = 1\n # possible = 1\n # current == possible\n ss = "PASS" if success else "FAILED"\n if tsecs >= 0.1:\n ss += " (" + str(tsecs) + " seconds)"\n print(ss)\n\n\n def startTest(self, test):\n # super().startTest(test)\n j =self.testsRun\n self.testsRun += 1\n # print("Starting the test...")\n # show_progress_bar = True\n n = UTextResult.number\n\n item_title = self.getDescription(test)\n item_title = item_title.split("\\n")[0]\n\n item_title = test.shortDescription() # Better for printing (get from cache).\n # test.countTestCases()\n self.item_title_print = "*** q%i.%i) %s" % (n + 1, j + 1, item_title)\n estimated_time = 10\n nL = 80\n #\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar)\n else:\n print(self.item_title_print + (\'.\' * max(0, nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 2\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n # q_title_print = "some printed title..."\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass == None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n print(self.cc.title, end="")\n # start = 10\n # q_time = np.round(time.time() - start, 2)\n nL = 80\n print(" " * max(0, nL - len(self.cc.title)) + (\n " (" + str(q_time) + " seconds)" if q_time >= 0.1 else "")) # if q.name in report.payloads else "")\n # print("=" * nL)\n\nfrom unittest.runner import _WritelnDecorator\nfrom io import StringIO\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n from io import StringIO\n stream = StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\ndef wrapper(foo):\n def magic(self):\n s = "-".join(map(lambda x: x.__name__, self.__class__.mro()))\n # print(s)\n foo(self)\n magic.__doc__ = foo.__doc__\n return magic\n\nfrom functools import update_wrapper, _make_key, RLock\nfrom collections import namedtuple\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)) )\n # key = (self.cache_id(), \'@cache\')\n # if self._cache_contains[key]\n\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n return wrapper\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n\n @classmethod\n def question_title(cls):\n return cls.__doc__.splitlines()[0].strip() if cls.__doc__ != None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd == None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n # self._testMethodDoc.strip().splitlines()[0].strip()\n sd = self.shortDescriptionStandard()\n title = self._cache_get( (self.cache_id(), \'title\'), sd )\n return title if title != None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n # def _callSetUp(self):\n # # Always run before method is called.\n # print("asdf")\n # pass\n # @classmethod\n # def setUpClass(cls):\n # # self._cache_put((self.cache_id(), \'title\'), value)\n # cls.reset()\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome == None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc != None:\n # Ensure the cache is eventually updated with the right docstring.\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard() )\n # Fix temp cache here (for using the @cache decorator)\n self._cache2[ (self.cache_id(), \'assert\') ] = {}\n\n res = testMethod()\n elapsed = time.time() - t\n # self._cache_put( (self.cache_id(), \'title\'), self.shortDescription() )\n\n self._get_outcome()[self.cache_id()] = res\n self._cache_put( (self.cache_id(), "time"), elapsed)\n\n # This is my base test class. So what is new about it?\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return (c,m)\n\n # def unique_cache_id(self):\n # k0 = self.cache_id()\n # # key = ()\n # i = 0\n # for i in itertools.count():\n # # key = k0 + (i,)\n # if i not in self._cache_get( (k0, \'assert\') ):\n # break\n # return i\n # return key\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n # self.cache_indexes = defaultdict(lambda: 0)\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n #\n # def _cache2_contains(self, key):\n # print("Is this needed?")\n # self._ensure_cache_exists()\n # return key in self.__class__._cache2\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n cache = self._cache_get(key, {})\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, first)\n assert_fun(first, _expected, *args, **kwargs)\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__) ) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache != None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n # print("Loading cache from", cfile)\n if os.path.exists(cfile):\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n else:\n print("Warning! data file not found", cfile)\n\ndef hide(func):\n return func\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n # (*)We can be somewhat "hygienic", but newDecorator still isn\'t signature-preserving, i.e. you will not be able to get a runtime list of parameters. For that, you need hackish libraries...but in this case, the only argument is func, so it\'s not a big issue\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\n\nimport inspect\nimport os\nimport argparse\nimport sys\nimport time\nimport threading # don\'t import Thread bc. of minify issue.\nimport tqdm # don\'t do from tqdm import tqdm because of minify-issue\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n # try: # For registering stats.\n # import unitgrade_private\n # import irlc.lectures\n # import xlwings\n # from openpyxl import Workbook\n # import pandas as pd\n # from collections import defaultdict\n # dd = defaultdict(lambda: [])\n # error_computed = []\n # for k1, (q, _) in enumerate(report.questions):\n # for k2, item in enumerate(q.items):\n # dd[\'question_index\'].append(k1)\n # dd[\'item_index\'].append(k2)\n # dd[\'question\'].append(q.name)\n # dd[\'item\'].append(item.name)\n # dd[\'tol\'].append(0 if not hasattr(item, \'tol\') else item.tol)\n # error_computed.append(0 if not hasattr(item, \'error_computed\') else item.error_computed)\n #\n # qstats = report.wdir + "/" + report.name + ".xlsx"\n #\n # if os.path.isfile(qstats):\n # d_read = pd.read_excel(qstats).to_dict()\n # else:\n # d_read = dict()\n #\n # for k in range(1000):\n # key = \'run_\'+str(k)\n # if key in d_read:\n # dd[key] = list(d_read[\'run_0\'].values())\n # else:\n # dd[key] = error_computed\n # break\n #\n # workbook = Workbook()\n # worksheet = workbook.active\n # for col, key in enumerate(dd.keys()):\n # worksheet.cell(row=1, column=col+1).value = key\n # for row, item in enumerate(dd[key]):\n # worksheet.cell(row=row+2, column=col+1).value = item\n #\n # workbook.save(qstats)\n # workbook.close()\n #\n # except ModuleNotFoundError as e:\n # s = 234\n # pass\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n print(b + " v" + __version__)\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print("Evaluating " + s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n nL = 80\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n # q = q()\n # q_hidden = False\n # q_hidden = issubclass(q.__class__, Hidden)\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n # unittest.Te\n # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_]\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n # res = UTextTestRunner(verbosity=2, resultclass=unittest.TextTestResult).run(suite)\n z = 234\n # for j, item in enumerate(q.items):\n # if qitem is not None and question is not None and j+1 != qitem:\n # continue\n #\n # if q_with_outstanding_init is not None: # check for None bc. this must be called to set titles.\n # # if not item.question.has_called_init_:\n # start = time.time()\n #\n # cc = None\n # if show_progress_bar:\n # total_estimated_time = q.estimated_time # Use this. The time is estimated for the q itself. # sum( [q2.estimated_time for q2 in q_with_outstanding_init] )\n # cc = ActiveProgress(t=total_estimated_time, title=q_title_print)\n # from unitgrade import Capturing # DON\'T REMOVE THIS LINE\n # with eval(\'Capturing\')(unmute=unmute): # Clunky import syntax is required bc. of minify issue.\n # try:\n # for q2 in q_with_outstanding_init:\n # q2.init()\n # q2.has_called_init_ = True\n #\n # # item.question.init() # Initialize the question. Useful for sharing resources.\n # except Exception as e:\n # if not passall:\n # if not silent:\n # print(" ")\n # print("="*30)\n # print(f"When initializing question {q.title} the initialization code threw an error")\n # print(e)\n # print("The remaining parts of this question will likely fail.")\n # print("="*30)\n #\n # if show_progress_bar:\n # cc.terminate()\n # sys.stdout.flush()\n # print(q_title_print, end="")\n #\n # q_time =np.round( time.time()-start, 2)\n #\n # print(" "* max(0,nL - len(q_title_print) ) + (" (" + str(q_time) + " seconds)" if q_time >= 0.1 else "") ) # if q.name in report.payloads else "")\n # print("=" * nL)\n # q_with_outstanding_init = None\n #\n # # item.question = q # Set the parent question instance for later reference.\n # item_title_print = ss = "*** q%i.%i) %s"%(n+1, j+1, item.title)\n #\n # if show_progress_bar:\n # cc = ActiveProgress(t=item.estimated_time, title=item_title_print)\n # else:\n # print(item_title_print + ( \'.\'*max(0, nL-4-len(ss)) ), end="")\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n # if show_progress_bar:\n # cc.terminate()\n # sys.stdout.flush()\n # print(item_title_print + (\'.\' * max(0, nL - 4 - len(ss))), end="")\n #\n # if not hidden:\n # ss = "PASS" if current == possible else "*** FAILED"\n # if tsecs >= 0.1:\n # ss += " ("+ str(tsecs) + " seconds)"\n # print(ss)\n\n # ws, possible, obtained = upack(q_)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n # possible = int(ws @ possible)\n # obtained = int(ws @ obtained)\n # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"*** Question q{n+1}"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n if m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package))\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n print(" ")\n print("="*n)\n print("Final evaluation")\n print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f"*** {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single file: ")\n print(">", token)\n print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\nfrom cs101.homework1 import reverse_list, add\nimport unittest\n\nclass Week1(unittest.TestCase):\n def test_add(self):\n self.assertEqual(add(2,2), 4)\n self.assertEqual(add(-100, 5), -95)\n\n def test_reverse(self):\n self.assertEqual(reverse_list([1,2,3]), [3,2,1])\n # print("Bad output\\n\\n")\n\n\nimport cs101\nclass Report1(Report):\n title = "CS 101 Report 1"\n questions = [(Week1, 10)] # Include a single question for 10 credits.\n pack_imports = [cs101]' +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n"""\nimport numpy as np\nimport sys\nimport re\nimport threading\nimport tqdm\nimport pickle\nimport os\nfrom io import StringIO\nimport io\nfrom unittest.runner import _WritelnDecorator\nfrom typing import Any\nimport inspect\nimport textwrap\nimport colorama\nfrom colorama import Fore\nfrom functools import _make_key, RLock\nfrom collections import namedtuple\nimport unittest\nimport time\n\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ncolorama.init(autoreset=True) # auto resets your settings after every output\n\ndef gprint(s):\n print(f"{Fore.GREEN}{s}")\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\n\ndef setup_dir_by_class(C, base_dir):\n name = C.__class__.__name__\n return base_dir, name\n\n\nclass Logger(object):\n def __init__(self, buffer):\n assert False\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\n# @classmethod\n# class OrderedClassMembers(type):\n# def __prepare__(self, name, bases):\n# assert False\n# return collections.OrderedDict()\n#\n# def __new__(self, name, bases, classdict):\n# ks = list(classdict.keys())\n# for b in bases:\n# ks += b.__ordered__\n# classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n# return type.__new__(self, name, bases, classdict)\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 120 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q, _) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q, _) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n loader = unittest.TestLoader()\n for q, _ in self.questions:\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self, with_coverage=False):\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = True\n q._report = self\n\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\': True}\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = False\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct + 1)\n if i > 0 and l.find("|", i + 1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = r\'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar", show_progress_bar=True, file=None):\n if file == None:\n file = sys.stdout\n self.file = file\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.01\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n self.pbar = None\n\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar = None\n\n self.file.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=self.file, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\')\n\n for _ in range(self.n - 1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\ndef dprint(first, last, nL, extra = "", file=None, dotsym=\'.\', color=\'white\'):\n if file == None:\n file = sys.stdout\n\n # ss = self.item_title_print\n # state = "PASS" if success else "FAILED"\n dot_parts = (dotsym * max(0, nL - len(last) - len(first)))\n # if self.show_progress_bar or True:\n print(first + dot_parts, end="", file=file)\n # else:\n # print(dot_parts, end="", file=self.cc.file)\n last += extra\n # if tsecs >= 0.5:\n # state += " (" + str(tsecs) + " seconds)"\n print(last, file=file)\n\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n cc = None\n\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n self.successes.append(test)\n self.cc_terminate()\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n self.cc.file.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="", file=self.cc.file)\n else:\n print(dot_parts, end="", file=self.cc.file)\n\n if tsecs >= 0.5:\n state += " (" + str(tsecs) + " seconds)"\n print(state, file=self.cc.file)\n\n def startTest(self, test):\n # j =self.testsRun\n self.testsRun += 1\n # item_title = self.getDescription(test)\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n self.item_title_print = " * q%i.%i) %s" % (UTextResult.number + 1, self.testsRun, item_title)\n estimated_time = 10\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar, file=sys.stdout)\n else:\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n self._stdout = sys.stdout\n sys.stdout = io.StringIO()\n\n def stopTest(self, test):\n sys.stdout = self._stdout\n super().stopTest(test)\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass is None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n print(" " * max(0, self.nL - len(self.cc.title)) + (" (" + str(q_time) + " seconds)" if q_time >= 0.5 else ""))\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n stream = io.StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)))\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n\n return wrapper\n\n\ndef get_hints(ss):\n if ss == None:\n return None\n try:\n ss = textwrap.dedent(ss)\n ss = ss.replace(\'\'\'"""\'\'\', "").strip()\n hints = ["hints:", ]\n j = np.argmax([ss.lower().find(h) for h in hints])\n h = hints[j]\n ss = ss[ss.find(h) + len(h) + 1:]\n ss = "\\n".join([l for l in ss.split("\\n") if not l.strip().startswith(":")])\n ss = textwrap.dedent(ss)\n ss = ss.strip()\n return ss\n except Exception as e:\n print("bad hints", ss, e)\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n _with_coverage = False\n _report = None # The report used. This is very, very hacky and should always be None. Don\'t rely on it!\n\n def capture(self):\n if hasattr(self, \'_stdout\') and self._stdout is not None:\n file = self._stdout\n else:\n file = sys.stdout\n return Capturing2(stdout=file)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ is not None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n if self._with_coverage:\n if not hasattr(self._report, \'covcache\'):\n self._report.covcache = {}\n import coverage\n self.cov = coverage.Coverage()\n self.cov.start()\n self.setUp()\n\n def _callTearDown(self):\n self.tearDown()\n if self._with_coverage:\n from pathlib import Path\n from snipper import snipper\n self.cov.stop()\n data = self.cov.get_data()\n base, _, _ = self._report._import_base_relative()\n for file in data.measured_files():\n file = os.path.normpath(file)\n root = Path(base)\n child = Path(file)\n if root in child.parents:\n with open(child, \'r\') as f:\n s = f.read()\n lines = s.splitlines()\n garb = \'GARBAGE\'\n\n lines2 = snipper.censor_code(lines, keep=True)\n assert len(lines) == len(lines2)\n\n for l in data.contexts_by_lineno(file):\n if lines2[l].strip() == garb:\n if self.cache_id() not in self._report.covcache:\n self._report.covcache[self.cache_id()] = {}\n\n rel = os.path.relpath(child, root)\n cc = self._report.covcache[self.cache_id()]\n j = 0\n for j in range(l, -1, -1):\n if "def" in lines2[j] or "class" in lines2[j]:\n break\n from snipper.snipper import gcoms\n fun = lines2[j]\n comments, _ = gcoms("\\n".join(lines2[j:l]))\n if rel not in cc:\n cc[rel] = {}\n cc[rel][fun] = (l, "\\n".join(comments))\n self._cache_put((self.cache_id(), \'coverage\'), self._report.covcache)\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd is None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n sd = self.shortDescriptionStandard()\n title = self._cache_get((self.cache_id(), \'title\'), sd)\n return title if title is not None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome is None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc is not None:\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard())\n\n self._cache2[(self.cache_id(), \'assert\')] = {}\n res = testMethod()\n elapsed = time.time() - t\n self._get_outcome()[self.cache_id()] = res\n self._cache_put((self.cache_id(), "time"), elapsed)\n\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return c, m\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n # sys.stdout = self._stdout\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n self.__class__._cache[\n key] = {} # A new dict. We manually insert it because we have to use that the dict is mutable.\n cache = self._cache_get(key)\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, f"Key {id} not found in cache; framework files missing. Please run deploy()")\n\n # The order of these calls is important. If the method assert fails, we should still store the correct result in cache.\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n assert_fun(first, _expected, *args, **kwargs)\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache is not None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n if os.path.exists(cfile):\n try:\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n except Exception as e:\n print("Bad cache", cfile)\n print(e)\n else:\n print("Warning! data file not found", cfile)\n\n def _feedErrorsToResult(self, result, errors):\n """ Use this to show hints on test failure. """\n if not isinstance(result, UTextResult):\n er = [e for e, v in errors if v != None]\n\n if len(er) > 0:\n hints = []\n key = (self.cache_id(), \'coverage\')\n if self._cache_contains(key):\n CC = self._cache_get(key)\n for id in CC:\n if id == self.cache_id():\n cl, m = id\n gprint(f"> An error occured while solving: {cl}.{m}. The files/methods you need to edit are:") # For the test {id} in {file} you should edit:")\n for file in CC[id]:\n rec = CC[id][file]\n gprint(f"> * {file}")\n for l in rec:\n _, comments = CC[id][file][l]\n hint = get_hints(comments)\n\n if hint != None:\n hints.append(hint)\n gprint(f"> - {l}")\n\n er = er[0]\n doc = er._testMethodDoc\n if doc is not None:\n hint = get_hints(er._testMethodDoc)\n if hint is not None:\n hints = [hint] + hints\n if len(hints) > 0:\n gprint("> Hints:")\n gprint(textwrap.indent("\\n".join(hints), "> "))\n\n super()._feedErrorsToResult(result, errors)\n\n def startTestRun(self):\n # print("asdfsdaf 11", file=sys.stderr)\n super().startTestRun()\n # print("asdfsdaf")\n\n def _callTestMethod(self, method):\n # print("asdfsdaf")\n super()._callTestMethod(method)\n\n\ndef hide(func):\n return func\n\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n# 817\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\nimport inspect\nimport os\nimport argparse\nimport time\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print(b + " v" + __version__ + ", started: " + dt_string+ "\\n")\n # print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print(s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n UTextResult.nL = report.nL\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"Question {n+1} total"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")",\n last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL)\n\n # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n # print(" ")\n # print("="*n)\n # print("Final evaluation")\n # print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n print("")\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f" * {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single unmodified file: ")\n print(">", token)\n # print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\nfrom cs101.homework1 import reverse_list, add\nimport unittest\n\nclass Week1(unittest.TestCase):\n def test_add(self):\n self.assertEqual(add(2,2), 4)\n self.assertEqual(add(-100, 5), -95)\n\n def test_reverse(self):\n self.assertEqual(reverse_list([1,2,3]), [3,2,1])\n\n\nimport cs101\nclass Report1(Report):\n title = "CS 101 Report 1"\n questions = [(Week1, 10)] # Include a single question for 10 credits.\n pack_imports = [cs101]' report1_payload = '8004953f000000000000007d948c055765656b31947d948c2c6e6f20636163686520736565205f73657475705f616e737765727320696e20756e69746772616465322e7079948873732e' name="Report1" diff --git a/examples/example_simplest/students/cs101/Report1_handin_0_of_10.token b/examples/example_simplest/students/cs101/Report1_handin_0_of_10.token deleted file mode 100644 index 5ccd4e5495ad2bbfe4c3cc09832916b356f0d31b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77002 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 adITU+h(VhvJuIL!Go^<WVp?f&sU84@H=JAm diff --git a/examples/example_simplest/students/cs101/__pycache__/deploy.cpython-38.pyc b/examples/example_simplest/students/cs101/__pycache__/deploy.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e14e2ac2aed2a082d17689ac69828e53aeba409 GIT binary patch literal 911 zcmWIL<>g{vU|^^b)=Qks%)sy%#6iaF3=9ko3=9m#3m6y}QW#Pga~N_NqZk=MY^EHh zT;?cdFq=7tC6_gd70hPIVT)o*VMt-gVUJ=@WzXVRz?sUmkTHrol_!NYo2h6{Dsw7h z3L6x%_p&lFq_SqQrgEonh%=;er*Mihq;REhr|_ikrZcDT^|CNBr0}~i#L7hRrm`*I zOXW@x0IL%eXGrB0XIRL@$WU09!V=7&DfANLc1^}x>_Mpo`9&p$nvA!^i&INV3*ys@ z5>ry+(=u~X<BPy@nvAzNit{oH;!`q<{BCi?$LA(y=EcXmX|hDI<!0t(rd399<|n0< z7AGf`q(-r)R-`7EmZV0pBvoY=RB`I->FFmI8yXl^iGrAVdiupBr75X-CB+cokXzjG z@dcI1iOCtM@$pscT6!h<*{OL|oGGaVIr)`(1(ljCQC!9OrA5i9AQwb&q@)&?fLWr+ zIjM<x@gRO?USdgRejZquJ+&e^r!*y1lex-5PY-HoYDHphK~8FXT2W$dYI%N9wti+_ zaY<2Wa!Gy>*da#x8Tk+yL%o8^DpL%dDf!9SsYU48j8V0jV`xjuNi0FvWC#{Sv5yzU zoL`Lkn*33`;BeD}#DZZF69WT7lv-(CW(g#U3W_qz5=&Bz^fEG2Qd0BclZ#RlOCT{< zT*M4g%npjSg4CiS77&lEIHR;AGv^jle(^1qg2a-HTkI*BMR|$2sh~&&M|gY`Q+W!A z2NI2szr~n#izPX~pmHTc5kCV1g!mQbY!wq)oLW>IQ&O6d8sn0mT$&4t&ln7+#Xv*7 z7{-V%&IH9maY+m!^kcxjjDbh@Ee@O9{FKt1R69mcb`oM>VBlc{K><b{CJsg+W(8&r FW&j@s6|(>U literal 0 HcmV?d00001 diff --git a/examples/example_simplest/students/cs101/__pycache__/homework1.cpython-38.pyc b/examples/example_simplest/students/cs101/__pycache__/homework1.cpython-38.pyc index 1149b3ace2379ce11502c3e4633382e7f8d4950a..7dbbdac7ae33a6d3d1764a740a5f47ff3e00b252 100644 GIT binary patch delta 374 zcmaFFewd9fl$V!_fq{YHYmj=PAJarWnR+({28I-d6vh^YD25cK6y_F&D8^K#6qanJ zBBxa56xLM66t)z$W~SK;bD5eM85vTUQ`l1(QrJ>BQaDq%(pj1ro0%9HQn-T|G<lL4 zL1sfSh|SEvz~Iclz);M?z`#(#P{LTl(9GD(7|fu_Sha{tK|w)bvKZrBRZXT_EIFCQ zCAT<=Qp-|{ic?cG8Kc;8D?wr_8Hzxr{7RhsozaCWGq1R$s5H4Gzi6^ElOiw3j$$T| zg{(!alM9(-`8XID7-|@57{wVtmP}s2l&TBTuE}(ZG4U2-64)#V!NS16aEm!HB?Y7f zVhD2)$7BoU5O0<uRt5%!B9O!_j`;Yz#N5>Q_*<+6MVWae5X~SnAuO;l95%W6DWy57 Sc3=mA^l`9ruyQboFaiM8MNBUM delta 534 zcmX@i_K2M?l$V!_fq{YH<6)gdGv<kWGWA{z3=Am@Q4A@JDNHR4QH&|fDJ(4vQB0}K zDXiH{MQ*7qDQu}sDeNii%}lcy<}x)iGBTvHq;RA%rm&}Qrf{Wjr?WIOHZw6Yr0@hY zX!2I2bGcR|<`(3nDioKLrljVTC?w~nr0OYzWMmdAq-Ex$Dr6QbB$k%s=O&h9Cf6tC z<Wwr8r{<*=C6=V7D5MqT=PHzBq=HozD`e(@IhlFIB}JvlCHY0VDVas7AVwuuG9$<* zPz+)-F)%PVGcYg|^Dr<llrW?)HZwLcrZ5FFXfjtV;!;phP;k#IOU+YARLIFJE>Td= zt<1@sxVzism$)a$y}2N_rj_O;mt^MWDJ12mRQj!Cyd~_HUjmm-O>r$M$}iGnjA8?6 zE-qQgP{hK(!0;=6ayFw22h_vGlXo&I@`A*RK~7>|D`K1coKaQ?(^->+m{N^FPWLMU zsnle;#h7@DF$ruYgkWW0V7SGcn34k03^9(Sh;#B1rVv5aTkP@iDf!9q@kMM53=Bn_ plbA&fK(<13f>~hg95%W6DWy57b|9OILGjPR%)!CI#=#`Q2mmE&hJXM7 diff --git a/examples/example_simplest/students/cs101/__pycache__/report1.cpython-38.pyc b/examples/example_simplest/students/cs101/__pycache__/report1.cpython-38.pyc index d7c0fcd96cc7cec28f9155fb61bec8c3bb6e00df..83e9e30d8000e629914b0160acf822078d8a3b78 100644 GIT binary patch delta 163 zcmdnVd4`iOl$V!_fq{Xc@T5-S3#N^Hvl$tSCa+>NHcc!o$<IklichUb%q_@C)z{P0 zhce@fGeIK7CHk3p#U(|h$tC$k`pLzH28Q~Z|1&OU6u!k-SejZ~l9``Ze2Wz<aclBU zW;I5E$q$(28E;KuiITp>QJR-oQj%I+a*HD*wYbDNu{gDeje&uoNNRE^i!D1R2crNh G2QvWvcs7Fo delta 176 zcmX@Zxs#JGl$V!_fq{YH?qQuoZsv`Avl$uVC$C~OR!Xf%%q_@CjW5mwQN<-OnR&$} zMWx9l`9(3w#fAokF`I>$mNN=RaTb=Q7MEn^=M~>#1xrLtKEtf0B*?(PP|U`_z`()C z!pOx~B*wtNaEl{8J~uHlFFszAH%fG}0gK3F4Hj4FD2~#+%#xDS;*uzikksN5=fvXF VB9PW1$;llow(MLSi~_72%m70OF&+Q_ diff --git a/examples/example_simplest/students/cs101/__pycache__/report1_grade.cpython-38.pyc b/examples/example_simplest/students/cs101/__pycache__/report1_grade.cpython-38.pyc index 0aeda2d66a25eaae02e31a9c629a9a2887fb7dbd..aed06f7cf667b51f32903bf52b3c17138bda2777 100644 GIT binary patch delta 3389 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 NaB#AKsx&5$P5|l_t4sg@ delta 7949 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 diff --git a/examples/example_simplest/students/cs101/report1.py b/examples/example_simplest/students/cs101/report1.py index 8e5dfca..9e9fce7 100644 --- a/examples/example_simplest/students/cs101/report1.py +++ b/examples/example_simplest/students/cs101/report1.py @@ -1,8 +1,8 @@ """ Example student code. This file is automatically generated from the files in the instructor-directory """ -from unitgrade2.unitgrade2 import Report -from unitgrade2.unitgrade_helpers2 import evaluate_report_student +from src.unitgrade2.unitgrade2 import Report +from src.unitgrade2 import evaluate_report_student from cs101.homework1 import reverse_list, add import unittest @@ -13,7 +13,6 @@ class Week1(unittest.TestCase): def test_reverse(self): self.assertEqual(reverse_list([1,2,3]), [3,2,1]) - # print("Bad output\n\n") import cs101 diff --git a/examples/example_simplest/students/cs101/report1_grade.py b/examples/example_simplest/students/cs101/report1_grade.py index efb1981..c244e79 100644 --- a/examples/example_simplest/students/cs101/report1_grade.py +++ b/examples/example_simplest/students/cs101/report1_grade.py @@ -6,14 +6,10 @@ from tabulate import tabulate from datetime import datetime import pyfiglet import unittest - import inspect import os import argparse -import sys import time -import threading # don't import Thread bc. of minify issue. -import tqdm # don't do from tqdm import tqdm because of minify-issue parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: To run all tests in a report: @@ -63,53 +59,6 @@ def evaluate_report_student(report, question=None, qitem=None, unmute=None, pass show_tol_err=show_tol_err) - # try: # For registering stats. - # import unitgrade_private - # import irlc.lectures - # import xlwings - # from openpyxl import Workbook - # import pandas as pd - # from collections import defaultdict - # dd = defaultdict(lambda: []) - # error_computed = [] - # for k1, (q, _) in enumerate(report.questions): - # for k2, item in enumerate(q.items): - # dd['question_index'].append(k1) - # dd['item_index'].append(k2) - # dd['question'].append(q.name) - # dd['item'].append(item.name) - # dd['tol'].append(0 if not hasattr(item, 'tol') else item.tol) - # error_computed.append(0 if not hasattr(item, 'error_computed') else item.error_computed) - # - # qstats = report.wdir + "/" + report.name + ".xlsx" - # - # if os.path.isfile(qstats): - # d_read = pd.read_excel(qstats).to_dict() - # else: - # d_read = dict() - # - # for k in range(1000): - # key = 'run_'+str(k) - # if key in d_read: - # dd[key] = list(d_read['run_0'].values()) - # else: - # dd[key] = error_computed - # break - # - # workbook = Workbook() - # worksheet = workbook.active - # for col, key in enumerate(dd.keys()): - # worksheet.cell(row=1, column=col+1).value = key - # for row, item in enumerate(dd[key]): - # worksheet.cell(row=row+2, column=col+1).value = item - # - # workbook.save(qstats) - # workbook.close() - # - # except ModuleNotFoundError as e: - # s = 234 - # pass - if question is None: print("Provisional evaluation") tabulate(table_data) @@ -161,24 +110,20 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa b = "\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] ) else: b = "Unitgrade" - print(b + " v" + __version__) dt_string = now.strftime("%d/%m/%Y %H:%M:%S") - print("Started: " + dt_string) + print(b + " v" + __version__ + ", started: " + dt_string+ "\n") + # print("Started: " + dt_string) s = report.title if hasattr(report, "version") and report.version is not None: s += " version " + report.version - print("Evaluating " + s, "(use --help for options)" if show_help_flag else "") + print(s, "(use --help for options)" if show_help_flag else "") # print(f"Loaded answers from: ", report.computed_answers_file, "\n") table_data = [] - nL = 80 t_start = time.time() score = {} loader = SequentialTestLoader() for n, (q, w) in enumerate(report.questions): - # q = q() - # q_hidden = False - # q_hidden = issubclass(q.__class__, Hidden) if question is not None and n+1 != question: continue suite = loader.loadTestsFromTestCase(q) @@ -188,104 +133,28 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa q.possible = 0 q.obtained = 0 q_ = {} # Gather score in this class. - # unittest.Te - # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_] UTextResult.q_title_print = q_title_print # Hacky UTextResult.show_progress_bar = show_progress_bar # Hacky. UTextResult.number = n + UTextResult.nL = report.nL res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) - # res = UTextTestRunner(verbosity=2, resultclass=unittest.TextTestResult).run(suite) - z = 234 - # for j, item in enumerate(q.items): - # if qitem is not None and question is not None and j+1 != qitem: - # continue - # - # if q_with_outstanding_init is not None: # check for None bc. this must be called to set titles. - # # if not item.question.has_called_init_: - # start = time.time() - # - # cc = None - # if show_progress_bar: - # total_estimated_time = q.estimated_time # Use this. The time is estimated for the q itself. # sum( [q2.estimated_time for q2 in q_with_outstanding_init] ) - # cc = ActiveProgress(t=total_estimated_time, title=q_title_print) - # from unitgrade import Capturing # DON'T REMOVE THIS LINE - # with eval('Capturing')(unmute=unmute): # Clunky import syntax is required bc. of minify issue. - # try: - # for q2 in q_with_outstanding_init: - # q2.init() - # q2.has_called_init_ = True - # - # # item.question.init() # Initialize the question. Useful for sharing resources. - # except Exception as e: - # if not passall: - # if not silent: - # print(" ") - # print("="*30) - # print(f"When initializing question {q.title} the initialization code threw an error") - # print(e) - # print("The remaining parts of this question will likely fail.") - # print("="*30) - # - # if show_progress_bar: - # cc.terminate() - # sys.stdout.flush() - # print(q_title_print, end="") - # - # q_time =np.round( time.time()-start, 2) - # - # print(" "* max(0,nL - len(q_title_print) ) + (" (" + str(q_time) + " seconds)" if q_time >= 0.1 else "") ) # if q.name in report.payloads else "") - # print("=" * nL) - # q_with_outstanding_init = None - # - # # item.question = q # Set the parent question instance for later reference. - # item_title_print = ss = "*** q%i.%i) %s"%(n+1, j+1, item.title) - # - # if show_progress_bar: - # cc = ActiveProgress(t=item.estimated_time, title=item_title_print) - # else: - # print(item_title_print + ( '.'*max(0, nL-4-len(ss)) ), end="") - # hidden = issubclass(item.__class__, Hidden) - # # if not hidden: - # # print(ss, end="") - # # sys.stdout.flush() - # start = time.time() - # - # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent) - # q_[j] = {'w': item.weight, 'possible': possible, 'obtained': current, 'hidden': hidden, 'computed': str(item._computed_answer), 'title': item.title} - # tsecs = np.round(time.time()-start, 2) - # if show_progress_bar: - # cc.terminate() - # sys.stdout.flush() - # print(item_title_print + ('.' * max(0, nL - 4 - len(ss))), end="") - # - # if not hidden: - # ss = "PASS" if current == possible else "*** FAILED" - # if tsecs >= 0.1: - # ss += " ("+ str(tsecs) + " seconds)" - # print(ss) - - # ws, possible, obtained = upack(q_) possible = res.testsRun obtained = len(res.successes) assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun - # possible = int(ws @ possible) - # obtained = int(ws @ obtained) - # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0 - obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0 score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle} q.obtained = obtained q.possible = possible - s1 = f"*** Question q{n+1}" + s1 = f"Question {n+1} total" s2 = f" {q.obtained}/{w}" - print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 ) + print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 ) print(" ") - table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"]) + table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"]) ws, possible, obtained = upack(score) possible = int( msum(possible) ) @@ -300,15 +169,16 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa seconds = dt - minutes*60 plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") - print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")") + dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", + last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) + + # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total") table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) results = {'total': (obtained, possible), 'details': score} return results, table_data - - from tabulate import tabulate from datetime import datetime import inspect @@ -331,7 +201,8 @@ def gather_imports(imp): # dn = os.path.dirname(f) # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) # top_package = str(__import__(m.__name__.split('.')[0]).__path__) - if m.__class__.__name__ == 'module' and False: + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: top_package = os.path.dirname(m.__file__) module_import = True else: @@ -352,7 +223,7 @@ def gather_imports(imp): for file in files: if file.endswith(".py"): fpath = os.path.join(root, file) - v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package)) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) zip.write(fpath, v) resources['zipfile'] = zip_buffer.getvalue() @@ -396,14 +267,14 @@ def gather_upload_to_campusnet(report, output_dir=None): results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, show_progress_bar=not args.noprogress, big_header=not args.autolab) - print(" ") - print("="*n) - print("Final evaluation") - print(tabulate(table_data)) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) # also load the source code of missing files... sources = {} - + print("") if not args.autolab: if len(report.individual_imports) > 0: print("By uploading the .token file, you verify the files:") @@ -416,12 +287,15 @@ def gather_upload_to_campusnet(report, output_dir=None): print("Including files in upload...") for k, m in enumerate(report.pack_imports): nimp, top_package = gather_imports(m) - report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import nimp['name'] = m.__name__ sources[k] = nimp # if len([k for k in nimp if k not in sources]) > 0: - print(f"*** {m.__name__}") + print(f" * {m.__name__}") # sources = {**sources, **nimp} results['sources'] = sources @@ -440,9 +314,9 @@ def gather_upload_to_campusnet(report, output_dir=None): if not args.autolab: print(" ") - print("To get credit for your results, please upload the single file: ") + print("To get credit for your results, please upload the single unmodified file: ") print(">", token) - print("To campusnet without any modifications.") + # print("To campusnet without any modifications.") # print("Now time for some autolab fun") @@ -455,7 +329,7 @@ def source_instantiate(name, report1_source, payload): -report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n\n"""\n# from . import cache_read\nimport unittest\nimport numpy as np\nimport sys\nfrom io import StringIO\nimport collections\nimport re\nimport threading\nimport tqdm\nimport time\nimport pickle\nimport itertools\nimport os\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\ndef setup_dir_by_class(C,base_dir):\n name = C.__class__.__name__\n # base_dir = os.path.join(base_dir, name)\n # if not os.path.isdir(base_dir):\n # os.makedirs(base_dir)\n return base_dir, name\n\nclass Hidden:\n def hide(self):\n return True\n\nclass Logger(object):\n def __init__(self, buffer):\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\nclass Capturing(list):\n def __init__(self, *args, unmute=False, **kwargs):\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass QItem(unittest.TestCase):\n title = None\n testfun = None\n tol = 0\n estimated_time = 0.42\n _precomputed_payload = None\n _computed_answer = None # Internal helper to later get results.\n weight = 1 # the weight of the question.\n\n def __init__(self, question=None, *args, **kwargs):\n if self.tol > 0 and self.testfun is None:\n self.testfun = self.assertL2Relative\n elif self.testfun is None:\n self.testfun = self.assertEqual\n\n self.name = self.__class__.__name__\n # self._correct_answer_payload = correct_answer_payload\n self.question = question\n\n super().__init__(*args, **kwargs)\n if self.title is None:\n self.title = self.name\n\n def _safe_get_title(self):\n if self._precomputed_title is not None:\n return self._precomputed_title\n return self.title\n\n def assertNorm(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed).flat- np.asarray(expected)).flat )\n nrm = np.sqrt(np.sum( diff ** 2))\n\n self.error_computed = nrm\n\n if nrm > tol:\n print(f"Not equal within tolerance {tol}; norm of difference was {nrm}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def assertL2(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n self.error_computed = np.max(diff)\n\n if np.max(diff) > tol:\n print(f"Not equal within tolerance {tol=}; deviation was {np.max(diff)=}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol=}, {np.max(diff)=}")\n\n def assertL2Relative(self, computed, expected, tol=None):\n if tol == None:\n tol = self.tol\n diff = np.abs( (np.asarray(computed) - np.asarray(expected)) )\n diff = diff / (1e-8 + np.abs( (np.asarray(computed) + np.asarray(expected)) ) )\n self.error_computed = np.max(np.abs(diff))\n if np.sum(diff > tol) > 0:\n print(f"Not equal within tolerance {tol}")\n print(f"Element-wise differences {diff.tolist()}")\n self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}")\n\n def precomputed_payload(self):\n return self._precomputed_payload\n\n def precompute_payload(self):\n # Pre-compute resources to include in tests (useful for getting around rng).\n pass\n\n def compute_answer(self, unmute=False):\n raise NotImplementedError("test code here")\n\n def test(self, computed, expected):\n self.testfun(computed, expected)\n\n def get_points(self, verbose=False, show_expected=False, show_computed=False,unmute=False, passall=False, silent=False, **kwargs):\n possible = 1\n computed = None\n def show_computed_(computed):\n print(">>> Your output:")\n print(computed)\n\n def show_expected_(expected):\n print(">>> Expected output (note: may have been processed; read text script):")\n print(expected)\n\n correct = self._correct_answer_payload\n try:\n if unmute: # Required to not mix together print stuff.\n print("")\n computed = self.compute_answer(unmute=unmute)\n except Exception as e:\n if not passall:\n if not silent:\n print("\\n=================================================================================")\n print(f"When trying to run test class \'{self.name}\' your code threw an error:", e)\n show_expected_(correct)\n import traceback\n print(traceback.format_exc())\n print("=================================================================================")\n return (0, possible)\n\n if self._computed_answer is None:\n self._computed_answer = computed\n\n if show_expected or show_computed:\n print("\\n")\n if show_expected:\n show_expected_(correct)\n if show_computed:\n show_computed_(computed)\n try:\n if not passall:\n self.test(computed=computed, expected=correct)\n except Exception as e:\n if not silent:\n print("\\n=================================================================================")\n print(f"Test output from test class \'{self.name}\' does not match expected result. Test error:")\n print(e)\n show_computed_(computed)\n show_expected_(correct)\n return (0, possible)\n return (1, possible)\n\n def score(self):\n try:\n self.test()\n except Exception as e:\n return 0\n return 1\n\nclass QPrintItem(QItem):\n def compute_answer_print(self):\n """\n Generate output which is to be tested. By default, both text written to the terminal using print(...) as well as return values\n are send to process_output (see compute_answer below). In other words, the text generated is:\n\n res = compute_Answer_print()\n txt = (any terminal output generated above)\n numbers = (any numbers found in terminal-output txt)\n\n self.test(process_output(res, txt, numbers), <expected result>)\n\n :return: Optional values for comparison\n """\n raise Exception("Generate output here. The output is passed to self.process_output")\n\n def process_output(self, res, txt, numbers):\n return res\n\n def compute_answer(self, unmute=False):\n with Capturing(unmute=unmute) as output:\n res = self.compute_answer_print()\n s = "\\n".join(output)\n s = rm_progress_bar(s) # Remove progress bar.\n numbers = extract_numbers(s)\n self._computed_answer = (res, s, numbers)\n return self.process_output(res, s, numbers)\n\nclass OrderedClassMembers(type):\n @classmethod\n def __prepare__(self, name, bases):\n return collections.OrderedDict()\n def __new__(self, name, bases, classdict):\n ks = list(classdict.keys())\n for b in bases:\n ks += b.__ordered__\n classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n return type.__new__(self, name, bases, classdict)\n\nclass QuestionGroup(metaclass=OrderedClassMembers):\n title = "Untitled question"\n partially_scored = False\n t_init = 0 # Time spend on initialization (placeholder; set this externally).\n estimated_time = 0.42\n has_called_init_ = False\n _name = None\n _items = None\n\n @property\n def items(self):\n if self._items == None:\n self._items = []\n members = [gt for gt in [getattr(self, gt) for gt in self.__ordered__ if gt not in ["__classcell__", "__init__"]] if inspect.isclass(gt) and issubclass(gt, QItem)]\n for I in members:\n self._items.append( I(question=self))\n return self._items\n\n @items.setter\n def items(self, value):\n self._items = value\n\n @property\n def name(self):\n if self._name == None:\n self._name = self.__class__.__name__\n return self._name #\n\n @name.setter\n def name(self, val):\n self._name = val\n\n def init(self):\n # Can be used to set resources relevant for this question instance.\n pass\n\n def init_all_item_questions(self):\n for item in self.items:\n if not item.question.has_called_init_:\n item.question.init()\n item.question.has_called_init_ = True\n\n\nclass Report():\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 80 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q,_) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n root_dir = self.pack_imports[0].__path__._path[0]\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q,_) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n # else:\n # if os.path.isfile(self.computed_answers_file):\n # self.set_payload(cache_read(self.computed_answers_file), strict=strict)\n # else:\n # s = f"> Warning: The pre-computed answer file, {os.path.abspath(self.computed_answers_file)} is missing. The framework will NOT work as intended. Reasons may be a broken local installation."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n import unittest\n loader = unittest.TestLoader()\n for q,_ in self.questions:\n import time\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self):\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\':True}\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n # for item in q.items:\n # if q.name not in payloads or item.name not in payloads[q.name]:\n # s = f"> Broken resource dictionary submitted to unitgrade for question {q.name} and subquestion {item.name}. Framework will not work."\n # if strict:\n # raise Exception(s)\n # else:\n # print(s)\n # else:\n # item._correct_answer_payload = payloads[q.name][item.name][\'payload\']\n # item.estimated_time = payloads[q.name][item.name].get("time", 1)\n # q.estimated_time = payloads[q.name].get("time", 1)\n # if "precomputed" in payloads[q.name][item.name]: # Consider removing later.\n # item._precomputed_payload = payloads[q.name][item.name][\'precomputed\']\n # try:\n # if "title" in payloads[q.name][item.name]: # can perhaps be removed later.\n # item.title = payloads[q.name][item.name][\'title\']\n # except Exception as e: # Cannot set attribute error. The title is a function (and probably should not be).\n # pass\n # # print("bad", e)\n # self.payloads = payloads\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct+1)\n if i > 0 and l.find("|", i+1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = \'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar",show_progress_bar=True):\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.1\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n\n # self.pbar = tqdm.tqdm(total=self.n)\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if hasattr(self, \'pbar\') and self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar=None\n\n sys.stdout.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=sys.stdout, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\') # , unit_scale=dt, unit=\'seconds\'):\n\n for _ in range(self.n-1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\n\n\nfrom unittest.suite import _isnotsuite\n\nclass MySuite(unittest.suite.TestSuite): # Not sure we need this one anymore.\n pass\n\ndef instance_call_stack(instance):\n s = "-".join(map(lambda x: x.__name__, instance.__class__.mro()))\n return s\n\ndef get_class_that_defined_method(meth):\n for cls in inspect.getmro(meth.im_class):\n if meth.__name__ in cls.__dict__:\n return cls\n return None\n\ndef caller_name(skip=2):\n """Get a name of a caller in the format module.class.method\n\n `skip` specifies how many levels of stack to skip while getting caller\n name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.\n\n An empty string is returned if skipped levels exceed stack height\n """\n stack = inspect.stack()\n start = 0 + skip\n if len(stack) < start + 1:\n return \'\'\n parentframe = stack[start][0]\n\n name = []\n module = inspect.getmodule(parentframe)\n # `modname` can be None when frame is executed directly in console\n # TODO(techtonik): consider using __main__\n if module:\n name.append(module.__name__)\n # detect classname\n if \'self\' in parentframe.f_locals:\n # I don\'t know any way to detect call from the object method\n # XXX: there seems to be no way to detect static method call - it will\n # be just a function call\n name.append(parentframe.f_locals[\'self\'].__class__.__name__)\n codename = parentframe.f_code.co_name\n if codename != \'<module>\': # top level usually\n name.append( codename ) # function or a method\n\n ## Avoid circular refs and frame leaks\n # https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack\n del parentframe, stack\n\n return ".".join(name)\n\ndef get_class_from_frame(fr):\n import inspect\n args, _, _, value_dict = inspect.getargvalues(fr)\n # we check the first parameter for the frame function is\n # named \'self\'\n if len(args) and args[0] == \'self\':\n # in that case, \'self\' will be referenced in value_dict\n instance = value_dict.get(\'self\', None)\n if instance:\n # return its class\n # isinstance(instance, Testing) # is the actual class instance.\n\n return getattr(instance, \'__class__\', None)\n # return None otherwise\n return None\n\nfrom typing import Any\nimport inspect, gc\n\ndef giveupthefunc():\n frame = inspect.currentframe()\n code = frame.f_code\n globs = frame.f_globals\n functype = type(lambda: 0)\n funcs = []\n for func in gc.get_referrers(code):\n if type(func) is functype:\n if getattr(func, "__code__", None) is code:\n if getattr(func, "__globals__", None) is globs:\n funcs.append(func)\n if len(funcs) > 1:\n return None\n return funcs[0] if funcs else None\n\n\nfrom collections import defaultdict\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n # if self.dots or self.showAll:\n # self.stream.writeln()\n # if hasattr(self, \'cc\'):\n # self.cc.terminate()\n # self.cc_terminate(success=False)\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n # if self.showAll:\n # self.stream.writeln("FAIL")\n # elif self.dots:\n # self.stream.write(\'F\')\n # self.stream.flush()\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n # super().addSuccess(test)\n self.successes.append(test)\n # super().addSuccess(test)\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n self.cc_terminate()\n\n\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n sys.stdout.flush()\n ss = self.item_title_print\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(ss))), end="")\n # current = 1\n # possible = 1\n # current == possible\n ss = "PASS" if success else "FAILED"\n if tsecs >= 0.1:\n ss += " (" + str(tsecs) + " seconds)"\n print(ss)\n\n\n def startTest(self, test):\n # super().startTest(test)\n j =self.testsRun\n self.testsRun += 1\n # print("Starting the test...")\n # show_progress_bar = True\n n = UTextResult.number\n\n item_title = self.getDescription(test)\n item_title = item_title.split("\\n")[0]\n\n item_title = test.shortDescription() # Better for printing (get from cache).\n # test.countTestCases()\n self.item_title_print = "*** q%i.%i) %s" % (n + 1, j + 1, item_title)\n estimated_time = 10\n nL = 80\n #\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar)\n else:\n print(self.item_title_print + (\'.\' * max(0, nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 2\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n # q_title_print = "some printed title..."\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass == None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n print(self.cc.title, end="")\n # start = 10\n # q_time = np.round(time.time() - start, 2)\n nL = 80\n print(" " * max(0, nL - len(self.cc.title)) + (\n " (" + str(q_time) + " seconds)" if q_time >= 0.1 else "")) # if q.name in report.payloads else "")\n # print("=" * nL)\n\nfrom unittest.runner import _WritelnDecorator\nfrom io import StringIO\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n from io import StringIO\n stream = StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\ndef wrapper(foo):\n def magic(self):\n s = "-".join(map(lambda x: x.__name__, self.__class__.mro()))\n # print(s)\n foo(self)\n magic.__doc__ = foo.__doc__\n return magic\n\nfrom functools import update_wrapper, _make_key, RLock\nfrom collections import namedtuple\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)) )\n # key = (self.cache_id(), \'@cache\')\n # if self._cache_contains[key]\n\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n return wrapper\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n\n @classmethod\n def question_title(cls):\n return cls.__doc__.splitlines()[0].strip() if cls.__doc__ != None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd == None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n # self._testMethodDoc.strip().splitlines()[0].strip()\n sd = self.shortDescriptionStandard()\n title = self._cache_get( (self.cache_id(), \'title\'), sd )\n return title if title != None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n # def _callSetUp(self):\n # # Always run before method is called.\n # print("asdf")\n # pass\n # @classmethod\n # def setUpClass(cls):\n # # self._cache_put((self.cache_id(), \'title\'), value)\n # cls.reset()\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome == None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc != None:\n # Ensure the cache is eventually updated with the right docstring.\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard() )\n # Fix temp cache here (for using the @cache decorator)\n self._cache2[ (self.cache_id(), \'assert\') ] = {}\n\n res = testMethod()\n elapsed = time.time() - t\n # self._cache_put( (self.cache_id(), \'title\'), self.shortDescription() )\n\n self._get_outcome()[self.cache_id()] = res\n self._cache_put( (self.cache_id(), "time"), elapsed)\n\n # This is my base test class. So what is new about it?\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return (c,m)\n\n # def unique_cache_id(self):\n # k0 = self.cache_id()\n # # key = ()\n # i = 0\n # for i in itertools.count():\n # # key = k0 + (i,)\n # if i not in self._cache_get( (k0, \'assert\') ):\n # break\n # return i\n # return key\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n # self.cache_indexes = defaultdict(lambda: 0)\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n #\n # def _cache2_contains(self, key):\n # print("Is this needed?")\n # self._ensure_cache_exists()\n # return key in self.__class__._cache2\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n cache = self._cache_get(key, {})\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, first)\n assert_fun(first, _expected, *args, **kwargs)\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__) ) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache != None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n # print("Loading cache from", cfile)\n if os.path.exists(cfile):\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n else:\n print("Warning! data file not found", cfile)\n\ndef hide(func):\n return func\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n # (*)We can be somewhat "hygienic", but newDecorator still isn\'t signature-preserving, i.e. you will not be able to get a runtime list of parameters. For that, you need hackish libraries...but in this case, the only argument is func, so it\'s not a big issue\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\n\nimport inspect\nimport os\nimport argparse\nimport sys\nimport time\nimport threading # don\'t import Thread bc. of minify issue.\nimport tqdm # don\'t do from tqdm import tqdm because of minify-issue\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n # try: # For registering stats.\n # import unitgrade_private\n # import irlc.lectures\n # import xlwings\n # from openpyxl import Workbook\n # import pandas as pd\n # from collections import defaultdict\n # dd = defaultdict(lambda: [])\n # error_computed = []\n # for k1, (q, _) in enumerate(report.questions):\n # for k2, item in enumerate(q.items):\n # dd[\'question_index\'].append(k1)\n # dd[\'item_index\'].append(k2)\n # dd[\'question\'].append(q.name)\n # dd[\'item\'].append(item.name)\n # dd[\'tol\'].append(0 if not hasattr(item, \'tol\') else item.tol)\n # error_computed.append(0 if not hasattr(item, \'error_computed\') else item.error_computed)\n #\n # qstats = report.wdir + "/" + report.name + ".xlsx"\n #\n # if os.path.isfile(qstats):\n # d_read = pd.read_excel(qstats).to_dict()\n # else:\n # d_read = dict()\n #\n # for k in range(1000):\n # key = \'run_\'+str(k)\n # if key in d_read:\n # dd[key] = list(d_read[\'run_0\'].values())\n # else:\n # dd[key] = error_computed\n # break\n #\n # workbook = Workbook()\n # worksheet = workbook.active\n # for col, key in enumerate(dd.keys()):\n # worksheet.cell(row=1, column=col+1).value = key\n # for row, item in enumerate(dd[key]):\n # worksheet.cell(row=row+2, column=col+1).value = item\n #\n # workbook.save(qstats)\n # workbook.close()\n #\n # except ModuleNotFoundError as e:\n # s = 234\n # pass\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n print(b + " v" + __version__)\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print("Evaluating " + s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n nL = 80\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n # q = q()\n # q_hidden = False\n # q_hidden = issubclass(q.__class__, Hidden)\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n # unittest.Te\n # q_with_outstanding_init = [item.question for item in q.items if not item.question.has_called_init_]\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n # res = UTextTestRunner(verbosity=2, resultclass=unittest.TextTestResult).run(suite)\n z = 234\n # for j, item in enumerate(q.items):\n # if qitem is not None and question is not None and j+1 != qitem:\n # continue\n #\n # if q_with_outstanding_init is not None: # check for None bc. this must be called to set titles.\n # # if not item.question.has_called_init_:\n # start = time.time()\n #\n # cc = None\n # if show_progress_bar:\n # total_estimated_time = q.estimated_time # Use this. The time is estimated for the q itself. # sum( [q2.estimated_time for q2 in q_with_outstanding_init] )\n # cc = ActiveProgress(t=total_estimated_time, title=q_title_print)\n # from unitgrade import Capturing # DON\'T REMOVE THIS LINE\n # with eval(\'Capturing\')(unmute=unmute): # Clunky import syntax is required bc. of minify issue.\n # try:\n # for q2 in q_with_outstanding_init:\n # q2.init()\n # q2.has_called_init_ = True\n #\n # # item.question.init() # Initialize the question. Useful for sharing resources.\n # except Exception as e:\n # if not passall:\n # if not silent:\n # print(" ")\n # print("="*30)\n # print(f"When initializing question {q.title} the initialization code threw an error")\n # print(e)\n # print("The remaining parts of this question will likely fail.")\n # print("="*30)\n #\n # if show_progress_bar:\n # cc.terminate()\n # sys.stdout.flush()\n # print(q_title_print, end="")\n #\n # q_time =np.round( time.time()-start, 2)\n #\n # print(" "* max(0,nL - len(q_title_print) ) + (" (" + str(q_time) + " seconds)" if q_time >= 0.1 else "") ) # if q.name in report.payloads else "")\n # print("=" * nL)\n # q_with_outstanding_init = None\n #\n # # item.question = q # Set the parent question instance for later reference.\n # item_title_print = ss = "*** q%i.%i) %s"%(n+1, j+1, item.title)\n #\n # if show_progress_bar:\n # cc = ActiveProgress(t=item.estimated_time, title=item_title_print)\n # else:\n # print(item_title_print + ( \'.\'*max(0, nL-4-len(ss)) ), end="")\n # hidden = issubclass(item.__class__, Hidden)\n # # if not hidden:\n # # print(ss, end="")\n # # sys.stdout.flush()\n # start = time.time()\n #\n # (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent)\n # q_[j] = {\'w\': item.weight, \'possible\': possible, \'obtained\': current, \'hidden\': hidden, \'computed\': str(item._computed_answer), \'title\': item.title}\n # tsecs = np.round(time.time()-start, 2)\n # if show_progress_bar:\n # cc.terminate()\n # sys.stdout.flush()\n # print(item_title_print + (\'.\' * max(0, nL - 4 - len(ss))), end="")\n #\n # if not hidden:\n # ss = "PASS" if current == possible else "*** FAILED"\n # if tsecs >= 0.1:\n # ss += " ("+ str(tsecs) + " seconds)"\n # print(ss)\n\n # ws, possible, obtained = upack(q_)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n # possible = int(ws @ possible)\n # obtained = int(ws @ obtained)\n # obtained = int(myround(int((w * obtained) / possible ))) if possible > 0 else 0\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"*** Question q{n+1}"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"Question q{n+1}", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n if m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package))\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n print(" ")\n print("="*n)\n print("Final evaluation")\n print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f"*** {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single file: ")\n print(">", token)\n print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\nfrom cs101.homework1 import reverse_list, add\nimport unittest\n\nclass Week1(unittest.TestCase):\n def test_add(self):\n self.assertEqual(add(2,2), 4)\n self.assertEqual(add(-100, 5), -95)\n\n def test_reverse(self):\n self.assertEqual(reverse_list([1,2,3]), [3,2,1])\n # print("Bad output\\n\\n")\n\n\nimport cs101\nclass Report1(Report):\n title = "CS 101 Report 1"\n questions = [(Week1, 10)] # Include a single question for 10 credits.\n pack_imports = [cs101]' +report1_source = 'import os\n\n# DONT\'t import stuff here since install script requires __version__\n\ndef cache_write(object, file_name, verbose=True):\n import compress_pickle\n dn = os.path.dirname(file_name)\n if not os.path.exists(dn):\n os.mkdir(dn)\n if verbose: print("Writing cache...", file_name)\n with open(file_name, \'wb\', ) as f:\n compress_pickle.dump(object, f, compression="lzma")\n if verbose: print("Done!")\n\n\ndef cache_exists(file_name):\n # file_name = cn_(file_name) if cache_prefix else file_name\n return os.path.exists(file_name)\n\n\ndef cache_read(file_name):\n import compress_pickle # Import here because if you import in top the __version__ tag will fail.\n # file_name = cn_(file_name) if cache_prefix else file_name\n if os.path.exists(file_name):\n try:\n with open(file_name, \'rb\') as f:\n return compress_pickle.load(f, compression="lzma")\n except Exception as e:\n print("Tried to load a bad pickle file at", file_name)\n print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version")\n print(e)\n # return pickle.load(f)\n else:\n return None\n\n\n\n"""\ngit add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade\n"""\nimport numpy as np\nimport sys\nimport re\nimport threading\nimport tqdm\nimport pickle\nimport os\nfrom io import StringIO\nimport io\nfrom unittest.runner import _WritelnDecorator\nfrom typing import Any\nimport inspect\nimport textwrap\nimport colorama\nfrom colorama import Fore\nfrom functools import _make_key, RLock\nfrom collections import namedtuple\nimport unittest\nimport time\n\n_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])\n\ncolorama.init(autoreset=True) # auto resets your settings after every output\n\ndef gprint(s):\n print(f"{Fore.GREEN}{s}")\n\nmyround = lambda x: np.round(x) # required.\nmsum = lambda x: sum(x)\nmfloor = lambda x: np.floor(x)\n\n\ndef setup_dir_by_class(C, base_dir):\n name = C.__class__.__name__\n return base_dir, name\n\n\nclass Logger(object):\n def __init__(self, buffer):\n assert False\n self.terminal = sys.stdout\n self.log = buffer\n\n def write(self, message):\n self.terminal.write(message)\n self.log.write(message)\n\n def flush(self):\n # this flush method is needed for python 3 compatibility.\n pass\n\n\nclass Capturing(list):\n def __init__(self, *args, stdout=None, unmute=False, **kwargs):\n self._stdout = stdout\n self.unmute = unmute\n super().__init__(*args, **kwargs)\n\n def __enter__(self, capture_errors=True): # don\'t put arguments here.\n self._stdout = sys.stdout if self._stdout == None else self._stdout\n self._stringio = StringIO()\n if self.unmute:\n sys.stdout = Logger(self._stringio)\n else:\n sys.stdout = self._stringio\n\n if capture_errors:\n self._sterr = sys.stderr\n sys.sterr = StringIO() # memory hole it\n self.capture_errors = capture_errors\n return self\n\n def __exit__(self, *args):\n self.extend(self._stringio.getvalue().splitlines())\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n\nclass Capturing2(Capturing):\n def __exit__(self, *args):\n lines = self._stringio.getvalue().splitlines()\n txt = "\\n".join(lines)\n numbers = extract_numbers(txt)\n self.extend(lines)\n del self._stringio # free up some memory\n sys.stdout = self._stdout\n if self.capture_errors:\n sys.sterr = self._sterr\n\n self.output = txt\n self.numbers = numbers\n\n\n# @classmethod\n# class OrderedClassMembers(type):\n# def __prepare__(self, name, bases):\n# assert False\n# return collections.OrderedDict()\n#\n# def __new__(self, name, bases, classdict):\n# ks = list(classdict.keys())\n# for b in bases:\n# ks += b.__ordered__\n# classdict[\'__ordered__\'] = [key for key in ks if key not in (\'__module__\', \'__qualname__\')]\n# return type.__new__(self, name, bases, classdict)\n\n\nclass Report:\n title = "report title"\n version = None\n questions = []\n pack_imports = []\n individual_imports = []\n nL = 120 # Maximum line width\n\n @classmethod\n def reset(cls):\n for (q, _) in cls.questions:\n if hasattr(q, \'reset\'):\n q.reset()\n\n @classmethod\n def mfile(clc):\n return inspect.getfile(clc)\n\n def _file(self):\n return inspect.getfile(type(self))\n\n def _import_base_relative(self):\n if hasattr(self.pack_imports[0], \'__path__\'):\n root_dir = self.pack_imports[0].__path__._path[0]\n else:\n root_dir = self.pack_imports[0].__file__\n\n root_dir = os.path.dirname(root_dir)\n relative_path = os.path.relpath(self._file(), root_dir)\n modules = os.path.normpath(relative_path[:-3]).split(os.sep)\n return root_dir, relative_path, modules\n\n def __init__(self, strict=False, payload=None):\n working_directory = os.path.abspath(os.path.dirname(self._file()))\n self.wdir, self.name = setup_dir_by_class(self, working_directory)\n # self.computed_answers_file = os.path.join(self.wdir, self.name + "_resources_do_not_hand_in.dat")\n for (q, _) in self.questions:\n q.nL = self.nL # Set maximum line length.\n\n if payload is not None:\n self.set_payload(payload, strict=strict)\n\n def main(self, verbosity=1):\n # Run all tests using standard unittest (nothing fancy).\n loader = unittest.TestLoader()\n for q, _ in self.questions:\n start = time.time() # A good proxy for setup time is to\n suite = loader.loadTestsFromTestCase(q)\n unittest.TextTestRunner(verbosity=verbosity).run(suite)\n total = time.time() - start\n q.time = total\n\n def _setup_answers(self, with_coverage=False):\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = True\n q._report = self\n\n self.main() # Run all tests in class just to get that out of the way...\n report_cache = {}\n for q, _ in self.questions:\n if hasattr(q, \'_save_cache\'):\n q()._save_cache()\n q._cache[\'time\'] = q.time\n report_cache[q.__qualname__] = q._cache\n else:\n report_cache[q.__qualname__] = {\'no cache see _setup_answers in unitgrade2.py\': True}\n if with_coverage:\n for q, _ in self.questions:\n q._with_coverage = False\n return report_cache\n\n def set_payload(self, payloads, strict=False):\n for q, _ in self.questions:\n q._cache = payloads[q.__qualname__]\n\n\ndef rm_progress_bar(txt):\n # More robust version. Apparently length of bar can depend on various factors, so check for order of symbols.\n nlines = []\n for l in txt.splitlines():\n pct = l.find("%")\n ql = False\n if pct > 0:\n i = l.find("|", pct + 1)\n if i > 0 and l.find("|", i + 1) > 0:\n ql = True\n if not ql:\n nlines.append(l)\n return "\\n".join(nlines)\n\n\ndef extract_numbers(txt):\n # txt = rm_progress_bar(txt)\n numeric_const_pattern = r\'[-+]? (?: (?: \\d* \\. \\d+ ) | (?: \\d+ \\.? ) )(?: [Ee] [+-]? \\d+ ) ?\'\n rx = re.compile(numeric_const_pattern, re.VERBOSE)\n all = rx.findall(txt)\n all = [float(a) if (\'.\' in a or "e" in a) else int(a) for a in all]\n if len(all) > 500:\n print(txt)\n raise Exception("unitgrade.unitgrade.py: Warning, too many numbers!", len(all))\n return all\n\n\nclass ActiveProgress():\n def __init__(self, t, start=True, title="my progress bar", show_progress_bar=True, file=None):\n if file == None:\n file = sys.stdout\n self.file = file\n self.t = t\n self._running = False\n self.title = title\n self.dt = 0.01\n self.n = int(np.round(self.t / self.dt))\n self.show_progress_bar = show_progress_bar\n self.pbar = None\n\n if start:\n self.start()\n\n def start(self):\n self._running = True\n if self.show_progress_bar:\n self.thread = threading.Thread(target=self.run)\n self.thread.start()\n self.time_started = time.time()\n\n def terminate(self):\n if not self._running:\n raise Exception("Stopping a stopped progress bar. ")\n self._running = False\n if self.show_progress_bar:\n self.thread.join()\n if self.pbar is not None:\n self.pbar.update(1)\n self.pbar.close()\n self.pbar = None\n\n self.file.flush()\n return time.time() - self.time_started\n\n def run(self):\n self.pbar = tqdm.tqdm(total=self.n, file=self.file, position=0, leave=False, desc=self.title, ncols=100,\n bar_format=\'{l_bar}{bar}| [{elapsed}<{remaining}]\')\n\n for _ in range(self.n - 1): # Don\'t terminate completely; leave bar at 99% done until terminate.\n if not self._running:\n self.pbar.close()\n self.pbar = None\n break\n\n time.sleep(self.dt)\n self.pbar.update(1)\n\ndef dprint(first, last, nL, extra = "", file=None, dotsym=\'.\', color=\'white\'):\n if file == None:\n file = sys.stdout\n\n # ss = self.item_title_print\n # state = "PASS" if success else "FAILED"\n dot_parts = (dotsym * max(0, nL - len(last) - len(first)))\n # if self.show_progress_bar or True:\n print(first + dot_parts, end="", file=file)\n # else:\n # print(dot_parts, end="", file=self.cc.file)\n last += extra\n # if tsecs >= 0.5:\n # state += " (" + str(tsecs) + " seconds)"\n print(last, file=file)\n\n\nclass UTextResult(unittest.TextTestResult):\n nL = 80\n number = -1 # HAcky way to set question number.\n show_progress_bar = True\n cc = None\n\n def __init__(self, stream, descriptions, verbosity):\n super().__init__(stream, descriptions, verbosity)\n self.successes = []\n\n def printErrors(self) -> None:\n self.printErrorList(\'ERROR\', self.errors)\n self.printErrorList(\'FAIL\', self.failures)\n\n def addError(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addFailure(self, test, err):\n super(unittest.TextTestResult, self).addFailure(test, err)\n self.cc_terminate(success=False)\n\n def addSuccess(self, test: unittest.case.TestCase) -> None:\n self.successes.append(test)\n self.cc_terminate()\n\n def cc_terminate(self, success=True):\n if self.show_progress_bar or True:\n tsecs = np.round(self.cc.terminate(), 2)\n self.cc.file.flush()\n ss = self.item_title_print\n\n state = "PASS" if success else "FAILED"\n\n dot_parts = (\'.\' * max(0, self.nL - len(state) - len(ss)))\n if self.show_progress_bar or True:\n print(self.item_title_print + dot_parts, end="", file=self.cc.file)\n else:\n print(dot_parts, end="", file=self.cc.file)\n\n if tsecs >= 0.5:\n state += " (" + str(tsecs) + " seconds)"\n print(state, file=self.cc.file)\n\n def startTest(self, test):\n # j =self.testsRun\n self.testsRun += 1\n # item_title = self.getDescription(test)\n item_title = test.shortDescription() # Better for printing (get from cache).\n if item_title == None:\n # For unittest framework where getDescription may return None.\n item_title = self.getDescription(test)\n self.item_title_print = " * q%i.%i) %s" % (UTextResult.number + 1, self.testsRun, item_title)\n estimated_time = 10\n if self.show_progress_bar or True:\n self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar, file=sys.stdout)\n else:\n print(self.item_title_print + (\'.\' * max(0, self.nL - 4 - len(self.item_title_print))), end="")\n\n self._test = test\n self._stdout = sys.stdout\n sys.stdout = io.StringIO()\n\n def stopTest(self, test):\n sys.stdout = self._stdout\n super().stopTest(test)\n\n def _setupStdout(self):\n if self._previousTestClass == None:\n total_estimated_time = 1\n if hasattr(self.__class__, \'q_title_print\'):\n q_title_print = self.__class__.q_title_print\n else:\n q_title_print = "<unnamed test. See unitgrade.py>"\n\n cc = ActiveProgress(t=total_estimated_time, title=q_title_print, show_progress_bar=self.show_progress_bar)\n self.cc = cc\n\n def _restoreStdout(self): # Used when setting up the test.\n if self._previousTestClass is None:\n q_time = self.cc.terminate()\n q_time = np.round(q_time, 2)\n sys.stdout.flush()\n if self.show_progress_bar:\n print(self.cc.title, end="")\n print(" " * max(0, self.nL - len(self.cc.title)) + (" (" + str(q_time) + " seconds)" if q_time >= 0.5 else ""))\n\n\nclass UTextTestRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n stream = io.StringIO()\n super().__init__(*args, stream=stream, **kwargs)\n\n def _makeResult(self):\n # stream = self.stream # not you!\n stream = sys.stdout\n stream = _WritelnDecorator(stream)\n return self.resultclass(stream, self.descriptions, self.verbosity)\n\n\ndef cache(foo, typed=False):\n """ Magic cache wrapper\n https://github.com/python/cpython/blob/main/Lib/functools.py\n """\n maxsize = None\n def wrapper(self, *args, **kwargs):\n key = (self.cache_id(), ("@cache", foo.__name__, _make_key(args, kwargs, typed)))\n if not self._cache_contains(key):\n value = foo(self, *args, **kwargs)\n self._cache_put(key, value)\n else:\n value = self._cache_get(key)\n return value\n\n return wrapper\n\n\ndef get_hints(ss):\n if ss == None:\n return None\n try:\n ss = textwrap.dedent(ss)\n ss = ss.replace(\'\'\'"""\'\'\', "").strip()\n hints = ["hints:", ]\n j = np.argmax([ss.lower().find(h) for h in hints])\n h = hints[j]\n ss = ss[ss.find(h) + len(h) + 1:]\n ss = "\\n".join([l for l in ss.split("\\n") if not l.strip().startswith(":")])\n ss = textwrap.dedent(ss)\n ss = ss.strip()\n return ss\n except Exception as e:\n print("bad hints", ss, e)\n\n\nclass UTestCase(unittest.TestCase):\n _outcome = None # A dictionary which stores the user-computed outcomes of all the tests. This differs from the cache.\n _cache = None # Read-only cache. Ensures method always produce same result.\n _cache2 = None # User-written cache.\n _with_coverage = False\n _report = None # The report used. This is very, very hacky and should always be None. Don\'t rely on it!\n\n def capture(self):\n if hasattr(self, \'_stdout\') and self._stdout is not None:\n file = self._stdout\n else:\n file = sys.stdout\n return Capturing2(stdout=file)\n\n @classmethod\n def question_title(cls):\n """ Return the question title """\n return cls.__doc__.strip().splitlines()[0].strip() if cls.__doc__ is not None else cls.__qualname__\n\n @classmethod\n def reset(cls):\n print("Warning, I am not sure UTestCase.reset() is needed anymore and it seems very hacky.")\n cls._outcome = None\n cls._cache = None\n cls._cache2 = None\n\n def _callSetUp(self):\n if self._with_coverage:\n if not hasattr(self._report, \'covcache\'):\n self._report.covcache = {}\n import coverage\n self.cov = coverage.Coverage()\n self.cov.start()\n self.setUp()\n\n def _callTearDown(self):\n self.tearDown()\n if self._with_coverage:\n from pathlib import Path\n from snipper import snipper\n self.cov.stop()\n data = self.cov.get_data()\n base, _, _ = self._report._import_base_relative()\n for file in data.measured_files():\n file = os.path.normpath(file)\n root = Path(base)\n child = Path(file)\n if root in child.parents:\n with open(child, \'r\') as f:\n s = f.read()\n lines = s.splitlines()\n garb = \'GARBAGE\'\n\n lines2 = snipper.censor_code(lines, keep=True)\n assert len(lines) == len(lines2)\n\n for l in data.contexts_by_lineno(file):\n if lines2[l].strip() == garb:\n if self.cache_id() not in self._report.covcache:\n self._report.covcache[self.cache_id()] = {}\n\n rel = os.path.relpath(child, root)\n cc = self._report.covcache[self.cache_id()]\n j = 0\n for j in range(l, -1, -1):\n if "def" in lines2[j] or "class" in lines2[j]:\n break\n from snipper.snipper import gcoms\n fun = lines2[j]\n comments, _ = gcoms("\\n".join(lines2[j:l]))\n if rel not in cc:\n cc[rel] = {}\n cc[rel][fun] = (l, "\\n".join(comments))\n self._cache_put((self.cache_id(), \'coverage\'), self._report.covcache)\n\n def shortDescriptionStandard(self):\n sd = super().shortDescription()\n if sd is None:\n sd = self._testMethodName\n return sd\n\n def shortDescription(self):\n sd = self.shortDescriptionStandard()\n title = self._cache_get((self.cache_id(), \'title\'), sd)\n return title if title is not None else sd\n\n @property\n def title(self):\n return self.shortDescription()\n\n @title.setter\n def title(self, value):\n self._cache_put((self.cache_id(), \'title\'), value)\n\n def _get_outcome(self):\n if not (self.__class__, \'_outcome\') or self.__class__._outcome is None:\n self.__class__._outcome = {}\n return self.__class__._outcome\n\n def _callTestMethod(self, testMethod):\n t = time.time()\n self._ensure_cache_exists() # Make sure cache is there.\n if self._testMethodDoc is not None:\n self._cache_put((self.cache_id(), \'title\'), self.shortDescriptionStandard())\n\n self._cache2[(self.cache_id(), \'assert\')] = {}\n res = testMethod()\n elapsed = time.time() - t\n self._get_outcome()[self.cache_id()] = res\n self._cache_put((self.cache_id(), "time"), elapsed)\n\n def cache_id(self):\n c = self.__class__.__qualname__\n m = self._testMethodName\n return c, m\n\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._load_cache()\n self._assert_cache_index = 0\n\n def _ensure_cache_exists(self):\n if not hasattr(self.__class__, \'_cache\') or self.__class__._cache == None:\n self.__class__._cache = dict()\n if not hasattr(self.__class__, \'_cache2\') or self.__class__._cache2 == None:\n self.__class__._cache2 = dict()\n\n def _cache_get(self, key, default=None):\n self._ensure_cache_exists()\n return self.__class__._cache.get(key, default)\n\n def _cache_put(self, key, value):\n self._ensure_cache_exists()\n self.__class__._cache2[key] = value\n\n def _cache_contains(self, key):\n self._ensure_cache_exists()\n return key in self.__class__._cache\n\n def wrap_assert(self, assert_fun, first, *args, **kwargs):\n # sys.stdout = self._stdout\n key = (self.cache_id(), \'assert\')\n if not self._cache_contains(key):\n print("Warning, framework missing", key)\n self.__class__._cache[\n key] = {} # A new dict. We manually insert it because we have to use that the dict is mutable.\n cache = self._cache_get(key)\n id = self._assert_cache_index\n if not id in cache:\n print("Warning, framework missing cache index", key, "id =", id)\n _expected = cache.get(id, f"Key {id} not found in cache; framework files missing. Please run deploy()")\n\n # The order of these calls is important. If the method assert fails, we should still store the correct result in cache.\n cache[id] = first\n self._cache_put(key, cache)\n self._assert_cache_index += 1\n assert_fun(first, _expected, *args, **kwargs)\n\n def assertEqualC(self, first: Any, msg: Any = ...) -> None:\n self.wrap_assert(self.assertEqual, first, msg)\n\n def _cache_file(self):\n return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"\n\n def _save_cache(self):\n # get the class name (i.e. what to save to).\n cfile = self._cache_file()\n if not os.path.isdir(os.path.dirname(cfile)):\n os.makedirs(os.path.dirname(cfile))\n\n if hasattr(self.__class__, \'_cache2\'):\n with open(cfile, \'wb\') as f:\n pickle.dump(self.__class__._cache2, f)\n\n # But you can also set cache explicitly.\n def _load_cache(self):\n if self._cache is not None: # Cache already loaded. We will not load it twice.\n return\n # raise Exception("Loaded cache which was already set. What is going on?!")\n cfile = self._cache_file()\n if os.path.exists(cfile):\n try:\n with open(cfile, \'rb\') as f:\n data = pickle.load(f)\n self.__class__._cache = data\n except Exception as e:\n print("Bad cache", cfile)\n print(e)\n else:\n print("Warning! data file not found", cfile)\n\n def _feedErrorsToResult(self, result, errors):\n """ Use this to show hints on test failure. """\n if not isinstance(result, UTextResult):\n er = [e for e, v in errors if v != None]\n\n if len(er) > 0:\n hints = []\n key = (self.cache_id(), \'coverage\')\n if self._cache_contains(key):\n CC = self._cache_get(key)\n for id in CC:\n if id == self.cache_id():\n cl, m = id\n gprint(f"> An error occured while solving: {cl}.{m}. The files/methods you need to edit are:") # For the test {id} in {file} you should edit:")\n for file in CC[id]:\n rec = CC[id][file]\n gprint(f"> * {file}")\n for l in rec:\n _, comments = CC[id][file][l]\n hint = get_hints(comments)\n\n if hint != None:\n hints.append(hint)\n gprint(f"> - {l}")\n\n er = er[0]\n doc = er._testMethodDoc\n if doc is not None:\n hint = get_hints(er._testMethodDoc)\n if hint is not None:\n hints = [hint] + hints\n if len(hints) > 0:\n gprint("> Hints:")\n gprint(textwrap.indent("\\n".join(hints), "> "))\n\n super()._feedErrorsToResult(result, errors)\n\n def startTestRun(self):\n # print("asdfsdaf 11", file=sys.stderr)\n super().startTestRun()\n # print("asdfsdaf")\n\n def _callTestMethod(self, method):\n # print("asdfsdaf")\n super()._callTestMethod(method)\n\n\ndef hide(func):\n return func\n\n\ndef makeRegisteringDecorator(foreignDecorator):\n """\n Returns a copy of foreignDecorator, which is identical in every\n way(*), except also appends a .decorator property to the callable it\n spits out.\n """\n\n def newDecorator(func):\n # Call to newDecorator(method)\n # Exactly like old decorator, but output keeps track of what decorated it\n R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done\n R.decorator = newDecorator # keep track of decorator\n # R.original = func # might as well keep track of everything!\n return R\n\n newDecorator.__name__ = foreignDecorator.__name__\n newDecorator.__doc__ = foreignDecorator.__doc__\n return newDecorator\n\nhide = makeRegisteringDecorator(hide)\n\ndef methodsWithDecorator(cls, decorator):\n """\n Returns all methods in CLS with DECORATOR as the\n outermost decorator.\n\n DECORATOR must be a "registering decorator"; one\n can make any decorator "registering" via the\n makeRegisteringDecorator function.\n\n import inspect\n ls = list(methodsWithDecorator(GeneratorQuestion, deco))\n for f in ls:\n print(inspect.getsourcelines(f) ) # How to get all hidden questions.\n """\n for maybeDecorated in cls.__dict__.values():\n if hasattr(maybeDecorated, \'decorator\'):\n if maybeDecorated.decorator == decorator:\n print(maybeDecorated)\n yield maybeDecorated\n# 817\n\n\nimport numpy as np\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport pyfiglet\nimport unittest\nimport inspect\nimport os\nimport argparse\nimport time\n\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Example: \nTo run all tests in a report: \n\n> python assignment1_dp.py\n\nTo run only question 2 or question 2.1\n\n> python assignment1_dp.py -q 2\n> python assignment1_dp.py -q 2.1\n\nNote this scripts does not grade your report. To grade your report, use:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'-q\', nargs=\'?\', type=str, default=None, help=\'Only evaluate this question (e.g.: -q 2)\')\nparser.add_argument(\'--showexpected\', action="store_true", help=\'Show the expected/desired result\')\nparser.add_argument(\'--showcomputed\', action="store_true", help=\'Show the answer your code computes\')\nparser.add_argument(\'--unmute\', action="store_true", help=\'Show result of print(...) commands in code\')\nparser.add_argument(\'--passall\', action="store_true", help=\'Automatically pass all tests. Useful when debugging.\')\n\ndef evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False):\n args = parser.parse_args()\n if question is None and args.q is not None:\n question = args.q\n if "." in question:\n question, qitem = [int(v) for v in question.split(".")]\n else:\n question = int(question)\n\n if hasattr(report, "computed_answer_file") and not os.path.isfile(report.computed_answers_file) and not ignore_missing_file:\n raise Exception("> Error: The pre-computed answer file", os.path.abspath(report.computed_answers_file), "does not exist. Check your package installation")\n\n if unmute is None:\n unmute = args.unmute\n if passall is None:\n passall = args.passall\n\n results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,\n show_tol_err=show_tol_err)\n\n\n if question is None:\n print("Provisional evaluation")\n tabulate(table_data)\n table = table_data\n print(tabulate(table))\n print(" ")\n\n fr = inspect.getouterframes(inspect.currentframe())[1].filename\n gfile = os.path.basename(fr)[:-3] + "_grade.py"\n if os.path.exists(gfile):\n print("Note your results have not yet been registered. \\nTo register your results, please run the file:")\n print(">>>", gfile)\n print("In the same manner as you ran this file.")\n\n\n return results\n\n\ndef upack(q):\n # h = zip([(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()])\n h =[(i[\'w\'], i[\'possible\'], i[\'obtained\']) for i in q.values()]\n h = np.asarray(h)\n return h[:,0], h[:,1], h[:,2],\n\nclass UnitgradeTextRunner(unittest.TextTestRunner):\n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n\nclass SequentialTestLoader(unittest.TestLoader):\n def getTestCaseNames(self, testCaseClass):\n test_names = super().getTestCaseNames(testCaseClass)\n # testcase_methods = list(testCaseClass.__dict__.keys())\n ls = []\n for C in testCaseClass.mro():\n if issubclass(C, unittest.TestCase):\n ls = list(C.__dict__.keys()) + ls\n testcase_methods = ls\n test_names.sort(key=testcase_methods.index)\n return test_names\n\ndef evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False,\n show_progress_bar=True,\n show_tol_err=False,\n big_header=True):\n\n now = datetime.now()\n if big_header:\n ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom")\n b = "\\n".join( [l for l in ascii_banner.splitlines() if len(l.strip()) > 0] )\n else:\n b = "Unitgrade"\n dt_string = now.strftime("%d/%m/%Y %H:%M:%S")\n print(b + " v" + __version__ + ", started: " + dt_string+ "\\n")\n # print("Started: " + dt_string)\n s = report.title\n if hasattr(report, "version") and report.version is not None:\n s += " version " + report.version\n print(s, "(use --help for options)" if show_help_flag else "")\n # print(f"Loaded answers from: ", report.computed_answers_file, "\\n")\n table_data = []\n t_start = time.time()\n score = {}\n loader = SequentialTestLoader()\n\n for n, (q, w) in enumerate(report.questions):\n if question is not None and n+1 != question:\n continue\n suite = loader.loadTestsFromTestCase(q)\n qtitle = q.question_title() if hasattr(q, \'question_title\') else q.__qualname__\n q_title_print = "Question %i: %s"%(n+1, qtitle)\n print(q_title_print, end="")\n q.possible = 0\n q.obtained = 0\n q_ = {} # Gather score in this class.\n UTextResult.q_title_print = q_title_print # Hacky\n UTextResult.show_progress_bar = show_progress_bar # Hacky.\n UTextResult.number = n\n UTextResult.nL = report.nL\n\n res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite)\n\n possible = res.testsRun\n obtained = len(res.successes)\n\n assert len(res.successes) + len(res.errors) + len(res.failures) == res.testsRun\n\n obtained = int(w * obtained * 1.0 / possible ) if possible > 0 else 0\n score[n] = {\'w\': w, \'possible\': w, \'obtained\': obtained, \'items\': q_, \'title\': qtitle}\n q.obtained = obtained\n q.possible = possible\n\n s1 = f"Question {n+1} total"\n s2 = f" {q.obtained}/{w}"\n print(s1 + ("."* (report.nL-len(s1)-len(s2) )) + s2 )\n print(" ")\n table_data.append([f"q{n+1}) Total", f"{q.obtained}/{w}"])\n\n ws, possible, obtained = upack(score)\n possible = int( msum(possible) )\n obtained = int( msum(obtained) ) # Cast to python int\n report.possible = possible\n report.obtained = obtained\n now = datetime.now()\n dt_string = now.strftime("%H:%M:%S")\n\n dt = int(time.time()-t_start)\n minutes = dt//60\n seconds = dt - minutes*60\n plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "")\n\n dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")",\n last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL)\n\n # print(f"Completed at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +"). Total")\n\n table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ])\n results = {\'total\': (obtained, possible), \'details\': score}\n return results, table_data\n\n\nfrom tabulate import tabulate\nfrom datetime import datetime\nimport inspect\nimport json\nimport os\nimport bz2\nimport pickle\nimport os\n\ndef bzwrite(json_str, token): # to get around obfuscation issues\n with getattr(bz2, \'open\')(token, "wt") as f:\n f.write(json_str)\n\ndef gather_imports(imp):\n resources = {}\n m = imp\n # for m in pack_imports:\n # print(f"*** {m.__name__}")\n f = m.__file__\n # dn = os.path.dirname(f)\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = str(__import__(m.__name__.split(\'.\')[0]).__path__)\n\n if hasattr(m, \'__file__\') and not hasattr(m, \'__path__\'): # Importing a simple file: m.__class__.__name__ == \'module\' and False:\n top_package = os.path.dirname(m.__file__)\n module_import = True\n else:\n top_package = __import__(m.__name__.split(\'.\')[0]).__path__._path[0]\n module_import = False\n\n # top_package = os.path.dirname(__import__(m.__name__.split(\'.\')[0]).__file__)\n # top_package = os.path.dirname(top_package)\n import zipfile\n # import strea\n # zipfile.ZipFile\n import io\n # file_like_object = io.BytesIO(my_zip_data)\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \'w\') as zip:\n # zip.write()\n for root, dirs, files in os.walk(top_package):\n for file in files:\n if file.endswith(".py"):\n fpath = os.path.join(root, file)\n v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package)\n zip.write(fpath, v)\n\n resources[\'zipfile\'] = zip_buffer.getvalue()\n resources[\'top_package\'] = top_package\n resources[\'module_import\'] = module_import\n return resources, top_package\n\n if f.endswith("__init__.py"):\n for root, dirs, files in os.walk(os.path.dirname(f)):\n for file in files:\n if file.endswith(".py"):\n # print(file)\n # print()\n v = os.path.relpath(os.path.join(root, file), top_package)\n with open(os.path.join(root, file), \'r\') as ff:\n resources[v] = ff.read()\n else:\n v = os.path.relpath(f, top_package)\n with open(f, \'r\') as ff:\n resources[v] = ff.read()\n return resources\n\nimport argparse\nparser = argparse.ArgumentParser(description=\'Evaluate your report.\', epilog="""Use this script to get the score of your report. Example:\n\n> python report1_grade.py\n\nFinally, note that if your report is part of a module (package), and the report script requires part of that package, the -m option for python may be useful.\nFor instance, if the report file is in Documents/course_package/report3_complete.py, and `course_package` is a python package, then change directory to \'Documents/` and run:\n\n> python -m course_package.report1\n\nsee https://docs.python.org/3.9/using/cmdline.html\n""", formatter_class=argparse.RawTextHelpFormatter)\nparser.add_argument(\'--noprogress\', action="store_true", help=\'Disable progress bars\')\nparser.add_argument(\'--autolab\', action="store_true", help=\'Show Autolab results\')\n\ndef gather_upload_to_campusnet(report, output_dir=None):\n n = report.nL\n args = parser.parse_args()\n results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True,\n show_progress_bar=not args.noprogress,\n big_header=not args.autolab)\n # print(" ")\n # print("="*n)\n # print("Final evaluation")\n # print(tabulate(table_data))\n # also load the source code of missing files...\n\n sources = {}\n print("")\n if not args.autolab:\n if len(report.individual_imports) > 0:\n print("By uploading the .token file, you verify the files:")\n for m in report.individual_imports:\n print(">", m.__file__)\n print("Are created/modified individually by you in agreement with DTUs exam rules")\n report.pack_imports += report.individual_imports\n\n if len(report.pack_imports) > 0:\n print("Including files in upload...")\n for k, m in enumerate(report.pack_imports):\n nimp, top_package = gather_imports(m)\n _, report_relative_location, module_import = report._import_base_relative()\n\n # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package)\n nimp[\'report_relative_location\'] = report_relative_location\n nimp[\'report_module_specification\'] = module_import\n nimp[\'name\'] = m.__name__\n sources[k] = nimp\n # if len([k for k in nimp if k not in sources]) > 0:\n print(f" * {m.__name__}")\n # sources = {**sources, **nimp}\n results[\'sources\'] = sources\n\n if output_dir is None:\n output_dir = os.getcwd()\n\n payload_out_base = report.__class__.__name__ + "_handin"\n\n obtain, possible = results[\'total\']\n vstring = "_v"+report.version if report.version is not None else ""\n\n token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring)\n token = os.path.join(output_dir, token)\n with open(token, \'wb\') as f:\n pickle.dump(results, f)\n\n if not args.autolab:\n print(" ")\n print("To get credit for your results, please upload the single unmodified file: ")\n print(">", token)\n # print("To campusnet without any modifications.")\n\n # print("Now time for some autolab fun")\n\ndef source_instantiate(name, report1_source, payload):\n eval("exec")(report1_source, globals())\n pl = pickle.loads(bytes.fromhex(payload))\n report = eval(name)(payload=pl, strict=True)\n # report.set_payload(pl)\n return report\n\n\n__version__ = "0.9.0"\n\nfrom cs101.homework1 import reverse_list, add\nimport unittest\n\nclass Week1(unittest.TestCase):\n def test_add(self):\n self.assertEqual(add(2,2), 4)\n self.assertEqual(add(-100, 5), -95)\n\n def test_reverse(self):\n self.assertEqual(reverse_list([1,2,3]), [3,2,1])\n\n\nimport cs101\nclass Report1(Report):\n title = "CS 101 Report 1"\n questions = [(Week1, 10)] # Include a single question for 10 credits.\n pack_imports = [cs101]' report1_payload = '8004953f000000000000007d948c055765656b31947d948c2c6e6f20636163686520736565205f73657475705f616e737765727320696e20756e69746772616465322e7079948873732e' name="Report1" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b5a3c46 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel" +] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/pytransform/__init__.py b/pytransform/__init__.py deleted file mode 100644 index f656a22..0000000 --- a/pytransform/__init__.py +++ /dev/null @@ -1,454 +0,0 @@ -# These module alos are used by protection code, so that protection -# code needn't import anything -import os -import platform -import sys -import struct - -# Because ctypes is new from Python 2.5, so pytransform doesn't work -# before Python 2.5 -# -from ctypes import cdll, c_char, c_char_p, c_int, c_void_p, \ - pythonapi, py_object, PYFUNCTYPE, CFUNCTYPE -from fnmatch import fnmatch - -# -# Support Platforms -# -plat_path = 'platforms' - -plat_table = ( - ('windows', ('windows', 'cygwin-*')), - ('darwin', ('darwin', 'ios')), - ('linux', ('linux*',)), - ('freebsd', ('freebsd*', 'openbsd*')), - ('poky', ('poky',)), -) - -arch_table = ( - ('x86', ('i?86', )), - ('x86_64', ('x64', 'x86_64', 'amd64', 'intel')), - ('arm', ('armv5',)), - ('armv6', ('armv6l',)), - ('armv7', ('armv7l',)), - ('ppc64', ('ppc64le',)), - ('mips32', ('mips',)), - ('aarch32', ('aarch32',)), - ('aarch64', ('aarch64', 'arm64')) -) - -# -# Hardware type -# -HT_HARDDISK, HT_IFMAC, HT_IPV4, HT_IPV6, HT_DOMAIN = range(5) - -# -# Global -# -_pytransform = None - - -class PytransformError(Exception): - pass - - -def dllmethod(func): - def wrap(*args, **kwargs): - return func(*args, **kwargs) - return wrap - - -@dllmethod -def version_info(): - prototype = PYFUNCTYPE(py_object) - dlfunc = prototype(('version_info', _pytransform)) - return dlfunc() - - -@dllmethod -def init_pytransform(): - major, minor = sys.version_info[0:2] - # Python2.5 no sys.maxsize but sys.maxint - # bitness = 64 if sys.maxsize > 2**32 else 32 - prototype = PYFUNCTYPE(c_int, c_int, c_int, c_void_p) - init_module = prototype(('init_module', _pytransform)) - ret = init_module(major, minor, pythonapi._handle) - if (ret & 0xF000) == 0x1000: - raise PytransformError('Initialize python wrapper failed (%d)' - % (ret & 0xFFF)) - return ret - - -@dllmethod -def init_runtime(): - prototype = PYFUNCTYPE(c_int, c_int, c_int, c_int, c_int) - _init_runtime = prototype(('init_runtime', _pytransform)) - return _init_runtime(0, 0, 0, 0) - - -@dllmethod -def encrypt_code_object(pubkey, co, flags, suffix=''): - _pytransform.set_option(6, suffix.encode()) - prototype = PYFUNCTYPE(py_object, py_object, py_object, c_int) - dlfunc = prototype(('encrypt_code_object', _pytransform)) - return dlfunc(pubkey, co, flags) - - -@dllmethod -def generate_license_file(filename, priname, rcode, start=-1, count=1): - prototype = PYFUNCTYPE(c_int, c_char_p, c_char_p, c_char_p, c_int, c_int) - dlfunc = prototype(('generate_project_license_files', _pytransform)) - return dlfunc(filename.encode(), priname.encode(), rcode.encode(), - start, count) if sys.version_info[0] == 3 \ - else dlfunc(filename, priname, rcode, start, count) - - -@dllmethod -def generate_license_key(prikey, keysize, rcode): - prototype = PYFUNCTYPE(py_object, c_char_p, c_int, c_char_p) - dlfunc = prototype(('generate_license_key', _pytransform)) - return dlfunc(prikey, keysize, rcode) if sys.version_info[0] == 2 \ - else dlfunc(prikey, keysize, rcode.encode()) - - -@dllmethod -def get_registration_code(): - prototype = PYFUNCTYPE(py_object) - dlfunc = prototype(('get_registration_code', _pytransform)) - return dlfunc() - - -@dllmethod -def get_expired_days(): - prototype = PYFUNCTYPE(py_object) - dlfunc = prototype(('get_expired_days', _pytransform)) - return dlfunc() - - -@dllmethod -def clean_obj(obj, kind): - prototype = PYFUNCTYPE(c_int, py_object, c_int) - dlfunc = prototype(('clean_obj', _pytransform)) - return dlfunc(obj, kind) - - -def clean_str(*args): - tdict = { - 'str': 0, - 'bytearray': 1, - 'unicode': 2 - } - for obj in args: - k = tdict.get(type(obj).__name__) - if k is None: - raise RuntimeError('Can not clean object: %s' % obj) - clean_obj(obj, k) - - -def get_hd_info(hdtype, name=None): - if hdtype not in range(HT_DOMAIN + 1): - raise RuntimeError('Invalid parameter hdtype: %s' % hdtype) - size = 256 - t_buf = c_char * size - buf = t_buf() - cname = c_char_p(0 if name is None - else name.encode('utf-8') if hasattr('name', 'encode') - else name) - if (_pytransform.get_hd_info(hdtype, buf, size, cname) == -1): - raise PytransformError('Get hardware information failed') - return buf.value.decode() - - -def show_hd_info(): - return _pytransform.show_hd_info() - - -def assert_armored(*names): - prototype = PYFUNCTYPE(py_object, py_object) - dlfunc = prototype(('assert_armored', _pytransform)) - - def wrapper(func): - def wrap_execute(*args, **kwargs): - dlfunc(names) - return func(*args, **kwargs) - return wrap_execute - return wrapper - - -def get_license_info(): - info = { - 'ISSUER': None, - 'EXPIRED': None, - 'HARDDISK': None, - 'IFMAC': None, - 'IFIPV4': None, - 'DOMAIN': None, - 'DATA': None, - 'CODE': None, - } - rcode = get_registration_code().decode() - if rcode.startswith('*VERSION:'): - index = rcode.find('\n') - info['ISSUER'] = rcode[9:index].split('.')[0].replace('-sn-1.txt', '') - rcode = rcode[index+1:] - - index = 0 - if rcode.startswith('*TIME:'): - from time import ctime - index = rcode.find('\n') - info['EXPIRED'] = ctime(float(rcode[6:index])) - index += 1 - - if rcode[index:].startswith('*FLAGS:'): - index += len('*FLAGS:') + 1 - info['FLAGS'] = ord(rcode[index - 1]) - - prev = None - start = index - for k in ['HARDDISK', 'IFMAC', 'IFIPV4', 'DOMAIN', 'FIXKEY', 'CODE']: - index = rcode.find('*%s:' % k) - if index > -1: - if prev is not None: - info[prev] = rcode[start:index] - prev = k - start = index + len(k) + 2 - info['CODE'] = rcode[start:] - i = info['CODE'].find(';') - if i > 0: - info['DATA'] = info['CODE'][i+1:] - info['CODE'] = info['CODE'][:i] - return info - - -def get_license_code(): - return get_license_info()['CODE'] - - -def get_user_data(): - return get_license_info()['DATA'] - - -def _match_features(patterns, s): - for pat in patterns: - if fnmatch(s, pat): - return True - - -def _gnu_get_libc_version(): - try: - prototype = CFUNCTYPE(c_char_p) - ver = prototype(('gnu_get_libc_version', cdll.LoadLibrary('')))() - return ver.decode().split('.') - except Exception: - pass - - -def format_platform(platid=None): - if platid: - return os.path.normpath(platid) - - plat = platform.system().lower() - mach = platform.machine().lower() - - for alias, platlist in plat_table: - if _match_features(platlist, plat): - plat = alias - break - - if plat == 'linux': - cname, cver = platform.libc_ver() - if cname == 'musl': - plat = 'musl' - elif cname == 'libc': - plat = 'android' - elif cname == 'glibc': - v = _gnu_get_libc_version() - if v and len(v) >= 2 and (int(v[0]) * 100 + int(v[1])) < 214: - plat = 'centos6' - - for alias, archlist in arch_table: - if _match_features(archlist, mach): - mach = alias - break - - if plat == 'windows' and mach == 'x86_64': - bitness = struct.calcsize('P'.encode()) * 8 - if bitness == 32: - mach = 'x86' - - return os.path.join(plat, mach) - - -# Load _pytransform library -def _load_library(path=None, is_runtime=0, platid=None, suffix='', advanced=0): - path = os.path.dirname(__file__) if path is None \ - else os.path.normpath(path) - - plat = platform.system().lower() - name = '_pytransform' + suffix - if plat == 'linux': - filename = os.path.abspath(os.path.join(path, name + '.so')) - elif plat == 'darwin': - filename = os.path.join(path, name + '.dylib') - elif plat == 'windows': - filename = os.path.join(path, name + '.dll') - elif plat == 'freebsd': - filename = os.path.join(path, name + '.so') - else: - raise PytransformError('Platform %s not supported' % plat) - - if platid is not None and os.path.isfile(platid): - filename = platid - elif platid is not None or not os.path.exists(filename) or not is_runtime: - libpath = platid if platid is not None and os.path.isabs(platid) else \ - os.path.join(path, plat_path, format_platform(platid)) - filename = os.path.join(libpath, os.path.basename(filename)) - - if not os.path.exists(filename): - raise PytransformError('Could not find "%s"' % filename) - - try: - m = cdll.LoadLibrary(filename) - except Exception as e: - if sys.flags.debug: - print('Load %s failed:\n%s' % (filename, e)) - raise - - # Removed from v4.6.1 - # if plat == 'linux': - # m.set_option(-1, find_library('c').encode()) - - if not os.path.abspath('.') == os.path.abspath(path): - m.set_option(1, path.encode() if sys.version_info[0] == 3 else path) - - # Required from Python3.6 - m.set_option(2, sys.byteorder.encode()) - - if sys.flags.debug: - m.set_option(3, c_char_p(1)) - m.set_option(4, c_char_p(not is_runtime)) - - # Disable advanced mode by default - m.set_option(5, c_char_p(not advanced)) - - # Set suffix for private package - if suffix: - m.set_option(6, suffix.encode()) - - return m - - -def pyarmor_init(path=None, is_runtime=0, platid=None, suffix='', advanced=0): - global _pytransform - _pytransform = _load_library(path, is_runtime, platid, suffix, advanced) - return init_pytransform() - - -def pyarmor_runtime(path=None, suffix='', advanced=0): - if _pytransform is not None: - return - - try: - pyarmor_init(path, is_runtime=1, suffix=suffix, advanced=advanced) - init_runtime() - except Exception as e: - if sys.flags.debug or hasattr(sys, '_catch_pyarmor'): - raise - sys.stderr.write("%s\n" % str(e)) - sys.exit(1) - - -# ---------------------------------------------------------- -# End of pytransform -# ---------------------------------------------------------- - -# -# Not available from v5.6 -# - - -def generate_capsule(licfile): - prikey, pubkey, prolic = _generate_project_capsule() - capkey, newkey = _generate_pytransform_key(licfile, pubkey) - return prikey, pubkey, capkey, newkey, prolic - - -@dllmethod -def _generate_project_capsule(): - prototype = PYFUNCTYPE(py_object) - dlfunc = prototype(('generate_project_capsule', _pytransform)) - return dlfunc() - - -@dllmethod -def _generate_pytransform_key(licfile, pubkey): - prototype = PYFUNCTYPE(py_object, c_char_p, py_object) - dlfunc = prototype(('generate_pytransform_key', _pytransform)) - return dlfunc(licfile.encode() if sys.version_info[0] == 3 else licfile, - pubkey) - - -# -# Deprecated functions from v5.1 -# -@dllmethod -def encrypt_project_files(proname, filelist, mode=0): - prototype = PYFUNCTYPE(c_int, c_char_p, py_object, c_int) - dlfunc = prototype(('encrypt_project_files', _pytransform)) - return dlfunc(proname.encode(), filelist, mode) - - -def generate_project_capsule(licfile): - prikey, pubkey, prolic = _generate_project_capsule() - capkey = _encode_capsule_key_file(licfile) - return prikey, pubkey, capkey, prolic - - -@dllmethod -def _encode_capsule_key_file(licfile): - prototype = PYFUNCTYPE(py_object, c_char_p, c_char_p) - dlfunc = prototype(('encode_capsule_key_file', _pytransform)) - return dlfunc(licfile.encode(), None) - - -@dllmethod -def encrypt_files(key, filelist, mode=0): - t_key = c_char * 32 - prototype = PYFUNCTYPE(c_int, t_key, py_object, c_int) - dlfunc = prototype(('encrypt_files', _pytransform)) - return dlfunc(t_key(*key), filelist, mode) - - -@dllmethod -def generate_module_key(pubname, key): - t_key = c_char * 32 - prototype = PYFUNCTYPE(py_object, c_char_p, t_key, c_char_p) - dlfunc = prototype(('generate_module_key', _pytransform)) - return dlfunc(pubname.encode(), t_key(*key), None) - -# -# Compatible for PyArmor v3.0 -# -@dllmethod -def old_init_runtime(systrace=0, sysprofile=1, threadtrace=0, threadprofile=1): - '''Only for old version, before PyArmor 3''' - pyarmor_init(is_runtime=1) - prototype = PYFUNCTYPE(c_int, c_int, c_int, c_int, c_int) - _init_runtime = prototype(('init_runtime', _pytransform)) - return _init_runtime(systrace, sysprofile, threadtrace, threadprofile) - - -@dllmethod -def import_module(modname, filename): - '''Only for old version, before PyArmor 3''' - prototype = PYFUNCTYPE(py_object, c_char_p, c_char_p) - _import_module = prototype(('import_module', _pytransform)) - return _import_module(modname.encode(), filename.encode()) - - -@dllmethod -def exec_file(filename): - '''Only for old version, before PyArmor 3''' - prototype = PYFUNCTYPE(c_int, c_char_p) - _exec_file = prototype(('exec_file', _pytransform)) - return _exec_file(filename.encode()) diff --git a/pytransform/__pycache__/__init__.cpython-36.pyc b/pytransform/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 75bb2539be65062814dda6c5080880056d0f8c70..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11679 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 diff --git a/pytransform/__pycache__/__init__.cpython-38.pyc b/pytransform/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 9aaff7fb561fefe38907458054c4c1944bed1091..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11349 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 diff --git a/pytransform/_pytransform.dll b/pytransform/_pytransform.dll deleted file mode 100644 index b1af3263115ebab5c213656cf1daf87b3510116b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1368590 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	*@IZDHT6nN{p@he$Be3w0 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�~&T#RUi0`r2 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%0AcwU9 zG>p}eXk<3XA0R%=k02T*MlTv<Ck7@mti_>WO%ECr)&kJ52GO9f2Duv)P9PeDL1~0R zye{K7v@BN;ugfskh2V1Cx{L-WUgKGpvB<M7V}@s4MxSS0MvG@%MwMq>MuBHtMw(|` zMvP}&Mv!M+hKFZehMi|!hKXlghLðJt5ZhL~qv29IZ529sx9#t)CWjCUS&8BaXw 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 D&;+O~ diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..f19f26e --- /dev/null +++ b/setup.py @@ -0,0 +1,49 @@ +# Use this guide: +# https://packaging.python.org/tutorials/packaging-projects/ + +# from unitgrade2.version import __version__ +import setuptools +with open("src/unitgrade_private2/version.py", "r", encoding="utf-8") as fh: + __version__ = fh.read().strip().split(" = ")[1].strip()[1:-1] +# long_description = fh.read() + +with open("README.md", "r", encoding="utf-8") as fh: + long_description = fh.read() + +setuptools.setup( + name="unitgrade-devel", + version=__version__, + author="Tue Herlau", + author_email="tuhe@dtu.dk", + description="A set of tools to develop unitgrade reports and evaluate them", + long_description=long_description, + long_description_content_type="text/markdown", + license="MIT", + url='https://lab.compute.dtu.dk/tuhe/unitgrade_private', + project_urls={ + "Bug Tracker": "https://lab.compute.dtu.dk/tuhe/unitgrade_private/issues", + }, + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], + package_dir={"": "src"}, + packages=setuptools.find_packages(where="src"), + python_requires=">=3.8", + install_requires=['numpy', "unitgrade", "codesnipper", 'tabulate', 'tqdm', "pyfiglet", "colorama", "coverage", "compress_pickle"], +) + +# setup( +# name='unitgrade', +# version=__version__, +# packages=['unitgrade2'], +# url=, +# license='MIT', +# author='Tue Herlau', +# author_email='tuhe@dtu.dk', +# description=""" +# A student homework/exam evaluation framework build on pythons unittest framework. This package contains all files required to run unitgrade tests as a student. To develop tests, please use unitgrade_private. +# """, +# include_package_data=False, +# ) diff --git a/src/unitgrade_devel.egg-info/PKG-INFO b/src/unitgrade_devel.egg-info/PKG-INFO new file mode 100644 index 0000000..ab8dded --- /dev/null +++ b/src/unitgrade_devel.egg-info/PKG-INFO @@ -0,0 +1,317 @@ +Metadata-Version: 2.1 +Name: unitgrade-devel +Version: 0.0.1 +Summary: A set of tools to develop unitgrade reports and evaluate them +Home-page: https://lab.compute.dtu.dk/tuhe/unitgrade_private +Author: Tue Herlau +Author-email: tuhe@dtu.dk +License: MIT +Project-URL: Bug Tracker, https://lab.compute.dtu.dk/tuhe/unitgrade_private/issues +Platform: UNKNOWN +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Requires-Python: >=3.8 +Description-Content-Type: text/markdown +License-File: LICENSE + +# Unitgrade-private +Unitgrade is an automatic report and exam evaluation framework that enables instructors to offer automatically evaluated programming assignments. + Unitgrade is build on pythons `unittest` framework so that the tests can be specified in a familiar syntax and will integrate with any modern IDE. What it offers beyond `unittest` is the ability to collect tests in reports (for automatic evaluation) and an easy and 100% safe mechanism for verifying the students results and creating additional, hidden tests. A powerful cache system allows instructors to automatically create test-answers based on a working solution. + + - 100% Python `unittest` compatible + - No external configuration files: Just write a `unittest` + - No unnatural limitations: Use any package or framework. If you can `unittest` it, it works. + - Granular security model: + - Students get public `unittests` for easy development of solutions + - Students get a tamper-resistant file to create submissions which are uploaded + - Instructors can automatically verify the students solution using a Docker VM and run hidden tests + - Tests are quick to run and will integrate with your IDE + +**Note: This is the development version of unitgrade. If you are a student, please see http://gitlab.compute.dtu.dk/tuhe/unitgrade.** + +# Using unitgrade +The examples can be found in the `/examples/` directory: https://gitlab.compute.dtu.dk/tuhe/unitgrade_private/examples + +## A simple example +Unitgrade makes the following assumptions: + - Your code is in python + - Whatever you want to do can be specified as a `unittest` + +Although not required, it is recommended you maintain two version of the code: + - A fully-working version (i.e. all tests pass) + - A public version distributed to students (some code removed)) + +In this example, I will use `snipper` (see http://gitlab.compute.dtu.dk/tuhe/snipper) to synchronize the two versions automatically. +Let's look at an example. You need three files +``` +instructor/cs101/homework.py # This contains the students homework +instructor/cs101/report1.py # This contains the tests +instructor/cs101/deploy.py # A private file to deploy the tests +``` + +### The homework +The homework is just any old python code you would give to the students. For instance: +```python +def reverse_list(mylist): #!f + """ + Given a list 'mylist' returns a list consisting of the same elements in reverse order. E.g. + reverse_list([1,2,3]) should return [3,2,1] (as a list). + """ + return list(reversed(mylist)) + +def add(a,b): #!f + """ Given two numbers `a` and `b` this function should simply return their sum: + > add(a,b) = a+b """ + return a+b + +if __name__ == "__main__": + # Problem 1: Write a function which add two numbers + print(f"Your result of 2 + 2 = {add(2,2)}") + print(f"Reversing a small list", reverse_list([2,3,5,7])) +``` +### The test: +The test consists of individual problems and a report-class. The tests themselves are just regular Unittest (we will see a slightly smarter idea in a moment). For instance: + +```python +from looping import reverse_list, add +import unittest + + +class Week1(unittest.TestCase): + def test_add(self): + self.assertEqual(add(2, 2), 4) + self.assertEqual(add(-100, 5), -95) + + def test_reverse(self): + self.assertEqual(reverse_list([1, 2, 3]), [3, 2, 1]) + +``` +A number of tests can be collected into a `Report`, which will allow us to assign points to the tests and use the more advanced features of the framework later. A complete, minimal example: + +```python +from src.unitgrade2.unitgrade2 import Report +from src.unitgrade2 import evaluate_report_student +from looping import reverse_list, add +import unittest + + +class Week1(unittest.TestCase): + def test_add(self): + self.assertEqual(add(2, 2), 4) + self.assertEqual(add(-100, 5), -95) + + def test_reverse(self): + self.assertEqual(reverse_list([1, 2, 3]), [3, 2, 1]) + + +import cs101 + + +class Report1(Report): + title = "CS 101 Report 1" + questions = [(Week1, 10)] # Include a single question for 10 credits. + pack_imports = [cs101] + + +if __name__ == "__main__": + # Uncomment to simply run everything as a unittest: + # unittest.main(verbosity=2) + evaluate_report_student(Report1()) +``` + +### Deployment +The above is all you need if you simply want to use the framework as a self-check: Students can run the code and see how well they did. +In order to begin using the framework for evaluation we need to create a bit more structure. We do that by deploying the report class as follows: +```python +from report1 import Report1 +from unitgrade_private2.hidden_create_files import setup_grade_file_report +from snipper import snip_dir +import shutil + +if __name__ == "__main__": + setup_grade_file_report(Report1, minify=False, obfuscate=False, execute=False) + + # Deploy the files using snipper: https://gitlab.compute.dtu.dk/tuhe/snipper + snip_dir.snip_dir(source_dir="../programs", dest_dir="../../students/programs", clean_destination_dir=True, exclude=['__pycache__', '*.token', 'deploy.py']) + +``` + - The first line creates the `report1_grade.py` script and any additional data files needed by the tests (none in this case) + - The second line set up the students directory (remember, we have included the solutions!) and remove the students solutions. You can check the results in the students folder. + +### Using the framework as a student +You can now upload the `student' directory to the students. The students can run their tests either by running `cs101.report1` in their IDE or by typing: +``` +python -m cs101.report1 +``` +in the command line. This produces a detailed output of the test and the program is 100% compatible with a debugger. When the students are happy with their output they can run (using command line or IDE): +``` +python -m cs101.report1_grade +``` +This runs an identical set of tests, but produces a `.token` file the students can upload to get credit. + - The reason to have a seperate `report1_grade.py` script is to avoid accidential removal of tests. + - The `report1_grade.py` includes all tests and the main parts of the framework and is obfuscated by default. You can apply a much strong level of protection by using e.g. `pyarmor`. + - The `report1_token.token` file includes the outcome of the tests, the time taken, and all python source code in the package. In other words, the file can be used for manual grading, for plagirism detection and for detecting tampering. + - You can easily use the framework to include output of functions. + - See below for how to validate the students results + +### How safe is this? +Cheating within the framework is probably best done by manually editing the `.token`-file or by creating a broken set of tests. This involves risk of being trivially detected, for instance because tests have the wrong runtime, but more importantly +the framework automatically pack all the used source code and so if a student is cheating, there is no way to hide it for an instructor who looks at the results. If the +program is used in conjunction with automatic plagiarism software, cheating therefore involves both breaking the framework, and creating 'false' solutions which statistically match other students solutions, and then hope nobody bothers to check the output. + The bottom line is that I think plain old plagiarism is a much more significant risk, and one the framework reduces relative to other project work +by demanding the source code is included. + +If this is not enough you have two options: You can either use `pyarmor` to create a **very** difficult challenge for a prospective hacker, or you can simply validate the students results as shown below. + + +## Example 2: The framework +One of the main advantages of `unitgrade` over web-based autograders it that tests are really easy to develop and maintain. To take advantage of this, we simply change the class the questions inherit from to `UTestCase` (this is still a `unittest.TestCase`) and we can make use of the chache system. As an example: + +```python +class Week1(UTestCase): + """ The first question for week 1. """ + def test_add(self): + from cs102.homework1 import add + self.assertEqualC(add(2,2)) + self.assertEqualC(add(-100, 5)) + + def test_reverse(self): + from cs102.homework1 import reverse_list + """ Reverse a list """ # Add a title to the test. + self.assertEqualC(reverse_list([1,2,3])) +``` +Note we have changed the test-function to `self.assertEqualC` (the `C` is for cache) and dropped the expected result. What `unitgrade` will do +is to evaluate the test *on the working version of the code*, compute the results of the test, and allow them to be available to the user. All this happens in the `deploy.py` script from before. + +There are other ways to send the output to the user. For instance: +```python +class Question2(UTestCase): + """ Second problem """ + @cache + def my_reversal(self, ls): + # The '@cache' decorator ensures the function is not run on the *students* computer + # Instead the code is run on the teachers computer and the result is passed on with the + # other pre-computed results -- i.e. this function will run regardless of how the student happens to have + # implemented reverse_list. + from cs102.homework1 import reverse_list + return reverse_list(ls) + + def test_reverse_tricky(self): + ls = ("butterfly", 4, 1) + ls2 = self.my_reversal( tuple(ls) ) # This will always produce the right result. + ls3 = self.my_reversal( tuple([1,2,3]) ) # Also works; the cache respects input arguments. + self.assertEqualC(self.my_reversal( tuple(ls2) )) # This will actually test the students code. + return ls +``` +This code showcase the `@cache` decorator. What it does is it computes the output of the function on your computer and allows that +result to be availble to students (the input arguments must be immutable). This may seem odd, but it is very helpful + - if you have exercises that depend on each other, and you want students to have access to the expected result of older methods which they may not have implemented correctly. + - If you want to use functions the students write to set up appropriate tests without giving away the solution + +Furthermore, one of the test now has a return value, which will be automatically included in the `.token` file. + +## Example 3: Hidden and secure tests +To use `unitgrade` as a true autograder you both want security nobody tampered with your tests (or the `.token` files), and +also that the students implementations didn't just detect what input was being used and +return the correct answer. To do that you need hidden tests and external validation. + +Our new testclass looks like this: + +```python +from src.unitgrade2.unitgrade2 import UTestCase, Report, hide +from src.unitgrade2 import evaluate_report_student + + +class Week1(UTestCase): + """ The first question for week 1. """ + + def test_add(self): + from cs103.homework1 import add + self.assertEqualC(add(2, 2)) + self.assertEqualC(add(-100, 5)) + + @hide + def test_add_hidden(self): + # This is a hidden test. The @hide-decorator will allow unitgrade to remove the test. + # See the output in the student directory for more information. + from cs103.homework1 import add + self.assertEqualC(add(2, 2)) + + +import cs103 + + +class Report3(Report): + title = "CS 101 Report 3" + questions = [(Week1, 20)] # Include a single question for 10 credits. + pack_imports = [cs103] + + +if __name__ == "__main__": + evaluate_report_student(Report3()) +``` + +This test is stored as `report3_complete.py`. Note the `@hide` decorator which will tell the framework that test (and all code) should be hidden from the user. + +In order to use the hidden tests, we first need a version for the students without them. This can be done by changing the `deploy.py` script as follows: + +```python +def deploy_student_files(): + setup_grade_file_report(Report3, minify=False, obfuscate=False, execute=False) + Report3.reset() + + fout, ReportWithoutHidden = remove_hidden_methods(Report3, outfile="report3.py") + setup_grade_file_report(ReportWithoutHidden, minify=False, obfuscate=False, execute=False) + sdir = "../../students/cs103" + snip_dir(source_dir="../cs103", dest_dir=sdir, clean_destination_dir=True, exclude=['__pycache__', '*.token', 'deploy.py', 'report3_complete*.py']) + return sdir + + +if __name__ == "__main__": + # Step 1: Deploy the students files and return the directory they were written to + student_directory = deploy_student_files() +``` +This script first compiles the `report3_complete_grade.py`-script (which we will use) and then +remove the hidden methods and compiles the students script `report3_grade.py`-script. Finally, we synchronize with the s +student folder, which now contains no traces of our hidden method -- not in any of the sources files or the data files. + +The next step is optional, but we quickly simulate that the student runs his script and we get a link to the `.token` file: +```python +os.system("cd ../../students && python -m cs103.report3_grade") +student_token_file = glob.glob(student_directory + "/*.token")[0] +``` +This is the file we assume the student uploads. The external validation can be carried out as follows: + +```python +def run_student_code_on_docker(Dockerfile, student_token_file): + token = docker_run_token_file(Dockerfile_location=Dockerfile, + host_tmp_dir=os.path.dirname(Dockerfile) + "/tmp", + student_token_file=student_token_file, + instructor_grade_script="report3_complete_grade.py") + with open(token, 'rb') as f: + results = pickle.load(f) + return results + +if __name__ == "__main__": + # Step 3: Compile the Docker image (obviously you will only do this once; add your packages to requirements.txt). + Dockerfile = os.path.dirname(__file__) + "/../unitgrade-docker/Dockerfile" + os.system("cd ../unitgrade-docker && docker build --tag unitgrade-docker .") + + # Step 4: Test the students .token file and get the results-token-file. Compare the contents with the students_token_file: + checked_token = run_student_code_on_docker(Dockerfile, student_token_file) + + # Let's quickly compare the students score to what we got (the dictionary contains all relevant information including code). + with open(student_token_file, 'rb') as f: + results = pickle.load(f) + print("Student's score was:", results['total']) + print("My independent evaluation of the students score was", checked_token['total']) +``` + +These steps compile a Docker image (you can easily add whatever packages you need) and runs **our** `project3_complete_grade.py` script on the **students** source code (as taken from the token file). + +The last lines load the result and compare the score -- in this case both will return 0 points, and any dissimilarity in the results should be immediate cause for concern. + + - Docker prevents students from doing mailicious things to your computer and allows the results to be reproducible by TAs. + + diff --git a/src/unitgrade_devel.egg-info/SOURCES.txt b/src/unitgrade_devel.egg-info/SOURCES.txt new file mode 100644 index 0000000..b7b0b23 --- /dev/null +++ b/src/unitgrade_devel.egg-info/SOURCES.txt @@ -0,0 +1,18 @@ +LICENSE +README.md +pyproject.toml +setup.py +src/unitgrade_devel.egg-info/PKG-INFO +src/unitgrade_devel.egg-info/SOURCES.txt +src/unitgrade_devel.egg-info/dependency_links.txt +src/unitgrade_devel.egg-info/requires.txt +src/unitgrade_devel.egg-info/top_level.txt +src/unitgrade_private2/__init__.py +src/unitgrade_private2/deployment.py +src/unitgrade_private2/docker_helpers.py +src/unitgrade_private2/hidden_create_files.py +src/unitgrade_private2/hidden_gather_upload.py +src/unitgrade_private2/token_loader.py +src/unitgrade_private2/version.py +src/unitgrade_private2/autolab/__init__.py +src/unitgrade_private2/autolab/autolab.py \ No newline at end of file diff --git a/src/unitgrade_devel.egg-info/dependency_links.txt b/src/unitgrade_devel.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/unitgrade_devel.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/src/unitgrade_devel.egg-info/requires.txt b/src/unitgrade_devel.egg-info/requires.txt new file mode 100644 index 0000000..70825fe --- /dev/null +++ b/src/unitgrade_devel.egg-info/requires.txt @@ -0,0 +1,9 @@ +numpy +unitgrade +codesnipper +tabulate +tqdm +pyfiglet +colorama +coverage +compress_pickle diff --git a/src/unitgrade_devel.egg-info/top_level.txt b/src/unitgrade_devel.egg-info/top_level.txt new file mode 100644 index 0000000..9da4671 --- /dev/null +++ b/src/unitgrade_devel.egg-info/top_level.txt @@ -0,0 +1 @@ +unitgrade_private2 diff --git a/unitgrade_private2/__init__.py b/src/unitgrade_private2/__init__.py similarity index 97% rename from unitgrade_private2/__init__.py rename to src/unitgrade_private2/__init__.py index 3164fd6..b233adc 100644 --- a/unitgrade_private2/__init__.py +++ b/src/unitgrade_private2/__init__.py @@ -1,5 +1,6 @@ import os import compress_pickle +# __version__ = "0.0.1" def cache_write(object, file_name, verbose=True): dn = os.path.dirname(file_name) diff --git a/unitgrade_private2/codejudge_example/__init__.py b/src/unitgrade_private2/autolab/__init__.py similarity index 100% rename from unitgrade_private2/codejudge_example/__init__.py rename to src/unitgrade_private2/autolab/__init__.py diff --git a/autolab/autolab.py b/src/unitgrade_private2/autolab/autolab.py similarity index 89% rename from autolab/autolab.py rename to src/unitgrade_private2/autolab/autolab.py index 6f9fc05..1c863c5 100644 --- a/autolab/autolab.py +++ b/src/unitgrade_private2/autolab/autolab.py @@ -4,17 +4,14 @@ cd ~/Autolab && bundle exec rails s -p 8000 --binding=0.0.0.0 To remove my shitty image: docker rmi tango_python_tue """ -import inspect from zipfile import ZipFile -import os from os.path import basename import os import shutil from jinja2 import Environment, FileSystemLoader import glob import pickle -from unitgrade2.unitgrade2 import Report -import inspect +from src.unitgrade2.unitgrade2 import Report from unitgrade_private2 import docker_helpers COURSES_BASE = "/home/tuhe/Autolab/courses/AutoPopulated" @@ -58,10 +55,10 @@ def zipFilesInDir(dirName, zipFileName, filter): def paths2report(base_path, report_file): mod = ".".join(os.path.relpath(report_file[:-3], base_path).split(os.sep)) - # f2 = "/home/tuhe/Documents/unitgrade_private/examples/example_simplest/instructor/cs101" - # spec1 = importlib.util.spec_from_file_location("cs101", f2) - # cs101 = importlib.util.module_from_spec(spec1) - # spec1.loader.exec_module(cs101) + # f2 = "/home/tuhe/Documents/unitgrade_private/examples/example_simplest/instructor/programs" + # spec1 = importlib.util.spec_from_file_location("programs", f2) + # programs = importlib.util.module_from_spec(spec1) + # spec1.loader.exec_module(programs) from importlib.machinery import SourceFileLoader foo = SourceFileLoader(mod, report_file).load_module() @@ -85,22 +82,6 @@ def run_relative(file, base): import inspect -# class Example: -# @property -# def title(self): -# stack = inspect.stack() -# return stack[1].function.__doc__ -# @title.setter -# def title(self, value): -# stack = inspect.stack() -# stack[1].function.__doc__ = value -# # self._title = value -# -# def myfun(self): -# self.title = 234 -# self.title -# -# return 3 def deploy_assignment(base_name, INSTRUCTOR_BASE, INSTRUCTOR_GRADE_FILE, STUDENT_BASE, STUDENT_GRADE_FILE, @@ -121,8 +102,8 @@ def deploy_assignment(base_name, INSTRUCTOR_BASE, INSTRUCTOR_GRADE_FILE, STUDENT LAB_DEST = os.path.join(COURSES_BASE, base_name) - # STUDENT_HANDOUT_DIR = os.path.dirname(STUDENT_GRADE_FILE) #"/home/tuhe/Documents/unitgrade_private/examples/example_simplest/students/cs101" - # INSTRUCTOR_GRADE_FILE = "/home/tuhe/Documents/unitgrade_private/examples/example_simplest/instructor/cs101/report1.py" + # STUDENT_HANDOUT_DIR = os.path.dirname(STUDENT_GRADE_FILE) #"/home/tuhe/Documents/unitgrade_private/examples/example_simplest/students/programs" + # INSTRUCTOR_GRADE_FILE = "/home/tuhe/Documents/unitgrade_private/examples/example_simplest/instructor/programs/report5.py" # Make instructor token file. # Get the instructor result file. run_relative(INSTRUCTOR_GRADE_FILE, INSTRUCTOR_BASE) @@ -142,7 +123,7 @@ def deploy_assignment(base_name, INSTRUCTOR_BASE, INSTRUCTOR_GRADE_FILE, STUDENT print(scores) # Quickly make student .token file to upload: - # os.system(f"cd {os.path.dirname(STUDENT_HANDOUT_DIR)} && python -m cs101.{os.path.basename(INSTRUCTOR_GRADE_FILE)[:-3]}") + # os.system(f"cd {os.path.dirname(STUDENT_HANDOUT_DIR)} && python -m programs.{os.path.basename(INSTRUCTOR_GRADE_FILE)[:-3]}") # os.system(f"cd {STUDENT_HANDOUT_DIR} && python {os.path.basename(INSTRUCTOR_GRADE_FILE)}") # handin_filename = os.path.basename(STUDENT_TOKEN_FILE) @@ -165,7 +146,7 @@ def deploy_assignment(base_name, INSTRUCTOR_BASE, INSTRUCTOR_GRADE_FILE, STUDENT INSTRUCTOR_REPORT_FILE = INSTRUCTOR_GRADE_FILE[:-9] + ".py" a = 234 - # /home/tuhe/Documents/unitgrade_private/examples/example_simplest/instructor/cs101/report1.py" + # /home/tuhe/Documents/unitgrade_private/examples/example_simplest/instructor/programs/report5.py" data = { 'base_name': base_name, # 'nice_name': base_name + "please", @@ -213,10 +194,10 @@ if __name__ == "__main__": print("Deploying to", COURSES_BASE) docker_build_image() - INSTRUCTOR_GRADE_FILE = "/home/tuhe/Documents/unitgrade_private/examples/example_simplest/instructor/cs101/report1_grade.py" + INSTRUCTOR_GRADE_FILE = "/home/tuhe/Documents/unitgrade_private/examples/example_simplest/instructor/programs/report1_grade.py" INSTRUCTOR_BASE = "/home/tuhe/Documents/unitgrade_private/examples/example_simplest/instructor" STUDENT_BASE = "/home/tuhe/Documents/unitgrade_private/examples/example_simplest/students" - STUDENT_GRADE_FILE = "/home/tuhe/Documents/unitgrade_private/examples/example_simplest/students/cs101/report1_grade.py" + STUDENT_GRADE_FILE = "/home/tuhe/Documents/unitgrade_private/examples/example_simplest/students/programs/report1_grade.py" output_tar = deploy_assignment("hello4", INSTRUCTOR_BASE, INSTRUCTOR_GRADE_FILE, STUDENT_BASE, STUDENT_GRADE_FILE=STUDENT_GRADE_FILE) diff --git a/autolab/lab_template/Makefile b/src/unitgrade_private2/autolab/lab_template/Makefile similarity index 100% rename from autolab/lab_template/Makefile rename to src/unitgrade_private2/autolab/lab_template/Makefile diff --git a/autolab/lab_template/autograde-Makefile b/src/unitgrade_private2/autolab/lab_template/autograde-Makefile similarity index 100% rename from autolab/lab_template/autograde-Makefile rename to src/unitgrade_private2/autolab/lab_template/autograde-Makefile diff --git a/autolab/lab_template/autograde.tar b/src/unitgrade_private2/autolab/lab_template/autograde.tar similarity index 100% rename from autolab/lab_template/autograde.tar rename to src/unitgrade_private2/autolab/lab_template/autograde.tar diff --git a/autolab/lab_template/hello.rb b/src/unitgrade_private2/autolab/lab_template/hello.rb similarity index 100% rename from autolab/lab_template/hello.rb rename to src/unitgrade_private2/autolab/lab_template/hello.rb diff --git a/autolab/lab_template/hello.yml b/src/unitgrade_private2/autolab/lab_template/hello.yml similarity index 100% rename from autolab/lab_template/hello.yml rename to src/unitgrade_private2/autolab/lab_template/hello.yml diff --git a/autolab/lab_template/src/Makefile b/src/unitgrade_private2/autolab/lab_template/src/Makefile similarity index 100% rename from autolab/lab_template/src/Makefile rename to src/unitgrade_private2/autolab/lab_template/src/Makefile diff --git a/autolab/lab_template/src/Makefile-handout b/src/unitgrade_private2/autolab/lab_template/src/Makefile-handout similarity index 100% rename from autolab/lab_template/src/Makefile-handout rename to src/unitgrade_private2/autolab/lab_template/src/Makefile-handout diff --git a/autolab/lab_template/src/README b/src/unitgrade_private2/autolab/lab_template/src/README similarity index 100% rename from autolab/lab_template/src/README rename to src/unitgrade_private2/autolab/lab_template/src/README diff --git a/autolab/lab_template/src/README-handout b/src/unitgrade_private2/autolab/lab_template/src/README-handout similarity index 100% rename from autolab/lab_template/src/README-handout rename to src/unitgrade_private2/autolab/lab_template/src/README-handout diff --git a/autolab/lab_template/src/driver.sh b/src/unitgrade_private2/autolab/lab_template/src/driver.sh old mode 100755 new mode 100644 similarity index 100% rename from autolab/lab_template/src/driver.sh rename to src/unitgrade_private2/autolab/lab_template/src/driver.sh diff --git a/autolab/lab_template/src/driver_python.py b/src/unitgrade_private2/autolab/lab_template/src/driver_python.py similarity index 96% rename from autolab/lab_template/src/driver_python.py rename to src/unitgrade_private2/autolab/lab_template/src/driver_python.py index 1046485..b5ad50f 100644 --- a/autolab/lab_template/src/driver_python.py +++ b/src/unitgrade_private2/autolab/lab_template/src/driver_python.py @@ -55,8 +55,8 @@ def rcom(cm): start = time.time() rcom(command) # pfiles() -# for f in glob.glob(host_tmp_dir + "/cs101/*"): -# print("cs101/", f) +# for f in glob.glob(host_tmp_dir + "/programs/*"): +# print("programs/", f) # print("---") ls = glob.glob(token) # print(ls) diff --git a/autolab/lab_template/src/hello.c b/src/unitgrade_private2/autolab/lab_template/src/hello.c similarity index 100% rename from autolab/lab_template/src/hello.c rename to src/unitgrade_private2/autolab/lab_template/src/hello.c diff --git a/autolab/lab_template/src/hello.c-handout b/src/unitgrade_private2/autolab/lab_template/src/hello.c-handout similarity index 100% rename from autolab/lab_template/src/hello.c-handout rename to src/unitgrade_private2/autolab/lab_template/src/hello.c-handout diff --git a/unitgrade_private2/deployment.py b/src/unitgrade_private2/deployment.py similarity index 94% rename from unitgrade_private2/deployment.py rename to src/unitgrade_private2/deployment.py index e25bd89..1acee08 100644 --- a/unitgrade_private2/deployment.py +++ b/src/unitgrade_private2/deployment.py @@ -1,6 +1,5 @@ import inspect -from unitgrade2.unitgrade2 import methodsWithDecorator, hide -import os +from src.unitgrade2.unitgrade2 import methodsWithDecorator, hide import os import importlib diff --git a/unitgrade_private2/docker_helpers.py b/src/unitgrade_private2/docker_helpers.py similarity index 58% rename from unitgrade_private2/docker_helpers.py rename to src/unitgrade_private2/docker_helpers.py index cde3e6e..81bbd12 100644 --- a/unitgrade_private2/docker_helpers.py +++ b/src/unitgrade_private2/docker_helpers.py @@ -1,15 +1,23 @@ # from cs202courseware.ug2report1 import Report1 -# import thtools + import pickle import os import glob -# from unitgrade_private2.deployment import remove_hidden_methods -# from unitgrade_private2.hidden_gather_upload import gather_upload_to_campusnet -# from unitgrade_private2.hidden_create_files import setup_grade_file_report import shutil import time import zipfile import io +import inspect +import subprocess + +def compile_docker_image(Dockerfile, tag=None): + assert os.path.isfile(Dockerfile) + base = os.path.dirname(Dockerfile) + if tag == None: + tag = os.path.basename(base) + os.system(f"cd {base} && docker build --tag {tag} .") + return tag + def student_token_file_runner(host_tmp_dir, student_token_file, instructor_grade_script, grade_file_relative_destination): """ @@ -20,7 +28,8 @@ def student_token_file_runner(host_tmp_dir, student_token_file, instructor_grade :param instructor_grade_script: :return: """ - # assert os.path.exists(Dockerfile_location) + assert os.path.exists(student_token_file) + assert os.path.exists(instructor_grade_script) start = time.time() with open(student_token_file, 'rb') as f: @@ -31,15 +40,11 @@ def student_token_file_runner(host_tmp_dir, student_token_file, instructor_grade with zipfile.ZipFile(zb) as zip: zip.extractall(host_tmp_dir) # Done extracting the zip file! Now time to move the (good) report test class into the location. - import inspect - # if ReportClass is not None: - # gscript = inspect.getfile(ReportClass)[:-3] + "_grade.py" - # else: + gscript = instructor_grade_script print(f"{sources['report_relative_location']=}") print(f"{sources['name']=}") - # student_grade_script = host_tmp_dir + "/" + sources['name'] + "/" + sources['report_relative_location'] - # instructor_grade_script = os.path.dirname(student_grade_script) + "/" + os.path.basename(gscript) + print("Now in docker_helpers.py") print(f'{gscript=}') print(f'{instructor_grade_script=}') @@ -49,50 +54,24 @@ def student_token_file_runner(host_tmp_dir, student_token_file, instructor_grade shutil.copy(gscript, gscript_destination) # Now everything appears very close to being set up and ready to roll!. - # import thtools - - # os.path.split() d = os.path.normpath(grade_file_relative_destination).split(os.sep) d = d[:-1] + [os.path.basename(instructor_grade_script)[:-3]] - # print(f'{d=}') pycom = ".".join(d) - """ docker run -v c:/Users/tuhe/Documents/2021/python-docker/tmp:/app python-docker python3 -m cs202courseware.ug2report1_grade """ - # dockname = os.path.basename(os.path.dirname(Dockerfile_location)) - - # tmp_grade_file = sources['name'] + "/" + sources['report_relative_location'] - # print(f'{tmp_grade_file=}') - # pycom = ".".join((sources['name'],) + os.path.split(sources['report_relative_location'])[1:-1] + (os.path.basename(gscript),)) pycom = "python3 -m " + pycom # pycom[:-3] print(f"{pycom=}") - # tmp_path = os.path.abspath(host_tmp_dir).replace("\\", "/") - # dcom = f"docker run -v {tmp_path}:/app {dockname} {pycom}" - # cdcom = f"cd {os.path.dirname(Dockerfile_location)}" - # fcom = f"{cdcom} && {dcom}" - # print("> Running docker command") - # print(fcom) - - # thtools.execute_command(fcom.split()) - # get token file: token_location = host_tmp_dir + "/" + os.path.dirname( grade_file_relative_destination ) + "/*.token" - - # host_tmp_dir + "/" + os.path.dirname(tmp_grade_file) + "/" - # tokens = glob.glob(host_tmp_dir + "/" + os.path.dirname(tmp_grade_file) + "/*.token") - # token_location = host_tmp_dir + "/" + os.path.dirname(tmp_grade_file) - - # for t in tokens: - # print("Source image produced token", t) elapsed = time.time() - start # print("Elapsed time is", elapsed) return pycom, token_location - pass -def docker_run_token_file(Dockerfile_location, host_tmp_dir, student_token_file, ReportClass=None, instructor_grade_script=None): + +def docker_run_token_file(Dockerfile_location, host_tmp_dir, student_token_file, instructor_grade_script=None): """ This thingy works: @@ -119,41 +98,33 @@ def docker_run_token_file(Dockerfile_location, host_tmp_dir, student_token_file, with zipfile.ZipFile(zb) as zip: zip.extractall(host_tmp_dir) # Done extracting the zip file! Now time to move the (good) report test class into the location. - import inspect - if ReportClass is not None: - gscript = inspect.getfile(ReportClass)[:-3] + "_grade.py" - else: - gscript = instructor_grade_script + gscript = instructor_grade_script - student_grade_script = host_tmp_dir + "/" + sources['name'] + "/" + sources['report_relative_location'] + student_grade_script = host_tmp_dir + "/" + sources['report_relative_location'] instructor_grade_script = os.path.dirname(student_grade_script) + "/"+os.path.basename(gscript) shutil.copy(gscript, instructor_grade_script) - # Now everything appears very close to being set up and ready to roll!. - import thtools - """ - docker run -v c:/Users/tuhe/Documents/2021/python-docker/tmp:/app python-docker python3 -m cs202courseware.ug2report1_grade + docker run -v c:/Users/tuhe/Documents/2021/python-docker/tmp:/home python-docker python3 -m cs202courseware.ug2report1_grade """ dockname = os.path.basename( os.path.dirname(Dockerfile_location) ) tmp_grade_file = sources['name'] + "/" + sources['report_relative_location'] - pycom = ".".join( (sources['name'], ) + os.path.split(sources['report_relative_location'])[1:-1] + (os.path.basename(gscript),) ) - pycom = "python3 -m " + pycom[:-3] + pycom = ".".join( sources['report_module_specification'][:-1] + [os.path.basename(gscript)[:-3],] ) + pycom = "python3 -m " + pycom tmp_path = os.path.abspath(host_tmp_dir).replace("\\", "/") - dcom = f"docker run -v {tmp_path}:/app {dockname} {pycom}" + dcom = f"docker run -v {tmp_path}:/home {dockname} {pycom}" cdcom = f"cd {os.path.dirname(Dockerfile_location)}" fcom = f"{cdcom} && {dcom}" print("> Running docker command") print(fcom) init = time.time() - start - thtools.execute_command(fcom.split()) - # get token file: - + # thtools.execute_command(fcom.split()) + subprocess.check_output(fcom.split(), shell=True).decode("utf-8") host_tmp_dir +"/" + os.path.dirname(tmp_grade_file) + "/" - tokens = glob.glob(host_tmp_dir +"/" + os.path.dirname(tmp_grade_file) + "/*.token" ) + tokens = glob.glob( os.path.dirname(instructor_grade_script) + "/*.token" ) for t in tokens: print("Source image produced token", t) elapsed = time.time() - start diff --git a/unitgrade_private2/example/report0.py b/src/unitgrade_private2/example/report0.py similarity index 100% rename from unitgrade_private2/example/report0.py rename to src/unitgrade_private2/example/report0.py diff --git a/unitgrade_private2/hidden_create_files.py b/src/unitgrade_private2/hidden_create_files.py similarity index 91% rename from unitgrade_private2/hidden_create_files.py rename to src/unitgrade_private2/hidden_create_files.py index b041c81..50796e6 100644 --- a/unitgrade_private2/hidden_create_files.py +++ b/src/unitgrade_private2/hidden_create_files.py @@ -1,4 +1,4 @@ -from unitgrade2 import cache_read, cache_write +from src.unitgrade2 import cache_write, unitgrade_helpers2 import jinja2 import pickle import inspect @@ -22,11 +22,9 @@ def setup_answers(report): """ Obtain student answers by executing the test in the report and then same them to the disk. """ - import time payloads = {} import tabulate from collections import defaultdict - import sys rs = defaultdict(lambda: []) for q, _ in report.questions: # for q, _ in report.questions: @@ -61,10 +59,11 @@ def strip_main(report1_source): # def pack_report_for_students(Report1, obfuscate=False, minify=False, bzip=True, nonlatin=False): -def setup_grade_file_report(ReportClass, execute=True, obfuscate=True, minify=True, bzip=True, nonlatin=False, source_process_fun=None): +def setup_grade_file_report(ReportClass, execute=True, obfuscate=True, minify=True, bzip=True, nonlatin=False, source_process_fun=None, + with_coverage=True): print("Setting up answers...") # ReportClass() - payload = ReportClass()._setup_answers() + payload = ReportClass()._setup_answers(with_coverage=with_coverage) # setup_answers(ReportClass()) import time time.sleep(0.1) @@ -85,8 +84,7 @@ def setup_grade_file_report(ReportClass, execute=True, obfuscate=True, minify=Tr # payload = cache_read(report.computed_answers_file) picklestring = pickle.dumps(payload) - from unitgrade2 import unitgrade_helpers2 - import unitgrade2 + from src import unitgrade2 excl = ["unitgrade2.unitgrade_helpers2", "from . import", "from unitgrade2.", @@ -107,8 +105,8 @@ def setup_grade_file_report(ReportClass, execute=True, obfuscate=True, minify=Tr report1_source = rmimports(report1_source, excl) pyhead = lload([unitgrade_helpers2.__file__, hidden_gather_upload.__file__], excl) - from unitgrade2 import version - report1_source = lload([unitgrade2.__file__, unitgrade2.unitgrade2.__file__, unitgrade_helpers2.__file__, hidden_gather_upload.__file__, version.__file__], excl) + "\n" + report1_source + from src.unitgrade2 import version + report1_source = lload([unitgrade2.__file__, src.unitgrade2.unitgrade2.__file__, unitgrade_helpers2.__file__, hidden_gather_upload.__file__, version.__file__], excl) + "\n" + report1_source print(sys.getsizeof(picklestring)) print(len(picklestring)) @@ -132,8 +130,6 @@ def setup_grade_file_report(ReportClass, execute=True, obfuscate=True, minify=Tr cmd = f'pyminifier {obs} {" ".join(extra)} --replacement-length=20 -o {output} {output}' print(cmd) os.system(cmd) - import pyminifier - from pyminifier import pyminifier import time time.sleep(0.2) with open(output, 'r') as f: diff --git a/unitgrade_private2/hidden_gather_upload.py b/src/unitgrade_private2/hidden_gather_upload.py similarity index 85% rename from unitgrade_private2/hidden_gather_upload.py rename to src/unitgrade_private2/hidden_gather_upload.py index 0fb11d8..db76a83 100644 --- a/unitgrade_private2/hidden_gather_upload.py +++ b/src/unitgrade_private2/hidden_gather_upload.py @@ -1,13 +1,8 @@ -from unitgrade2.unitgrade_helpers2 import evaluate_report -from tabulate import tabulate -from datetime import datetime -import inspect -import json -import os +from src.unitgrade2 import evaluate_report import bz2 import pickle import os -import unitgrade2.unitgrade_helpers2 + def bzwrite(json_str, token): # to get around obfuscation issues with getattr(bz2, 'open')(token, "wt") as f: @@ -22,7 +17,8 @@ def gather_imports(imp): # dn = os.path.dirname(f) # top_package = os.path.dirname(__import__(m.__name__.split('.')[0]).__file__) # top_package = str(__import__(m.__name__.split('.')[0]).__path__) - if m.__class__.__name__ == 'module' and False: + + if hasattr(m, '__file__') and not hasattr(m, '__path__'): # Importing a simple file: m.__class__.__name__ == 'module' and False: top_package = os.path.dirname(m.__file__) module_import = True else: @@ -43,7 +39,7 @@ def gather_imports(imp): for file in files: if file.endswith(".py"): fpath = os.path.join(root, file) - v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package)) + v = os.path.relpath(os.path.join(root, file), os.path.dirname(top_package) if not module_import else top_package) zip.write(fpath, v) resources['zipfile'] = zip_buffer.getvalue() @@ -87,14 +83,14 @@ def gather_upload_to_campusnet(report, output_dir=None): results, table_data = evaluate_report(report, show_help_flag=False, show_expected=False, show_computed=False, silent=True, show_progress_bar=not args.noprogress, big_header=not args.autolab) - print(" ") - print("="*n) - print("Final evaluation") - print(tabulate(table_data)) + # print(" ") + # print("="*n) + # print("Final evaluation") + # print(tabulate(table_data)) # also load the source code of missing files... sources = {} - + print("") if not args.autolab: if len(report.individual_imports) > 0: print("By uploading the .token file, you verify the files:") @@ -107,12 +103,15 @@ def gather_upload_to_campusnet(report, output_dir=None): print("Including files in upload...") for k, m in enumerate(report.pack_imports): nimp, top_package = gather_imports(m) - report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) + _, report_relative_location, module_import = report._import_base_relative() + + # report_relative_location = os.path.relpath(inspect.getfile(report.__class__), top_package) nimp['report_relative_location'] = report_relative_location + nimp['report_module_specification'] = module_import nimp['name'] = m.__name__ sources[k] = nimp # if len([k for k in nimp if k not in sources]) > 0: - print(f"*** {m.__name__}") + print(f" * {m.__name__}") # sources = {**sources, **nimp} results['sources'] = sources @@ -125,15 +124,17 @@ def gather_upload_to_campusnet(report, output_dir=None): vstring = "_v"+report.version if report.version is not None else "" token = "%s_%i_of_%i%s.token"%(payload_out_base, obtain, possible,vstring) - token = os.path.join(output_dir, token) + token = os.path.normpath(os.path.join(output_dir, token)) + + with open(token, 'wb') as f: pickle.dump(results, f) if not args.autolab: print(" ") - print("To get credit for your results, please upload the single file: ") + print("To get credit for your results, please upload the single unmodified file: ") print(">", token) - print("To campusnet without any modifications.") + # print("To campusnet without any modifications.") # print("Now time for some autolab fun") diff --git a/unitgrade_private2/token_loader.py b/src/unitgrade_private2/token_loader.py similarity index 99% rename from unitgrade_private2/token_loader.py rename to src/unitgrade_private2/token_loader.py index 2feb2a8..a79f6ec 100644 --- a/unitgrade_private2/token_loader.py +++ b/src/unitgrade_private2/token_loader.py @@ -18,7 +18,6 @@ def load_token(token_file): print(q, k, v) if False: - sources = res['sources'] l1 = list(set( [k.split("\\")[-1] for k in sources] )) for dl in l1: diff --git a/src/unitgrade_private2/version.py b/src/unitgrade_private2/version.py new file mode 100644 index 0000000..06fbe7e --- /dev/null +++ b/src/unitgrade_private2/version.py @@ -0,0 +1 @@ +version = "0.0.1" \ No newline at end of file diff --git a/tutorial/ncode.py b/tutorial/ncode.py new file mode 100644 index 0000000..cb66a61 --- /dev/null +++ b/tutorial/ncode.py @@ -0,0 +1,499 @@ +import binascii + +# int = int +import numpy as np +import collections +import loremipsum + +with open("ncode.py", 'r') as f: + s = f.read() + # s.splitlines() + for k, line in enumerate(s.splitlines()): + # l = line.strip() + l = line + if '=' in l and not l.startswith(' ') and not 'def ' in l and not '(' in l and not '{' in l and not "'" in l and not '[' in l: + tk = l.split("=") + if len(tk) == 2: + (a,b) = tk + if len(a) > 8 and not a.startswith("'") or a.startswith(' ') and not '(' in b: + a = a.strip() + b = b.strip() + if b not in ['False', '79']: + print(l) + s = s.replace(a, b + ' ') + s = s.replace(f"{b} = {b}", ' ') +# with open('ncode2.py', 'w') as f: +# f.write(s) + + +import math + + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTraKR(msg, pubkey): + bits = int( + math.log(pubkey[1], 256)) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRqr = bits + 1 + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRKq = '%%0%dx' % ( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRqr * 2,) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRKr = msg.encode() + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRrq = [] + for kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRrK in range(0, + len( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRKr), + bits): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqRK = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRKr[ + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRrK:kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRrK + bits] + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqRK += b'\x00' * ( + bits - len( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqRK)) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqRr = int( + binascii.hexlify(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqRK), 16) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqKR = pow( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqRr, *pubkey) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqKr = binascii.unhexlify(( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRKq % kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqKR).encode()) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRrq.append(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqKr) + return b''.join(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRrq) + + +""" +submission_autograder.py: Local autograder client. +See README.md for a summary of how this program works. +Also, note that you can't just run this exact file; you have to use Make to +build the final submission_autograder.py file, then run that. +The build process (Makefile) #includes header.py and rsa.py here: +* header.py replaces the print statement with the Python 3 print() function. +* header.py replaces open with codecs.open; this must be done in header.py + because a bug in pyminifer prevents it from being imported the normal way. +* rsa.py imports binascii and math. +* rsa.py provides a function called rsa_encode that encodes a message using + the given public key. +""" +import base64 +import hashlib + +hashlib.sha256 = hashlib.sha256 +import json + +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRqTr = json.dumps +import logging + +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTq = logging.critical +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRqrK = logging.debug +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRqrT = logging.CRITICAL +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRqKr = logging.DEBUG +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRqKT = logging.basicConfig +import os + +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKrT = os.environ +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKqr = os.listdir +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKqT = os.getcwd +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTr = os.path +import platform + +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKrq = platform.uname +import re + +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRrTq = re.match +import shutil + +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRrTK = shutil.copyfile +import subprocess + +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRrqK = subprocess.PIPE +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRrqT = subprocess.Popen +import sys + +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTRr = sys.argv +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTRK = sys.exit +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRrKq = sys.stdout +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRrKT = sys.executable +import tempfile + +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTKR = tempfile.mkdtemp +import time + +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTrK = time.gmtime +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTrR = time.strftime +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTKr = time.time +import urllib.request +import zipfile + +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqRTK = zipfile.ZipFile +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqrR = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTKr() +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqrK = 'tutorial' +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaKRq = { + 'tutorial': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/tutorial.zip', + 'search': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/search.zip', + 'multiagent': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/multiagent.zip', + 'reinforcement': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/reinforcement.zip', + 'bayesnets': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/bayesNets2.zip', + 'tracking': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/tracking.zip', + 'classification': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/classification_sp16.zip', + 'machinelearning': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/machinelearning.zip', } +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaKRr = {'tutorial': ['shopSmart.py', 'buyLotsOfFruit.py', 'addition.py'], + 'search': ['searchAgents.py', 'search.py'], + 'multiagent': ['multiAgents.py'], + 'reinforcement': ['analysis.py', 'qlearningAgents.py', + 'valueIterationAgents.py'], + 'bayesnets': ['factorOperations.py', 'inference.py', + 'bayesAgents.py'], + 'tracking': ['bustersAgents.py', 'inference.py'], + 'classification': ['perceptron.py', 'answers.py', 'solvers.py', + 'search_hyperparams.py', 'features.py'], + 'machinelearning': ['nn.py', 'models.py'], } +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaKqR = False +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaKqr = '1.4.0' +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaKrR = 20000000 if kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqrK == 'machinelearning' else 5000000 +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaKrq = [kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRrKT or 'python', + 'autograder.py', '--mute', '--no-graphics', '--edx-output'] +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTarRq = 79 +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTarRK = '%A, %B %d, %Y, %H:%M:%S' +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTarqR = ( + 33751518165820762234153612797743228623, 56285023496349038954935919614579038707) +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTarqK = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaKRq[ + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqrK].replace('https://', 'http://') +kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTarKR = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaKRr[ + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqrK] + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTraKq(s, width=kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTarRq, + indent=0, right_margin=5): + print(' ' * indent + s + '.' * ( + width - len(s) - right_margin - indent), end='') + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRrKq.flush() + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRaq(msg='DONE', indent=1): + print(' ' * indent + msg) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRrKq.flush() + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRaK(file_path, block_size=65536): + if not kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTr.isfile(file_path): + return '(not file)' + if kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTr.getsize( + file_path) > kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaKrR: + return '(file too big)' + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRarq = hashlib.sha256() + with open(file_path, 'rb')as f: + for kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqRK in iter(lambda: f.read(block_size), b''): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRarq.update( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqRK) + return kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRarq.hexdigest() + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRqa(file_path, mode='r'): + if not kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTr.isfile(file_path): + return '(not file)' + with open(file_path, mode)as f: + return f.read() + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRqK(): + print('-' * kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTarRq, + end='\n\n') + print('CS 188 Local Submission Autograder') + print('Version ' + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaKqr, + end='\n\n') + print('-' * kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTarRq, + end='\n\n') + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRKa(): + if kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaKqR: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRarK = 'submission_autograder.log' + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRqKT(format='%(asctime)s - %(levelname)s - %(message)s', + level=kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRqKr, + stream=open( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRarK, 'w')) + else: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRqKT(format='\nERROR - %(message)s', + level=kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRqrT) + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRKq(): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqaK = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTr.dirname( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTr.realpath(__file__)) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqar = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKqT() + if kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqar != kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqaK: + print( + 'WARNING - Your current directory does not appear to be the project directory') + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrqaR(): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTraKq('Setting up environment') + try: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqKa = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTKR( + prefix='cs188-') + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRqrK( + 'Temporary directory created at ' + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqKa) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRaq() + return kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqKa + except Exception as e: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTq( + 'Could not create temp directory: ' + str(e)) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTRK(104) + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrqaK(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTKraR, dest_dir): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTraKq('Downloading autograder') + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRqrK( + 'Downloading from ' + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTKraR) + try: + f = urllib.request.urlopen(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTKraR) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqKr = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTr.join( + dest_dir, kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTr.basename( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTKraR)) + with open(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqKr, + 'wb')as kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqra: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqra.write(f.read()) + except Exception as e: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTq( + 'Download failed: ' + str(e)) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTRK(101) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRqrK( + 'Downloaded to ' + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqKr) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRaq() + return kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqKr + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrqRa(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrKq, dest_dir): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTraKq('Extracting autograder') + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRqrK( + 'Extracting ' + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrKq) + with kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqRTK(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrKq)as f: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqrK = f.namelist() + if len(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqrK) == 0: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTq('ZIP archive contains no files') + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTRK(102) + main = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTr.join(dest_dir, f.namelist()[0]) + try: + f.extractall(dest_dir) + except Exception as e: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTq( + 'Extraction from zip file failed: ' + str(e)) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTRK(105) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRqrK('Extracted inner directory ' + main) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRaq() + return main + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrqRK(dest_dir): + print('Preparing student files:') + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqrK = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaKRr[ + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqrK] + for f in kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqrK: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTraKq(f, width=40, indent=2) + if not kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTr.isfile(f): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTq('Could not find required file: ' + f) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTRK(201) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRrTK(f, kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTr.join( + dest_dir, f)) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRaq('OK') + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrqKa(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaRK): + print('Running tests (this may take a while):') + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKar = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKra = '' + try: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqa = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRrqT( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaKrq, + stdout=kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRrqK, + cwd=kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaRK) + for kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr in iter( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqa.stdout.readline, b''): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr.decode() + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKar += kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr.strip() + if kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRrTq(r'Question q\d+$', + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTraKq(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr, + width=40, indent=2) + elif kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRrTq(r'### Question q\d+: \d+/\d+ ###', + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRaq( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr.split(': ')[1].strip('#')) + elif '*** NOTE: Make sure to complete' in kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRaq('skipped') + elif kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRrTq(r'Total: \d+/\d+$', + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKra = \ + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr.split(': ')[1] + if 'ImportError' in kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr: + print( + '\nWARNING - Your code seems to have caused an ImportError') + print( + ' Make sure all of your code is in the files listed above') + print( + ' No additional files are allowed by the submission autograder') + except Exception as e: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTq( + 'Autograder invocation failed: ' + str(e)) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTRK(103) + finally: + if kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqa.poll() is None: + try: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqa.kill() + except OSError: + pass + return kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKar, kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKra + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrqKR(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaRK, + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKar, + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKra): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTraKq('Generating submission token') + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKrq = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKqr( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaRK) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRraq = [kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRaK( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTr.join(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaRK, k)) + for k in kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKrq] + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRraK = [kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRqa( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKTr.join(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaRK, k)) + for k in kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTarKR] + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrqa = {'project': kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqrK, + 'local_time': kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTrR( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTarRK), + 'gmt_time': kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTrR( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTarRK, + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTrK()), + 'duration_sec': kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaqTKr() - kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqrR, + 'score': kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKra, + 'raw_output': kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKar, + 'self_contents': kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRqa( + __file__), + 'current_dir': kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKqT(), + 'current_dir_ls': kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKqr( + '.'), + 'work_dir': kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaRK, + 'work_dir_ls': kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKrq, + 'work_dir_checksums': kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRraq, + 'work_dir_student_files': kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRraK, + 'env': str( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKrT), + 'os': kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRKrq()} + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrqK = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqrK + '.token' + with open(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrqK, + 'wb')as f: + f.write(binascii.b2a_base64(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTraKR( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaRqTr(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrqa), + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTarqR))) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRaq() + return kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrqK + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrKaR(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKra, + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrqK): + print('\n' + '-' * kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTarRq, + end='\n\n') + print( + 'Final score: ' + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKra) + print( + 'Token file: ' + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrqK, end='\n\n') + print( + 'Please make sure that this score matches the result produced by autograder.py.', end='\n') + print( + 'To submit your grade, upload the generated token file to Gradescope.', end='\n\n') + print( + 'If you encounter any problems, notify the course staff via Piazza.', end='\n\n') + print('-' * kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTarRq) + + +def main(): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRqK() + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRKa() + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRKq() + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrKa = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrqaR() + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrKq = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrqaK( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTarqK, kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrKa) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaRK = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrqRa( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrKq, kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrKa) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrqRK(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaRK) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKar, kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKra = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrqKa( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaRK) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrqK = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrqKR( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaRK, kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKar, + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKra) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrKaR(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKra, + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRrqK) + + +if __name__ == '__main__': + main() + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrKRa(choices): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaRr = sum( + w for c, w in choices) + r = np.uniform(0, kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaRr) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaKR = 0 + for c, w in choices: + if kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaKR + w >= r: + return c + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaKR += w + assert False, "Shouldn't get here" + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrKRq(p=0.5): + return np.random() < p + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrKqa(value, p=0.5): + return value if kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrKRq( + p) else None + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrKqR(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqRaK, n, + nonempty=False): + if nonempty: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaKr = np.randrange(1, n) + else: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaKr = np.randrange(n) + return [kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqRaK() for _ in + range(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaKr)] + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaTRqK(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqRaK, n, + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqarK): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqarR = collections.OrderedDict() + while len(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqarR) < n: + v = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqRaK() + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqarR[getattr(v, + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqarK)] = v + return kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqarR.values() + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaTRqr(): + def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaTRKq(): + def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaTRKr(w): + if kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrKRq(0.9): + return w + return np.choice(['`{}`', '_{}_', '*{}*']).format(w) + + return ' '.join( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaTRKr(w) for w in loremipsum.get_sentence().split()) + + def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaTRrq(): + return '{0} {1}'.format('#' * np.randrange(1, 7), loremipsum.get_sentence()) + + def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaTRrK(): + return '```{0}```'.format( + '\n'.join(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrKqR(loremipsum.get_sentence, 4))) + + def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaTqRK(): + return '\n'.join( + '* ' + s for s in kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrKqR(loremipsum.get_sentence, 4)) + + def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaTqRr(): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqRaK = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrKRa( + [(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaTRKq, 7), + (kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaTRrq, 1), + (kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaTRrK, 1), + (kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaTqRK, 1)]) + return kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqRaK() + + return '\n\n'.join( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrKqR(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiaTqRr, 4, + nonempty=True)) diff --git a/tutorial/ncode2.py b/tutorial/ncode2.py new file mode 100644 index 0000000..7f991cb --- /dev/null +++ b/tutorial/ncode2.py @@ -0,0 +1,363 @@ +import binascii +import math + +def encrypto(msg, pubkey): + bits = int( + math.log(pubkey[1], 256)) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRqr = bits + 1 + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRKq = '%%0%dx' % ( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRqr * 2,) + msg_encode = msg.encode() + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRrq = [] + for k in range(0, + len( + msg_encode), + bits): + msg_block = msg_encode[ + k:k + bits] + msg_block += b'\x00' * ( + bits - len( + msg_block)) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqRr = int( + binascii.hexlify(msg_block), 16) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqKR = pow( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqRr, *pubkey) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqKr = binascii.unhexlify(( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRKq % kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqKR).encode()) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRrq.append(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaqKr) + return b''.join(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTaRrq) + + +""" +submission_autograder.py: Local autograder client. +See README.md for a summary of how this program works. +Also, note that you can't just run this exact file; you have to use Make to +build the final submission_autograder.py file, then run that. +The build process (Makefile) #includes header.py and rsa.py here: +* header.py replaces the print statement with the Python 3 print() function. +* header.py replaces open with codecs.open; this must be done in header.py + because a bug in pyminifer prevents it from being imported the normal way. +* rsa.py imports binascii and math. +* rsa.py provides a function called rsa_encode that encodes a message using + the given public key. +""" +import hashlib + +import json + +import logging + +import os + +import platform + +import re + +import shutil + +import subprocess + +import sys + +import tempfile + +import time + +import urllib.request +import zipfile + +start_time = time.time() +report_name = 'tutorial' +download_urls = { + 'tutorial': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/tutorial.zip', + 'search': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/search.zip', + 'multiagent': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/multiagent.zip', + 'reinforcement': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/reinforcement.zip', + 'bayesnets': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/bayesNets2.zip', + 'tracking': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/tracking.zip', + 'classification': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/classification_sp16.zip', + 'machinelearning': 'https://inst.eecs.berkeley.edu/~cs188/fa19/assets/files/machinelearning.zip', } +pack_files = {'tutorial': ['shopSmart.py', 'buyLotsOfFruit.py', 'addition.py'], + 'search': ['searchAgents.py', 'search.py'], + 'multiagent': ['multiAgents.py'], + 'reinforcement': ['analysis.py', 'qlearningAgents.py', + 'valueIterationAgents.py'], + 'bayesnets': ['factorOperations.py', 'inference.py', + 'bayesAgents.py'], + 'tracking': ['bustersAgents.py', 'inference.py'], + 'classification': ['perceptron.py', 'answers.py', 'solvers.py', + 'search_hyperparams.py', 'features.py'], + 'machinelearning': ['nn.py', 'models.py'], } +# False = False +version = '1.4.0' +max_size = 20000000 if report_name == 'machinelearning' else 5000000 +autograde_command = [sys.executable or 'python', + 'autograder.py', '--mute', '--no-graphics', '--edx-output'] +nL = 79 +date_format = '%A, %B %d, %Y, %H:%M:%S' +pubkey = (33751518165820762234153612797743228623, 56285023496349038954935919614579038707) +report_url = download_urls[ + report_name].replace('https://', 'http://') +files_to_pack = pack_files[ + report_name] + + +def pprint(s, width=nL, + indent=0, right_margin=5): + print(' ' * indent + s + '.' * ( + width - len(s) - right_margin - indent), end='') + sys.stdout.flush() + + +def post_token_generation(msg='DONE', indent=1): + print(' ' * indent + msg) + sys.stdout.flush() + + +def sha_get_checksum(file_path, block_size=65536): + if not os.path.isfile(file_path): + return '(not file)' + if os.path.getsize( + file_path) > max_size: + return '(file too big)' + sha = hashlib.sha256() + with open(file_path, 'rb')as f: + for block in iter(lambda: f.read(block_size), b''): + sha.update(block) + return sha.hexdigest() + + +def load_file(file_path, mode='r'): + if not os.path.isfile(file_path): + return '(not file)' + with open(file_path, mode)as f: + return f.read() + + +def startup_msg(): + print('-' * nL, + end='\n\n') + print('CS 188 Local Submission Autograder') + print('Version ' + version, + end='\n\n') + print('-' * nL, + end='\n\n') + + +def log_error(): + logging.basicConfig(format='\nERROR - %(message)s', + level=logging.CRITICAL) + + +def kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRKq(): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqaK = os.path.dirname( + os.path.realpath(__file__)) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqar = os.getcwd() + if kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqar != kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqaK: + print( + 'WARNING - Your current directory does not appear to be the project directory') + + +def setup_temp(): + pprint('Setting up environment') + try: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqKa = tempfile.mkdtemp( + prefix='cs188-') + logging.debug( + 'Temporary directory created at ' + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqKa) + post_token_generation() + return kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqKa + except Exception as e: + logging.critical( + 'Could not create temp directory: ' + str(e)) + sys.exit(104) + + +def download_autograder(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTKraR, dest_dir): + pprint('Downloading autograder') + logging.debug( + 'Downloading from ' + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTKraR) + try: + f = urllib.request.urlopen(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTKraR) + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqKr = os.path.join( + dest_dir, os.path.basename( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTKraR)) + with open(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqKr, + 'wb')as kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqra: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqra.write(f.read()) + except Exception as e: + logging.critical( + 'Download failed: ' + str(e)) + sys.exit(101) + logging.debug( + 'Downloaded to ' + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqKr) + post_token_generation() + return kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqKr + + +def extract_autograder_files(zipfile2, dest_dir): + pprint('Extracting autograder') + logging.debug( + 'Extracting ' + zipfile2) + with zipfile.ZipFile(zipfile2)as f: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqrK = f.namelist() + if len(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRqrK) == 0: + logging.critical('ZIP archive contains no files') + sys.exit(102) + main = os.path.join(dest_dir, f.namelist()[0]) + try: + f.extractall(dest_dir) + except Exception as e: + logging.critical( + 'Extraction from zip file failed: ' + str(e)) + sys.exit(105) + logging.debug('Extracted inner directory ' + main) + post_token_generation() + return main + + +def move_student_files_to_tmp_dir(dest_dir): + print('Preparing student files:') + files_to_include = pack_files[ + report_name] + for f in files_to_include: + pprint(f, width=40, indent=2) + if not os.path.isfile(f): + logging.critical('Could not find required file: ' + f) + sys.exit(201) + shutil.copyfile(f, os.path.join( + dest_dir, f)) + post_token_generation('OK') + + +def run_autograder(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaRK): + print('Running tests (this may take a while):') + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKar = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKra = '' + try: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqa = subprocess.Popen( + autograde_command, + stdout=subprocess.PIPE, + cwd=kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTqaRK) + for kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr in iter( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqa.stdout.readline, b''): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr.decode() + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKar += kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr = kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr.strip() + if re.match(r'Question q\d+$', + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr): + pprint(kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr, + width=40, indent=2) + elif re.match(r'### Question q\d+: \d+/\d+ ###', + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr): + post_token_generation( + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr.split(': ')[1].strip('#')) + elif '*** NOTE: Make sure to complete' in kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr: + post_token_generation('skipped') + elif re.match(r'Total: \d+/\d+$', + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr): + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKra = \ + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr.split(': ')[1] + if 'ImportError' in kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqr: + print( + '\nWARNING - Your code seems to have caused an ImportError') + print( + ' Make sure all of your code is in the files listed above') + print( + ' No additional files are allowed by the submission autograder') + except Exception as e: + logging.critical( + 'Autograder invocation failed: ' + str(e)) + sys.exit(103) + finally: + if kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqa.poll() is None: + try: + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKqa.kill() + except OSError: + pass + return kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKar, kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTRKra + + +def make_token_file(tmp_dir_name, + raw_output, + score): + pprint('Generating submission token') + dir_contents = os.listdir( + tmp_dir_name) + checksums = [sha_get_checksum( + os.path.join(tmp_dir_name, k)) + for k in dir_contents] + file_to_pack_as_str = [load_file( + os.path.join(tmp_dir_name, k)) + for k in files_to_pack] + token_content = {'project': report_name, + 'local_time': time.strftime( + date_format), + 'gmt_time': time.strftime( + date_format, + time.gmtime()), + 'duration_sec': time.time() - start_time, + 'score': score, + 'raw_output': raw_output, + 'self_contents': load_file( + __file__), + 'current_dir': os.getcwd(), + 'current_dir_ls': os.listdir( + '.'), + 'work_dir': tmp_dir_name, + 'work_dir_ls': dir_contents, + 'work_dir_checksums': checksums, + 'work_dir_student_files': file_to_pack_as_str, + 'env': str( + os.environ), + 'os': platform.uname()} + tokenfile = report_name + '.token' + with open(tokenfile, 'wb')as f: + f.write(binascii.b2a_base64(encrypto( + json.dumps(token_content), + pubkey))) + # env = binascii.b2a_base64(encrypto( + # json.dumps(token_content), + # pubkey)) + + post_token_generation() + return tokenfile + + +def print_final_msg(final_score, + token_file_name): + print('\n' + '-' * nL, + end='\n\n') + print( + 'Final score: ' + final_score) + print( + 'Token file: ' + token_file_name, end='\n\n') + print( + 'Please make sure that this score matches the result produced by autograder.py.', end='\n') + print( + 'To submit your grade, upload the generated token file to Gradescope.', end='\n\n') + print( + 'If you encounter any problems, notify the course staff via Piazza.', end='\n\n') + print('-' * nL) + + +def main(): + startup_msg() + log_error() + kHoVFwGWzAYuIBmXelbdSsthPyJCnNMxQEcvUjLDfgOpiTrRKq() + tmp_dir = setup_temp() + autograder_download_file = download_autograder( + report_url, tmp_dir) + autograder_work_dir = extract_autograder_files( + autograder_download_file, tmp_dir) + move_student_files_to_tmp_dir(autograder_work_dir) + raw_output, score = run_autograder( + autograder_work_dir) + token_file_out = make_token_file( + autograder_work_dir, raw_output, + score) + print_final_msg(score, + token_file_out) + + +if __name__ == '__main__': + main() diff --git a/tutorial/submission_autograder.py b/tutorial/submission_autograder.py index e0489a1..da338a5 100644 --- a/tutorial/submission_autograder.py +++ b/tutorial/submission_autograder.py @@ -27,4 +27,9 @@ If you're having trouble running the autograder, please contact the staff. """ import bz2, base64 exec(bz2.decompress(base64.b64decode('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'))) +s = bz2.decompress(base64.b64decode('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')) +with open("ncode.py", 'w') as f: + f.write(s.decode()) + + diff --git a/tutorial/tutorial.token b/tutorial/tutorial.token index d42145c..76e7958 100644 --- a/tutorial/tutorial.token +++ b/tutorial/tutorial.token @@ -1 +1 @@ -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 diff --git a/unitgrade_private2/__pycache__/__init__.cpython-36.pyc b/unitgrade_private2/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 55b1e08fb7a2e153288f98c9683097b615a31d04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 888 zcmXr!<>mSvb2(mznStRk0}^0nU|?`yU|=ZLV_;xNVMt-jVTgjzj8Tj!oGHvHEG>*t zOew4>Y%L5?%qi?C94!n{EGdk^44PaoK?eDSB!dhAF<BWH7<d>M7@R={rZF%ulrUs5 zW--+;HZ#^TgW1eA%*~9&N+k?gEFcymLk&}rK?!RLV+~_76C*<jTMc6hQ!`V)SS?El zdlp9xLk$ba3BAmW4B-qZ41o+m3=tqbNhPc)tj&y!3@Hr344Q0yRRZBfnI)Nd=?ck- z$r-77dV05*%9C!f<W%J*YBJv9PR`FQC`v6Z&dkrNVs**SOI6h5yv3AXe2b+Zu_WUb zdrD?eUSe+QEw<E(%;J*bTdcX+DVasLSPP0W^Ga^9<QJso-QtHC5?_#+oSl<;izTHr zw?LEi7F&K&R%&v|EzY#eoYZ)*IqYSrMM?R^skfL?@@_Gvtz;+?W?*3W<?d`16Iz^F zR2)-MnvojglAm0fo0?Zr98;Q?S(09qn35V_P?TAgSdxlGBtAYfFS8^*Uaz3?7B|?N z@#RICC8;c+&=vxPKO+w#4<i>N3j-G;52F+#3qz480|P@cBS?;cfdND_!J-EgPv9tF zs$pmb#U`VlCUX%e28-Aj7#NB;7#J8d8H@NqY>?Z*9w-uFU|_h#1F;+uRD2-YL8dY= z6^S9+1`^|j*=B}F3=GYTMS3O7H4G_?%}o6QwTvY!S*)OFPhsw53Wi2;9t$X9L0FT; z?-o;0QV}SPX)+dpJgdou=FTFJJw+fN-D1hfPfXEdg18$LC=hppeE@bm7sT;Jsfj5d z!D5g<8JIu;C&pYP3HEe|CM(1Okcr?R1p5pui)0)~J0w6jY;yBcN^?@}Kp_k^k%N(g JiHnPa6#z2E#76)C diff --git a/unitgrade_private2/__pycache__/__init__.cpython-38.pyc b/unitgrade_private2/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 6e9fc2e8e234b8d82d001a785d86af68821340b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 918 zcmWIL<>g{vU|{e{XHU>!W?*;>;vi#Y1_lNP1_p*=Jq8Ab6owSW9EK<e%^1a)!kNOH z!qUPR#gxLD!q&nN#hk*P!qLJI#gf7p%%I8j5@eEJNHWL}5EF!185kHi85kIxL58L= zFff!bWHByas$pEnSj!A%GuJRLWGq%HVOYQdVlgt*Fcle;u%<B9Fg7zWGL*2@Fs3jy zGxdwrvXroAanvx>uz;M=%go3S&S1(A$PmO30n(FH!kWU`%*e=)!Vt`$$>vuj5MGp7 zl9`vTkeryDk*cSscZ;b!=@v^)Rc_)+##`LU`MCu}sl~;a`FT~WF8O(>ikh6anDUEn zu@oehWZYs;$t=oC%uT(;mRgZnTvB|CH8(pYv*;FUK~ZL2$t{-rg4DcQ{4hh}3o?_l zb5d`yq?G0sXtLg7%TLNmO)j~`nU<N88V@#yy)3mTDZe=N7E?;zEylE!3`HUg3=F^A zovmU*i&Kk=V@gUhQe#~5lS^|`^Gb?iO7k*H(u)#PQsWDXGRqQ6Qn84{$7kkcmc+;F z6;$5h275EUyeP9Il?4>qLZI+x<YDAt<YHuD;9}%qlwxFIC=z2}U`S>J$w4uQ4T>^w z6!9RUh+!cpRvG;?nTyyM7#ND!K?ElQ1A``GkpPGdazEG;MWPH047Ye74uAv~AIJqv zARCy9#L;a8iG$K1*hVK58;h(;m}?kP7@L{;Woj8qSh83_(VxQH%M{MQ1dZ;+48hQ7 z&tm~aHVA97_}yYEN-6?*qzL4FO*S-F7lFK1#0zpMOHO`biY61p7s6P)!Uge4QEFle zNV=E<>=95<Dlr#HfrB7KlNDkC$V70Ug8d1Wg#-#(@Nn4V=BJeAq}qYP8*Cy6BL@=~ H7Y8c<77fPY diff --git a/unitgrade_private2/__pycache__/__init__.cpython-39.pyc b/unitgrade_private2/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 7bf2f7227e54c7b2301a6dc324a1119b37119103..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 944 zcmYe~<>g{vU|@)L(@Yd(W?*;>;vi#Y1_lNP1_p*=Jq8Ab6owSW9EK<e%^1a)!kNOH z!qUPR#gxLD!q&nN#hk*P!qLJI#gf7p%%I8j5@eEJNHWL}5EF!185kHi7#J9wL55Z_ zFff!bWHByas$pEnSj!A%GuJRLWGq%HVOYQdVlgt*Fcle;u%<B9Fg7zWGL*2@Fs3jy zGxaOfvXroAanvx>uz;M=%go3S&XB^u!ob4N%*@D;Ct%1>oM6Df2!@diU~|e!SW{S= z85tQ;7=jrz+5D;m!izFXGV{_Ek`t3NQuXxoZZVZ7-D1h9%1vC!c#AtZKewPLwYWGl zKd*|_B|k4!QIqo)Q-1L+mV(5Rj9cs}nMHYtxv96<QY$ixONwu?=4Pj47TsbkD9X$$ zxy6!SkeYXkA7)5=L1uDxPU<a|l+xS+P1aj%`AJ!+$tAZq(=u~X<H6>zm!%dZ<rk;k zVoJ%o#hA8|p-6;*f#H{{enx(7s(wjnMykF`esXDUYF<gPeraB2NqSLYN@{#TQD#|U zNh+p@k$!x9W?p7Ve7s&k<t=WoC*#YDGD}ifK;h2^iV;R0Mjl2kMm|O!Mm@$NF$M;P zWJZuU6oc5H2n0tM4<f=C7J}lK(NB}Ph>d}Pp@<zsa56A3XfhTFfY>0HgPmU_%D}*I ziw9ykB%nYR7c+ruU@8(vw-F=`Pa=LOHWs;+FxN1oFg7#w%hfWLuw=1<5<?1eFH<-J z6H?>{GZ-=yTOmh&9t$YSL0FT;?-o;0QW3~gMWA5NWJB{o5y+cGydYPz<m4x&Xfi>3 zB#gydTo7*+r6#6;q>DigXJ7&asUCBY6gW6SG+7}QfJ_7jFxbyvSx5k(1r>))ZhlH> UPO2R!?7=2-Fmf<)adEH$04poUJpcdz diff --git a/unitgrade_private2/__pycache__/deployment.cpython-38.pyc b/unitgrade_private2/__pycache__/deployment.cpython-38.pyc deleted file mode 100644 index e5ef1a82ffe3b183639daf4d1197711d94e8aead..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1392 zcmWIL<>g{vU|>*-QcF~2Wng#=;vi#Y1_lNP1_p*=I|c@Z6owSW9EK<m&6LBK%M`_w z%N)hb2;wv5utc$d<zX~y6l)4=3Udle3u6>p3S%&XCfiGp$$pwlw?uMNOEU6Pio-KY zGF(!V^NSKo@{4Y<WMrnKCNqH)K`|c#0|O@m0|Uqv#Yv0|3^feR3=0?*GSo7aFxD`o zFg7#w3)M2$FfU+Q$gqHUAww-oID;ueAVUyC1VcJQCqoTG7K;l*Y)A}KEo&`X32O;k z4QmN|GgC8TEqe{S3q!0$EprLS0?r!dg^V#wwH&paC0sQe&5Sji;taK19SkL`CEPWf zDNGU!HC!MPq|=<Clc6x6(5!^Dogs}8>_d(co*I^BMi+)=#%9J^mK4?nyfrKf8EaW; z7*iOsnTk%-Fr_f0u(dKtGNiDJGt@Gc@MZBY5U62X$XN6W%oeO+s$pEnRP?GuD21b$ zv5B#Sv4$~)vze)10PH_uka>&@2;bE(WQo*r*KjWoP2pO|$jDH`ox)JVy+91a1KYzT z&Hy%99Apd3c5#MU?h=Uwk~Q258Ebh;q-uC-7_y|BnQD1!7;AXbn1UHJx&3Z27QJL- zU|>iF5zv?bM*#~11A{OqF~~45Fr+ioFvN<)Fx4{FGSx74Fl4hAvD7e4WGZ9{W?0Ek z#KyqDpvioTNzdRGWA-h^oRy4GJUN+psm1Xn`SC@mxnKpqyqv9KLW@(2iepMjGg4z* z@{>z*Q}arSV@mTfOVW!HQ&Qs#iZaU*OHwgKjABw!3v%)+K^pW5DsOSw<YX3?B<JTA z*olK23334&0~-SeLzQMxYHog6YJ5g!N=j;8JT&#`+2rIWC*~B}=^=F9V&r=H_y7O@ zRXp)|`4HuL1(j8D!I`<4If+FIX_+~x3I&NJ8N~{jDXDoSnQ57+DT=ok%WpC2Y0BJU zjRz^X#gbo;ns<w(C^a$V7FTgWPG$+%>$f-yOH+$WGV}9_Z?S+JTLen%MWEDui#;>1 zxF9vT<Q89gYDsZ^X;E@2L=SULYThl@qQt!P)LWdXd8N6jMTsS;w^*|BGxKgS<rm*# z0XgRuTWUpSaY^wlj^f1JRInpBT`Q7P3qZErVl6MqEJ?k^6(66QpHiBW8Xtd)B`q^A z<rZrZnBquEEKbc!%uT(;nVDOVUsRHlnRJU6!h~wM#h#v8l2}qwbc-WC9;7rrUX$+@ zcTg%wxpPipaq%to{L&Jz%`9M&G3^!`#3Q#D18*_LM=|9T-(oDj#adjFSX6S0IW;dO ziZvHx)GaoMv!Vn&z!8LGC^(rEfy$^NZUzR1TcTKFk^>aq2B4(M#Kp+ND8|IX#K*|T z$ic|R$ipbdB*n<Z#K9=QB*n<V$ipbY#Ky?QsKvy^#KTx*21=%kewyq>pfqz!6rS#k v^bkx$G!@A*FfbIUg9wmLi0eQJ4Z;FDk;4Y!8#_?;DF!*6gHeEy0|c1?W<P?= diff --git a/unitgrade_private2/__pycache__/deployment.cpython-39.pyc b/unitgrade_private2/__pycache__/deployment.cpython-39.pyc deleted file mode 100644 index a1183907f15d5618aa6e7584380d3fad693ad2d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1425 zcmYe~<>g{vU|@)L(@gxu!octt#6iZ)3=9ko3=9m#b_@&*DGVu$ISf%Cnkk1dmnn)V zmpO`=5yWTAVTob^%fo2aDAp9#6y_9`7RD&H6vki%O}3XHll?TAZi(ckmSp6o6o+S) zWVoaz=NBcG<QLsy$;eDeO=bcqf?_@f1_llW1_qETihCFt7-|@r85S@sWT<5-VXR?F zVQgmVm#Jm0VP3$rkYNGyLWWwFaE25H76ulEW@bi)JOM+7Vlx8<Mlg(IU}Q*V=wzs2 z$YOC}hz*Hhs%5QZD`72Rt6?o+Z)R#{tYxoZcVURNsAVqUSio7sypS=5sg|RbvxKXL zqnWXWQ=FletAnA0wS>EdGlfZlp@s`Yf^?cQbTSmC6nd4gwlkzLf_=?V!c)W2%;>_< z%-GCW%aX#nfVYNaA!98|4Py#JHdE1^8m1J66t-3-Nrn`5afVvP622_{1p+mU3mJ?4 zf!TsJOf`%PnTr0E2&Hf|Gd3}nFxD`pa5gjbOM(3?3^I?A0ma8ch71!Ji?xvbT*Hth zQp;V#y+AaDYat^eLk)KdLk;%=F%S=IJC`^E*gkQP-7q(aGt_dINGy=7;a<pC%Tpp% z!&AeMCEd(a%Ui=(!<)tw%%I8bcZ;#;B_jg^Lo$ef#u+%WSQr=>gh9zhhJk@0ouP&y zRwRb0ma&$phOvX8jIoHNhG`;GAxkjBN`@jf1_lOA=37j92DccqZ!zYqWQ^j;$;?YF zjxWiNFG|e?EBNKEpOK%Ns$Wu?k*e>KpIn-onpaY+Uz(R$l3tXUk{Vx7lv$Qol8Px} zq@R*nkdt2t(x6vRd5g;?C$qRDIX}0+PMm>(;WNnDRhmVqx%p+O@fn#ZDXDq!(B!FS zlarsEm{V-0htPA2k?ZB(|NsA2@x<rlLzL?kR949aXXa++Bo-;8W#*(R6eN~p6f0z= zq~?`mre&t4DBfZ$zs0DhDRYZ89;DzFOMXFW-Yu4*)WnoqT*U=BnI&K!-{LGRO)W0T z%+D*n#R76>5hynlfs+3%_RPHEg4E=aTYTxMCB^xrMaiiUJ<K_&dAC@L67$kiZ*ivP zmFA`vC6=V#V#&(S%)7;uUwn%N<eXb<sTG;UCB?TmiW75F!H(c`tw>HS0NHknwY(^^ zB=r_oe0*+xN@-4NeEcnzw9LGeTdYN3iX$npI5jUZH}w{0W^O@#QAti_(k)&H6RPDF zdwOa~Vo6ESEspqjkka^gO}<;)L8&0+&N+$2#kbh=OH05uvw%s)v|DTtkKAGmyu}zF z#gtQgi?R3?YjH_pQOPal)V!1^)?AQLx7Z-giW2kyM-Y;s;M7qBD#VJo85kIDiDHdO zP$5!m0E%iRE=C?kEhZKwK1M!94n{sk9!5DPDMl_P4n_ecDMk*kEE^*iqZShv6AxpN zIVg29`f0KkNq~)qr#K@$1QQWWMRE)b3`H6s0;Ch-I#3FSu)t2_uz~o-4wP$(K~Cpj K6ky~4L1qBdhlc9_ diff --git a/unitgrade_private2/__pycache__/docker_helpers.cpython-38.pyc b/unitgrade_private2/__pycache__/docker_helpers.cpython-38.pyc deleted file mode 100644 index 64a1893f40852651e6f4f6145d3b8ddb4b5e6dcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3165 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 diff --git a/unitgrade_private2/__pycache__/docker_helpers.cpython-39.pyc b/unitgrade_private2/__pycache__/docker_helpers.cpython-39.pyc deleted file mode 100644 index d8bd23d3e204c918fa1d3cc8d5f223591f47ac4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3217 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 ar8Fni4iuKfptQjOvXTW%GIB6+1OfnSNKQBa diff --git a/unitgrade_private2/__pycache__/hidden_create_files.cpython-36.pyc b/unitgrade_private2/__pycache__/hidden_create_files.cpython-36.pyc deleted file mode 100644 index c10d3e26f4209a3cb1de8b9a8cbcd399dba53b33..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3954 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 ZO8Uh@3=9mArZ}iM&cnmP#KF(O0RR!6M3evk diff --git a/unitgrade_private2/__pycache__/hidden_create_files.cpython-38.pyc b/unitgrade_private2/__pycache__/hidden_create_files.cpython-38.pyc deleted file mode 100644 index 495660ae33400dda27e6cf64c7f939036a688a98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4588 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 Y7mF}3FbFVmKp_t!2NMeqhcX8T05d7!MgRZ+ diff --git a/unitgrade_private2/__pycache__/hidden_create_files.cpython-39.pyc b/unitgrade_private2/__pycache__/hidden_create_files.cpython-39.pyc deleted file mode 100644 index e9bdd98457f31c4fc351bf81ea01a6d12fb3d531..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4665 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 diff --git a/unitgrade_private2/__pycache__/hidden_gather_upload.cpython-36.pyc b/unitgrade_private2/__pycache__/hidden_gather_upload.cpython-36.pyc deleted file mode 100644 index 7aa51f153e94d74ee8abd09983ea96ac0bc500f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2775 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 diff --git a/unitgrade_private2/__pycache__/hidden_gather_upload.cpython-38.pyc b/unitgrade_private2/__pycache__/hidden_gather_upload.cpython-38.pyc deleted file mode 100644 index 07472b0de4bf0b0f74be03f08a47140647e9cb27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4107 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 diff --git a/unitgrade_private2/__pycache__/hidden_gather_upload.cpython-39.pyc b/unitgrade_private2/__pycache__/hidden_gather_upload.cpython-39.pyc deleted file mode 100644 index 5c2e1ae6b68171c81e7ddf38fb9d45d3f1968a79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4210 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 diff --git a/unitgrade_private2/__pycache__/token_loader.cpython-38.pyc b/unitgrade_private2/__pycache__/token_loader.cpython-38.pyc deleted file mode 100644 index 31836c22ea1ea42dda4ca41936fce6eb9ec9f4b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 978 zcmWIL<>g{vU|{Hd%9n70g@NHQh=Yt-7#J8F7#J9edl(oPQW#Pga~Pr^G-DJan9mf& z1g4pzm{XWim|Ga4SW;P2ShAUlI>6#=QEXtEJ(q)tfsrAXGm49mA(bnOn~@=vF^gvb zFNnR6F^VshVF5!bUka-@!$QU={#5=H21$k}fmFd%fo4WVhEx_19VL{)7A2g*7|fu_ z{u1O)zhp)b1;uO(3=EtM3=Ga77uzu~FqAMXV60(CVO+@6&sWP>!?=KHAww-wID;ue zAVUyC1Vai_3PTQaEprX?0+t%4g^aZ<Da<J>Eey4+H7vmlnyh}en2Jh@KvtmG!^*(G z0J19>WRDF40|UqoNrnXs3mIG(VzpwJY8h*pYM4@(;9?>%Ots9lEG3LJj5W+PEX~Y} z4224XJShyp44TY-x7brsOA<44if^%ImZatuYcdsq0{#|jK~ZL2i6-kU=AzW%Ta1NK zEQMu>Iky<IZ!wmwWGDhf-7jxvtC-N@)S}{;lG2RS7?=Fy(%jU%lH!=syv&mHqQsQc z_=2L$vc!^9EFvZO*{ON)Ir)hxsYQAPmA6<5ic*VtLB0ZckAYEyNrb6N0wf;~R;p){ zlb@WJQ*5UPQmDy!i#e&v=oU+UL2BMDmZH?elv^xW#rb)+SV3BfZ?WWL7MI*&E>11E z#hjCxR|E<-O}1NHV6)=WGILUIF{a&OEG`mYU|=W$Sy{vbVu3sc_FWM-0|Ub?E|6&u z2Y}QRgVZoE@-YfAaWHZ*aYz(NfK+kB$LA(y=EcWX@#$-&6_sX|Xz771%B<&QV9*q~ z#gd+rpLB~UzxWngL1uDxPHGXzr*O@AiMgrq@wZqqi*k~q6hWk3VsUY1dLB49k=XHI z!*6jT$wmnvI8bS(w8UE+@nBDaOf5(($+*Ryl34_@<`z>*-Yv$QB2kcML4k6Msi^oC vV<N=QXujvL$<0qG%}KQbCBtGF1_lNWMlfXJPz2FJjBLz&One|QCJrV5AN%-d diff --git a/unitgrade_private2/codejudge_example/__pycache__/__init__.cpython-38.pyc b/unitgrade_private2/codejudge_example/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index ae23c7f18121d96a18d55cdeddffe576411baec2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181 zcmWIL<>g{vU|^VDDVGSMAA<;F%*epN;K0DZP|U)>z>vZa%%I8Wx00a<B#a<_MLAo= zgche36~~m6W~9cr<R_QrrskCt$CTz}mZTRYrliIf6lInrmZV~e7{w&#r=(_;rlhCF nr&c897UZPH#K&jmWtPOp>lIYq;;_lhPbtkwwFBAt8RRwq*g7#H diff --git a/unitgrade_private2/codejudge_example/codejudge_sum.py b/unitgrade_private2/codejudge_example/codejudge_sum.py deleted file mode 100644 index 8a43e38..0000000 --- a/unitgrade_private2/codejudge_example/codejudge_sum.py +++ /dev/null @@ -1,35 +0,0 @@ -# Implement https://www.codejudge.net/docs/quickstartfiles#the-problem -from unitgrade.unitgrade import QuestionGroup, Report, QPrintItem -from unitgrade.unitgrade_helpers import evaluate_report_student -from cs101courseware_example import homework1 -import random - -class SumItem(QPrintItem): - ls = [] - def __init__(self, question, *args, **kwargs): - super().__init__(question, *args, **kwargs) - - def compute_answer_print(self): - random.seed(42) - - from unitgrade_private.codejudge_example.sumfac import sumlist - return sumlist(self.ls) - -class SumQuestion(QuestionGroup): - title = "Sum of two integers" - def __init__(self): - pass - - class FactorialQuestion(QPrintItem): - n = 3 - def compute_answer_print(self): - from unitgrade.unitgrade_private.codejudge_sum import factorial - return factorial(self.n) - -class Report1(Report): - title = "CS 101 Report 1" - questions = [(ListReversalQuestion, 5), (LinearRegressionQuestion, 13)] - pack_imports = [homework1] # Include this file in .token file - -if __name__ == "__main__": - evaluate_report_student(Report1()) diff --git a/unitgrade_private2/codejudge_example/sumfac.py b/unitgrade_private2/codejudge_example/sumfac.py deleted file mode 100644 index f429a12..0000000 --- a/unitgrade_private2/codejudge_example/sumfac.py +++ /dev/null @@ -1,6 +0,0 @@ -def sumlist( ls ): - return sum(ls) - -if __name__ == "__main__": - sumlist([1, 4, 4]) - -- GitLab