Beispiel #1
0
    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)
Beispiel #2
0
    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
Beispiel #3
0
    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]}'")
Beispiel #5
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)
Beispiel #6
0
 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()