Commit 5de647ae authored by Olivier's avatar Olivier
Browse files

New funtionality: suggest buddy to participants

New functionality, implemented by Johchen König in the spring 2020:
participating students are grouped in pairs, so they can each be
suggested a work buddy for each assignment.

The changes in this commit were  authored by Jochen König.
parent 7ffffcbc
# -*- coding: utf-8 -*-
"""
Author: Germán Santa-Maria
Script: Assignment Customizer
Description:
This script takes in three tex files with marked parameters, as well as a
list of student emails to produce:
1) three tex files with custom parameters for each student, and;
2) a summary csv file with all the output assignments data.
"""
# Import modules
import csv # For reading csv files
import math
from hash_function import hash_function # Import hash_function
# Initialize arrays to store data
first_names = [] # Student first name
last_names = [] # Student last name
emails = [] # Student email address
markers = [] # Marker for the number to customize
custom_ID_A = [] # Custom ID of the assignment for the student
custom_ID_G = [] # Custom ID of the grading file for the student
rand_matrix = [] # These are the random numbers to customize the assignments
custom_values = [] # Matrix with all custom values for each student assingment
###########################################################################
# Parameters specific to assignment exercise
# Number of assignment
assignment_suffix = "_example"
# Markers for values:
# those are text markers to be replaced by numbers in each student assignment
markers = ['XXlengthXX', 'XXwidthXX',
'XXareaXX', 'XXarea_doubledXX'
]
###########################################################################
"""-------------------------Hard coded input--------------------------------"""
# Input file paths
assignment_path = 'script_input/assignment'+assignment_suffix+'.tex'
assig_solution_grading_path = 'script_input/solution_grading'+assignment_suffix+'.tex'
assig_solution_graded_path = 'script_input/solution_graded'+assignment_suffix+'.tex'
canoniccl_path = 'script_input/canonical.csv'
# Output file paths
output_csv_path = 'script_output/output_assignments.csv'
output_assignment_preamble = 'script_output/assignment'+assignment_suffix+'_'
output_solution_grading_preamble = 'script_output/solution_grading'+assignment_suffix+'_'
output_solution_graded_preamble = 'script_output/solution_graded'+assignment_suffix+'_'
# Markers for student custom ID
marker_customID_A = 'XXcustomIDAXX'
marker_customID_G = 'XXcustomIDGXX'
marker_firstname = 'XXstudentfirstnameXX'
marker_lastname = 'XXstudentlastnameXX'
marker_fullname = 'XXstudentfullnameXX'
"""-------------------------------------------------------------------------"""
# Open and read the template assignment tex file
with open(assignment_path,'r', encoding="utf-8") as f:
assignment = f.read()
# Open and read the template solution tex files
with open(assig_solution_grading_path,'r', encoding="utf-8") as f:
assig_solution_grading = f.read()
with open(assig_solution_graded_path,'r', encoding="utf-8") as f:
assig_solution_graded = f.read()
# Store students data from csv file into variables
with open(canoniccl_path, newline='', encoding="utf-8") as f:
canoniccl_reader = csv.reader(f)
next(canoniccl_reader) # Skip header (first line)
canoniccl_data = [row for row in canoniccl_reader]
for row in canoniccl_data:
first_names.append(row[0])
last_names.append(row[1])
emails.append(row[2])
# Generate anonymous ID and numbers specific to each student
for email in emails:
[ID_A, ID_G, rand_numbers]=hash_function(email)
custom_ID_A.append(ID_A)
custom_ID_G.append(ID_G)
rand_matrix.append(rand_numbers)
###########################################################################
# Parameters and solutions are specific for this excercise
# Example exercise for demonstration: calculate area of rectangle given its sides
# Generate custom values for each student from the semi-random numbers
for i in range (0,len(first_names)):
# Input values for the side lenghts [m]
min_x = 10
max_x = 20
width = min_x+(max_x-min_x)*rand_matrix[i][0]
length = min_x+(max_x-min_x)*rand_matrix[i][1]
# side lengths [m]
width = round(width) # round-off decimals = 0
length = round(length) # round-off decimals = 0
# area of rectangle [m^2]
area = width*length
# area of rectangle when sides doubled [m^2]
area_doubled = area * 4
# Store the calculated custom numbers for the assignments
custom_values.append([
length, width,
area, area_doubled,
])
###########################################################################
# Store the custom assignment data for all the students in a csv file
with open (output_csv_path,'w',newline='', encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(['first name','last name','email','custom_ID_A',
'custom_ID_G'] + markers)
for i in range(0,len(first_names)):
writer.writerow([first_names[i],last_names[i],emails[i],
custom_ID_A[i], custom_ID_G[i]]+custom_values[i])
# For each student, generate a tex file for the assignment
for i in range (0,len(first_names)): # For each student
temp_assig = assignment
for j in range (0, len(markers)): # Replace each marker by a custom value
temp_assig = temp_assig.replace(markers[j],str(custom_values[i][j]))
# Mark assignment with the student custom ID
temp_assig = temp_assig.replace(marker_customID_A, str(custom_ID_A[i]))
temp_assig = temp_assig.replace(marker_customID_G, str(custom_ID_G[i]))
# Mark assignment with the student first name
temp_assig = temp_assig.replace(marker_firstname, str(first_names[i]))
# Mark assignment with the student first name and last name
full_name = str(first_names[i]) + ' ' + str(last_names[i])
temp_assig = temp_assig.replace(marker_fullname, full_name)
# Produce the custom tex file for each student
with open ((output_assignment_preamble+custom_ID_A[i]+'.tex'),'w', encoding="utf-8") as f:
for line in temp_assig:
f.write(line)
# For each peer, generate a tex file for the grading
for i in range (0,len(first_names)): # For each student
temp_sol_grading = assig_solution_grading
for j in range (0, len(markers)): # Replace each marker by a custom value
temp_sol_grading = temp_sol_grading.replace(markers[j],str(custom_values[i][j]))
# Mark solution with the student custom ID
temp_sol_grading = temp_sol_grading.replace(marker_customID_A, str(custom_ID_A[i]))
temp_sol_grading = temp_sol_grading.replace(marker_customID_G, str(custom_ID_G[i]))
# Mark solution with the student first name
temp_sol_grading = temp_sol_grading.replace(marker_firstname, str(first_names[i]))
# Mark solution with the student first name and last name
full_name = str(first_names[i]) + ' ' + str(last_names[i])
temp_sol_grading = temp_sol_grading.replace(marker_fullname, full_name)
# Produce the custom tex file for each student
with open ((output_solution_grading_preamble+custom_ID_G[i]+'.tex'),'w', encoding="utf-8") as f:
for line in temp_sol_grading:
f.write(line)
# For each student, generate a tex file for the solution
for i in range (0,len(first_names)): # For each student
temp_sol_graded = assig_solution_graded
for j in range (0, len(markers)): # Replace each marker by a custom value
temp_sol_graded = temp_sol_graded.replace(markers[j],str(custom_values[i][j]))
# Mark solution with the student custom ID
temp_sol_graded = temp_sol_graded.replace(marker_customID_A, str(custom_ID_A[i]))
temp_sol_graded = temp_sol_graded.replace(marker_customID_G, str(custom_ID_G[i]))
# Mark solution with the student first name
temp_sol_graded = temp_sol_graded.replace(marker_firstname, str(first_names[i]))
# Mark solution with the student first name and last name
full_name = str(first_names[i]) + ' ' + str(last_names[i])
temp_sol_graded = temp_sol_graded.replace(marker_fullname, full_name)
# Produce the custom tex file for each student
with open ((output_solution_graded_preamble+custom_ID_A[i]+'.tex'),'w', encoding="utf-8") as f:
for line in temp_sol_graded:
f.write(line)
# End of script
# -*- coding: utf-8 -*-
"""
Authors: Germán Santa-Maria, Jochen König, Olivier Cleynen
Script: Assignment Customizer
Description:
This script takes in three tex files with marked parameters, as well as a
list of student emails, and produces:
1) three tex files with custom parameters for each student, and;
2) a summary csv file with all the output assignments data.
"""
# Import modules
import csv # For reading csv files
import math
from hash_function import hash_function # Import hash_function
from random import shuffle # shuffles a sequence
import sys
# Initialize arrays to store data
first_names = [] # Student first name
last_names = [] # Student last name
emails = [] # Student email address
markers = [] # Marker for the number to customize
custom_ID_A = [] # Custom ID of the assignment for the student
custom_ID_G = [] # Custom ID of the grading file for the student
rand_matrix = [] # These are the random numbers to customize the assignments
custom_values = [] # Matrix with all custom values for each student assignment
buddy = [] # Optional buddy program
buddy_two = [] # One student is assigned two buddies in case of odd number of students
peer = [] # Tuple of peers
###########################################################################
# Parameters specific to assignment exercise
# Number of assignment
assignment_suffix = "_example"
# Markers for values:
# those are text markers to be replaced by numbers in each student assignment
markers = ['XXlengthXX', 'XXwidthXX',
'XXareaXX', 'XXarea_doubledXX'
]
# Markers for student custom ID
marker_customID_A = 'XXcustomIDAXX'
marker_customID_G = 'XXcustomIDGXX'
marker_firstname = 'XXstudentfirstnameXX'
marker_lastname = 'XXstudentlastnameXX'
marker_fullname = 'XXstudentfullnameXX'
# Markers for buddy program
marker_buddy_firstname = 'XXbuddyfirstnameXX'
marker_buddy_email = 'XXbuddyemailXX'
###########################################################################
"""-------------------------Hard coded input--------------------------------"""
# Input file paths
assignment_path = 'script_input/assignment'+assignment_suffix+'.tex'
assig_solution_grading_path = 'script_input/solution_grading'+assignment_suffix+'.tex'
assig_solution_graded_path = 'script_input/solution_graded'+assignment_suffix+'.tex'
canoniccl_path = 'script_input/canonical.csv'
# Output file paths
output_csv_path = 'script_output/output_assignments.csv'
output_assignment_preamble = 'script_output/assignment'+assignment_suffix+'_'
output_solution_grading_preamble = 'script_output/solution_grading'+assignment_suffix+'_'
output_solution_graded_preamble = 'script_output/solution_graded'+assignment_suffix+'_'
# Markers for student custom ID
marker_customID_A = 'XXcustomIDAXX'
marker_customID_G = 'XXcustomIDGXX'
marker_firstname = 'XXstudentfirstnameXX'
marker_lastname = 'XXstudentlastnameXX'
marker_fullname = 'XXstudentfullnameXX'
"""-------------------------------------------------------------------------"""
# Open and read the template assignment tex file
with open(assignment_path,'r', encoding="utf-8") as f:
assignment = f.read()
# Open and read the template solution tex files
with open(assig_solution_grading_path,'r', encoding="utf-8") as f:
assig_solution_grading = f.read()
with open(assig_solution_graded_path,'r', encoding="utf-8") as f:
assig_solution_graded = f.read()
# Store students data from csv file into variables
with open(canoniccl_path, newline='', encoding="utf-8") as f:
canoniccl_reader = csv.reader(f)
next(canoniccl_reader) # Skip header (first line)
canoniccl_data = [row for row in canoniccl_reader]
for row in canoniccl_data:
peer.append((row[0],row[1], row[2])) # list of tuples of peers (first_names, last_names, emails)
buddy.append((row[0], row[1], row[2])) # create a list of tuples for buddy program (firstname, lastname, email)
# Buddy Program
shuffle(peer) # Shuffle peer list once, to randomize buddy for each new assignment
# Unzip peer tuple and convert to lists
first_names, last_names, emails = zip(*peer)
first_names = list(first_names)
last_names = list(last_names)
emails = list(emails)
# Shuffle buddy list so that no student is assigned to himself as a buddy
shuffle(buddy)
i = 0
while i in range(len(emails)):
if buddy[i][2] == emails[i]:
shuffle(buddy)
i = 0
else:
i += 1
# Assign students to eachother
buddy_email = []
order = []
for i in buddy:
buddy_email.append(i[2])
for i in range(len(emails)):
email = emails[i]
order.append(buddy_email.index(email))
order.reverse()
buddy_ordered = [buddy[i] for i in order]
buddy_two = [('', '', '')] * len((emails)) # creates an empty list of tuples
# Check for peer without a buddy and if necessary assigns a student a second buddy
for i in range(len(emails)):
if buddy_ordered[i][2] == emails[i]:
if emails[i] != emails[0]:
buddy_two[0] = buddy_ordered[i]
buddy_ordered[i] = (first_names[0], last_names[0], emails[0]) # Assign buddyless peer to buddy with two buddies
# End of Buddy Code
# Generate anonymous ID and numbers specific to each student
for email in emails:
[ID_A, ID_G, rand_numbers]=hash_function(email)
custom_ID_A.append(ID_A)
custom_ID_G.append(ID_G)
rand_matrix.append(rand_numbers)
###########################################################################
# Parameters and solutions are specific for this excercise
# Example exercise for demonstration: calculate area of rectangle given its sides
# Generate custom values for each student from the semi-random numbers
for i in range (0,len(first_names)):
# Input values for the side lengths [m]
min_x = 10
max_x = 20
width = min_x+(max_x-min_x)*rand_matrix[i][0]
length = min_x+(max_x-min_x)*rand_matrix[i][1]
# side lengths [m]
width = round(width) # round-off decimals = 0
length = round(length) # round-off decimals = 0
# area of rectangle [m^2]
area = width*length
# area of rectangle when sides doubled [m^2]
area_doubled = area * 4
# Store the calculated custom numbers for the assignments
custom_values.append([
length, width,
area, area_doubled,
])
###########################################################################
# Store the custom assignment data for all the students in a csv file
with open (output_csv_path,'w',newline='', encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(['first name','last name','email','custom_ID_A',
'custom_ID_G','buddy first name', 'buddy email', 'buddy two first name', 'buddy two email']
+ markers)
for i in range(0,len(first_names)):
writer.writerow([first_names[i], last_names[i], emails[i],
custom_ID_A[i], custom_ID_G[i], buddy_ordered[i][0], buddy_ordered[i][2],
buddy_two[i][0], buddy_two[i][2]] + custom_values[i])
# For each student, generate a tex file for the assignment
for i in range (0,len(first_names)): # For each student
temp_assig = assignment
for j in range (0, len(markers)): # Replace each marker by a custom value
temp_assig = temp_assig.replace(markers[j],str(custom_values[i][j]))
# Mark assignment with the student custom ID
temp_assig = temp_assig.replace(marker_customID_A, str(custom_ID_A[i]))
temp_assig = temp_assig.replace(marker_customID_G, str(custom_ID_G[i]))
# Mark assignment with the student first name
temp_assig = temp_assig.replace(marker_firstname, str(first_names[i]))
# Mark assignment with the student first name and last name
full_name = str(first_names[i]) + ' ' + str(last_names[i])
temp_assig = temp_assig.replace(marker_fullname, full_name)
# Produce the custom tex file for each student
with open ((output_assignment_preamble+custom_ID_A[i]+'.tex'),'w', encoding="utf-8") as f:
for line in temp_assig:
f.write(line)
# For each peer, generate a tex file for the grading
for i in range (0,len(first_names)): # For each student
temp_sol_grading = assig_solution_grading
for j in range (0, len(markers)): # Replace each marker by a custom value
temp_sol_grading = temp_sol_grading.replace(markers[j],str(custom_values[i][j]))
# Mark solution with the student custom ID
temp_sol_grading = temp_sol_grading.replace(marker_customID_A, str(custom_ID_A[i]))
temp_sol_grading = temp_sol_grading.replace(marker_customID_G, str(custom_ID_G[i]))
# Mark solution with the student first name
temp_sol_grading = temp_sol_grading.replace(marker_firstname, str(first_names[i]))
# Mark solution with the student first name and last name
full_name = str(first_names[i]) + ' ' + str(last_names[i])
temp_sol_grading = temp_sol_grading.replace(marker_fullname, full_name)
# Produce the custom tex file for each student
with open ((output_solution_grading_preamble+custom_ID_G[i]+'.tex'),'w', encoding="utf-8") as f:
for line in temp_sol_grading:
f.write(line)
# For each student, generate a tex file for the solution
for i in range (0,len(first_names)): # For each student
temp_sol_graded = assig_solution_graded
for j in range (0, len(markers)): # Replace each marker by a custom value
temp_sol_graded = temp_sol_graded.replace(markers[j],str(custom_values[i][j]))
# Mark solution with the student custom ID
temp_sol_graded = temp_sol_graded.replace(marker_customID_A, str(custom_ID_A[i]))
temp_sol_graded = temp_sol_graded.replace(marker_customID_G, str(custom_ID_G[i]))
# Mark solution with the student first name
temp_sol_graded = temp_sol_graded.replace(marker_firstname, str(first_names[i]))
# Mark solution with the student first name and last name
full_name = str(first_names[i]) + ' ' + str(last_names[i])
temp_sol_graded = temp_sol_graded.replace(marker_fullname, full_name)
# Produce the custom tex file for each student
with open ((output_solution_graded_preamble+custom_ID_A[i]+'.tex'),'w', encoding="utf-8") as f:
for line in temp_sol_graded:
f.write(line)
# End of script
......@@ -3,6 +3,7 @@
Author: Germán Santa-Maria
Script: Email Assignments
"""
###########################################################################
# Parameters specific to assignment exercise
......@@ -29,7 +30,7 @@ password = input("Type your password and press enter:")
# Delay time between sending two emails, in seconds
# For example, set 240 seconds if you are limited to less than 30 emails/hour
# and 40 seconds if the limit is less than 30 emails/10 min
delay_time = 45
delay_time = 2 # just 2 seconds, low enough to be fast, high enough to see what is happening
"""-------------------------------------------------------------------------"""
# Import email sender function
......@@ -43,11 +44,15 @@ from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# Initialize arrays to store data
names = [] # Student first name
first_name = [] # Student first name
last_name = [] # Student last name
emails = [] # Student email address
custom_ID_A = [] # Custom ID of the assignment for the student
custom_ID_G = [] # Custom ID of the grading file for the student
buddy_name = [] # First name of buddy
buddy_email = [] # Email of buddy
buddytwo_name = []
buddytwo_email = []
attachments = [] # Name of the attachment for each student
email_bodies = [] # Body of the email for each student
text_emails = [] # Array with the complete email text for each student
......@@ -59,11 +64,15 @@ with open(input_csv_path, newline='') as f:
next(student_info_reader) # Skip header [name, last name, email, ...]
student_data = [row for row in student_info_reader]
for row in student_data:
names.append(row[0])
first_name.append(row[0])
last_name.append(row[1])
emails.append(row[2])
custom_ID_A.append(row[3])
custom_ID_G.append(row[4])
buddy_name.append(row[5])
buddy_email.append(row[6])
buddytwo_name.append(row[7])
buddytwo_email.append(row[8])
for ID in custom_ID_A:
attachment_name = "assignment"+assignment_suffix+"_"+ID+".pdf"
......@@ -84,25 +93,95 @@ for i in range(0, len(custom_ID_A)):
email_subject = "Your coursework assignment #1"
standard_body = """Dear XXstudent_nameXX,
standard_body_onebuddy = """
Dear XXstudent_first_nameXX,
I have the pleasure of handing you (attached) your first coursework assignment in our course. I hope you will participate in the program, and have fun doing so! The coursework program has a total of three assignments, and is managed entirely by e-mail. Below, I explain how the program works and what to expect.
...
(insert email body here)
...
Buddy Program
=============
For this assignment, we are introducing you to a work buddy! Your buddy for this assignment is XXbuddy_first_nameXX, and his or her email is XXbuddy_emailXX .
We paired up buddies to give everyone a chance to work with somebody different than usual. Your grade and your buddy’s grade are completely unrelated, and you still receive different assignments. You may work together if you wish, sharing your findings and reviewing one another’s work. Or, you may quietly ignore one another. It is completely up to you. Still, we hope you will take the chance, and ping them for a Zoom talk or something.
Grading
=======
Your assignment will be graded anonymously by two fellow students. In turn, next week, you will be asked to grade two assignments from other students. To do this, you will be provided with a complete answer sheet and a marking template. You are required to grade your peers rigorously and thoroughly in order to receive a grade yourself.
Format of your answer
=====================
Please send one PDF file containing your complete answer. The PDF file should be named:
XXsubmissionfilenameXX
Send your file to this email address as an attachment (the title and content of the email do not matter; they will not be read).
Your answer will be graded anonymously, and so it must not contain *any* personally-identifying information: no name, no pseudonym, no matriculation number, no code number of any kind. Non-anonymous answers will be automatically discarded.
You can write your answer as you like (i.e. by hand, or by typing it up), and create a PDF with your preferred tools. It only matters that your answer arrives as a clearly-legible PDF document.
Conclusion
==========
This is a lot of explanations, but in principle, this should be fun, and the coursework is not very hard. If you have any question about how this program works, you can contact me at (redacted). I look forward to receiving your answer!
With best regards,
(Author)
"""
###########################################################################
# Email for student with two buddies
standard_body_twobuddies = """
Dear XXstudent_first_nameXX,
I have the pleasure of handing you (attached) your first coursework assignment in our course. I hope you will participate in the program, and have fun doing so! The coursework program has a total of three assignments, and is managed entirely by e-mail. Below, I explain how the program works and what to expect.
...
(insert email body here)
...
Buddy Program
=============
%%% Format of your answer
For this last assignment, we are introducing you to a work buddy! Because there are an odd number of students in this program, you are matched to two buddies this time.
Your first buddy for this assignment is XXbuddy_first_nameXX, and his or her email is XXbuddy_emailXX. Your second buddy is XXbuddytwo_first_nameXX, and his or her email is XXbuddytwo_emailXX .
We paired up buddies to give everyone a chance to work with somebody different than usual. Your grade and your buddies’ grades are completely unrelated, and you still receive different assignments. You may work together if you wish, sharing your findings and reviewing one another’s work. Or, you may quietly ignore one another. It is completely up to you. Still, we hope you will take the chance, and ping them for a Zoom talk or something.
Grading
=======
Your assignment will be graded anonymously by two fellow students. In turn, next week, you will be asked to grade two assignments from other students. To do this, you will be provided with a complete answer sheet and a marking template. You are required to grade your peers rigorously and thoroughly in order to receive a grade yourself.
Format of your answer
=====================
Please send one PDF file containing your complete answer. The PDF file should be named:
XXsubmissionfilenameXX
XXsubmissionfilenameXX
Send your file to this email address as an attachment (the title and content of the email do not matter; they will not be read).
Your answer will be graded anonymously, and so it must not contain *any* personally-identifying information: no name, no pseudonym, no matriculation number of any kind.
Your answer will be graded anonymously, and so it must not contain *any* personally-identifying information: no name, no pseudonym, no matriculation number, no code number of any kind. Non-anonymous answers will be automatically discarded.
You can write your answer as you like (i.e. by hand, or by typing it up), and create a PDF with your preferred tools. It only matters that your answer arrives as a clearly-legible PDF document.
%%% Conclusion
Conclusion
==========
This is a lot of