From 0bf623bd3bbcd5761f3f0755f2bd6ce6a7d7d91d Mon Sep 17 00:00:00 2001
From: Tue Herlau <tuhe@dtu.dk>
Date: Fri, 3 Sep 2021 21:10:02 +0200
Subject: [PATCH] updates to readme

---
 README.md                                     |  79 +++++++----
 docs/README.jinja.md                          | 108 ++++++++++++++
 docs/README.md                                | 134 ++++++++++++++++++
 docs/mkdocs.py                                |  30 ++++
 requirements.txt                              |  12 ++
 .../__pycache__/unitgrade2.cpython-38.pyc     | Bin 22713 -> 22727 bytes
 .../unitgrade_helpers2.cpython-38.pyc         | Bin 0 -> 7000 bytes
 .../__pycache__/version.cpython-38.pyc        | Bin 0 -> 171 bytes
 src/unitgrade2/unitgrade2.py                  |   4 +-
 src/unitgrade2/unitgrade_helpers2.py          |   4 +-
 unitgrade/__pycache__/__init__.cpython-38.pyc | Bin 1369 -> 1316 bytes
 .../unitgrade_helpers.cpython-38.pyc          | Bin 8421 -> 8318 bytes
 unitgrade/unitgrade_helpers.py                |   8 +-
 .../__pycache__/unitgrade2.cpython-39.pyc     | Bin 29261 -> 0 bytes
 .../unitgrade_helpers2.cpython-39.pyc         | Bin 6930 -> 0 bytes
 15 files changed, 344 insertions(+), 35 deletions(-)
 create mode 100644 docs/README.jinja.md
 create mode 100644 docs/README.md
 create mode 100644 docs/mkdocs.py
 create mode 100644 requirements.txt
 create mode 100644 src/unitgrade2/__pycache__/unitgrade_helpers2.cpython-38.pyc
 create mode 100644 src/unitgrade2/__pycache__/version.cpython-38.pyc
 delete mode 100644 unitgrade2/__pycache__/unitgrade2.cpython-39.pyc
 delete mode 100644 unitgrade2/__pycache__/unitgrade_helpers2.cpython-39.pyc

diff --git a/README.md b/README.md
index c1a3fc8..6db6471 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+
 # Unitgrade
 
 Unitgrade is an automatic report and exam evaluation framework that enables instructors to offer automatically evaluated programming assignments. 
@@ -42,34 +43,60 @@ 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:
 
