Example #1
0
def generate(name: str, courses: Dict[Tuple[int, int], List[Dict]],
             semester: Semester, ics_token: str):
    """
    生成 ics 文件并保存到目录

    :param name: 姓名
    :param courses: 参与的课程
    :param semester: 当前导出的学期
    :param ics_token: ics 令牌
    :return: None
    """
    semester_string = semester.to_str(simplify=True)
    semester = semester.to_tuple()

    # 创建 calender 对象
    cal = Calendar()
    cal.add('prodid', '-//Admirable//EveryClass//EN')
    cal.add('version', '2.0')
    cal.add('calscale', 'GREGORIAN')
    cal.add('method', 'PUBLISH')
    cal.add('X-WR-CALNAME', name + '的' + semester_string + '课表')
    cal.add('X-WR-TIMEZONE', 'Asia/Shanghai')

    # 时区
    tzc.add_component(tzs)
    cal.add_component(tzc)

    # 创建 events
    for time in range(1, 7):
        for day in range(1, 8):
            if (day, time) in courses:
                for course in courses[(day, time)]:
                    for week in course['week']:
                        dtstart = _get_datetime(week, day,
                                                get_time(time)[0], semester)
                        dtend = _get_datetime(week, day,
                                              get_time(time)[1], semester)

                        if dtstart.year == 1984:
                            continue

                        cal.add_component(
                            _build_event(course_name=course['name'],
                                         times=(dtstart, dtend),
                                         classroom=course['classroom'],
                                         teacher=course['teacher'],
                                         week_string=course['week_string'],
                                         current_week=week,
                                         cid=course['cid']))

    # 写入文件
    import os

    with open(
            os.path.join(os.path.dirname(__file__),
                         '../../../calendar_files/{}.ics'.format(ics_token)),
            'w') as f:
        f.write(cal.to_ical().decode(encoding='utf-8'))
Example #2
0
def get_classes_for_student(student_id: str, sem: Semester) -> dict:
    """
    获得一个学生在指定学期的全部课程
    如学生不存在当前学期则引出 NoStudentException

    :param student_id: 学号
    :param sem: 学期,`Semester` 类型对象
    :return: dict,键为 (day, time),值为课程列表,每一节课为一个包含了课程名称、老师、上课时间、地点信息的 dict
    """
    from everyclass.server.exceptions import NoStudentException, IllegalSemesterException

    db = get_connection()
    cursor = db.cursor()

    # 初步合法性检验
    if sem.to_tuple() not in app.config['AVAILABLE_SEMESTERS']:
        raise IllegalSemesterException('No such semester for the student')

    mysql_query = "SELECT classes FROM ec_students_" + sem.to_db_code(
    ) + " WHERE xh=%s"
    cursor.execute(mysql_query, (student_id, ))
    result = cursor.fetchall()
    if not result:
        cursor.close()
        raise NoStudentException(student_id)
    else:
        courses_list = json.loads(result[0][0])
        courses = dict()
        for classes in courses_list:
            mysql_query = "SELECT clsname,day,time,teacher,duration,week,location,id FROM {} WHERE id=%s" \
                .format("ec_classes_" + sem.to_db_code())
            cursor.execute(mysql_query, (classes, ))
            result = cursor.fetchall()
            if (result[0][1], result[0][2]) not in courses:
                courses[(result[0][1], result[0][2])] = list()
            courses[(result[0][1], result[0][2])].append(
                dict(name=result[0][0],
                     teacher=result[0][3],
                     duration=result[0][4],
                     week=result[0][5],
                     location=result[0][6],
                     id=result[0][7]))
        cursor.close()
        return courses
Example #3
0
def get_ics(student_id, semester_str):
    """
    iCalendar service
    """
    from everyclass.server.calendar import ics_generator
    from everyclass.server.db.dao import check_if_stu_exist, get_my_semesters, get_classes_for_student
    from everyclass.server.db.model import Semester
    from everyclass.server.exceptions import IllegalSemesterException
    from everyclass.server import logger

    # TODO: generate ics here and return it to user, instead of generating .ics files in other places.
    # 临时 fix
    place = student_id.find('-')
    semester_str = student_id[place + 1:len(student_id)] + '-' + semester_str
    student_id = student_id[:place]

    # 学号检测
    if not check_if_stu_exist(student_id):
        flash("{} 学号不存在".format(student_id))
        logger.warning("[ics] {} 学号不存在".format(student_id))
        return redirect(url_for("main.main"))

    # 学期检测
    my_available_semesters, student_name = get_my_semesters(student_id)
    try:
        semester = Semester(semester_str)
    except IllegalSemesterException:
        flash("{} 学期格式错误".format(semester_str))
        logger.warning("{} 学期格式错误".format(semester_str))
        return redirect(url_for("main.main"))
    if semester not in my_available_semesters:
        flash("{} 学期不适用于此学生".format(semester_str))
        logger.warning("{} 学期不适用于此学生".format(semester_str))
        return redirect(url_for("main.main"))

    student_classes = get_classes_for_student(student_id, semester)
    ics_generator.generate(student_id, student_name, student_classes,
                           semester.to_str(simplify=True), semester.to_tuple())

    return send_from_directory("../../calendar_files",
                               student_id + "-" + semester_str + ".ics",
                               as_attachment=True,
                               mimetype='text/calendar')
def batch_generate():
    """生成当前学期所有学生的 ics 文件,每次更新当前学期数据后使用"""
    from everyclass.server import create_app
    from everyclass.server.db.dao import get_all_students, get_classes_for_student
    from everyclass.server.db.model import Semester

    config = get_config()
    now_semester = Semester(config.DEFAULT_SEMESTER)
    now_semester_str = str(now_semester)

    with create_app(offline=True).app_context():
        students = get_all_students()
        print("Total {} students".format(len(students)))
        for each in students:
            if now_semester_str in each[2]:
                print("Generate .ics for [{}]{}...".format(each[0], each[1]))
                student_classes = get_classes_for_student(
                    each[0], now_semester)
                generate(student_id=each[0],
                         student_name=each[1],
                         student_classes=student_classes,
                         semester_string=now_semester.to_str(simplify=True),
                         semester=now_semester.to_tuple())
        print("Done.")
Example #5
0
def query():
    """
    查询本人课表视图函数
    正常情况应该是 post 方法,但是也兼容 get 防止意外情况,提高用户体验
    """
    from flask import request, render_template, redirect, url_for, session
    from flask import current_app as app
    import elasticapm

    from everyclass.server.tools import is_chinese_char
    from everyclass.server.exceptions import NoStudentException, IllegalSemesterException
    from everyclass.server.db.mysql import get_local_conn
    from everyclass.server.db.dao import faculty_lookup
    from everyclass.server.db.dao import class_lookup
    from everyclass.server.db.dao import get_classes_for_student
    from everyclass.server.db.model import Semester
    from everyclass.server.db.dao import get_privacy_settings
    from everyclass.server.db.dao import get_my_semesters
    from everyclass.server.db.dao import check_if_stu_exist

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

    db = get_local_conn()
    cursor = db.cursor()

    # 如 URL 中有 id 参数,判断是姓名还是学号,然后赋学号给student_id
    if request.values.get('id'):
        id_or_name = request.values.get('id')

        # 首末均为中文,判断为人名
        if is_chinese_char(id_or_name[0:1]) and is_chinese_char(
                id_or_name[-1:]):
            # 使用人名查询打点
            elasticapm.tag(ec_query_method='by_name')

            mysql_query = "SELECT name,xh FROM ec_students WHERE name=%s"
            cursor.execute(mysql_query, (id_or_name, ))
            result = cursor.fetchall()
            if cursor.rowcount > 1:
                # 查询到多个同名,进入选择界面
                students_list = list()
                for each_student in result:
                    students_list.append([
                        each_student[0], each_student[1],
                        faculty_lookup(each_student[1]),
                        class_lookup(each_student[1])
                    ])
                return render_template("query_same_name.html",
                                       count=cursor.rowcount,
                                       student_info=students_list)
            elif cursor.rowcount == 1:
                # 仅能查询到一个人,则赋值学号
                student_id = result[0][1]
            else:
                # 查无此人
                elasticapm.tag(ec_query_not_found=True)
                return _no_student_handle(id_or_name)

        # id 不为中文,则为学号
        else:
            # 学号查询打点
            elasticapm.tag(ec_query_method='by_id')
            student_id = request.values.get('id')

            # 判断学号是否有效
            if not check_if_stu_exist(student_id):
                elasticapm.tag(ec_query_not_found=True)
                return _no_student_handle(student_id)

        # 写入 session 的学号一定有效
        session['stu_id'] = student_id

    # url 中没有 id 参数但 session 中有
    elif session.get('stu_id', None):
        elasticapm.tag(ec_query_method='by_session')
        student_id = session['stu_id']

    # 既没有 id 参数也没有 session,无法知道需要查询谁的课表,返回主页
    else:
        elasticapm.tag(ec_query_method='exception')
        return redirect(url_for('main.main'))

    # 查询学生本人的可用学期
    my_available_semesters, student_name = get_my_semesters(student_id)

    # 如果没有学期,则直接返回
    if not my_available_semesters:
        logger.warning('Not any semester in ec_student', stack=True)
        return _no_student_handle()

    # 如URL参数中包含学期,判断有效性后更新 session
    if request.values.get('semester'):
        try:
            sem = Semester(request.values.get('semester'))
            if sem in my_available_semesters:
                session['semester'] = sem.to_tuple()
                if app.config['DEBUG']:
                    print('[query.query] updated session semester to',
                          Semester(session['semester']).to_str())

        # 用户指定的学期格式不合法
        except IllegalSemesterException:
            if app.config['DEBUG']:
                print('[query.query] IllegalSemesterException handled.' +
                      Semester(session['semester']).to_str())
            session['semester'] = my_available_semesters[-1].to_tuple()

    cursor.close()  # 关闭数据库连接

    # 如果 session 中无学期或学期无效,回落到本人可用最新学期
    # session 中学期使用 tuple 保存,因为 Semester 对象无法被序列化
    semester = session.get('semester', None)
    if not semester or Semester(semester) not in my_available_semesters:
        session['semester'] = my_available_semesters[-1].to_tuple()

    try:
        student_classes = get_classes_for_student(student_id=student_id,
                                                  sem=Semester(
                                                      session['semester']))
    except NoStudentException:
        return _no_student_handle(student_id)
    else:
        # 空闲周末判断,考虑到大多数人周末都是没有课程的
        empty_weekend = True
        for cls_time in range(1, 7):
            for cls_day in range(6, 8):
                if (cls_day, cls_time) in student_classes:
                    empty_weekend = False

        # 空闲课程判断,考虑到大多数人11-12节都是没有课程的
        empty_6 = True
        for cls_day in range(1, 8):
            if (cls_day, 6) in student_classes:
                empty_6 = False
        empty_5 = True
        for cls_day in range(1, 8):
            if (cls_day, 5) in student_classes:
                empty_5 = False

        # available_semesters 为当前学生所能选择的学期,是一个list。
        # 当中每一项又是一个包含两项的list,第一项为学期string,第二项为True/False表示是否为当前学期。
        available_semesters = []

        for each_semester in my_available_semesters:
            if session['semester'] == each_semester:
                available_semesters.append([each_semester, True])
            else:
                available_semesters.append([each_semester, False])

        # Privacy settings
        # Available privacy settings: "show_table_on_page", "import_to_calender", "major"
        privacy_settings = get_privacy_settings(student_id)

        # privacy on
        if "show_table_on_page" in privacy_settings:
            return render_template(
                'blocked.html',
                name=student_name,
                falculty=faculty_lookup(student_id),
                class_name=class_lookup(student_id),
                stu_id=student_id,
                available_semesters=available_semesters,
                no_import_to_calender=True
                if "import_to_calender" in privacy_settings else False)

        # privacy off
        return render_template('query.html',
                               name=student_name,
                               falculty=faculty_lookup(student_id),
                               class_name=class_lookup(student_id),
                               stu_id=student_id,
                               classes=student_classes,
                               empty_wkend=empty_weekend,
                               empty_6=empty_6,
                               empty_5=empty_5,
                               available_semesters=available_semesters)