示例#1
0
def rat_grade(file_input, file_path):
    """
    Evaluate the results of a RAT.
    """
    teampy = Teampy()
    rat = RATContext(teampy)

    # read in the questionaire
    questionaire = _load_rat_file(
        click.open_file(rat.questionaire_file, encoding="utf-8"))
    if questionaire is None:
        tell("Could not read questions. Aborting.", "error")
        return
    # read in the solutions file
    solutions = SolutionDocument()
    solutions.load(rat.solutions_file, teampy.students, teampy.teams)

    result = Result(teampy.students, teampy.teams, questionaire, solutions)
    student_results_count, team_results_count = result.load_results(file_input)

    if (student_results_count + team_results_count) == 0:
        tell("The results file does not contain any results.", "warning")
        return

    results_file_path = parallel_file_path(file_path, ".xlsx")
    result.store_results(results_file_path)
    results_file_path = parallel_file_path(file_path, ".html")
    result.store_results_html(results_file_path)
    result.stats(filename=parallel_file_path(file_path, "_stats.html"))
示例#2
0
def rat_check(file_input, file_path):
    """
    Read the RAT questionaire, check for consistency, print it as tex document,
    but without solutions or fo specific students or teams.
    """
    questionaire = _load_rat_file(file_input)
    if questionaire is None:
        return

    # TODO does not detect 4 fake answers, or several true answers, improve parser
    tell("The RAT has {} questions.".format(len(questionaire.questions)))
    for question in questionaire.questions:
        fake = len(question.fake)
        if fake != 3:
            print(Fore.RED +
                  "Question {} has only {} fake answers. Must be 3.".format(
                      question.number, fake) + Style.RESET_ALL)
            return
        if question.true is None:
            print(
                Fore.RED +
                "Question {} has no true answer. The first answer alternative must be the true one."
                .format(question.number) + Style.RESET_ALL)
            return
    tell("The rat looks okay.")
    latex = questionaire.write_latex()
    write_latex(latex, file_path)
示例#3
0
def write_latex(latex, file_path, old_latex=False):
    tex_file_name = os.path.splitext(os.path.basename(file_path))[0] + ".tex"
    tex_file_path = os.path.join(os.path.dirname(file_path), tex_file_name)
    # TODO check if file already exists
    with open(tex_file_path, "w", encoding="utf-8") as file:
        file.write(latex)
    if old_latex:
        # copy also ratbox figures
        copy_figures(os.path.dirname(file_path))
    tell("Write LaTeX into file {}.".format(tex_file_path))
示例#4
0
def rat_print(file_input, file_path, team_solution, old_latex=False, pdf=True):
    """
    Create a document with RATs for all students and all teams.
    """
    t = Teampy()
    questionaire = _load_rat_file(file_input)
    if questionaire is None:
        return

    if team_solution in t.scratchcards:
        scratch_card_id = team_solution
        team_solution = t.scratchcards[team_solution]
        # TODO check if solution matches requirement from questionaire
        tell("Found scratch card {} with solution {}".format(
            scratch_card_id, team_solution.to_string()))
    else:
        # TODO check if solution matches requirement from questionaire
        team_solution = Solution.create_solution_from_string(team_solution)

    # TODO check if team solution has correct number
    # TODO look for already existing solution document, load and update it

    # create a solution file
    sd = SolutionDocument()
    sd.create_solution_document(t.teams, t.students, questionaire,
                                team_solution)
    solutions_file_path = os.path.join(os.path.dirname(file_path),
                                       "solutions.teampy")
    sd.store(solutions_file_path)
    tell("Write solutions into file {}.".format(solutions_file_path))

    latex = questionaire.write_latex(sd, t.teams, t.students, old_latex)
    if pdf:
        pdf_file_name = os.path.splitext(
            os.path.basename(file_path))[0] + ".pdf"
        tell("Creating PDF...")
        # empty directory for tex reasons...
        current_dir = os.path.abspath(os.path.dirname(file_path))
        pdf = build_pdf(latex, texinputs=[current_dir, ""])
        pdf.save_to(pdf_file_name)
        tell("Done!")
    else:
        write_latex(latex, file_path, old_latex=old_latex)
        tell("Done!")
        print("   As a next step, print the LaTeX document and do the RAT.")
