def __call__(self, *args): exercise_number, debug = self. \ _parse_arguments(args) if debug: self.printer.confirm("Running in debug mode.") assignment_file = p_join( self._storage.get_exercise_folder(exercise_number), "cross-assignments.json") assert not os.path.isfile( assignment_file), "You already sent cross-feedback tasks to people" ex_folder = Path(self._storage.get_exercise_folder(exercise_number)) name_file = ex_folder / "names.json" with open(name_file, "r") as file: names = j_load(file) raw_folder = Path(self._storage.get_raw_folder(exercise_number)) cross_folder = Path(self._storage.get_cross_folder(exercise_number)) if cross_folder.is_dir(): if len(list(cross_folder.iterdir())) > 0: raise ValueError(f"{cross_folder} exists and is not empty.") else: cross_folder.mkdir() # Collect all submission files and corresponding uploader submissions = [] for file_name, data in names.items(): file_path = raw_folder / file_name submissions.append((file_path, data["muesli_student_ids"])) # Find a permutation without self-assignment while True: new_order = np.random.permutation(len(submissions)) if np.all(new_order != np.arange(len(submissions))): break with open(assignment_file, "w") as file: data = [] for src_idx, tgt_idx in enumerate(new_order): src_file, src_students = submissions[src_idx] tgt_file, tgt_students = submissions[tgt_idx] shutil.copyfile(src_file, cross_folder / tgt_file.name) data.append({ "submission": src_file.name, "submission_by_muesli_student_ids": src_students, "assigned_to_muesli_student_ids": tgt_students, }) json_save(data, file)
def __call__(self, exercise_number, skip_existing=False): if skip_existing is not False: if skip_existing == "--skip": skip_existing = True raise NotImplementedError() else: raise ValueError(f"Did not understand second parameter {skip_existing}, should be '--skip' or nothing.") raw_folder = self._storage.get_raw_folder(exercise_number) preprocessed_folder = self._storage.get_preprocessed_folder(exercise_number) multi_hand_in_tracker = defaultdict(list) HandInTuple = namedtuple("HandInTuple", ["students", "hand_in", "problems"]) hand_in_notifications = [] for file in os.listdir(raw_folder): if file.endswith((".zip", ".tar.gz", ".tar", ".7z")): if file.endswith(".tar.gz"): extension = ".tar.gz" file_name = file[:len(extension)] else: file_name, extension = os.path.splitext(file) source_path = os.path.join(raw_folder, file) name_parser = FileNameParser(self.printer, self._storage, file_name, exercise_number) problems = list(name_parser.problems) if name_parser.normalized_name in multi_hand_in_tracker: problems.append(f"There appear to be more than one submission by your group. We overwrote the contents of {', '.join(map(repr, multi_hand_in_tracker[name_parser.normalized_name]))} with {file}.") self.printer.warning(f"A submission by {name_parser.normalized_name} was already extracted!") self.printer.warning("Previously extracted file names are:") self.printer.indent() for prev_name in multi_hand_in_tracker[name_parser.normalized_name]: self.printer.warning(f" - {prev_name}") self.printer.outdent() self.printer.inform("You can manually delete the .zip file from the raw folder or ignore this message.") if not self.printer.yes_no("Continue?"): break multi_hand_in_tracker[name_parser.normalized_name].append(file) target_path = os.path.join(preprocessed_folder, name_parser.normalized_name) if os.path.isdir(target_path): self.printer.warning(f"Target path {name_parser.normalized_name} exists!") if not self.printer.yes_no("Continue?"): break if not extension.endswith("zip"): problems.append(f"Minor: Wrong archive format, please use '.zip' instead of '{extension}'.") self.printer.inform("Found: " + ", ".join([student.muesli_name for student in name_parser.students])) try: self.printer.inform(f"Unpacking {file} ... ", end="") try: shutil.unpack_archive(source_path, target_path) except (BadZipFile, NotImplementedError) as e: self.printer.warning("") self.printer.warning(f"Detected bad zip file: {e}") self.printer.warning(f"Trying different archive types ...") with self.printer: problem = None for type in ("7z", "tar", "gztar", "bztar", "xztar"): try: shutil.unpack_archive(source_path, target_path, format=type) problem = f"Wrong file extension provided - this file was actually a {type}!" break except: self.printer.warning(f"... {type} failed!") if problem is None: problems.append("Could not unzip zip file. Copying zip file to target.") self.printer.error(f"Fatal error: {file} could not be unpacked!") self.printer.error("[ERR]") shutil.copy(source_path, target_path) self.printer.inform("Copied zip file to target.") else: problems.append(problem) if len(problems) > 0: self.printer.inform() self.printer.warning("While normalizing name there were some problems:") self.printer.indent() for problem in problems: self.printer.warning("- " + problem) self.printer.outdent() if self.printer.ask("Continue? ([y]/n)") == "n": break self.printer.confirm("[OK]") except shutil.ReadError: self.printer.error(f"Not supported archive-format: '{extension}'") with open(os.path.join(target_path, "submission_meta.json"), 'w', encoding='utf-8') as fp: data = { "original_name": file, "problems": problems, "muesli_student_ids": [student.muesli_student_id for student in name_parser.students] } json_save(data, fp) hand_in_notifications.append(HandInTuple(name_parser.students, file, problems)) self.printer.inform("─" * 100) elif file != "meta.json": self.printer.error(f"File name is {file} -- no known compressed file!") if self.printer.ask("Continue? ([y]/n)") == "n": break
def __call__(self, *args): exercise_number, debug = self. \ _parse_arguments(args) if debug: self.printer.confirm("Running in debug mode.") raw_folder = self._storage.get_raw_folder(exercise_number) assignment_file = p_join(self._storage.get_exercise_folder(exercise_number), "cross-assignments.json") assert not os.path.isfile(assignment_file), "You already sent cross-feedback tasks to people" # Collect all submission files and corresponding uploader submissions = [] with open(p_join(raw_folder, "meta.json")) as file: submission_list = j_load(file) for submission in submission_list: sub_info = SimpleNamespace(**submission) student = self._storage.get_student_by_moodle_id(sub_info.moodle_student_id) submissions.append((student, sub_info.file_name)) # Find a permutation without self-assignment while True: new_order = np.random.permutation(len(submissions)) if np.all(new_order != np.arange(len(submissions))): break with open(assignment_file, "w") as file: data = [] for submission_idx, (assigned_to_student, _) in zip(new_order, submissions): creator_student, assigned_file = submissions[submission_idx] data.append({ "submission": assigned_file, "submission_by_muesli_student_id": creator_student.muesli_student_id, "assigned_to_muesli_student_id": assigned_to_student.muesli_student_id, }) json_save(data, file) with EMailSender(self._storage.email_account, self._storage.my_name) as sender: for submission_idx, (student, _) in zip(new_order, submissions): creator_student, assigned_file = submissions[submission_idx] message = f"""Dear {student.moodle_name}, please provide cross-feedback to the appended submission by another student group. For instructions, check the current Exercise Sheet. Remember that you have to give cross-feedback at least four times over the semester. Have an awesome day! {self._storage.my_name} """ self.printer.inform(f"Sending cross-feedback task to {student.moodle_name} ... ", end='') tmp_path = p_join(self._storage.get_exercise_folder(exercise_number), "cross-feedback-task.zip") try: assigned_path = p_join(raw_folder, assigned_file) shutil.copy(assigned_path, tmp_path) sender.send_mail([student], message, f'[Fundamentals of Machine Learning] Your Cross-Feedback Task {self._storage.muesli_data.exercise_prefix} {exercise_number}', tmp_path, debug=debug) self.printer.confirm("[Ok]") except BaseException as e: self.printer.error(f"[Err] - {e}") finally: os.unlink(tmp_path)
def __call__(self, *args): try: exercise_number = int(args[0]) raw_folder = self._storage.get_raw_folder(exercise_number) preprocessed_folder = self._storage.get_preprocessed_folder(exercise_number) for file in os.listdir(raw_folder): if file.endswith((".zip", ".tar.gz", ".tar", ".7z")): if file.endswith(".tar.gz"): extension = ".tar.gz" file_name = file[:len(extension)] else: file_name, extension = os.path.splitext(file) try: source_path = os.path.join(raw_folder, file) normalized_name, problems = self._normalize_file_name(file_name, exercise_number) target_path = os.path.join(preprocessed_folder, normalized_name) if not extension.endswith("zip"): problems.append(f"Minor: Wrong archive format, please use '.zip' instead of '{extension}'.") self.printer.inform(f"Unpacking {file} ... ", end="") if len(problems) > 0: self.printer.inform() self.printer.warning("While normalizing name there were some problems:") self.printer.indent() for problem in problems: self.printer.warning("- " + problem) self.printer.outdent() try: shutil.unpack_archive(source_path, target_path) except BadZipFile as e: self.printer.warning("") self.printer.warning(f"Detected bad zip file: {e}") self.printer.warning(f"Trying different archive types ...") with self.printer: problem = None for type in ("7z", "tar", "gztar", "bztar", "xztar"): try: shutil.unpack_archive(source_path, target_path, format=type) problem = f"Wrong file extension provided - this file was actually a {type}!" break except: self.printer.warning(f"... {type} failed!") if problem is None: self.printer.error(f"Fatal error: {file} could not be unpacked!") self.printer.error("[ERR]") continue else: problems.append(problem) self.printer.confirm("[OK]") with open(os.path.join(target_path, "submission_meta.json"), 'w', encoding='utf-8') as fp: data = { "original_name": file, "problems": problems } json_save(data, fp) except shutil.ReadError: self.printer.error(f"Not supported archive-format: '{extension}'") self.printer.inform("─" * 100) except ValueError: self.printer.error(f"Exercise number must be an integer, not '{args[0]}'")
def __call__(self, exercise_number, skip_existing=False): if skip_existing is not False: if skip_existing == "--skip": skip_existing = True else: raise ValueError( f"Did not understand second parameter {skip_existing}, should be '--skip' or nothing." ) ex_folder = Path(self._storage.get_exercise_folder(exercise_number)) name_file = ex_folder / "names.json" with open(name_file, "r") as file: names = j_load(file) raw_folder = Path(self._storage.get_raw_folder(exercise_number)) preprocessed_folder = Path( self._storage.get_preprocessed_folder(exercise_number)) for file, data in names.items(): problems = data["problems"] zip_path = raw_folder / file target_path = preprocessed_folder / normalized_name( self._storage.get_student_by_muesli_id(muesli_id) for muesli_id in data["muesli_student_ids"]) if not zip_path.is_file(): self.printer.error(f"File {file} does not exist!") break if target_path.exists(): self.printer.warning(f"Target path {target_path.name} exists!") if skip_existing: self.printer.ask("Skipping. Hit enter to continue.") continue else: self.printer.error("Please remove or retry with '--skip'") break else: target_path.mkdir(parents=True) if is_zip_file(file): self.printer.inform(f"Unpacking {file} ... ", end="") extension = zip_path.suffix try: if not file.endswith("zip"): problems.append( f"Minor: Wrong archive format, please use '.zip' instead of '{extension}'." ) try: shutil.unpack_archive(zip_path, target_path) self.printer.confirm("[OK]") except (BadZipFile, NotImplementedError) as e: self.printer.warning("") self.printer.warning(f"Detected bad zip file: {e}") self.printer.warning( f"Trying different archive types ...") with self.printer: problem = None for type in ("7z", "tar", "gztar", "bztar", "xztar"): try: shutil.unpack_archive(zip_path, target_path, format=type) problem = f"Wrong file extension provided - this file was actually a {type}!" break except: self.printer.warning(f"... {type} failed!") if problem is None: problems.append( "Could not unzip zip file. Copying zip file to target." ) self.printer.error( f"Fatal error: {file} could not be unpacked!") self.printer.error("[ERR]") shutil.copy(zip_path, target_path) self.printer.inform("Copied zip file to target.") else: problems.append(problem) except shutil.ReadError: self.printer.error( f"Not supported archive-format: '{extension}'") problems.append( f"Not supported archive-format: '{extension}'") elif file != "meta.json": self.printer.warning( f"File name is {file} -- no known compressed file!") while True: answer = self.printer.ask( "Choose [s]kip, [l]eave uncompressed or [a]bort." ).strip().lower() if answer[0] in "sla": break else: self.printer.warning("Did not understand your answer.") if answer[0] == "s": continue elif answer[0] == "a": raise ValueError( "Found invalid file name, aborting due to user request." ) elif answer[0] == "l": shutil.copy(zip_path, target_path) with open(target_path / "submission_meta.json", 'w') as fp: json_save(data, fp)
def store_result(self, name_file, names): with open(name_file, "w") as file: json_save(names, file, indent=4) self._storage.save_student_name_matches()