示例#1
0
def get_student(url_sid: str, url_semester: str):
    """学生查询"""
    # decrypt identifier in URL
    try:
        _, student_id = decrypt(url_sid, resource_type='student')
    except ValueError:
        return render_template("common/error.html", message=MSG_INVALID_IDENTIFIER)

    if url_semester == URL_EMPTY_SEMESTER:
        try:
            student = entity_service.get_student(student_id)
        except Exception as e:
            return handle_exception_with_error_page(e)
    else:
        # RPC 获得学生课表
        try:
            student = entity_service.get_student_timetable(student_id, url_semester)
        except Exception as e:
            return handle_exception_with_error_page(e)

    # 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] = StudentSession(sid_orig=student.student_id,
                                                          sid=student.student_id_encoded,
                                                          name=student.name)

    # 权限检查,如果没有权限则返回
    has_permission, return_val = check_permission(student)
    if not has_permission:
        return return_val

    if url_semester != URL_EMPTY_SEMESTER:
        with tracer.trace('process_rpc_result'):
            cards: Dict[Tuple[int, int], List[Dict[str, str]]] = dict()
            for card in student.cards:
                day, time = lesson_string_to_tuple(card.lesson)
                if (day, time) not in cards:
                    cards[(day, time)] = list()
                cards[(day, time)].append(card)
            empty_5, empty_6, empty_sat, empty_sun = _empty_column_check(cards)
            available_semesters = semester_calculate(url_semester, sorted(student.semesters))

        return render_template('entity/student.html',
                               have_semesters=True,
                               student=student,
                               cards=cards,
                               empty_sat=empty_sat,
                               empty_sun=empty_sun,
                               empty_6=empty_6,
                               empty_5=empty_5,
                               available_semesters=available_semesters,
                               current_semester=url_semester)
    else:
        # 无学期
        return render_template('entity/student.html',
                               have_semesters=False,
                               student=student)
