Skip to content

Instantly share code, notes, and snippets.

@Jazy5552
Created July 1, 2019 01:34
Show Gist options
  • Save Jazy5552/2787512ee9c5f740047a6daa7fe5d032 to your computer and use it in GitHub Desktop.
Save Jazy5552/2787512ee9c5f740047a6daa7fe5d032 to your computer and use it in GitHub Desktop.
My UF enrollment python script
'''
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