-```
-Starting on 02/12/2020 14:57:06
-Evaluating CS 101 Report 1
-
-Question 1: Reversal of list
-================================================================================
-*** q1.1) ListReversalItem..................................................PASS
-*** q1.2) ListReversalWordsItem.............................................PASS
-*** Question q1............................................................. 5/5
-
-Question 2: Linear regression and Boston dataset
-================================================================================
-*** q2.1) CoefficientsItem..................................................PASS
-*** q2.2) RMSEItem..........................................................PASS
-*** Question q2........................................................... 13/13
-
-Finished at 14:57:06
+```terminal
+ _   _       _ _   _____               _      
+| | | |     (_) | |  __ \             | |     
+| | | |_ __  _| |_| |  \/_ __ __ _  __| | ___ 
+| | | | '_ \| | __| | __| '__/ _` |/ _` |/ _ \
+| |_| | | | | | |_| |_\ \ | | (_| | (_| |  __/
+ \___/|_| |_|_|\__|\____/_|  \__,_|\__,_|\___| v0.0.2, started: 03/09/2021 21:08:18
+
+Week 4: Looping (use --help for options)
+Question 1: Test the cluster analysis method                                                                            
+ * q1.1) clusterAnalysis([0.8, 0.0, 0.6]) = [1, 2, 1] ?.............................................................PASS
+ * q1.2) clusterAnalysis([0.5, 0.6, 0.3, 0.3]) = [2, 2, 1, 1] ?.....................................................PASS
+ * q1.3) clusterAnalysis([0.2, 0.7, 0.3, 0.5, 0.0]) = [1, 2, 1, 2, 1] ?.............................................PASS
+ * q1.4) Cluster analysis for tied lists............................................................................PASS
+ * q1)   Total.................................................................................................... 10/10
+ 
+Question 2: Remove incomplete IDs                                                                                       
+ * q2.1) removeId([1.3, 2.2, 2.3, 4.2, 5.1, 3.2,...]) = [2.2, 2.3, 5.1, 3.2, 5.3, 3.3,...] ?........................PASS
+ * q2.2) removeId([1.1, 1.2, 1.3, 2.1, 2.2, 2.3]) = [1.1, 1.2, 1.3, 2.1, 2.2, 2.3] ?................................PASS
+ * q2.3) removeId([5.1, 5.2, 4.1, 4.3, 4.2, 8.1,...]) = [4.1, 4.3, 4.2, 8.1, 8.2, 8.3] ?............................PASS
+ * q2.4) removeId([1.1, 1.3, 2.1, 2.2, 3.1, 3.3,...]) = [4.1, 4.2, 4.3] ?...........................................PASS
+ * q2.5) removeId([6.1, 3.2, 7.2, 4.2, 6.2, 9.1,...]) = [9.1, 5.2, 1.2, 5.1, 1.2, 9.2,...] ?........................PASS
+ * q2)   Total.................................................................................................... 10/10
+ 
+Question 3: Bacteria growth rates                                                                                       
+ * q3.1) bacteriaGrowth(100, 0.4, 1000, 500) = 7 ?..................................................................PASS
+ * q3.2) bacteriaGrowth(10, 0.4, 1000, 500) = 14 ?..................................................................PASS
+ * q3.3) bacteriaGrowth(100, 1.4, 1000, 500) = 3 ?..................................................................PASS
+ * q3.4) bacteriaGrowth(100, 0.0004, 1000, 500) = 5494 ?............................................................PASS
+ * q3.5) bacteriaGrowth(100, 0.4, 1000, 99) = 0 ?...................................................................PASS
+ * q3)   Total.................................................................................................... 10/10
+ 
+Question 4: Test the fermentation rate question                                                                         
+ * q4.1) fermentationRate([20.1, 19.3, 1.1, 18.2, 19.7, ...], 15, 25) = 19.600 ?....................................PASS
+ * q4.2) fermentationRate([20.1, 19.3, 1.1, 18.2, 19.7, ...], 1, 200) = 29.975 ?....................................PASS
+ * q4.3) fermentationRate([1.75], 1, 2) = 1.750 ?...................................................................PASS
+ * 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:08:18 (0 minutes, 0 seconds)....................................................................40/40
 Provisional evaluation
------------  -----
-Question q1  5/5
-Question q2  13/13
-Total        18/18
------------  -----
-
-Note your results have not yet been registered.
+---------  -----
+q1) Total  10/10
+q2) Total  10/10
+q3) Total  10/10
+q4) Total  10/10
+Total      40/40
+---------  -----
+ 
+Note your results have not yet been registered. 
 To register your results, please run the file:
->>> cs101report1_grade.py
+>>> report1intro_grade.py
 In the same manner as you ran this file.