示例#2
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.
    """
    # 检查 URL 参数
    try:
        res_type, res_id = decrypt(identifier)
    except ValueError:
        return "Invalid identifier", 400
    if resource_type not in ('student',
                             'teacher') or resource_type != res_type:
        return "Unknown resource type", 400

    if resource_type == 'teacher':
        try:
            teacher = entity_service.get_teacher_timetable(res_id, semester)
        except Exception as e:
            return handle_exception_with_error_page(e)

        cal_token = calendar_service.get_calendar_token(
            resource_type=resource_type,
            identifier=teacher.teacher_id,
            semester=semester)
        return redirect(
            url_for('calendar.ics_download', calendar_token=cal_token))
    else:  # student
        try:
            student = entity_service.get_student_timetable(res_id, semester)
        except Exception as e:
            return handle_exception_with_error_page(e)

        with tracer.trace('get_privacy_settings'):
            privacy_level = user_service.get_privacy_level(student.student_id)

        # 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 user_service.check_password(username, password):
                return "Unauthorized (password wrong)", 401
            if student.student_id != username:
                return "Unauthorized (username mismatch)", 401

        cal_token = calendar_service.get_calendar_token(
            resource_type=resource_type,
            identifier=student.student_id,
            semester=semester)
        return redirect(
            url_for('calendar.ics_download', calendar_token=cal_token))
示例#3
0
def email_verification():
    """注册:邮箱验证"""
    if request.method == 'POST':
        # 设置密码表单提交
        if not session.get(SESSION_EMAIL_VER_REQ_ID, None):
            return render_template("common/error.html", message=MSG_400)
        if any(
                map(lambda x: not request.form.get(x, None),
                    ("password", "password2"))):  # check if empty password
            flash(MSG_EMPTY_PASSWORD)
            return redirect(url_for("user.email_verification"))
        if request.form["password"] != request.form["password2"]:
            flash(MSG_PWD_DIFFERENT)
            return redirect(url_for("user.email_verification"))

        try:
            identifier = user_service.register_by_email_set_password(
                session[SESSION_EMAIL_VER_REQ_ID], request.form["password"])
        except everyclass.server.user.exceptions.IdentityVerifyRequestNotFoundError:
            return render_template("common/error.html",
                                   message=MSG_TOKEN_INVALID)
        except everyclass.server.user.exceptions.PasswordTooWeakError:
            flash(MSG_WEAK_PASSWORD)
            return redirect(url_for("user.email_verification"))
        except everyclass.server.user.exceptions.AlreadyRegisteredError:
            flash(MSG_ALREADY_REGISTERED)
            return redirect(url_for("user.email_verification"))

        del session[SESSION_EMAIL_VER_REQ_ID]
        flash(MSG_REGISTER_SUCCESS)

        # 查询 entity 获得基本信息
        try:
            _set_current_user(identifier)
        except Exception as e:
            return handle_exception_with_error_page(e)
        return redirect(url_for("user.main"))
    else:
        if not request.args.get("token", None) and session.get(
                SESSION_EMAIL_VER_REQ_ID, None):
            return render_template("common/error.html", message=MSG_400)
        if request.args.get("token", None):
            try:
                request_id = user_service.register_by_email_token_check(
                    request.args.get("token"))
            except Exception as e:
                return handle_exception_with_error_page(e)

            session[SESSION_EMAIL_VER_REQ_ID] = request_id
        return render_template('user/emailVerificationProceed.html')
示例#4
0
def get_teacher(url_tid, url_semester):
    """老师查询"""

    # decrypt identifier in URL
    try:
        _, teacher_id = decrypt(url_tid, resource_type='teacher')
    except ValueError:
        return render_template("common/error.html", message=MSG_INVALID_IDENTIFIER)

    if url_semester == URL_EMPTY_SEMESTER:
        # RPC to get teacher timetable
        try:
            teacher = entity_service.get_teacher(teacher_id)
        except Exception as e:
            return handle_exception_with_error_page(e)
    else:
        # RPC to get teacher timetable
        try:
            teacher = entity_service.get_teacher_timetable(teacher_id, url_semester)
        except Exception as e:
            return handle_exception_with_error_page(e)

    if url_semester != URL_EMPTY_SEMESTER:
        with tracer.trace('process_rpc_result'):
            cards = defaultdict(list)
            for card in teacher.cards:
                day, time = lesson_string_to_tuple(card.lesson)
                if (day, time) not in cards:
                    cards[(day, time)] = list()
                cards[(day, time)].append(card)

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

        available_semesters = semester_calculate(url_semester, teacher.semesters)

        return render_template('entity/teacher.html',
                               have_semesters=True,
                               teacher=teacher,
                               cards=cards,
                               empty_sat=empty_sat,
                               empty_sun=empty_sun,
                               empty_6=empty_6,
                               empty_5=empty_5,
                               available_semesters=available_semesters,
                               current_semester=url_semester)
    else:
        # 无学期
        return render_template('entity/teacher.html',
                               have_semesters=False,
                               teacher=teacher)
示例#5
0
def cal_page(url_res_type: str, url_res_identifier: str, url_semester: str):
    """课表导出页面视图函数"""
    # 检查 URL 参数
    try:
        res_type, res_id = decrypt(url_res_identifier)
    except ValueError:
        return render_template("common/error.html",
                               message=MSG_INVALID_IDENTIFIER)
    if url_res_type not in ('student', 'teacher') or url_res_type != res_type:
        return render_template("common/error.html", message=MSG_400)

    if url_res_type == 'student':
        try:
            student = entity_service.get_student_timetable(
                res_id, url_semester)
        except Exception as e:
            return handle_exception_with_error_page(e)

        # 权限检查,如果没有权限则返回
        has_permission, return_val = check_permission(student)
        if not has_permission:
            return return_val

        token = calendar_service.get_calendar_token(
            resource_type=url_res_type,
            identifier=student.student_id,
            semester=url_semester)
    else:
        try:
            teacher = entity_service.get_teacher_timetable(
                res_id, url_semester)
        except Exception as e:
            return handle_exception_with_error_page(e)

        token = calendar_service.get_calendar_token(
            resource_type=url_res_type,
            identifier=teacher.teacher_id,
            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'])
示例#6
0
def register():
    """注册:第一步:输入学号或教工号"""
    if request.method == 'GET':
        return render_template('user/register.html')
    else:
        if not request.form.get("xh", None):  # 表单为空
            flash(MSG_EMPTY_USERNAME)
            return redirect(url_for("user.register"))
        # todo: change frontend to tell users that teachers can register now
        # 检查学号/教工号是否存在
        try:
            entity_service.get_people_info(request.form.get("xh", None))
        except entity_service.PeopleNotFoundError:
            flash(MSG_USERNAME_NOT_EXIST)
            return redirect(url_for("user.register"))
        except Exception as e:
            return handle_exception_with_error_page(e)

        r = _set_current_registering(request.form.get("xh", None))
        if r:
            return r

        # 如果输入的学号或教工号已经注册,跳转到登录页面
        if user_service.user_exist(
                session[SESSION_USER_REGISTERING].identifier):
            flash(MSG_ALREADY_REGISTERED)
            return redirect(url_for('user.login'))

        return redirect(url_for('user.register_choice'))
示例#7
0
def main():
    """用户主页"""
    try:
        is_student, student = entity_service.get_people_info(
            session[SESSION_CURRENT_USER].identifier)
        if not is_student:
            return "Teacher is not supported at the moment. Stay tuned!"
    except Exception as e:
        return handle_exception_with_error_page(e)

    pending_grant_reqs = user_service.get_pending_requests(
        session[SESSION_CURRENT_USER].identifier)
    pending_grant_names = []
    for req in pending_grant_reqs:
        pending_grant_names.append(
            entity_service.get_people_info(req.user_id)[1].name)

    return render_template(
        'user/main.html',
        name=session[SESSION_CURRENT_USER].name,
        student_id_encoded=session[SESSION_CURRENT_USER].identifier_encoded,
        last_semester=student.semesters[-1] if student.semesters else None,
        privacy_level=user_service.get_privacy_level(
            session[SESSION_CURRENT_USER].identifier),
        pending_grant_names=pending_grant_names)
示例#8
0
def get_card(url_cid: str, url_semester: str):
    """课程查询"""
    # decrypt identifier in URL
    try:
        _, card_id = decrypt(url_cid, resource_type='klass')
    except ValueError:
        return render_template("common/error.html", message=MSG_INVALID_IDENTIFIER)

    # RPC to get card
    try:
        card = entity_service.get_card(url_semester, card_id)
    except Exception as e:
        return handle_exception_with_error_page(e)

    day, time = lesson_string_to_tuple(card.lesson)

    # cotc_id = COTeachingClass.get_id_by_card(card)
    # course_review_doc = CourseReview.get_review(cotc_id)

    return render_template('entity/card.html',
                           card=card,
                           card_day=get_day_chinese(day),
                           card_time=get_time_chinese(time),
                           # cotc_id=cotc_id,
                           # cotc_rating=course_review_doc["avg_rate"],
                           cotc_id=0,
                           cotc_rating=0,
                           current_semester=url_semester
                           )
示例#9
0
def get_classroom(url_rid, url_semester):
    """教室查询"""
    # decrypt identifier in URL
    try:
        _, room_id = decrypt(url_rid, resource_type='room')
    except ValueError:
        return render_template("common/error.html", message=MSG_INVALID_IDENTIFIER)
    # todo 支持没有学期的room
    # RPC to get classroom timetable
    try:
        room = entity_service.get_classroom_timetable(url_semester, room_id)
    except Exception as e:
        return handle_exception_with_error_page(e)

    with tracer.trace('process_rpc_result'):
        cards = defaultdict(list)
        for card in room.cards:
            day, time = lesson_string_to_tuple(card.lesson)
            cards[(day, time)].append(card)

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

    available_semesters = semester_calculate(url_semester, room.semesters)

    return render_template('entity/room.html',
                           room=room,
                           cards=cards,
                           empty_sat=empty_sat,
                           empty_sun=empty_sun,
                           empty_6=empty_6,
                           empty_5=empty_5,
                           available_semesters=available_semesters,
                           current_semester=url_semester)
示例#10
0
def register_by_email():
    """注册:第三步:使用邮箱验证注册"""
    if not session.get(SESSION_USER_REGISTERING, None):  # 步骤异常,跳回第一步
        return redirect(url_for('user.register'))

    identifier = session[SESSION_USER_REGISTERING].identifier

    try:
        request_id = user_service.register_by_email(identifier)
    except Exception as e:
        return handle_exception_with_error_page(e)
    else:
        return render_template('user/emailSent.html', request_id=request_id)
示例#11
0
def edit_review(cotc_id: int):
    """进行评价"""
    cotc_id = int(cotc_id)
    cotc = COTeachingClass.get_doc(cotc_id)
    if not cotc:
        return render_template('common/error.html', message=MSG_404)

    if not is_taking(cotc):
        return render_template('common/error.html', message=MSG_NOT_IN_COURSE)

    if request.method == 'GET':  # 展示表单页面
        doc = CourseReview.get_my_review(cotc_id=cotc_id, student_id=session[SESSION_CURRENT_USER].identifier)  # 已经评分
        if doc:
            my_rating = doc["rate"]
            my_review = doc["review"]
        else:
            my_rating = 0
            my_review = ""
        return render_template("course/add_review.html",
                               cotc=cotc,
                               my_rating=my_rating,
                               my_review=my_review)
    else:  # 表单提交
        if not request.form.get("rate", None) or request.form["rate"] not in map(str, (1, 2, 3, 4, 5)):
            flash("请填写正确的评分")
            return redirect(url_for("course.edit_review", cotc_id=cotc_id))
        if not request.form.get("review", None):
            flash("请填写评价")
            return redirect(url_for("course.edit_review", cotc_id=cotc_id))
        if len(request.form["review"]) > 200:
            flash("评论不要超过200个字符")
            return redirect(url_for("course.edit_review", cotc_id=cotc_id))

        try:
            student = entity_service.get_student(session[SESSION_CURRENT_USER].identifier)
        except Exception as e:
            return handle_exception_with_error_page(e)

        fuzzy_name = student.klass + "学生"

        CourseReview.edit_my_review(cotc_id,
                                    session[SESSION_CURRENT_USER].identifier,
                                    int(request.form["rate"]),
                                    escape(request.form["review"]),
                                    fuzzy_name)
        flash("评分成功。")
        return redirect(url_for("course.show_review", cotc_id=cotc_id))
示例#12
0
def _set_current_registering(identifier: str):
    """
    将正在注册的用户并保存到 SESSION_USER_REGISTERING

    :param identifier: 学号或教工号
    :return: None if there is no exception. Otherwise return an error page.
    """
    with tracer.trace('rpc_get_student'):
        try:
            is_student, people = entity_service.get_people_info(identifier)
            session[SESSION_USER_REGISTERING] = UserSession(
                user_type=USER_TYPE_STUDENT
                if is_student else USER_TYPE_TEACHER,
                identifier=people.student_id
                if is_student else people.teacher_id,
                identifier_encoded=people.student_id_encoded
                if is_student else people.teacher_id_encoded,
                name=people.name)
        except Exception as e:
            return handle_exception_with_error_page(e)
示例#13
0
def android_client_get_semester(identifier):
    """android client get a student or teacher's semesters
    """
    try:
        search_result = entity_service.search(identifier)
    except Exception as e:
        return handle_exception_with_error_page(e)

    if len(search_result.students) == 1:
        return jsonify({
            'type': 'student',
            'sid': search_result.students[0].student_id_encoded,
            'semesters': search_result.students[0].semesters
        })
    if len(search_result.teachers) == 1:
        return jsonify({
            'type': 'teacher',
            'tid': search_result.teachers[0].teacher_id_encoded,
            'semesters': search_result.teachers[0].semesters
        })
    return "Bad request (got multiple people)", 400
示例#14
0
def register_by_password():
    """注册:第三步:使用密码验证注册"""
    if not session.get(SESSION_USER_REGISTERING, None):
        return render_template('common/error.html',
                               message=MSG_VIEW_SCHEDULE_FIRST)

    if request.method == 'POST':
        if any(
                map(lambda x: not request.form.get(x, None),
                    ("password", "password2", "jwPassword"))):
            flash(MSG_EMPTY_PASSWORD)
            return redirect(url_for("user.register_by_password"))
        if request.form["password"] != request.form["password2"]:
            flash(MSG_PWD_DIFFERENT)
            return redirect(url_for("user.register_by_password"))
        # captcha
        if not TencentCaptcha.verify_old():
            flash(MSG_INVALID_CAPTCHA)
            return redirect(url_for("user.register_by_password"))

        try:
            request_id = user_service.register_by_password(
                request.form["jwPassword"], request.form["password"],
                session.get(SESSION_USER_REGISTERING, None).identifier)
        except everyclass.server.user.exceptions.PasswordTooWeakError:
            flash(MSG_WEAK_PASSWORD)
            return redirect(url_for("user.register_by_password"))
        except Exception as e:
            return handle_exception_with_error_page(e)

        session[SESSION_PWD_VER_REQ_ID] = request_id
        return render_template('user/passwordRegistrationPending.html',
                               request_id=request_id)
    else:
        # show password registration page
        return render_template("user/passwordRegistration.html",
                               name=session[SESSION_USER_REGISTERING].name)
示例#15
0
def login():
    """
    登录页

    判断学生是否未注册,若已经注册,渲染登录页。否则跳转到注册页面。
    """
    if request.method == 'GET':
        if session.get(SESSION_LAST_VIEWED_STUDENT, None):
            user_name = session[SESSION_LAST_VIEWED_STUDENT].name
        else:
            user_name = None

        return render_template('user/login.html', name=user_name)
    else:  # 表单提交
        if not request.form.get("password", None):
            flash(MSG_EMPTY_PASSWORD)
            return redirect(url_for("user.login"))

        # captcha
        if not TencentCaptcha.verify_old():
            flash(MSG_INVALID_CAPTCHA)
            return redirect(url_for("user.login"))

        if request.form.get("xh", None):  # 已手动填写用户名
            identifier = request.form["xh"]

            # 检查学号/教工号是否存在
            try:
                entity_service.get_people_info(identifier)
            except entity_service.PeopleNotFoundError:
                flash(MSG_USERNAME_NOT_EXIST)
                return redirect(url_for("user.login"))
            except Exception as e:
                return handle_exception_with_error_page(e)

        else:
            if session.get(SESSION_LAST_VIEWED_STUDENT, None):
                identifier = session[
                    SESSION_LAST_VIEWED_STUDENT].sid_orig  # 没有手动填写,使用获取最后浏览的学生
            else:
                flash(MSG_EMPTY_USERNAME)  # 没有最后浏览的学生,必须填写用户名
                return redirect(url_for("user.login"))

        try:
            success = user_service.check_password(identifier,
                                                  request.form["password"])
        except everyclass.server.user.exceptions.UserNotExists:
            # 未注册
            flash(MSG_NOT_REGISTERED)
            return redirect(url_for("user.register"))

        if success:
            try:
                _set_current_user(identifier)
            except Exception as e:
                return handle_exception_with_error_page(e)

            return redirect(url_for("user.main"))
        else:
            flash(MSG_WRONG_PASSWORD)
            return redirect(url_for("user.login"))
示例#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
    """

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

    keyword = request.values.get('id')

    # todo 简拼积分制
    if not keyword or len(keyword) < 2:
        flash('请输入需要查询的姓名、学号、教工号或教室名称,长度不要小于2个字符')
        return redirect(url_for('main.main'))

    # 调用 api-server 搜索
    with tracer.trace('rpc_search'):
        try:
            rpc_result = entity_service.search(keyword)
        except Exception as e:
            return handle_exception_with_error_page(e)

    # 不同类型渲染不同模板
    if len(rpc_result.classrooms) >= 1:  # 优先展示教室
        # 我们在 kibana 中使用服务名过滤 apm 文档,所以 tag 不用增加服务名前缀
        tracer.current_root_span().set_tag("query_resource_type", "classroom")
        tracer.current_root_span().set_tag("query_type", "by_name")

        if len(rpc_result.classrooms) > 1:  # 多个教室选择
            return render_template('entity/multipleClassroomChoice.html',
                                   name=keyword,
                                   classrooms=rpc_result.classrooms)
        return redirect(
            url_for("query.get_classroom",
                    url_rid=rpc_result.classrooms[0].room_id_encoded,
                    url_semester=rpc_result.classrooms[0].semesters[-1] if rpc_result.classrooms[0].semesters else URL_EMPTY_SEMESTER))
    elif len(rpc_result.students) == 1 and len(rpc_result.teachers) == 0:  # 一个学生
        tracer.current_root_span().set_tag("query_resource_type", "single_student")
        if contains_chinese(keyword):
            tracer.current_root_span().set_tag("query_type", "by_name")
        else:
            tracer.current_root_span().set_tag("query_type", "by_id")

        if len(rpc_result.students[0].semesters) < 1:
            flash('没有可用学期')
            return redirect(url_for('main.main'))

        return redirect(
            url_for("query.get_student",
                    url_sid=rpc_result.students[0].student_id_encoded,
                    url_semester=rpc_result.students[0].semesters[-1] if rpc_result.students[0].semesters else URL_EMPTY_SEMESTER))
    elif len(rpc_result.teachers) == 1 and len(rpc_result.students) == 0:  # 一个老师
        tracer.current_root_span().set_tag("query_resource_type", "single_teacher")
        if contains_chinese(keyword):
            tracer.current_root_span().set_tag("query_type", "by_name")
        else:
            tracer.current_root_span().set_tag("query_type", "by_id")

        if len(rpc_result.teachers[0].semesters) < 1:
            flash('没有可用学期')
            return redirect(url_for('main.main'))

        return redirect(
            url_for("query.get_teacher",
                    url_tid=rpc_result.teachers[0].teacher_id_encoded,
                    url_semester=rpc_result.teachers[0].semesters[-1] if rpc_result.teachers[0].semesters else URL_EMPTY_SEMESTER))
    elif len(rpc_result.teachers) >= 1 or len(rpc_result.students) >= 1:
        # multiple students, multiple teachers, or mix of both
        tracer.current_root_span().set_tag("query_resource_type", "multiple_people")

        if contains_chinese(keyword):
            tracer.current_root_span().set_tag("query_type", "by_name")

        else:
            tracer.current_root_span().set_tag("query_type", "by_id")

        students = []
        student_filtered = False
        for student in rpc_result.students:
            if len(student.klass) >= 6 and student.klass[-4:].isdigit() and int(student.klass[-4:-2]) <= 12:
                # 过滤<13级的学生
                student_filtered = True
                continue
            students.append(student)
        # todo 找校友功能
        return render_template('entity/peopleWithSameName.html',
                               name=keyword,
                               students=students,
                               teachers=rpc_result.teachers,
                               student_filtered=student_filtered)
    else:
        logger.info("No result for user search", extra={"keyword": request.values.get('id')})
        tracer.current_root_span().set_tag("query_resource_type", "not_exist")
        tracer.current_root_span().set_tag("query_type", "other")

        flash('没有找到任何有关 {} 的信息,如果你认为这不应该发生,请联系我们。'.format(escape(request.values.get('id'))))
        return redirect(url_for('main.main'))