Skip to content

Commit

Permalink
Merge pull request #923 from cornell-dti/automate_email
Browse files Browse the repository at this point in the history
Eric / Implemented Script For Automating Email Outreach Information (In Progress)
  • Loading branch information
EricWeng23 authored Feb 25, 2025
2 parents aa9c89f + 9f40527 commit c1b46d2
Show file tree
Hide file tree
Showing 4 changed files with 437 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ yarn-error.log*

.idea
.DS_Store

# ignore virtual environment
venv/
187 changes: 187 additions & 0 deletions src/scripts/classes.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
Course,Professor,Emails,Has course/professor use QMI before
AEP 1100,Chris Xu,[email protected],-1
AEP 2550,Or Katz,[email protected],-1
AEP 3100,Peter McMahon,[email protected],-1
AEP 3200,Ankit Disa,[email protected],-1
AEP 3550,Valla Fatemi,[email protected],-1
AEP 3620,Kenji Yasuda,[email protected],-1
AEP 4340,Lisa Wickham,[email protected],-1
AEP 4400,Jeffrey Moses,[email protected],-1
BIOMG 1290,Charles Aquadro,[email protected],-1
BIOMG 1350,"Chris Fromme, Kelly Liu","[email protected], [email protected]",-1
BIOMG 2800,"Kristina Blake-Hodek, Maria Serrano","[email protected], [email protected]",-1
BIOMG 3300,Kevin Siegenthaler,[email protected],-1
BIOMG 3350,"Stephen Jesch, Frank Pugh","[email protected], [email protected]",-1
BIOMG 4320,Chun Han,[email protected],-1
BIOMG 4400,Stephen Jesch,[email protected],-1
BIOMG 4610,Mariana Wolfner,[email protected],-1
BIOMG 4880,Zeribe Nwosu,[email protected],-1
CHEM 2070,Julie Laudenschlager,[email protected],-1
CHEM 2080,Robert DiStasio,[email protected],-1
CHEM 2090,Youn Jue Bae,[email protected],-1
CHEM 3580,"Geoffrey Coates, Brett Fors","[email protected], [email protected]",-1
CHEM 3880,Barbara Baird,[email protected],-1
CHEM 4100,Peter Wolczanski,[email protected],-1
CHEM 4810,Fernando Escobedo,[email protected],-1
CHEME 2200,T. Michael Duncan,[email protected],-1
CHEME 3230,Sarah Hormozi,[email protected],-1
CHEME 3320,Shuwen Yue,[email protected],-1
CHEME 3900,Sijin Li,[email protected],-1
CHEME 4020,Ilana Brito,[email protected],-1
CHEME 4620,"Brian Bauer, William Cleary, Marsha Kowal, Jeffrey Varner, Alex Woltornist","[email protected], [email protected], [email protected], [email protected], [email protected]",-1
CS 1110,"Michael Clarkson, Lillian Lee","[email protected], [email protected]",0
CS 1112,Daisy Fan,[email protected],2
CS 1340,"Jon Kleinberg, Karen Levy","[email protected], [email protected]",-1
CS 1700,Haym Hirsh,[email protected],-1
CS 2110,"Matthew Eichhorn, Curran Muhlberger","[email protected], [email protected]",2
CS 2800,"Joe Halpern, Eva Tardos","[email protected], [email protected]",0
CS 3110,Anshuman Mohan,[email protected],0
CS 3410,"Zachary Susag, Hakim Weatherspoon","[email protected], [email protected]",2
CS 3420,Nils Napp,[email protected],-1
CS 3700,Kevin Ellis,[email protected],-1
CS 3780,"Tushaar Gangavarapu, Karthik Sridharan","[email protected], [email protected]",-1
CS 4300,Cristian Danescu-Niculescu-Mizil,[email protected],2
CS 4410,Robbert VanRenesse,[email protected],0
CS 4450,Rachit Agarwal,[email protected],-1
CS 4670,"Bharath Hariharan, Wei-Chiu Ma","[email protected], [email protected]",2
CS 4740,"Claire Cardie, Tanya Goyal","[email protected], [email protected]",-1
CS 4744,Mats Rooth,[email protected],-1
CS 4756,Kuan Fang,[email protected],-1
CS 4782,"Jennifer Sun, Kilian Weinberger","[email protected], [email protected]",-1
CS 4789,Wen Sun,[email protected],0
CS 4810,Dexter Kozen,[email protected],-1
CS 4813,Nicholas Spooner,[email protected],-1
CS 4820,Michael Kim,[email protected],2
CS 4850,Robert Kleinberg,[email protected],-1
ECE 2100,Carl Bernard,[email protected],-1
ECE 2300,Zhiru Zhang,[email protected],2
ECE 2400,Anne Bracy,[email protected],-1
ECE 2720,Vikram Krishnamurthy,[email protected],-1
ECE 3100,Qing Zhao,[email protected],-1
ECE 3140,Nils Napp,[email protected],2
ECE 3150,Amit Lal,[email protected],-1
ECE 3200,Ziv Goldfeld,[email protected],-1
ECE 4150,David Hysell,[email protected],-1
ECE 4160,Elizabeth Helbling,[email protected],-1
ECE 4210,Francesca Parise,[email protected],-1
ECE 4271,David Delchamps,[email protected],-1
ECE 4370,Karan Mehta,[email protected],-1
ECE 4520,Lang Tong,[email protected],-1
ECE 4590,R. van Dover,[email protected],-1
ECE 4670,Aaron Wagner,[email protected],-1
ECE 4740,Alyosha Molnar,[email protected],-1
ECE 4760,Van Adams,[email protected],-1
ECE 4910,Bruce Johnson,[email protected],-1
ECON 1110,George Orlov,[email protected],-1
ECON 1120,Fikri Pitsuwan,[email protected],-1
ECON 2300,Arnab Basu,[email protected],-1
ECON 3030,Philipp Kircher,[email protected],-1
ECON 3040,Mathieu Taschereau-Dumouchel,[email protected],-1
ECON 3110,Kevin Packard,[email protected],-1
ECON 3120,Douglas McKee,[email protected],-1
ECON 3140,Joerg Stoye,[email protected],-1
ECON 3430,Stephanie Thomas,[email protected],-1
ECON 3475,Michele Belot,[email protected],-1
ECON 3550,Elizabeth Tennant,[email protected],-1
ECON 3670,Brandon Tripp,[email protected],-1
ECON 3711,John Cawley,[email protected],-1
ECON 3770,Evan Riehl,[email protected],-1
ECON 3860,C.-Y. Cynthia Lin Lawell,[email protected],-1
ECON 3865,Shanjun Li,[email protected],-1
ECON 4020,Lawrence Blume,[email protected],-1
ECON 4210,Kristoffer Nimark,[email protected],-1
ECON 4270,Stephen Coate,[email protected],-1
ECON 4600,Levon Barseghyan,[email protected],-1
ECON 4620,Adam Harris,[email protected],-1
ECON 4660,Ted O'Donoghue,[email protected],-1
ECON 4903,Eleonora Patacchini,[email protected],-1
INFO 1260,"Jon Kleinberg, Karen Levy","[email protected], [email protected]",-1
INFO 1300,Royal Westwater,[email protected],-1
INFO 3130,Moon Duchin,[email protected],-1
INFO 3140,Shimon Edelman,[email protected],-1
INFO 3200,Claire Wardle,[email protected],-1
INFO 3950,Paul Ginsparg,[email protected],-1
INFO 4100,Rene Kizilcec,[email protected],-1
INFO 4120,Cheng Zhang,[email protected],-1
INFO 4152,Walker White,[email protected],-1
INFO 4220,Cristobal Cheyre Forestier,[email protected],-1
INFO 4250,Daniel Susser,[email protected],-1
INFO 4260,Steven Jackson,[email protected],-1
INFO 4300,Cristian Danescu-Niculescu-Mizil,[email protected],-1
INFO 4310,Jeff Rzeszotarski,[email protected],-1
INFO 4320,Francois Guimbretiere,[email protected],-1
INFO 4360,Drew Margolin,[email protected],-1
INFO 4450,Susan Fussell,[email protected],-1
INFO 4940,Sharlane Cleare,[email protected],-1
MATH 1106,Marie MacDonald,[email protected],-1
MATH 1110,Hosea Wondo,[email protected],-1
MATH 1120,Luke Serafin,[email protected],-1
MATH 1340,Matthew Haulmark,[email protected],-1
MATH 1910,Timothy Healey,[email protected],-1
MATH 1920,Sergio Chaves,[email protected],-1
MATH 2210,Benjamin Dozier,[email protected],-1
MATH 2220,Justin Moore,[email protected],-1
MATH 2240,Jason Manning,[email protected],-1
MATH 2310,Alex Townsend,[email protected],-1
MATH 2930,David Freund,[email protected],-1
MATH 2940,Frans Schalekamp,[email protected],-1
MATH 3110,Timothy Riley,[email protected],-1
MATH 3320,Dan Barbasch,[email protected],-1
MATH 3340,Rachel Webb,[email protected],-1
MATH 3360,Karola Meszaros,[email protected],-1
MATH 4180,Chun Pong Chu,[email protected],-1
MATH 4200,John Hubbard,[email protected],-1
MATH 4280,Alexander Vladimirsky,[email protected],-1
MATH 4310,Andreea Iorga,[email protected],-1
MATH 4500,James West,[email protected],-1
MATH 4540,Xiaodong Cao,[email protected],-1
MATH 4710,Laurent Saloff-Coste,[email protected],-1
MATH 4740,Sungwoo Jeong,[email protected],-1
ORIE 3120,Peter Frazier,[email protected],-1
ORIE 3310,David Shmoys,[email protected],-1
ORIE 3741,Soroosh Shafiee,[email protected],-1
ORIE 4126,Eric Gentsch,[email protected],-1
ORIE 4130,Shane Henderson,[email protected],-1
ORIE 4152,Negin Majedi,[email protected],-1
ORIE 4160,Paul Golz,[email protected],-1
ORIE 4600,John Richard Callister,[email protected],-1
ORIE 4742,Sid Banerjee,[email protected],-1
PHYS 1102,Nam Jung Kim,[email protected],-1
PHYS 1112,Philip Krasicky,[email protected],-1
PHYS 1116,Csaba Csaki,[email protected],-1
PHYS 1203,Nils Deppe,[email protected],-1
PHYS 2208,Matthias Liepe,[email protected],-1
PHYS 2213,Ryan Tapping,[email protected],-1
PHYS 2214,Itai Cohen,[email protected],-1
PHYS 2218,Liam McAllister,[email protected],-1
PHYS 3310,"Jim Alexander, Kyle Shen","[email protected], [email protected]",-1
PHYS 3316,Xiaomeng Liu,[email protected],-1
PHYS 3318,Ivan Bazarov,[email protected],-1
PHYS 4410,Anders Ryd,[email protected],-1
PHYS 4443,Katja Nowack,[email protected],-1
PHYS 4444,Yuval Grossman,[email protected],-1
PHYS 4488,James Sethna,[email protected],-1
PHYS 4688,Georg Hoffstaetter de Torquat,[email protected],-1
STSCI 2150,Melissa Smith,[email protected],-1
STSCI 3740,Yang Ning,[email protected],-1
STSCI 4520,David Kent,[email protected],-1
STSCI 4550,Younghoon Kim,[email protected],-1
"PHYS 2217, AEP 2170",Abigail Crites,[email protected],-1
"AEP 3630, PHYS 3360",Earl Kirkland,[email protected],-1
"ECE 4380, AEP 4450",Francesco Monticone,[email protected],-1
"AEP 4840, ECE 4840",David Hammer,[email protected],-1
"BTRY 3020, STSCI 2100, STSCI 3200",Jeremy Entner,[email protected],-1
"STSCI 3080, BTRY 3080",Florentina Bunea,[email protected],-1
"STSCI 4090, BTRY 4090, MATH 4720",Marten Wegkamp,[email protected],-1
"STSCI 4100, BTRY 4100",Dana Yang,[email protected],-1
"CHEM 1570, CHEM 3570",Cynthia Kinsland,[email protected],-1
"CS 4220, MATH 4260",Anil Damle,[email protected],-1
"CS 4754, INFO 4410",Malte Jung,[email protected],-1
"CS 4758, ECE 4180",Anastasia Bizyaeva,[email protected],-1
"CS 4852, ECON 3825",Cristobal Cheyre Forestier,[email protected],-1
"ECON 3280, ECON 4280",Milena Djourelova,[email protected],-1
"INFO 2310, INFO 4340",Kyle Harms,[email protected],-1
"INFO 3312, INFO 2951",Benjamin Soltoff,[email protected],-1
"MATH 4030, MATH 3040",Anil Nerode,[email protected],-1
"STSCI 3510, ORIE 3510",Jim Dai,[email protected],-1
"STSCI 3110, STSCI 2110",Kevin Packard,[email protected],-1
193 changes: 193 additions & 0 deletions src/scripts/course-scrape.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import requests
import csv
import json

def grab_json(url):
return json.loads(requests.get(url).text)

rosterURL = "https://classes.cornell.edu/api/2.0/config/rosters.json"
rosters = grab_json(rosterURL)['data']['rosters']
# Get the most recent semester from the json
# Note that slug is sem/year (e.g SP25)
roster = rosters[-1]['slug']
# If the most recent semester is a WINTER or SUMMER sem, use the sem before it
if "WI" in roster or "SU" in roster:
roster = rosters[-2]['slug']

# Get all subjects for that semester
subjectsURL = "https://classes.cornell.edu/api/2.0/config/subjects.json?roster="+roster
subjects = grab_json(subjectsURL)['data']['subjects']

relevent_courses = [
'CS',
'BTRY',
'INFO',
'ORIE',
'ECON',
'PHYS',
'CHEM',
'CHEME',
'ECE',
'AEP',
'BIO',
'BIOMG',
'MATH',
'STSCI'
]

# Classes to blacklist because professors are not interested in using QMI
blacklist = [
'INFO 3152',
'CS 3152',
'INFO 4400',
'INFO 4390'
'INFO 4152',
'CS 4152'
]

def getCourseStatus(course, professors):
"""
Returns the status of the course/professors in the past semester on whether they have used QMI before
2 indicates that both the course and professor have used QMI before
1 indicates that professor used QMI before
0 indicates that course used QMI before
-1 indicates that neither the course nor the professor have used QMI before
:param course: The course subject and its code (e.g CS 1110)
:param professors: The string that consist of all the professors teaching the course this sem (e.g "Michael Clarkson, Lillian Lee" )
"""
with open('../scripts/trackers/past_semester.csv', 'r') as file:
csvreader = csv.reader(file)
header = next(csvreader)

for row in csvreader:
if row[0] == course:
# List of professors in the current semester
current_professors = professors.split(", ")

# List of professors in the past semester
past_professors = row[1].split(", ")

# Checks if at least one professor in the current semester is in the past professors
if any(p in past_professors for p in current_professors) or professors in past_professors:
# If the course previously decided to use QMI and the professor is in the past professors, return 2 (both course/prof used QMI)
if row[4] == "yes":
return 2

# If the course previously decided not to use QMI and the professor is in the past professors, return the status from the past semester
return row[3]

# If the professor is not in the past professors
else:
# If the course previously decided to use QMI, return 0 (course used QMI)
if row[4] == "yes":
return 0
# If the course previously decided not to use QMI, but the course and professor has used QMI before, return 0 (course used QMI)
elif row[4] == "no" and row[3] in ["2", "0"]:
return 0
return -1
# If no match is found, return -1 after checking all rows
return -1

classesJSON = []
for s in subjects:
if s['value'] in relevent_courses:
classURL = "https://classes.cornell.edu/api/2.0/search/classes.json?roster="+roster+"&subject=" + s['value']
classesJSON.extend(grab_json(classURL)['data']['classes'])

def getProfessorInfo(instructors):
"""
Returns the names and emails of the professors teaching the course
:param instructors: The list of instructors teaching the course
"""
accum_names = "" # To accumulate all the professor names
accum_emails = "" # To accumulate all the professor emails
for professor in instructors:
name = professor['firstName'] + " " + professor['lastName'] + ", "
email = professor['netid'] + '@cornell.edu' + ", "
accum_names += name
accum_emails += email
# Remove preceding and trailing , and "
return accum_names.strip(', ').strip('\"'), accum_emails.strip(', ').strip('\"')