示例#5
0
def rat_email(file_input, file_path, testonly):
    """
    Send the results of a RAT to students via email.
    """
    if not file_path.endswith(".xlsx"):
        tell("This command only takes an *.xlsx file as input.", "error")
        return

    teampy = Teampy()

    if teampy.smtp_settings is None:
        tell("Your SMTP settings are not complete. Please check.", "error")
        return

    # TODO give RAT context the directory relative to the file_input
    rat = RATContext(teampy)
    # read in the questionaire
    questionaire = _load_rat_file(
        click.open_file(rat.questionaire_file, encoding="utf-8"))
    if questionaire is None:
        tell("Could not read questions. Aborting.", "error")
        return
    # read in the solutions file
    solutions = SolutionDocument()
    solutions.load(rat.solutions_file, teampy.students, teampy.teams)

    # read in the corresponding result file
    results_file_path = parallel_file_path(file_path, ".txt")
    result = Result(teampy.students, teampy.teams, questionaire, solutions)
    result.load_results(click.open_file(results_file_path, encoding="utf-8"))

    # read in the graded file
    df = pd.read_excel(
        file_path,
        dtype={
            "id": str,
            "team": str,
            "email": str,
            "comment": str,
            "feedback": str
        },
    )
    df = df.set_index("id")

    # TODO check that table is consistent
    # TODO abort if there is nothing to send
    # TODO check if all email adresses are valid

    # create all messages first
    messages = {}
    for student_id, row in df.iterrows():
        # TODO check if we need to send email to this student
        if row["feedback"] != "sent":
            messages[student_id] = create_message(student_id, row, result,
                                                  teampy)

    # store the messages
    make_sure_path_exists("emails")
    for student_id, message in messages.items():
        html_file_path = os.path.join(os.path.dirname(file_path),
                                      "emails/{}.html".format(student_id))
        with open(html_file_path, "w") as html_file:
            # html_file.write(message.as_string())
            html_file.write(message.get_payload()[0].get_payload())

    if len(messages) == 0:
        tell("There is nothing to send.")
        return
    elif testonly:
        tell(
            "Messages written to email folder, but nothing will be sent due to option --testonly."
        )
        return
    else:
        tell(
            "Will send messages to {} students. (Press CTRL+C to quit and not send anything. Restart with option --testonly to only store a preview of the emails.)"
            .format(len(messages)))

    # connect to SMTP server
    print("\n")
    password = getpass.getpass(
        prompt="Password for the user {} on server {}: ".format(
            teampy.smtp_settings["from"], teampy.smtp_settings["smtp"]))

    # create server
    print("Create client...")
    server = smtplib.SMTP("{}: {}".format(teampy.smtp_settings["smtp"],
                                          teampy.smtp_settings["port"]))
    statuses = {}
    try:
        print("Connecting to server...")
        server.starttls()
        # Login Credentials for sending the mail
        server.login(teampy.smtp_settings["from"], password)
        print("Logged in...")
        # send email
        bar = progressbar.ProgressBar(
            max_value=len(messages),
            widgets=[progressbar.Percentage(),
                     progressbar.Bar()],
        )
        for student_id, message in messages.items():
            try:
                server.send_message(message)
                time.sleep(0.1)
                statuses[student_id] = "sent"
            except (
                    SMTPHeloError,
                    SMTPRecipientsRefused,
                    SMTPNotSupportedError,
                    SMTPSenderRefused,
                    SMTPDataError,
            ) as e:
                tell("There was an error sending a mail to {}.".format(
                    student_id))
                statuses[student_id] = "error"
                print(e)
            bar += 1
    except (
            SMTPHeloError,
            SMTPAuthenticationError,
            SMTPNotSupportedError,
            SMTPException,
    ) as e:
        tell("There was an error connecting to the SMTP server {}".format(
            teampy.smtp_settings["smtp"]))
    bar.finish()
    server.quit()

    # update the status
    changes = False
    for student_id, status in statuses.items():
        df.at[student_id, "feedback"] = status
        changes = True
    if changes:
        keep_trying = True
        while keep_trying:
            try:
                df.to_excel(file_path)
                tell("Updated results file with email send status.")
                keep_trying = False
            except PermissionError:
                tell(
                    "Can't write file {}. Maybe Excel is open? Close it and press enter."
                    .format(file_path),
                    "error",
                )
                input("")