# Feedback Release ```python= ''' MODULE DOCSTRING ---------------- ''' import os import sys import csv import time import re from dataclasses import dataclass, fields from typing import List, Dict, Tuple from yagmail import SMTP from mdutils.mdutils import MdUtils as MD from pyhtml import html, head, body, p, br, i, b, a ITERATION_MAPPING: Dict[int, Tuple[int, int]] = { 0: (30, 10), 1: (15, 15), 2: (12, 8), 3: (5, 5) } emailer: SMTP = SMTP(os.environ.get('EMAIL'), os.environ.get('PASSWORD')) @dataclass class Student: ''' CLASS DOCSTRING --------------- ''' zid: str name: str tute: str group: str @dataclass class Feedback: ''' CLASS DOCSTRING --------------- ''' commits: Dict[str, Dict[str, str]] merge_reqs: Dict[str, Dict[str, str]] tdd: Dict[str, Dict[str, str]] issue_board: Dict[str, Dict[str, str]] comms: Dict[str, Dict[str, str]] check_ins: Dict[str, Dict[str, str]] minutes: Dict[str, Dict[str, str]] @dataclass class IterationDetails: ''' CLASS DOCSTRING --------------- ''' milestone: int git_percentage: int project_percentage: int def __init__(self) -> None: ''' CLASS METHOD DOCSTRING --------------- ''' self.milestone = int(input('Enter the milestone: ')) if self.milestone not in ITERATION_MAPPING: raise ValueError('Invalid milestone') self.git_percentage, self.project_percentage = ITERATION_MAPPING[self.milestone] def generate_html_content(recipient_name: str) -> str: ''' Generates HTML content for the message body of the personalised email being sent to the recipient Parameters ---------- recipient_name : str name of the recipient receiving the email Returns ------- str HTML string with no extra spaces or new lines that can be directly used to write the message body of the email ''' content = str( html( head(), body( p(f"Hi {recipient_name}!"), p("PFA your individual feedback. You will need to open this markdown file in VS Code and open its preview to be able to easily read the file."), p( "Cheers", br(), "Chitrakshi" ), p( i( "P.S. If you have any concerns about your marking, feel free to ", a(href="https://teams.microsoft.com/l/chat/0/0?users=c.gosain@unsw.edu.au")("contact me on Teams"), "." ) ), p( b( "This is an auto-generated email, please do not reply." ) ) ) ) ) return re.sub(r'(\s{2}|\n)', '', content) def process_data(file_path: str) -> List[Tuple[Student, Feedback]]: ''' Reads the csv file and processes the feedback based for every student based on predefined categories Parameters ---------- file_path : str name of the .csv file containing the feedback data Returns ------- List[Tuple[Student, Feedback]] list of tuples of student's zid and their corresponding feedback ''' data: List[Tuple[Student, Feedback]] = [] with open(file_path) as spreadsheet: reader = csv.reader(spreadsheet) for row in reader: zid, name, tute_group, *marks_and_comments = row tute, group = tute_group.split('_') categories = [field.name for field in fields(Feedback)] marks_and_comments = [(mark, comments) for mark, comments in zip(marks_and_comments[::2], marks_and_comments[1::2])] category_data = {category: {'mark': mark, 'comments': comments} for category, (mark, comments) in zip(categories, marks_and_comments)} student = Student(zid, name, tute, group) feedback = Feedback(**category_data) data.append((student, feedback)) return data def create_markdown(iteration: IterationDetails, student_info: Student, marking_data: Dict[str, Dict[str, Dict[str, str]]]) -> None: ''' DESCRIPTION ----------- Parameters ---------- iteration : IterationDetails student_info : Student marking_data : Dict[str, Dict[str, Dict[str, str]]] ''' if len(student_info.zid) != 7: raise Exception(f"Could not create markdown for {student_info.zid}, {student_info.name} from {student_info.tute}_{student_info.group} due to an invalid zid") with open(f"{student_info.tute}/IT{iteration.milestone}/{student_info.group}.md", 'r') as source_file: source_content = source_file.read() table_text = [ 'General', 'Information', 'zID', student_info.zid, 'NAME', student_info.name, 'COURSE', 'COMP1531', 'ITERATION', iteration.milestone, 'CLASS', student_info.tute, 'GROUP', student_info.group ] categories = { 'git': { 'header': f"Git Practices ({iteration.git_percentage}%)", 'commits': 'Commits', 'merge_reqs': 'Merge Requests', 'tdd': 'Tests before implementation', }, 'project': { 'header': f"Project Management & Teamwork ({iteration.project_percentage}%)", 'issue_board': 'Gitlab board', 'comms': 'Communications', 'check_ins': 'Check in attendance', 'minutes': 'Standups/meetings/minutes', } } md_file = MD( file_name=f"individuals/{student_info.zid}", title=f"COMP1531 Iteration {iteration.milestone} Feedback" ) md_file.new_table(columns=2, rows=7, text=table_text, text_align='left') md_file.new_header(level=1, title='INDIVIDUAL MARK DISTRIBUTION') md_file.write(source_content) md_file.new_header(level=2, title='INDIVIDUALLY MARKED COMPONENTS') for category_data in categories.values(): md_file.new_header(level=3, title=category_data['header']) for category_key, category_value in category_data.items(): if category_key in marking_data: md_file.new_header( level=4, title=f"{category_value}: {marking_data[category_key]['mark']}" ) comments = marking_data[category_key]['comments'].split(';') for comment in comments: md_file.new_line(comment) md_file.new_line("Note: marks will be visible to you directly in 'give'. If you have any questions about the manual mark and need to discuss your marks with me, feel free to get in touch on Teams") md_file.new_line("P.S. this file was auto-generated from an excel sheet, if you believe there was an error or a typo, please bring it to my attention ASAP and I'll fix it, thanks!") md_file.new_line() md_file.create_md_file() def send_feedback_email(iteration: IterationDetails, student: Student) -> None: ''' DESCRIPTION ----------- Parameters ---------- iteration : IterationDetails student : Student ''' recipient_email = f"z{student.zid}@ad.unsw.edu.au" subject = f"COMP1531 Iteration {iteration.milestone} individual feedback" message_body = generate_html_content(student.name) attachment_file = f"individuals/{student.zid}.md" try: emailer.send( to=recipient_email, subject=subject, contents=message_body, attachments=attachment_file ) print(f"Email sent to {recipient_email}") except: print(f"Could not send email for {student.zid}, {student.name} from {student.tute}_{student.group}") def main() -> None: ''' DESCRIPTION ----------- ''' try: iteration = IterationDetails() except ValueError as err: sys.exit(err) data = process_data('23t3_my_marking.csv') for student, feedback in data: print(f"Starting {student.zid}, {student.name} from {student.tute}_{student.group}") try: create_markdown(iteration, student, feedback.__dict__) send_feedback_email(iteration, student) time.sleep(5) except Exception as err: sys.exit(err) if __name__ == "__main__": main() ```
{"title":"Feedback Release","contributors":"[{\"id\":\"1af41921-4c53-49ab-8ccf-91013d422d48\",\"add\":8509,\"del\":0}]"}
Expand menu