# 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}]"}