def next_course_to_review(): current_user = view_helpers.get_current_user() uc = current_user.next_course_to_review() if current_user else None if not uc: return util.json_dumps({}) uc.select_for_review(current_user) return util.json_dumps(uc.to_dict())
def schedule_screenshot_url(): user = view_helpers.get_current_user() return util.json_dumps({ # Note that this may be None "url": schedule_screenshot.get_screenshot_url(user) })
def user_course_share(): user_course_id = flask.request.form['user_course_id'] review_type = flask.request.form['review_type'] current_user = view_helpers.get_current_user() review = None points_gained = 0 user_course = m.UserCourse.objects.get( id=user_course_id, user_id=current_user.id) if review_type == 'course': review = user_course.course_review points_gained = m.PointSource.SHARE_COURSE_REVIEW elif review_type == 'professor': review = user_course.professor_review points_gained = m.PointSource.SHARE_PROFESSOR_REVIEW # Only award points on the first share if not review.share_date: redis = view_helpers.get_redis_instance() current_user.award_points(points_gained, redis) else: points_gained = 0 review.share_date = datetime.now() user_course.save() current_user.save() return util.json_dumps({ 'points_gained': points_gained, })
def search_courses(): current_user = view_helpers.get_current_user() courses, has_more = m.Course.search(flask.request.values, current_user) course_dict_list, user_course_dict_list, user_course_list = ( m.Course.get_course_and_user_course_dicts( courses, current_user, include_friends=True, full_user_courses=False, include_sections=True)) professor_dict_list = m.Professor.get_reduced_professors_for_courses( courses) user_dict_list = [] if current_user: user_ids = [uc['user_id'] for uc in user_course_dict_list if uc['user_id'] != current_user.id] users = m.User.objects(id__in=user_ids).only(*m.User.CORE_FIELDS) user_dict_list = [u.to_dict() for u in users] return util.json_dumps({ 'user_objs': user_dict_list, 'course_objs': course_dict_list, 'professor_objs': professor_dict_list, 'user_course_objs': user_course_dict_list, 'has_more': has_more, })
def user_course_share(): user_course_id = flask.request.form['user_course_id'] review_type = flask.request.form['review_type'] current_user = view_helpers.get_current_user() review = None points_gained = 0 user_course = m.UserCourse.objects.get(id=user_course_id, user_id=current_user.id) if review_type == 'course': review = user_course.course_review points_gained = m.PointSource.SHARE_COURSE_REVIEW elif review_type == 'professor': review = user_course.professor_review points_gained = m.PointSource.SHARE_PROFESSOR_REVIEW # Only award points on the first share if not review.share_date: redis = view_helpers.get_redis_instance() current_user.award_points(points_gained, redis) else: points_gained = 0 review.share_date = datetime.now() user_course.save() current_user.save() return util.json_dumps({ 'points_gained': points_gained, })
def search_courses(): current_user = view_helpers.get_current_user() courses, has_more = m.Course.search(flask.request.values, current_user) course_dict_list, user_course_dict_list, user_course_list = ( m.Course.get_course_and_user_course_dicts(courses, current_user, include_friends=True, full_user_courses=False, include_sections=True)) professor_dict_list = m.Professor.get_reduced_professors_for_courses( courses) user_dict_list = [] if current_user: user_ids = [ uc['user_id'] for uc in user_course_dict_list if uc['user_id'] != current_user.id ] users = m.User.objects(id__in=user_ids).only(*m.User.CORE_FIELDS) user_dict_list = [u.to_dict() for u in users] return util.json_dumps({ 'user_objs': user_dict_list, 'course_objs': course_dict_list, 'professor_objs': professor_dict_list, 'user_course_objs': user_course_dict_list, 'has_more': has_more, })
def get_courses(course_ids): course_ids = [c.lower() for c in course_ids.split(',')] courses = m.Course.objects(id__in=course_ids, ) # TODO(mack): not currently being called, fix it when it is needed # course_objs = map(clean_course, courses) course_objs = [] professor_objs = m.Professor.get_reduced_professor_for_courses(courses) return util.json_dumps({ 'course_objs': course_objs, 'professor_objs': professor_objs, })
def invite_friend(): current_user = view_helpers.get_current_user() orig_points = current_user.num_points current_user.invite_friend(view_helpers.get_redis_instance()) current_user.save() points_gained = current_user.num_points - orig_points return util.json_dumps({ 'num_invites': current_user.num_invites, 'points_gained': points_gained, })
def last_schedule_paste(): user_id = flask.request.values.get('user_id') if not user_id: user_id = view_helpers.get_current_user().id else: user_id = bson.ObjectId(user_id) user = m.User.objects.with_id(user_id) last_schedule_paste = user.last_schedule_paste return util.json_dumps({ 'last_schedule_paste': last_schedule_paste, })
def add_course_to_shortlist(): current_user = view_helpers.get_current_user() user_course = m.UserCourse( user_id=current_user.id, course_id=flask.request.form.get('course_id'), term_id=m.Term.SHORTLIST_TERM_ID, ) user_course.save() current_user.update(add_to_set__course_history=user_course.id) return util.json_dumps({ 'user_course': user_course.to_dict(), })
def get_courses(course_ids): course_ids = [c.lower() for c in course_ids.split(',')] courses = m.Course.objects( id__in=course_ids, ) # TODO(mack): not currently being called, fix it when it is needed # course_objs = map(clean_course, courses) course_objs = [] professor_objs = m.Professor.get_reduced_professor_for_courses(courses) return util.json_dumps({ 'course_objs': course_objs, 'professor_objs': professor_objs, })
def pasted_schedule_users(): include_good_paste = bool(flask.request.values.get('include_good_paste')) include_bad_paste = bool(flask.request.values.get('include_bad_paste')) # Start off with a query that maches no one query = me.Q(id__exists=False) if include_good_paste: query = query | me.Q(last_good_schedule_paste__exists=True) if include_bad_paste: query = query | me.Q(last_bad_schedule_paste__exists=True) users = m.User.objects.filter(query).only('id') user_ids = [user.id for user in users] print 'num_users', len(user_ids) return util.json_dumps({ 'user_ids': user_ids, })
def test_json_dumps_prevents_xss(self): self.assertEquals('["<\\/script>"]', util.json_dumps(["</script>"]))
def user_course(): uc_data = util.json_loads(flask.request.data) user = view_helpers.get_current_user() rmclogger.log_event( rmclogger.LOG_CATEGORY_API, rmclogger.LOG_EVENT_USER_COURSE, { 'uc_data': uc_data, 'user_id': user.id, }, ) # Validate request object course_id = uc_data.get('course_id') term_id = uc_data.get('term_id') if course_id is None or term_id is None: logging.error("/api/user/course got course_id (%s) and term_id (%s)" % (course_id, term_id)) # TODO(david): Perhaps we should have a request error function that # returns a 400 raise exceptions.ImATeapot('No course_id or term_id set') if not m.UserCourse.can_review(term_id): logging.warning("%s attempted to rate %s in future/shortlist term %s" % (user.id, course_id, term_id)) raise exceptions.ImATeapot( "Can't review a course in the future or shortlist") # Fetch existing UserCourse uc = m.UserCourse.objects( user_id=user.id, course_id=uc_data['course_id'], term_id=uc_data['term_id'] ).first() if uc is None: logging.error("/api/user/course User course not found for " "user_id=%s course_id=%s term_id=%s" % (user.id, course_id, term_id)) # TODO(david): Perhaps we should have a request error function that # returns a 400 raise exceptions.ImATeapot('No user course found') orig_points = uc.num_points # TODO(Sandy): Consider the case where the user picked a professor and # rates them, but then changes the professor. We need to remove the ratings # from the old prof's aggregated ratings and add them to the new prof's # Maybe create professor if newly added if uc_data.get('new_prof_added'): new_prof_name = uc_data['new_prof_added'] # TODO(mack): should do guess_names first, and use that to # generate the id prof_id = m.Professor.get_id_from_name(new_prof_name) uc.professor_id = prof_id # TODO(Sandy): Have some kind of sanity check for professor names. # Don't allow ridiculousness like "Santa Claus", "aksnlf", # "swear words" if m.Professor.objects(id=prof_id).count() == 0: first_name, last_name = m.Professor.guess_names(new_prof_name) m.Professor( id=prof_id, first_name=first_name, last_name=last_name, ).save() course = m.Course.objects.with_id(uc.course_id) course.professor_ids = list(set(course.professor_ids) | {prof_id}) course.save() logging.info("Added new course professor %s (name: %s)" % (prof_id, new_prof_name)) elif uc_data.get('professor_id'): uc.professor_id = uc_data['professor_id'] else: uc.professor_id = None now = datetime.now() if uc_data.get('course_review'): # New course review data uc_data['course_review']['comment_date'] = now uc.course_review.update(**uc_data['course_review']) if uc_data.get('professor_review'): # New prof review data uc_data['professor_review']['comment_date'] = now uc.professor_review.update(**uc_data['professor_review']) uc.save() points_gained = uc.num_points - orig_points user.award_points(points_gained, view_helpers.get_redis_instance()) user.save() return util.json_dumps({ 'professor_review.comment_date': uc['professor_review'][ 'comment_date'], 'course_review.comment_date': uc['course_review']['comment_date'], 'points_gained': points_gained, })
def user_course(): uc_data = util.json_loads(flask.request.data) user = view_helpers.get_current_user() rmclogger.log_event( rmclogger.LOG_CATEGORY_API, rmclogger.LOG_EVENT_USER_COURSE, { 'uc_data': uc_data, 'user_id': user.id, }, ) # Validate request object course_id = uc_data.get('course_id') term_id = uc_data.get('term_id') if course_id is None or term_id is None: logging.error("/api/user/course got course_id (%s) and term_id (%s)" % (course_id, term_id)) # TODO(david): Perhaps we should have a request error function that # returns a 400 raise exceptions.ImATeapot('No course_id or term_id set') if not m.UserCourse.can_review(term_id): logging.warning("%s attempted to rate %s in future/shortlist term %s" % (user.id, course_id, term_id)) raise exceptions.ImATeapot( "Can't review a course in the future or shortlist") # Fetch existing UserCourse uc = m.UserCourse.objects(user_id=user.id, course_id=uc_data['course_id'], term_id=uc_data['term_id']).first() if uc is None: logging.error("/api/user/course User course not found for " "user_id=%s course_id=%s term_id=%s" % (user.id, course_id, term_id)) # TODO(david): Perhaps we should have a request error function that # returns a 400 raise exceptions.ImATeapot('No user course found') orig_points = uc.num_points # TODO(Sandy): Consider the case where the user picked a professor and # rates them, but then changes the professor. We need to remove the ratings # from the old prof's aggregated ratings and add them to the new prof's # Maybe create professor if newly added if uc_data.get('new_prof_added'): new_prof_name = uc_data['new_prof_added'] # TODO(mack): should do guess_names first, and use that to # generate the id prof_id = m.Professor.get_id_from_name(new_prof_name) uc.professor_id = prof_id # TODO(Sandy): Have some kind of sanity check for professor names. # Don't allow ridiculousness like "Santa Claus", "aksnlf", # "swear words" if m.Professor.objects(id=prof_id).count() == 0: first_name, last_name = m.Professor.guess_names(new_prof_name) m.Professor( id=prof_id, first_name=first_name, last_name=last_name, ).save() course = m.Course.objects.with_id(uc.course_id) course.professor_ids = list(set(course.professor_ids) | {prof_id}) course.save() logging.info("Added new course professor %s (name: %s)" % (prof_id, new_prof_name)) elif uc_data.get('professor_id'): uc.professor_id = uc_data['professor_id'] else: uc.professor_id = None now = datetime.now() if uc_data.get('course_review'): # New course review data uc_data['course_review']['comment_date'] = now uc.course_review.update(**uc_data['course_review']) if uc_data.get('professor_review'): # New prof review data uc_data['professor_review']['comment_date'] = now uc.professor_review.update(**uc_data['professor_review']) uc.save() points_gained = uc.num_points - orig_points user.award_points(points_gained, view_helpers.get_redis_instance()) user.save() return util.json_dumps({ 'professor_review.comment_date': uc['professor_review']['comment_date'], 'course_review.comment_date': uc['course_review']['comment_date'], 'points_gained': points_gained, })
def tojson(obj): return util.json_dumps(obj)
def generic_stats(): return util.json_dumps(dashboard_data())
def search_courses(): # TODO(mack): create enum of sort options # num_friends, num_ratings, overall, interest, easiness request = flask.request keywords = request.values.get('keywords') sort_mode = request.values.get('sort_mode', 'popular') default_direction = COURSES_SORT_MODES_BY_NAME[sort_mode]['direction'] direction = int(request.values.get('direction', default_direction)) count = int(request.values.get('count', 10)) offset = int(request.values.get('offset', 0)) exclude_taken_courses = request.values.get('exclude_taken_courses') current_user = view_helpers.get_current_user() # TODO(david): These logging things should be done asynchronously rmclogger.log_event(rmclogger.LOG_CATEGORY_COURSE_SEARCH, rmclogger.LOG_EVENT_SEARCH_PARAMS, request.values) filters = {} if keywords: # Clean keywords to just alphanumeric and space characters keywords = re.sub(r'[^\w ]', ' ', keywords) keywords = re.sub('\s+', ' ', keywords) keywords = keywords.split(' ') def regexify_keywords(keyword): keyword = keyword.lower() return re.compile('^%s' % keyword) keywords = map(regexify_keywords, keywords) filters['_keywords__all'] = keywords if exclude_taken_courses == "yes": if current_user: ucs = (current_user.get_user_courses().only( 'course_id', 'term_id')) filters['id__nin'] = [ uc.course_id for uc in ucs if not m.term.Term.is_shortlist_term(uc.term_id) ] else: logging.error('Anonymous user tried excluding taken courses') if sort_mode == 'friends_taken': # TODO(mack): should only do if user is logged in friends = m.User.objects( id__in=current_user.friend_ids).only('course_history') # TODO(mack): need to majorly optimize this num_friends_by_course = {} for friend in friends: for course_id in friend.course_ids: if not course_id in num_friends_by_course: num_friends_by_course[course_id] = 0 num_friends_by_course[course_id] += 1 filters['id__in'] = num_friends_by_course.keys() existing_courses = m.Course.objects(**filters).only('id') existing_course_ids = set(c.id for c in existing_courses) for course_id in num_friends_by_course.keys(): if course_id not in existing_course_ids: del num_friends_by_course[course_id] sorted_course_count_tuples = sorted( num_friends_by_course.items(), key=lambda (_, total): total, reverse=direction < 0, )[offset:offset + count] sorted_course_ids = [ course_id for (course_id, total) in sorted_course_count_tuples ] unsorted_limited_courses = m.Course.objects(id__in=sorted_course_ids) limited_courses_by_id = {} for course in unsorted_limited_courses: limited_courses_by_id[course.id] = course limited_courses = [] for course_id in sorted_course_ids: limited_courses.append(limited_courses_by_id[course_id]) else: sort_options = COURSES_SORT_MODES_BY_NAME[sort_mode] if sort_mode in RATING_SORT_MODES: sort_instr = '-' + sort_options['field'] sort_instr += "_positive" if direction < 0 else "_negative" else: sort_instr = '' if direction < 0: sort_instr = '-' sort_instr += sort_options['field'] unsorted_courses = m.Course.objects(**filters) sorted_courses = unsorted_courses.order_by(sort_instr) limited_courses = sorted_courses.skip(offset).limit(count) has_more = len(limited_courses) == count course_dict_list, user_course_dict_list, user_course_list = ( m.Course.get_course_and_user_course_dicts(limited_courses, current_user, include_friends=True, full_user_courses=False, include_sections=True)) professor_dict_list = m.Professor.get_reduced_professors_for_courses( limited_courses) user_dict_list = [] if current_user: user_ids = [ uc['user_id'] for uc in user_course_dict_list if uc['user_id'] != current_user.id ] users = m.User.objects(id__in=user_ids).only(*m.User.CORE_FIELDS) user_dict_list = [u.to_dict() for u in users] return util.json_dumps({ 'user_objs': user_dict_list, 'course_objs': course_dict_list, 'professor_objs': professor_dict_list, 'user_course_objs': user_course_dict_list, 'has_more': has_more, })
def search_courses(): # TODO(mack): create enum of sort options # num_friends, num_ratings, overall, interest, easiness request = flask.request keywords = request.values.get('keywords') sort_mode = request.values.get('sort_mode', 'popular') default_direction = COURSES_SORT_MODES_BY_NAME[sort_mode]['direction'] direction = int(request.values.get('direction', default_direction)) count = int(request.values.get('count', 10)) offset = int(request.values.get('offset', 0)) exclude_taken_courses = request.values.get('exclude_taken_courses') current_user = view_helpers.get_current_user() # TODO(david): These logging things should be done asynchronously rmclogger.log_event( rmclogger.LOG_CATEGORY_COURSE_SEARCH, rmclogger.LOG_EVENT_SEARCH_PARAMS, request.values ) filters = {} if keywords: # Clean keywords to just alphanumeric and space characters keywords = re.sub(r'[^\w ]', ' ', keywords) keywords = re.sub('\s+', ' ', keywords) keywords = keywords.split(' ') def regexify_keywords(keyword): keyword = keyword.lower() return re.compile('^%s' % keyword) keywords = map(regexify_keywords, keywords) filters['_keywords__all'] = keywords if exclude_taken_courses == "yes": if current_user: ucs = (current_user.get_user_courses() .only('course_id', 'term_id')) filters['id__nin'] = [ uc.course_id for uc in ucs if not m.term.Term.is_shortlist_term(uc.term_id) ] else: logging.error('Anonymous user tried excluding taken courses') if sort_mode == 'friends_taken': # TODO(mack): should only do if user is logged in friends = m.User.objects(id__in=current_user.friend_ids).only( 'course_history') # TODO(mack): need to majorly optimize this num_friends_by_course = {} for friend in friends: for course_id in friend.course_ids: if not course_id in num_friends_by_course: num_friends_by_course[course_id] = 0 num_friends_by_course[course_id] += 1 filters['id__in'] = num_friends_by_course.keys() existing_courses = m.Course.objects(**filters).only('id') existing_course_ids = set(c.id for c in existing_courses) for course_id in num_friends_by_course.keys(): if course_id not in existing_course_ids: del num_friends_by_course[course_id] sorted_course_count_tuples = sorted( num_friends_by_course.items(), key=lambda (_, total): total, reverse=direction < 0, )[offset:offset + count] sorted_course_ids = [course_id for (course_id, total) in sorted_course_count_tuples] unsorted_limited_courses = m.Course.objects(id__in=sorted_course_ids) limited_courses_by_id = {} for course in unsorted_limited_courses: limited_courses_by_id[course.id] = course limited_courses = [] for course_id in sorted_course_ids: limited_courses.append(limited_courses_by_id[course_id]) else: sort_options = COURSES_SORT_MODES_BY_NAME[sort_mode] if sort_mode in RATING_SORT_MODES: sort_instr = '-' + sort_options['field'] sort_instr += "_positive" if direction < 0 else "_negative" else: sort_instr = '' if direction < 0: sort_instr = '-' sort_instr += sort_options['field'] unsorted_courses = m.Course.objects(**filters) sorted_courses = unsorted_courses.order_by(sort_instr) limited_courses = sorted_courses.skip(offset).limit(count) has_more = len(limited_courses) == count course_dict_list, user_course_dict_list, user_course_list = ( m.Course.get_course_and_user_course_dicts( limited_courses, current_user, include_friends=True, full_user_courses=False, include_sections=True)) professor_dict_list = m.Professor.get_reduced_professors_for_courses( limited_courses) user_dict_list = [] if current_user: user_ids = [uc['user_id'] for uc in user_course_dict_list if uc['user_id'] != current_user.id] users = m.User.objects(id__in=user_ids).only(*m.User.CORE_FIELDS) user_dict_list = [u.to_dict() for u in users] return util.json_dumps({ 'user_objs': user_dict_list, 'course_objs': course_dict_list, 'professor_objs': professor_dict_list, 'user_course_objs': user_course_dict_list, 'has_more': has_more, })