# Some courses are not relevant
# We will filter out these courses if they contain these words
irrelevant_courses = ["Independent", "Research", "Project", "Projects", "Academic Support", "Sem", "Seminar", "Supplement", "Honors", "Thesis"]

classes = []
for c in classesJSON:
instructors = c['enrollGroups'][0]['classSections'][0]['meetings'][0]['instructors']
names, emails = getProfessorInfo(instructors)
hasProfessor = names != ""
isUndergrad = int(c['catalogNbr']) < 5000
isNonResearch = all(word not in c['titleShort'] for word in irrelevant_courses)
# At least 3 credits
isCredit = c['enrollGroups'][0]['unitsMinimum'] > 2
# Course must be on campus and not Cornell Tech
isOnCampus = c['enrollGroups'][0]['classSections'][0]['locationDescr'] == "Ithaca, NY (Main Campus)"

course = c['subject'] + " " + c['catalogNbr']

# Course must not be blacklisted
isNotBlacklisted = course not in blacklist
item = {
'Course': course,
'Professor': names,
'Emails': emails,
'Has course/professor use QMI before': getCourseStatus(course, names)
}
if hasProfessor and isUndergrad and isNonResearch and isCredit and isOnCampus and isNotBlacklisted:
classes.append(item)

# Dictionary to store professor and the courses they teach
professor_courses = {}

