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))
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'))
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.")