import pymongo, json, re from bson import json_util print('ver: 1.5') # dbprep fsroot = '/autograder/source/' datasets = ['movies', 'comments'] db = pymongo.MongoClient('mongodb://127.0.0.1')['test'] points = (4,4,5,5,8,8,8,8) def postproc_str(data : str): # relaxed str matching return re.sub(r'[\s|_|.]', '', data.lower()) def postproc_keys(data : str): if type(data) is not str: return postproc_iter(data) _dict = {} data = postproc_str(data) for k, v in _dict.items(): data = re.sub(k, v, data) return data 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: 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): c = comparator(aa, bb) if c != 0: return c else: return 1 return 0 from bson import ObjectId match (a, b): case (dict(), dict()): return itcmp([*a.keys(), *a.values()], [*b.keys(), *b.values()]) case (ObjectId(aa), ObjectId(bb)): return cmp(aa, bb) 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, naive = False): from collections.abc import Iterable from functools import cmp_to_key try: match data: case str(): return postproc_str(data) case dict(): return { (i if naive else postproc_keys(k)) : postproc_iter(v, naive) for i, (k, v) in enumerate(data.items()) } case Iterable(): # flatten, remove order and empty iterables res = type(data)( sorted( [postproc_iter(d, naive) 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): if type(query) is not str: return tuple(evaluate(q) for q in query) query = re.sub(r'//[^\n]*', '', query) query = re.sub(r'(\$?[\d\w_]+)[\s\r\n]*:', r'"\1" :', query) query = re.sub(r'[\r\n]|.\s*pretty\s*\(\s*\)|.\s*sort\s*\([^\)]*\)', '', query).strip() if not query: return [None] * 2 query = re.sub(r'.\s*aggregate\s*\(\s*([^\[^\s][^\)]*)\)', r'.aggregate([\1])', query) if query.endswith(';'): query = query[:-1] true = True false = False data = list(eval(query)) return [postproc_iter(data, n) if query else None for n in (True, False)] for d in datasets: with open(fsroot + d + '.json', encoding = 'utf-8') as f: # ds = re.sub(r'{\s*"\$oid"\s*:\s*("\w*")\s*}', r'ObjectId(\1)', f.read()) jsonds = '[' + f.read().strip().replace('\n', ',') + ']' db[d].insert_many(json.loads(jsonds, object_hook=json_util.object_hook)) from solution import sols answers = [evaluate(s) for s in sols] # grading from os import listdir from importlib.util import module_from_spec, spec_from_file_location subroot = '/autograder/submission/' feedback = '' submissions = [subroot + f for f in listdir(subroot) if f.strip().lower().endswith('.py')] grade = 0 n_queries = len(sols) def grade78(ans, i): sol = answers[i] others = ('otherbill', 'otherperson')[i - 6] if type(ans) != list or len(ans) != len(sol): return False for a in ans: if a not in sol: if type(a) is dict: try: for ak in a.keys(): if ak.startswith(others): key_others = ak[len(others):] t = a[key_others] a[key_others] = a[ak] a[ak] = t if a not in sol: return False else: sol.remove(a) except Exception as e: print(e) return False else: return False return True wa = rte = False if submissions: submission = submissions[0] for i in range(n_queries): feedback += f'Query {i + 1}: ' try: spec = spec_from_file_location('curr', submission) module = module_from_spec(spec) spec.loader.exec_module(module) q = getattr(module, f'query{i + 1}')() ans = evaluate(q) def eq(i): _cmp = lambda a, b: any(aa == bb for aa, bb in zip(a, b)) if type(answers[i]) is tuple: return any(_cmp(ans, aa) for aa in answers[i]) else: return _cmp(ans, answers[i]) if eq(i): grade += points[i] feedback += 'Correct.\n' else: feedback += 'Wrong Answer.\n' wa = True print('ans: ', ans, '\nsol: ', answers[i]) except Exception as e: rte = True feedback += 'Runtime Error.\n' print (e) else: feedback += 'No python file in submission.\n' max_score = sum(points) if rte: feedback += ('\nPlease check for syntax errors first if you encountered runtime errors.\n' + 'If you believe it\'s a mistake, contact the TA at sun1226@purdue.edu for assistance.') # output results = { 'output': feedback, 'score': grade, 'max_score': max_score, } with open('/autograder/results/results.json', 'w') as res: json.dump(results, res)