Skip to content
Snippets Groups Projects
Commit 674c90a8 authored by tuhe's avatar tuhe
Browse files

Updating with latest changes

parent 649cda88
No related branches found
No related tags found
No related merge requests found
pandas pandas
coverage coverage
pyfiglet
openpyxl openpyxl
tabulate tabulate
compress_pickle compress_pickle
...@@ -17,4 +16,5 @@ diskcache # dashboard ...@@ -17,4 +16,5 @@ diskcache # dashboard
watchdog # dashboard watchdog # dashboard
flask_socketio # dashboard flask_socketio # dashboard
flask # dashboard flask # dashboard
Werkzeug>=2.2.0 # dashboard Werkzeug>=2.3.0 # dashboard. Bumping it one more bc. of other issue with Anaconda.
pyfiglet<1.0.0
...@@ -38,8 +38,8 @@ setuptools.setup( ...@@ -38,8 +38,8 @@ setuptools.setup(
packages=setuptools.find_packages(where="src"), packages=setuptools.find_packages(where="src"),
python_requires=">=3.8", python_requires=">=3.8",
license="MIT", license="MIT",
install_requires=['numpy', 'tabulate', "pyfiglet", "coverage", "colorama", 'tqdm', 'importnb', 'requests', "pandas", install_requires=['numpy', 'tabulate', "pyfiglet<1.0.0", "coverage", "colorama", 'tqdm', 'importnb', 'requests', "pandas",
'watchdog', 'flask_socketio', 'flask', 'Werkzeug', 'diskcache', # These are for the dashboard. 'watchdog', 'flask_socketio', 'flask', 'Werkzeug>=2.3.0', 'diskcache', # These are for the dashboard.
], ],
include_package_data=True, include_package_data=True,
package_data={'': ['dashboard/static/*', 'dashboard/templates/*'],}, # so far no Manifest.in. package_data={'': ['dashboard/static/*', 'dashboard/templates/*'],}, # so far no Manifest.in.
......
Metadata-Version: 2.1 Metadata-Version: 2.1
Name: unitgrade Name: unitgrade
Version: 1.0.0.0 Version: 1.0.0.7
Summary: A student homework/exam evaluation framework build on pythons unittest framework. Summary: A student homework/exam evaluation framework build on pythons unittest framework.
Home-page: https://lab.compute.dtu.dk/tuhe/unitgrade Home-page: https://lab.compute.dtu.dk/tuhe/unitgrade
Author: Tue Herlau Author: Tue Herlau
...@@ -13,6 +13,20 @@ Classifier: Operating System :: OS Independent ...@@ -13,6 +13,20 @@ Classifier: Operating System :: OS Independent
Requires-Python: >=3.8 Requires-Python: >=3.8
Description-Content-Type: text/markdown Description-Content-Type: text/markdown
License-File: LICENSE License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: tabulate
Requires-Dist: pyfiglet<1.0.0
Requires-Dist: coverage
Requires-Dist: colorama
Requires-Dist: tqdm
Requires-Dist: importnb
Requires-Dist: requests
Requires-Dist: pandas
Requires-Dist: watchdog
Requires-Dist: flask_socketio
Requires-Dist: flask
Requires-Dist: Werkzeug>=2.3.0
Requires-Dist: diskcache
# Unitgrade # Unitgrade
Unitgrade is an autograding framework which enables instructors to offer automatically evaluated programming assignments in a maximally convenient format for the students. Unitgrade is an autograding framework which enables instructors to offer automatically evaluated programming assignments in a maximally convenient format for the students.
......
numpy numpy
tabulate tabulate
pyfiglet pyfiglet<1.0.0
coverage coverage
colorama colorama
tqdm tqdm
...@@ -10,5 +10,5 @@ pandas ...@@ -10,5 +10,5 @@ pandas
watchdog watchdog
flask_socketio flask_socketio
flask flask
Werkzeug Werkzeug>=2.3.0
diskcache diskcache
...@@ -244,9 +244,23 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa ...@@ -244,9 +244,23 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa
return results, table_data return results, table_data
def python_code_binary_id(python_code):
"""
Return an unique id of this python code assuming it is in a binary encoding. This is similar to the method below,
but the method below removes docstrings and comments (and take a str as input). I have opted not to do that since
it mess up encoding on clients computers -- so we just digest everything.
:param python_code:
:return:
"""
hash_object = hashlib.blake2b(python_code)
return hash_object.hexdigest()
def python_code_str_id(python_code, strip_comments_and_docstring=True): def python_code_str_id(python_code, strip_comments_and_docstring=True):
s = python_code s = python_code
print(s)
if strip_comments_and_docstring: if strip_comments_and_docstring:
try: try:
s = remove_comments_and_docstrings(s) s = remove_comments_and_docstrings(s)
......
...@@ -18,6 +18,7 @@ from unitgrade.runners import UTextResult ...@@ -18,6 +18,7 @@ from unitgrade.runners import UTextResult
from unitgrade.utils import gprint, Capturing2, Capturing from unitgrade.utils import gprint, Capturing2, Capturing
from unitgrade.artifacts import StdCapturing from unitgrade.artifacts import StdCapturing
from unitgrade.utils import DKPupDB from unitgrade.utils import DKPupDB
import platform
colorama.init(autoreset=True) # auto resets your settings after every output colorama.init(autoreset=True) # auto resets your settings after every output
...@@ -40,7 +41,6 @@ class classmethod_dashboard(classmethod): ...@@ -40,7 +41,6 @@ class classmethod_dashboard(classmethod):
r = np.random.randint(1000 * 1000) r = np.random.randint(1000 * 1000)
db.set('run_id', r) db.set('run_id', r)
db.set('coverage_files_changed', None) db.set('coverage_files_changed', None)
state_ = 'fail' state_ = 'fail'
try: try:
_stdout = sys.stdout _stdout = sys.stdout
...@@ -74,6 +74,7 @@ class classmethod_dashboard(classmethod): ...@@ -74,6 +74,7 @@ class classmethod_dashboard(classmethod):
super().__init__(dashboard_wrap) super().__init__(dashboard_wrap)
class Report: class Report:
title = "report title" title = "report title"
abbreviate_questions = False # Should the test items start with 'Question ...' or just be q1). abbreviate_questions = False # Should the test items start with 'Question ...' or just be q1).
...@@ -128,12 +129,49 @@ class Report: ...@@ -128,12 +129,49 @@ class Report:
else: else:
root_dir = self.pack_imports[0].__file__ root_dir = self.pack_imports[0].__file__
self_file = self._file()
root_dir_0 = root_dir # Backup for pretty printing.
self_file_0 = self_file
if platform.system() == "Windows":
# Windows does not distinguish upper/lower case paths. We convert all of them to lower case for simplicity.
self_file = self_file.lower()
root_dir = root_dir.lower()
root_dir = os.path.dirname(root_dir) root_dir = os.path.dirname(root_dir)
relative_path = os.path.relpath(self._file(), root_dir) relative_path = os.path.relpath(self_file, root_dir)
modules = os.path.normpath(relative_path[:-3]).split(os.sep) modules = os.path.normpath(relative_path[:-3]).split(os.sep)
relative_path = relative_path.replace("\\", "/") relative_path = relative_path.replace("\\", "/")
if relative_path.startswith(".."): if relative_path.startswith(".."):
raise Exception("Bad relative path. setup failed. ", root_dir, self._file()) error = """
--------------------------------------------------------------------------------------
Oh no, you got an installation problem!
You have accidentally downloaded (and installed) the course software in two locations on your computer.
The first place is:
> %s
And the second place is the location that contains this file, namely:
> %s
I can't be certain which of these two contains your actual homework, so therefore I have to give you an error :-(.
But it is easy to fix! Determine which of the two folders contain your homework and simply delete the other one. That
should take care of the problem!
(The terminal in VS Code will tell you the location on your computer you have open right now -- most likely, that is the
location of the right 02002student folder!).
In the future, try to avoid downloading and installing the course software many times -- most issues
can be solved in a much simpler way.
If this problem persists, please contact us on piazza, discord, or directly on tuhe@dtu.dk (or come by my office, building 321, room 127).
Include a copy of this error and a screenshot of VS Code.
"""%(root_dir_0, self_file_0)
print(error)
sys.exit(1)
raise Exception(error)
return root_dir, relative_path, modules return root_dir, relative_path, modules
......
...@@ -282,10 +282,6 @@ def hash_string(s): ...@@ -282,10 +282,6 @@ def hash_string(s):
Right now it is used in the function in 02002 when a creating the index of student-downloadable evaluations.""" Right now it is used in the function in 02002 when a creating the index of student-downloadable evaluations."""
# gfg = hashlib.blake2b() # gfg = hashlib.blake2b()
return hashlib.blake2b(s.encode("utf-8")).hexdigest() return hashlib.blake2b(s.encode("utf-8")).hexdigest()
# return base64.b64encode(b).decode("utf-8")
# gfg.update(s.encode("utf-8"))
# return gfg.digest()
def hash2url(hash): def hash2url(hash):
return hash[:16] return hash[:16]
...@@ -351,16 +347,50 @@ def checkout_remote_results(remote_url, manifest): ...@@ -351,16 +347,50 @@ def checkout_remote_results(remote_url, manifest):
html = response.read().decode() html = response.read().decode()
# print( html ) # print( html )
break break
# if debug:
# url = f"https://cp.pages.compute.dtu.dk/02002public/_static/evaluation/project_evaluations_2023fall/project0/student_html/5a2db54fcce2f3ee/index.html"
# with urllib.request.urlopen(url) as response:
# html = response.read().decode()
if html is not None: if html is not None:
import pandas as pd try:
dfs = pd.read_html(html) from xml.etree import ElementTree as ET
df = dfs[0] s = html[html.find("<table"):html.find("</table>")+len("</table>")]
table = ET.XML(s)
head = list(iter(table))[0]
body = list(iter(table))[1]
from collections import defaultdict
dd = defaultdict(list)
keys = []
for tr in head:
for td in tr:
keys.append(td.text)
for tr in body:
for k, td in enumerate(tr):
dd[keys[k]].append(td.text)
except Exception as e:
print("Sorry results not parsed. Perhaps bad file upload?")
keys = ["Key", "Values"]
dd = {keys[0]: ['Results were not parsed', 'Score'], keys[1]: ['Results were not readable. Contact a TA', 0]}
# rows = iter(table)
# for r in list(iter(table)):
# print(r)
# import tabulate
# print( tabulate.tabulate(dd, headers="keys") )
# headers = [col.text for col in next(rows)]
# for row in rows:
# values = [col.text for col in row]
# print(dict(zip(headers, values)))
# dd[keys[1]][-1]
# dfs = pd.read_html(html)
# df = dfs[0]
# df.__format__() # df.__format__()
# tabular # tabular
# print( df.to_string(index=False) ) # print( df.to_string(index=False) )
# df.as # df.as
result = dict(html=html, df=df, score=float( df.iloc[2].to_list()[-1] ), url=url) result = dict(html=html, dict=dd, score=float( dd[keys[1]][-1] ), url=url)
else: else:
result=dict(html=html) result=dict(html=html)
...@@ -368,8 +398,6 @@ def checkout_remote_results(remote_url, manifest): ...@@ -368,8 +398,6 @@ def checkout_remote_results(remote_url, manifest):
## Key/value store related. ## Key/value store related.
class DKPupDB: class DKPupDB:
DISABLE_DB = False DISABLE_DB = False
...@@ -433,10 +461,3 @@ class DKPupDB: ...@@ -433,10 +461,3 @@ class DKPupDB:
return item in self.dk[self.name_] #keys() return item in self.dk[self.name_] #keys()
# return item in self.dk # return item in self.dk
# if __name__ == "__main__":
# url = "https://cp.pages.compute.dtu.dk/02002public/_static/evaluation/"
# manifest = """
# /home/tuhe/Documents/unitgrade_private/src/unitgrade_private/pipelines/tmp/students/cp/project0/Project0_handin_0_of_10.token 7720b41ab925098956c7db37c8292ce3a7b4ded96f4442234dee493c021fc5f7294e543de78630aaf873b756d25bf7b4fd7eb6e66cec282b54f0c35b83e9071f
# """
# # checkout_remote_results(url, manifest = manifest)
__version__ = "1.0.0.0" __version__ = "1.0.0.8"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment