예제 #1
0
def register_by_password_status():
    if not request.args.get("request", None) or not isinstance(request.args["request"], str):
        return "Invalid request"
    req = IdentityVerificationDAO.get_request_by_id(request.args.get("request"))
    if not req:
        return "Invalid request"
    if req["verification_method"] != "password":
        logger.warn("Non-password verification request is trying get status from password interface")
        return "Invalid request"
    # fetch status from everyclass-auth
    with elasticapm.capture_span('rpc_get_auth_state'):
        rpc_result = HttpRpc.call_with_error_page('{}/get_result'.format(app.config['AUTH_BASE_URL']),
                                                  data={'request_id': str(request.args.get("request"))},
                                                  retry=True)
        if isinstance(rpc_result, str):
            return rpc_result
        api_response = rpc_result

    if api_response['success']:
        IdentityVerificationDAO.set_request_status(str(request.args.get("request")), ID_STATUS_PWD_SUCCESS)
        return jsonify({"message": "SUCCESS"})
    elif api_response["message"] in ("PASSWORD_WRONG", "INTERNAL_ERROR"):
        return jsonify({"message": api_response["message"]})
    else:
        return jsonify({"message": "next-time"})
예제 #2
0
 def get_visitors(cls, sid_orig: str) -> List[Dict]:
     """获得访客列表"""
     db = get_mongodb()
     result = db[cls.collection_name].find({
         "host": sid_orig
     }).sort("last_time", -1).limit(50)
     visitor_list = []
     for people in result:
         stu_cache = RedisCacheDAO.get_student(people["visitor"])
         if stu_cache:
             visitor_list.append({
                 "name": stu_cache.name,
                 "sid": stu_cache.sid,
                 "visit_time": people["last_time"]
             })
         else:
             # query api-server
             with elasticapm.capture_span('rpc_search'):
                 rpc_result = HttpRpc.call(
                     method="GET",
                     url='{}/v1/search/{}'.format(
                         current_app.config['API_SERVER_BASE_URL'],
                         people["visitor"]),
                     retry=True)
             visitor_list.append({
                 "name": rpc_result["student"][0]["name"],
                 "sid": rpc_result["student"][0]["sid"],
                 "visit_time": people["last_time"]
             })
             RedisCacheDAO.set_student(
                 Student(sid_orig=people["visitor"],
                         name=rpc_result["student"][0]["name"],
                         sid=rpc_result["student"][0]["sid"]))
     return visitor_list
예제 #3
0
def cal_page(resource_type: str, resource_identifier: str, url_semester: str):
    """课表导出页面视图函数"""
    from flask import current_app as app, render_template, url_for, flash, redirect

    from everyclass.server.utils.rpc import HttpRpc
    from everyclass.server.db.dao import CalendarTokenDAO

    if resource_type not in ('student', 'teacher'):
        flash('请求异常')
        return redirect(url_for('main.main'))

    with elasticapm.capture_span('rpc_query_student'):
        rpc_result = HttpRpc.call_with_error_page('{}/v1/{}/{}/{}'.format(
            app.config['API_SERVER_BASE_URL'], resource_type,
            resource_identifier, url_semester),
                                                  retry=True)
        if isinstance(rpc_result, str):
            return rpc_result

    token = CalendarTokenDAO.get_or_set_calendar_token(
        resource_type=resource_type,
        identifier=rpc_result["sid"],
        semester=url_semester)

    ics_url = url_for('calendar.ics_download',
                      calendar_token=token,
                      _external=True)
    ics_webcal = ics_url.replace('https', 'webcal').replace('http', 'webcal')

    return render_template('calendarSubscribe.html',
                           ics_url=ics_url,
                           ics_webcal=ics_webcal,
                           android_client_url=app.config['ANDROID_CLIENT_URL'])
예제 #4
0
def register_by_password_success():
    """验证成功后新增用户、写入登录态,然后跳转到用户首页"""
    if not session.get(SESSION_VER_REQ_ID, None):
        return "Invalid request"
    verification_req = IdentityVerificationDAO.get_request_by_id(str(session[SESSION_VER_REQ_ID]))
    if not verification_req or verification_req["status"] != ID_STATUS_PWD_SUCCESS:
        return "Invalid request"

    # fetch student basic information from api-server
    with elasticapm.capture_span('rpc_get_student_info'):
        rpc_result = HttpRpc.call_with_error_page('{}/v1/search/{}'.format(app.config['API_SERVER_BASE_URL'],
                                                                           verification_req["sid_orig"]),
                                                  retry=True)
        if isinstance(rpc_result, str):
            return rpc_result
        api_response = rpc_result

    UserDAO.add_user(sid_orig=verification_req["sid_orig"], password=verification_req["password"])

    # write login state to session
    flash(MSG_REGISTER_SUCCESS)
    del session[SESSION_VER_REQ_ID]
    session[SESSION_CURRENT_USER] = Student(sid_orig=api_response["student"][0]["sid_orig"],
                                            sid=api_response["student"][0]["sid"],
                                            name=api_response["student"][0]["name"])
    return redirect(url_for("user.main"))
예제 #5
0
def android_client_get_semester(identifier):
    """android client get a student or teacher's semesters
    """
    from flask import current_app as app, jsonify
    from everyclass.server.utils.rpc import HttpRpc

    with elasticapm.capture_span('rpc_search'):
        rpc_result = HttpRpc.call_with_handle_message('{}/v1/search/{}'.format(
            app.config['API_SERVER_BASE_URL'], identifier))
        if isinstance(rpc_result, tuple):
            return rpc_result
        api_response = rpc_result

    if len(api_response['student']) == 1:
        return jsonify({
            'type':
            'student',
            'sid':
            api_response['student'][0]['sid'],
            'semesters':
            sorted(api_response['student'][0]['semester'])
        })
    if len(api_response['teacher']) == 1:
        return jsonify({
            'type':
            'teacher',
            'tid':
            api_response['teacher'][0]['tid'],
            'semesters':
            sorted(api_response['teacher'][0]['semester'])
        })
    return "Bad request (got multiple people)", 400
예제 #6
0
def register_by_email():
    """学生注册-邮件"""
    if not session.get(SESSION_LAST_VIEWED_STUDENT, None):
        return render_template('common/error.html', message=MSG_400)

    sid_orig = session[SESSION_LAST_VIEWED_STUDENT].sid_orig

    if UserDAO.exist(sid_orig):
        return render_template("common/error.html", message="您已经注册过了,请勿重复注册。")

    request_id = IdentityVerificationDAO.new_register_request(sid_orig, "email", ID_STATUS_SENT)

    # call everyclass-auth to send email
    with elasticapm.capture_span('rpc_send_email'):
        rpc_result = HttpRpc.call_with_error_page('{}/register_by_email'.format(app.config['AUTH_BASE_URL']),
                                                  data={'request_id': request_id,
                                                        'student_id': sid_orig},
                                                  method='POST',
                                                  retry=True)
        if isinstance(rpc_result, str):
            return rpc_result
        api_response = rpc_result

    if api_response['acknowledged']:
        return render_template('user/emailSent.html', request_id=request_id)
    else:
        return render_template('common/error.html', message=MSG_INTERNAL_ERROR)
예제 #7
0
def ics_download(calendar_token):
    """
    iCalendar ics file download

    因为课表会更新,所以 ics 文件只能在这里动态生成,不能在日历订阅页面就生成
    """
    from flask import send_from_directory, current_app
    from everyclass.server.db.dao import CalendarTokenDAO
    from everyclass.server.db.model import Semester
    from everyclass.server.calendar import ics_generator
    from everyclass.server.utils.rpc import HttpRpc
    from everyclass.server.utils import teacher_list_fix
    from everyclass.server.utils import teacher_list_to_str
    from everyclass.server.utils import lesson_string_to_dict

    result = CalendarTokenDAO.find_calendar_token(token=calendar_token)
    if not result:
        return 'invalid calendar token', 404

    with elasticapm.capture_span('rpc_find_people'):
        rpc_result = HttpRpc.call_with_error_page('{}/v1/{}/{}/{}'.format(
            current_app.config['API_SERVER_BASE_URL'], result['type'],
            result['sid'] if result['type'] == 'student' else result['tid'],
            result['semester']),
                                                  params={
                                                      'week_string': 'true'
                                                  },
                                                  retry=True)
        if isinstance(rpc_result, str):
            return rpc_result
        api_response = rpc_result

    with elasticapm.capture_span('process_rpc_result'):
        semester = Semester(result['semester'])

        courses = dict()
        for each_class in api_response['course']:
            day, time = lesson_string_to_dict(each_class['lesson'])
            if (day, time) not in courses:
                courses[(day, time)] = list()
            courses[(day, time)].append(
                dict(name=each_class['name'],
                     teacher=teacher_list_to_str(
                         teacher_list_fix(each_class['teacher'])),
                     week=each_class['week'],
                     week_string=each_class['week_string'],
                     classroom=each_class['room'],
                     classroom_id=each_class['rid'],
                     cid=each_class['cid']))

    ics_generator.generate(name=api_response['name'],
                           courses=courses,
                           semester=semester,
                           ics_token=calendar_token)

    return send_from_directory("../../calendar_files",
                               calendar_token + ".ics",
                               as_attachment=True,
                               mimetype='text/calendar')
예제 #8
0
def get_classroom(url_rid, url_semester):
    """教室查询"""
    from everyclass.server.utils import lesson_string_to_dict
    from everyclass.server.utils import teacher_list_fix
    from everyclass.server.utils import semester_calculate
    from .utils.rpc import HttpRpc

    with elasticapm.capture_span('rpc_query_room'):
        rpc_result = HttpRpc.call_with_error_page('{}/v1/room/{}/{}'.format(
            app.config['API_SERVER_BASE_URL'], url_rid, url_semester),
                                                  params={
                                                      'week_string': 'true',
                                                      'other_semester': 'true'
                                                  },
                                                  retry=True)
        if isinstance(rpc_result, str):
            return rpc_result
        api_response = rpc_result

    if 'name' not in api_response:
        logger.info("Hit classroom 'name' KeyError temporary fix")
        flash("教务数据异常,暂时无法查询本教室。其他教室不受影响。")
        return redirect(url_for("main.main"))

    with elasticapm.capture_span('process_rpc_result'):
        courses = dict()
        for each_class in api_response['course']:
            day, time = lesson_string_to_dict(each_class['lesson'])
            if (day, time) not in courses:
                courses[(day, time)] = list()
            courses[(day, time)].append(
                dict(name=each_class['name'],
                     week=each_class['week_string'],
                     teacher=teacher_list_fix(each_class['teacher']),
                     location=each_class['room'],
                     cid=each_class['cid']))

    empty_5, empty_6, empty_sat, empty_sun = _empty_column_check(courses)

    available_semesters = semester_calculate(
        url_semester, sorted(api_response['semester_list']))

    return render_template('query/room.html',
                           name=api_response['name'],
                           campus=api_response['campus'],
                           building=api_response['building'],
                           rid=url_rid,
                           classes=courses,
                           empty_sat=empty_sat,
                           empty_sun=empty_sun,
                           empty_6=empty_6,
                           empty_5=empty_5,
                           available_semesters=available_semesters,
                           current_semester=url_semester)
예제 #9
0
 def get_android_download_link():
     """
     It's not possible to make a HTTP request during `create_app` since the urllib2 is patched by gevent
     and the gevent engine is not started yet (controlled by uWSGI). So we can only do the initialization
     here.
     """
     from everyclass.server.utils.rpc import HttpRpc
     android_manifest = HttpRpc.call(method="GET",
                                     url="https://everyclass.cdn.admirable.pro/android/manifest.json",
                                     retry=True)
     android_ver = android_manifest['latestVersions']['mainstream']['versionCode']
     __app.config['ANDROID_CLIENT_URL'] = android_manifest['releases'][android_ver]['url']
예제 #10
0
def android_client_get_ics(resource_type, identifier, semester):
    """
    android client get a student or teacher's ics file

    If the student does not have privacy mode, anyone can use student number to subscribe his calendar.
    If the privacy mode is on and there is no HTTP basic authentication, return a 401(unauthorized)
    status code and the Android client ask user for password to try again.
    """
    from flask import current_app as app, redirect, url_for, request

    from everyclass.server.utils.rpc import HttpRpc
    from everyclass.server.db.dao import PrivacySettingsDAO, CalendarTokenDAO, UserDAO

    if resource_type not in ('student', 'teacher'):
        return "Unknown resource type", 400

    with elasticapm.capture_span('rpc_search'):
        rpc_result = HttpRpc.call_with_handle_message('{}/v1/{}/{}/{}'.format(
            app.config['API_SERVER_BASE_URL'], resource_type, identifier,
            semester))
        if isinstance(rpc_result, tuple):
            return rpc_result
        api_response = rpc_result

    if resource_type == 'teacher':
        cal_token = CalendarTokenDAO.get_or_set_calendar_token(
            resource_type=resource_type,
            identifier=rpc_result["sid"],
            semester=semester)
        return redirect(
            url_for('calendar.ics_download', calendar_token=cal_token))
    else:
        # student
        with elasticapm.capture_span('get_privacy_settings'):
            privacy_level = PrivacySettingsDAO.get_level(api_response['sid'])

        # get authorization from HTTP header and verify password if privacy is on
        if privacy_level != 0:
            if not request.authorization:
                return "Unauthorized (privacy on)", 401
            username, password = request.authorization
            if not UserDAO.check_password(username, password):
                return "Unauthorized (password wrong)", 401
            if api_response['sid'] != username:
                return "Unauthorized (username mismatch)", 401

        cal_token = CalendarTokenDAO.get_or_set_calendar_token(
            resource_type=resource_type,
            identifier=rpc_result["sid"],
            semester=semester)
        return redirect(
            url_for('calendar.ics_download', calendar_token=cal_token))
예제 #11
0
def get_course(url_cid: str, url_semester: str):
    """课程查询"""

    from everyclass.server.utils import teacher_list_to_str
    from everyclass.server.utils import lesson_string_to_dict
    from everyclass.server.utils import get_time_chinese
    from everyclass.server.utils import get_day_chinese
    from everyclass.server.utils import teacher_list_fix
    from .utils.rpc import HttpRpc

    with elasticapm.capture_span('rpc_query_course'):
        rpc_result = HttpRpc.call_with_error_page(
            '{}/v1/course/{}/{}'.format(app.config['API_SERVER_BASE_URL'],
                                        url_cid, url_semester),
            params={'week_string': 'true'},
            retry=True)
        if isinstance(rpc_result, str):
            return rpc_result
        api_response = rpc_result

    day, time = lesson_string_to_dict(api_response['lesson'])

    # student list
    students = list()
    for each in api_response['student']:
        students.append(
            [each['name'], each['sid'], each['deputy'], each['class']])

    # 给“文化素质类”等加上“课”后缀
    if api_response['type'] and api_response['type'][-1] != '课':
        api_response['type'] = api_response['type'] + '课'

    # 合班名称为数字时不展示合班名称
    show_heban = True
    if api_response['class'].isdigit():
        show_heban = False

    return render_template('query/course.html',
                           course_name=api_response['name'],
                           course_day=get_day_chinese(day),
                           course_time=get_time_chinese(time),
                           study_hour=api_response['hour'],
                           show_heban=show_heban,
                           heban_name=api_response['class'],
                           course_type=api_response['type'],
                           week=api_response['week_string'],
                           room=api_response['room'],
                           course_teacher=teacher_list_to_str(
                               teacher_list_fix(api_response['teacher'])),
                           students=students,
                           student_count=len(api_response['student']),
                           current_semester=url_semester)
예제 #12
0
def legacy_get_ics(student_id, semester_str):
    """
    legacy iCalendar endpoint

    query the student first, if the student is not privacy protected, redirect to new ics. else return 401.

    this route is bad. however, many users have already been using it. breaking experience is bad. so we have
    to keep the route here for now. and (maybe) remove it in the future.
    """
    from flask import current_app as app, abort, redirect, url_for

    from everyclass.server.db.dao import PrivacySettingsDAO, CalendarTokenDAO
    from everyclass.server.utils.rpc import HttpRpc
    from everyclass.server.db.model import Semester

    # fix parameters
    place = student_id.find('-')
    semester_str = student_id[place + 1:len(student_id)] + '-' + semester_str
    student_id = student_id[:place]

    semester = Semester(semester_str)

    with elasticapm.capture_span('rpc_search'):
        rpc_result = HttpRpc.call_with_error_page('{}/v1/search/{}'.format(
            app.config['API_SERVER_BASE_URL'], student_id),
                                                  retry=True)
        if isinstance(rpc_result, str):
            return rpc_result
        api_response = rpc_result

    if len(api_response['student']) != 1:
        # bad request
        return abort(400)

    if semester.to_str() not in api_response['student'][0]['semester']:
        return abort(400)

    with elasticapm.capture_span('get_privacy_settings'):
        privacy_settings = PrivacySettingsDAO.get_level(
            api_response['student'][0]['sid_orig'])

    if privacy_settings != 0:
        # force user to get a calendar token when the user is privacy-protected but accessed through legacy interface
        return "Visit {} to get your calendar".format(
            url_for("main.main")), 401
    else:
        token = CalendarTokenDAO.get_or_set_calendar_token(
            resource_type="student",
            identifier=api_response['student'][0]['sid_orig'],
            semester=semester.to_str())
        return redirect(url_for('calendar.ics_download', calendar_token=token))
예제 #13
0
def get_teacher(url_tid, url_semester):
    """老师查询"""
    from everyclass.server.utils import lesson_string_to_dict
    from everyclass.server.utils import semester_calculate
    from .utils.rpc import HttpRpc

    with elasticapm.capture_span('rpc_query_student'):
        rpc_result = HttpRpc.call_with_error_page('{}/v1/teacher/{}/{}'.format(
            app.config['API_SERVER_BASE_URL'], url_tid, url_semester),
                                                  params={
                                                      'week_string': 'true',
                                                      'other_semester': 'true'
                                                  },
                                                  retry=True)
        if isinstance(rpc_result, str):
            return rpc_result
        api_response = rpc_result

    with elasticapm.capture_span('process_rpc_result'):
        courses = dict()
        for each_class in api_response['course']:
            day, time = lesson_string_to_dict(each_class['lesson'])
            if (day, time) not in courses:
                courses[(day, time)] = list()
            courses[(day, time)].append(
                dict(name=each_class['name'],
                     week=each_class['week_string'],
                     classroom=each_class['room'],
                     classroom_id=each_class['rid'],
                     cid=each_class['cid']))

    empty_5, empty_6, empty_sat, empty_sun = _empty_column_check(courses)

    available_semesters = semester_calculate(
        url_semester, sorted(api_response['semester_list']))

    return render_template('query/teacher.html',
                           name=api_response['name'],
                           falculty=api_response['unit'],
                           title=api_response['title'],
                           tid=url_tid,
                           classes=courses,
                           empty_sat=empty_sat,
                           empty_sun=empty_sun,
                           empty_6=empty_6,
                           empty_5=empty_5,
                           available_semesters=available_semesters,
                           current_semester=url_semester)
예제 #14
0
def register_by_password():
    """学生注册-密码"""
    if request.method == 'POST':
        if any(map(lambda x: x not in request.form, ("password", "jwPassword"))) or not request.form["password"] or \
                not request.form["jwPassword"]:
            flash(MSG_EMPTY_PASSWORD)
            return redirect(url_for("user.register_by_password"))

        # 密码强度检查
        pwd_strength_report = zxcvbn(password=request.form["password"])
        if pwd_strength_report['score'] < 3:
            SimplePasswordDAO.new(password=request.form["password"],
                                  sid_orig=session[SESSION_LAST_VIEWED_STUDENT].sid_orig)
            flash(MSG_WEAK_PASSWORD)
            return redirect(url_for("user.register_by_password"))

        if not recaptcha.verify():
            flash(MSG_INVALID_CAPTCHA)
            return redirect(url_for("user.register_by_password"))

        request_id = IdentityVerificationDAO.new_register_request(session[SESSION_LAST_VIEWED_STUDENT].sid_orig,
                                                                  "password",
                                                                  ID_STATUS_WAIT_VERIFY,
                                                                  password=generate_password_hash(
                                                                          request.form["password"]))

        # call everyclass-auth to verify password
        with elasticapm.capture_span('rpc_submit_auth'):
            rpc_result = HttpRpc.call_with_error_page('{}/register_by_password'.format(app.config['AUTH_BASE_URL']),
                                                      data={'request_id': str(request_id),
                                                            'student_id': session[SESSION_LAST_VIEWED_STUDENT].sid_orig,
                                                            'password'  : request.form["jwPassword"]},
                                                      method='POST')
            if isinstance(rpc_result, str):
                return rpc_result
            api_response = rpc_result

        if api_response['acknowledged']:
            session[SESSION_VER_REQ_ID] = request_id
            return render_template('user/passwordRegistrationPending.html', request_id=request_id)
        else:
            return render_template('common/error.html', message=MSG_INTERNAL_ERROR)
    else:
        # show password registration page
        return render_template("user/passwordRegistration.html", name=session[SESSION_LAST_VIEWED_STUDENT].name)
예제 #15
0
def email_verification():
    """邮箱验证及注册"""
    if request.method == 'POST':
        # 设置密码表单提交
        if not session.get(SESSION_VER_REQ_ID, None):
            return render_template("common/error.html", message=MSG_400)

        req = IdentityVerificationDAO.get_request_by_id(session[SESSION_VER_REQ_ID])
        if not req:
            return render_template("common/error.html", message=MSG_TOKEN_INVALID)

        # 由于 SESSION_VER_REQ_ID 在密码验证和邮件验证两个验证方式中共享,当使用密码验证写入了 session 之后,如果马上在邮件验证页面
        # POST,并且此处不做请求状态的判断,将会绕过验证过程直接设置密码
        if req["status"] != ID_STATUS_TKN_PASSED:
            return render_template("common/error.html", message=MSG_TOKEN_INVALID)

        if not request.form.get("password", None):  # check if empty password
            flash(MSG_EMPTY_PASSWORD)
            return redirect(url_for("user.email_verification"))

        sid_orig = req['sid_orig']

        # 密码强度检查
        pwd_strength_report = zxcvbn(password=request.form["password"])
        if pwd_strength_report['score'] < 3:
            SimplePasswordDAO.new(password=request.form["password"], sid_orig=sid_orig)
            flash(MSG_WEAK_PASSWORD)
            return redirect(url_for("user.email_verification"))

        UserDAO.add_user(sid_orig=sid_orig, password=request.form['password'])
        del session[SESSION_VER_REQ_ID]
        IdentityVerificationDAO.set_request_status(str(req["request_id"]), ID_STATUS_PASSWORD_SET)
        flash(MSG_REGISTER_SUCCESS)

        # fetch student basic information from api-server
        with elasticapm.capture_span('rpc_get_student_info'):
            rpc_result = HttpRpc.call_with_error_page('{}/v1/search/{}'.format(app.config['API_SERVER_BASE_URL'],
                                                                               sid_orig), retry=True)
            if isinstance(rpc_result, str):
                return rpc_result
            api_response = rpc_result

        # 登录态写入 session
        session[SESSION_CURRENT_USER] = Student(sid_orig=api_response["student"][0]["sid_orig"],
                                                sid=api_response["student"][0]["sid"],
                                                name=api_response["student"][0]["name"])
        return redirect(url_for("user.main"))
    else:
        # 设置密码页面
        if not session.get(SESSION_VER_REQ_ID, None):
            if not request.args.get("token", None):
                return render_template("common/error.html", message=MSG_400)
            rpc_result = HttpRpc.call_with_error_page('{}/verify_email_token'.format(app.config['AUTH_BASE_URL']),
                                                      data={"email_token": request.args.get("token", None)},
                                                      method='POST',
                                                      retry=True)
            if isinstance(rpc_result, str):
                return rpc_result
            api_response = rpc_result

            if api_response['success']:
                session[SESSION_VER_REQ_ID] = api_response['request_id']
                IdentityVerificationDAO.set_request_status(api_response['request_id'], ID_STATUS_TKN_PASSED)
                return render_template('user/emailVerificationProceed.html')
            else:
                return render_template("common/error.html", message=MSG_TOKEN_INVALID)
        else:
            # have session
            return render_template('user/emailVerificationProceed.html')
예제 #16
0
def query():
    """
    All in one 搜索入口,可以查询学生、老师、教室,然后跳转到具体资源页面

    正常情况应该是 post 方法,但是也兼容 get 防止意外情况,提高用户体验

    埋点:
    - `query_resource_type`, 查询的资源类型: classroom, single_student, single_teacher, multiple_people, or not_exist.
    - `query_type`, 查询方式(姓名、学工号): by_name, by_id, other
    """
    import re
    from everyclass.server.utils.rpc import HttpRpc

    # if under maintenance, return to maintenance.html
    if app.config["MAINTENANCE"]:
        return render_template("maintenance.html")

    # transform upper case xh to lower case(currently api-server does not support upper case xh)
    to_search = request.values.get('id')

    if not to_search:
        flash('请输入需要查询的姓名、学号、教工号或教室名称')
        return redirect(url_for('main.main'))

    if re.match('^[A-Za-z0-9]*$', request.values.get('id')):
        to_search = to_search.lower()

    # add ‘座‘ since many users may search classroom in new campus without '座' and api-server doesn't not support
    if to_search[0] in ('a', 'b', 'c', 'd') and len(to_search) <= 5:
        to_search = to_search[0] + '座' + to_search[1:]

    # call api-server to search
    with elasticapm.capture_span('rpc_search'):
        rpc_result = HttpRpc.call_with_error_page('{}/v1/search/{}'.format(
            app.config['API_SERVER_BASE_URL'], to_search.replace("/", "")),
                                                  retry=True)
        if isinstance(rpc_result, str):
            return rpc_result
        api_response = rpc_result

    # render different template for different resource types
    if len(api_response['room']) >= 1:
        # classroom
        # we will use service name to filter apm document first, so it's not required to add service name prefix here
        elasticapm.tag(query_resource_type='classroom')
        elasticapm.tag(query_type='by_name')
        api_response['room'][0]['semester'].sort()
        return redirect('/classroom/{}/{}'.format(
            api_response['room'][0]['rid'],
            api_response['room'][0]['semester'][-1]))
    elif len(api_response['student']) == 1 and len(
            api_response['teacher']) == 0:
        # only one student
        elasticapm.tag(query_resource_type='single_student')
        if contains_chinese(to_search):
            elasticapm.tag(query_type='by_name')
        else:
            elasticapm.tag(query_type='by_id')
        if len(api_response['student'][0]['semester']) < 1:
            flash('没有可用学期')
            return redirect(url_for('main.main'))
        api_response['student'][0]['semester'].sort()
        return redirect('/student/{}/{}'.format(
            api_response['student'][0]['sid'],
            api_response['student'][0]['semester'][-1]))
    elif len(api_response['teacher']) == 1 and len(
            api_response['student']) == 0:
        # only one teacher
        elasticapm.tag(query_resource_type='single_teacher')
        if contains_chinese(to_search):
            elasticapm.tag(query_type='by_name')
        else:
            elasticapm.tag(query_type='by_id')
        if len(api_response['teacher'][0]['semester']) < 1:
            flash('没有可用学期')
            return redirect(url_for('main.main'))
        api_response['teacher'][0]['semester'].sort()
        return redirect('/teacher/{}/{}'.format(
            api_response['teacher'][0]['tid'],
            api_response['teacher'][0]['semester'][-1]))
    elif len(api_response['teacher']) >= 1 or len(
            api_response['student']) >= 1:
        # multiple students, multiple teachers, or mix of both
        elasticapm.tag(query_resource_type='multiple_people')
        if contains_chinese(to_search):
            elasticapm.tag(query_type='by_name')
        else:
            elasticapm.tag(query_type='by_id')
        return render_template('query/peopleWithSameName.html',
                               name=to_search,
                               students_count=len(api_response['student']),
                               students=api_response['student'],
                               teachers_count=len(api_response['teacher']),
                               teachers=api_response['teacher'])
    else:
        elasticapm.tag(query_resource_type='not_exist')
        elasticapm.tag(query_type='other')
        flash('没有找到任何有关 {} 的信息,如果你认为这不应该发生,请联系我们。'.format(
            escape(request.values.get('id'))))
        return redirect(url_for('main.main'))
