Created
July 1, 2019 01:34
-
-
Save Jazy5552/2787512ee9c5f740047a6daa7fe5d032 to your computer and use it in GitHub Desktop.
My UF enrollment python script
This file contains 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
''' | |
UF Scheduler made in Python | |
By: Jazy Llerena | |
This messy little script will log in to your uf accound and check | |
for available courses according to the courses array. If a course | |
is found available an email is sent to the destination email and loged | |
to the console. | |
settings.txt file | |
email -- If false an email will not be sent when a course is found or section added | |
removecourse -- If true the appropriate course will be removed from further search after | |
it has been found | |
autoaddcourse -- If true the appropriate course will automatically be added when it is found | |
if more than one are found then the first one will be added | |
ignoreschedule -- If true then any sections (or courses maybe) you are currently | |
onlyemailforsections -- If true then an email will only be sent when a section for the course is found (The section found must be in sections.txt) | |
autoaddsection -- If true then will attempt to enroll into section if found | |
enrolled in will be ignored/skipped when sending the email | |
''' | |
settings = {'email':'true', | |
'removecourse':'false', | |
'autoaddcourse':'false', | |
'ignoreschedule':'true', | |
'onlyemailforsections':'false', | |
'autoaddsection':'false'} #Defaults | |
import os | |
import sys | |
import smtplib | |
from email.mime.text import MIMEText | |
from email.mime.image import MIMEImage | |
from email.mime.multipart import MIMEMultipart | |
import time | |
import base64 | |
from selenium import webdriver | |
from selenium.webdriver.common.keys import Keys | |
courses = []#'ENC1102', 'ENC1101', 'ENC1145', 'ENC2305'] #ENC1145 | |
sections = []#'7370', '07F0'] #Any sections in here will be added to your schedule | |
#if they become available | |
destemails = [] #Read from file | |
username = '' #Read from file | |
password = '' #Will be encoded into a file | |
#Dont change below | |
ignoresections = [] | |
ignorecourses = [] | |
passfile = 'pass.txt' | |
coursesfile = 'courses.txt' | |
sectionsfile = 'sectionstoadd.txt' | |
emailsfile = 'emails.txt' | |
settingsfile = 'settings.txt' | |
eusername = '' | |
epassword = b'' # Ma-ma mia, that is one spicy plain text password (Actually encoded) | |
semestercode = 'S'; | |
authenticationtitle = "Web Login Service"; | |
#Spring: S | |
#Summer: U | |
#Fall: F | |
if semestercode == 'S': | |
semester = 'Spring' | |
elif semestercode == 'U': | |
semester = 'Summer' | |
elif semestercode == 'F': | |
semester = 'Fall' | |
#endif | |
#Look for a settings file or make one | |
if os.path.isfile(settingsfile): | |
with open(settingsfile, 'r') as f: | |
for line in f: | |
line = line.strip() | |
s = line.split(':') | |
if len(s) > 0: | |
try: | |
settings[s[0]] = s[1] | |
except: | |
print('Error with setting ' + line) | |
#endtry | |
#endif | |
#endfor | |
#endwith | |
else: | |
#Write to file | |
with open(settingsfile, 'w') as f: | |
for key in settings: | |
f.write(key + ':' + settings[key] + '\n') #Trailing \n may cause problems | |
#endfor | |
#endwith | |
#endif | |
#Get the courses from the file or ask the user to enter some | |
if os.path.isfile(coursesfile): | |
with open(coursesfile, 'r') as f: | |
courses = f.readlines() | |
#endwith | |
else: | |
c = input('Please enter courses to stalk seperated by (,)\n' | |
'Example: ENC1101,ENC1102,ENC1145,STA3032\n' | |
': ') | |
courses = c.strip().split(',') | |
#Write to file | |
with open(coursesfile, 'w') as f: | |
for course in courses: | |
f.write(course.strip() + '\n') #Trailing \n may cause problems | |
#endfor | |
#endwith | |
#endif | |
#Get the sections to autoenroll or ask the user to enter some | |
if os.path.isfile(sectionsfile): | |
with open(sectionsfile, 'r') as f: | |
sections = f.readlines() | |
#endwith | |
else: | |
s = input('Please enter sections to auto-enroll in if \n' | |
'they become available seperated by (,)\n' | |
'Example: 1234,4321,EO43,R231\n' | |
': ') | |
sections = s.strip().split(',') | |
#Write to file | |
with open(sectionsfile, 'w') as f: | |
for section in sections: | |
f.write(section.strip() + '\n') #Trailing \n may cause problems | |
#endfor | |
#endwith | |
#endif | |
#Get the username/password for the uf login | |
if os.path.isfile(passfile): | |
#Read the username/password from the file | |
with open(passfile, 'r') as f: | |
enusername = f.readline() | |
enpassword = f.readline() | |
#endwith | |
password = base64.b64decode(enpassword.encode('utf-8')).decode('utf-8') | |
username = base64.b64decode(enusername.encode('utf-8')).decode('utf-8') | |
else: #Ask for a username/password and save it to the file | |
username = input('Please enter your UF username (To be saved to a file):').strip() | |
password = input('Please enter your UF password (To be saved to a file):').strip() | |
enusername = base64.b64encode(username.encode('utf-8')).decode('utf-8') | |
enpassword = base64.b64encode(password.encode('utf-8')).decode('utf-8') | |
with open(passfile, 'w') as f: | |
f.write(enusername + '\n') | |
f.write(enpassword) | |
#endwith | |
#endif | |
#Get the email destinations to send when a class is available or added | |
if os.path.isfile(emailsfile): | |
#Read the emails from the file | |
with open(emailsfile, 'r') as f: | |
destemails = f.readlines() | |
#endwith | |
else: #ask for 1 or multiple emails to send notifications to | |
emails = input('Enter the email(s) you wish the notifications to be\n' | |
'sent to. (Multiple seperated by a comma (,))\n' | |
'Example: jazy555@hotmail.com,jazy555@gmail.com\n' | |
': ') | |
destemails = emails.strip().split(',') | |
#Write to file | |
with open(emailsfile, 'w') as f: | |
for email in destemails: | |
f.write(email.strip() + '\n') #Trailing \n may cause problems | |
#endfor | |
#endwith | |
#endif | |
def DeletePics(): | |
for file in os.listdir('./'): | |
if file.endswith('.png'): #Check for course image only? | |
os.remove(file) | |
#endif | |
#endfor | |
#enddef | |
def SendEmail(msg, course, dest): | |
fromaddr = eusername | |
toaddr = dest | |
message = MIMEMultipart() | |
message['Subject'] = 'UF Scheduler ' + course + ' available' | |
message['From'] = fromaddr | |
message['To'] = toaddr | |
text = MIMEText(msg) | |
message.attach(text) | |
for file in os.listdir('./'): | |
if file.endswith('.png') and course in file: #Check for course image only? | |
img_data = open(file, 'rb').read() | |
image = MIMEImage(img_data, name=os.path.basename(file)) | |
message.attach(image) | |
#endif | |
#endfor | |
server = smtplib.SMTP('smtp.gmail.com:587') | |
server.ehlo() | |
server.starttls() | |
server.login(eusername,base64.b64decode(epassword).decode('utf-8')) | |
server.sendmail(fromaddr, toaddr, message.as_string()) | |
server.quit() | |
print('Email sent to ' + dest) | |
#enddef | |
def RemoveCourse(course): | |
lines = [] | |
if os.path.isfile(coursesfile): | |
with open(coursesfile, 'r') as f: | |
lines = f.readlines() | |
#endwith | |
with open(coursesfile, 'w') as f: | |
for line in lines: | |
if line.strip() != course: | |
f.write(line) | |
#endif | |
#endfor | |
#endwith | |
#endif | |
#enddef | |
def RemoveSection(section): | |
lines = [] | |
if os.path.isfile(sectionsfile): | |
with open(sectionsfile, 'r') as f: | |
lines = f.readlines() | |
#endwith | |
with open(sectionsfile, 'w') as f: | |
for line in lines: | |
if line.strip() != section: | |
f.write(line) | |
#endif | |
#endfor | |
#endwith | |
#endif | |
#enddef | |
print("\n") | |
print('*'*60) | |
print(time.strftime('%c')) | |
print('Commencing UF Scheduler for ' + semester) | |
loaded = False #Check for browser load (Weird bug in firefox/selenium) | |
for i in range(3): | |
try: | |
driver = webdriver.Chrome('/home/jazy/uf-scheduler/chromedriver') | |
driver.maximize_window(); #Maximize window | |
loaded = True | |
except Exception as e: | |
print('Error starting firefox: ' + str(e)) | |
#endtry | |
if loaded: break #Browser has loaded break | |
#endfor | |
if not loaded: sys.exit() #Browser failed to load | |
#endif | |
driver.get('https://student.ufl.edu/cgi-bin/nirvana?MDASTRAN=' + | |
'RSI-' + semestercode + 'SCHED') | |
time.sleep(3) | |
assert authenticationtitle in driver.title | |
print('Browser Loaded...') | |
if authenticationtitle in driver.title: | |
#Enter the password | |
elem = driver.find_element_by_id('username') | |
elem.send_keys(username) | |
elem = driver.find_element_by_id('password') | |
elem.send_keys(password) | |
elem = driver.find_element_by_name('_eventId_proceed') | |
elem.click() | |
#elem = driver.find_element_by_name('login') | |
time.sleep(2) | |
#Check for lousy terms of service | |
src = driver.page_source | |
if src.find('<h3>Terms of Service') > -1: | |
#Uggg | |
elem = driver.find_element_by_id('accept') | |
elem.click() | |
elem = driver.find_element_by_xpath('//input[@id="accept"]/../button') | |
elem.click() | |
time.sleep(2) | |
#endif | |
#Now check if that all worked | |
if authenticationtitle in driver.title: | |
#Failed authentication | |
driver.save_screenshot('debug.jpg') | |
print('Authentication failed... if password has changed\n' | |
'delete pass.txt file to be prompted for the username\n' | |
'and password again.') | |
driver.quit() | |
sys.exit() | |
#endif | |
print('Authenticated...') | |
#endif | |
#Get currently enrolled sections and courses to ignore | |
if settings['ignoreschedule'] == 'true': | |
try: | |
elem = driver.find_element_by_id('reg_sched') | |
except Exception as e: | |
driver.save_screenshot('debug.jpg') | |
print('Error (debug.jpg screenshot taken): ' + str(e)) | |
driver.quit() | |
sys.exit() | |
#endtry | |
elem = elem.find_element_by_tag_name('tbody') | |
rows = elem.find_elements_by_tag_name('tr') | |
rows = rows[1:-1] | |
for r in rows: | |
#These will be the ones with sections or empty | |
e = r.find_elements_by_tag_name('td') | |
ignoresections.append(e[0].get_attribute('innerHTML')) | |
ignorecourses.append(e[2].get_attribute('innerHTML')) | |
#print('Section: ' + e[0].get_attribute('innerHTML')) | |
#print('Course: ' + e[2].get_attribute('innerHTML')) | |
#endfor | |
#endif | |
#print("\n".join([str(x) for x in ignorecourses])) #Debug | |
#Take a pic of the current schedule | |
driver.execute_script('document.getElementById("links").' + | |
'style.display="none";') | |
driver.execute_script('document.getElementsByClassName("menuGroup")[0]' + | |
'.style.display = "none";') | |
driver.save_screenshot('schedule.jpg') | |
# #Now get time schedule Do not do for summer! | |
if semestercode != 'U' : | |
e = driver.find_element_by_xpath('//*[@value=' + | |
'"View your Weekly Planner Schedule"]') | |
e.submit() | |
driver.execute_script('document.getElementById("links").' + | |
'style.display="none";') | |
driver.execute_script('document.getElementsByClassName("menuGroup")[0]' + | |
'.style.display = "none";') | |
#Clutter removed now take a screenshot | |
driver.save_screenshot('timeschedule.jpg') | |
#endif | |
#Search for INSERT SEMESTER HERE with all the courses in courses[] | |
for course in courses: | |
course = course.strip() #Strip to remove trailing \n | |
#Check if ignoring this course | |
ignore = False | |
for c in ignorecourses: | |
if c.strip() == course: | |
ignore = True | |
print('Ignoring course: ' + course) | |
#endif | |
#endfor | |
if course == '': | |
continue | |
#endif | |
driver.execute_script('doeagle("RS' + semestercode + '-RGCHK2");') | |
time.sleep(2) #eww | |
try: | |
driver.execute_script('toggleLayer("search_std");') | |
except BaseException as e: | |
print('Error toggleing search_std picture in debug.jpg') | |
driver.save_screenshot('debug.jpg') | |
driver.quit() | |
sys.exit() | |
#endtry | |
elem = driver.find_element_by_name('REGCSE') | |
elem.send_keys(course) | |
elem.submit() | |
print('Submitted course: ' + course) | |
courseInfos = '' | |
elem = driver.find_element_by_xpath('//div[@id="reg_srch_results"]') | |
sectionadded = False | |
addsection = False | |
for e in elem.find_elements_by_xpath('.//td[contains(., "' + course + '")]'): | |
if sectionadded: | |
break | |
#Course td is selected | |
e = e.find_element_by_xpath('..') | |
if not 'NO SECTIONS AVAILABLE' in e.get_attribute('innerHTML'): | |
#Found | |
driver.save_screenshot(course + '.png') #temporary | |
#print('element: ' + e.get_attribute('innerHTML') + '\n\n') | |
#Parse the html | |
courseInfo = '' | |
cName = e.find_elements_by_xpath('td')[0].get_attribute('innerHTML') | |
cSection = e.find_element_by_xpath('td//form//input[contains(@value, "ADD ")]').get_attribute('value') | |
cSection = cSection[4:] #ew c Section | |
cSeats = e.find_elements_by_xpath('td')[2].get_attribute('innerHTML') | |
cTitle = e.find_elements_by_xpath('td')[3].get_attribute('innerHTML') | |
cCredits = e.find_elements_by_xpath('td')[4].get_attribute('innerHTML') | |
#WARNING In summer there is an ABC column before meet1! | |
cMeet1 = e.find_elements_by_xpath('td')[5].get_attribute('innerHTML') | |
cMeet1 = cMeet1.replace('<br><hr>', ' Room:') | |
cMeet2 = e.find_elements_by_xpath('td')[6].get_attribute('innerHTML') | |
cMeet2 = cMeet2.replace('<br><hr>', ' Room:') | |
cMeet3 = e.find_elements_by_xpath('td')[7].get_attribute('innerHTML') | |
cMeet3 = cMeet3.replace('<br><hr>', ' Room:') | |
courseInfo = ''' | |
Course: %s | |
Section: %s | |
Seats: %s | |
Title: %s | |
Meet1: %s | |
Meet2: %s | |
Meet3: %s | |
''' % (cName, cSection, cSeats, cTitle, cMeet1, cMeet2, cMeet3) | |
print(courseInfo) | |
#Check if ignored section | |
for s in ignoresections: | |
if s.strip() == cSection.strip(): | |
courseInfo = '' | |
print('Skipping section ' + cSection) | |
break | |
#endif | |
#endif | |
#Check if ignoreing course to not email | |
if ignore: | |
courseInfo = '' | |
#endif | |
#Check settings | |
if settings['autoaddcourse'] == 'true' and courseInfo != '': | |
sections.append(cSection) #Cheeky | |
addsection = True | |
#endif | |
#Check if the section is up for autoenroll | |
if settings['onlyemailforsections'] == 'true': | |
if any(cSection.strip() in s for s in sections): | |
#Send the email with course info | |
pass | |
else: | |
print('Course ignored because section ' | |
+ cSection + ' is not being looked for and the ' | |
+ 'onlyemailforsectinos setting is enabled\n') | |
courseInfo = '' | |
#endif | |
#endif | |
if settings['autoaddsection'] == 'true' or addsection: | |
for s in sections: | |
if cSection.strip() == s.strip(): | |
#We have a match boissss! Hook it up | |
e.find_element_by_xpath('td//form//input[contains(@value, "ADD ")]').submit() | |
driver.find_element_by_name('FORM1').submit() | |
mes = driver.find_element_by_id('reg_error').text | |
courseInfo += mes | |
print(mes + '\n') | |
# if driver.page_source.find('reg_good') > -1: | |
# courseInfo += '***SECTION ' + cSection + ' HAS BEEN ADDED***' | |
# print('Section ' + s + ' was added.\n') | |
# else: | |
# error = driver.find_element_by_id('reg_error').text | |
# courseInfo += 'Failed to add ' + cSection + ' section: ' + error | |
# print('Failed to add section ' + s + ': ' + error + '\n') | |
# #endif | |
RemoveSection(s.strip()) #Everyone strips around here... | |
driver.back() | |
driver.back() | |
#Break out to outer for loop somehow | |
sectionadded = True | |
#endif | |
#endfor | |
#endif | |
courseInfos += courseInfo + '\n' | |
#endif | |
#endfor td | |
if not courseInfos.strip() == '': | |
#Send email and take screenshot | |
if settings['email'] == 'true': | |
for email in destemails: | |
SendEmail(courseInfos, course, email) | |
#endfor | |
#endif | |
if settings['removecourse'] == 'true': | |
RemoveCourse(course) | |
#endif | |
elif not ignore: | |
print(course + ' course is not available.\n') | |
#endif | |
DeletePics() | |
#endfor courses | |
driver.quit() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment