Last active
May 2, 2018 16:07
-
-
Save DeflateAwning/5878a699af8477806891d9971e918666 to your computer and use it in GitHub Desktop.
Tool for watching class availability for non-waitlistable UCalgary classes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import urllib.request | |
import xml.etree.ElementTree as ET | |
import time, random, math | |
import smtplib | |
from email.message import EmailMessage | |
# from IPython import embed # for debugging, requires pip install | |
# Configuration: Email Login | |
fromEmail = "[email protected]" | |
fromEmailPassword = "asdfASDF1234" | |
# Configuration: Email Sending | |
watchList = {"2018 Spring|MATH 375":["<your email>"], "2018 Spring|ENCM 339":["<your email>"]} | |
# General Constant Declaration | |
semesterList = {"2018 Spring":"32183", "2018 Summer":"32185", "2018 Fall":"32187", "2019 Winter":"32191"} | |
requestString = "https://vsb.my.ucalgary.ca/getclassdata.jsp?term=%TermNumber%&course_0_0=%CourseName%&rq_0_0=null&t=%tCode%&e=%eCode%&nouser=1&_=%dateCode%" | |
def getOpenSpots(course): | |
# Generate stupid request data to please server | |
dateCode = str(int(time.time())) + str(random.randint(111, 999)) # get time since epoch, in ms (add the ms component to seconds randomly) | |
tCode = (math.floor((int(dateCode)) / 60000)) % 1000 | |
eCode = tCode%3 + tCode%29 + tCode%42 | |
thisSemester = course.split("|")[0] | |
thisCourse = course.split("|")[1] | |
response = urllib.request.urlopen(requestString.replace("%TermNumber%", semesterList[thisSemester]).replace("%CourseName%", thisCourse.replace(" ", "-")).replace("%tCode%", str(tCode)).replace("%eCode%", str(eCode)).replace("%dateCode%", dateCode)).read() | |
root = ET.fromstring(response) | |
#print(response) | |
if "Check your PC" in str(response): | |
# An error has occured, return false and print an error | |
print("PC time error.") | |
return False | |
OSReport = {} # example: {'LEC': 0, 'TUT': 0, 'LAB': 0} | |
for block in root.iter("block"): | |
# each block is a TUT0x or LEC0x or other entry | |
disp = block.attrib["disp"] # TUT 01 | |
os = int(block.attrib["os"]) # number of open spots in TUT 01 | |
typeof = block.attrib["type"] # TUT or LEC or LAB | |
#print(os) | |
if typeof not in OSReport: | |
OSReport[typeof] = os | |
else: | |
OSReport[typeof] += os | |
return OSReport | |
def sendNotificationEmail(toAddress, courseName, OSReport): | |
totalOS = sum([OSReport[i] for i in OSReport]) | |
detailsBreakdown = "\r\n".join(["\t- " + i + ": " + str(OSReport[i]) + " Open Spots" for i in OSReport]) | |
message = """ | |
courseName has totalOS open spots (including labs, lectures, and tutorials)! Sign up immediately on http://my.ucalgary.ca or with the UCalgary Schedule Builder (https://vsb.my.ucalgary.ca/login.jsp) to get a spot in courseName. | |
Open spots in each course component: | |
detailsBreakdown""".replace("courseName", courseName).replace("totalOS", str(totalOS)).replace("detailsBreakdown", detailsBreakdown) | |
body = "\r\n".join(["To: " + toAddress, "From: " + fromEmail, "Subject: Class Availability Notifier: " + courseName + " has an open spot!", '', message]) | |
server = smtplib.SMTP("smtp.mail.com", 587) # Port: 587 | |
server.ehlo() | |
server.starttls() | |
server.login(fromEmail, fromEmailPassword) | |
try: | |
server.sendmail(fromEmail, [toAddress], body) | |
print("Message successfully sent to " + toAddress + " for spot in " + courseName) | |
except: | |
print("Error: Message not sent to " + toAddress + " for spot in " + courseName) | |
server.quit() | |
#sendNotificationEmail(fromEmail, "Math 200", {'LEC': 1, 'TUT': 1, 'LAB': 2}) # Sample Run for Testing Mailer | |
#sendNotificationEmail(fromEmail, "Math 200", {}) # Sample Run for Testing Mailer | |
while 1: | |
for course in watchList: | |
courseOSReport = getOpenSpots(course) | |
totalOS = sum([courseOSReport[i] for i in courseOSReport]) # Current used metric | |
minOS = min([courseOSReport[i] for i in courseOSReport]) # Really the useful one because you need all course components | |
if totalOS > 0: | |
for toAddress in watchList[course]: | |
sendNotificationEmail(toAddress, course, courseOSReport) | |
print(course, str(courseOSReport)) | |
#print(courseOSReport) | |
time.sleep(1) | |
time.sleep(6) | |
# embed() # Debug Console | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment