diff --git a/antplanner.py b/antplanner.py index 30cb2f0..7f9c8f1 100644 --- a/antplanner.py +++ b/antplanner.py @@ -10,6 +10,8 @@ from google.appengine.api import memcache from google.appengine.api import users +import logging + urls = ( '/', 'index', '/search', 'search', @@ -19,7 +21,8 @@ '/admin', 'admin', '/admin/flush-cache', 'adminFlushCache', '/admin/latest-web-soc', 'latestWebSoc', - '/admin/delete-old-schedules', 'deleteOldSchedules' + '/admin/delete-old-schedules', 'deleteOldSchedules', + '/prof', 'getProf' ) render = web.template.render('templates/') @@ -107,6 +110,29 @@ class loadSchedule(): def GET(self): return schedule.load_schedule(web.input().username) +class getProf(): + def GET(self): + p = web.input() + #logging.debug(p) + #data = memcache.get("PROF") + if p is None or p.name is None: + return get_rmp_error('Empty Request','The professor must have a last name in order to find ratings.') + #if data is None: + try: + q = urllib.quote_plus(p.name[0]) + #logging.debug('Query param: ' + q) + raw_page = urlfetch.fetch("http://www.ratemyprofessors.com/SelectTeacher.jsp?the_dept=All&sid=1074&orderby=TLName&letter=" + q, + method=urlfetch.GET, + deadline=10) + data = scraper.strip_professors(raw_page.content, unicode(p.name)) + #memcache.add("PROF", data, 60 * 60) + except urlfetch.DownloadError: + data = get_rmp_error('urlfetch.DownloadError','RateMyProfessors.com request exceeded 10 seconds') + except urlfetch.Error: + data = get_rmp_error('urlfetch.Error','RateMyProfessors.com is not available at the moment') + + return data + if __name__ == "__main__": app = web.application(urls, globals()) app.cgirun() diff --git a/app.yaml b/app.yaml index 7c89692..a5e287b 100644 --- a/app.yaml +++ b/app.yaml @@ -1,5 +1,5 @@ -application: antplanner -version: release-0-9-8-1 +application: antplanner-fork +version: release-0-9-8-2 api_version: 1 runtime: python @@ -10,4 +10,4 @@ handlers: script: antplanner.py login: admin - url: .* - script: antplanner.py \ No newline at end of file + script: antplanner.py diff --git a/scraper.py b/scraper.py index 9b1fdd7..31dd3f8 100644 --- a/scraper.py +++ b/scraper.py @@ -1,5 +1,7 @@ from lib.BeautifulSoup import BeautifulSoup import re +from django.utils import simplejson as json +import logging def strip_search(html): form_html = BeautifulSoup(html).find('form', action='http://websoc.reg.uci.edu/') @@ -28,4 +30,66 @@ def strip_websoc_version(html): return 'Couldn\'t find a match' else: return version_matches[0] - \ No newline at end of file + +def get_rmp_error(title, message): + data = { + 'name': title, + 'href': 'javascript:void(0);', + 'dept': message, + 'ratings': '0', + 'quality': '0', + 'easiness': '0', + 'hot': 'Ø' + } + return json.dumps(data) + +def strip_professors(html, name): + table = BeautifulSoup(html).find('div', {'id': 'ratingTable'}) + if table is None: + logging.debug(html[500:]) + return get_rmp_error('Parse Error','Could not find "ratingTable" at RateMyProfessors.com') + else: + profs = list() + #name = name.upper() + split = name.split(','); + qLastName = split[0].strip() + qFirstName = split[1].strip() + if (qFirstName == None or qFirstName == ''): + qFirstName = '!' + rows = table.findAll('div', {'class': re.compile(r".*\bentry\b.*")}) + for row in rows: + divName = row.find('div', {'class': 'profName'}) + anchor = divName.find('a') + profName = unicode(anchor.renderContents().strip(), 'utf-8', 'ignore').upper() + split = profName.split(','); + lastName = split[0].strip() + firstName = split[1].strip() + if (firstName == None or firstName == ''): + firstName = '!' + #logging.debug(qLastName + ' =? ' + lastName + ' && ' + qFirstName + ' =? ' + firstName) + if lastName == qLastName and firstName[0] == qFirstName[0]: + href = 'http://www.ratemyprofessors.com/' + anchor['href'].strip() + profDept = row.find('div', {'class': 'profDept'}).renderContents().strip() + profRatings = row.find('div', {'class': 'profRatings'}).renderContents().strip() + profQuality = row.find('div', {'class': 'profAvg'}).renderContents().strip() + profEasiness = row.find('div', {'class': 'profEasy'}).renderContents().strip() + profHot = row.find('div', {'class': re.compile(r".*\bprofHot\b.*")}).renderContents().strip() + if profHot == 'Hot': + profHot = '✓' + else: + profHot = ' ' + + prof = { + 'name': profName, + 'href': href, + 'dept': profDept, + 'ratings': profRatings, + 'quality': profQuality, + 'easiness': profEasiness, + 'hot': profHot + } + #logging.debug(prof) + profs.append(prof) + return json.dumps(profs) + + diff --git a/static/css/main.css b/static/css/main.css index 1e30cc4..72c9294 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -111,3 +111,19 @@ iframe#school { width: 50%; float: left; } + +/* professor modal popup */ +#prof-select { + font-size: 14px; + border-collapse: collapse; + margin:10px; +} +#prof-select td, #prof-select th { + border: 1px solid gray; + padding: 5px; +} + +#prof-select a { + color: #688FE7; + font-size: 14px; +} diff --git a/static/js/main.js b/static/js/main.js index 6981062..4962f23 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -307,6 +307,7 @@ function WindowManager() { function SOCParser() { this.SCHEDULE_CODE_INDEX = 0; this.SCHEDULE_TYPE_INDEX = 1; + this.SCHEDULE_PROF_INDEX = 4; this.SCHEDULE_TIME_INDEX = 5; this.getTimeString = function(element) { @@ -326,22 +327,23 @@ function SOCParser() { }; this.getCourseCode = function(element) { - var courseCode = $(element).find('td').eq(this.SCHEDULE_CODE_INDEX).html(); - return courseCode; + return $(element).find('td').eq(this.SCHEDULE_CODE_INDEX).html(); }; this.getCourseType = function(element) { - var courseType = $(element).find('td').eq(this.SCHEDULE_TYPE_INDEX).html(); - return courseType; + return $(element).find('td').eq(this.SCHEDULE_TYPE_INDEX).html(); }; + this.getCourseProf = function(element) { + return $(element).find('td').eq(this.SCHEDULE_PROF_INDEX).html(); + } + this.getCourseString = function(element) { - var courseString = $(element).prevAll().find('.CourseTitle:last').html(); - return courseString; + return $(element).prevAll().find('.CourseTitle:last').html(); } }; -function SOC() { +function SOC() { this.initSOC = function(bridge) { var list = $('.course-list', frames['school'].document); @@ -355,7 +357,7 @@ function SOC() { } ); - //click on course + //click on course (this should be placed on all tr's except instructor) $("tr[valign*='top']", list).click(function() { var socParser = new SOCParser(); var courseUtils = new CourseUtils(); @@ -402,6 +404,66 @@ function SOC() { bridge.addEvent(calEvents[i]); } }); + + // click on instructor + $("tr[valign*='top'] td:nth-child(5)", list).click(function() { + var prof = $(this).html(); + showProfessors(prof); + }); + + // instructor hover + $("tr[valign*='top'] td:nth-child(5)", list).hover( + function() { + $(this).css({'color': 'blue', 'cursor': 'pointer'}); + }, + function() { + $(this).css({'color': 'inherit', 'cursor': 'inherit'}); + } + ); + } +} + +function showProfessors(nameString) { + // name is the query string for professors + var profTable = $('#prof-select'); + profTable.html('