def search(cls, keyword: str) -> SearchResult: """在 API Server 上搜索 :param keyword: 需要搜索的关键词 :return: 搜索结果列表 """ keyword = keyword.replace("/", "") resp = HttpRpc.call( method="GET", url='{}/search/query?key={}&page_size={}'.format( app.config['API_SERVER_BASE_URL'], keyword, 100), retry=True, headers={'X-Auth-Token': get_config().API_SERVER_TOKEN}) if resp["status"] != "success": raise RpcException('API Server returns non-success status') page_num = resp['info']['page_num'] search_result = SearchResult.make(resp) # 多页结果 if page_num > 1: for page_index in range(2, page_num + 1): resp = HttpRpc.call( method="GET", url='{}/search/query?key={}&page_size={}&page_index={}'. format(app.config['API_SERVER_BASE_URL'], keyword, 100, page_index), retry=True, headers={'X-Auth-Token': get_config().API_SERVER_TOKEN}) if resp["status"] != "success": raise RpcException('API Server returns non-success status') search_result.append(resp) return search_result
def _get_datetime(week: int, day: int, time: Tuple[int, int], semester: Tuple[int, int, int]) -> datetime: """ 根据学期、周次、时间,生成 `datetime` 类型的时间 :param week: 周次 :param day: 星期 :param time: 时间tuple(时,分) :param semester: 学期 :return: datetime 类型的时间 """ config = get_config() tz = pytz.timezone("Asia/Shanghai") dt = datetime(*(config.AVAILABLE_SEMESTERS[semester]['start'] + time), tzinfo=tz) # noqa: T484 dt += timedelta(days=(week - 1) * 7 + (day - 1)) # 调整到当前周 if 'adjustments' in config.AVAILABLE_SEMESTERS[semester]: ymd = (dt.year, dt.month, dt.day) adjustments = config.AVAILABLE_SEMESTERS[semester]['adjustments'] if ymd in adjustments: if adjustments[ymd]['to']: # 调课 dt = dt.replace(year=adjustments[ymd]['to'][0], month=adjustments[ymd]['to'][1], day=adjustments[ymd]['to'][2]) else: # 冲掉的课年份设置为1984,返回之后被抹去 dt = dt.replace(year=1984) return dt
def get_level(cls, student_id: str) -> int: with pg_conn_context() as conn, conn.cursor() as cursor: select_query = "SELECT level FROM privacy_settings WHERE student_id=%s" cursor.execute(select_query, (student_id, )) result = cursor.fetchone() return result[0] if result is not None else get_config( ).DEFAULT_PRIVACY_LEVEL
def get_level(cls, sid_orig: str) -> int: """获得学生的隐私级别。0为公开,1为实名互访,2为自己可见。默认为配置文件中定义的 DEFAULT_PRIVACY_LEVEL""" db = get_mongodb() doc = mongo_with_retry(db[cls.collection_name].find_one, {"sid_orig": sid_orig}, num_retries=1) return doc["level"] if doc else get_config().DEFAULT_PRIVACY_LEVEL
def plugin_available(plugin_name: str) -> bool: """ check if a plugin (Sentry, apm, logstash) is available in the current environment. :return True if available else False """ config = get_config() mode = os.environ.get("MODE", None) if mode: return mode.lower() in getattr( config, "{}_AVAILABLE_IN".format(plugin_name).upper()) else: raise EnvironmentError("MODE not in environment variables")
def __get_datetime(week, day, time, semester): """ 输入周次,星期、时间tuple(时,分),输出datetime类型的时间 :param week: 周次 :param day: 星期 :param time: 时间tuple(时,分) :param semester: 学期 :return: datetime 类型的时间 """ return datetime(*get_config().AVAILABLE_SEMESTERS.get(semester)['start'], *time, tzinfo=pytz.timezone("Asia/Shanghai")) + timedelta( days=(int(week) - 1) * 7 + (int(day) - 1))
def enter_maintenance(): config = get_config() auth = request.authorization if auth \ and auth.username in config.MAINTENANCE_CREDENTIALS \ and config.MAINTENANCE_CREDENTIALS[auth.username] == auth.password: open(config.MAINTENANCE_FILE, "w+").close() # maintenance file open(os.path.join(os.getcwd(), 'reload'), "w+").close() # uwsgi reload return 'success' else: return Response( 'Could not verify your access level for that URL.\n' 'You have to login with proper credentials', 401, {'WWW-Authenticate': 'Basic realm="Login Required"'})
def encrypt(resource_type: str, data: str, encryption_key: str = None) -> Text: """ 加密资源标识符 :param resource_type: student、teacher、klass、room :param data: 资源标识符 :param encryption_key: 加密使用的 key :return: 加密后的资源标识符 """ if resource_type not in ('student', 'teacher', 'klass', 'room'): raise ValueError("resource_type not valid") if not encryption_key: encryption_key = get_config().RESOURCE_IDENTIFIER_ENCRYPTION_KEY return _aes_encrypt(encryption_key, "%s;%s" % (resource_type, data))
def get_student(cls, student_id: str): """ 根据学号获得学生课表 :param student_id: 学号 :return: """ resp = HttpRpc.call( method="GET", url='{}/student/{}'.format(app.config['API_SERVER_BASE_URL'], student_id), retry=True, headers={'X-Auth-Token': get_config().API_SERVER_TOKEN}) if resp["status"] != "success": raise RpcException('API Server returns non-success status') search_result = StudentResult.make(resp) return search_result
def get_classroom_timetable(cls, semester: str, room_id: str): """ 根据学期和教室ID获得教室课表 :param semester: 学期,如 2018-2019-1 :param room_id: 教室ID :return: """ resp = HttpRpc.call( method="GET", url='{}/room/{}/timetable/{}'.format( app.config['API_SERVER_BASE_URL'], room_id, semester), retry=True, headers={'X-Auth-Token': get_config().API_SERVER_TOKEN}) if resp["status"] != "success": raise RpcException('API Server returns non-success status') search_result = ClassroomTimetableResult.make(resp) return search_result
def get_card(cls, semester: str, card_id: str) -> CardResult: """ 根据学期和card ID获得card :param semester: 学期,如 2018-2019-1 :param card_id: card ID :return: """ resp = HttpRpc.call( method="GET", url='{}/card/{}/timetable/{}'.format( app.config['API_SERVER_BASE_URL'], card_id, semester), retry=True, headers={'X-Auth-Token': get_config().API_SERVER_TOKEN}) if resp["status"] != "success": raise RpcException('API Server returns non-success status') search_result = CardResult.make(resp) return search_result
def decrypt(data: str, encryption_key: str = None, resource_type: str = None): """ 解密资源标识符 :param data: 加密后的字符串 :param encryption_key: 可选的 key :param resource_type: 验证资源类型(student、teacher、klass、room) :return: """ if not encryption_key: encryption_key = get_config().RESOURCE_IDENTIFIER_ENCRYPTION_KEY data = _aes_decrypt(encryption_key, data) group = re.match(r'^(student|teacher|klass|room);([\s\S]+)$', data) # 通过正则校验确定数据的正确性 if group is None: raise ValueError('Decrypted data is invalid: %s' % data) else: if resource_type and group.group(1) != resource_type: raise ValueError('Resource type not correspond') return group.group(1), group.group(2)
def get(): """ 获取当前学期。进入此模块前必须保证 session 内有 stu_id。 当 url 中没有显式表明 semester 时,不设置 session,而是在这里设置默认值。 """ from everyclass.server.exceptions import IllegalSemesterException from everyclass.server.config import get_config config = get_config() my_available_semesters = get_my_semesters(session.get('stu_id'))[0] if config.DEBUG: print('[model.Semester.get()] my_available_semesters:', my_available_semesters) # 如果 session 中包含学期信息且有效 if session.get('semester', None) and session.get( 'semester', None) in my_available_semesters: if config.DEBUG: print('[model.Semester.get()]have valid session') return Semester(session['semester']) # 如果没有 session或session无效 else: if config.DEBUG: print('[model.Semester.get()] no session or invalid session') # 选择对本人有效的最后一个学期 if my_available_semesters: if config.DEBUG: print( '[model.Semester.get()] choose last available semester' ) return my_available_semesters[-1] # 如果本人没有一个有效学期,则引出IllegalSemesterException else: raise IllegalSemesterException( 'No any available semester for this student')
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.")
def get_connection() -> database.Database: """在连接池中获得连接""" if not has_app_context(): config = get_config() return MongoClient(**config.MONGODB).get_database(config.MONGODB_DB) return current_app.mongo.get_database(current_app.config['MONGODB_DB'])
def create_app() -> Flask: """创建 flask app""" from everyclass.server.db.dao import new_user_id_sequence from everyclass.server.consts import MSG_INTERNAL_ERROR from everyclass.server.utils import plugin_available app = Flask(__name__, static_folder='../../frontend/dist', static_url_path='', template_folder="../../frontend/templates") # load app config from everyclass.server.config import get_config _config = get_config() app.config.from_object(_config) # noqa: T484 """ 每课统一日志机制 规则如下: - DEBUG 模式下会输出 DEBUG 等级的日志,否则输出 INFO 及以上等级的日志 - 日志为 JSON 格式,会被节点上的 agent 采集并发送到 datadog,方便结合 metrics 和 APM 数据分析 - WARNING 以上级别的输出到 Sentry 做错误聚合 日志等级: critical – for errors that lead to termination error – for errors that occur, but are handled warning – for exceptional circumstances that might not be errors notice – for non-error messages you usually want to see info – for messages you usually don’t want to see debug – for debug messages Sentry: https://docs.sentry.io/clients/python/api/#raven.Client.captureMessage - stack 默认是 False """ if app.config['DEBUG']: logging.getLogger().setLevel(logging.DEBUG) else: logging.getLogger().setLevel(logging.INFO) # CDN CDN(app) # moment Moment(app) # encrypted session app.session_interface = EncryptedSessionInterface() # 导入并注册 blueprints from everyclass.server.calendar.views import cal_blueprint from everyclass.server.query import query_blueprint from everyclass.server.views import main_blueprint as main_blueprint from everyclass.server.user.views import user_bp from everyclass.server.course_review.views import cr_blueprint app.register_blueprint(cal_blueprint) app.register_blueprint(query_blueprint) app.register_blueprint(main_blueprint) app.register_blueprint(user_bp, url_prefix='/user') # 初始化 RPC 模块 from everyclass.server.utils.resource_identifier_encrypt import encrypt from everyclass.rpc import init as init_rpc from everyclass.rpc.entity import Entity from everyclass.rpc.auth import Auth init_rpc(resource_id_encrypt_function=encrypt ) # 为 everyclass.rpc 模块注入 encrypt 函数 if 'API_SERVER_BASE_URL' in app.config: Entity.set_base_url(app.config['API_SERVER_BASE_URL']) if 'API_SERVER_TOKEN' in app.config: Entity.set_request_token(app.config['API_SERVER_TOKEN']) if 'AUTH_BASE_URL' in app.config: Auth.set_base_url(app.config['AUTH_BASE_URL']) # course review feature gating if app.config['FEATURE_GATING']['course_review']: app.register_blueprint(cr_blueprint, url_prefix='/course_review') @app.before_request def set_user_id(): """在请求之前设置 session uid,方便 APM 标识用户""" from everyclass.server.consts import SESSION_CURRENT_USER if not session.get('user_id', None) and request.endpoint not in ( "main.health_check", "static"): logger.info( f"Give a new user ID for new user. endpoint: {request.endpoint}" ) session['user_id'] = new_user_id_sequence() if session.get('user_id', None): tracer.current_root_span().set_tag("user_id", session['user_id']) # 唯一用户 ID if session.get(SESSION_CURRENT_USER, None): tracer.current_root_span().set_tag( "username", session[SESSION_CURRENT_USER]) # 学号 @app.before_request def log_request(): """日志中记录请求""" logger.info(f'Request received: {request.method} {request.path}') @app.before_request def delete_old_session(): """删除旧的客户端 session(长度过长导致无法在 mongodb 中建立索引)""" if request.cookies.get("session") and len( request.cookies.get("session")) > 50: session.clear() return redirect(request.url) @app.after_request def response_minify(response): """用 htmlmin 压缩 HTML,减轻带宽压力""" if app.config[ 'HTML_MINIFY'] and response.content_type == u'text/html; charset=utf-8': response.set_data(minify(response.get_data(as_text=True))) return response @app.template_filter('versioned') def version_filter(filename): """ 模板过滤器。如果 STATIC_VERSIONED,返回类似 'style-v1-c012dr.css' 的文件,而不是 'style-v1.css' :param filename: 文件名 :return: 新的文件名 """ if app.config['STATIC_VERSIONED']: if filename[:4] == 'css/': new_filename = app.config['STATIC_MANIFEST'][filename[4:]] return 'css/' + new_filename elif filename[:3] == 'js/': new_filename = app.config['STATIC_MANIFEST'][filename[3:]] return new_filename else: return app.config['STATIC_MANIFEST'][filename] return filename @app.errorhandler(500) def internal_server_error(error): if plugin_available("sentry"): return render_template( 'common/error.html', message=MSG_INTERNAL_ERROR, event_id=g.sentry_event_id, public_dsn=sentry.client.get_public_dsn('https')) return "<h4>500 Error: {}</h4><br>You are seeing this page because Sentry is not available.".format( repr(error)) global __app __app = app return app
def test_import_config(self): from everyclass.server.config import get_config config = get_config() self.assertTrue(config)
def create_app() -> Flask: """创建 flask app""" from everyclass.server.db.dao import new_user_id_sequence from everyclass.server.utils.logbook_logstash.formatter import LOG_FORMAT_STRING from everyclass.server.consts import MSG_INTERNAL_ERROR from everyclass.server.utils import plugin_available print("Creating app...") app = Flask(__name__, static_folder='../../frontend/dist', static_url_path='', template_folder="../../frontend/templates") # load app config from everyclass.server.config import get_config _config = get_config() app.config.from_object(_config) # noqa: T484 """ 每课统一日志机制 规则如下: - WARNING 以下 log 输出到 stdout - WARNING 以上输出到 stderr - DEBUG 以上日志以 json 形式通过 TCP 输出到 Logstash,然后发送到日志中心 - WARNING 以上级别的输出到 Sentry 日志等级: critical – for errors that lead to termination error – for errors that occur, but are handled warning – for exceptional circumstances that might not be errors notice – for non-error messages you usually want to see info – for messages you usually don’t want to see debug – for debug messages Sentry: https://docs.sentry.io/clients/python/api/#raven.Client.captureMessage - stack 默认是 False """ if app.config['CONFIG_NAME'] in app.config['DEBUG_LOG_AVAILABLE_IN']: stdout_handler = logbook.StreamHandler( stream=sys.stdout, bubble=True, filter=lambda r, h: r.level < 13) else: # ignore debug when not in debug stdout_handler = logbook.StreamHandler( stream=sys.stdout, bubble=True, filter=lambda r, h: 10 < r.level < 13) stdout_handler.format_string = LOG_FORMAT_STRING logger.handlers.append(stdout_handler) stderr_handler = logbook.StreamHandler(stream=sys.stderr, bubble=True, level='WARNING') stderr_handler.format_string = LOG_FORMAT_STRING logger.handlers.append(stderr_handler) # CDN CDN(app) # moment Moment(app) # 导入并注册 blueprints from everyclass.server.calendar.views import cal_blueprint from everyclass.server.query import query_blueprint from everyclass.server.views import main_blueprint as main_blueprint from everyclass.server.user.views import user_bp from everyclass.server.course_review.views import cr_blueprint app.register_blueprint(cal_blueprint) app.register_blueprint(query_blueprint) app.register_blueprint(main_blueprint) app.register_blueprint(user_bp, url_prefix='/user') # course review feature gating if app.config['FEATURE_GATING']['course_review']: app.register_blueprint(cr_blueprint, url_prefix='/course_review') @app.before_request def set_user_id(): """在请求之前设置 session uid,方便 Elastic APM 记录用户请求""" if not session.get('user_id', None) and request.endpoint != "main.health_check": session['user_id'] = new_user_id_sequence() @app.before_request def delete_old_session(): """删除旧的客户端 session(长度过长导致无法在 mongodb 中建立索引)""" if request.cookies.get("session") and len( request.cookies.get("session")) > 50: session.clear() return redirect(request.url) @app.after_request def response_minify(response): """用 htmlmin 压缩 HTML,减轻带宽压力""" if app.config[ 'HTML_MINIFY'] and response.content_type == u'text/html; charset=utf-8': response.set_data(minify(response.get_data(as_text=True))) return response @app.template_filter('versioned') def version_filter(filename): """ 模板过滤器。如果 STATIC_VERSIONED,返回类似 'style-v1-c012dr.css' 的文件,而不是 'style-v1.css' :param filename: 文件名 :return: 新的文件名 """ if app.config['STATIC_VERSIONED']: if filename[:4] == 'css/': new_filename = app.config['STATIC_MANIFEST'][filename[4:]] return 'css/' + new_filename elif filename[:3] == 'js/': new_filename = app.config['STATIC_MANIFEST'][filename[3:]] return new_filename else: return app.config['STATIC_MANIFEST'][filename] return filename @app.errorhandler(500) def internal_server_error(error): if plugin_available("sentry"): return render_template( 'common/error.html', message=MSG_INTERNAL_ERROR, event_id=g.sentry_event_id, public_dsn=sentry.client.get_public_dsn('https')) return "<h4>500 Error: {}</h4><br>You are seeing this page because Sentry is not available.".format( repr(error)) global __app __app = app return app
def create_app(offline=False) -> Flask: """创建 flask app @param offline: 如果设置为 `True`,则为离线模式。此模式下不会连接到 Sentry 和 ElasticAPM """ app = Flask(__name__, static_folder='../../frontend/dist', static_url_path='', template_folder="../../frontend/templates") # load app config from everyclass.server.config import get_config _config = get_config() app.config.from_object(_config) # CDN CDN(app) # logbook handlers # 规则如下: # - 全部输出到 stdout(本地开发调试、服务器端文件日志) # - Elastic APM 或者 LogStash(日志中心) # - WARNING 以上级别的输出到 Sentry # # 日志等级: # critical – for errors that lead to termination # error – for errors that occur, but are handled # warning – for exceptional circumstances that might not be errors # notice – for non-error messages you usually want to see # info – for messages you usually don’t want to see # debug – for debug messages # # Elastic APM: # log.info("Nothing to see here", stack=False) # stack 默认是 True,设置为 False 将不会向 Elastic APM 发送 stack trace # https://discuss.elastic.co/t/how-to-use-logbook-handler/146209/6 # # Sentry: # https://docs.sentry.io/clients/python/api/#raven.Client.captureMessage # - stack 默认是 False,和 Elastic APM 的不一致,所以还是每次手动指定吧... # - 默认事件类型是 `raven.events.Message`,设置 `exc_info` 为 `True` 将把事件类型升级为`raven.events.Exception` stderr_handler = logbook.StderrHandler(bubble=True) logger.handlers.append(stderr_handler) if not offline: # Sentry sentry.init_app(app=app) sentry_handler = SentryHandler( sentry.client, level='WARNING') # Sentry 只处理 WARNING 以上的 logger.handlers.append(sentry_handler) # Elastic APM apm = ElasticAPM(app) elastic_handler = ElasticHandler(client=apm.client, bubble=True) logger.handlers.append(elastic_handler) # 初始化数据库 if os.getenv('MODE', None) != "CI": init_pool(app) # 导入并注册 blueprints from everyclass.server.calendar.views import cal_blueprint from everyclass.server.query import query_blueprint from everyclass.server.views import main_blueprint as main_blueprint from everyclass.server.api import api_v1 as api_blueprint app.register_blueprint(cal_blueprint) app.register_blueprint(query_blueprint) app.register_blueprint(main_blueprint) app.register_blueprint(api_blueprint, url_prefix='/api/v1') @app.before_request def set_user_id(): """在请求之前设置 session uid,方便 Elastic APM 记录用户请求""" if not session.get('user_id', None): # 数据库中生成唯一 ID,参考 https://blog.csdn.net/longjef/article/details/53117354 conn = get_local_conn() cursor = conn.cursor() cursor.execute( "REPLACE INTO user_id_sequence (stub) VALUES ('a');") session['user_id'] = cursor.lastrowid cursor.close() @app.teardown_request def close_db(error): """结束时关闭数据库连接""" if hasattr(g, 'mysql_db') and g.mysql_db: g.mysql_db.close() @app.after_request def response_minify(response): """用 htmlmin 压缩 HTML,减轻带宽压力""" if app.config[ 'HTML_MINIFY'] and response.content_type == u'text/html; charset=utf-8': response.set_data(minify(response.get_data(as_text=True))) return response @app.template_filter('versioned') def version_filter(filename): """ 模板过滤器。如果 STATIC_VERSIONED,返回类似 'style-v1-c012dr.css' 的文件,而不是 'style-v1.css' :param filename: 文件名 :return: 新的文件名 """ if app.config['STATIC_VERSIONED']: if filename[:4] == 'css/': new_filename = app.config['STATIC_MANIFEST'][filename[4:]] return 'css/' + new_filename elif filename[:3] == 'js/': new_filename = app.config['STATIC_MANIFEST'][filename[3:]] return new_filename else: return app.config['STATIC_MANIFEST'][filename] return filename @app.errorhandler(500) def internal_server_error(error): return render_template( '500.html', event_id=g.sentry_event_id, public_dsn=sentry.client.get_public_dsn('https')) logger.info('App created with `{}` config'.format( app.config['CONFIG_NAME']), stack=False) return app
def get_connection(): """获得单个 Connection""" import pymysql from everyclass.server.config import get_config config = get_config() return pymysql.Connection(config['MYSQL_CONFIG'])
def create_app(outside_container=False) -> Flask: """创建 flask app @param outside_container: 是否不在容器内运行 """ from everyclass.server.db.dao import new_user_id_sequence from everyclass.server.db.mysql import init_pool from everyclass.server.utils.logbook_logstash.formatter import LOG_FORMAT_STRING app = Flask(__name__, static_folder='../../frontend/dist', static_url_path='', template_folder="../../frontend/templates") # load app config from everyclass.server.config import get_config _config = get_config() app.config.from_object(_config) """ 每课统一日志机制 规则如下: - WARNING 以下 log 输出到 stdout - WARNING 以上输出到 stderr - DEBUG 以上日志以 json 形式通过 TCP 输出到 Logstash,然后发送到日志中心 - WARNING 以上级别的输出到 Sentry 日志等级: critical – for errors that lead to termination error – for errors that occur, but are handled warning – for exceptional circumstances that might not be errors notice – for non-error messages you usually want to see info – for messages you usually don’t want to see debug – for debug messages Sentry: https://docs.sentry.io/clients/python/api/#raven.Client.captureMessage - stack 默认是 False """ stdout_handler = logbook.StreamHandler(stream=sys.stdout, bubble=True, filter=lambda r, h: r.level < 13) stdout_handler.format_string = LOG_FORMAT_STRING logger.handlers.append(stdout_handler) stderr_handler = logbook.StreamHandler(stream=sys.stderr, bubble=True, level='WARNING') stderr_handler.format_string = LOG_FORMAT_STRING logger.handlers.append(stderr_handler) # CDN CDN(app) # 容器外运行(无 uWSGI)时初始化数据库 if outside_container and (app.config['CONFIG_NAME'] == "development"): init_pool(app) # 导入并注册 blueprints from everyclass.server.calendar.views import cal_blueprint from everyclass.server.query import query_blueprint from everyclass.server.views import main_blueprint as main_blueprint from everyclass.server.api import api_v1 as api_blueprint app.register_blueprint(cal_blueprint) app.register_blueprint(query_blueprint) app.register_blueprint(main_blueprint) app.register_blueprint(api_blueprint, url_prefix='/api/v1') @app.before_request def set_user_id(): """在请求之前设置 session uid,方便 Elastic APM 记录用户请求""" if not session.get('user_id', None): session['user_id'] = new_user_id_sequence() @app.after_request def response_minify(response): """用 htmlmin 压缩 HTML,减轻带宽压力""" if app.config[ 'HTML_MINIFY'] and response.content_type == u'text/html; charset=utf-8': response.set_data(minify(response.get_data(as_text=True))) return response @app.template_filter('versioned') def version_filter(filename): """ 模板过滤器。如果 STATIC_VERSIONED,返回类似 'style-v1-c012dr.css' 的文件,而不是 'style-v1.css' :param filename: 文件名 :return: 新的文件名 """ if app.config['STATIC_VERSIONED']: if filename[:4] == 'css/': new_filename = app.config['STATIC_MANIFEST'][filename[4:]] return 'css/' + new_filename elif filename[:3] == 'js/': new_filename = app.config['STATIC_MANIFEST'][filename[3:]] return new_filename else: return app.config['STATIC_MANIFEST'][filename] return filename @app.errorhandler(500) def internal_server_error(error): return render_template( '500.html', event_id=g.sentry_event_id, public_dsn=sentry.client.get_public_dsn('https')) global __app __app = app return app
def get_connection() -> MongoClient: """在连接池中获得连接""" if not has_app_context(): config = get_config() return MongoClient(**config.MONGODB)[config.MONGODB_DB] return current_app.mongo[current_app.config['MONGODB_DB']]
import redis from everyclass.server.config import get_config config = get_config() redis = redis.Redis(**config.REDIS)
def wrapped(*args, **kwargs): config = get_config() if config.MAINTENANCE: return render_template('maintenance.html') return func(*args, **kwargs)