def checkout_to_tag_then_test(hw_instance): nonlocal tag_name current_tag = hw_instance.repo.git.describe("--always") if tag_name != current_tag: # Clean first hw_instance.repo.git.checkout("--", "*") try: if tag_name == "master": tag_name = f"{hw_instance.submitter}" hw_instance.repo.git.checkout(tag_name) printing.print_green( f"[ Checked out to {tag_name}/master ]\n") else: hw_instance.repo.git.checkout(tag_name) printing.print_green(f"[ Checked out to {tag_name} ]\n") except git.GitError: printing.print_red(f"[ Couldn't checkout to {tag_name} ]") printing.print_cyan( "[ Opening shell -- ^D/exit when resolved ]") os.system("bash") else: # No cleaning in case the TA made necessary changes to the # submission that we don't want to throw away printing.print_green(f"[ Checked out to {tag_name} ]\n") hw_instance.repo.git.clean("-f", "-d") return test_func(hw_instance)
def check_late(deadline_path, iso_timestamp): """Checks if iso_timestamp is past the deadline Arguments: deadline_path: Location of the recorded assignment deadline (e.g. ~/.grade/hw1/deadline.txt) iso_timestamp: The ISO timestamp to compare against deadline (e.g. git log -n 1 --format='%aI') """ submission = datetime.fromisoformat(iso_timestamp) with open(deadline_path, "r") as d: deadline_string = d.readline() if not deadline_string: return False raw_deadline = datetime.strptime(deadline_string, "%m/%d/%y %I:%M %p") nyc_tz = timezone("America/New_York") deadline = nyc_tz.localize(raw_deadline.replace(second=59, tzinfo=None)) if submission <= deadline: printing.print_green("[ SUBMISSION ON TIME ]") return False diff = relativedelta(submission, deadline) printing.print_red(f"[SUBMISSION LATE]: Submitted {diff.days} days, " f"{diff.hours} hrs, {diff.minutes} mins, " f"and {diff.seconds} secs late") return True
def cd_then_test(hw_instance): try: hw_instance.do_cd('' if start_dir == "root" else start_dir) except ValueError: printing.print_red("[ Couldn't cd into tester's @directory, " "opening shell.. ]") os.system("bash") return test_func(hw_instance)
def grade_C2(self): """C2: dynamic analysis""" if u.compile_code(): # Non-zero exit code (e.g. compilation failed) return swap = u.cmd_popen(f"{os.path.join(os.getcwd(), 'swap')} 1 2") out, code = swap.communicate() if code: pr.print_red("[ Non-zero exit code ]") print(out) if out.splitlines()[1] == "After: 2 1": pr.print_green("[ PASS ]") else: pr.print_red("[ FAIL ] Didn't match 'After: 2 1'")
def grade_item(self, rubric_item: RubricItem): if (not self.env["test_only"] and not self.env["regrade"] and all( self.grades.is_graded(f"{rubric_item.code}.{si}") for si, _ in enumerate(rubric_item.subitems, 1))): p.print_yellow( f"[ {rubric_item.code} has been graded, skipping... ]") return # if --grade-only/-g is not provided, run tests else skip tests autogrades = None if not self.env["grade_only"]: def test_wrapper(): nonlocal autogrades self.print_header(rubric_item) autogrades = rubric_item.tester() try: utils.run_and_prompt(test_wrapper) except Exception as e: # pylint: disable=W0703 p.print_red(f"\n\n[ Exception: {e} ]") else: self.print_headerline(rubric_item) # if -t is not provided, ask for grade. If -t is provided skip if not self.env["test_only"]: p.print_line() is_late = self.hw_class.check_late_submission() if is_late: # Once we find one part of the submission that is late, the # whole thing is considered late. # Since this happens after run_and_prompt, the submission_dir # will still be checked out to the specified tag. # TODO: I actually think we shouldn't do this here, since # we don't want to mark submissions that are late on master # late if none of the tags are late... self.grades.set_late(True) self.prompt_grade(rubric_item, autogrades) else: # Let the grader know if the subitems have been graded yet for i in range(1, len(rubric_item.subitems) + 1): code = f"{rubric_item.code}.{i}" self.print_subitem_grade(code, warn=True)
def to_branch(hw_instance, branch_name: str): current_branch = hw_instance.repo.git.rev_parse("--abbrev-ref", "HEAD") target_branch = f"{hw_instance.submitter}-{branch_name}" if current_branch != target_branch: # Clean first hw_instance.repo.git.checkout("--", "*") try: hw_instance.repo.git.checkout(target_branch) printing.print_green(f"[ Checked out to {branch_name} ]\n") except git.GitError: printing.print_red(f"[ Couldn't checkout to {branch_name} ]") printing.print_cyan( "[ Opening shell -- ^D/exit when resolved ]") os.system("bash") else: # No cleaning in case the TA made necessary changes to the # submission that we don't want to throw away printing.print_green(f"[ Checked out to {branch_name} ]\n") hw_instance.repo.git.clean("-f", "-d")
def main(): """Entry-point into the grader""" parser = argparse.ArgumentParser( description="pygrader: Python Grading Framework") parser.add_argument("hw", type=str, help="homework to grade") parser.add_argument("submitter", type=str, nargs="?", default=None, help="the name of student/group to grade") parser.add_argument("-c", "--code", type=str, nargs="?", default="all", help=("rubric item (e.g. A, B4) to grade; " "defaults to all")) grading_mode = parser.add_mutually_exclusive_group() grading_mode.add_argument("-g", "--grade-only", action="store_true", help="grade without running any tests", dest="grade_only") grading_mode.add_argument("-t", "--test-only", action="store_true", help=("run tests without grading"), dest="test_only") script_mode = parser.add_mutually_exclusive_group() script_mode.add_argument("-r", "--regrade", action="store_true", help="do not skip previously graded items", dest="regrade") script_mode.add_argument("-d", "--dump-grades", action="store_true", help=("dump grades for this homework -- " "all if no submitter specified"), dest="dump_grades") script_mode.add_argument( "-s", "--status", action="store_true", help=("return grading status for this homework -- " "all if no submitter specified"), dest="status") script_mode.add_argument("-i", "--inspect", action="store_true", help=("drop into shell to inspect submission"), dest="inspect") args = parser.parse_args() env = { "regrade": args.regrade, "grade_only": args.grade_only, "test_only": args.test_only, "dump_grades": args.dump_grades, "status": args.status, "inspect": args.inspect } rubric_code = args.code if args.code else "all" tester = Grader(args.hw, args.submitter, rubric_code, env) if args.dump_grades: tester.grades.dump_grades(args.submitter, rubric_code.upper()) sys.exit() if args.status: all_graded = tester.grades.status(args.submitter, rubric_code.upper()) sys.exit(not all_graded) # If all graded, exit with 0 (success) if args.inspect: # (pygrader)user@host:pwd $ prompt = (f"{p.CGREEN}({p.CYELLOW}pygrader{p.CGREEN}){p.CEND}" f":{p.CBLUE}\\w{p.CCYAN} \${p.CEND} ") p.print_red("[ ^D/exit when done ]") os.system(f"PROMPT_COMMAND='PS1=\"{prompt}\"; unset PROMPT_COMMAND' " f"bash") sys.exit() if not args.submitter: sys.exit("unspecified student/team") tester.grade() # TODO: add progress/percentage complete? p.print_magenta( f"\n[ Pretty-printing pts/comments for {args.submitter}... ]") _, _, s = tester.grades.get_submission_grades(args.submitter, rubric_code.upper()) print(s) # clean up tester.hw_class.cleanup()
def default_grader(self): """Generic grade function.""" printing.print_red("[ Opening shell, ^D/exit when done. ]") os.system("bash")