From b6ead38d702e0b3d2161b1e5e4a05a17fe173ae5 Mon Sep 17 00:00:00 2001 From: Tue Herlau <tuhe@dtu.dk> Date: Thu, 18 Mar 2021 02:13:52 +0100 Subject: [PATCH] Update to version 1.3: Added progress bars --- unitgrade/__init__.py | 3 +- unitgrade/__pycache__/__init__.cpython-38.pyc | Bin 1080 -> 1125 bytes .../__pycache__/unitgrade.cpython-38.pyc | Bin 11875 -> 13332 bytes .../unitgrade_helpers.cpython-38.pyc | Bin 5764 -> 6555 bytes unitgrade/__pycache__/version.cpython-38.pyc | Bin 167 -> 166 bytes unitgrade/unitgrade.py | 70 +++++++++++++-- unitgrade/unitgrade_helpers.py | 84 ++++++++++++++---- unitgrade/version.py | 2 +- 8 files changed, 131 insertions(+), 28 deletions(-) diff --git a/unitgrade/__init__.py b/unitgrade/__init__.py index d7f3cb7..605eedf 100644 --- a/unitgrade/__init__.py +++ b/unitgrade/__init__.py @@ -1,5 +1,6 @@ from unitgrade.version import __version__ import os + # DONT't import stuff here since install script requires __version__ def cache_write(object, file_name, verbose=True): @@ -28,4 +29,4 @@ def cache_read(file_name): else: return None -from unitgrade.unitgrade import Hidden, myround, mfloor, msum +from unitgrade.unitgrade import Hidden, myround, mfloor, msum, Capturing, ActiveProgress diff --git a/unitgrade/__pycache__/__init__.cpython-38.pyc b/unitgrade/__pycache__/__init__.cpython-38.pyc index 57e9c8499c7c715af060fa5dac373ccf7e32da49..dc097231ab1cc676f96b6e553c3f3a9d29cccfee 100644 GIT binary patch delta 138 zcmdnN@sxu%l$V!_fq{Wx&$OU~<cYkpj6oB%-8pi(qquW<qIfnIEoNk7n=HoE%qTc{ z7Sk;$F$M;PVz35A4kjK(9!4eRB54K&22Hlfea!L-w>X^>3rb3hGV{`J@i``!WR|4{ o6y>KEr4|=!GKOgKPTs^E#-_l)z)++(nT17{orQ~=gO!5|0M=X|*Z=?k delta 76 zcmaFLv4ev*l$V!_fq{V`(MKY|Z6dEMqt`@j_l;GH85vn8%P}=G3QS(abc<1RaxJq2 hBg^Dz%<`<7j3Jubldm#|O*UoGWM}5$=3wRE0sz1(5fK0Y diff --git a/unitgrade/__pycache__/unitgrade.cpython-38.pyc b/unitgrade/__pycache__/unitgrade.cpython-38.pyc index e2fdc61918eb16cd7180fda4503cdcf6bc966acf..5601367584ffb2dd33f99e057b47d73958c6bf42 100644 GIT binary patch delta 4860 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 JbMSHS0008JKurJu delta 3443 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 diff --git a/unitgrade/__pycache__/unitgrade_helpers.cpython-38.pyc b/unitgrade/__pycache__/unitgrade_helpers.cpython-38.pyc index 29fcc07326d80d9125066f6a3ef9ee265e53a622..d7f1dcc24f9053facd9ab6146edd241ac30a3ca1 100644 GIT binary patch delta 3493 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 delta 2618 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 QhD}t2Q2+utxD+|q08}?@B>(^b diff --git a/unitgrade/__pycache__/version.cpython-38.pyc b/unitgrade/__pycache__/version.cpython-38.pyc index d8b1642642aadb9136268b7e9bd9759eb8164c3a..79890100982f99dfa74eaaa28454b969f4f9d8fb 100644 GIT binary patch delta 31 mcmZ3^xQvlEl$V!_fq{Wx?slI9(TTh|Y^(-)hI+;m9nt}5^#<qw delta 32 ncmZ3+xSWwUl$V!_fq{YH>q)r;v5CAo>}&>lhI)p{6YbLhb_EBA diff --git a/unitgrade/unitgrade.py b/unitgrade/unitgrade.py index df8a7f8..e57ccbc 100644 --- a/unitgrade/unitgrade.py +++ b/unitgrade/unitgrade.py @@ -11,6 +11,9 @@ from io import StringIO import collections import inspect import re +import threading +import tqdm +import time myround = lambda x: np.round(x) # required. msum = lambda x: sum(x) @@ -71,8 +74,11 @@ class QItem(unittest.TestCase): title = None testfun = None tol = 0 - + estimated_time = 0.42 + _precomputed_payload = None _computed_answer = None # Internal helper to later get results. + # _precomputed_payload = None + def __init__(self, working_directory=None, correct_answer_payload=None, question=None, *args, **kwargs): if self.tol > 0 and self.testfun is None: self.testfun = self.assertL2Relative @@ -82,6 +88,8 @@ class QItem(unittest.TestCase): self.name = self.__class__.__name__ self._correct_answer_payload = correct_answer_payload self.question = None + # self.a = "not set" + super().__init__(*args, **kwargs) if self.title is None: self.title = self.name @@ -105,7 +113,14 @@ class QItem(unittest.TestCase): print(f"Element-wise differences {diff.tolist()}") self.assertEqual(computed, expected, msg=f"Not equal within tolerance {tol}") - def precomputed_resources(self): + # def set_precomputed_payload(self, payload): + # self.a = "blaaah" + # self._precomputed_payload = payload + + def precomputed_payload(self): + return self._precomputed_payload + + def precompute_payload(self): # Pre-compute resources to include in tests (useful for getting around rng). pass @@ -128,8 +143,8 @@ class QItem(unittest.TestCase): correct = self._correct_answer_payload try: - if unmute: - print("\n") + if unmute: # Required to not mix together print stuff. + print("") computed = self.compute_answer(unmute=unmute) except Exception as e: if not passall: @@ -190,8 +205,8 @@ class QPrintItem(QItem): def process_output(self, res, txt, numbers): return (res, txt) - def compute_local(self): - pass + # def compute_local(self): # Dunno + # pass def compute_answer(self, unmute=False): with Capturing(unmute=unmute) as output: @@ -215,6 +230,7 @@ class QuestionGroup(metaclass=OrderedClassMembers): items = None partially_scored = False t_init = 0 # Time spend on initialization (placeholder; set this externally). + estimated_time = 0.42 def __init__(self, *args, **kwargs): self.name = self.__class__.__name__ @@ -224,6 +240,11 @@ class QuestionGroup(metaclass=OrderedClassMembers): for gt in members: self.items.append( (gt, 1) ) self.items = [(I(question=self), w) for I, w in self.items] + self.has_called_init_ = False + + def init(self): + # Can be used to set resources relevant for this question instance. + pass class Report(): title = "report title" @@ -239,9 +260,13 @@ class Report(): import time qs = [] # Has to accumulate to new array otherwise the setup/evaluation steps cannot be run in sequence. for k, (Q, w) in enumerate(self.questions): + # print(k, Q) start = time.time() q = (Q(working_directory=self.wdir), w) q[0].t_init = time.time() - start + # if time.time() -start > 0.2: + # raise Exception(Q, "Question takes to long to initialize. Use the init() function to set local variables instead") + # print(time.time()-start) qs.append(q) self.questions = qs # self.questions = [(Q(working_directory=self.wdir),w) for Q,w in self.questions] @@ -257,6 +282,7 @@ class Report(): else: print(s) + def set_payload(self, payloads, strict=False): for q, _ in self.questions: for item, _ in q.items: @@ -268,7 +294,9 @@ class Report(): print(s) else: item._correct_answer_payload = payloads[q.name][item.name]['payload'] - if "precomputed" in payloads[q.name][item.name]: + item.estimated_time = payloads[q.name][item.name]['time'] + q.estimated_time = payloads[q.name]['time'] + if "precomputed" in payloads[q.name][item.name]: # Consider removing later. item._precomputed_payload = payloads[q.name][item.name]['precomputed'] self.payloads = payloads @@ -297,3 +325,31 @@ def extract_numbers(txt): print(txt) raise Exception("unitgrade.unitgrade.py: Warning, many numbers!", len(all)) return all + + +class ActiveProgress(): + def __init__(self, t, start=True, title="my progress bar"): + self.t = t + self._running = False + self.title = title + if start: + self.start() + + def start(self): + self._running = True + self.thread = threading.Thread(target=self.run, args=(10,)) + self.thread.start() + + def terminate(self): + self._running = False + self.thread.join() + sys.stdout.flush() + + def run(self, n): + dt = 0.1 + + n = int(np.round(self.t/dt)) + for _ in tqdm.tqdm(range(n), file=sys.stdout, position=0, leave=False, desc=self.title, ncols=100, bar_format='{l_bar}{bar}| [{elapsed}<{remaining}]'): #, unit_scale=dt, unit='seconds'): + if not self._running: + break + time.sleep(dt) \ No newline at end of file diff --git a/unitgrade/unitgrade_helpers.py b/unitgrade/unitgrade_helpers.py index 85b51ff..5edd3ee 100644 --- a/unitgrade/unitgrade_helpers.py +++ b/unitgrade/unitgrade_helpers.py @@ -2,7 +2,8 @@ import numpy as np from tabulate import tabulate from datetime import datetime import pyfiglet -from unitgrade import Hidden, myround, msum, mfloor +from unitgrade import Hidden, myround, msum, mfloor, ActiveProgress +# import unitgrade from unitgrade import __version__ # from unitgrade.unitgrade import Hidden @@ -12,6 +13,10 @@ import inspect import os import argparse import sys +import time +import threading # don't import Thread bc. of minify issue. +import tqdm # don't do from tqdm import tqdm because of minify-issue +#from threading import Thread # This import presents a problem for the minify-code compression tool. parser = argparse.ArgumentParser(description='Evaluate your report.', epilog="""Example: To run all tests in a report: @@ -62,8 +67,8 @@ def evaluate_report_student(report, question=None, qitem=None, unmute=None, pass unmute = args.unmute if passall is None: passall = args.passall - # print(passall) - results, table_data = evaluate_report(report, question=question, 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, qitem=qitem, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute) if question is None: print("Provisional evaluation") @@ -85,7 +90,10 @@ def upack(q): h = np.asarray(h) return h[:,0], h[:,1], h[:,2], -def evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False): + + +def evaluate_report(report, question=None, qitem=None, passall=False, verbose=False, show_expected=False, show_computed=False,unmute=False, show_help_flag=True, silent=False, + show_progress_bar=True): from unitgrade.version import __version__ now = datetime.now() ascii_banner = pyfiglet.figlet_format("UnitGrade", font="doom") @@ -100,40 +108,71 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa print(f"Loaded answers from: ", report.computed_answers_file, "\n") table_data = [] nL = 80 - + t_start = time.time() score = {} for n, (q, w) in enumerate(report.questions): q_hidden = issubclass(q.__class__, Hidden) + # report.globals = q.globals + # q.globals = report.globals if question is not None and n+1 != question: continue # Don't use f format strings. - print(f"Question {n+1}: {q.title}" + (" (" + str( np.round(report.payloads[q.name].get('time', 0), 2) ) + " seconds)" if q.name in report.payloads else "" ) ) - print("="*nL) + q_title_print = "Question %i: %s"%(n+1, q.title) + print(q_title_print, end="") + # sys.stdout.flush() q.possible = 0 q.obtained = 0 - q_ = {} # Gather score in this class. + # Active progress bar. + for j, (item, iw) in enumerate(q.items): if qitem is not None and question is not None and item is not None and j+1 != qitem: continue + if not q.has_called_init_: + start = time.time() + + cc = None + + if show_progress_bar: + # cc.start() + cc = ActiveProgress(t=q.estimated_time, title=q_title_print) + from unitgrade import Capturing + #eval('from unitgrade import Capturing') + with eval('Capturing')(unmute=unmute): # Clunky import syntax is required bc. of minify issue. + q.init() # Initialize the question. Useful for sharing resources. + if show_progress_bar: + cc.terminate() + print(q_title_print, end="") + + q.has_called_init_ = True + q_time =np.round( time.time()-start, 2) + + + print(" "* max(0,nL - len(q_title_print) ) + " (" + str(q_time) + " seconds)") # if q.name in report.payloads else "") + print("=" * nL) + + item.question = q # Set the parent question instance for later reference. + item_title_print = ss = "*** q%i.%i) %s"%(n+1, j+1, item.title) + + if show_progress_bar: + cc = ActiveProgress(t=item.estimated_time, title=item_title_print) + else: + print(item_title_print + ( '.'*max(0, nL-4-len(ss)) ), end="") - ss = f"*** q{n+1}.{j+1}) {item.title}" - el = nL-4 - if len(ss) < el: - ss += '.'*(el-len(ss)) hidden = issubclass(item.__class__, Hidden) - if not hidden: - print(ss, end="") - sys.stdout.flush() - import time + # if not hidden: + # print(ss, end="") + # sys.stdout.flush() start = time.time() (current, possible) = item.get_points(show_expected=show_expected, show_computed=show_computed,unmute=unmute, passall=passall, silent=silent) q_[j] = {'w': iw, 'possible': possible, 'obtained': current, 'hidden': hidden, 'computed': str(item._computed_answer), 'title': item.title} tsecs = np.round(time.time()-start, 2) + if show_progress_bar: + cc.terminate() + sys.stdout.flush() + print(item_title_print + ('.' * max(0, nL - 4 - len(ss))), end="") - # q.possible += possible * iw - # q.obtained += current * iw if not hidden: ss = "PASS" if current == possible else "*** FAILED" ss += " ("+ str(tsecs) + " seconds)" @@ -161,7 +200,14 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa report.obtained = obtained now = datetime.now() dt_string = now.strftime("%H:%M:%S") - print(f"Completed: "+ dt_string) + + dt = int(time.time()-t_start) + minutes = dt//60 + seconds = dt - minutes*60 + plrl = lambda i, s: str(i) + " " + s + ("s" if i != 1 else "") + + print(f"Completed: "+ dt_string + " (" + plrl(minutes, "minute") + ", "+ plrl(seconds, "second") +")") + table_data.append(["Total", ""+str(report.obtained)+"/"+str(report.possible) ]) results = {'total': (obtained, possible), 'details': score} return results, table_data diff --git a/unitgrade/version.py b/unitgrade/version.py index 48fef32..acf3be3 100644 --- a/unitgrade/version.py +++ b/unitgrade/version.py @@ -1 +1 @@ -__version__ = "0.1.2" \ No newline at end of file +__version__ = "0.1.3" \ No newline at end of file -- GitLab