+
 ```
 Once you are happy with the result run the script with the `_grade.py`-postfix, in this case `cs101report1_grade.py`:
 
@@ -130,4 +157,4 @@ No. Unitgrade makes no changes outside the courseware directory and it does not
   
 - **I still have concerns about running code on my computer I cannot easily read**
 Please contact me and we can discuss your specific concerns.
-  
+  
\ No newline at end of file
diff --git a/docs/README.jinja.md b/docs/README.jinja.md
new file mode 100644
index 0000000..1ca2f9c
--- /dev/null
+++ b/docs/README.jinja.md
@@ -0,0 +1,108 @@
+{{ 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. 
+
+ - 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
+
+## Installation
+Unitgrade can be installed using `pip`:
+```
+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
+```
+source activate myenv
+conda install git pip
+pip install unitgrade
+```
+
+When you are done, you should be able to import unitgrade:
+```
+import unitgrade
+```
+
+## 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):
+```
+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:
+
+```terminal
+{{out}}
+```
+Once you are happy with the result run the script with the `_grade.py`-postfix, in this case `cs101report1_grade.py`:
+
+```
+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). 
+
+### 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.
+
+# 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.
+   
+ - **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.
+   
+### 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. 
+However, obivously you might get to a situation where your code seems to work, but a test fails. In that case, it is worth looking into the code in `report1.py` to work out what is going on. 
+
+ - **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. 
+   
+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?**
+To make sure the desired output of the tests is always up to date, the tests are computed from a working version of the code and loaded from the disk rather than being hard-coded.
+
+ - **How do I see the output of my programs in the tests? Or the intended output?**
+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. 
+
+### Privacy/security   
+ - **I managed to reverse engineer the `report1_grade.py`/`*.token` files in about 30 minutes. If the safety measures are so easily broken, how do you ensure people do not cheat?**
+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. 
+
+- **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.
+  
+- **I still have concerns about running code on my computer I cannot easily read**
+Please contact me and we can discuss your specific concerns.
+  
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..406230f
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,134 @@
+
+# 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. 
+
+ - 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
+
+## Installation
+Unitgrade can be installed using `pip`:
+```
+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
+```
+source activate myenv
+conda install git pip
+pip install unitgrade
+```
+
+When you are done, you should be able to import unitgrade:
+```
+import unitgrade
+```
+
+## 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):
+```
+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:
+
+```
+Starting on 02/12/2020 14:57:06
+Evaluating CS 101 Report 1
+
+Question 1: Reversal of list
+================================================================================
+*** q1.1) ListReversalItem..................................................PASS
+*** q1.2) ListReversalWordsItem.............................................PASS
+*** Question q1............................................................. 5/5
+
+Question 2: Linear regression and Boston dataset
+================================================================================
+*** q2.1) CoefficientsItem..................................................PASS
+*** q2.2) RMSEItem..........................................................PASS
+*** Question q2........................................................... 13/13
+
+Finished at 14:57:06
+Provisional evaluation
+-----------  -----
+Question q1  5/5
+Question q2  13/13
+Total        18/18
+-----------  -----
+
+Note your results have not yet been registered.
+To register your results, please run the file:
+>>> cs101report1_grade.py
+In the same manner as you ran this file.
+```
+Once you are happy with the result run the script with the `_grade.py`-postfix, in this case `cs101report1_grade.py`:
+
+```
+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). 
+
+### 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.
+
+# 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.
+   
+ - **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.
+   
+### 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. 
+However, obivously you might get to a situation where your code seems to work, but a test fails. In that case, it is worth looking into the code in `report1.py` to work out what is going on. 
+
+ - **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. 
+   
+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?**
+To make sure the desired output of the tests is always up to date, the tests are computed from a working version of the code and loaded from the disk rather than being hard-coded.
+
+ - **How do I see the output of my programs in the tests? Or the intended output?**
+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. 
+
+### Privacy/security   
+ - **I managed to reverse engineer the `report1_grade.py`/`*.token` files in about 30 minutes. If the safety measures are so easily broken, how do you ensure people do not cheat?**
+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. 
+
+- **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.
+  
+- **I still have concerns about running code on my computer I cannot easily read**
+Please contact me and we can discuss your specific concerns.
+  
\ No newline at end of file
diff --git a/docs/mkdocs.py b/docs/mkdocs.py
new file mode 100644
index 0000000..7e3b4e6
--- /dev/null
+++ b/docs/mkdocs.py
@@ -0,0 +1,30 @@
+import jinjafy
+import jinja2
+import os
+import unitgrade_private2
+import subprocess
+
+if __name__ == "__main__":
+    out = subprocess.check_output("python --version").decode("utf-8")
+    fn = unitgrade_private2.__path__[0] + "/../../examples/02631/instructor/programs/report1intro.py"
+    # fn = unitgrade_private2.__path__[0] + "/../../examples/02631/instructor/programs"
+    print(fn)
+    # out = subprocess.check_output(f"cd {os.path.dirname(fn)}", shell=True)
+    out = subprocess.check_output(f"cd {os.path.dirname(fn)} && python {os.path.basename(fn)} --noprogress", shell=True, encoding='utf8', errors='strict')
+    # out = out.decode()
+    out = out.replace("", "")
+    out = out.replace("[0m", "")
+    print(out)
+
+
+    env = jinja2.Environment()
+    data = {'out': out}
+    with open('README.jinja.md', 'r') as f:
+        output = env.from_string(f.read()).render(data)
+
+    with open("../README.md", 'w') as f:
+        f.write(output)
+
+    print(out)
+
+    pass
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..5fcd905
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,12 @@
+pandas
+coverage
+pyfiglet
+openpyxl
+tabulate
+compress_pickle
+tqdm
+xlwings
+colorama
+numpy
+scikit_learn
+snipper
diff --git a/src/unitgrade2/__pycache__/unitgrade2.cpython-38.pyc b/src/unitgrade2/__pycache__/unitgrade2.cpython-38.pyc
index 3f772f63e4a0c6727d15b104e6cf981814c53043..1b38fe05882214361b2093708ba905f5979e9d14 100644
GIT binary patch
delta 2719
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
Q$$k+j?8O``9Ksw-00X+BA^-pY

delta 2696
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

diff --git a/src/unitgrade2/__pycache__/unitgrade_helpers2.cpython-38.pyc b/src/unitgrade2/__pycache__/unitgrade_helpers2.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d80c99b05ba9b98faefe0a10c407cb0555e97b32
GIT binary patch
literal 7000
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
b!t#bqRD@9g2AQ}tIM{?(gm@Sj!SF8t6%&rN

literal 0
HcmV?d00001

diff --git a/src/unitgrade2/__pycache__/version.cpython-38.pyc b/src/unitgrade2/__pycache__/version.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..db464afea681e8e5e699a657c42c25cc14e1298b
GIT binary patch
literal 171
zcmWIL<>g{vU|^75Z;&X;z`*br#6iZ43=9ko3=9m#91IK$DGX5zDU87knoL!!26_g1
zMt+)%x47fu%TkMqGxPJ}<5w~iF)=Veh+o#uRxzQ)sYS&xC8ZguF)sPZrManjCB-qN
pd6^~YMTse?F~vp62%b?4)EvEn%3B;Zx%nxjIjMFa2Yd#(2mpJ;E2;nh