# Populate the dictionary
for item in classes:
course = item['Course']
professor = item['Professor']
email = item['Emails']
status = item['Has course/professor use QMI before']
key = (professor, email, status)
if key in professor_courses:
# Adds the course to the set of courses the professor teaches
professor_courses[key].add(course)
else:
professor_courses[key] = {course}

professors_with_multiple_courses = {}

# Find professors teaching multiple courses
for professor, course_list in professor_courses.items():
if len(course_list) > 1:
professors_with_multiple_courses[professor] = course_list

# Output results
if professors_with_multiple_courses:
for (professor_name, professor_email, status), course_list in professors_with_multiple_courses.items():
# Remove the individual courses from the classes list that have the same professor
for item in classes:
if item['Course'] in course_list:
classes.remove(item)
class_list = list(course_list) # Convert set to list
# Append the combined version of the courses
classes.append({
'Course': ', '.join(course_list),
'Professor': professor_name,
'Emails': professor_email,
'Has course/professor use QMI before': status
})
else:
print("No professors teach multiple courses.")

with open('../scripts/classes.csv', 'w') as file:
field = ["Course", "Professor", "Emails", 'Has course/professor use QMI before']
writer = csv.DictWriter(file, fieldnames=field)

# Write header
writer.writeheader()

# Write rows to the CSV
writer.writerows(classes)
Loading

0 comments on commit c1b46d2

Please sign in to comment.