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.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 = Entity.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 = 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 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.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 = Entity.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_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)
def ics_download(calendar_token: str): """ iCalendar ics 文件下载 2019-8-25 改为预先缓存文件而非每次动态生成,降低 CPU 压力。如果一小时内两次访问则强刷缓存。 """ import time from everyclass.server import statsd if not is_valid_uuid(calendar_token): return 'invalid calendar token', 404 result = CalendarToken.find_calendar_token(token=calendar_token) if not result: return 'invalid calendar token', 404 CalendarToken.update_last_used_time(calendar_token) cal_dir = calendar_dir() cal_filename = f"{result['type']}_{result['identifier']}_{result['semester']}.ics" cal_full_path = os.path.join(cal_dir, cal_filename) # 有缓存、且缓存时间小于一天,且不用强刷缓存 if os.path.exists(cal_full_path) \ and time.time() - os.path.getmtime(cal_full_path) < SECONDS_IN_ONE_DAY \ and Redis.calendar_token_use_cache(calendar_token): logger.info("ics cache hit") statsd.increment("calendar.ics.cache.hit") return send_from_directory(cal_dir, cal_filename, as_attachment=True, mimetype='text/calendar') statsd.increment("calendar.ics.cache.miss") # 无缓存、或需要强刷缓存 with tracer.trace('rpc'): # 获得原始学号或教工号 if result['type'] == 'student': rpc_result = Entity.get_student_timetable(result['identifier'], result['semester']) else: # teacher rpc_result = Entity.get_teacher_timetable(result['identifier'], result['semester']) semester = Semester(result['semester']) cards: Dict[Tuple[int, int], List[Dict]] = defaultdict(list) for card in rpc_result.cards: cards[lesson_string_to_tuple(card.lesson)].append( dict(name=card.name, teacher=teacher_list_to_name_str(card.teachers), week=card.weeks, week_string=card.week_string, classroom=card.room, cid=card.card_id_encoded)) ics_generator.generate(name=rpc_result.name, cards=cards, semester=semester, filename=cal_filename) return send_from_directory(cal_dir, cal_filename, as_attachment=True, mimetype='text/calendar')
def get_teacher_timetable(teacher_id: str, semester: str): return Entity.get_teacher_timetable(teacher_id, semester)