예제 #17
0
def get_student(url_sid, url_semester):
    """学生查询"""
    from everyclass.server.db.dao import PrivacySettingsDAO, VisitorDAO
    from everyclass.server.utils import lesson_string_to_dict
    from everyclass.server.utils import teacher_list_fix
    from everyclass.server.utils import semester_calculate
    from everyclass.server.utils.rpc import HttpRpc
    from everyclass.server.consts import SESSION_LAST_VIEWED_STUDENT, SESSION_CURRENT_USER

    with elasticapm.capture_span('rpc_query_student'):
        rpc_result = HttpRpc.call_with_error_page('{}/v1/student/{}/{}'.format(
            app.config['API_SERVER_BASE_URL'], url_sid, url_semester),
                                                  params={
                                                      'week_string': 'true',
                                                      'other_semester': 'true'
                                                  },
                                                  retry=True)
        if isinstance(rpc_result, str):
            return rpc_result
        api_response = rpc_result

    # save sid_orig to session for verifying purpose
    # must be placed before privacy level check. Otherwise a registered user could be redirected to register page.
    session[SESSION_LAST_VIEWED_STUDENT] = Student(
        sid_orig=api_response['sid'], sid=url_sid, name=api_response['name'])

    # get privacy level, if current user has no permission to view, return now
    with elasticapm.capture_span('get_privacy_settings'):
        privacy_level = PrivacySettingsDAO.get_level(api_response['sid'])

    # 仅自己可见、且未登录或登录用户非在查看的用户,拒绝访问
    if privacy_level == 2 and (
            not session.get(SESSION_CURRENT_USER, None)
            or session[SESSION_CURRENT_USER].sid_orig != api_response['sid']):
        return render_template('query/studentBlocked.html',
                               name=api_response['name'],
                               falculty=api_response['deputy'],
                               class_name=api_response['class'],
                               sid=url_sid,
                               level=2)
    # 实名互访
    if privacy_level == 1:
        # 未登录,要求登录
        if not session.get(SESSION_CURRENT_USER, None):
            return render_template('query/studentBlocked.html',
                                   name=api_response['name'],
                                   falculty=api_response['deputy'],
                                   class_name=api_response['class'],
                                   sid=url_sid,
                                   level=1)
        # 仅自己可见的用户访问实名互访的用户,拒绝,要求调整自己的权限
        if PrivacySettingsDAO.get_level(
                session[SESSION_CURRENT_USER].sid_orig) == 2:
            return render_template('query/studentBlocked.html',
                                   name=api_response['name'],
                                   falculty=api_response['deputy'],
                                   class_name=api_response['class'],
                                   sid=url_sid,
                                   level=3)

    with elasticapm.capture_span('process_rpc_result'):
        courses = dict()
        for each_class in api_response['course']:
            day, time = lesson_string_to_dict(each_class['lesson'])
            if (day, time) not in courses:
                courses[(day, time)] = list()
            courses[(day, time)].append(
                dict(name=each_class['name'],
                     teacher=teacher_list_fix(each_class['teacher']),
                     week=each_class['week_string'],
                     classroom=each_class['room'],
                     classroom_id=each_class['rid'],
                     cid=each_class['cid']))
        empty_5, empty_6, empty_sat, empty_sun = _empty_column_check(courses)
        available_semesters = semester_calculate(
            url_semester, sorted(api_response['semester_list']))

    # 公开模式或实名互访模式,留下轨迹
    if privacy_level != 2 and \
            session.get(SESSION_CURRENT_USER, None) and \
            session[SESSION_CURRENT_USER] != session[SESSION_LAST_VIEWED_STUDENT]:
        VisitorDAO.update_track(host=api_response['sid'],
                                visitor=session[SESSION_CURRENT_USER])

    return render_template('query/student.html',
                           name=api_response['name'],
                           falculty=api_response['deputy'],
                           class_name=api_response['class'],
                           sid=url_sid,
                           classes=courses,
                           empty_sat=empty_sat,
                           empty_sun=empty_sun,
                           empty_6=empty_6,
                           empty_5=empty_5,
                           available_semesters=available_semesters,
                           current_semester=url_semester)