def add_analytics_wishlist(type_of_wishlist, email, first_name, last_name, os_info, ip_addr): """ Helper method that takes in all parameters and inserts analytics in db. """ timestamp = get_current_time() # write calls to analytics for google signup analytics_to_add = { "type_of_call": "Wishlist", "type_of_wishlist": type_of_wishlist, "email": email, "first_name": first_name, "last_name": last_name, "timestamp": timestamp, "os_info": os_info, "ip_address": ip_addr, "location": get_location_info(ip_addr) } if not validate_analytics_auth(analytics_to_add): return False # add analytics to db grades_db.analytics_user_data.insert_one(analytics_to_add)
def reset_wishlist(): """ Method to reset the wishlist to what the user inputted after deleting selected items. """ #save data data = json.loads(request.get_data()) #decode token and save new wishlist data new_wishlist = data["wishlist"] user_data = jwt.decode(data["token"], SECRET, algorithms=["HS256"])["identity"] #use user_query as it removes wishlist and searches #for the correct user whose info was provided user_query = { "first_name": user_data["first_name"], "last_name": user_data["last_name"], "email": user_data["email"] } if not validate_analytics_auth(user_query): return json.dumps({"success": False}), 400, { "ContentType": "application/json" } #update db with new wishlist update_wishlist = grades_db.users.update_one( user_query, {"$set": { "wishlist": new_wishlist }}) return json.dumps({"success": True}), 200, { "ContentType": "application/json" }
def save_user_major(): """ Method to save a user's chosen major in the database for future use. """ #save token from headers token = request.headers["Token"] user_data = jwt.decode(token, SECRET, algorithms=['HS256'])["identity"] user_query = { "first_name": user_data["first_name"], "last_name": user_data["last_name"], "email": user_data["email"] } if not validate_analytics_auth(user_query): return json.dumps({"success": False}), 400, { "ContentType": "application/json" } #save new user major information major = request.headers["Major"] major_query = {"major": major} #update db for user with inputted major user_db_data = grades_db.users.update_one(user_query, {"$set": major_query}) return json.dumps({"success": True}), 200, { "ContentType": "application/json" }
def view_wishlist(): """ Method to display the wishlist for a particular user whose token is sent by the frontend. """ token = request.headers["Token"] ip_addr = request.access_route[0] # get user agent user_agent = str(request.headers.get("User-Agent")) try: os_info = user_agent.split(')')[0].split('(')[1].strip() except: os_info = "Unknown" user_data = jwt.decode(token, SECRET, algorithms=['HS256'])["identity"] user_query = { "first_name": user_data["first_name"], "last_name": user_data["last_name"], "email": user_data["email"] } if not validate_analytics_auth(user_query): return json.dumps({"success": False}), 400, { "ContentType": "application/json" } #find wishlist for user user_db_data = grades_db.users.find_one(user_query) #collect wishlist from data wishlist = user_db_data["wishlist"] #go through each term and ensure that #it has courses for term in list(wishlist): #if term is empty, remove it from the items #to be rendered to the user. if len(wishlist[term]) == 0: del wishlist[term] # write calls to analytics for google signup add_analytics_wishlist("View", user_data["email"], user_data["first_name"], user_data["last_name"], os_info, ip_addr) #print(wishlist) return json.dumps(wishlist), 200, {'ContentType': 'application/json'}
def check_user_major(): """ Method to check whether a given user in the DB has selected a major. If not, it returns false and the frontend prompts the user to select a major. If so, it returns true and the frontend gives back information for the major to the user. """ #save token from headers token = request.headers["Token"] user_data = jwt.decode(token, SECRET, algorithms=['HS256'])["identity"] user_query = { "first_name": user_data["first_name"], "last_name": user_data["last_name"], "email": user_data["email"] } if not validate_analytics_auth(user_query): return json.dumps({"success":False}),400,{"ContentType":"application/json"} #find wishlist for user user_db_data = grades_db.users.find_one( user_query ) #check for major try: #save major name user_major = user_db_data["major"] #save major query major_query = { "major": user_major } #save major courses major_courses = grades_db.majordatancsu.find_one(major_query)["courses"] return_json = { "major": user_major, "courses": major_courses, "success": True } #return info on user major and courses return json.dumps(return_json), 200, {'ContentType':'application/json'} #if the user does not have a major except: return json.dumps({"success": False}), 500, {"ContentType":"application/json"}
def add_course_to_wishlist(): """ Method to process input from frontend containing course to add to wishlist and which user the course is for. """ #save data data = json.loads(request.get_data()) ip_addr = request.access_route[0] # get user agent user_agent = str(request.headers.get("User-Agent")) try: os_info = user_agent.split(')')[0].split('(')[1].strip() except: os_info = "Unknown" #decode token and save course to add to wishlist wishlist_course_data = data["course_data"] user_data = jwt.decode(data["token"], SECRET, algorithms=['HS256'])["identity"] user_query = { "first_name": user_data["first_name"], "last_name": user_data["last_name"], "email": user_data["email"] } if not validate_analytics_auth(user_query): return json.dumps({"success": False}), 400, { "ContentType": "application/json" } #current wishlist current_wishlist = grades_db.users.find_one(user_query)["wishlist"] #check for duplicates in wishlist for wishlist_term in current_wishlist.keys(): for wishlist_item in current_wishlist[wishlist_term]: if wishlist_item == wishlist_course_data: return json.dumps({ "success": False, "duplicate": True }), 404, { "ContentType": "application/json" } #term_to_update term_to_update = wishlist_course_data["Semester"] try: current_wishlist[term_to_update].append(wishlist_course_data) except: current_wishlist[term_to_update] = [wishlist_course_data] #update db with wishlist course add_wishlist = grades_db.users.update_one( user_query, {"$set": { "wishlist": current_wishlist }}, upsert=True) # write calls to analytics for google signup add_analytics_wishlist("Add", user_data["email"], user_data["first_name"], user_data["last_name"], os_info, ip_addr) #return true if successful return json.dumps({'success': True}), 200, { 'ContentType': 'application/json' }
def google_auth(): """ Method to handle user signing in via Google. This method will either connect the user to their account in the database, or create a new account for them. """ google_user_data = json.loads(request.get_data()) try: token = google_user_data['token'] idinfo = {} # Specify the CLIENT_ID of the app that accesses the backend: for client_id in CLIENT_IDS: try: idinfo = id_token.verify_token(token, requests.Request(), client_id) break except: pass # save information based on token user_email = idinfo['email'] user_first = idinfo['given_name'] user_last = idinfo['family_name'] except: return 'Invalid token', 400 user_query = { "email": google_user_data["email"] } # check that the user email is equivalent to the token if user_query['email'] != user_email: return 'Account does not match token', 400 ip_addr = request.access_route[0] # get user agent user_agent = str(request.headers.get("User-Agent")) try: os_info = user_agent.split(')')[0].split('(')[1].strip() except: os_info = "Unknown" # protects against noSQL injection if not is_clean_email(user_query['email']): #return that user made invalid email return json.dumps({"success":False, "message": "email was not clean."}), 400, {"ContentType":"application/json"} #attempt to find user with information given google_user = grades_db.users.find_one(user_query) if google_user == None: """ Options: - here we can add a user to the database with no password, and thus no one can access their account unless they sign in with their Google account - alternatively, we could redirect a user to a signup page on the frontend where they can enter a password """ #save user information user = { "first_name": google_user_data["first_name"], "last_name": "", "email": google_user_data["email"], "hashed_pw":"", "wishlist": {}, "os_info": [os_info], "ip_address_list": [ip_addr], "timestamp": get_current_time(), "location": get_location_info(ip_addr), "account_type": "Google" } try: user["last_name"] = google_user_data["last_name"] except: pass # check that user info is valid if not validate_analytics_auth(user): return json.dumps({"success":False}), 400, {"ContentType":"application/json"} #add user to db grades_db.users.insert_one(user) #send the user an email send_signup_email(google_user_data["first_name"], google_user_data["email"]) #try to find existing user current_user = grades_db.users.find_one(user) #get user info user_info = get_user_token(current_user) user_info["type"] = "SignUp" # write calls to analytics for google signup analytics = add_analytics_auth("SignUp", "Google", google_user_data["email"], google_user_data["first_name"], google_user_data["last_name"], os_info, ip_addr) # if analytics was malicious, return false if not analytics: return json.dumps({"success":False}), 400, {"ContentType":'application/json'} #return that user was signed up return json.dumps(user_info), 200, {"ContentType":"application/json"} else: """ Send user to their homepage """ user_info = get_user_token(google_user) # ensure that os and ip are clean if not is_clean_query(os_info) or not is_clean_query(ip_addr): return json.dumps({"success":False, "message": "IP/OS was not clean."}), 400, {"ContentType":"application/json"} # update os and ip address in db update_os_ip(google_user, os_info, ip_addr) # write calls to analytics for google signup analytics = add_analytics_auth("Login", "Google", google_user_data["email"], google_user_data["first_name"], google_user_data["last_name"], os_info, ip_addr) # if analytics was malicious, return false if not analytics: return json.dumps({"success":False, "message": "Analytics was not clean."}), 400, {"ContentType":'application/json'} return json.dumps(user_info), 200, {'ContentType':'application/json'} return json.dumps({'success':False}), 404, {"ContentType":"application/json"}
def sign_up(): """ Method to enter new user information in the database and hash authenticated password. """ #get user data user_data = json.loads(request.get_data()) ip_addr = request.access_route[0] # get user agent user_agent = str(request.headers.get("User-Agent")) try: os_info = user_agent.split(')')[0].split('(')[1].strip() except: os_info = "Unknown" #use hash library to hash the password # generate new salt, and hash a password custom_algo = pbkdf2_sha256.using(rounds=NUM_ROUNDS) hashed_pw = custom_algo.hash(user_data["password"].encode("utf-8")) # remove dollar sign and save indices dollar_sign_indices = [i for i,j in enumerate(list(hashed_pw)) if j=='$'] hashed_pw = hashed_pw.replace('$', '') #save user information user = { "first_name":user_data["first_name"], "last_name":user_data["last_name"], "email":user_data["email"], "hashed_pw":hashed_pw, "wishlist": {}, "os_info": [os_info], "ip_address_list": [ip_addr], "timestamp": get_current_time(), "location": get_location_info(ip_addr), "account_type": "PackRanks" } # check that user info is valid if not validate_analytics_auth(user): return json.dumps({"success":False}), 400, {"ContentType":"application/json"} #try to find existing user current_user = grades_db.users.find_one({"email":user["email"]}) if current_user != None: return json.dumps({'success':False}), 404, {"ContentType":"application/json"} #if no existing user, add to db else: # save dollar sign indices and save in db user['dollar_sign_indices'] = dollar_sign_indices grades_db.users.insert_one(user) #send user confirmation email! first_name = user["first_name"] last_name = user["last_name"] email = user["email"] #send an email to user who signed up send_signup_email(first_name, email) # write calls to analytics analytics = add_analytics_auth("SignUp", "PackRanks", user_data["email"], user_data["first_name"], user_data["last_name"], os_info, ip_addr) # if analytics was malicious, return false if not analytics: return json.dumps({"success":False}), 400, {"ContentType":'application/json'} return json.dumps({'success':True}), 200, {'ContentType':'application/json'}
def upvote_course(): """ Method that attempts to upvote course for given user. """ #save data data = eval(request.get_data()) ip_addr = request.access_route[0] # get user agent user_agent = str(request.headers.get("User-Agent")) try: os_info = user_agent.split(')')[0].split('(')[1].strip() except: os_info = "Unknown" #decode token and save course to add to wishlist course_data = data["course_data"] user_data = jwt.decode(data["token"], SECRET, algorithms=['HS256'])["identity"] user_query = { "first_name": user_data["first_name"], "last_name": user_data["last_name"], "email": user_data["email"] } if not validate_analytics_auth(user_query): return json.dumps({"success": False}), 400, { "ContentType": "application/json" } # save information to uniquely identify course course_query = { "course_name": course_data['Catalog Link'][0], "semester": course_data['Semester'], "section": course_data['Section'], "professor": course_data['RateMyProfessor Link'][0] } # get user information on whether they have upvoted this course user = grades_db.users.find_one(user_query) user_has_upvoted = False # get current upvotes try: current_upvotes = grades_db.catalogncsu.find_one( course_query)['user_upvotes'] except: current_upvotes = 0 try: user_upvoted_courses = user['user_upvoted_courses'] except: user_upvoted_courses = [] for course in user_upvoted_courses: course_info = { "course_name": course_data['Catalog Link'][0], "semester": course_data['Semester'], "section": course_data['Section'], "professor": course_data['RateMyProfessor Link'][0] } if course_info == course_query: user_has_upvoted = True break # add analytics for the upvote analytics = add_analytics_upvote(course_query, user_data['email'], user_data['first_name'], user_data['last_name'], os_info, ip_addr) if not analytics: return json.dumps({"success": False}), 400, { "ContentType": "application/json" } if not user_has_upvoted: # add course to list of courses user has upvoted grades_db.users.update_one( user_query, {"$push": { "user_upvoted_courses": course_data }}) # upvote course grades_db.catalogncsu.update_one( course_query, {"$set": { "user_upvotes": current_upvotes + 1 }}) else: return json.dumps({"success": False}), 200, { "ContentType": "application/json" } return json.dumps({"success": True}), 200, { "ContentType": "application/json" }
def deptRoute(): """ This function receives a call from the Dept component of the ReactJS and returns a JSON of the top 5 courses from the database based on the metrics calculated. It takes into account the level, department, and term that the course is offered in. """ # set valid coures types VALID_COURSE_TYPES = ["Lecture", "Problem Session"] #access MongoDb database crowdsourced = MongoClient(DBSTR) grades_db = crowdsourced.Coursesnc #get which GEP was requested #print(request.headers) dept_requested = request.headers.get("Dept") term_requested = request.headers.get("term") level_min = request.headers.get("level_min") level_max = request.headers.get("level_max") num_to_show = int(request.headers.get("num_courses")) ip_addr = request.access_route[0] loc_info = get_location_info(ip_addr) # get user agent user_agent = str(request.headers.get("User-Agent")) try: os_info = user_agent.split(')')[0].split('(')[1].strip() except: os_info = "Unknown" # write calls to analytics analytics_to_add = { "type_of_call": "Dept", "dept_requested": dept_requested, "term_requested": term_requested, "level_min": level_min, "level_max": level_max, "num_courses": num_to_show, "timestamp": get_current_time(), "os_info": os_info, "ip_address": ip_addr, "location": loc_info } # check that analytics is clean if not validate_analytics_auth(analytics_to_add): return json.dumps({"success": False}), 400, { "ContentType": "application/json" } # add analytics to db grades_db.analytics_user_data.insert_one(analytics_to_add) if (not str.isnumeric(level_min) and level_min != "ANY") or (not str.isnumeric(level_max) and level_max != "ANY"): return ["NotNumeric"] if (level_min != "ANY" and level_max != "ANY"): level_l = [int(level_min), int(level_max)] if level_l[1] < level_l[0]: return ["Min>Max"] if len([i for i in level_l if i < 100 or i > 899 and i != "ANY"]) > 0: return ["NotNumeric"] # check if health if "HES" in dept_requested: course_types = ["Lecture", "Problem Session"] # temporary fix, still need to figure out which depts # should not show problem sessions elif 'ACC' in dept_requested: course_types = ["Lecture"] else: course_types = VALID_COURSE_TYPES course_type_search = '|'.join(course_types) # convert level min and max to valid if level_min == 'ANY': level_min = 100 if level_max == 'ANY': level_max = 899 level_min = int(level_min) level_max = int(level_max) #access collection with the correct data catalog_data = grades_db.catalogncsu.aggregate([ { "$match": { "department": dept_requested, "course_number": { "$gte": level_min, "$lte": level_max }, "course_type": { "$regex": course_type_search }, "semester": { "$regex": term_requested }, "professor": { "$ne": "Staff" } } }, #group by professor and add unique sections and raw_official_scores #to aggregation { "$group": { "_id": { "course_name": "$course_name", "professor": "$professor", "semester": "$semester" }, "section": { "$addToSet": "$section" }, "raw_official_score": { "$addToSet": "$raw_official_score" } } }, { "$unwind": "$raw_official_score" }, { "$sort": { "raw_official_score": -1 } }, { "$limit": NUM_COURSES } ]) #save data to dictionary relevant_data = save_course_data(catalog_data, num_to_show) #del catalog_data["_id"] if len(relevant_data) == 0: return [] return relevant_data
def gepRoute(): """ This function receives a call from the GEP component of the ReactJS and returns a JSON of the top 5 courses from the database based on the metrics calculated. """ #access MongoDb database crowdsourced = MongoClient(DBSTR) grades_db = crowdsourced.Coursesnc #get which GEP was requested gep_requested = request.headers.get("GEP") term_requested = request.headers.get("term") num_to_show = int(request.headers.get("num_courses")) ip_addr = request.access_route[0] loc_info = get_location_info(ip_addr) # get user agent user_agent = str(request.headers.get("User-Agent")) try: os_info = user_agent.split(')')[0].split('(')[1].strip() except: os_info = "Unknown" # write calls to analytics analytics_to_add = { "type_of_call": "GEP", "gep_requested": gep_requested, "term_requested": term_requested, "num_courses": num_to_show, "timestamp": get_current_time(), "os_info": os_info, "ip_address": ip_addr, "location": loc_info } # check that analytics is clean if not validate_analytics_auth(analytics_to_add): return json.dumps({"success": False}), 400, { "ContentType": "application/json" } # add analytics to db grades_db.analytics_user_data.insert_one(analytics_to_add) course_types = ['Lecture'] if gep_requested == "HES": course_types.append("Problem Session") course_type_s = '|'.join(course_types) #if additional breadth, either hum or ss if gep_requested == "ADDTL": #print("ADDLT") geps_req = ["HUM", "SS"] geps_regex = '|'.join(geps_req) else: geps_req = [gep_requested] geps_regex = "|".join(geps_req) #access collection with the correct data catalog_data = grades_db.catalogncsu.aggregate([ { "$match": { "gep": { "$regex": geps_regex }, "course_type": { "$regex": course_type_s }, "semester": { "$regex": term_requested }, "professor": { "$ne": "Staff" } } }, #group by professor and add unique sections and raw_official_scores #to aggregation { "$group": { "_id": { "course_name": "$course_name", "professor": "$professor", "semester": "$semester" }, "section": { "$addToSet": "$section" }, "raw_official_score": { "$addToSet": "$raw_official_score" } } }, { "$unwind": "$raw_official_score" }, { "$sort": { "raw_official_score": -1 } }, { "$limit": NUM_COURSES } ]) relevant_data = save_course_data(catalog_data, num_to_show) return relevant_data