literal 0
HcmV?d00001

diff --git a/src/unitgrade2/unitgrade2.py b/src/unitgrade2/unitgrade2.py
index a0290ca..a97d2c5 100644
--- a/src/unitgrade2/unitgrade2.py
+++ b/src/unitgrade2/unitgrade2.py
@@ -582,7 +582,8 @@ class UTestCase(unittest.TestCase):
         self.wrap_assert(self.assertEqual, first, msg)
 
     def _cache_file(self):
-        return os.path.dirname(inspect.getfile(self.__class__)) + "/unitgrade/" + self.__class__.__name__ + ".pkl"
+        # The filename-directory stuff is a bit tricky but this seems robust.
+        return os.path.dirname(inspect.getabsfile(type(self))) + "/unitgrade/" + self.__class__.__name__ + ".pkl"
 
     def _save_cache(self):
         # get the class name (i.e. what to save to).
@@ -602,6 +603,7 @@ class UTestCase(unittest.TestCase):
         cfile = self._cache_file()
         if os.path.exists(cfile):
             try:
+                # print("\ncache file", cfile)
                 with open(cfile, 'rb') as f:
                     data = pickle.load(f)
                 self.__class__._cache = data
diff --git a/src/unitgrade2/unitgrade_helpers2.py b/src/unitgrade2/unitgrade_helpers2.py
index d007fd2..6838647 100644
--- a/src/unitgrade2/unitgrade_helpers2.py
+++ b/src/unitgrade2/unitgrade_helpers2.py
@@ -36,6 +36,7 @@ parser.add_argument('--showexpected',  action="store_true",  help='Show the expe
 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.')
+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):
     args = parser.parse_args()
@@ -54,7 +55,8 @@ def evaluate_report_student(report, question=None, qitem=None, unmute=None, pass
     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,
+
+    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,
                                           show_tol_err=show_tol_err)
 
 
