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 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 render_template(*args, **kwargs): redis = view_helpers.get_redis_instance() current_user = view_helpers.get_current_user() should_renew_fb_token = False if (current_user and current_user.fbid and not current_user.is_demo_account and not hasattr(flask.request, 'as_user_override')): should_renew_fb_token = current_user.should_renew_fb_token kwargs.update({ 'env': app.config['ENV'], 'VERSION': VERSION, 'NUM_KITTENS': len(KITTEN_DATA), 'js_dir': app.config['JS_DIR'], 'ga_property_id': app.config['GA_PROPERTY_ID'], 'total_points': int(redis.get('total_points') or 0), 'current_user': current_user, 'should_renew_fb_token': should_renew_fb_token, 'current_term_id': util.get_current_term_id(), 'user_agent': flask.request.headers['User-Agent'] if 'User-Agent' in flask.request.headers else 'No Info', }) return flask_render_template(*args, **kwargs)
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 upload_transcript(): req = flask.request user = view_helpers.get_current_user() user_id = user.id rmclogger.log_event( rmclogger.LOG_CATEGORY_API, rmclogger.LOG_EVENT_TRANSCRIPT, { 'user_id': user_id, 'requset_form': req.form, }, ) def get_term_id(term_name): season, year = term_name.split() return m.Term.get_id_from_year_season(year, season) transcript_data = util.json_loads(req.form['transcriptData']) courses_by_term = transcript_data['coursesByTerm'] # TODO(Sandy): Batch request fetch to mongo instead of fetch while looping for term in courses_by_term: term_id = get_term_id(term['name']) program_year_id = term['programYearId'] for course_id in term['courseIds']: # TODO(Sandy): Fill in course weight and grade info here user.add_course(course_id.lower(), term_id, program_year_id) if courses_by_term: last_term = courses_by_term[0] term_id = get_term_id(last_term['name']) user.last_term_id = term_id user.last_program_year_id = last_term['programYearId'] user.program_name = transcript_data['programName'] student_id = transcript_data.get('studentId') if student_id: user.student_id = str(student_id) user.cache_mutual_course_ids(view_helpers.get_redis_instance()) user.transcripts_imported += 1 user.save() rmclogger.log_event( rmclogger.LOG_CATEGORY_TRANSCRIPT, rmclogger.LOG_EVENT_UPLOAD, user_id ) return ''
def remove_transcript(): current_user = view_helpers.get_current_user() current_user.course_history = [] current_user.save() # Remove cached mutual courses current_user.remove_mutual_course_ids(view_helpers.get_redis_instance()) # Remove term_id from user_courses # TODO(mack): Display message notifying users how many reviews they will # lose by removing their transcript. m.UserCourse.objects(user_id=current_user.id).delete() rmclogger.log_event(rmclogger.LOG_CATEGORY_TRANSCRIPT, rmclogger.LOG_EVENT_REMOVE, current_user.id) return ''
def remove_transcript(): current_user = view_helpers.get_current_user() current_user.course_history = [] current_user.save() # Remove cached mutual courses current_user.remove_mutual_course_ids(view_helpers.get_redis_instance()) # Remove term_id from user_courses # TODO(mack): Display message notifying users how many reviews they will # lose by removing their transcript. m.UserCourse.objects(user_id=current_user.id).delete() rmclogger.log_event( rmclogger.LOG_CATEGORY_TRANSCRIPT, rmclogger.LOG_EVENT_REMOVE, current_user.id ) return ''
def render_template(*args, **kwargs): redis = view_helpers.get_redis_instance() current_user = view_helpers.get_current_user() should_renew_fb_token = False if (current_user and not current_user.is_demo_account and not hasattr(flask.request, 'as_user_override')): should_renew_fb_token = current_user.should_renew_fb_token kwargs.update({ 'env': app.config['ENV'], 'VERSION': VERSION, 'NUM_KITTENS': len(KITTEN_DATA), 'js_dir': app.config['JS_DIR'], 'ga_property_id': app.config['GA_PROPERTY_ID'], 'total_points': int(redis.get('total_points') or 0), 'current_user': current_user, 'should_renew_fb_token': should_renew_fb_token, 'current_term_id': util.get_current_term_id(), }) return flask_render_template(*args, **kwargs)
def render_profile_page(profile_user_id, current_user=None): # TODO(mack): for dict maps, use .update() rather than overwriting to # avoid subtle overwrites by data that has fields filled out LAST_TERM_ID = util.get_current_term_id() # PART ONE - VALIDATION current_user = current_user or view_helpers.get_current_user() try: if profile_user_id: profile_user_id = bson.ObjectId(profile_user_id) except: logging.warn('Invalid profile_user_id (%s)' % profile_user_id) return view_helpers.redirect_to_profile(current_user) if not profile_user_id: return view_helpers.redirect_to_profile(current_user) if profile_user_id == current_user.id: own_profile = True profile_user = current_user else: own_profile = False # Allow only friends to view profile if not (profile_user_id in current_user.friend_ids or (current_user.is_admin and flask.request.values.get('admin'))): logging.info("User (%s) tried to access non-friend profile (%s)" % (current_user.id, profile_user_id)) return view_helpers.redirect_to_profile(current_user) profile_user = m.User.objects.with_id(profile_user_id) # Technically we don't need this check due to above (under normal # operation). Though have this anyway as a failsafe if profile_user is None: logging.warn('profile_user is None') return view_helpers.redirect_to_profile(current_user) if own_profile: profile_user_secret_id = profile_user.get_secret_id() else: profile_user_secret_id = None show_import_schedule = False # Redirect the user appropriately... to /onboarding if they have no course # history, and to wherever they logged in from if they just logged in # TODO(david): Should have frontend decide whether to take us to /profile # or /onboarding and not redirect in one of these two places if own_profile: redirect_url = flask.request.values.get('next') show_onboarding = False if not current_user.has_course_history: if not current_user.last_show_onboarding: show_onboarding = True else: time_delta = datetime.now() - current_user.last_show_onboarding # If they haven't imported any courses yet and the last time # the user was on the onboarding page is more than 5 days ago, # show the onboarding page again if time_delta.days > RESHOW_ONBOARDING_DELAY_DAYS: show_onboarding = True # See https://uwflow.uservoice.com/admin/tickets/62 if profile_user_id == '50b8ce2cd89d62310645ca78': show_onboarding = False if show_onboarding: onboarding_url = '/onboarding' if flask.request.query_string: onboarding_url = '%s?%s' % (onboarding_url, flask.request.query_string) return flask.make_response(flask.redirect(onboarding_url)) else: redirect_url = flask.request.values.get('next') if redirect_url: return flask.make_response(flask.redirect(redirect_url)) # Show the import schedule view if it's been long enough if not current_user.has_schedule: if current_user.last_show_import_schedule: time_delta = (datetime.now() - current_user.last_show_import_schedule) # User didn't import schedule yet, reshow every few days if time_delta.days > RESHOW_SCHEDULE_DELAY_DAYS: show_import_schedule = True else: show_import_schedule = True if show_import_schedule: # TODO(Sandy): Do this on modal dismiss instead current_user.last_show_import_schedule = datetime.now() current_user.save() # PART TWO - DATA FETCHING # Get the mutual course ids of friends of profile user mutual_course_ids_by_friend = {} if own_profile: mutual_course_ids_by_friend = profile_user.get_mutual_course_ids( view_helpers.get_redis_instance()) def get_friend_course_ids_in_term(friend_ids, term_id): user_courses = m.UserCourse.objects(term_id=term_id, user_id__in=friend_ids).only( 'user_id', 'course_id') last_term_course_ids_by_friend = {} for uc in user_courses: last_term_course_ids_by_friend.setdefault(uc.user_id, []).append(uc.course_id) return last_term_course_ids_by_friend # Get the course ids of last term courses of friends of profile user last_term_course_ids_by_friend = get_friend_course_ids_in_term( profile_user.friend_ids, LAST_TERM_ID) # Get the course ids of courses profile user has taken profile_course_ids = set(profile_user.course_ids) # Fetch courses for transcript, which need more detailed information # than other courses (such as mutual and last term courses for friends) transcript_courses = list(m.Course.objects(id__in=profile_course_ids)) # Fetch remainining courses that need less data. This will be mutual # and last term courses for profile user's friends friend_course_ids = set() friend_courses = [] if own_profile: for course_ids in mutual_course_ids_by_friend.values(): friend_course_ids = friend_course_ids.union(course_ids) for course_ids in last_term_course_ids_by_friend.values(): friend_course_ids = friend_course_ids.union(course_ids) friend_course_ids = friend_course_ids - profile_course_ids friend_courses = m.Course.objects(id__in=friend_course_ids).only( 'id', 'name') # Fetch simplified information for friends of profile user # (for friend sidebar) friends = profile_user.get_friends() # Fetch all professors for all courses professor_objs = m.Professor.get_reduced_professors_for_courses( transcript_courses) # PART THREE - TRANSFORM DATA TO DICTS # Convert professors to dicts professor_dicts = {} for professor_obj in professor_objs: professor_dicts[professor_obj['id']] = professor_obj # Convert courses to dicts course_dict_list, user_course_dict_list, user_course_list = ( m.Course.get_course_and_user_course_dicts(transcript_courses, current_user, include_friends=own_profile)) course_dicts = {} for course_dict in course_dict_list: course_dicts[course_dict['id']] = course_dict user_course_dicts = {} for user_course_dict in user_course_dict_list: user_course_dicts[user_course_dict['id']] = user_course_dict profile_uc_dict_list = [] # We only need to fetch usercourses for profile user if it is not the # current user since m.Course.get_course_and_user_course_dicts() will # have already fetched usercourses for the current user if not own_profile: # Get the user courses of profile user profile_uc_dict_list = [ uc.to_dict() for uc in profile_user.get_user_courses() ] # Get a mapping from course id to user_course for profile user profile_user_course_by_course = {} for uc_dict in profile_uc_dict_list: profile_user_course_by_course[uc_dict['course_id']] = uc_dict # Fill in with information about profile user for course in transcript_courses: course_dict = course_dicts[course.id] if not own_profile: # This has already been done for current user profile_uc_dict = profile_user_course_by_course.get(course.id) profile_user_course_id = profile_uc_dict['id'] user_course_dicts[profile_user_course_id] = profile_uc_dict # Since we only fetched the user courses of the logged in user in # m.Course.get_course_and_user_course_dicts() above, gotta also # add the user courses of the profile user here user_course_dict_list.append(profile_uc_dict) else: profile_user_course_id = course_dict.get('user_course_id') if profile_user_course_id: profile_uc_dict_list.append( user_course_dicts[profile_user_course_id]) course_dict['profile_user_course_id'] = profile_user_course_id for course in friend_courses: course_dicts[course.id] = course.to_dict() def filter_course_ids(course_ids): return [ course_id for course_id in course_ids if course_id in course_dicts ] # Convert friend users to dicts user_dicts = {} # TODO(mack): should really be named current_term last_term = m.Term(id=LAST_TERM_ID) for friend in friends: user_dict = friend.to_dict(extended=False) if own_profile: user_dict.update({ 'last_term_name': last_term.name, 'last_term_course_ids': filter_course_ids( last_term_course_ids_by_friend.get(friend.id, [])), 'mutual_course_ids': filter_course_ids( mutual_course_ids_by_friend.get(friend.id, [])), }) user_dicts[friend.id] = user_dict # Convert profile user to dict # TODO(mack): This must be after friend user dicts since it can override # data in it. Remove this restriction profile_dict = profile_user.to_dict(include_course_ids=True) profile_dict.update({ 'last_program_year_id': profile_user.get_latest_program_year_id(), }) user_dicts.setdefault(profile_user.id, {}).update(profile_dict) # Convert current user to dict # TODO(mack): This must be after friend user dicts since it can override # data in it. Remove this restriction if not own_profile: user_dicts.setdefault(current_user.id, {}).update( current_user.to_dict(include_course_ids=True)) def get_ordered_transcript(profile_uc_dict_list): transcript_by_term = {} for uc_dict in profile_uc_dict_list: (transcript_by_term.setdefault(uc_dict['term_id'], []).append(uc_dict)) ordered_transcript = [] for term_id, uc_dicts in sorted(transcript_by_term.items(), reverse=True): curr_term = m.Term(id=term_id) term_dict = { 'id': curr_term.id, 'name': curr_term.name, 'program_year_id': uc_dicts[0].get('program_year_id'), 'course_ids': [ uc_dict['course_id'] for uc_dict in uc_dicts if uc_dict['course_id'] in course_dicts ], } ordered_transcript.append(term_dict) return ordered_transcript, transcript_by_term # Store courses by term as transcript using the current user's friends ordered_transcript, transcript_by_term = get_ordered_transcript( profile_uc_dict_list) # Fetch exam schedules and schedule items current_term_id = util.get_current_term_id() current_term_courses = transcript_by_term.get(current_term_id, []) current_course_ids = [c['course_id'] for c in current_term_courses] exam_objs = profile_user.get_current_term_exams(current_course_ids) exam_dicts = [e.to_dict() for e in exam_objs] exam_updated_date = None if exam_objs: exam_updated_date = exam_objs[0].id.generation_time # Set the course to prompt the user to review if it's time course_id_to_review = None if own_profile and profile_user.should_prompt_review(): profile_user_courses = filter(lambda uc: uc.user_id == profile_user.id, user_course_list) uc_to_review = m.UserCourse.select_course_to_review( profile_user_courses) course_id_to_review = uc_to_review and uc_to_review.course_id if uc_to_review: uc_to_review.select_for_review(current_user) # NOTE: This implictly requires that the courses on the schedule are on the # transcript, since these course objects are needed by the schedule on the # frontend. This should be the case since when we add a schedule item, a # corresponding item is added to the transcript. schedule_item_dicts = profile_user.get_schedule_item_dicts(exam_objs) failed_schedule_item_dicts = profile_user.get_failed_schedule_item_dicts() referrals = m.User.objects(referrer_id=current_user.id) referral_objs = [referral.to_dict() for referral in referrals] rmclogger.log_event( rmclogger.LOG_CATEGORY_IMPRESSION, rmclogger.LOG_EVENT_PROFILE, { 'current_user': current_user.id, 'profile_user': profile_user.id, }, ) schedule_screenshot.update_screenshot_async(profile_user) scholarships_dict = [] if profile_user.id == current_user.id: scholarships = m.Scholarship.objects() # Filter scholarships based on program closed_scholarship_ids_set = set(profile_user.closed_scholarship_ids) scholarships = [ s for s in scholarships if profile_user.short_program_name in s.programs and s.id not in closed_scholarship_ids_set ] scholarships_dict = [s.to_dict() for s in scholarships] recommendation_dict = [] recommended_course_ids = [] if profile_user.id == current_user.id: recommended_course_ids = current_user.recommended_courses recommendation_dict = [ m.Course.objects(id=course_id).first().to_dict() for course_id in recommended_course_ids ] return flask.render_template( 'profile_page.html', page_script='profile_page.js', transcript_obj=ordered_transcript, user_objs=user_dicts.values(), referral_objs=referral_objs, user_course_objs=user_course_dicts.values(), course_objs=course_dicts.values(), professor_objs=professor_dicts.values(), # TODO(mack): currently needed by jinja to do server-side rendering # figure out a cleaner way to do this w/o passing another param profile_obj=profile_dict, profile_user_id=profile_user.id, current_user_id=current_user.id, profile_user_secret_id=profile_user_secret_id, own_profile=own_profile, has_courses=profile_user.has_course_history, exam_objs=exam_dicts, exam_updated_date=exam_updated_date, schedule_item_objs=schedule_item_dicts, failed_schedule_item_objs=failed_schedule_item_dicts, has_shortlisted=profile_user.has_shortlisted, show_import_schedule=show_import_schedule, show_import_schedule_button=own_profile and (not profile_user.has_schedule), course_id_to_review=course_id_to_review, scholarship_objs=scholarships_dict, recommended_objs=recommendation_dict, )
def render_profile_page(profile_user_id, current_user=None): # TODO(mack): for dict maps, use .update() rather than overwriting to # avoid subtle overwrites by data that has fields filled out LAST_TERM_ID = util.get_current_term_id() # PART ONE - VALIDATION current_user = current_user or view_helpers.get_current_user() try: if profile_user_id: profile_user_id = bson.ObjectId(profile_user_id) except: logging.warn('Invalid profile_user_id (%s)' % profile_user_id) return view_helpers.redirect_to_profile(current_user) if not profile_user_id: return view_helpers.redirect_to_profile(current_user) if profile_user_id == current_user.id: own_profile = True profile_user = current_user else: own_profile = False # Allow only friends to view profile if not (profile_user_id in current_user.friend_ids or ( current_user.is_admin and flask.request.values.get('admin'))): logging.info("User (%s) tried to access non-friend profile (%s)" % (current_user.id, profile_user_id)) return view_helpers.redirect_to_profile(current_user) profile_user = m.User.objects.with_id(profile_user_id) # Technically we don't need this check due to above (under normal # operation). Though have this anyway as a failsafe if profile_user is None: logging.warn('profile_user is None') return view_helpers.redirect_to_profile(current_user) if own_profile: profile_user_secret_id = profile_user.get_secret_id() else: profile_user_secret_id = None show_import_schedule = False # Redirect the user appropriately... to /onboarding if they have no course # history, and to wherever they logged in from if they just logged in # TODO(david): Should have frontend decide whether to take us to /profile # or /onboarding and not redirect in one of these two places if own_profile: redirect_url = flask.request.values.get('next') show_onboarding = False if not current_user.has_course_history: if not current_user.last_show_onboarding: show_onboarding = True else: time_delta = datetime.now() - current_user.last_show_onboarding # If they haven't imported any courses yet and the last time # the user was on the onboarding page is more than 5 days ago, # show the onboarding page again if time_delta.days > RESHOW_ONBOARDING_DELAY_DAYS: show_onboarding = True # See https://uwflow.uservoice.com/admin/tickets/62 if profile_user_id == '50b8ce2cd89d62310645ca78': show_onboarding = False if show_onboarding: onboarding_url = '/onboarding' if flask.request.query_string: onboarding_url = '%s?%s' % ( onboarding_url, flask.request.query_string) return flask.make_response(flask.redirect(onboarding_url)) else: redirect_url = flask.request.values.get('next') if redirect_url: return flask.make_response(flask.redirect(redirect_url)) # Show the import schedule view if it's been long enough if not current_user.has_schedule: if current_user.last_show_import_schedule: time_delta = (datetime.now() - current_user.last_show_import_schedule) # User didn't import schedule yet, reshow every few days if time_delta.days > RESHOW_SCHEDULE_DELAY_DAYS: show_import_schedule = True else: show_import_schedule = True if show_import_schedule: # TODO(Sandy): Do this on modal dismiss instead current_user.last_show_import_schedule = datetime.now() current_user.save() # PART TWO - DATA FETCHING # Get the mutual course ids of friends of profile user mutual_course_ids_by_friend = {} if own_profile: mutual_course_ids_by_friend = profile_user.get_mutual_course_ids( view_helpers.get_redis_instance()) def get_friend_course_ids_in_term(friend_ids, term_id): user_courses = m.UserCourse.objects( term_id=term_id, user_id__in=friend_ids).only( 'user_id', 'course_id') last_term_course_ids_by_friend = {} for uc in user_courses: last_term_course_ids_by_friend.setdefault( uc.user_id, []).append(uc.course_id) return last_term_course_ids_by_friend # Get the course ids of last term courses of friends of profile user last_term_course_ids_by_friend = get_friend_course_ids_in_term( profile_user.friend_ids, LAST_TERM_ID) # Get the course ids of courses profile user has taken profile_course_ids = set(profile_user.course_ids) # Fetch courses for transcript, which need more detailed information # than other courses (such as mutual and last term courses for friends) transcript_courses = list(m.Course.objects(id__in=profile_course_ids)) # Fetch remainining courses that need less data. This will be mutual # and last term courses for profile user's friends friend_course_ids = set() friend_courses = [] if own_profile: for course_ids in mutual_course_ids_by_friend.values(): friend_course_ids = friend_course_ids.union(course_ids) for course_ids in last_term_course_ids_by_friend.values(): friend_course_ids = friend_course_ids.union(course_ids) friend_course_ids = friend_course_ids - profile_course_ids friend_courses = m.Course.objects( id__in=friend_course_ids).only('id', 'name') # Fetch simplified information for friends of profile user # (for friend sidebar) friends = profile_user.get_friends() # Fetch all professors for all courses professor_objs = m.Professor.get_reduced_professors_for_courses( transcript_courses) # PART THREE - TRANSFORM DATA TO DICTS # Convert professors to dicts professor_dicts = {} for professor_obj in professor_objs: professor_dicts[professor_obj['id']] = professor_obj # Convert courses to dicts course_dict_list, user_course_dict_list, user_course_list = ( m.Course.get_course_and_user_course_dicts( transcript_courses, current_user, include_friends=own_profile)) course_dicts = {} for course_dict in course_dict_list: course_dicts[course_dict['id']] = course_dict user_course_dicts = {} for user_course_dict in user_course_dict_list: user_course_dicts[user_course_dict['id']] = user_course_dict profile_uc_dict_list = [] # We only need to fetch usercourses for profile user if it is not the # current user since m.Course.get_course_and_user_course_dicts() will # have already fetched usercourses for the current user if not own_profile: # Get the user courses of profile user profile_uc_dict_list = [ uc.to_dict() for uc in profile_user.get_user_courses()] # Get a mapping from course id to user_course for profile user profile_user_course_by_course = {} for uc_dict in profile_uc_dict_list: profile_user_course_by_course[uc_dict['course_id']] = uc_dict # Fill in with information about profile user for course in transcript_courses: course_dict = course_dicts[course.id] if not own_profile: # This has already been done for current user profile_uc_dict = profile_user_course_by_course.get(course.id) profile_user_course_id = profile_uc_dict['id'] user_course_dicts[profile_user_course_id] = profile_uc_dict # Since we only fetched the user courses of the logged in user in # m.Course.get_course_and_user_course_dicts() above, gotta also # add the user courses of the profile user here user_course_dict_list.append(profile_uc_dict) else: profile_user_course_id = course_dict.get('user_course_id') if profile_user_course_id: profile_uc_dict_list.append( user_course_dicts[profile_user_course_id]) course_dict['profile_user_course_id'] = profile_user_course_id for course in friend_courses: course_dicts[course.id] = course.to_dict() def filter_course_ids(course_ids): return [course_id for course_id in course_ids if course_id in course_dicts] # Convert friend users to dicts user_dicts = {} # TODO(mack): should really be named current_term last_term = m.Term(id=LAST_TERM_ID) for friend in friends: user_dict = friend.to_dict(extended=False) if own_profile: user_dict.update({ 'last_term_name': last_term.name, 'last_term_course_ids': filter_course_ids( last_term_course_ids_by_friend.get(friend.id, [])), 'mutual_course_ids': filter_course_ids( mutual_course_ids_by_friend.get(friend.id, [])), }) user_dicts[friend.id] = user_dict # Convert profile user to dict # TODO(mack): This must be after friend user dicts since it can override # data in it. Remove this restriction profile_dict = profile_user.to_dict(include_course_ids=True) profile_dict.update({ 'last_program_year_id': profile_user.get_latest_program_year_id(), }) user_dicts.setdefault(profile_user.id, {}).update(profile_dict) # Convert current user to dict # TODO(mack): This must be after friend user dicts since it can override # data in it. Remove this restriction if not own_profile: user_dicts.setdefault(current_user.id, {}).update( current_user.to_dict(include_course_ids=True)) def get_ordered_transcript(profile_uc_dict_list): transcript_by_term = {} for uc_dict in profile_uc_dict_list: (transcript_by_term.setdefault(uc_dict['term_id'], []) .append(uc_dict)) ordered_transcript = [] for term_id, uc_dicts in sorted(transcript_by_term.items(), reverse=True): curr_term = m.Term(id=term_id) term_dict = { 'id': curr_term.id, 'name': curr_term.name, 'program_year_id': uc_dicts[0].get('program_year_id'), 'course_ids': [uc_dict['course_id'] for uc_dict in uc_dicts if uc_dict['course_id'] in course_dicts], } ordered_transcript.append(term_dict) return ordered_transcript, transcript_by_term # Store courses by term as transcript using the current user's friends ordered_transcript, transcript_by_term = get_ordered_transcript( profile_uc_dict_list) # Fetch exam schedules and schedule items current_term_id = util.get_current_term_id() current_term_courses = transcript_by_term.get(current_term_id, []) current_course_ids = [c['course_id'] for c in current_term_courses] exam_objs = profile_user.get_current_term_exams(current_course_ids) exam_dicts = [e.to_dict() for e in exam_objs] exam_updated_date = None if exam_objs: exam_updated_date = exam_objs[0].id.generation_time # Set the course to prompt the user to review if it's time course_id_to_review = None if own_profile and profile_user.should_prompt_review(): profile_user_courses = filter(lambda uc: uc.user_id == profile_user.id, user_course_list) uc_to_review = m.UserCourse.select_course_to_review( profile_user_courses) course_id_to_review = uc_to_review and uc_to_review.course_id if uc_to_review: uc_to_review.select_for_review(current_user) # NOTE: This implictly requires that the courses on the schedule are on the # transcript, since these course objects are needed by the schedule on the # frontend. This should be the case since when we add a schedule item, a # corresponding item is added to the transcript. schedule_item_dicts = profile_user.get_schedule_item_dicts(exam_objs) failed_schedule_item_dicts = profile_user.get_failed_schedule_item_dicts() referrals = m.User.objects(referrer_id=current_user.id) referral_objs = [referral.to_dict() for referral in referrals] rmclogger.log_event( rmclogger.LOG_CATEGORY_IMPRESSION, rmclogger.LOG_EVENT_PROFILE, { 'current_user': current_user.id, 'profile_user': profile_user.id, }, ) schedule_screenshot.update_screenshot_async(profile_user) scholarships_dict = [] if profile_user.id == current_user.id: scholarships = m.Scholarship.objects() # Filter scholarships based on program closed_scholarship_ids_set = set(profile_user.closed_scholarship_ids) scholarships = [s for s in scholarships if profile_user.short_program_name in s.programs and s.id not in closed_scholarship_ids_set] scholarships_dict = [s.to_dict() for s in scholarships] recommendation_dict = [] recommended_course_ids = [] if profile_user.id == current_user.id: recommended_course_ids = current_user.recommended_courses recommendation_dict = [m.Course.objects(id=course_id).first().to_dict() for course_id in recommended_course_ids] return flask.render_template('profile_page.html', page_script='profile_page.js', transcript_obj=ordered_transcript, user_objs=user_dicts.values(), referral_objs=referral_objs, user_course_objs=user_course_dicts.values(), course_objs=course_dicts.values(), professor_objs=professor_dicts.values(), # TODO(mack): currently needed by jinja to do server-side rendering # figure out a cleaner way to do this w/o passing another param profile_obj=profile_dict, profile_user_id=profile_user.id, current_user_id=current_user.id, profile_user_secret_id=profile_user_secret_id, own_profile=own_profile, has_courses=profile_user.has_course_history, exam_objs=exam_dicts, exam_updated_date=exam_updated_date, schedule_item_objs=schedule_item_dicts, failed_schedule_item_objs=failed_schedule_item_dicts, has_shortlisted=profile_user.has_shortlisted, show_import_schedule=show_import_schedule, show_import_schedule_button=own_profile and (not profile_user.has_schedule), course_id_to_review=course_id_to_review, scholarship_objs=scholarships_dict, recommended_objs=recommendation_dict, )
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, })