Exemplo n.º 1
0
Arquivo: server.py Projeto: Kapin/rmc
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,
    })
Exemplo n.º 2
0
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,
    })
Exemplo n.º 3
0
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)
Exemplo n.º 4
0
Arquivo: server.py Projeto: Kapin/rmc
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,
    })
Exemplo n.º 5
0
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,
    })
Exemplo n.º 6
0
Arquivo: server.py Projeto: Kapin/rmc
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 ''
Exemplo n.º 7
0
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 ''
Exemplo n.º 8
0
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 ''
Exemplo n.º 9
0
Arquivo: server.py Projeto: Kapin/rmc
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 ''
Exemplo n.º 10
0
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)
Exemplo n.º 11
0
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,
    )
Exemplo n.º 12
0
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,
    )
Exemplo n.º 13
0
Arquivo: server.py Projeto: Kapin/rmc
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,
    })
Exemplo n.º 14
0
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,
    })