diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5110d89 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +playground.* diff --git a/mongodb/source/autograder.py b/mongodb/source/autograder.py index e3614e8..30c1eab 100644 --- a/mongodb/source/autograder.py +++ b/mongodb/source/autograder.py @@ -5,11 +5,67 @@ fsroot = '/autograder/source/' datasets = ['congress', 'bills'] db = pymongo.MongoClient('mongodb://127.0.0.1')['test'] +def postproc_str(data : str): # relaxed str matching + import re + return re.sub(r'[\s|_]', '', data.lower()) + +def comparator(a, b): + cmp = lambda x, y: 1 if x < y else -1 if x > y else 0 + try: + return cmp(a, b) + except Exception as e: + from collections.abc import Iterable + def itcmp(a: Iterable, b: Iterable): + if len(a) < len(b): + return -1 + elif len(a) == len(b): + for aa, bb in zip(a, b): + cmp = comparator(aa, bb) + if cmp != 0: + return cmp + else: return 1 + return 0 + + match (a, b): + case (dict(), dict()): + return itcmp([*a.keys(), *a.values()], [*b.keys(), *b.values()]) + case (Iterable(), Iterable()): + return itcmp(a, b) + case _ if type(a) == type(b): + return cmp(f'{a}', f'{b}') + case _: + return cmp(hash(type(a)), hash(type(b))) + +def postproc_iter(data): + from collections.abc import Iterable + from functools import cmp_to_key + try: + match data: + case str(): + return postproc_str(data) + case dict(): + return { postproc_iter(k):postproc_iter(v) for k, v in data.items() } + case Iterable(): # flatten, remove order and empty iterables + res = type(data)( + sorted( + [postproc_iter(d) for d in data + if not isinstance(d, Iterable) or d] + , key = cmp_to_key(comparator)) + ) + return res[0] if len(res) == 1 else res + case _: # primitives + return data + except Exception as e: # fail proof + print(e) + return data + + def evaluate(query : str): import re - query = re.sub(r'(\$?[\d\w]+)\s*:', r'"\1" :', query) + query = re.sub(r'(\$?[\d\w_]+)\s*:', r'"\1" :', query) query = re.sub(r'[\r|\n]|.\s*pretty\s*\(\s*\)', '', query).strip() - return list(eval(query))[0] if query else None + if query.endswith(';'): query = query[:-1] + return postproc_iter(list(eval(query))) if query else None for d in datasets: with open(fsroot + d + '.json', encoding = 'utf-8') as f: @@ -38,13 +94,16 @@ if submissions: module = module_from_spec(spec) spec.loader.exec_module(module) q = getattr(module, f'query{i + 1}')() - if evaluate(q) == answers[i]: + ans = evaluate(q) + if ans == answers[i]: grade += 1 feedback += 'Correct.\n' else: feedback += 'Wrong Answer.\n' - except Exception: + print('ans: ', ans, '\nsol: ', answers[i]) + except Exception as e: feedback += 'Runtime Error.\n' + print (e) else: feedback += 'No python file in submission.\n' diff --git a/neo4j/push.cmd b/neo4j/push.cmd old mode 100644 new mode 100755 diff --git a/neo4j/source/autograder.py b/neo4j/source/autograder.py index 8c1c403..3e7df4c 100644 --- a/neo4j/source/autograder.py +++ b/neo4j/source/autograder.py @@ -4,9 +4,64 @@ import neo4j, json fsroot = '/autograder/source/' datasets = ['Neo4J_dataset'] db = neo4j.GraphDatabase.driver('bolt://localhost:7687', auth = ('neo4j', '4Sfz541Lm')).session() + +def postproc_str(data : str): # relaxed str matching + import re + return re.sub(r'[\s|_]', '', data.lower()) + +def comparator(a, b): + cmp = lambda x, y: 1 if x < y else -1 if x > y else 0 + try: + return cmp(a, b) + except Exception as e: + from collections.abc import Iterable + def itcmp(a: Iterable, b: Iterable): + if len(a) < len(b): + return -1 + elif len(a) == len(b): + for aa, bb in zip(a, b): + cmp = comparator(aa, bb) + if cmp != 0: + return cmp + else: return 1 + return 0 + + match (a, b): + case (dict(), dict()): + return itcmp([*a.keys(), *a.values()], [*b.keys(), *b.values()]) + case (Iterable(), Iterable()): + return itcmp(a, b) + case _ if type(a) == type(b): + return cmp(f'{a}', f'{b}') + case _: + return cmp(hash(type(a)), hash(type(b))) + +def postproc_iter(data): + from collections.abc import Iterable + from functools import cmp_to_key + try: + match data: + case str(): + return postproc_str(data) + case dict(): + return { postproc_iter(k):postproc_iter(v) for k, v in data.items() } + case Iterable(): # flatten, remove order and empty iterables + res = type(data)( + sorted( + [postproc_iter(d) for d in data + if not isinstance(d, Iterable) or d] + , key = cmp_to_key(comparator)) + ) + return res[0] if len(res) == 1 else res + case _: # primitives + return data + except Exception as e: # fail proof + print(e) + return data + def evaluate(query : str): query = query.strip() - return db.run(query).data() if query else None + return postproc_iter(db.run(query).data()) if query else None while True: try: @@ -43,19 +98,19 @@ if submissions: spec.loader.exec_module(module) q = getattr(module, f'query{i + 1}')() def eq(a: list, b): - if a is None: return False if type(b) is tuple: return any(eq(a, bb) for bb in b) - if len(a) != len(b): return False - return all(aa == bb for aa, bb in zip(a, b)) - - if eq(evaluate(q), answers[i]): + else: return a == b + ans = evaluate(q) + if eq(ans, answers[i]): grade += 1 feedback += 'Correct.\n' else: feedback += 'Wrong Answer.\n' - except Exception: + print('ans: ', ans, '\nsol: ', answers[i]) + except Exception as e: feedback += 'Runtime Error.\n' + print(e) else: feedback += 'No python file in submission.\n'