def update(data: DataDirectory) -> None: backend = data.get_backend().client current = get_current_version() available = backend.get_current_version() if current < available: interface_print("Update available") if prompt("Do you want to view the changelog?", YesNoParser(True)): changelogs = backend.get_changelogs() for key in sorted(changelogs.keys()): interface_print("=== Changes in version %s ===" % str(key)) interface_print(changelogs[key]) interface_print() if prompt("Do you want to download the update now?", YesNoParser(True)): with TemporaryDirectory() as temp_dir: path = Path(temp_dir) / "client.tgz" with path.open("wb") as f: f.write(backend.get()) with tarfile.open(str(path)) as f: f.extractall(str(Path.cwd().parent)) interface_print("Update complete. Restarting script.") execute_subprocess(["./main.sh"], capture_output=False) exit(0)
def main(): root_directory = Path.cwd().parent.parent data_directory = DataDirectory(Path.cwd().parent / "data") set_global_log_file(data_directory.create_logfile()) bootstrap_log_path = Path("bootstrap.log") if bootstrap_log_path.is_file(): global_log_file.log("Bootstrap log:\n%s" % bootstrap_log_path.open().read()) interface_print("=== Welcome ===") update(data_directory) check_for_new_results(data_directory) ensure_team_existence(data_directory) def start_submission(): interface_print() success = submit(root_directory, data_directory) interface_print() if success: if prompt("Do you want to submit another solution?", YesNoParser(True)): start_submission() else: if prompt("Do you want to try again?", YesNoParser(True)): start_submission() if prompt("Do you want to submit a solution now?", YesNoParser(True)): start_submission()
def check_for_new_results(data_directory: DataDirectory): # Check every submission for new results try: for assignment in data_directory.get_assignments(): for submission in assignment.get_submissions(): if not submission.bookkeeping_data.get( ).status == SubmissionStatus.accepted: continue if submission.check_for_evaluation_results(): submission.print_evaluation_results() continue # Check if the submission still exists (may have been removed) and if it is eligible for result subscription if submission.bookkeeping_data.get( ).status == SubmissionStatus.accepted and submission.submission_response.get( ).immediate_evaluation: if prompt( "Do you want to subscribe to the results of submission %s?" % submission.submission_data.get().submission_id, YesNoParser(True)): submission.wait_for_evaluation_results() except Exception as e: global_log_file.error("".join( traceback.format_exception(*sys.exc_info()))) # Errors when checking for new results should never prevent students from submitting, so we are a fault tolerant. interface_print("Error while checking for new results: %s" % str(e)) interface_print( "Please report this issue. You will still be able to submit a solution." )
def prompt(description: str, input_parser: InputParser): while True: result = input_parser.parse(input("%s %s " % (description, input_parser.get_format_description()))) if result is None: interface_print("Sorry, again.") continue return result
def submit(root_directory: Path, data: DataDirectory) -> bool: """ :returns Whether a solution was submitted. """ interface_print("=== Submitting ===") submission = None try: selected_assignment = select_assignment(data.get_assignments()) interface_print() if selected_assignment is None: return False selected_variant = select_variant(root_directory, data) interface_print() if selected_variant is None: return False interface_print( "Selected variant '%s' for assignment '%s'." % (selected_variant.name, selected_assignment.assignment_id)) submission = selected_assignment.create_submission() submission.team.set(data.team.get()) submission.variant.set(selected_variant.variant_data) # Create temporary directory to build submission. with tempfile.TemporaryDirectory() as tmpdir: tmp_solution_root = Path(tmpdir) / selected_variant.root shutil.copytree(root_directory / selected_variant.root, tmp_solution_root) if not build_solution(tmp_solution_root, selected_variant): return False clean_directory(tmp_solution_root, selected_variant) with tempfile.TemporaryFile() as temp_file: with tarfile.open(fileobj=temp_file, mode='w:gz', format=tarfile.GNU_FORMAT) as tar: tar.add(tmp_solution_root, recursive=True, arcname="/%s" % selected_variant.root) submission.set_upload_file(temp_file) return submission.submit() except KeyboardInterrupt: interface_print("=== Aborted Submitting ===") if submission is not None: submission.delete() return False
def start_submission(): interface_print() success = submit(root_directory, data_directory) interface_print() if success: if prompt("Do you want to submit another solution?", YesNoParser(True)): start_submission() else: if prompt("Do you want to try again?", YesNoParser(True)): start_submission()
def create_team_json(data: DataDirectory) -> None: members = [] interface_print("A new team.json will now be created.\n") while True: members.append(prompt_student(len(members) + 1)) if len(members) >= data.config.get().max_team_size or not prompt( "Another?", YesNoParser(True)): break data.team.set(Team(members)) interface_print("team.json successfully created.\n")
def check_for_evaluation_results(self) -> Optional[bool]: if not self.bookkeeping_data.has_value(): interface_print( "Error: There appears to be a data corruption. Bookkeeping data for a submission are missing." ) return None if self.bookkeeping_data.get().status != SubmissionStatus.accepted: interface_print( "Error: Can only check for results of accepted submissions. Please report this as a bug in the script." ) return None evaluated = self.get_backend().is_evaluated() if evaluated is None: interface_print( "- Submission '%s' can no longer be found on the server and will be marked as [removed]." % self.submission_data.get().submission_id) self.set_status(SubmissionStatus.removed) return None elif evaluated: self.submission_data.set(self.get_backend().get(True)) self.evaluation_log.set( self.submission_data.get().evaluation_result.log) self.set_status(SubmissionStatus.evaluated) interface_print( "\n=== Evaluation results for submission '%s' retrieved. ===" % self.submission_data.get().submission_id) return True return False
def ensure_team_existence(data: DataDirectory, can_be_skipped: bool = False) -> None: if not data.team.has_value(): interface_print( "There is no team.json saved yet. You will need to provide information about your team before you can submit solutions." ) if not can_be_skipped or prompt( "Do you want to create a team.json now?", YesNoParser(True)): create_team_json(data) else: interface_print( "Please review the currently saved information about your team:") i = 1 for member in data.team.get().members: interface_print( " %d - First name: %s; Last name: %s; Matriculation number: %s" % (i, member.first_name, member.last_name, member.matriculation_number)) i += 1 if not prompt("Is this information up to date?", YesNoParser(True)): create_team_json(data) interface_print()
def clean_directory(directory: Path, selected_variant: SkeletonVariant): def clean_helper(path: Path): if path.is_file(): str(path.relative_to(directory)) if not selected_variant.is_filename_allowed( str(path.relative_to(directory))): path.unlink() elif path.is_dir(): for child in path.iterdir(): clean_helper(child) if not any(path.iterdir()): path.rmdir() interface_print("=== Cleaning directory for submission ===") clean_helper(directory) interface_print("=== Cleaning directory finished ===")
def select_variant(root_directory: Path, data: DataDirectory) -> Optional[SkeletonVariant]: found_options = [ v for v in data.config.get().skeleton_variants if (root_directory / v.root).is_dir() ] if len(found_options) == 0: interface_print( "No skeleton variant found! Did you delete all of them?") return None if len(found_options) == 1: return found_options[0] return prompt("Please choose the variant you want to submit.", SelectionParser([(a.name, a) for a in found_options], None))
def prompt_student(member_number: int) -> Student: while True: interface_print("Please enter details of team member %d." % member_number) lastname = prompt("Last Name:", NameParser()) firstname = prompt("First Name:", NameParser()) matriculation_number = prompt("Matriculation number:", MatriculationNumberParser()) member = Student(lastname, firstname, matriculation_number) interface_print() interface_print( "First name: %s; Last name: %s; Matriculation number: %s" % (member.first_name, member.last_name, member.matriculation_number)) if prompt("Is this correct?", YesNoParser(False)): return member
def wait_for_evaluation_results(self) -> bool: interface_print("Polling for results (Press CTRL + C to abort).", end="") try: import time while True: results_found = self.check_for_evaluation_results() if results_found: self.print_evaluation_results() break if results_found is None: break interface_print(".", end="", flush=True) time.sleep(1) except KeyboardInterrupt: interface_print("\nResult polling aborted.") return True
def start_submission(): interface_print() success = submit(root_directory, data_directory) interface_print() if success: if prompt("Do you want to submit another solution?", YesNoParser(True)): start_submission() else: if prompt("Do you want to try again?", YesNoParser(True)): start_submission() if prompt("Do you want to submit a solution now?", YesNoParser(True)): start_submission() if __name__ == '__main__': try: main() except requests.exceptions.ConnectionError as e: interface_print( "The server can not be reached. You may need to be logged in to the VPN." ) interface_print("Aborting.") exit(1) except KeyboardInterrupt: interface_print( ) # Print a single linefeed to avoid having the new command prompt dangle in the same line as the last output line exit(0)
def submit(self) -> bool: interface_print("=== Starting Upload ===") self.submission_response.set( self.parent.get_backend().submissions.post( self.upload_tgz_path, self.variant.base_property.get(), self.team.base_property.get())) self.submission_data.set(self.submission_response.get().submission) self.submission_log.set(self.submission_response.get().log) interface_print("=== Upload finished ===") interface_print("=== BEGIN Upload Log") interface_print(self.submission_response.get().log) interface_print("=== END Upload Log") if not self.submission_response.get().accepted: interface_print( "Submitting your solution failed. Please see the upload log above for more details." ) self.set_status(SubmissionStatus.rejected) return False else: interface_print( "Successfully submitted your solution. Please see the upload log above for more details." ) self.set_status( SubmissionStatus.accepted, self.submission_response.get().submission.submission_id) if self.submission_response.get().immediate_evaluation: self.wait_for_evaluation_results() return True
def print_evaluation_results(self) -> None: score_percentage = \ 100 * self.submission_data.get().evaluation_result.score / self.submission_data.get().evaluation_result.max_score \ if self.submission_data.get().evaluation_result.max_score != 0 \ else 0 interface_print( "You achieved the score %d/%d (%d%%)." % (self.submission_data.get().evaluation_result.score, self.submission_data.get().evaluation_result.max_score, score_percentage)) if self.submission_data.get().evaluation_result.comment is not None: interface_print("The following comment was left by your teacher:") interface_print( self.submission_data.get().evaluation_result.comment) if self.submission_data.get().evaluation_result.passed: interface_print("You have PASSED this assignment.") else: interface_print("You have NOT PASSED this assignment.") if self.submission_data.get().evaluation_result.log is not None: if prompt("Do you want to view the evaluation log now?", YesNoParser(True)): interface_print("=== BEGIN Evaluation Log") interface_print( self.submission_data.get().evaluation_result.log) interface_print("=== END Evaluation Log") interface_print("NOTE: This evaluation log is also available at '%s'" % self.evaluation_log.path.absolute())
def build_solution(directory: Path, selected_variant: SkeletonVariant) -> bool: interface_print("=== Building submission ===") interface_print("Running '%s'." % selected_variant.build) interface_print("=== Building submission finished ===") build_result = execute_subprocess([selected_variant.build], cwd=directory, is_shell_command=True, merge_stdout_stderr=True) if not build_result.is_success(): interface_print("Building your solution failed!") if prompt("Do you want to view the build log?", YesNoParser(True)): interface_print("=== BEGIN Build Log ===") interface_print(build_result.stdout, end='') interface_print("=== END Build Log ===") if not prompt( "Do you want to submit your solution anyway, despite the failing build?", YesNoParser(False)): interface_print("Aborting submission.") return False return True