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
    with elasticapm.capture_span('rpc_get_card'):
        try:
            card = APIServer.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)

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

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

    return render_template(
        'query/card.html',
        card=card,
        card_day=get_day_chinese(day),
        card_time=get_time_chinese(time),
        show_union_class=not card.union_name.isdigit(),  # 合班名称为数字时不展示合班名称
        cotc_id=cotc_id,
        cotc_rating=course_review_doc["avg_rate"],
        current_semester=url_semester)
Beispiel #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.
    """
    from flask import redirect, url_for, request

    from everyclass.server.db.dao import PrivacySettings, CalendarToken, User
    from everyclass.server.rpc.api_server import APIServer
    from everyclass.server.utils.resource_identifier_encrypt import decrypt

    # 检查 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 = APIServer.get_teacher_timetable(res_id, semester)
        except Exception as e:
            return handle_exception_with_error_page(e)

        cal_token = CalendarToken.get_or_set_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 = APIServer.get_student_timetable(res_id, semester)
        except Exception as e:
            return handle_exception_with_error_page(e)

        with elasticapm.capture_span('get_privacy_settings'):
            privacy_level = PrivacySettings.get_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.check_password(username, password):
                return "Unauthorized (password wrong)", 401
            if student.student_id != username:
                return "Unauthorized (username mismatch)", 401

        cal_token = CalendarToken.get_or_set_calendar_token(
            resource_type=resource_type,
            identifier=student.student_id,
            semester=semester)
        return redirect(
            url_for('calendar.ics_download', calendar_token=cal_token))
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)

    # RPC to get classroom timetable
    with elasticapm.capture_span('rpc_get_classroom_timetable'):
        try:
            room = APIServer.get_classroom_timetable(url_semester, room_id)
        except Exception as e:
            return handle_exception_with_error_page(e)

    with elasticapm.capture_span('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('query/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)
Beispiel #4
0
def cal_page(url_res_type: str, url_res_identifier: str, url_semester: str):
    """课表导出页面视图函数"""
    from flask import current_app as app, render_template, url_for, session

    from everyclass.server.db.dao import CalendarToken, PrivacySettings
    from everyclass.server.consts import MSG_400, SESSION_CURRENT_USER, MSG_401
    from everyclass.server.rpc.api_server import APIServer
    from everyclass.server.utils.resource_identifier_encrypt import decrypt
    from everyclass.server.consts import MSG_INVALID_IDENTIFIER

    # 检查 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 = APIServer.get_student_timetable(res_id, url_semester)
        except Exception as e:
            return handle_exception_with_error_page(e)

        # 检查是否有权限访问日历订阅页面
        with elasticapm.capture_span('get_privacy_settings'):
            privacy_level = PrivacySettings.get_level(student.student_id)
        if privacy_level != 0:
            if not (session.get(SESSION_CURRENT_USER, None)
                    and session[SESSION_CURRENT_USER].sid_orig
                    == student.student_id):
                return render_template("common/error.html", message=MSG_401)

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

        token = CalendarToken.get_or_set_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'])
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)

    # RPC 获得学生课表
    with elasticapm.capture_span('rpc_get_student_timetable'):
        try:
            student = APIServer.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

    with elasticapm.capture_span('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))

    # 增加访客记录
    Redis.add_visitor_count(student.student_id,
                            session.get(SESSION_CURRENT_USER, None))

    return render_template('query/student.html',
                           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)
Beispiel #6
0
def cal_page(url_res_type: str, url_res_identifier: str, url_semester: str):
    """课表导出页面视图函数"""
    from flask import current_app as app, render_template, url_for

    from everyclass.server.db.dao import CalendarToken
    from everyclass.server.consts import MSG_400
    from everyclass.server.rpc.api_server import APIServer
    from everyclass.server.utils.resource_identifier_encrypt import decrypt
    from everyclass.server.consts import MSG_INVALID_IDENTIFIER

    # 检查 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 = APIServer.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 = CalendarToken.get_or_set_calendar_token(resource_type=url_res_type,
                                                        identifier=student.student_id,
                                                        semester=url_semester)
    else:
        try:
            teacher = APIServer.get_teacher_timetable(res_id, url_semester)
        except Exception as e:
            return handle_exception_with_error_page(e)

        token = CalendarToken.get_or_set_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'])
Beispiel #7
0
def get_teacher(url_tid, url_semester):
    """老师查询"""
    from collections import defaultdict

    from everyclass.server.utils import lesson_string_to_tuple
    from everyclass.server.utils import semester_calculate
    from everyclass.server.rpc.api_server import APIServer
    from everyclass.server.utils.resource_identifier_encrypt import decrypt
    from everyclass.server.consts import MSG_INVALID_IDENTIFIER

    # 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)

    # RPC to get teacher timetable
    with elasticapm.capture_span('rpc_get_teacher_timetable'):
        try:
            teacher = APIServer.get_teacher_timetable(teacher_id, url_semester)
        except Exception as e:
            return handle_exception_with_error_page(e)

    with elasticapm.capture_span('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('query/teacher.html',
                           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)
Beispiel #8
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)

    # RPC to get teacher timetable
    with tracer.trace('rpc_get_teacher_timetable'):
        try:
            teacher = Entity.get_teacher_timetable(teacher_id, url_semester)
        except Exception as e:
            return handle_exception_with_error_page(e)

    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('query/teacher.html',
                           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)
Beispiel #9
0
def get_student(url_sid: str, url_semester: str):
    """学生查询"""
    from everyclass.server.db.dao import PrivacySettings, VisitTrack, Redis
    from everyclass.server.utils import lesson_string_to_tuple
    from everyclass.server.rpc.api_server import APIServer
    from everyclass.server.utils.resource_identifier_encrypt import decrypt
    from everyclass.server.consts import MSG_INVALID_IDENTIFIER
    from everyclass.server.utils import semester_calculate
    from everyclass.server.consts import SESSION_LAST_VIEWED_STUDENT, SESSION_CURRENT_USER

    # 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)

    # RPC to get student timetable
    with elasticapm.capture_span('rpc_get_student_timetable'):
        try:
            student = APIServer.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)

    # get privacy level, if current user has no permission to view, return now
    with elasticapm.capture_span('get_privacy_settings'):
        privacy_level = PrivacySettings.get_level(student.student_id)

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

    with elasticapm.capture_span('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))

    # 公开或实名互访模式、已登录、不是自己访问自己,则留下轨迹
    if privacy_level != 2 and \
            session.get(SESSION_CURRENT_USER, None) and \
            session[SESSION_CURRENT_USER].sid_orig != session[SESSION_LAST_VIEWED_STUDENT].sid_orig:
        VisitTrack.update_track(host=student.student_id,
                                visitor=session[SESSION_CURRENT_USER])

    # 增加访客记录
    Redis.add_visitor_count(student.student_id,
                            session.get(SESSION_CURRENT_USER, None))

    return render_template('query/student.html',
                           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)
Beispiel #10
0
 def test_decrypt(self):
     from everyclass.server.utils.resource_identifier_encrypt import decrypt
     for tp, data, encrypted in self.cases:
         self.assertTrue(
             decrypt(encrypted, encryption_key=self.key, resource_type=tp)
             == (tp, data))