def post(self): errors = dict() success = False form = SettingsForm(self.request.arguments) current_user = self.decoded_username() if form.validate(): try: current_password = self.get_argument('current_password') new_password = self.get_argument('new_password') new_password_confirm = self.get_argument( 'new_password_confirm') new_email = self.get_argument('new_email') # Do the updates in 2 seperate query sessions. The first session updates the # email address. If an exception occurs (i.e., duplicate email), we skip the second session, # causing nothing to update. If we did both updates in the same session, the password would # still update despite the duplicate email problem. with Query(self.db) as q: # first, ensure that current_password is correct user = q.query(self.db.account).filter_by( username=current_user).one() h = user.password_hash s = user.password_salt email = user.email_address if authenticate.hash_pw(current_password, s) == h: user.email_address = new_email else: errors['password'] = ["Incorrect password"] email = new_email #snag it here (if no exception) so we can pass it in in the self.render(...) call # if len is 0, they omitted the new password from the form, so no need to update if len(new_password) > 0 and authenticate.hash_pw( current_password, s) == h: with Query(self.db) as q: user.password_hash, user.password_salt = authenticate.create_password( new_password) if len(errors) == 0: success = True except IntegrityError as e: errors['email'] = [ "A user with that email address already exists" ] else: # Invalid forms-- have to re-query the old email address to prepopulate it errors = form.errors email = self._get_current_user_email() return self.render("settings.html", email=email, errors=errors, update_success=success)
def test_nested_queries(self): with Query(self.db) as outer: with Query(self.db) as inner: inner.add( Account(username="******", email_address="b", password_hash=b"c", password_salt=b"d")) acct = outer.query(Account).one() self.assertEqual("a", acct.username)
def test_query_working(self): with Query(self.db) as q: q.add( Account(username="******", email_address="b", password_hash=b"c", password_salt=b"d")) with Query(self.db) as q: acct = q.query(Account).one() self.assertEqual("a", acct.username)
def test_mismatch_new_passwords(self): body = urllib.parse.urlencode({ "current_password": "******", "new_password": "******", "new_password_confirm": "orange", "new_email": "*****@*****.**" }) with patch('classrank.handlers.BaseHandler.get_current_user' ) as get_current_user: get_current_user.return_value = b'"andrew"' response = self.fetch("/settings", method="POST", body=body) self.assertEqual(response.code, 200) self.assertIn(b"Passwords did not match", response.body) with Query(self._app.db) as q: user = q.query( self._app.db.account).filter_by(username="******").one() self.assertEqual( user.password_hash, authenticate.hash_pw("password", user.password_salt)) # Did not update.. Even though entering new email wasn't erroneous, either ALL # updates should go through or NONE. Since new passwords didn't match, NO # updates should go through, so don't update email. self.assertEqual(user.email_address, "*****@*****.**")
def test_success(self): """Test success condition of rating.""" self.register() self.login() body = urllib.parse.urlencode({"course": "CS 4641", "section": "A", "semester": "spring", "year": "2016", "rating": "5"}) with patch(get_current_user_func_path) as auth: auth.return_value = b'"tester"' response = self.fetch("/rate", method="POST", body=body) self.assertEqual(self.fetch("/rate").body, response.body) with Query(self._app.db) as q: rating = q.query(self._app.db.rating).one() self.assertEqual(5, rating.rating) self.assertEqual("spring", rating.section.semester) print("\n\n\n\n\n", type(rating.section.year)) self.assertEqual(2016, rating.section.year) self.assertEqual(1, rating.section.crn) self.assertEqual(1, rating.section.course_id) self.assertEqual("A", rating.section.name) self.assertEqual(1, rating.student.user_id)
def get_app(self): self.settings = { "static_path": path.join(path.dirname(__file__), static_path), "template_path": path.join(path.dirname(__file__), template_path), "logged_in_pages": {}, "logged_out_pages": {}, "cookie_secret": test_cookie_secret, "login_url": "/login", "grouch_results": "test/empty.json.example" } cr = ClassRankApp(None, routes, **self.settings) with Query(cr.db) as q: q.add(cr.db.school(**{"name": "Georgia Institute of Technology", "abbreviation": "gatech"})) q.add(cr.db.course(**{"school_id": 1, "name": "Machine Learning", "subject": "CS", "number": "4641"})) q.add(cr.db.section(**{"course_id": 1, "semester": "spring", "year": "2016", "name": "A", "crn": 1})) return cr
def test_filter_query(self): with self.assertRaises(ValueError): cf = CollaborativeFilter(db=self.conn) with Query(self.conn) as q: q.add(self.conn.rating(student=self.student2, section=self.section3, rating=4)) cf = CollaborativeFilter(db=self.conn) self.assertIsInstance(cf.getData(), type([]))
def test_cleanup(self): with self.assertRaises(TypeError): with Query(self.db) as q: with patch.object(q.session, 'rollback', return_value=5) as mock_rollback: raise TypeError self.assertEqual(mock_rollback.called, True)
def add_filters(self): # attrs = set(x for x in dir(self.db.rating) if not x.startswith("_")) # attrs -= set(["metadata", "section_id", "student", "section", "student_id"]) # return {attr: CollaborativeFilter(db=self.db, metric=attr) for attr in attrs} with Query(self.db)as q: num = max(1, int(q.query(self.db.student).count() / 5)) return {'rating': CollaborativeFilter(db=self.db, metric='rating', numRecommendations=num)}
def _get_current_user_email(self): current_user = self.decoded_username() with Query(self.db) as q: user = q.query( self.db.account).filter_by(username=current_user).one() return user.email_address
def get_app(self): self.settings = { "logged_in_pages": {}, "logged_out_pages": {}, "static_path": os.path.join(os.path.dirname(__file__), "../classrank/static"), "template_path": os.path.join(os.path.dirname(__file__), "../classrank/templates"), "cookie_secret": test_cookie_secret, "login_url": "/login", "grouch_results": "test/empty.json.example" } cr = ClassRankApp(None, routes, **self.settings) with Query(cr.db) as q: q.add( cr.db.school(**{ "name": "Georgia Test University", "abbreviation": "test" })) phash, psalt = authenticate.create_password("password") q.add( cr.db.account(username="******", email_address="*****@*****.**", password_hash=phash, password_salt=psalt)) return cr
def post(self): """Method that processes information placed into the rate form. Results are validated through the 'RateForm' object. If they are validated, check the database to see if the given course and section exist. If so, put it in. Else, fail. :returns: redirect to '/rate' regardless of success or failure """ form = RateForm(self.request.arguments) if form.validate(): try: # store argument data subject, number = self.get_argument('course').split(" ") section = self.get_argument('section') semester = self.get_argument('semester') rating = self.get_argument('rating') year = self.get_argument('year') cur_user = self.__decoded_username() print(subject, number, section, semester, rating, year, cur_user) with Query(self.db) as q: # get queries for all concerned tables course_q = q.query(Course) section_q = q.query(Section) account_q = q.query(Account) # get needed course, section, and account info from db # calling 'one()' verifies that one match exists course = course_q.filter_by(subject=subject, number=number).one() section = section_q.filter_by(course_id=course.uid, name=section, semester=semester, year=year).one() account = account_q.filter_by(username=cur_user).one() # generate rating object/row rating = self.db.rating(student_id=account.student.uid, section_id=section.uid, rating=rating, section=section, student=account.student) # add rating to db q.add(rating) return self.render("rate.html", error=False) except Exception as e: print(e) return self.render("rate.html", error=True) else: return self.render("rate.html", error=True)
def test_register_post_success(self): body = urllib.parse.urlencode({ "email": "*****@*****.**", "school": "test", "username": "******", "password": "******", "password_confirm": "password" }) response = self.fetch("/register", method="POST", body=body) self.assertEqual(self.fetch("/login").body, response.body) with Query(self._app.db) as q: user = q.query(self._app.db.account).one() self.assertEqual("*****@*****.**", user.email_address)
def get(self): course = self.request.arguments['query'][0] or "" s, n = course.decode('utf-8').partition(" ")[::2] with Query(self.db) as q: courses = q.query(self.db.course.subject, self.db.course.number).filter( self.db.course.number.like(n + "%"), self.db.course.subject.like(s + "%")).all() return self.write({ "suggestions": sorted([course[0] + " " + course[1] for course in courses]), "query": course.decode('utf-8') })
def test_fail_section_name_not_found(self): """Test failure if rating a non-existent section (name not found).""" self.register() self.login() body = urllib.parse.urlencode({"course": "CS 4641", "section": "XXX", "semester": "spring", "year": "2016", "rating": "5"}) with patch(get_current_user_func_path) as auth: auth.return_value = b'"tester"' response = self.fetch("/rate", method="POST", body=body) self.assertIn(b"There was an error adding your rating.", response.body) with Query(self._app.db) as q: rating = q.query(self._app.db.rating).all() self.assertEqual(len(rating), 0)
def get_app(self): self.settings = { "static_path": os.path.join(os.path.dirname(__file__), "../classrank/static"), "template_path": os.path.join(os.path.dirname(__file__), "../classrank/templates"), "logged_in_pages": {}, "logged_out_pages": {}, "cookie_secret": test_cookie_secret, "login_url": "/login", "grouch_results": "test/empty.json.example" } cr = ClassRankApp(os.environ.get("CONNECTION", "sqlite://"), routes, **self.settings) with Query(cr.db) as q: q.add(cr.db.school(**{"name":"Georgia Test University", "abbreviation": "test"})) q.add(cr.db.course(**{"school_id": 1, "name": "CS-4641", "description": "Machine learning", "number": "4641", "subject": "CS"})) return cr
def add_to_database(grouch_output, db): """ Add courses from Grouch's output to a db. Keyword arguments: grouch_output -- the output of Grouch (the scraped info) db -- the db to add to """ print("Beginning Grouch parse ({}).".format(datetime.datetime.now())) all_courses = parse(grouch_output) print("Ending Grouch parse ({}).".format(datetime.datetime.now())) if len(all_courses) != 0: print("Beginning database add ({}).".format(datetime.datetime.now())) with Query(db) as q: school_dict = {"name": "Georgia Institute of Technology", "abbreviation": "gatech"} if not _school_in_database(school_dict, db, q): q.add(db.school(**school_dict)) school_id = q.query(db.school).filter_by(**school_dict).one().uid for course, sections in all_courses: course_dict = {"school_id": school_id, "name": course['name'], "description": course['fullname'], "number": course['number'], "subject": course['school']} if not _course_in_database(course_dict, db, q): q.add(db.course(**course_dict)) course_id = q.query(db.course).filter_by(**course_dict).one().uid for section in sections: section_dict = {"course_id": course_id, "semester": course['semester'], "year": course['year'], "name": section['section_id'], "crn": section['crn']} q.add(db.section(**section_dict)) print("Ending database add ({}).".format(datetime.datetime.now()))
def get(self): page_data = {"error": False, "data":{}} user = self.decoded_username() data = dict() try: filters = self.add_filters() with Query(self.db) as q: student = q.query(self.db.account).filter_by(username=user).one().student courses = set(c[0] for c in q.query(self.db.course.name).all()) - set([c.name for c in student.courses]) student_id = student.uid ratings = filters['rating'].getRecommendation({student_id: courses})[student_id] page_data['data']['rating'] = sorted([(k, v) for k, v in ratings.items()], key=lambda x: x[1] or 0)[::-1] self.render('suggestion.html', **page_data) except Exception as e: print(e) page_data['error'] = True self.render("suggestion.html", **page_data)
def create_example_user(self): """ Add's a user to the database and creates corresponding login headers :return: the HTTP headers corresponding to the user's data """ body = urllib.parse.urlencode({ "email": "*****@*****.**", "password": "******" }) user = self._app.db.account(username="******", email_address="*****@*****.**", password_hash=b"secret", password_salt=b"salt") with Query(self._app.db) as db: db.add(user) return body
def post(self): errors = dict() form = LoginForm(self.request.arguments) if form.validate(): try: with Query(self.db) as q: user = q.query(self.db.account).filter_by( email_address=self.get_argument('email')).one() h = user.pw_hash s = user.pw_salt if authenticate.hash_pw(self.get_argument('password'), s) == h: self.authorize(user.username) return self.redirect('/dashboard') else: errors['password'] = ["Incorrect password"] except NoResultFound: errors['email'] = ["No user exists with that email address"] else: errors = form.errors return self.render("login.html", errors=errors)
def queryDB(self): with Query(self.db) as query: for student in query.query(self.db.student).filter( self.db.school.abbreviation == self.school).all(): results = query.query(self.db.rating, self.db.section).filter(self.db.rating.student_id == student.uid).\ filter(self.db.rating.section_id==self.db.section.uid).all() #a tuple of lists #results = list(zip(*results)) #a list of tuples instance = {} for result in results: courseName = query.query(self.db.course).filter( self.db.course.uid == result[1].course_id).first() courseName = courseName.name rating = result[0].__getattribute__(self.metric) #if self.metric == "rating": # rating = result[0][0].rating #elif self.metric == "grade": # rating = result[0][0].grade #elif self.metric == "workload": # rating = result[0][0].workload #elif self.metric == "difficulty": # rating = result[0][0].difficulty instance[courseName] = rating self.dataDict[student.uid] = instance
def post(self): errors = dict() form = RegistrationForm(self.request.arguments) if form.validate(): h, s = authenticate.create_password(self.get_argument('password')) user = self.db.account(username=self.get_argument('username'), email_address=self.get_argument('email'), password_hash=h, password_salt=s) try: with Query(self.db) as q: q.add(user) q.add(self.db.student(account=user, school=q.query(self.db.school). filter_by(abbreviation=self.get_argument('school')).one())) except IntegrityError: errors['username'] = ["A user with that username or email address already exists, or invalid school"] except Exception as e: raise else: #on success return self.redirect('/login') else: errors = form.errors return self.render('register.html', errors=errors)
def test_update_email_only(self): body = urllib.parse.urlencode({ "current_password": "******", "new_password": "", "new_password_confirm": "", "new_email": "*****@*****.**" }) with patch('classrank.handlers.BaseHandler.get_current_user' ) as get_current_user: get_current_user.return_value = b'"andrew"' response = self.fetch("/settings", method="POST", body=body) self.assertEqual(response.code, 200) self.assertIn(b"Your information has been updated", response.body) with Query(self._app.db) as q: user = q.query( self._app.db.account).filter_by(username="******").one() self.assertEqual( user.password_hash, authenticate.hash_pw("password", user.password_salt)) self.assertEqual(user.email_address, "*****@*****.**")
def setUp(self): self.conn = Database(engine=os.environ.get("CONNECTION", "sqlite:///:memory:")) school = self.conn.school(name="Georgia Tech", abbreviation="gatech") course = self.conn.course(school=school, name="Intro Java", number="1331", subject="CS") course2 = self.conn.course(school=school, name="Stuff", number="1332", subject="CS") section1 = self.conn.section(course=course, semester="fall", year=2016, name="A1") section2 = self.conn.section(course=course, semester="fall", year=2016, name="A2") self.section3 = self.conn.section(course=course2, semester="spring",year=2015, name="A") account = self.conn.account(username="******", email_address="*****@*****.**", password_hash=b"t", password_salt=b"t") student = self.conn.student(account=account, school=school) account2 = self.conn.account(username="******", email_address="*****@*****.**", password_hash=b"t", password_salt=b"t") self.student2 = self.conn.student(account=account2, school=school) with Query(self.conn) as q: q.add(school) q.add(course) q.add(section1) q.add(section2) q.add(course2) q.add(self.section3) q.add(account) q.add(student) q.add(self.student2) q.add(self.conn.rating(student=student, section=section1, rating=5)) q.add(self.conn.rating(student=self.student2, section=section2, rating=3))
def test_query_throws(self): with self.assertRaises(IntegrityError): with Query(self.db) as q: q.add(Account())
if __name__ == "__main__": parser = parser() args = parser.parse_args() settings = { "static_path": os.path.join(os.path.dirname(__file__), "classrank/static"), "template_path": os.path.join(os.path.dirname(__file__), "classrank/templates") } try: with open(args.settings) as f: settings.update(json.loads(f.read())) except FileNotFoundError: # no additional settings file so we ignore pass settings['debug'] == args.debug db_config = settings['db_config'] del settings['db_config'] cr = ClassRankApp(args.connection, routes, **settings) try: with Query(cr.db) as q: for table in db_config: for item in db_config[table]: q.add(cr.db.__getattribute__(table)(**item)) except IntegrityError as e: print(e) cr.listen(args.port) tornado.ioloop.IOLoop.current().start()
def create_example_database(app): with Query(app.db) as q: # first a school s = app.db.school(**{ "name": "Georgia Test University", "abbreviation": "test" }) q.add(s) usernames = [ "A", "B", "Casey", "D", "E", "Fortnow", "G", "H", "I", "Josh", "K", "L", "Mitchell", "N", "Omojokun", "P", "Q", "R", "S", "T" ] courses = [ "CS 1301", "CS 1331", "CS 1332", "CS 2110", "CS 4641", "MATH 1552", "MATH 3406", "ENGL 1101", "ENGL 1102" ] students = [] teachers = [] classes = [] sections = [] with Query(app.db) as q: # now create some accounts and the users for username in usernames: a = app.db.account(username=username, email_address=username + "@gmail.com", password_hash=b'hash', password_salt=b'salt') q.add(a) if len(username) > 1: f = app.db.faculty(account=a, school=s) teachers.append(f) q.add(f) if username not in {"Fortnow", "Omojokun"}: p = app.db.student(account=a, school=s) students.append(p) q.add(p) print(len(students)) for course in courses: subj, _, num = course.partition(" ") cs = app.db.course(name=course, subject=subj, number=num, school=s) q.add(cs) classes.append(cs) if cs.subject == "CS": for i in range(classes.index(cs) + 1): char = string.ascii_uppercase[i] sec = app.db.section(course=cs, professor=teachers[i], name=char, semester="Spring", year=2016) sections.append(sec) q.add(sec) elif cs.subject == "MATH": sec = app.db.section(course=cs, professor=teachers[4], name="A", semester="Spring", year=2016) sections.append(sec) q.add(sec) else: sec = app.db.section(course=cs, name="A", semester="Spring", year=2016) sections.append(sec) q.add(sec) sec = app.db.section(course=cs, name="B", semester="Spring", year=2016) sections.append(sec) q.add(sec) for sec in cs.sections: q.add(app.db.rating(section=sec, student=students[0], rating=1)) # rates all courses one for i in range(1, 6): if cs.sections: q.add( app.db.rating(section=cs.sections[0], student=students[i], rating=i)) # each rates section 0 as n if cs.subject == "CS": if len(cs.sections) > 1: q.add( app.db.rating(section=cs.sections[1], student=students[7], rating=3)) if cs.subject == "ENGL": if cs.number == "1101": q.add( app.db.rating(section=cs.sections[0], student=students[8], rating=1)) q.add( app.db.rating(section=cs.sections[0], student=students[10], rating=5)) else: q.add( app.db.rating(section=cs.sections[1], student=students[9], rating=5)) q.add( app.db.rating(section=cs.sections[0], student=students[11], rating=1)) if cs.subject == "CS" and cs.number == "1301": q.add( app.db.rating(section=cs.sections[0], student=students[11], rating=1)) q.add( app.db.rating(section=cs.sections[0], student=students[12], rating=3)) q.add( app.db.rating(section=cs.sections[0], student=students[13], rating=5)) if cs.subject == "CS" and cs.number == "1331": q.add( app.db.rating(section=cs.sections[0], student=students[11], rating=1)) q.add( app.db.rating(section=cs.sections[0], student=students[12], rating=3)) q.add( app.db.rating(section=cs.sections[0], student=students[13], rating=5)) if cs.subject == "CS" and cs.number == "1332": q.add( app.db.rating(section=cs.sections[0], student=students[11], rating=1)) q.add( app.db.rating(section=cs.sections[0], student=students[12], rating=3))
def test_db_example(self): with Query(self._app.db) as q: q.session.delete(q.query(self._app.db.school).one()) create_example_database(self._app)
def test_fail_invalid_formdata_rating(self): """Tests for all types of invalid rating formdata coming in. These should be detected by the RateForm validator. The invalid types are: 01. rating too high (> 5), 02. rating too low (< 1), 03. rating not extant, 04. name too short (< 5), 05. name too long (> 30), 06. name not extant, 07. section name too short / not extant, 08. section name too long (> 4), 09. year too high (> current year) 10. year too low (< 1970) In each case, we want to go back to a blank 'rate' page. Finally, we then wish to assert that nothing was added to the database from these attempts. """ self.register() self.login() rate_body = self.fetch("/rate").body cur_year = datetime.datetime.now().year invalid_forms = [ {"name": "CS-4641", "section": "A", "semester": "spring", "rating": "6", "year": "2016"}, {"name": "CS-4641", "section": "A", "semester": "spring", "rating": "0", "year": "2016"}, {"name": "CS-4641", "section": "A", "semester": "spring", "rating": "", "year": "2016"}, {"name": "C", "section": "A", "semester": "spring", "rating": "3", "year": "2016"}, {"name": "CS-0123456789abcdefghijklmnopqr", "section": "A", "semester": "spring ", "rating": "3", "year": "2016"}, {"name": "", "section": "A", "semester": "spring", "rating": "3", "year": "2016"}, {"name": "CS-4641", "section": "", "semester": "spring", "rating": "3", "year": "2016"}, {"name": "CS-4641", "section": "A 123456789", "semester": "spring", "rating": "3", "year": "2016"}, {"name": "CS-4641", "section": "A", "semester": "spring", "rating": "3", "year": str(cur_year + 1)}, {"name": "CS-4641", "section": "A", "semester": "spring", "rating": "3", "year": "1969"} ] with patch(get_current_user_func_path) as auth: auth.return_value = b'"tester"' # iterate over all forms, returning to register page each time for form in invalid_forms: response = self.post_form(form) self.assertIn(b"There was an error adding your rating.", response.body) # assert that nothing was added to database from all attempts with Query(self._app.db) as q: rating = q.query(self._app.db.rating).all() self.assertEqual(0, len(rating))