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"))
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)
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))
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.")
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("")