diff --git a/unitgrade/__pycache__/__init__.cpython-38.pyc b/unitgrade/__pycache__/__init__.cpython-38.pyc
index 1e78fe7d5e0b0725357397a89d63a7266b1f0aa7..9bc3be67fa220ac7114c9c4fa873a07a52b5d4d5 100644
GIT binary patch
delta 368
zcmcb~wS<c|l$V!_fq{WR%t<}bXCkj`pc?}NLkdF*V-7<ULkd?4a|%lfV-#ZwYYJNn
zLljdAdkRMjLlkogLkedOOD<~^Yc5+9TP}MPdoD*5M=oa+XD(M17b8OoV=#jz_e+q;
z6Q^xqyfxXIv5xx|Q%c?~#<Z0TMIw{0FnThwPL^YmWfYri!{n(c!oa{#1SUio7#MEx
zBqt_kq{gRKWEPhc3otM+6f=R$Vk#1!ypCx*_bsNPq#|aJ7M95c%vY3!L3$WdAqF5!
z;ewb_l$w|#Iys$1mQiMMD~oeEcM&^Cs~Ct72N5z13=C1irFoep=|zbtsd{i$kt|4B
v4n)X<2n7(K2qKg~1lT470d_fuO>TZlX-=vgBgoYp3=9k$EL_|itQ`CRqSZ~&

delta 465
zcmZ3&b(4!Xl$V!_fq{YH;IGhx@`=2%^+5~_3@HpLj5!Rsj8Tk?AU0DDQxsDQcM3}i
zYYSr(a|&AudkaGpOA1E{XA46VYYIaOR}Nb)dlY*vM-)dcXB1~HR}@z+cNBLnPZSR$
zLkd$cgC@^QkS&^wx47fu%TkMqGxPJ}<NZRCnL)x(%*MdLFtKkTW7K2=#yajOrj)!}
zjA<(wii9WcWb~8=$rg)%lrnNK@-Xr+axt<na53^QN-?r96p2n&Wb)J$W?*0_0uvyG
zw|J5hlQUA|Q!6ryONs><7#NC~K>C@A#3oN>+Rh!tRFqW20@A`dIh6T|vJe9U!!5>C
zhye&wxFDt!r6#6`P4;1tWt5tn#o}Dg8zoqpmsyftl$esL2lY}B$owL9kddMwLJUMm
zfs7SKsD-nNq(Ra$AVL;I$bkrX5TO7fz%D=#U>|YV<mRW8=A_y&f*e~6iaH)f4i+wM
I4pt6+05GFpQ2+n{

diff --git a/unitgrade/__pycache__/unitgrade_helpers.cpython-38.pyc b/unitgrade/__pycache__/unitgrade_helpers.cpython-38.pyc
index f096dbdbc9ef5f84f60d6d6f8229efe6b68c7e04..d857353c3bd718bb3c0ba382c9781006cabc9fbb 100644
GIT binary patch
delta 2520
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

delta 2764
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
Y4V$P4qW}bQa0PR4FfcMOGBNxE0E5V(V*mgE

diff --git a/unitgrade/unitgrade_helpers.py b/unitgrade/unitgrade_helpers.py
index d5b1c53..06d2e37 100644
--- a/unitgrade/unitgrade_helpers.py
+++ b/unitgrade/unitgrade_helpers.py
@@ -43,12 +43,6 @@ parser.add_argument('--showcomputed',  action="store_true",  help='Show the answ
 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.')
 
-# parser.add_argument('integers', metavar='N', type=int, nargs='+',
-#                     help='an integer for the accumulator')
-# parser.add_argument('--sum', dest='accumulate', action='store_const',
-#                     const=sum, default=max,
-#                     help='sum the integers (default: find the max)')
-
 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:
@@ -66,7 +60,7 @@ def evaluate_report_student(report, question=None, qitem=None, unmute=None, pass
     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,
+    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,
                                           show_tol_err=show_tol_err)
 
     try:  # For registering stats.
diff --git a/unitgrade2/__pycache__/unitgrade2.cpython-39.pyc b/unitgrade2/__pycache__/unitgrade2.cpython-39.pyc
deleted file mode 100644
index 1e5d2f9ddba8b33738b71732e85178cc0753f2bc..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 29261
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
Yhl2yO!;6oJOIWC$L4<>uLzsgJ0NMKGhX4Qo

diff --git a/unitgrade2/__pycache__/unitgrade_helpers2.cpython-39.pyc b/unitgrade2/__pycache__/unitgrade_helpers2.cpython-39.pyc
deleted file mode 100644
index a2beacfe63070c1f9567a43328ec23c91aaeb00b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 6930
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
SiA$Y>O^8K^hk+3c{{jFY7k-8S

-- 
GitLab