From a6734a9a62c7c74b550cc0097de25a9c87a67270 Mon Sep 17 00:00:00 2001 From: Tue Herlau <tuhe@dtu.dk> Date: Mon, 13 Sep 2021 15:31:15 +0200 Subject: [PATCH] Updates to readme.md --- .gitignore | 138 ++++++++++++++++++ .../__pycache__/__init__.cpython-36.pyc | Bin 149 -> 0 bytes .../cs101report1_grade.cpython-36.pyc | Bin 2656 -> 0 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 152 -> 0 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 160 -> 0 bytes .../__pycache__/cs101report1.cpython-36.pyc | Bin 2741 -> 0 bytes .../cs101report1_grade.cpython-36.pyc | Bin 2703 -> 0 bytes .../__pycache__/homework1.cpython-36.pyc | Bin 1189 -> 0 bytes .../__pycache__/homework1.cpython-38.pyc | Bin 1208 -> 0 bytes docs/README.jinja.md | 97 ++++++------ .../cs101courseware}/cs101report1_grade.py | 0 .../Report0_handin_18_of_18.token | Bin .../Report0_resources_do_not_hand_in.dat | Bin .../cs101courseware_example}/Report1.xlsx | Bin .../Report1_handin_18_of_18.token | Bin .../Report1_resources_do_not_hand_in.dat | Bin .../Report2_resources_do_not_hand_in.dat | Bin .../cs101courseware_example}/__init__.py | 0 .../cs101courseware_example}/cs101report1.py | 0 .../cs101report1_grade.py | 0 .../cs101courseware_example}/cs101report2.py | 0 .../cs101report2_grade.py | 0 .../cs101courseware_example}/homework1.py | 0 .../cs101courseware_example}/instructions.py | 0 docs/mkdocs.py | 1 - setup.py | 7 +- src/unitgrade.egg-info/PKG-INFO | 20 ++- src/unitgrade.egg-info/SOURCES.txt | 5 + src/unitgrade.egg-info/requires.txt | 4 +- src/unitgrade.egg-info/top_level.txt | 1 + {unitgrade => src/unitgrade}/__init__.py | 0 {unitgrade => src/unitgrade}/unitgrade.py | 0 .../unitgrade}/unitgrade_grade.py | 0 .../unitgrade}/unitgrade_helpers.py | 0 {unitgrade => src/unitgrade}/version.py | 0 src/unitgrade2/__init__.py | 70 +++++---- .../__pycache__/__init__.cpython-38.pyc | Bin 1306 -> 494 bytes .../__pycache__/unitgrade2.cpython-38.pyc | Bin 22740 -> 22788 bytes .../unitgrade_helpers2.cpython-38.pyc | Bin 7000 -> 6853 bytes .../__pycache__/version.cpython-38.pyc | Bin 171 -> 171 bytes src/unitgrade2/unitgrade2.py | 53 ++----- src/unitgrade2/unitgrade_helpers2.py | 32 ++-- src/unitgrade2/version.py | 2 +- unitgrade/__pycache__/__init__.cpython-36.pyc | Bin 998 -> 0 bytes unitgrade/__pycache__/__init__.cpython-38.pyc | Bin 1316 -> 0 bytes .../__pycache__/unitgrade.cpython-36.pyc | Bin 11636 -> 0 bytes .../__pycache__/unitgrade.cpython-38.pyc | Bin 14886 -> 0 bytes .../unitgrade_helpers.cpython-36.pyc | Bin 5594 -> 0 bytes .../unitgrade_helpers.cpython-38.pyc | Bin 8338 -> 0 bytes unitgrade/__pycache__/version.cpython-38.pyc | Bin 166 -> 0 bytes 50 files changed, 290 insertions(+), 140 deletions(-) create mode 100644 .gitignore delete mode 100644 cs101courseware/__pycache__/__init__.cpython-36.pyc delete mode 100644 cs101courseware/__pycache__/cs101report1_grade.cpython-36.pyc delete mode 100644 cs101courseware_example/__pycache__/__init__.cpython-36.pyc delete mode 100644 cs101courseware_example/__pycache__/__init__.cpython-38.pyc delete mode 100644 cs101courseware_example/__pycache__/cs101report1.cpython-36.pyc delete mode 100644 cs101courseware_example/__pycache__/cs101report1_grade.cpython-36.pyc delete mode 100644 cs101courseware_example/__pycache__/homework1.cpython-36.pyc delete mode 100644 cs101courseware_example/__pycache__/homework1.cpython-38.pyc rename {cs101courseware => docs/legacy/cs101courseware}/cs101report1_grade.py (100%) rename {cs101courseware_example => docs/legacy/cs101courseware_example}/Report0_handin_18_of_18.token (100%) rename {cs101courseware_example => docs/legacy/cs101courseware_example}/Report0_resources_do_not_hand_in.dat (100%) rename {cs101courseware_example => docs/legacy/cs101courseware_example}/Report1.xlsx (100%) rename {cs101courseware_example => docs/legacy/cs101courseware_example}/Report1_handin_18_of_18.token (100%) rename {cs101courseware_example => docs/legacy/cs101courseware_example}/Report1_resources_do_not_hand_in.dat (100%) rename {cs101courseware_example => docs/legacy/cs101courseware_example}/Report2_resources_do_not_hand_in.dat (100%) rename {cs101courseware_example => docs/legacy/cs101courseware_example}/__init__.py (100%) rename {cs101courseware_example => docs/legacy/cs101courseware_example}/cs101report1.py (100%) rename {cs101courseware_example => docs/legacy/cs101courseware_example}/cs101report1_grade.py (100%) rename {cs101courseware_example => docs/legacy/cs101courseware_example}/cs101report2.py (100%) rename {cs101courseware_example => docs/legacy/cs101courseware_example}/cs101report2_grade.py (100%) rename {cs101courseware_example => docs/legacy/cs101courseware_example}/homework1.py (100%) rename {cs101courseware_example => docs/legacy/cs101courseware_example}/instructions.py (100%) rename {unitgrade => src/unitgrade}/__init__.py (100%) rename {unitgrade => src/unitgrade}/unitgrade.py (100%) rename {unitgrade => src/unitgrade}/unitgrade_grade.py (100%) rename {unitgrade => src/unitgrade}/unitgrade_helpers.py (100%) rename {unitgrade => src/unitgrade}/version.py (100%) delete mode 100644 unitgrade/__pycache__/__init__.cpython-36.pyc delete mode 100644 unitgrade/__pycache__/__init__.cpython-38.pyc delete mode 100644 unitgrade/__pycache__/unitgrade.cpython-36.pyc delete mode 100644 unitgrade/__pycache__/unitgrade.cpython-38.pyc delete mode 100644 unitgrade/__pycache__/unitgrade_helpers.cpython-36.pyc delete mode 100644 unitgrade/__pycache__/unitgrade_helpers.cpython-38.pyc delete mode 100644 unitgrade/__pycache__/version.cpython-38.pyc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5391d87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,138 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ \ No newline at end of file diff --git a/cs101courseware/__pycache__/__init__.cpython-36.pyc b/cs101courseware/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index d33bfab045b3ca7b901756260b46811e19f99be0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 149 zcmXr!<>gv6<9Iv+0|Ucj5COxC3=9ko3=9m#EDQ_`DGb33nv8xc8Hzx{2;!HMvsFxJ zacWU<Oi5`*YK%*Ma%paAUP*C`k%5sxOle+bNqSLYN@`4Uv7v!sa(-!1acX&DQEE(l dd}dx|NqoFsLFFwDo80`A(wtN~klDo``vG3xCJz7r diff --git a/cs101courseware/__pycache__/cs101report1_grade.cpython-36.pyc b/cs101courseware/__pycache__/cs101report1_grade.cpython-36.pyc deleted file mode 100644 index 8de424470b4aaa51585fb35660bf3c8d4221dcf7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2656 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 diff --git a/cs101courseware_example/__pycache__/__init__.cpython-36.pyc b/cs101courseware_example/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 98d393f34d0bda15a5bea3d0235370d3a1f2b301..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 152 zcmXr!<>g8_bR(XDfq~&Mh=5^61_lNP1_p*=76t}}6oz01O-8?!3`HPe1o6w&*(xTq zIJKxarld3@HO3`BximL5ucSDpG%vFxy(lpyH72>((7-S`zqF`0wLGyXH9oZ>F}ENm gH6}hjGcU6wK3=b&@)n0pZhlH>PO2Tq_+pR~01(h9G5`Po diff --git a/cs101courseware_example/__pycache__/__init__.cpython-38.pyc b/cs101courseware_example/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index b4b9f0b3284b408c2767cd2e0f124b2b7a70cb86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmWIL<>g{vU|>)aIhFvTAA<;F%*epN;K0DZP|U)>z>vZa%%I8Wx00a<B#a<_xjI|L zgche36~~m6W~9cr<R_QrrskCt$CTz}mZTRYrliIs7aJNFCg+zH6{nUb7Ny3gRwU*Y i<fO*L$7kkcmc+;F6;$5hu*uC&Da}c>0~!Ar<O~3!*eBNj diff --git a/cs101courseware_example/__pycache__/cs101report1.cpython-36.pyc b/cs101courseware_example/__pycache__/cs101report1.cpython-36.pyc deleted file mode 100644 index b66271bfa506d026fd4bbe74cdca575612225b1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2741 zcmXr!<>lHl@kYEk8w0~*1|-13z`)?Zz`#&k#K6Fi!jQt4!;s4u#hA+!#gxk&#mop2 zW6EL4WsPD5vzc?)a@nKU85!IeQdm-0TNqMUQyH6?qd43dQrJ@1TNqN<!F*14h7^tz z&K8Cg&Qzvm<|wXI?i8+UrlP!5)>N)$hGs@ah7|5#22GxqAjfDj-{K7{O)W0T%+GT# z$}cUr#TJxWkY7}Ciz_goC^N6bvm`ZFlkt{#YFT1VX<|ugd=XeWzPO|`B{i=^lkpa3 zMt*K;d45s0VKT@>7>4`LfPsM_l_82Tg&~S5g)xfRogsxOg}H?xg*la_nK_CT$v>>k z%u#G9Y{3kg?6*XGGK))sQp-|{iW75SPOK7ui74c!Ddc1pm!R0m3JX&u6gxror7)(j zq_ajbr?955wJ=1nq_77wXmV7kVzWgLSr8NeFBusa7+x}i2#^(-NlXk3$sjS1XPFon z7(mz=6q$0M$Ye}m0!3ymBO^lzQwlRE$ZHsD7_yj~8G{)#S^SDXS~VGO@f3l*ml_Z9 zNEMe$X|6&_qC!eyrY6fR#pGf`1H<I}(xT$j^2DOl_|%HT+=84`J$PsraWgP5++r;N z#nLUt98IQMEXAogX<*@%3`GJA3=F?KovmU*i&Kk=V@gUhQe#~5lS^|`^Gb?iO7k*H z(u)#PQe$w~0}+QrwxM1@<t>rq{M>@llGOObyyEiIqIj^qTp&NQfkJ_ihmnJ^$_hE; zV3CFqoO;-d^3!C!#StH$mzbLxAAgH0K0Y@;r8Eb`=82ClEKSUT$Q1E0FfbGefe4Tf z!QO-rf}r3KKz0Bq>Nr3y1f@j|Mn1+OF$M+(loSJ!0p~&q%oM|t&H_p=!3>&gRa(gA zV)a;feo;y>$i^tvjMSW*{3zD){GyzcD7NC#+}zZnDCU&>bU#g&B4LozML>iohyZ&B zn@>cMZG;=n5AqHuwQ(>Oi8C-T_-V4EnRtt}B(o$ZwFu-huz$hoAVGw%2NYOE><kPH zp!8e}ia`!W7A669uo(6bA~PRL_+;j#CKd&yrWd6a7iZ?@!9uS}1tOzR1ea1s%u7*l z$}cX-&r?WAEJ-X*Ma|zJkAq`N4Jo!#nX_1;SW_6I*g*Nbg&~R^Qa(m;KuVG-O^7|1 zF3@w%Pfbh9OwI&l2~gCf?~rSK9k^qjJ@(8EN<Aq^nY)IunE_Nb2Qz3g!!tE+64?Fm zIbd&VG8OTFqFIW8fkBh02o!xqAU0BbfTIGGpg~En7!+*`j6950c0{`xYj#Qo8^plC z0HQ%97r1x=xuu3Vg&~Ehl}VBT;%-n5dHMhU|Nme&X|jMFd5gIywfGiuNkz#m_Po;E zq|~Bfur!1KIr$b}K~a8kYH@LVerZWTX^9LhWil{USrG4bR73nUInk0cI2}iEBo-H^ z7M1uIMX{Hp7MG-z=0!1=<mW)j0eMgWBa$|l1$LnTdi;X2T`|bN9E@y?EKD3MMGEKz z0z8R<)FP^_6s8u2DCQJqQ2AY@h|_yPzQL}bgoG0MAS1z{53;$2IRz!;i=<($hlYF+ zC?JbuL873_1QOtIKNW#I3vzTZ$W#W#Dg%OU(L)bnj4}vhCXzjH%WiSN`~y+}wvmIe zND*tXQv^!A;K%}pDOeRYGm9h{7#KjQ9aiWt3UGtPPzoJTg2l+_pvFTgOA1pOQz~l; za~e}RQxsb&dpctjM+!?YgC^@Oc1Y93po-r)Si#W1PyxbMFf0PqQ!jbJ4#!AfV26W> zLa;NUI18c8q2eM?u^7cukeHkupP3746d~sUcqkQt8XGF0)DKe6!N|fWzz7xtRcE=0 znR)T?ewsW{!tg3r56&tAMPd=iv*3`6l7`D5)Wm0`<`jUkAE=~`630<pgWU=)yurQz p+YiY}V0R)2aK_-UfmE(`poT~>s0tEc<YDAt65tgO5@6+E1^`6Xc~t-a diff --git a/cs101courseware_example/__pycache__/cs101report1_grade.cpython-36.pyc b/cs101courseware_example/__pycache__/cs101report1_grade.cpython-36.pyc deleted file mode 100644 index 6f0848e6a8e76af9b5877f00a49b72f484752463..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2703 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 diff --git a/cs101courseware_example/__pycache__/homework1.cpython-36.pyc b/cs101courseware_example/__pycache__/homework1.cpython-36.pyc deleted file mode 100644 index afa293b6628d6b283bb49fb86c7b3055fb97400b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1189 zcmXr!<>gv6<9K{4D+9x01|-15z`)?Zz`#(Pz`($e!jQt4!w|)o!jQt0!<5S$#mop2 zW6oj8WsPD5vsrT3a@nKUb2*|o7#UJnQ`lM<qBv97Q#e`}qPS8xQ@C0fqPSCeQn<63 ziegf^ni&`wQn^873S%&XCeKTd)qa|cx43ii6I0@o@{3FI^E4T63HW5@r6v{yrKT69 z78hsc=V>zC63R_Y%!@BBEKMv*O^Ht}D#|aq#ZhDwU!0s@l$y*4GKqnKff<UOL7q@x zU|>jRsAa4Wt6^|qh~=wgs$oiJtYH*qsAVkV3TLPRIfBt|C4(k&6kBd(PG)h*Ew-Z6 z;?kUwTa4L7YzzzxVB(jvvsFxJacWU<Oi5`*YK%*Ma%paAUP*C`k%5sxOle+bNqSLY zN@`4Uv7v!sa(-!1acX&DQEE&^er{@ceo?leUP0w8o}$#U)S}|lc#u6@Aop>A9L>bS z$i>LVSOgMD1_^*L$U7kG!QRkfU|=X=NMUSdWMoKTN?}f6NnuTAsRfC#)i9*6moU~a zG&44{fMO(=L6gI;N+%>ERRL<90?0arw9>rflFa-(h2+FMxJ)8g?j^{wm!Oz`2@1WJ zEDQ_`Rh&MV#U%>miN#h`0#Jie6hO|0L<Va?QD$BV*v*=Zw-|H4!2}^V85kIDi4<q% z7UV#@8edqNS`4yV5aef&uNW9P7<m|rKzz85Ss54@KsJMYoCWo93R5p*3{x#bEn^L1 z31b#h7IO__7E3c@3bP1<IYTXD32QS0C{nZ7Y8Yx5o0%9HO4w6aYM8P(K<pZ(EKX3! z)H0VaW^t8p*D%&FH#0Re*0Pkar?A$rfYhWg1T$!|`GsgQMsXFTmXsFd#Ye<frdNxs zbAMRXVqYcToS&MOmYJLhif)C(qSPw(Am3nDh0J12o+40~-D1isxW!hKn3s~Ddy5rB z=iOo{&a6r;0tMzR=CsU`B7O!2hA7tL{M5AgDE5M))RfHRl3Ogrg+(PrATMjO-eQcn z#aJ1|nUk3ppPQeOniIuY84pz$#Zr`8oLU45HwXa^U|vXGiO&IN6e&>13V;(1AEOu( z8zT=R4-*%o0AmqI<`zeMd~RZ9UVOYJ?=9B6(%gc|A|a5I1d6kBQWJ~v^imQ_5{pwy ziorgL5`hUr42QS~?42lnm{e|RNl|8UG1&b@ptu941#pzY?aWKeO^uI-I2z6Q95%W6 gDWy57cA&IZ3~~%8Mer~RuyL?+aB}c)FtRZM0CZy}L;wH) diff --git a/cs101courseware_example/__pycache__/homework1.cpython-38.pyc b/cs101courseware_example/__pycache__/homework1.cpython-38.pyc deleted file mode 100644 index 0d3386b94e926f441ec02b6e5452a23dc1d8f02d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1208 zcmWIL<>g{vU|{&hrk2>w%E0g##6iYP3=9ko3=9m#2@DJjDGVu$ISf&ZDGVu0IZV0C zQOt}WG3FeWT-GR7Fq<WZEtfrtJ(nYjgOMSHHHEE(A&N7FJ%yu%A&M)7Gli>#A&NVd zCxttksVF9utC@k3A(b0MrZ5IGX!5)S+3lyvc#AtHKQSdfDZjWRKTnhKmVi%YUTR`d zP-=QnYH@L9ex4@NEuq}h#Ju?8!qUW|)Rg$tqN4nwTO373@x{scMXAY*Ad{e&nSp@; zgq=Yikzrt9NN1>JtYL6rh~=wgs$oiJtYH*qsAVkVr~x^G(QhS#Ci5+}+{&EH;*wiz zMXAN5IVHCkvy0dm7#P6BFE?kan9$<XqT-m6(u~v?m;B_?+|<01;+WFB%#!q?#FW&S z<YGeu!{q$ZqT<x@#G=&r)QZI1f}GTtjQrfx^8BJ~L%o8^TRcUnWvNBQsqrAIKrvCw z0dg}F3nK?34^t600|P@cNC1XGz5=-d>=P{p28I%b6vk#oMurro6y_9`6xMW>T96o9 z4MPfh31baIGh;IgC}M&cG&%gLbV4#x6`)osfUHzVE6qzT$;{7FNKVXy%Ory3UV<!p z2}%GjL4o&@g@J*giqj{vxJ02mvDm6g0BTT*0>}%HIAJX)%FHVPyIYg-7Gn-Lv>*h? zceg}}GeLm>_HKM(X=*XZZXr;#fPBRO@-<@-h>zlHRt5$J5C;1?3+n3>re4Mvrdm); zGL|rAF)d)OVa#G#$e6+`!eGu&%UHtN%m9kr1#C48HH-_H7#T{~Q&?)4vN)QV7#V7q z7I4-uEM%-@E@522Rl;4vSi{`Rw2-lurG!0&wT1<xCWRrGL6gldM3eCrS5az7X;EH$ zM0{m>wa7a6hgB{1RRYfWscC7M$(f*-S4b>Mtzr-I4R%$?EY{>H0)^Wxro4h%Y(<HA zDfzj#SV45&EtcZUs?;J-fZk$G%Pc7pU|?Xl#hRR-nihYHy`U&HB{R9?7E5tqQArUf zz%^NKF-F{Cth~jUlbIKvo1c=JbBnVw9<Hz`w>T9NzMuds0tYfLB-_O2fb)zrD0D%# zFfj5liZQV<@-Xr+aWM)o7Ab<P=7^8aP0Y-TkJseA#hO=|TTodf0umP}&dy0qEXvbM zNi0b$PAw^h_(%jM3^5$yBCvO&_+e7HsU=03$;Dvz7lGmpoF2f@3b!*aF*h|n9?9hh l=X2QP=BJeAq}qYfUoj{#aDd#*D8R<S!NJMF!@<bL2mnPqEU*9o diff --git a/docs/README.jinja.md b/docs/README.jinja.md index 9563b1b..7fbbecc 100644 --- a/docs/README.jinja.md +++ b/docs/README.jinja.md @@ -1,83 +1,80 @@ -{{ input }} # Unitgrade -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. +Unitgrade is an automatic software testing framework that enables instructors to offer automatically evaluated programming assignments with a minimal overhead for students. + +Unitgrade is build on pythons `unittest` framework so that the tests can be specified and run in a familiar syntax, + and will integrate well with any modern IDE. What it offers beyond `unittest` is the ability to collect tests in reports (for automatic evaluation) +and an easy and safe mechanism for verifying results. - 100% Python `unittest` compatible - - No external configuration files, just write a `unittest` - - No unnatural limitations: 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 Docker VM and by running hidden tests - - Allow export of assignments to Autolab (no `make` file mysteries!) - - Tests are quick to run and will integrate with your IDE + - Integrates with any modern IDE (VSCode, Pycharm, Eclipse) + - No external configuration files or setup required + - Tests are quick to run and will tell you where your mistake is + - Hint-system collects hints from code and display it with failed unittests ## Installation -Unitgrade can be installed using `pip`: -``` +Unitgrade is simply installed like any other package using `pip`: +```terminal pip install unitgrade ``` -This will install unitgrade in your site-packages directory. If you want to upgrade an old installation of unitgrade: -``` -pip install unitgrade --upgrade -``` -If you are using anaconda+virtual environment you can install it as +This will install unitgrade in your site-packages directory and you should be all set. If you want to upgrade an old version of unitgrade run: +```terminal +pip install unitgrade --upgrade --no-cache-dir ``` +If you are using anaconda+virtual environment you can install it as any other package: +```terminal source activate myenv conda install git pip pip install unitgrade ``` - -When you are done, you should be able to import unitgrade: -``` -import unitgrade +When you are done, you should be able to import unitgrade. Type `python` in the termial and try: +```pycon +>>> import unitgrade2 ``` -## Evaluating a report -Homework is broken down into **reports**. A report is a collection of questions which are individually scored, and each question may in turn involve multiple tests. Each report is therefore given an overall score based on a weighted average of how many tests are passed. -In practice, a report consist of an ordinary python file which they simply run. It looks like this (to run this on your local machine, follow the instructions in the previous section): +## Using Unitgrade +In unitgrade, your homework assignments are called **reports** and are distributed as regular `.py`-files. I am going to use `cs101report1.py` as a generic example in the following, but a real-world example can be found here: https://gitlab.compute.dtu.dk/tuhe/unitgrade_private/-/blob/master/examples/example_simplest/students/cs101/report1.py . + +The report is simply a collection of questions which are individually scored, and each question may in turn involve checking several sub-cases. + +You should think of the tests as a help for you when you are debugging your code and when you are trying to figure out what to do. +I recommend running the tests through your IDE. In pycharm, this is as simple as right-clicking on the test and selecting `Run as unittest`. The image belows shows the outcome in Pycharm: + + + +The tests are shown in the lower-left corner, and in this case they are all green meaning they have passed. If a test fails, you can right-click and select `debug as unittest`, or you can click on it and see the output it produced, and you can right-click on individual tests to re-run them. + +### Checking your score +To check your score, you have to run the main script (`cs101report1.py`) as a regular python file. This can be done either through pycharm (Hint: Open the file and press `alt-shift-F10`) or in the console by running the command: ``` python cs101report1.py ``` -The file `cs101report1.py` is just an ordinary, non-obfuscated file which they can navigate and debug using a debugger. The file may contain the homework, or it may call functions the students have written. Running the file creates console output which tells the students their current score for each test: - +The file will run and show an output where the score of each question is computed as a (weighted) average of the individual passed tests. An example is given below: ```terminal {{out}} ``` -Once you are happy with the result run the script with the `_grade.py`-postfix, in this case `cs101report1_grade.py`: - +### Handing in your homework +Once you are happy with your results and want to hand in, you should run the script with the `_grade.py`-postfix, in this case `cs101report1_grade.py` (see console output above): ``` python cs101report1_grade.py ``` -This runs the same tests, and generates a file `Report0_handin_18_of_18.token`. The file name indicates how many points you got. Upload this file to campusnet (and no other). - -## Running the tests in pycharm -Naturally, you can also run the tests in pycharm, and this offers you a lot of cool features such as integration with the debugger and the ability to see which tests have failed. -To do this, simply right-click on the `report.py`-file and select `Run as unittest` (or alternatively, `debug as unittest`). This will take you to a screen such as shown below: - - - -You can see all tests are green indicating they all pass. If you click on a test you can see the console output it generates and you can -right-click on the tests to re-run individual tests. - +This script will run *the same tests as before* and generates a file named `Report0_handin_18_of_18.token` (this is called the `token`-file because of the extension). The token-file contains all your results and it is the token-file you should upload (and no other). Because you cannot (and most definitely should not!) edit it, it shows the number of points in the file-name. ### Why are there two scripts? -The reason why we use a standard test script, and one with the `_grade.py` extension, is because the tests should both be easy to debug, but at the same time we have to prevent accidential changes to the test scripts. Hence, we include two versions of the tests. +The reason why we use a standard test script (one with the `_grade.py` extension and one without), is because the tests should both be easy to debug, but at the same time we have to avoid accidential changes to the test scripts. The tests themselves are the same, so if one script works, so should the other. # FAQ - **My non-grade script and the `_grade.py` script gives different number of points** Since the two scripts should contain the same code, the reason is nearly certainly that you have made an (accidental) change to the test scripts. Please ensure both scripts are up-to-date and if the problem persists, try to get support. - **Why is there a `unitgrade` directory with a bunch of pickle files? Should I also upload them?** -No. The file contains the pre-computed test results your code is compared against. If you want to load this file manually, the unitgrade package contains helpful functions for doing so. +No. The file contains the pre-computed test results your code is compared against. You should only upload the `.token` file, nothing else - **I am worried you might think I cheated because I opened the '_grade.py' script/token file** This should not be a concern. Both files are in a binary format (i.e., if you open them in a text editor they look like garbage), which means that if you make an accidential change, they will with all probability simply fail to work. - **I think I might have edited the `report1.py` file. Is this a problem since one of the tests have now been altered?** -Feel free to edit/break this file as much as you like if it helps you work out the correct solution. In fact, I recommend you just run `report1.py` from your IDE and use the debugger to work out the current state of your program. However, since the `report1_grade.py` script contains a seperate version of the tests, please ensure your `report1.py` file is up to date. +Feel free to edit/break this file as much as you like if it helps you work out the correct solution. However, since the `report1_grade.py` script contains a seperate version of the tests, please ensure both files are in sync to avoid unexpected behavior. ### Debugging your code/making the tests pass The course material should contain information about the intended function of the scripts used in the tests, and the file `report1.py` should mainly be used to check which of your code is being run. In other words, first make sure your code solves the exercises, and only later run the test script which is less easy/nice to read. @@ -85,8 +82,8 @@ However, obivously you might get to a situation where your code seems to work, b - **I am 99% sure my code is correct, but the test still fails. Why is that?** The testing framework offers a great deal of flexibility in terms of what is compared. This is either: (i) The value a function returns, (ii) what the code print to the console (iii) something derived from these. - Since the test *might* compare the console output, i.e. what you generate using `print("...")`-statements, innnocent changes to the script, like an extra print statement, can cause the test to fail, which is counter-intuitive. For this reason, please look at the error message carefully (or the code in `report1.py`) to understand what is being compared. - +When a test fails, you should always try to insert a breakpoint on exactly the line that generate the problem, run the test in the debugger, and figure out what the expected result was supposed to be. This should give you a clear hint as to what may be wrong. + One possibility that might trick some is that if the test compares a value computed by your code, the datatype of that value is important. For instance, a `list` is not the same as a python `ndarray`, and a `tuple` is different from a `list`. This is the correct behavior of a test: These things are not alike and correct code should not confuse them. - **The `report1.py` class is really confusing. I can see the code it runs on my computer, but not the expected output. Why is it like this?** @@ -96,10 +93,7 @@ To make sure the desired output of the tests is always up to date, the tests are There are a number of console options available to help you figure out what your program should output and what it currently outputs. They can be found using: ```python report1.py --help``` Note these are disabled for the `report1_grade.py` script to avoid confusion. It is not recommended you use the grade script to debug your code. - - - **How do I see the output generated by my scripts in the IDE?** -The file `unitgrade/unitgrade.py` contains all relevant information. Look at the `QItem` class and the function `get_points`, which is the function that strings together all the tests. - + - **Since I cannot read the `.token` file, can I trust it contains the same number of points internally as the file name indicate?** Yes. @@ -108,14 +102,13 @@ Yes. That the script `report1_grade.py` is difficult to read is not the principle safety measure. Instead, it ensures there is no accidential tampering. If you muck around with these files and upload the result, we will very likely know. - **I have private data on my computer. Will this be read or uploaded?** -No. The code will look for and upload your solutions, but it will not read/look at other directories in your computer. In the example provided with this code, this means you should expect unitgrade to read/run all files in the `cs101courseware_example`-directory, but **no** other files on your computer. So as long as you keep your private files out of the base courseware directory, you should be fine. +No. The code will look for and upload your solutions, but it will not read/look at other directories in your computer. As long as your keep your private files out of the directory containing your homework you have nothing to worry about. - **Does this code install any spyware/etc.? Does it communicate with a website/online service?** -No. Unitgrade makes no changes outside the courseware directory and it does not do anything tricky. It reads/runs code and write the `.token` file. +No. Unitgrade makes no changes outside the courseware directory and it does not do anything tricky. It reads/runs code and produce the `.token` file. - **I still have concerns about running code on my computer I cannot easily read** Please contact me and we can discuss your specific concerns. - # Citing ```bibtex diff --git a/cs101courseware/cs101report1_grade.py b/docs/legacy/cs101courseware/cs101report1_grade.py similarity index 100% rename from cs101courseware/cs101report1_grade.py rename to docs/legacy/cs101courseware/cs101report1_grade.py diff --git a/cs101courseware_example/Report0_handin_18_of_18.token b/docs/legacy/cs101courseware_example/Report0_handin_18_of_18.token similarity index 100% rename from cs101courseware_example/Report0_handin_18_of_18.token rename to docs/legacy/cs101courseware_example/Report0_handin_18_of_18.token diff --git a/cs101courseware_example/Report0_resources_do_not_hand_in.dat b/docs/legacy/cs101courseware_example/Report0_resources_do_not_hand_in.dat similarity index 100% rename from cs101courseware_example/Report0_resources_do_not_hand_in.dat rename to docs/legacy/cs101courseware_example/Report0_resources_do_not_hand_in.dat diff --git a/cs101courseware_example/Report1.xlsx b/docs/legacy/cs101courseware_example/Report1.xlsx similarity index 100% rename from cs101courseware_example/Report1.xlsx rename to docs/legacy/cs101courseware_example/Report1.xlsx diff --git a/cs101courseware_example/Report1_handin_18_of_18.token b/docs/legacy/cs101courseware_example/Report1_handin_18_of_18.token similarity index 100% rename from cs101courseware_example/Report1_handin_18_of_18.token rename to docs/legacy/cs101courseware_example/Report1_handin_18_of_18.token diff --git a/cs101courseware_example/Report1_resources_do_not_hand_in.dat b/docs/legacy/cs101courseware_example/Report1_resources_do_not_hand_in.dat similarity index 100% rename from cs101courseware_example/Report1_resources_do_not_hand_in.dat rename to docs/legacy/cs101courseware_example/Report1_resources_do_not_hand_in.dat diff --git a/cs101courseware_example/Report2_resources_do_not_hand_in.dat b/docs/legacy/cs101courseware_example/Report2_resources_do_not_hand_in.dat similarity index 100% rename from cs101courseware_example/Report2_resources_do_not_hand_in.dat rename to docs/legacy/cs101courseware_example/Report2_resources_do_not_hand_in.dat diff --git a/cs101courseware_example/__init__.py b/docs/legacy/cs101courseware_example/__init__.py similarity index 100% rename from cs101courseware_example/__init__.py rename to docs/legacy/cs101courseware_example/__init__.py diff --git a/cs101courseware_example/cs101report1.py b/docs/legacy/cs101courseware_example/cs101report1.py similarity index 100% rename from cs101courseware_example/cs101report1.py rename to docs/legacy/cs101courseware_example/cs101report1.py diff --git a/cs101courseware_example/cs101report1_grade.py b/docs/legacy/cs101courseware_example/cs101report1_grade.py similarity index 100% rename from cs101courseware_example/cs101report1_grade.py rename to docs/legacy/cs101courseware_example/cs101report1_grade.py diff --git a/cs101courseware_example/cs101report2.py b/docs/legacy/cs101courseware_example/cs101report2.py similarity index 100% rename from cs101courseware_example/cs101report2.py rename to docs/legacy/cs101courseware_example/cs101report2.py diff --git a/cs101courseware_example/cs101report2_grade.py b/docs/legacy/cs101courseware_example/cs101report2_grade.py similarity index 100% rename from cs101courseware_example/cs101report2_grade.py rename to docs/legacy/cs101courseware_example/cs101report2_grade.py diff --git a/cs101courseware_example/homework1.py b/docs/legacy/cs101courseware_example/homework1.py similarity index 100% rename from cs101courseware_example/homework1.py rename to docs/legacy/cs101courseware_example/homework1.py diff --git a/cs101courseware_example/instructions.py b/docs/legacy/cs101courseware_example/instructions.py similarity index 100% rename from cs101courseware_example/instructions.py rename to docs/legacy/cs101courseware_example/instructions.py diff --git a/docs/mkdocs.py b/docs/mkdocs.py index 589d74a..9e525e5 100644 --- a/docs/mkdocs.py +++ b/docs/mkdocs.py @@ -7,7 +7,6 @@ if __name__ == "__main__": from jinjafy.bibliography_maker import make_bibliography bibtex = make_bibliography("../setup.py", "./") - out = subprocess.check_output("python --version").decode("utf-8") fn = unitgrade_private2.__path__[0] + "/../../examples/02631/instructor/programs/report1intro.py" diff --git a/setup.py b/setup.py index aaa1240..0527345 100644 --- a/setup.py +++ b/setup.py @@ -1,15 +1,16 @@ # Use this guide: # https://packaging.python.org/tutorials/packaging-projects/ - +# py -m build && twine upload dist/* # from unitgrade2.version import __version__ import setuptools with open("src/unitgrade2/version.py", "r", encoding="utf-8") as fh: - __version__ = fh.read().split(" = ")[1].strip()[1:-1] + __version__ = fh.read().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", version=__version__, @@ -31,5 +32,5 @@ setuptools.setup( packages=setuptools.find_packages(where="src"), python_requires=">=3.8", license="MIT", - install_requires=['numpy', 'tabulate', 'tqdm', "pyfiglet", "colorama", "coverage"], + install_requires=['numpy', 'tabulate', "pyfiglet", "coverage", "colorama", 'tqdm'], ) diff --git a/src/unitgrade.egg-info/PKG-INFO b/src/unitgrade.egg-info/PKG-INFO index 64824b5..5e7bafc 100644 --- a/src/unitgrade.egg-info/PKG-INFO +++ b/src/unitgrade.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: unitgrade -Version: 0.0.3 +Version: 0.1.1 Summary: A student homework/exam evaluation framework build on pythons unittest framework. Home-page: https://lab.compute.dtu.dk/tuhe/unitgrade Author: Tue Herlau @@ -66,7 +66,7 @@ The file `cs101report1.py` is just an ordinary, non-obfuscated file which they c | | | |_ __ _| |_| | \/_ __ __ _ __| | ___ | | | | '_ \| | __| | __| '__/ _` |/ _` |/ _ \ | |_| | | | | | |_| |_\ \ | | (_| | (_| | __/ - \___/|_| |_|_|\__|\____/_| \__,_|\__,_|\___| v0.0.2, started: 03/09/2021 21:18:46 + \___/|_| |_|_|\__|\____/_| \__,_|\__,_|\___| v0.0.3, started: 07/09/2021 00:42:25 Week 4: Looping (use --help for options) Question 1: Test the cluster analysis method @@ -99,7 +99,7 @@ Question 4: Test the fermentation rate question * q4.4) fermentationRate([20.1, 19.3, 1.1, 18.2, 19.7, ...], 18.2, 20) = 19.500 ?..................................PASS * q4) Total.................................................................................................... 10/10 -Total points at 21:18:46 (0 minutes, 0 seconds)....................................................................40/40 +Total points at 00:42:25 (0 minutes, 0 seconds)....................................................................40/40 Provisional evaluation --------- ----- q1) Total 10/10 @@ -125,6 +125,7 @@ This runs the same tests, and generates a file `Report0_handin_18_of_18.token`. ## Running the tests in pycharm Naturally, you can also run the tests in pycharm, and this offers you a lot of cool features such as integration with the debugger and the ability to see which tests have failed. To do this, simply right-click on the `report.py`-file and select `Run as unittest` (or alternatively, `debug as unittest`). This will take you to a screen such as shown below: +  You can see all tests are green indicating they all pass. If you click on a test you can see the console output it generates and you can @@ -185,3 +186,16 @@ No. Unitgrade makes no changes outside the courseware directory and it does not Please contact me and we can discuss your specific concerns. +# Citing +```bibtex +@online{unitgrade, + title={Unitgrade (0.0.3): \texttt{pip install unitgrade}}, + url={https://lab.compute.dtu.dk/tuhe/unitgrade}, + urldate = {2021-09-07}, + month={9}, + publisher={Technical University of Denmark (DTU)}, + author={Tue Herlau}, + year={2021}, +} +``` + diff --git a/src/unitgrade.egg-info/SOURCES.txt b/src/unitgrade.egg-info/SOURCES.txt index 6644370..778240d 100644 --- a/src/unitgrade.egg-info/SOURCES.txt +++ b/src/unitgrade.egg-info/SOURCES.txt @@ -2,6 +2,11 @@ LICENSE README.md pyproject.toml setup.py +src/unitgrade/__init__.py +src/unitgrade/unitgrade.py +src/unitgrade/unitgrade_grade.py +src/unitgrade/unitgrade_helpers.py +src/unitgrade/version.py src/unitgrade.egg-info/PKG-INFO src/unitgrade.egg-info/SOURCES.txt src/unitgrade.egg-info/dependency_links.txt diff --git a/src/unitgrade.egg-info/requires.txt b/src/unitgrade.egg-info/requires.txt index 680bd8b..f0d444b 100644 --- a/src/unitgrade.egg-info/requires.txt +++ b/src/unitgrade.egg-info/requires.txt @@ -1,6 +1,6 @@ numpy tabulate -tqdm pyfiglet -colorama coverage +colorama +tqdm diff --git a/src/unitgrade.egg-info/top_level.txt b/src/unitgrade.egg-info/top_level.txt index 8943cd4..948295a 100644 --- a/src/unitgrade.egg-info/top_level.txt +++ b/src/unitgrade.egg-info/top_level.txt @@ -1 +1,2 @@ +unitgrade unitgrade2 diff --git a/unitgrade/__init__.py b/src/unitgrade/__init__.py similarity index 100% rename from unitgrade/__init__.py rename to src/unitgrade/__init__.py diff --git a/unitgrade/unitgrade.py b/src/unitgrade/unitgrade.py similarity index 100% rename from unitgrade/unitgrade.py rename to src/unitgrade/unitgrade.py diff --git a/unitgrade/unitgrade_grade.py b/src/unitgrade/unitgrade_grade.py similarity index 100% rename from unitgrade/unitgrade_grade.py rename to src/unitgrade/unitgrade_grade.py diff --git a/unitgrade/unitgrade_helpers.py b/src/unitgrade/unitgrade_helpers.py similarity index 100% rename from unitgrade/unitgrade_helpers.py rename to src/unitgrade/unitgrade_helpers.py diff --git a/unitgrade/version.py b/src/unitgrade/version.py similarity index 100% rename from unitgrade/version.py rename to src/unitgrade/version.py diff --git a/src/unitgrade2/__init__.py b/src/unitgrade2/__init__.py index d62d54d..caeb0a3 100644 --- a/src/unitgrade2/__init__.py +++ b/src/unitgrade2/__init__.py @@ -1,36 +1,44 @@ -import os +import unitgrade2.version as __version__ +from unitgrade2.unitgrade2 import myround, mfloor, msum, ActiveProgress +from unitgrade2.unitgrade2 import Capturing, Report, UTestCase, cache, hide -# DONT't import stuff here since install script requires __version__ - -def cache_write(object, file_name, verbose=True): - import compress_pickle - dn = os.path.dirname(file_name) - if not os.path.exists(dn): - os.mkdir(dn) - if verbose: print("Writing cache...", file_name) - with open(file_name, 'wb', ) as f: - compress_pickle.dump(object, f, compression="lzma") - if verbose: print("Done!") +from unitgrade2.unitgrade_helpers2 import evaluate_report_student +# import os +# import lzma +# import pickle -def cache_exists(file_name): - # file_name = cn_(file_name) if cache_prefix else file_name - return os.path.exists(file_name) - +# DONT't import stuff here since install script requires __version__ -def cache_read(file_name): - import compress_pickle # Import here because if you import in top the __version__ tag will fail. - # file_name = cn_(file_name) if cache_prefix else file_name - if os.path.exists(file_name): - try: - with open(file_name, 'rb') as f: - return compress_pickle.load(f, compression="lzma") - except Exception as e: - print("Tried to load a bad pickle file at", file_name) - print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version") - print(e) - # return pickle.load(f) - else: - return None +# def cache_write(object, file_name, verbose=True): +# # raise Exception("bad") +# # import compress_pickle +# dn = os.path.dirname(file_name) +# if not os.path.exists(dn): +# os.mkdir(dn) +# if verbose: print("Writing cache...", file_name) +# with lzma.open(file_name, 'wb', ) as f: +# pickle.dump(object, f) +# if verbose: print("Done!") +# +# +# def cache_exists(file_name): +# # file_name = cn_(file_name) if cache_prefix else file_name +# return os.path.exists(file_name) +# +# +# def cache_read(file_name): +# # import compress_pickle # Import here because if you import in top the __version__ tag will fail. +# # file_name = cn_(file_name) if cache_prefix else file_name +# if os.path.exists(file_name): +# try: +# with lzma.open(file_name, 'rb') as f: +# return pickle.load(f) +# except Exception as e: +# print("Tried to load a bad pickle file at", file_name) +# print("If the file appears to be automatically generated, you can try to delete it, otherwise download a new version") +# print(e) +# # return pickle.load(f) +# else: +# return None -from unitgrade2.unitgrade2 import myround, mfloor, msum, Capturing, ActiveProgress diff --git a/src/unitgrade2/__pycache__/__init__.cpython-38.pyc b/src/unitgrade2/__pycache__/__init__.cpython-38.pyc index 4429c41fb31c60f6c1074d20620caf2572fce98e..d56ac9f30a848efb81409d9ed034b964f0dabe35 100644 GIT binary patch literal 494 zcmWIL<>g{vU|?|Iuu61iV_<j;;vi!tkR$^GLva8D149Z!3S$mKE@KoEBZ$qE!<@?! z#gfY!#hS|&#g@w+#SRu{&SB2wh~miQjN;7YisH)Uj^fVciQ)mvvE=aP@<s76GNdpD zGib8D1li@M$#RQ5x3Va|G%w{ATW(rTetyv{mfYgf+*^E($t9U(sR2d#=|!o<#hR?Q zIGqy<N=l0|^U`mz1*I0`7nR)N3=K&wE^$sQPQAsNoS2-EdW$6^GbL4%@s@aMSz=CU zVo7Rz5m;$_aY<=PYF>#Zf0R&ZUS>&pQDRD}kzQG9QE_H|-Ys?*gF8MR%8ZYX5=Byp zU>1Qwriht=fuV>6M6iMgHW0xMA~-+<Cx`%rZ<H)TqaK_YpOKnVkXlr1RK(4|z_5~` zh!3P5MEtUGwu%WYPAw{qDJjiJjd96OF3nBND=ChFYmO-{N=EREV&da7^D;}~<Mj$E aZ*kb<=BJeAq}nlpVh-de9!3d9872UL_==?f literal 1306 zcmWIL<>g{vU|_hl*dWo3m4V?gh=Yuo85kHG7#J9eofsGxQW#Pga~Pr+Qn*r>Q&?IU zqZm_IQ`lM<qL@<HQ#e`}qL@<{QaE#1a#^ETbJ?QUa@nKUb2*|oayg?o85vR-gBdir zUxG~c3rPkU3}S*X8v_FaCj$e6GsvPmkVT9s3^@$7%q5IjObeK67#A|uvVhqvH7pAm zi?vD^7qEg@j0`nQMHVG&DNHqt%}k68CG0hfDa_4G{bIGOHOyHYH4HVZDJ&_hz08aZ z;S8n>feb+m5g<KTC2T2d&5Vo;DGb33n(Tf>j0_A6RRZBfnI)Nd=?ck-$r-77dV05* z%9C!f<W%J*u4IhjPR`FQC`v6Z&dkrNVs**SOI6h5yu}X_jW5Vd&dy1_#gt!si=`m3 zB;yu)N@h`BVs7d!w$zHu;*#Q9thw1KnMJo)3yL!HN^Y^_7o_IhVo52@Ezo4U#g?Cx zm6}{~i!&`VCp8|djlC?jC@H@<wTP900Tjl0w;0n_G873hFfjbGakh#HElw>ejwvb4 zNR4sHPcF?(%_}L6Db33)NiRxFNsTEkN=EREV&da7^D;}~<Mj$EZ*hY?7++qLS&|Ae zyjTPj4U8O&Jd8YyT#PIXT#P)7Qj9DNMIeb}P~d<RfG~&+iaT&z@h~tjlrUs5E?}x* zSO|(<Mn6sFB2aV|u`@6*6mc>zFlaIs@q^ePXM^2dBn(P$JP;>9LPh`-@k}7&nTkXi z7#L7&WMN=n03}kejeUsJ!3a(f%pjYK+Nz97SZWwjn3|dT<!YH~7_(Swm_UiNmnocq z2^#Z@8NwNYp=ltTp|FdEp@c1)tEh>Qp_ZAEp-QfVJ%zP~0g{d&siTCwh8dJfKxrq1 zp_aLhIf5aQp|C50A&&)=azGfCdTuclB^7}}rU>NVDy5L3%+wTxl6-}n{KOQ6M1`cp z6a`2UQ2<4;LSji(u4kG;Nk%G^Ur>;mSX2zsk(8>CSXz>wn^=;WoS2hSsgRzUms*rq zlA5BUP?=w<keryOP*PM0Qks&QlUkChkXfRmkYAFKT2!7{oT`wLU!DiGH7~VXp)9os zlx8(KzzGR0dWt}Cc#8$3{1&HcMRICENoIat5jQC2SRgS6N+Cspph#m(g~T8@F2T{r z1&PL@)Wj4}#K9606AvR3BM+k*qW~ipBL|}lbCDFtK-OFAxs^rvrFkj0*mBcy^7D&s zvE&w)=HB9TPAn)XEy~PGzs2X6T#{Lq8c>v<4$5qrj3Js_5I2L;A~<<~4T}<mCv_t| z1hYsQl-OiIge-`V0}=8d0_<o60d_uzO>TZlX-=vgC|MVCFfcH1uyAp6uyXJN0CxU1 ASO5S3 diff --git a/src/unitgrade2/__pycache__/unitgrade2.cpython-38.pyc b/src/unitgrade2/__pycache__/unitgrade2.cpython-38.pyc index d2b644a5d515e7f207ce5605697ad2e458fd1527..59bb92f4d418f4c6e20156d57457f67fac1d2d28 100644 GIT binary patch delta 2120 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 K94s8d983U5+aq}Z delta 2066 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 Z#3d*cA;iNa$RQ+D&rr<4!XeDT1OTB>8O8tr diff --git a/src/unitgrade2/__pycache__/unitgrade_helpers2.cpython-38.pyc b/src/unitgrade2/__pycache__/unitgrade_helpers2.cpython-38.pyc index d80c99b05ba9b98faefe0a10c407cb0555e97b32..ebae664e67677e245b6a227b53dd7158fc9deb68 100644 GIT binary patch delta 2043 zcmca%cGQ$Fl$V!_fq{WRSHUVVSz#id45PqA?MKGSDbgu2EsRmZDY7YYEeug2?hGmN zDGDtNDGI6VS)$F%QDP}7DT*mdEsRm(DU87knyQ<F7#FhCha_`?jDuo+1_lNm1_lOa z1_p-WBt`~?5{3ng3mIxTYdBIEvN?*xYB;l)YB_2c!MunX&Mf8>hHTCvFsqg^g|UV) zo2@7yg(-!(g`tLV0ZR(wLPi&cX2uw%TE<$YJdP698pdYETE-H#8io{>W~Tb0t`hbv zjs=`G3|U+Y8H>AWSc|(#xKmicqC61M6t-sOCdL|&RUAb#YB;laYnf|Uz)o0J!<ofb z%TmH$!%)Lm!_32wzpaLYhoOckg}sKQhBHf`hBHgBhPj3<g`<}%hN+gTmK);u35-Qt zHS7~)8H>0|gi<)085tQ$glo7#bPabcPl-^8a1GDopKOZGykMtD)bK83OkqgjX=RdR zNZ}P{sO1BTOV;o$WSqcQ6jvgY!Uxh_B9y{k!`IBj$WS7bA^;Mp;hG%FzKYRsGY7|O zMlP<r{DPwV^rF<_$sak3Cg*cyGxATq&1EJMB~YA^Umjmjlv$QpoSC1Om{X)WnU6cx z*bNjpj2w&{jBHFojBJb?j8aS-OfrmOOdMby8?z8269W?i3j+%S6C=|<E>?lb6SyT9 zZ6>eee#IC*c?wT}wi*Ki!!5>)B0&ZQ1_&Vv5@9VZNKDQS1;sWS$QC9cMy|=pd{@|S zu@;vWq!vxK;g4av#ZsJ_lQwxSzscl-yb`Ql3=9kjlb`WBPUhfKW%Qb?$0utU4l)*G zVv#!o14BH>JSGMP1`b9rKbaR~HWcfC#9>M7$mCXmQ;a_*2MDgIcVUPXs1-_)sFAG^ z%96KXn7~+ARwJLK;KC3q5yMm~RI898S)-7~ge2crBcG*+Brk;~zph3;OQ}||M7c&j zORkx*Rtd%e(~8ZE;tXJxLNlXyJp)K_Hq!*AVx|(6CdL}Y6vk|p2~0%_C8{-&HA*53 zDbgYgHHy*<&5U_WDU7vBHIg9l8rd4@8i_2m6qz|}HKH}*HVidNH4^cnHHtM7@gga* zHR36P;taDHQsm|`*D9yT*GOcE&SpqasF9q_FqfrPrG&FaIYqICp*~BdM7c(}nNb4d zCKV7Z!XVC2qaw}#N<<}`De@q?MtFgGiW1yV<+;o#1~R8JEo5Y5C_GZ4Q6t*S7{gqv zTB}&ARH9m<S)<qt%6f`mwi1}FRHInK5HC`rRKpNoFIvL1fO#Q9Enf}a0*(}wg^aa= zC8{O73wReY)QCthG&9z!)u^SYN-@-`*QnK~r>KfBNHElDq^PB+w=mRdrZA>x<cQ^J z)oL*^)M%w>)(DC-q-cpV)M(bIr)W1bi8G|=fO#4zx?rB3I75m+iFOS`mMAD9!cr8( z8Ngf-5VwXQOD09Xmzj|P>|+CDrKl<)KCV%!QA}g15lu0i!&IwXqg^AM#uUt;X*Bth z@FK>N$x}rxGdfPr6qR+>WV*#tkXT&ocZ(%0F*D~DYidzZeo;Ckb$+ndWGk`-C1yJi zVGqtDnI);Y#kW{XGD~t&Z?WVh=1yKKs=%l=`J8B$NHPNhgC^rG=HikfO{O9Tkfy@P zK4LPAJ11v~^^5D=5-QEhEJ-g)Oi4A;D@!c`W$dEZ$-?5!()u99xgbIVL?nUiVkybY zO)YYtTqtfRkOC5o1`!aqO!kwKW=xxWNnEwQ8l;xFs5I{uXK`tAa%ypLYVj@p_>$D( z65rI4jQkY8#N5<d93cOd7Nr*7VgviJ_!b99MR8DRUJ)pZ-r{pCE>0~f$;{7l1%*M8 z5XiDfklAdB1qG>jDMg^7r-%h4TnZwJK)%%}Vl<naC=tpQ3lgfIyhlPIrVzxmWME*3 zVlRm=E=epZxy4$XoL`iBi!CQVF(tL=7Gqu!s0;!JE~s$HEJ=-GD+GsWQ71?vQ(=5j z8pv*Dkc*jfi_?ps{x+7>Q3hLY0kQ?P1S`^GU|`4wl{a0Jn<QODxfuBv`51*jn3;`{ zi%E+~iBV$m1xb^7kU~ukKR-V|H@6T!O&$>K=H?axf|?SySo2DA3o4616<-mk=qh3c zS=In@5Emi}Az|SQlIj2vtRTVwL}-FkawHa|7bF%Hrxvw<xO|R9>7}`;c_jgfMUy8> z=_-Q4ugC_Z92Bs(xI$8kOMJlLQv@nEi}WUclTu?en=C7>!>hx=CB(zP2!?+FAN2l` delta 2223 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 aUW$j&Vsej^4!;Hmn-Gf-4+A3@{sjPhxg0J4 diff --git a/src/unitgrade2/__pycache__/version.cpython-38.pyc b/src/unitgrade2/__pycache__/version.cpython-38.pyc index fb1c90bd161b62260f9408fa471df6c4c537f234..ad6a8f2a32ed6b8a848b77969560d841ab9cb8d0 100644 GIT binary patch delta 26 hcmZ3@xSEkWl$V!_fq{X6Pt0;6w;{8kp20->8~{Cz1l0fl delta 26 hcmZ3@xSEkWl$V!_fq{YH;ydGs+=k2sdd3s&a{y6U25bNT diff --git a/src/unitgrade2/unitgrade2.py b/src/unitgrade2/unitgrade2.py index 8e34131..087343c 100644 --- a/src/unitgrade2/unitgrade2.py +++ b/src/unitgrade2/unitgrade2.py @@ -1,6 +1,3 @@ -""" -git add . && git commit -m "Options" && git push && pip install git+ssh://git@gitlab.compute.dtu.dk/tuhe/unitgrade.git --upgrade -""" import numpy as np import sys import re @@ -13,13 +10,13 @@ import io from unittest.runner import _WritelnDecorator from typing import Any import inspect -import textwrap import colorama from colorama import Fore from functools import _make_key, RLock from collections import namedtuple import unittest import time +import textwrap _CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"]) @@ -96,20 +93,6 @@ class Capturing2(Capturing): self.numbers = numbers -# @classmethod -# class OrderedClassMembers(type): -# def __prepare__(self, name, bases): -# assert False -# return collections.OrderedDict() -# -# def __new__(self, name, bases, classdict): -# ks = list(classdict.keys()) -# for b in bases: -# ks += b.__ordered__ -# classdict['__ordered__'] = [key for key in ks if key not in ('__module__', '__qualname__')] -# return type.__new__(self, name, bases, classdict) - - class Report: title = "report title" version = None @@ -269,17 +252,9 @@ class ActiveProgress(): def dprint(first, last, nL, extra = "", file=None, dotsym='.', color='white'): if file == None: file = sys.stdout - - # ss = self.item_title_print - # state = "PASS" if success else "FAILED" dot_parts = (dotsym * max(0, nL - len(last) - len(first))) - # if self.show_progress_bar or True: print(first + dot_parts, end="", file=file) - # else: - # print(dot_parts, end="", file=self.cc.file) last += extra - # if tsecs >= 0.5: - # state += " (" + str(tsecs) + " seconds)" print(last, file=file) @@ -406,13 +381,12 @@ def get_hints(ss): try: ss = textwrap.dedent(ss) ss = ss.replace('''"""''', "").strip() - hints = ["hints:", ] + hints = ["hints:", "hint:"] j = np.argmax([ss.lower().find(h) for h in hints]) h = hints[j] - ss = ss[ss.find(h) + len(h) + 1:] + ss = ss[ss.lower().find(h) + len(h) + 1:] ss = "\n".join([l for l in ss.split("\n") if not l.strip().startswith(":")]) - ss = textwrap.dedent(ss) - ss = ss.strip() + ss = textwrap.dedent(ss).strip() return ss except Exception as e: print("bad hints", ss, e) @@ -472,10 +446,8 @@ class UTestCase(unittest.TestCase): s = f.read() lines = s.splitlines() garb = 'GARBAGE' - lines2 = snipper_main.censor_code(lines, keep=True) assert len(lines) == len(lines2) - for l in data.contexts_by_lineno(file): if lines2[l].strip() == garb: if self.cache_id() not in self._report.covcache: @@ -635,7 +607,8 @@ class UTestCase(unittest.TestCase): hint = get_hints(comments) if hint != None: - hints.append(hint) + # hint = textwrap.dedent(hint) + hints.append((hint, file, l) ) gprint(f"> - {l}") er = er[0] @@ -643,10 +616,16 @@ class UTestCase(unittest.TestCase): if doc is not None: hint = get_hints(er._testMethodDoc) if hint is not None: - hints = [hint] + hints + hints = [(hint, None, self.cache_id()[1] )] + hints if len(hints) > 0: - gprint("> Hints:") - gprint(textwrap.indent("\n".join(hints), "> ")) + for hint, file, method in hints: + s = (f"'{method.strip()}'" if method is not None else "") + if method is not None and file is not None: + s += " in " + s += (file.strip() if file is not None else "") + gprint(">") + gprint("> Hints (from " + s + ")") + gprint(textwrap.indent(hint, "> ")) super()._feedErrorsToResult(result, errors) @@ -704,4 +683,4 @@ def methodsWithDecorator(cls, decorator): if maybeDecorated.decorator == decorator: print(maybeDecorated) yield maybeDecorated -# 817 +# 817, 705 \ No newline at end of file diff --git a/src/unitgrade2/unitgrade_helpers2.py b/src/unitgrade2/unitgrade_helpers2.py index 6838647..7f538f0 100644 --- a/src/unitgrade2/unitgrade_helpers2.py +++ b/src/unitgrade2/unitgrade_helpers2.py @@ -38,8 +38,11 @@ parser.add_argument('--unmute', action="store_true", help='Show result of prin parser.add_argument('--passall', action="store_true", help='Automatically pass all tests. Useful when debugging.') parser.add_argument('--noprogress', action="store_true", help='Disable progress bars.') -def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False): +def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, show_tol_err=False, show_privisional=True, noprogress=None): args = parser.parse_args() + if noprogress is None: + noprogress = args.noprogress + if question is None and args.q is not None: question = args.q if "." in question: @@ -56,11 +59,12 @@ def evaluate_report_student(report, question=None, qitem=None, unmute=None, pass passall = args.passall - results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute and not args.noprogress, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute, + results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute and not noprogress, 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: + if question is None and show_privisional: print("Provisional evaluation") tabulate(table_data) table = table_data @@ -84,9 +88,9 @@ def upack(q): 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 UnitgradeTextRunner(unittest.TextTestRunner): +# def __init__(self, *args, **kwargs): +# super().__init__(*args, **kwargs) class SequentialTestLoader(unittest.TestLoader): def getTestCaseNames(self, testCaseClass): @@ -105,7 +109,7 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa show_tol_err=False, big_header=True): - from src.unitgrade2.version import __version__ + from unitgrade2.version import __version__ now = datetime.now() if big_header: ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom") @@ -135,7 +139,7 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa q.possible = 0 q.obtained = 0 q_ = {} # Gather score in this class. - from src.unitgrade2.unitgrade2 import UTextTestRunner + from unitgrade2.unitgrade2 import UTextTestRunner UTextResult.q_title_print = q_title_print # Hacky UTextResult.show_progress_bar = show_progress_bar # Hacky. UTextResult.number = n @@ -143,13 +147,21 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa res = UTextTestRunner(verbosity=2, resultclass=UTextResult).run(suite) + for s in res.successes: + q_[s._testMethodName] = ("pass",None) + for (s,msg) in res.failures: + q_[s._testMethodName] = ("fail", msg) + for (s,msg) in res.errors: + q_[s._testMethodName] = ("error", msg) + + 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} + score[n] = {'w': w, 'possible': w, 'obtained': obtained, 'items': q_, 'title': qtitle, 'name': q.__name__} q.obtained = obtained q.possible = possible @@ -172,7 +184,7 @@ 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 "") - from src.unitgrade2.unitgrade2 import dprint + from unitgrade2.unitgrade2 import dprint dprint(first = "Total points at "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")", last=""+str(report.obtained)+"/"+str(report.possible), nL = report.nL) diff --git a/src/unitgrade2/version.py b/src/unitgrade2/version.py index e344246..d1f2e39 100644 --- a/src/unitgrade2/version.py +++ b/src/unitgrade2/version.py @@ -1 +1 @@ -__version__ = "0.0.3" \ No newline at end of file +__version__ = "0.1.1" \ No newline at end of file diff --git a/unitgrade/__pycache__/__init__.cpython-36.pyc b/unitgrade/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 41c8cae17ecac0b36d54439732f20e778a3e90b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 998 zcmXr!<>jg>`XArI%)s!N0SPcOFfceUFfbH*F)%QsFhnt=Fs3l&Fh((gX{IQq6z&w3 z6xJ5TDCQKl6!sQ|D3%nC6wVfgDAp9l6s{b$T=ppTT#hJ?T+S%YT&^fCMurroU<OT| zDpmtM13k-^AiMlRl0o)>n5+y83_J`B49*}s(-;^SN*J;jvzTfan;C1F!EELl=4QrX zr4oiL77&Y(p@yl*poBGrv4*jkiIJg%t%fm$shO!?td^yOJ&U7;p@t=eIfbQ{nUNu! zA%!83A&4OYq$jC_HHEdAk&z*VA(%mv&96!zyeP9IGcR2sIWajSRZmav7E^iBEtZ_B z+(b>rTinU{xdlb3#l@NVc~z_~`FW{|nw+<o@{4b=6eN~p++t73EXqsFO})jIT9H{? zQhbXwH#;S>=oV{1QD$DrEtdR()Vy2#Fhk-CGLy4&Qg5-Ol;##_vfg6LPs&P7F1f{- zmYI_p4>pIrEVU>pzc}?4Q%c?~#<Z0TMZydW48P2stztrpQ;UjYN=h?QV_fo+OLJ56 zN{VAj^D;})ixN{(ky!EZnR%Hd@$q^EmAAOT-ij|T$}CA`1BI>-D7+bY7<m}E7+Dy& z7<m|_7+DyKL>U+uk{LmA3=9k)nh6#ipm+gC0aFb_Gh;A=CZnGwa}g-Uia-VyaWF72 zXfhV@GcZ8h3U+Xj2m=GdEgp#Fkf0F&*$y(5fvHFg**1_EH_SFOMC3CxGZyKUFxN1o zFg7#w3)C`}uw=1<qB(`Rmnj$;rFkr%hy-Cx7Qb6eMM*`VG^NQ{!~${_8=5<dK=u@Y zd~}N?CqFSolL_K(P@q8E4GL|D<GCP?FG@{J0R>Jm$e#>Mpnwx&E|LV9&T@;*BQqr> zHSZRCZe>w^X<o`Lw%oLw{QRO@EV;#{xtfe2n%uXz<KxRxi$Dn|J|5y6kp18Q2ZsaL ztSDi4MC!p=MN*(Jl?D+qAVL;IfE|b+z~RhclbfGXnv-e=inwBs@hprSj2z5dTpX+% FoB#y+;o<-Q diff --git a/unitgrade/__pycache__/__init__.cpython-38.pyc b/unitgrade/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 9bc3be67fa220ac7114c9c4fa873a07a52b5d4d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1316 zcmWIL<>g{vU|<k)Qcv_@Wng#=;vi#Y1_lNP1_p*=HwFfV6owSW9EK={6s{EJ6qXjo zD8>}l6t)(ID5ezl6pj{#DCQJ~6wVx$T-GSoT(&5-T=ppTT#hJ?T+S%YT&^fCMurr| zU<OU@mmnMbLXts-gP0)9#=yY9$-uzi46-Z_WEo=$Lk>eNa|vS>(*oui#)XWvEMPWE z4a-8tVyzO!1*{+zBSQ^Skwpnx3R4YZGZQ02340A=3Uf14zgR764RaPp4MPoU3QG!W zFEb-UID;ueAVUyC1V~R-30n$VGb1BI3PUi1Cc9q|BLf3Nl|Xn=W=UpVx<Ybdaz?72 zp585{@}yfVIaRrdD;aNbC+FuD6r~myXXfWsvAX2vr7CK2-r|Re#usEJXXm8eV#+VR z#Zr)1l5vYYC9^0mF*o%VTWUpSaY^wl*4*rr%%WSY1x1;8CAV1e3sUoLv80sd7HG2F zV#`m;N=+`g#hI3wlNt}!#$J|Ml$2kbS_BH8TTCf=w;0n_G8Bn0FfjZwceaWNElw>e zjwvb4NR4sHPcF?(%_}L6Db33)NiRxFNkwAC$7kkcmc+;F6;$5h2Kz0(yeP9Il@%1; zAmbPqIT(2uc^J7ESs1t&c^IV_Ss04M7#J9mLE!?5A`k|#LD2?|CLRU`h7yJ>#sy3@ z3=2Uq%jl=cT*S`6z)-{iB0xz?ld(tu#0FUbc6N~{0|Ub?9*7elfdW!c%mgx?sYo2% zMivGJ29QBu8~YFmgAtq<m_ardwN)9Fu+%W5Ff}vv%hfW~FlMpVFoDu&FH<-J6ExBn zGlVk)L!&>Op|FdEp@c1)tEh>Qp_ZAEp-QfVJ%zP~0g{9u38RF)h8dJVK*=VBp_aLh zIf5aQp|C50A&&)=Xh0a2aBeXbB^7}}rU>NVDy5L3%+wTxl6-}n{KOQ6M1`cp6a`4? zPyofQLSji(u4kG;Nk%G^Ur>;mSX2zsk(8>CSXz>wn^=;WoS2hSsgRzUms*rqlA5BU zP?=w<keryOP*PM0Qks&QlUkChkXfRmkYAFKT2!7{oT`wLU!DiGH7~VXp)9oslw37A zz-b6AdWv{KvB3gTev8w!B0067Br`v+2o$rLERdK3rMx0xkTS+pNDPAG5*&?OkZ3GQ zO-upti$O9Bj2uinj7*F?jB1Pmj9iQyj55qcG7Jn1pcL+rnUa#4cZ)r@vM9eaFXa|n zZdy)$e$g$K+~U&QTb#~`1tq0LnR)5A_#Bf<GRsl}it^Jz`Aw5CM3Wohc2JrGClj!t zQNr-lt_Noo$%2xc9Egwy5egte5kx3~2#_n$eZparo1apelWGS_;l-fj%fZ6M&B4mS F4*-zdILrV5 diff --git a/unitgrade/__pycache__/unitgrade.cpython-36.pyc b/unitgrade/__pycache__/unitgrade.cpython-36.pyc deleted file mode 100644 index 4fdacefd982002048b628fb9e4ce082fe99b01ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11636 zcmXr!<>fN8Vo9jhVqkd8fCN|>7#JKF7#NB<7#SE+7@`<b7*m*X7;~ATm>3ySm{VAC zn4_4%G)ojKm}ZM&1JmqL>?zDCtT`OHoKc)mwOmnLU^(t6ZZORg#goF8!rsCV#hbzb zrukAh!8CsgR|<CvLzI9!Lkdp{Zwo^TZ!=SrpgThfUkZN<LkfSYP&0FsuscJFK#E`s zLyBOkNHcSks5?W7P>OI1LyB-JbCy^$bCkF{LyAa>XbVG%XsUQKbCiTTLyB06cnd>{ zc&cPGbCi@jLyAO-WD7%zWU5q(R4;Rsv^ztJbc#$1LyAl@Q<O}KY>He9LzHZae2PK~ zLzG+!OE80`VpR)QdS;12VoHjFo`Ra10*I5GpPQRmqM)0rpyXdrl9``ZtOS!PC@sza zu@wq33luW*ic1o6azJ{ti;FX?^!3v-OB~WOOL7vE^pf*)3rb5;^-@Yo^-{9+OG-0R z^-J?IOVW!HQ&ROn*68Y%7JzwNTrU|J7#K7eZ*e6jCTFC^7o{eqyadIapC;oij^L7_ z%)E3@|74INkSK^|Vqjo!21O?q0|P?|Ll$EVLo;JAgC?V&CetmZyn<V-Mfs(9DVmJ8 z7%NsX6mc*xF#NJ`wu%WYPAw{q0l6l|B|o_|H#M)MI0o)6Y%INk%3B;ZIf=PRDT#Lc zAS*#`C<57w-9;d~N*ECCVZ6m$T$-!NSj5i20C5S}IYpcxF31=Gn4#Fs11UlCVG$@b zN3o{m<mVURF<208Fetb{&Sqv{U;wefK?kz8h9Qd~i?NoehOvezjR_WjobmC=If=!^ z@$t7f;^XrYb5rBvHJNWQI^W_*N-R!|PsuF0#R3wB_ypu*uy<|=6{nVz7J%gAlPV!* z2!kBK0<xTugQW=Mq+}2WhC!|Y=SvX=28L9ID8>|qC?-(eY+(T9$6y9cmRoEdnJFo$ zc__gHN(5kEfQ(K7MLm;W2-rEAjJH^dQ*+Y590&ml>RT)snJKB#AlHEGWMHh~h8U{{ z68F<&DdJ^dV7SE<AD^3_Qks(*AAgG{KEALtF$d()`1m4_10lwN{R<{Q1{Vo2Ffd5L z%;jJN^RfF;1*;!ZSXvmOSW;M1*jgB(SX0=888kU=vH9eur>7PrgWU>^H4q!@XONAM zBoPmaIfi)V6b4A#vD{)Vt}MRAR$P*jUs`gDqa?K`H#09W=N5BLe!3=85hylpu_cwJ zrKJ`jc?RSUP*i8;WtPOp%Y!@v3Reb34#p~eh)eY1;~}!xog~A+zyL{1HH^)Sj372s z79)fWa~yLKD41@smKSA~q!xjq0_Gz2+|=UY#Pn1&Cl$#tFfb@1T*Jdy#RGK>*bF== z0umlz^E4U3-UhQF1Scq_S<`Y#i!;<grh@{OfeCIlSi(<}4V)s1L_zsf3`EF+2#}MC z6hJI+3P2DbLyN>27#Kh*irE+#7&sU?SU6a~VkqeW9JC;NP0-TA9L9ErG)7R3(!v$R zoWcdJQy8OIQaDmLL209%frTN8Eto-*`xd8jVnInMsAP20WW2>znwMKzk_rwFP|g6^ z0Lts&@=5{}HH<Y3@eC!5%?w#gHB2?keT<9@h-gs)hevT~L26ME$dj5ZkPrilvm_R! z7vEybE(g(?j72<fzk_`YQm+H@49HFfMmEMOA-L1vX(j}FqVa+k-tkN&%*_ln4Dl>A z3|Xv2DkW?+3|TDAj3o^5jCmX&Hi%WjSmaQ`ki}lZ5YGWpU&0X2Sp)JJR}Cb-HMxsG zzAcgjd5Aq8k_(DJF1y7UUkt8DGV_Z--U7!8IG>~z72RTsFD?PmtPqMX863E&@u@{c z`9;OhR9d7B_c=J$LEgW`86TgTSCU#3A8!B(1W+LX$~7E}Y>a$N984^X9E>1#l`wLk zKy`ql3sew-(jN%BgN!f&MHfRBC<ifSF*P$}F*h?na#R8%I3QT#L4m_wq*ud`#Zkf< z&k2i2u3K!W6(y;8DMg@EeTySKwWKUDr!@5zS8+j3W=T$FUTSfX8pspyK!W&56C|bu zs)HdVL=mW<f@BqhPdVb_Q!7BlgB{3~pjwH6QG^lXLG(C>DAQyN(PYP(g+R%r2ozkm zc%c<da%xUad_2NNSTX|zagibe0|O}Np?OM(g^Ld?hLWd1H6kb|i}O(OR1|XxDE}}; zv8FIav89N>^A~3ndx|Ky%w>$?ND)h6PvK}`jN(k;OyO!_h~i2S2e;N3qqrgMv?!hw z32+OIF^U(`GK=B^=SKcu22IIZtbv{-skwedpgflhB0x?7ISIrDXTlh0tfnw#vlPkI zFk~^NFl2LpSxhycM9x*hoW+vG3S!qVWU)aKc@0R8vxKXeA&a|)xrPOlZEF~^c))ri zK=EJ05YGdSe<q0Sn!LA|OY(DWv6rM4m!y^E-4aMFE>0~f@i7WY%}FfDEK9w`0c!7n zq;GMAq!yPrCl;sP;)bYl1=V+u91f}ti$G0;TjKG_`9(#k$tCfLdBx?aMezlRl{xu| zDMg@?8C+i6VlBxm$w}2@1s5B)1k3Y_vNQA2LG6Il<dXcN$|x}$Mt~EVDk$Y)Em(X& zNe$!(P*US!;$q?g<ts)m#wtE=h{Fq*WKbT51v)6;!S!SoDA1Wx7_ym)_&}9YEprKD z7E=jh7Bi?~0@F;*j1mmZjI}H!j9Dx-EX|BH%-JkOX(g;F;A*UdHHE2$C5x?@VK&2D zCP+5SVh6RBQkZL)L1HN^z0A<El+~|F!7sl=Ar%yg3gww48JT$sCHXn2MTvRIsS4F4 z`8l;!lCC+apk}9Td1i5{LP{p6CQi*uPAyiblE$N1lkpaFZgILMN0Biovx2;Mi#ai= z_!fI&abi(XV&yI7+{B7otOZ4xc_p{lO7e3ui%W_uKp_i_6%J^VJmnTgYDGb6a!G0m z*sNPDAV(nb9;7A50Z9-(MiHPW-~$CMxFC~a<YVMu6oN+pOc_c9fGR(51WY3#0%}-N z7_mi<I72g|ID<3;EQ&x8)XbQ|l+98!se~044WOtgVNGEn5LvA7$YS$Lx9Tx?)K}?h z2hO&5LmyA9X)+esfI=B5Vv1}*Q3NU`i?~57Q2VyX5yV1@k0K|K04S8eu~6g!;(|+C zFaa+4Oc@v$5<%e)$pWB?M2b;~QHYTb6a!+AEC7oESpJ6BDxih{2!re>2IUWMIl<@$ zj%pl@BGH1PR9HHRFG?-WFD*(=EzSnH0aQUTFjYxIY{jb14SO{YYB7{BfHG7QBiId0 zepP&+0!Se_KP6QmBef`1lkt|YUw(;aZUH2Bq^7uns$N*l3$C%j;Q}T=fpd!w>P~2> zln-|!W0eTRji?IX<ua&D24QfxfI=2jG=Q>nGiWFP+K^x@G6BUZGq^-UiYu`FU;^w2 z7LYfJkgVo{SPc?R24w?K#sTFXP@~Wrq%I0H{=itvp2ASek;0V1+`>@HnZlC7+QLxF z1#(C&ccn}XOOZ|qV+tE6QP+T4Y%D44y^OUS;S70sEDR;g*<3{{7#V7L7#XU9YFLYB zlrW}nfYg>Srf`Cq2(uYdxaKm~@PIs2!&So#l1t%EVaQ>v<t<^X;RTgvAh8n06dn+r z!d=4->ivZ?L^7l>q%hR-)bT_x<S|8n8X@f9%4Sy$M-4+fdks_Zp&I6*Q*hTY6<veb zP{UmGsD`r!?2<~c8rI@(;0lVPhC79~mx+-foFOj);`#}UMFCLvTOqj}R7SJRW=P?m z%M5Z=31bb|bucqw_P|_=?9LQMhz~Rc{E9$n9wifiQf@FP>_M$FNJ3;}fOOHJB{!2_ zm4Ka{okC=OX^}#HX-Pq8iIpZ}5h&woGD2b>9OqyH9Pxa`8TsY$&=i&uUjZ^6l*d4g zzbXw#B1lgyi7&{{%quC@v&qR%PRuE`(?iupx-;EC&UA&A-%zJ2XyoOWq*^KDCRQqB zB$lNrB&DY2DHIgtC#Mz{r>0mdfCf|)N>VFI6pE9JG7CyHkvs>=1>h0^LLhk#R!hX! zkmNbIwp)x`nvCGac2y9UEww?Fe0WA`o<d1cWoBNwLP@?tQE8q6DBCH3hb|P<tEANx zDnaoHE@VnFic-rJ67v+mO?oRxksnBHN7-sJK~f7SRI6k`%_~?Or4{ApqPkC4T_Ghu zwOAo9zeFK7u_QS|0Ty%$MXAN5IVE}uU;`j7*5oX*1XU5BG+E>U>fdm>RwSo_2k0Q} zG|rNu#N^bZ#N_N-Txt15xrrt5sTIk$1Q4kd(ta!Q1eI)j;F9eYds%8xQhsr26fZbs z!dw95!qPW57Ngh;5{ruyb8?D64Ypex1^LCrnMpaR5Fdm3az&sLE{Yx6*1N@+TI2_E zGC0?O32^G-Lgb55P|5=7WME_gbtPChSlAeuKrBW9MgcG_0`5WaFmf<Tfm(k|p#Bu7 z(*eRDl|qa{;06guoP$w>u}T0v^T9J2s2l*5_n^or26eM4c|av@GXuD6Qea^yVa(<# z(qUw%Wn^TiVgZdRr7+Yo)-giL!W7W>7&D}q4K8FfnZQ9=<O>Q<XwL~$&OmY!!o#e^ z$@xX8OF({wbeotMdBFaw;)b{tEU(Gr=LZ_UWc1Tybkk&Y0|89|^bV92C_q6~6QtD; z0OEqW;6<(=7O25c<OX7aQV7I#As}ujh%f{ZU}u2{ls*<HG(h@_K|MnbaAdGBu`u%S zaB*^Raj<YPb1-x17lBlv^twRRC@4V|>!I|zQkWp4cW5J9+~9ExP(O?-m_d{07FS>Z zsI>~}I>F-zRCt5p2^>c#4F_=OC$cj!FmNeAfqQCRYEfcIDlCtcXJjU4C}b9cii4z7 zaBfRY(Nl1$R7gopODxSP(NRdsFUf!uh2=$=B_*H|BwwK<1Ed%-Ql?N^3@T8-?Pv`> zJv~i@#A1c=)SMg;U6fi<T9l^%?o}7Vosn3Ss!*JomjbdDTI$9_DiH;Z;?z_HXfA>_ zR1}g@bMnhI^%Okw6!J?lQi~MI^NUi7b)c@OC{aj<dMiaCv)GCYZcR~Yv4Sn!Fh^)# z3>1PIns7xW6(tI`3L1%dl?t%2KUnA>j7?0+FH1$}$t%rGN-Y8z4%P?brh!J#6+m4W z=pdpl)KeuDC7K9lfyP?(Kn1S`s^2w=QbC@oDA7@Xny9IxU;`^)phcV=l0jCG(6v&4 z%sM3IK*L!fEx!ot?Zl$Y;`}_gSF0S+;~&(L)Kdt_fG3E|Vg*o{l?sj;utQPZ=%>jD z$-UTz2t`n1E*_jH)`0RcD2X!gR>>n$Fl@#H8p{y1@Lmz9X#mm#PS&8@2WoGEnp~Q& z_Bdq7?G|%UYVj@Rl8TaB?9fm{FHQJRy|5eZ9L6e1B<G;2LdjpCJO*|M$W`D5Im{8* zgMk+o4B$r0J|qhzkSs(|gy;!?8o{7m5ZJ;X@PJ<pV+vz0V?SRl6KEWynW2_BoFRoF zkRga6f+2+oG(ZXNkTZk2<Fza$;BI*>D`@<qhBb}3h9RC6G(rO5H!}w_XflK15!^=r z4H#&$gNKKT!a<cJOIChn-Yx#3-1vf`{Pd#K;^O$E#G+gLsTH8wvLqfB$l!uB8dQ+8 zfh$IEjdzO;lH!X%o#0!H#YIslB`CP346ch0g2DvU%x7TaVH9HIg4S6m5dsSzNF@v| z<S~ZFKu#_KxuOU(UIaE8Df&U_24ZIz0|UcqkewhQ4n_`s4rUJKB9JKd;u$nv4Vzp^ zWrj?zG=m1ln88IfYYJ;HgC^T8VgI6()S}cBXVB!2Zz{M{K<PHYQx3>p&<sQ~w7bM~ zi#s_#Cnq%-JYRT=8)}eCW^#!pv=0TUd2g|T=8uYz`~+?haL30N6r~m<7Ny3=p9Xmg zlvNm*s^p=@AiGBoNi9nD0u70P6B%fP6XL}h<}8M01{Vg<s7?yA4FhO+p@spJ^=g=# zLGzz1e(;O|vJzwjr~(Jqg6Rx346y>Wj5VOK2KFMB8perCg&<ALh$#V0CIlPoDbVbr zp202V?9@syA3}gU`HRa2G$aE`1$O6Qeq>{)(#P({ywvjectp1Wq4ySde0)A6n!uBC zEZM1*#kW{WDhpC?vB$@Q^})vPK|x-W3d#nY;6`yuW^xI{MWFbDB&A#i28MGW_kaom z21YT)Drr0}!kiVk#SJz(H?<@qKcy%QltDm7B3Xm5D~W-D;S|g+4n`j4B9I75k_ANr zxJ7}SWK$TUm{XXdSW=jySRsAoC^m3wjO`Y0U}<VGDABnW<(C#z@wgWy7Gx+CrzRF9 zXZX3nV;2;&AP<7aVsjW67&^ccpB)VGOdSkapdmtzqOf#^4$xE@BdGZT9VljM;pkw< zVgV%~5Uypekf~vIVTcu}WdZ5SVy$6GVMzlI3>MmjGo-MB^nzqSs==m#G6)-vWCj}j z1Sd0@T1HTA0*y)*v6L{?FqSYkGa-^3I50I?Zn0<P6@y0ZqS!Nw!J&SOE3>$`GzrWG zx6U+~iqb*x&6JK2(U}Yk47Wjn4+>&7hAJ(Tkb{q;VoP)IU<7Fe1*I^^9MGH?*!6rM z*LN_~Fg7zmvIxYzjJMd+Q%e#{N{XPaDk=t*ugG-_C}R|%ISkyihT8~gVS*wKoGn4q zhhQ6pVwh^dv)0WFpm}Q~TOn36LqdVk^A=+{SOh}At-A+uKgcs|44Q1{<K3*8C8@c^ zMWDgsTWpC11*v%{(2;G(ya>4W!=4K%9>IPBn+_XM16u-Wl3xc|0;v?Z7(oNqa*SM{ z4vHA|fXAFH0k`$b85kI%1PT(1N-`63aw_A&eT5Y8ushgVBm!)60RscWC6LXaYzJ$} z7lA}I8E<jsrj{gvQwvI_0|hcT(}6k`uzCWP>CzdZSkoDz*wPuI*i$&b*$*^j!U>(6 z<OF9#u3KzDsRj8(B~?5{U|In@BjD!-uRcJ@282O&6@v-^P=e250+Y-oELp52Y#j{E zj1XAMT*BVLP{Z8J6vNcP5YN%U5YGu#$<@J-1<H|}DU9L_%^*2$a83mcq=CeFp!trc zhAD+1o3p5_gCUEzhN*@zg#|POl@|aug|CAli@TYzXckDegdf6#tI<he1*-y;4BX9( zvl&v@<}%l^)G!w9D-i&-Ntzh*I7$RTEa>nNyI++oVze|qB|kndza%~*F)t-PGfyui zu>>VKgF*qEfj~tZI5|tek~64ON@3^)FUEi*YE4E>#v*HQzPZIxo|0Ll2}|37MP*=N zNOJ`g+mHesl$~CK5&$H9S1Cgx25$xjO(BD(N0UpcH0>0^6N~aP^U|%rE$M=yR9#S| zRa%moq5x^TD5PcPr0OVCC4#zIxtYbF4ilu=omP~Xn+lr!Q7F&M$x-n04*_*<vO!&^ z%skM{B&a(Ul$uzapH~bXu1ZQ(NK{BF%Fj;CQvkOvVXKM~L1nt0rqC^>{Nh_I1&JjY zx7ZVtiXn`Y%p%aDjG{tN-T)OcAO{tJigoY;lOoW76?jafC>Eqb6y|<t+q^g)r2ZCX zA++i!zQtV(8mfa#Yu#eYEC$IJfx-{mJAjM>LZ(rQ>OjRSJ8TLJ6f;GjA$o8%xu zgW>_yrDkB1V-#YPU=(BIU=(2DV-#Z&V`O3EVyfatiye4=0j(ASVQ`aX3j+f~#X8Vn zg$qOMk{G61=317DSvAaAj4lkZ6JnTZS!-Epn6sE_7_-@nt!h|7ED&FkA)CD@BZU#f zp3RWL1d^G}kitBdwU(`hsVJ_5xrVKov5B#8MF|Ut#mG=7Q3F;j$pBI-$&kVVV#Twj zu%e1*vlXGMWCg2aE0hRlDC7XO=s|5tzbZ#N1t&-Z!{!qdK<$vs{Jg}XN`>Omq+C!x z9Na?#O-!bP7Knkne6Z+Is1j62%u4|&MF>ghDY&5~HqbB{DCy}HwSgj?8)=3@lf9@J z)Gz^+rI2(18nOXLD|k>XN(9vYgr^c{<Z7~mvsM%blwS;qd&a_BjPbWvGD}i(5it*r zen|dnVPIhR0g89fXfp$NoJWcg65Cu%VxS5@2pZ=|iB6Nr4csH(!ZzLm9;7SI%+CY+ z;ucRqVsdtTW-h3>Exsj`nU|7TmYGtTm;)1oR1P3_K%50G8^8p(hNxy>V0Zv>AgFGF z))1f(A0{rrB9I)sumW}6K!p`J36?N0Fr+ioGFGI3k|C&79UD>0RKt|T2%6ZbWlmuN zCBYQX>}dp8l%<9_g%wmt*Rs~IqKLCX#35oSY_%*kEX8~^jF65WBSRryI713MWR4cJ z7Rv7zqpBNt!1fno4Y+j>QUvbju@vcpG9yb`W?o7WXapXd-8I>YB0&j&Ef2il;TB^~ z6mvmxNfc9I&Mn4Fq!0lIMjQhJ13M!FLoui|!vGrcV&npkI|?w$fC7b)kFf}(1|BG& z#s~<50|nG;Oko5ye?V)UN*J=3nwh|X!VFsf2MUxF<`xD}i?NxpmbrwbhPjzBg(aJ% z$ftxAq=J#5ge`>?BHql@#8?CB&9eDbIYsMg$J#4s*js^dOp2C5jGjVFinfBLLJd?% zTOmfzUO`hq6QnBIH8oZtT3a{P9;(V7?qE<c268aC1qE7q0j_dXY8g`)Y8bQGiX~DQ zL3EKq38)3fn8#KEUbY3Tewc1C>J@>sYBJpdEjUapxy78BR|0L(-C|5c4^U9`BLxaj zkiBdSRmx~RZEWq!mnjSk3{`sY?2p7MsI*dm)<-%Dxu9V!Xpdb{ll>M`QR*#rP`1s? zNxj7$<{ISWAMARI9h4ptb3kj}Qu8309F*NPSs>{`G_N!_wJ0+=J~=<HxFo(Hv7{um zC@+essNxngXmA^xQNRQ^fy6T~Fo=Tu1L{n{5(pn74<iQ?A7c?nR#P6-@k+^0j*q{^ z0I4<1KnajFuQa!yvIvxeit0hd52zxz#gv&3UXWA-YJq`ku%hXpRLq`R30|^#izT<X zH1`%;E_ih(q&@?!_q)ZGpOlrFTmo6R!~*I|LWViaK}H*a2vGkYQVW8*Rp2%sI75RI zFxaU`kp=hREe;#Vibp$8Dglq=vVgn?9>d^(vB2Z*984VSd@KUI0@6aM0`dYJLVSGc G9DD$>$2iOY diff --git a/unitgrade/__pycache__/unitgrade.cpython-38.pyc b/unitgrade/__pycache__/unitgrade.cpython-38.pyc deleted file mode 100644 index 75fca3f30359a786a8edcc4725b1b6e9cff4b706..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14886 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%0ABh7S|sfEi%}KQ zFZ#t;1CIQVBJdy%OOYukURlyI^HRV?7P$D+WGl)96_jjw;5B}?7;~bS3qT#!!ki+| z+6SaC0S8AeNHHilKrK@SCKg5>MlSHYfB>Tmc;<kIu?VCFC0IZ$LJ$TA3#dDm!U*db zl`v#6Eo1@*3^QmUe=Rd)dbfrd+!bRkVX0wmW=vtpW-0P1VFfL#XJja0OJRkGH#0Rc z)_{hq*!-%TqII=n?G-fat-v@YMN1(@Pa!5nTR~Hy1}dbj5Tj?WpsAn<QWfo*8mkbk zts83(Rb`LjXi(V=ax}P02U_|F?hUEbGNv%pFlMtAOMse~*=$7$pwYKx#yqwX#u~<E z*kA?IEk-@?G=wJ8EzlbM#FAUgnRz9e(2g8qB6`4r`f<6SfCbqJ>K|1pqYbHH8=J`d z!pOi-r3WuGkXQwkRtnI{T1O!lG@=Y0np4zdzr|FPdW#)Y@nz<uMzM#v208f$yGF5t z(newqXi0c#9;7S)l?s|HkTfC+t_w4h<CF9Aib0hfsHvG3#Z**ri#ag|qr;HLz`&3J z@)4+&3rZ@WMR<ISpeZFj#v+g`?iM6y*(Q840=yNc1vKUWT5t(k77tl)$qE@yh+<3O z4rb8gy~XF43|eCt03DYM`Nf}G37V<}k5VcmB^IHKZGplW)Y}CoO3(}lwEqWMTE$oc z8WROAbYqBTE@3HQT>x4+3+k*fOEWBFYzC=euVE^(sbN^a0h+-Abuu)W{oK-L%>;p+ z_L`ix7)x$(#21z3ftpK@@{=j0q-Zm!j0BYtMWDqFw;1z^mVr7R&?VHnL9!qLum<!r z0;=RoK_Ln%i$IH~*%;Xv*_b3)SQx9sQ9X~S&_Vq&ScMK^gDQBipFt&X3dko6C5&01 zmLqctQ!j`QT64g%fECGKA)q#3Nn%lYYKbOW(H>Aa!U>xCO-#wmOOIj;$pFoQ-(oH* z&AY`05eNGU+qgVfq6y>)P#}OjA;4HAg64Q|YX>DaqPQG1fd+9os3Q$p>&(bd!d%0U z!qm)E<N#Xx0B%czSqoTGm_el^NEEbs1JwEjEgCOj&*A_rZUpf`8%zA$Zm|?3B^H5; zOieDZCyNe&q65@Qyv3f8SPUN9I1KVUTWLW`VoB;P*5sW0;#9=)ambo~P|=7Mb+<U7 z(>W!n?I6E{BaoSmk&96TT!OMORY{<Q0bB!0IDkqe_}D*a^t^;23zRMyOPI32L(w3A zf;I-Eu=au$y@UHgS&S)c5)9y;i5aLq%3?3}0WF$`rQI5acveP+!n6|31)yFLH#EFh z7x1L8f!Kw5;PlN7nRRu037VL!QmxJbjr`YECnXlu)+j_*r{*LU6sM-t+Ef>%<|bx> zl6`HgCVLcXNq$LU&Mg*Dix@nAoe3Jyj$+M8O)N`|Vo6CYPL5*DOU}<Jj^av6EQ*KB zrbFU^_ZCY@VM=b%K2Su+fC$hkeMsg2#{_FpVqSVGB(^|vyWqC-E!N_k)YO8ay&w%o zLD>t^uP*{OP{4INm;l#k2N@U`CWGPxw2FlR)b{w#!7K!79W$~qvM}*5RtcfSOi^i` zpC(gC(Jqk5*w?9pv+!|{^&sPs5(C2CoeT^NB_Ml2t!xfPE?y2+4xS>AsHXBQ_W1ae z{N(ufqG*sAph%D602T6}iMm^?d8N4pm5_y#panAE^mmIXGatNZp{NL?1=RZ~+6rPF z01>A^1gJT9i#@jzyg%+1OKx##?k%=l$ObY{*9g2(=@wglQdVkm33#tVQ58rZXdQhN z2WZ8Fb7FBSct8?7LjvhkfrgU7g9G5^5V-OO7w%xMVDtGc4jaghDmzf7F1BD`U|?YZ p)e4|p51^562n{Bgpd>pVivX{Hv`{?*tALCEw~#iUH3uID4*(I~-lPBk diff --git a/unitgrade/__pycache__/unitgrade_helpers.cpython-36.pyc b/unitgrade/__pycache__/unitgrade_helpers.cpython-36.pyc deleted file mode 100644 index 4ec59209cb632db45b779350e883f1da9136585c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5594 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 diff --git a/unitgrade/__pycache__/unitgrade_helpers.cpython-38.pyc b/unitgrade/__pycache__/unitgrade_helpers.cpython-38.pyc deleted file mode 100644 index a40d58d2db15f43562585d0900f3bf41997aa7d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8338 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 bA}nv%L`A^k69S9^OkBYn91M&Mj12z(za{tO diff --git a/unitgrade/__pycache__/version.cpython-38.pyc b/unitgrade/__pycache__/version.cpython-38.pyc deleted file mode 100644 index 60c21c4fd046cdd33eb3c601e3df33ed76e6e12f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 166 zcmWIL<>g{vU|?w6zbiqMfq~&Mh=Yt785kHG7#J9eIT#oiQW&BbQW%37G?}Vc4fG84 zEc`SXZ*j-Rm!%dJXXfX{$FF24Vq#!`5WmcvtztrpQ;UjYN=h?QV_fo+OLJ56N{VAj j^D;})ixN{(kyub8^a?6(aoFVMr<CTT+JWr;3~~$rIqoXe -- GitLab