Spaces:
Running
Running
| [ | |
| { | |
| "label": "os", | |
| "kind": 6, | |
| "isExtraImport": true, | |
| "importPath": "os", | |
| "description": "os", | |
| "detail": "os", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "sys", | |
| "kind": 6, | |
| "isExtraImport": true, | |
| "importPath": "sys", | |
| "description": "sys", | |
| "detail": "sys", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "json", | |
| "kind": 6, | |
| "isExtraImport": true, | |
| "importPath": "json", | |
| "description": "json", | |
| "detail": "json", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "time", | |
| "kind": 6, | |
| "isExtraImport": true, | |
| "importPath": "time", | |
| "description": "time", | |
| "detail": "time", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "datetime", | |
| "importPath": "datetime", | |
| "description": "datetime", | |
| "isExtraImport": true, | |
| "detail": "datetime", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "datetime", | |
| "importPath": "datetime", | |
| "description": "datetime", | |
| "isExtraImport": true, | |
| "detail": "datetime", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "Path", | |
| "importPath": "pathlib", | |
| "description": "pathlib", | |
| "isExtraImport": true, | |
| "detail": "pathlib", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "Path", | |
| "importPath": "pathlib", | |
| "description": "pathlib", | |
| "isExtraImport": true, | |
| "detail": "pathlib", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "Path", | |
| "importPath": "pathlib", | |
| "description": "pathlib", | |
| "isExtraImport": true, | |
| "detail": "pathlib", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "Path", | |
| "importPath": "pathlib", | |
| "description": "pathlib", | |
| "isExtraImport": true, | |
| "detail": "pathlib", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "Path", | |
| "importPath": "pathlib", | |
| "description": "pathlib", | |
| "isExtraImport": true, | |
| "detail": "pathlib", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "Path", | |
| "importPath": "pathlib", | |
| "description": "pathlib", | |
| "isExtraImport": true, | |
| "detail": "pathlib", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "Path", | |
| "importPath": "pathlib", | |
| "description": "pathlib", | |
| "isExtraImport": true, | |
| "detail": "pathlib", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "Flask", | |
| "importPath": "flask", | |
| "description": "flask", | |
| "isExtraImport": true, | |
| "detail": "flask", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "render_template", | |
| "importPath": "flask", | |
| "description": "flask", | |
| "isExtraImport": true, | |
| "detail": "flask", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "send_from_directory", | |
| "importPath": "flask", | |
| "description": "flask", | |
| "isExtraImport": true, | |
| "detail": "flask", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "jsonify", | |
| "importPath": "flask", | |
| "description": "flask", | |
| "isExtraImport": true, | |
| "detail": "flask", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "request", | |
| "importPath": "flask", | |
| "description": "flask", | |
| "isExtraImport": true, | |
| "detail": "flask", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "session", | |
| "importPath": "flask", | |
| "description": "flask", | |
| "isExtraImport": true, | |
| "detail": "flask", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "CORS", | |
| "importPath": "flask_cors", | |
| "description": "flask_cors", | |
| "isExtraImport": true, | |
| "detail": "flask_cors", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "logging", | |
| "kind": 6, | |
| "isExtraImport": true, | |
| "importPath": "logging", | |
| "description": "logging", | |
| "detail": "logging", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "RotatingFileHandler", | |
| "importPath": "logging.handlers", | |
| "description": "logging.handlers", | |
| "isExtraImport": true, | |
| "detail": "logging.handlers", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "get_db_instance", | |
| "importPath": "db_manager", | |
| "description": "db_manager", | |
| "isExtraImport": true, | |
| "detail": "db_manager", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "re", | |
| "kind": 6, | |
| "isExtraImport": true, | |
| "importPath": "re", | |
| "description": "re", | |
| "detail": "re", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "subprocess", | |
| "kind": 6, | |
| "isExtraImport": true, | |
| "importPath": "subprocess", | |
| "description": "subprocess", | |
| "detail": "subprocess", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "urllib.parse", | |
| "kind": 6, | |
| "isExtraImport": true, | |
| "importPath": "urllib.parse", | |
| "description": "urllib.parse", | |
| "detail": "urllib.parse", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "urlparse", | |
| "importPath": "urllib.parse", | |
| "description": "urllib.parse", | |
| "isExtraImport": true, | |
| "detail": "urllib.parse", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "unquote", | |
| "importPath": "urllib.parse", | |
| "description": "urllib.parse", | |
| "isExtraImport": true, | |
| "detail": "urllib.parse", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "List", | |
| "importPath": "typing", | |
| "description": "typing", | |
| "isExtraImport": true, | |
| "detail": "typing", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "Dict", | |
| "importPath": "typing", | |
| "description": "typing", | |
| "isExtraImport": true, | |
| "detail": "typing", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "Set", | |
| "importPath": "typing", | |
| "description": "typing", | |
| "isExtraImport": true, | |
| "detail": "typing", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "Dict", | |
| "importPath": "typing", | |
| "description": "typing", | |
| "isExtraImport": true, | |
| "detail": "typing", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "List", | |
| "importPath": "typing", | |
| "description": "typing", | |
| "isExtraImport": true, | |
| "detail": "typing", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "Optional", | |
| "importPath": "typing", | |
| "description": "typing", | |
| "isExtraImport": true, | |
| "detail": "typing", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "Tuple", | |
| "importPath": "typing", | |
| "description": "typing", | |
| "isExtraImport": true, | |
| "detail": "typing", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "hashlib", | |
| "kind": 6, | |
| "isExtraImport": true, | |
| "importPath": "hashlib", | |
| "description": "hashlib", | |
| "detail": "hashlib", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "sqlite3", | |
| "kind": 6, | |
| "isExtraImport": true, | |
| "importPath": "sqlite3", | |
| "description": "sqlite3", | |
| "detail": "sqlite3", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "requests", | |
| "kind": 6, | |
| "isExtraImport": true, | |
| "importPath": "requests", | |
| "description": "requests", | |
| "detail": "requests", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "multiprocessing", | |
| "kind": 6, | |
| "isExtraImport": true, | |
| "importPath": "multiprocessing", | |
| "description": "multiprocessing", | |
| "detail": "multiprocessing", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "OpenSearch", | |
| "importPath": "opensearchpy", | |
| "description": "opensearchpy", | |
| "isExtraImport": true, | |
| "detail": "opensearchpy", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "RequestsHttpConnection", | |
| "importPath": "opensearchpy", | |
| "description": "opensearchpy", | |
| "isExtraImport": true, | |
| "detail": "opensearchpy", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "OpenSearchException", | |
| "importPath": "opensearchpy.exceptions", | |
| "description": "opensearchpy.exceptions", | |
| "isExtraImport": true, | |
| "detail": "opensearchpy.exceptions", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "setup_logging", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def setup_logging():\n \"\"\"配置日志系统\"\"\"\n if not app.debug:\n # 创建日志目录\n log_dir = Path('logs')\n log_dir.mkdir(exist_ok=True)\n # 文件日志处理器(每个文件最大10MB,保留10个备份)\n file_handler = RotatingFileHandler(\n log_dir / 'app.log',\n maxBytes=10 * 1024 * 1024,", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "get_client_ip", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def get_client_ip():\n \"\"\"\n 获取客户端真实 IP 地址\n 在代理服务器(如 Hugging Face Spaces)后面时,需要检查代理头部\n \"\"\"\n # 按优先级检查各种代理头部\n headers_to_check = [\n 'X-Forwarded-For',\n 'X-Real-IP',\n 'CF-Connecting-IP', # Cloudflare", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "load_book_data", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def load_book_data():\n \"\"\"\n 加载书籍数据(已废弃,保留是为了兼容性)\n 现在使用数据库接口,不再加载JSON文件\n \"\"\"\n global BOOK_DATA\n app.logger.info('ℹ️ load_book_data 已废弃,使用数据库接口')\n # 设置为空字典表示已初始化,但不再使用\n BOOK_DATA = {}\n return True", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "init_database", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def init_database():\n \"\"\"初始化数据库连接\"\"\"\n global DB\n try:\n DB = get_db_instance('books.db')\n app.logger.info('✅ 数据库连接初始化成功')\n return True\n except Exception as e:\n app.logger.error(f'❌ 数据库初始化失败: {e}')\n return False", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "index", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def index():\n \"\"\"主页 - 书籍目录\"\"\"\n return send_from_directory('.', 'index.html')\n@app.route('/reader')\ndef reader():\n \"\"\"阅读页面\"\"\"\n return send_from_directory('.', 'reader.html')\n@app.route('/<path:filename>')\ndef serve_static(filename):\n \"\"\"提供根目录下的静态文件(如 style.css, script.js)\"\"\"", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "reader", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def reader():\n \"\"\"阅读页面\"\"\"\n return send_from_directory('.', 'reader.html')\n@app.route('/<path:filename>')\ndef serve_static(filename):\n \"\"\"提供根目录下的静态文件(如 style.css, script.js)\"\"\"\n # 避免与API路由冲突\n if filename.startswith('api/'):\n return jsonify({'error': '接口不存在'}), 404\n return send_from_directory('.', filename)", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "serve_static", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def serve_static(filename):\n \"\"\"提供根目录下的静态文件(如 style.css, script.js)\"\"\"\n # 避免与API路由冲突\n if filename.startswith('api/'):\n return jsonify({'error': '接口不存在'}), 404\n return send_from_directory('.', filename)\n# ============================================================================\n# 路由:API 端点\n# ============================================================================\n@app.route('/api/health')", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "health_check", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def health_check():\n \"\"\"健康检查端点\"\"\"\n return jsonify({\n 'status': 'healthy',\n 'timestamp': datetime.now().isoformat(),\n 'version': '2.0.0-flask'\n })\n@app.route('/api/book/info')\ndef get_book_info():\n \"\"\"", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "get_book_info", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def get_book_info():\n \"\"\"\n 获取书籍信息(旧接口,保留兼容性)\n 推荐使用: /api/v2/books/<book_id>\n \"\"\"\n try:\n if not DB:\n return jsonify({'error': '数据库未初始化'}), 500\n # 默认获取第一本书的信息(为了兼容旧版本)\n books = DB.get_all_books()", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "get_page_content", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def get_page_content(page_num):\n \"\"\"\n 获取指定页面内容(旧接口,保留兼容性)\n 推荐使用: /api/v2/books/<book_id>/pages/<page_num>\n \"\"\"\n try:\n if not DB:\n return jsonify({'error': '数据库未初始化'}), 500\n # 获取最后导入的书籍\n books = DB.get_all_books()", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "save_progress", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def save_progress():\n \"\"\"保存学习进度\"\"\"\n try:\n data = request.get_json()\n # 这里可以保存到数据库,现在先存到 session\n if 'progress' not in session:\n session['progress'] = {}\n session['progress'].update({\n 'current_page': data.get('current_page', 0),\n 'bookmarks': data.get('bookmarks', []),", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "load_progress", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def load_progress():\n \"\"\"加载学习进度\"\"\"\n progress = session.get('progress', {\n 'current_page': 0,\n 'bookmarks': [],\n 'settings': {}\n })\n return jsonify(progress)\n@app.route('/api/search', methods=['POST'])\ndef search_content():", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "search_content", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def search_content():\n \"\"\"搜索内容(未来可集成 OpenSearch)\"\"\"\n try:\n data = request.get_json()\n keyword = data.get('keyword', '').strip()\n if not keyword:\n return jsonify({'error': '搜索关键词不能为空'}), 400\n # 简单搜索实现(在书籍数据中搜索)\n if not BOOK_DATA:\n return jsonify({'error': '书籍数据未加载'}), 500", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "opensearch_status", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def opensearch_status():\n \"\"\"检查 OpenSearch 连接状态(示例)\"\"\"\n try:\n # 这里可以实际连接 OpenSearch\n # from opensearchpy import OpenSearch\n # client = OpenSearch([{'host': OPENSEARCH_CONFIG['host'], 'port': OPENSEARCH_CONFIG['port']}])\n # info = client.info()\n return jsonify({\n 'configured': True,\n 'host': OPENSEARCH_CONFIG['host'],", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "get_stats", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def get_stats():\n \"\"\"获取学习统计数据\"\"\"\n # 从 session 或数据库获取统计数据\n progress = session.get('progress', {})\n return jsonify({\n 'current_page': progress.get('current_page', 0),\n 'total_bookmarks': len(progress.get('bookmarks', [])),\n 'last_visit': progress.get('last_updated', None),\n 'total_pages': len(BOOK_DATA.get('pages', [])) if BOOK_DATA else 0\n })", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "get_books", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def get_books():\n \"\"\"\n 获取所有书籍列表\n Query Parameters:\n grade_id: 年级ID(可选)\n Returns:\n {\n \"success\": true,\n \"count\": 10,\n \"books\": [", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "get_book_info_v2", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def get_book_info_v2(book_id):\n \"\"\"\n 获取指定书籍的详细信息\n Args:\n book_id: 书籍ID\n Returns:\n {\n \"success\": true,\n \"book\": {\n \"book_id\": 1,", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "get_book_pages", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def get_book_pages(book_id):\n \"\"\"\n 获取书籍的所有页面列表(不含片段内容)\n Args:\n book_id: 书籍ID\n Returns:\n {\n \"success\": true,\n \"book_id\": 168,\n \"book_name\": \"一年级上册\",", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "get_book_catalog", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def get_book_catalog(book_id):\n \"\"\"\n 获取书籍目录结构(章节目录)\n Args:\n book_id: 书籍ID\n Returns:\n {\n \"success\": true,\n \"book_id\": 168,\n \"book_name\": \"一年级上册\",", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "get_page_content_v2", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def get_page_content_v2(book_id, page_num):\n \"\"\"\n 获取指定页面的完整内容\n Args:\n book_id: 书籍ID\n page_num: 页码\n Returns:\n {\n \"success\": true,\n \"book_id\": 1,", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "search_book_content", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def search_book_content(book_id):\n \"\"\"\n 在书籍中搜索内容\n Query Parameters:\n keyword: 搜索关键词\n limit: 返回结果数量(默认20)\n Returns:\n {\n \"success\": true,\n \"book_id\": 168,", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "search_all_content", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def search_all_content():\n \"\"\"\n 在所有书籍中搜索内容\n Query Parameters:\n keyword: 搜索关键词\n limit: 返回结果数量(默认50)\n Returns:\n {\n \"success\": true,\n \"keyword\": \"hello\",", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "get_book_statistics", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def get_book_statistics(book_id):\n \"\"\"\n 获取书籍统计信息\n Args:\n book_id: 书籍ID\n Returns:\n {\n \"success\": true,\n \"statistics\": {\n \"book_id\": 168,", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "get_overall_statistics", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def get_overall_statistics():\n \"\"\"\n 获取整体统计信息\n Returns:\n {\n \"success\": true,\n \"statistics\": {\n \"total_books\": 30,\n \"total_pages\": 2000,\n \"total_pieces\": 50000,", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "not_found", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def not_found(error):\n \"\"\"404错误处理\"\"\"\n if request.path.startswith('/api/'):\n return jsonify({'error': '接口不存在'}), 404\n return send_from_directory('.', 'index.html')\n@app.errorhandler(500)\ndef internal_error(error):\n \"\"\"500错误处理\"\"\"\n app.logger.error(f'服务器错误: {error}')\n return jsonify({'error': '服务器内部错误'}), 500", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "internal_error", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def internal_error(error):\n \"\"\"500错误处理\"\"\"\n app.logger.error(f'服务器错误: {error}')\n return jsonify({'error': '服务器内部错误'}), 500\n# ============================================================================\n# 请求钩子\n# ============================================================================\n@app.before_request\ndef before_request():\n \"\"\"请求前处理\"\"\"", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "before_request", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def before_request():\n \"\"\"请求前处理\"\"\"\n # 获取客户端真实 IP\n client_ip = get_client_ip()\n # 记录所有请求信息(包括静态文件)\n user_agent = request.headers.get('User-Agent', 'Unknown')[:100] # 限制长度\n # API 请求记录详细信息\n if request.path.startswith('/api/'):\n app.logger.info(\n f\"[{client_ip}] {request.method} {request.path} \"", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "after_request", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def after_request(response):\n \"\"\"请求后处理 - 添加缓存控制\"\"\"\n # API 端点不缓存\n if request.path.startswith('/api/'):\n response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'\n response.headers['Pragma'] = 'no-cache'\n response.headers['Expires'] = '0'\n else:\n # 静态资源缓存1小时\n response.headers['Cache-Control'] = 'public, max-age=3600'", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "auto_generate_database", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def auto_generate_database():\n \"\"\"\n 自动生成数据库\n 如果 books.db 不存在,自动运行导入脚本生成数据库\n \"\"\"\n db_path = 'books.db'\n schema_path = 'db_schema.sql'\n data_dir = 'books_api_data'\n # 检查必要的文件和目录\n if not os.path.exists(schema_path):", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "initialize_app", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def initialize_app():\n \"\"\"初始化应用\"\"\"\n print(\"🚀 交互式英语学习应用 - Flask 版本\")\n print(\"=\" * 60)\n # 设置日志\n setup_logging()\n # 检查必要文件(暂时不检查 books.db,因为会自动生成)\n if not os.path.exists('index.html'):\n app.logger.error(f\"❌ 缺少必要文件: index.html\")\n return False", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "main", | |
| "kind": 2, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "def main():\n \"\"\"主函数\"\"\"\n # 初始化应用\n if not initialize_app():\n print(\"❌ 应用初始化失败\")\n return 1\n # Hugging Face Spaces 要求监听 7860 端口\n port = int(os.environ.get('PORT', 7860))\n debug = os.environ.get('FLASK_DEBUG', 'False').lower() == 'true'\n print(f\"🌐 监听端口: {port}\")", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "app", | |
| "kind": 5, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "app = Flask(__name__, \n static_folder='static',\n static_url_path='/static')\n# 配置\napp.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-secret-key-change-in-production')\napp.config['JSON_AS_ASCII'] = False # 支持中文JSON\napp.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 最大上传16MB\n# 启用CORS\nCORS(app, resources={\n r\"/*\": {", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "app.config['SECRET_KEY']", | |
| "kind": 5, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-secret-key-change-in-production')\napp.config['JSON_AS_ASCII'] = False # 支持中文JSON\napp.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 最大上传16MB\n# 启用CORS\nCORS(app, resources={\n r\"/*\": {\n \"origins\": \"*\",\n \"methods\": [\"GET\", \"POST\", \"PUT\", \"DELETE\", \"OPTIONS\"],\n \"allow_headers\": [\"Content-Type\", \"Authorization\"]\n }", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "app.config['JSON_AS_ASCII']", | |
| "kind": 5, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "app.config['JSON_AS_ASCII'] = False # 支持中文JSON\napp.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 最大上传16MB\n# 启用CORS\nCORS(app, resources={\n r\"/*\": {\n \"origins\": \"*\",\n \"methods\": [\"GET\", \"POST\", \"PUT\", \"DELETE\", \"OPTIONS\"],\n \"allow_headers\": [\"Content-Type\", \"Authorization\"]\n }\n})", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "app.config['MAX_CONTENT_LENGTH']", | |
| "kind": 5, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 最大上传16MB\n# 启用CORS\nCORS(app, resources={\n r\"/*\": {\n \"origins\": \"*\",\n \"methods\": [\"GET\", \"POST\", \"PUT\", \"DELETE\", \"OPTIONS\"],\n \"allow_headers\": [\"Content-Type\", \"Authorization\"]\n }\n})\n# 配置日志", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "OPENSEARCH_CONFIG", | |
| "kind": 5, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "OPENSEARCH_CONFIG = {\n 'host': os.environ.get('OPENSEARCH_HOST', '192.168.3.33'),\n 'port': int(os.environ.get('OPENSEARCH_PORT', 9200)),\n 'use_ssl': os.environ.get('OPENSEARCH_USE_SSL', 'False').lower() == 'true',\n}\n# 全局变量:存储学习数据\nBOOK_DATA = None\n# 数据库实例\nDB = None\ndef get_client_ip():", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "BOOK_DATA", | |
| "kind": 5, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "BOOK_DATA = None\n# 数据库实例\nDB = None\ndef get_client_ip():\n \"\"\"\n 获取客户端真实 IP 地址\n 在代理服务器(如 Hugging Face Spaces)后面时,需要检查代理头部\n \"\"\"\n # 按优先级检查各种代理头部\n headers_to_check = [", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "DB", | |
| "kind": 5, | |
| "importPath": "app", | |
| "description": "app", | |
| "peekOfCode": "DB = None\ndef get_client_ip():\n \"\"\"\n 获取客户端真实 IP 地址\n 在代理服务器(如 Hugging Face Spaces)后面时,需要检查代理头部\n \"\"\"\n # 按优先级检查各种代理头部\n headers_to_check = [\n 'X-Forwarded-For',\n 'X-Real-IP',", | |
| "detail": "app", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "ResourceManager", | |
| "kind": 6, | |
| "importPath": "check_and_download", | |
| "description": "check_and_download", | |
| "peekOfCode": "class ResourceManager:\n def __init__(self, json_file: str, assets_dir: str):\n self.json_file = json_file\n self.assets_dir = Path(assets_dir)\n self.audio_dir = self.assets_dir / \"audios\"\n self.image_dir = self.assets_dir / \"images\"\n # 确保目录存在\n self.audio_dir.mkdir(parents=True, exist_ok=True)\n self.image_dir.mkdir(parents=True, exist_ok=True)\n def load_json_data(self) -> Dict:", | |
| "detail": "check_and_download", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "main", | |
| "kind": 2, | |
| "importPath": "check_and_download", | |
| "description": "check_and_download", | |
| "peekOfCode": "def main():\n rm = ResourceManager(\"book_10242.json\", \"assets\")\n print(\"=== 资源完整性检查和下载脚本 ===\")\n # 检查并找出缺失的文件\n missing_urls = rm.check_and_find_missing()\n if not missing_urls[\"audio\"] and not missing_urls[\"image\"]:\n print(\"\\n所有文件都已正确下载,无需额外操作!\")\n return\n # 生成下载脚本\n script_content = rm.generate_download_script(missing_urls)", | |
| "detail": "check_and_download", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "Config", | |
| "kind": 6, | |
| "importPath": "config", | |
| "description": "config", | |
| "peekOfCode": "class Config:\n \"\"\"基础配置\"\"\"\n # Flask 基础配置\n SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key-please-change-in-production'\n JSON_AS_ASCII = False # 支持中文JSON\n MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 最大上传16MB\n # 会话配置\n SESSION_TYPE = 'filesystem'\n SESSION_PERMANENT = False\n SESSION_USE_SIGNER = True", | |
| "detail": "config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "DevelopmentConfig", | |
| "kind": 6, | |
| "importPath": "config", | |
| "description": "config", | |
| "peekOfCode": "class DevelopmentConfig(Config):\n \"\"\"开发环境配置\"\"\"\n DEBUG = True\n TESTING = False\nclass ProductionConfig(Config):\n \"\"\"生产环境配置\"\"\"\n DEBUG = False\n TESTING = False\n # 生产环境应该从环境变量读取敏感信息\n # SECRET_KEY 验证在应用启动时进行,而不是在导入时", | |
| "detail": "config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "ProductionConfig", | |
| "kind": 6, | |
| "importPath": "config", | |
| "description": "config", | |
| "peekOfCode": "class ProductionConfig(Config):\n \"\"\"生产环境配置\"\"\"\n DEBUG = False\n TESTING = False\n # 生产环境应该从环境变量读取敏感信息\n # SECRET_KEY 验证在应用启动时进行,而不是在导入时\n SECRET_KEY = os.environ.get('SECRET_KEY', Config.SECRET_KEY)\nclass TestingConfig(Config):\n \"\"\"测试环境配置\"\"\"\n TESTING = True", | |
| "detail": "config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "TestingConfig", | |
| "kind": 6, | |
| "importPath": "config", | |
| "description": "config", | |
| "peekOfCode": "class TestingConfig(Config):\n \"\"\"测试环境配置\"\"\"\n TESTING = True\n DEBUG = True\n # 使用内存数据库进行测试\n # SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'\n# 配置字典\nconfig = {\n 'development': DevelopmentConfig,\n 'production': ProductionConfig,", | |
| "detail": "config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "config", | |
| "kind": 5, | |
| "importPath": "config", | |
| "description": "config", | |
| "peekOfCode": "config = {\n 'development': DevelopmentConfig,\n 'production': ProductionConfig,\n 'testing': TestingConfig,\n 'default': DevelopmentConfig\n}", | |
| "detail": "config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "DatabaseManager", | |
| "kind": 6, | |
| "importPath": "db_manager", | |
| "description": "db_manager", | |
| "peekOfCode": "class DatabaseManager:\n \"\"\"数据库管理器类\"\"\"\n def __init__(self, db_path: str = 'books.db'):\n \"\"\"\n 初始化数据库管理器\n Args:\n db_path: 数据库文件路径\n \"\"\"\n self.db_path = db_path\n self.conn = None", | |
| "detail": "db_manager", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "get_db_instance", | |
| "kind": 2, | |
| "importPath": "db_manager", | |
| "description": "db_manager", | |
| "peekOfCode": "def get_db_instance(db_path: str = 'books.db') -> DatabaseManager:\n \"\"\"\n 获取数据库管理器单例\n Args:\n db_path: 数据库文件路径\n Returns:\n DatabaseManager实例\n \"\"\"\n global _db_instance\n if _db_instance is None:", | |
| "detail": "db_manager", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "close_db", | |
| "kind": 2, | |
| "importPath": "db_manager", | |
| "description": "db_manager", | |
| "peekOfCode": "def close_db():\n \"\"\"关闭全局数据库连接\"\"\"\n global _db_instance\n if _db_instance:\n _db_instance.close()\n _db_instance = None", | |
| "detail": "db_manager", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "logger", | |
| "kind": 5, | |
| "importPath": "db_manager", | |
| "description": "db_manager", | |
| "peekOfCode": "logger = logging.getLogger(__name__)\nclass DatabaseManager:\n \"\"\"数据库管理器类\"\"\"\n def __init__(self, db_path: str = 'books.db'):\n \"\"\"\n 初始化数据库管理器\n Args:\n db_path: 数据库文件路径\n \"\"\"\n self.db_path = db_path", | |
| "detail": "db_manager", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "_db_instance", | |
| "kind": 5, | |
| "importPath": "db_manager", | |
| "description": "db_manager", | |
| "peekOfCode": "_db_instance = None\ndef get_db_instance(db_path: str = 'books.db') -> DatabaseManager:\n \"\"\"\n 获取数据库管理器单例\n Args:\n db_path: 数据库文件路径\n Returns:\n DatabaseManager实例\n \"\"\"\n global _db_instance", | |
| "detail": "db_manager", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "main", | |
| "kind": 2, | |
| "importPath": "debug_pages", | |
| "description": "debug_pages", | |
| "peekOfCode": "def main():\n \"\"\"检查第4页和第5页的数据\"\"\"\n try:\n with open('book_10242.json', 'r', encoding='utf-8') as f:\n data = json.load(f)\n # 解析Data字段中的JSON字符串\n pages_data = json.loads(data['Data'])\n print(f\"总页数: {len(pages_data)}\")\n print(\"=\" * 80)\n # 检查第4页和第5页(数组索引为3和4)", | |
| "detail": "debug_pages", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "extract_filename_from_url", | |
| "kind": 2, | |
| "importPath": "delete_encrypted_files", | |
| "description": "delete_encrypted_files", | |
| "peekOfCode": "def extract_filename_from_url(url):\n \"\"\"从URL中提取文件名\"\"\"\n if not url:\n return None\n # 解析URL,去掉查询参数\n parsed = urlparse(url)\n path = parsed.path\n # URL解码\n path = unquote(path)\n # 提取文件名", | |
| "detail": "delete_encrypted_files", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "main", | |
| "kind": 2, | |
| "importPath": "delete_encrypted_files", | |
| "description": "delete_encrypted_files", | |
| "peekOfCode": "def main():\n json_file = '/data/zhangl/code/hf/point/book_10242.json'\n audio_dir = '/data/zhangl/code/hf/point/assets/audios'\n image_dir = '/data/zhangl/code/hf/point/assets/images'\n print(\"正在解析 JSON 文件...\")\n try:\n with open(json_file, 'r', encoding='utf-8') as f:\n data = json.load(f)\n # 解析Data字段中的JSON字符串\n pages_data = json.loads(data['Data'])", | |
| "detail": "delete_encrypted_files", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "create_directories", | |
| "kind": 2, | |
| "importPath": "download_resources", | |
| "description": "download_resources", | |
| "peekOfCode": "def create_directories():\n \"\"\"创建assets目录结构\"\"\"\n assets_dir = Path(\"assets\")\n images_dir = assets_dir / \"images\"\n audios_dir = assets_dir / \"audios\"\n images_dir.mkdir(parents=True, exist_ok=True)\n audios_dir.mkdir(parents=True, exist_ok=True)\n print(f\"✅ 创建目录: {images_dir}\")\n print(f\"✅ 创建目录: {audios_dir}\")\n return images_dir, audios_dir", | |
| "detail": "download_resources", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "get_filename_from_url", | |
| "kind": 2, | |
| "importPath": "download_resources", | |
| "description": "download_resources", | |
| "peekOfCode": "def get_filename_from_url(url):\n \"\"\"从URL中提取文件名\"\"\"\n # 解析URL并获取路径部分\n parsed_url = urllib.parse.urlparse(url)\n path = parsed_url.path\n # 获取文件名\n filename = os.path.basename(path)\n # 如果没有文件名,使用URL的hash作为文件名\n if not filename or '.' not in filename:\n url_hash = hashlib.md5(url.encode()).hexdigest()[:8]", | |
| "detail": "download_resources", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "download_file", | |
| "kind": 2, | |
| "importPath": "download_resources", | |
| "description": "download_resources", | |
| "peekOfCode": "def download_file(url, save_path, max_retries=3):\n \"\"\"下载文件\"\"\"\n headers = {\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'\n }\n for attempt in range(max_retries):\n try:\n print(f\"📥 下载中: {os.path.basename(save_path)}\")\n response = requests.get(url, headers=headers, timeout=30)\n response.raise_for_status()", | |
| "detail": "download_resources", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "extract_urls_from_json", | |
| "kind": 2, | |
| "importPath": "download_resources", | |
| "description": "download_resources", | |
| "peekOfCode": "def extract_urls_from_json(json_file):\n \"\"\"从JSON文件中提取所有图片和音频URL\"\"\"\n with open(json_file, 'r', encoding='utf-8') as f:\n data = json.load(f)\n urls = {\n 'images': set(),\n 'audios': set()\n }\n # 解析Data字段中的JSON字符串\n if 'Data' in data:", | |
| "detail": "download_resources", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "main", | |
| "kind": 2, | |
| "importPath": "download_resources", | |
| "description": "download_resources", | |
| "peekOfCode": "def main():\n \"\"\"主函数\"\"\"\n print(\"🚀 开始下载资源...\")\n # 创建目录\n images_dir, audios_dir = create_directories()\n # 提取URL\n print(\"📋 解析JSON文件...\")\n urls = extract_urls_from_json('book_10242.json')\n print(f\"📊 找到 {len(urls['images'])} 个图片URL\")\n print(f\"📊 找到 {len(urls['audios'])} 个音频URL\")", | |
| "detail": "download_resources", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "extract_data_field", | |
| "kind": 2, | |
| "importPath": "extract_data", | |
| "description": "extract_data", | |
| "peekOfCode": "def extract_data_field(input_file, output_file):\n \"\"\"\n 从输入文件中提取Data字段并保存到输出文件\n Args:\n input_file: 输入JSON文件路径\n output_file: 输出JSON文件路径\n \"\"\"\n try:\n # 读取输入文件\n print(f\"正在读取文件: {input_file}\")", | |
| "detail": "extract_data", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "on_starting", | |
| "kind": 2, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "def on_starting(server):\n \"\"\"服务器启动时执行\"\"\"\n print(\"=\" * 60)\n print(\"🚀 Gunicorn 服务器启动\")\n print(f\"📍 绑定地址: {bind}\")\n print(f\"👷 工作进程数: {workers}\")\n print(f\"🧵 每进程线程数: {threads}\")\n print(f\"⏱️ 超时时间: {timeout}s\")\n print(\"=\" * 60)\ndef worker_int(worker):", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "worker_int", | |
| "kind": 2, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "def worker_int(worker):\n \"\"\"工作进程被中断时执行\"\"\"\n print(f\"⚠️ 工作进程 {worker.pid} 被中断\")\ndef worker_abort(worker):\n \"\"\"工作进程异常退出时执行\"\"\"\n print(f\"❌ 工作进程 {worker.pid} 异常退出\")\ndef post_worker_init(worker):\n \"\"\"工作进程初始化后执行\"\"\"\n print(f\"✅ 工作进程 {worker.pid} 初始化完成\")", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "worker_abort", | |
| "kind": 2, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "def worker_abort(worker):\n \"\"\"工作进程异常退出时执行\"\"\"\n print(f\"❌ 工作进程 {worker.pid} 异常退出\")\ndef post_worker_init(worker):\n \"\"\"工作进程初始化后执行\"\"\"\n print(f\"✅ 工作进程 {worker.pid} 初始化完成\")", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "post_worker_init", | |
| "kind": 2, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "def post_worker_init(worker):\n \"\"\"工作进程初始化后执行\"\"\"\n print(f\"✅ 工作进程 {worker.pid} 初始化完成\")", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "bind", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "bind = f\"0.0.0.0:{os.environ.get('PORT', 7860)}\"\n# 工作进程数(根据 CPU 核心数自动调整)\nworkers = int(os.environ.get('GUNICORN_WORKERS', multiprocessing.cpu_count() * 2 + 1))\n# 如果资源受限,可以手动设置较小的值\nworkers = min(workers, 4)\n# 工作进程类型\nworker_class = 'sync'\n# 每个工作进程的线程数\nthreads = 2\n# 超时时间(秒)", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "workers", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "workers = int(os.environ.get('GUNICORN_WORKERS', multiprocessing.cpu_count() * 2 + 1))\n# 如果资源受限,可以手动设置较小的值\nworkers = min(workers, 4)\n# 工作进程类型\nworker_class = 'sync'\n# 每个工作进程的线程数\nthreads = 2\n# 超时时间(秒)\ntimeout = 120\n# 保持活动连接的时间", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "workers", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "workers = min(workers, 4)\n# 工作进程类型\nworker_class = 'sync'\n# 每个工作进程的线程数\nthreads = 2\n# 超时时间(秒)\ntimeout = 120\n# 保持活动连接的时间\nkeepalive = 5\n# 最大请求数(处理后重启进程,防止内存泄漏)", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "worker_class", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "worker_class = 'sync'\n# 每个工作进程的线程数\nthreads = 2\n# 超时时间(秒)\ntimeout = 120\n# 保持活动连接的时间\nkeepalive = 5\n# 最大请求数(处理后重启进程,防止内存泄漏)\nmax_requests = 1000\nmax_requests_jitter = 50", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "threads", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "threads = 2\n# 超时时间(秒)\ntimeout = 120\n# 保持活动连接的时间\nkeepalive = 5\n# 最大请求数(处理后重启进程,防止内存泄漏)\nmax_requests = 1000\nmax_requests_jitter = 50\n# 日志配置\naccesslog = '-' # 输出到 stdout", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "timeout", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "timeout = 120\n# 保持活动连接的时间\nkeepalive = 5\n# 最大请求数(处理后重启进程,防止内存泄漏)\nmax_requests = 1000\nmax_requests_jitter = 50\n# 日志配置\naccesslog = '-' # 输出到 stdout\nerrorlog = '-' # 输出到 stderr\nloglevel = 'info'", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "keepalive", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "keepalive = 5\n# 最大请求数(处理后重启进程,防止内存泄漏)\nmax_requests = 1000\nmax_requests_jitter = 50\n# 日志配置\naccesslog = '-' # 输出到 stdout\nerrorlog = '-' # 输出到 stderr\nloglevel = 'info'\n# 自定义访问日志格式 - 包含真实客户端 IP\n# %(h)s - 远程地址", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "max_requests", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "max_requests = 1000\nmax_requests_jitter = 50\n# 日志配置\naccesslog = '-' # 输出到 stdout\nerrorlog = '-' # 输出到 stderr\nloglevel = 'info'\n# 自定义访问日志格式 - 包含真实客户端 IP\n# %(h)s - 远程地址\n# %({X-Forwarded-For}i)s - X-Forwarded-For 头部(代理后面的真实 IP)\n# %(t)s - 时间", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "max_requests_jitter", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "max_requests_jitter = 50\n# 日志配置\naccesslog = '-' # 输出到 stdout\nerrorlog = '-' # 输出到 stderr\nloglevel = 'info'\n# 自定义访问日志格式 - 包含真实客户端 IP\n# %(h)s - 远程地址\n# %({X-Forwarded-For}i)s - X-Forwarded-For 头部(代理后面的真实 IP)\n# %(t)s - 时间\n# %(m)s - 请求方法", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "accesslog", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "accesslog = '-' # 输出到 stdout\nerrorlog = '-' # 输出到 stderr\nloglevel = 'info'\n# 自定义访问日志格式 - 包含真实客户端 IP\n# %(h)s - 远程地址\n# %({X-Forwarded-For}i)s - X-Forwarded-For 头部(代理后面的真实 IP)\n# %(t)s - 时间\n# %(m)s - 请求方法\n# %(U)s - URL 路径\n# %(q)s - 查询字符串", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "errorlog", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "errorlog = '-' # 输出到 stderr\nloglevel = 'info'\n# 自定义访问日志格式 - 包含真实客户端 IP\n# %(h)s - 远程地址\n# %({X-Forwarded-For}i)s - X-Forwarded-For 头部(代理后面的真实 IP)\n# %(t)s - 时间\n# %(m)s - 请求方法\n# %(U)s - URL 路径\n# %(q)s - 查询字符串\n# %(s)s - 状态码", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "loglevel", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "loglevel = 'info'\n# 自定义访问日志格式 - 包含真实客户端 IP\n# %(h)s - 远程地址\n# %({X-Forwarded-For}i)s - X-Forwarded-For 头部(代理后面的真实 IP)\n# %(t)s - 时间\n# %(m)s - 请求方法\n# %(U)s - URL 路径\n# %(q)s - 查询字符串\n# %(s)s - 状态码\n# %(b)s - 响应大小", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "access_log_format", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "access_log_format = (\n '[%(t)s] '\n 'Client: %({X-Forwarded-For}i)s (Remote: %(h)s) '\n '%(m)s %(U)s%(q)s '\n 'Status: %(s)s '\n 'Size: %(b)s bytes '\n 'Time: %(D)s μs '\n 'UA: \"%({User-Agent}i)s\"'\n)\n# 进程名称前缀", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "proc_name", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "proc_name = 'english_learning_app'\n# 守护进程(通常设为 False,让容器管理进程)\ndaemon = False\n# 预加载应用(减少内存占用)\npreload_app = True\n# 在请求处理前后执行的钩子\ndef on_starting(server):\n \"\"\"服务器启动时执行\"\"\"\n print(\"=\" * 60)\n print(\"🚀 Gunicorn 服务器启动\")", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "daemon", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "daemon = False\n# 预加载应用(减少内存占用)\npreload_app = True\n# 在请求处理前后执行的钩子\ndef on_starting(server):\n \"\"\"服务器启动时执行\"\"\"\n print(\"=\" * 60)\n print(\"🚀 Gunicorn 服务器启动\")\n print(f\"📍 绑定地址: {bind}\")\n print(f\"👷 工作进程数: {workers}\")", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "preload_app", | |
| "kind": 5, | |
| "importPath": "gunicorn_config", | |
| "description": "gunicorn_config", | |
| "peekOfCode": "preload_app = True\n# 在请求处理前后执行的钩子\ndef on_starting(server):\n \"\"\"服务器启动时执行\"\"\"\n print(\"=\" * 60)\n print(\"🚀 Gunicorn 服务器启动\")\n print(f\"📍 绑定地址: {bind}\")\n print(f\"👷 工作进程数: {workers}\")\n print(f\"🧵 每进程线程数: {threads}\")\n print(f\"⏱️ 超时时间: {timeout}s\")", | |
| "detail": "gunicorn_config", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "create_database", | |
| "kind": 2, | |
| "importPath": "import_book_data", | |
| "description": "import_book_data", | |
| "peekOfCode": "def create_database(db_path='books.db', schema_path='db_schema.sql'):\n \"\"\"\n 创建数据库和表结构\n Args:\n db_path: 数据库文件路径\n schema_path: Schema SQL文件路径\n Returns:\n sqlite3.Connection: 数据库连接对象\n \"\"\"\n logger.info(f\"📦 创建数据库: {db_path}\")", | |
| "detail": "import_book_data", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "import_book_info", | |
| "kind": 2, | |
| "importPath": "import_book_data", | |
| "description": "import_book_data", | |
| "peekOfCode": "def import_book_info(conn, book_dir: Path) -> int:\n \"\"\"\n 导入书籍基本信息\n Args:\n conn: 数据库连接\n book_dir: 书籍目录\n Returns:\n int: 书籍ID\n \"\"\"\n book_info_file = book_dir / \"book_info.json\"", | |
| "detail": "import_book_data", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "import_pages", | |
| "kind": 2, | |
| "importPath": "import_book_data", | |
| "description": "import_book_data", | |
| "peekOfCode": "def import_pages(conn, book_dir: Path, book_id: int):\n \"\"\"\n 导入页面数据\n Args:\n conn: 数据库连接\n book_dir: 书籍目录\n book_id: 书籍ID\n \"\"\"\n pages_file = book_dir / \"pages.json\"\n if not pages_file.exists():", | |
| "detail": "import_book_data", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "generate_catalog_from_pages", | |
| "kind": 2, | |
| "importPath": "import_book_data", | |
| "description": "import_book_data", | |
| "peekOfCode": "def generate_catalog_from_pages(book_dir: Path, book_id: int, pages_data: list) -> list:\n \"\"\"\n 从pages.json自动生成目录结构\n 为每一页生成一个目录项,使用该页第一个piece的文本作为标题\n Args:\n book_dir: 书籍目录\n book_id: 书籍ID\n pages_data: 页面数据列表\n Returns:\n 生成的目录列表", | |
| "detail": "import_book_data", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "import_catalog", | |
| "kind": 2, | |
| "importPath": "import_book_data", | |
| "description": "import_book_data", | |
| "peekOfCode": "def import_catalog(conn, book_dir: Path, book_id: int, pages_data: list = None):\n \"\"\"\n 导入目录数据(强制从pages.json生成)\n Args:\n conn: 数据库连接\n book_dir: 书籍目录\n book_id: 书籍ID\n pages_data: 页面数据(用于自动生成目录)\n \"\"\"\n catalogs = []", | |
| "detail": "import_book_data", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "import_book", | |
| "kind": 2, | |
| "importPath": "import_book_data", | |
| "description": "import_book_data", | |
| "peekOfCode": "def import_book(conn, book_dir: Path):\n \"\"\"\n 导入单本书籍的完整数据\n Args:\n conn: 数据库连接\n book_dir: 书籍目录\n \"\"\"\n book_name = book_dir.name\n logger.info(f\"\\n{'=' * 60}\")\n logger.info(f\"开始导入书籍: {book_name}\")", | |
| "detail": "import_book_data", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "import_all_books", | |
| "kind": 2, | |
| "importPath": "import_book_data", | |
| "description": "import_book_data", | |
| "peekOfCode": "def import_all_books(conn, data_dir: str):\n \"\"\"\n 导入所有书籍\n Args:\n conn: 数据库连接\n data_dir: books_api_data 目录路径\n \"\"\"\n logger.info(\"=\" * 60)\n logger.info(\"开始批量导入书籍数据\")\n logger.info(f\"数据目录: {data_dir}\")", | |
| "detail": "import_book_data", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "verify_data", | |
| "kind": 2, | |
| "importPath": "import_book_data", | |
| "description": "import_book_data", | |
| "peekOfCode": "def verify_data(conn):\n \"\"\"\n 验证导入的数据\n Args:\n conn: 数据库连接\n \"\"\"\n logger.info(\"\\n🔍 验证数据...\")\n cursor = conn.cursor()\n # 统计数据\n cursor.execute('SELECT COUNT(*) FROM books')", | |
| "detail": "import_book_data", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "main", | |
| "kind": 2, | |
| "importPath": "import_book_data", | |
| "description": "import_book_data", | |
| "peekOfCode": "def main():\n \"\"\"主函数\"\"\"\n import argparse\n print(\"=\" * 80)\n print(\"📚 Books API Data 导入工具\")\n print(\"=\" * 80)\n # 命令行参数解析\n parser = argparse.ArgumentParser(description='导入 books_api_data 数据到 SQLite 数据库')\n parser.add_argument('data_dir', nargs='?', default='books_api_data',\n help='books_api_data 数据目录路径 (默认: books_api_data)')", | |
| "detail": "import_book_data", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "logger", | |
| "kind": 5, | |
| "importPath": "import_book_data", | |
| "description": "import_book_data", | |
| "peekOfCode": "logger = logging.getLogger(__name__)\ndef create_database(db_path='books.db', schema_path='db_schema.sql'):\n \"\"\"\n 创建数据库和表结构\n Args:\n db_path: 数据库文件路径\n schema_path: Schema SQL文件路径\n Returns:\n sqlite3.Connection: 数据库连接对象\n \"\"\"", | |
| "detail": "import_book_data", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "OpenSearchClient", | |
| "kind": 6, | |
| "importPath": "opensearch_client", | |
| "description": "opensearch_client", | |
| "peekOfCode": "class OpenSearchClient:\n \"\"\"OpenSearch 客户端封装类\"\"\"\n def __init__(self, host='192.168.3.33', port=9200, use_ssl=False, verify_certs=False):\n \"\"\"\n 初始化 OpenSearch 客户端\n Args:\n host: OpenSearch 主机地址\n port: OpenSearch 端口\n use_ssl: 是否使用 SSL\n verify_certs: 是否验证证书", | |
| "detail": "opensearch_client", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "initialize_learning_index", | |
| "kind": 2, | |
| "importPath": "opensearch_client", | |
| "description": "opensearch_client", | |
| "peekOfCode": "def initialize_learning_index(client, index_name='english_learning_content'):\n \"\"\"\n 初始化英语学习内容索引\n 索引包含:单词、例句、页面内容等\n \"\"\"\n # 定义索引映射\n mappings = {\n 'properties': {\n 'type': {'type': 'keyword'}, # 内容类型:word, sentence, page\n 'page_num': {'type': 'integer'}, # 页码", | |
| "detail": "opensearch_client", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "search_english_content", | |
| "kind": 2, | |
| "importPath": "opensearch_client", | |
| "description": "opensearch_client", | |
| "peekOfCode": "def search_english_content(client, keyword, index_name='english_learning_content'):\n \"\"\"\n 搜索英语学习内容\n Args:\n client: OpenSearchClient 实例\n keyword: 搜索关键词\n index_name: 索引名称\n Returns:\n 搜索结果列表\n \"\"\"", | |
| "detail": "opensearch_client", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "logger", | |
| "kind": 5, | |
| "importPath": "opensearch_client", | |
| "description": "opensearch_client", | |
| "peekOfCode": "logger = logging.getLogger(__name__)\nclass OpenSearchClient:\n \"\"\"OpenSearch 客户端封装类\"\"\"\n def __init__(self, host='192.168.3.33', port=9200, use_ssl=False, verify_certs=False):\n \"\"\"\n 初始化 OpenSearch 客户端\n Args:\n host: OpenSearch 主机地址\n port: OpenSearch 端口\n use_ssl: 是否使用 SSL", | |
| "detail": "opensearch_client", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "main", | |
| "kind": 2, | |
| "importPath": "quick_check", | |
| "description": "quick_check", | |
| "peekOfCode": "def main():\n with open('book_10242.json', 'r', encoding='utf-8') as f:\n data = json.load(f)\n pages_data = json.loads(data['Data'])\n # 检查第4页(索引3)和第5页(索引4)\n for i in [3, 4]:\n if i < len(pages_data):\n page = pages_data[i]\n print(f\"=== 第{page['pageNumber']}页 ===\")\n print(f\"pageId: {page['pageId']}\")", | |
| "detail": "quick_check", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "test_ip_logging", | |
| "kind": 2, | |
| "importPath": "test_ip_logging", | |
| "description": "test_ip_logging", | |
| "peekOfCode": "def test_ip_logging():\n \"\"\"测试 IP 地址记录\"\"\"\n base_url = \"http://localhost:7860\"\n print(\"=\" * 60)\n print(\"🧪 测试客户端 IP 记录功能\")\n print(\"=\" * 60)\n # 测试场景\n test_cases = [\n {\n \"name\": \"正常请求(无代理)\",", | |
| "detail": "test_ip_logging", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "show_example_logs", | |
| "kind": 2, | |
| "importPath": "test_ip_logging", | |
| "description": "test_ip_logging", | |
| "peekOfCode": "def show_example_logs():\n \"\"\"显示日志示例\"\"\"\n print(\"\\n\" + \"=\" * 60)\n print(\"📝 日志输出示例\")\n print(\"=\" * 60)\n examples = [\n \"[2025-10-16 18:58:23] INFO in app: [203.0.113.1] GET /api/health | UA: Mozilla/5.0 (Windows NT 10.0)\",\n \"[2025-10-16 18:58:24] INFO in app: [198.51.100.1] POST /api/search | UA: Mozilla/5.0 (iPhone; CPU iPhone OS)\",\n \"[2025-10-16 18:58:25] INFO in app: [192.0.2.1] GET /api/book/info | UA: curl/7.68.0\",\n ]", | |
| "detail": "test_ip_logging", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "print_status", | |
| "kind": 2, | |
| "importPath": "test_setup", | |
| "description": "test_setup", | |
| "peekOfCode": "def print_status(message, status):\n \"\"\"打印状态信息\"\"\"\n if status:\n print(f\"✅ {message}\")\n else:\n print(f\"❌ {message}\")\n return status\ndef check_python_version():\n \"\"\"检查Python版本\"\"\"\n version = sys.version_info", | |
| "detail": "test_setup", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "check_python_version", | |
| "kind": 2, | |
| "importPath": "test_setup", | |
| "description": "test_setup", | |
| "peekOfCode": "def check_python_version():\n \"\"\"检查Python版本\"\"\"\n version = sys.version_info\n is_ok = version.major == 3 and version.minor >= 12\n print_status(\n f\"Python版本: {version.major}.{version.minor}.{version.micro} {'(需要 3.12+)' if not is_ok else ''}\",\n is_ok\n )\n return is_ok\ndef check_dependencies():", | |
| "detail": "test_setup", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "check_dependencies", | |
| "kind": 2, | |
| "importPath": "test_setup", | |
| "description": "test_setup", | |
| "peekOfCode": "def check_dependencies():\n \"\"\"检查Python依赖\"\"\"\n dependencies = ['flask', 'flask_cors']\n all_ok = True\n for dep in dependencies:\n try:\n __import__(dep)\n print_status(f\"Python包 '{dep}' 已安装\", True)\n except ImportError:\n print_status(f\"Python包 '{dep}' 未安装\", False)", | |
| "detail": "test_setup", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "check_files", | |
| "kind": 2, | |
| "importPath": "test_setup", | |
| "description": "test_setup", | |
| "peekOfCode": "def check_files():\n \"\"\"检查必要文件\"\"\"\n files = [\n 'app.py',\n 'db_manager.py',\n 'import_book_data.py',\n 'db_schema.sql',\n 'index.html',\n 'reader.html',\n 'static/js/catalog.js',", | |
| "detail": "test_setup", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "check_data_directory", | |
| "kind": 2, | |
| "importPath": "test_setup", | |
| "description": "test_setup", | |
| "peekOfCode": "def check_data_directory():\n \"\"\"检查数据目录\"\"\"\n data_dir = Path('books_api_data')\n if not data_dir.exists():\n print_status(\"数据目录 'books_api_data' 不存在\", False)\n print(\" 提示: 请将 books_api_data 目录放置在项目根目录下\")\n return False\n # 检查是否有书籍数据\n book_dirs = [d for d in data_dir.iterdir() if d.is_dir()]\n if len(book_dirs) == 0:", | |
| "detail": "test_setup", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "check_database", | |
| "kind": 2, | |
| "importPath": "test_setup", | |
| "description": "test_setup", | |
| "peekOfCode": "def check_database():\n \"\"\"检查数据库\"\"\"\n db_file = Path('books.db')\n if not db_file.exists():\n print_status(\"数据库 'books.db' 不存在\", False)\n print(\" 提示: 运行 'python3 import_book_data.py books_api_data' 来导入数据\")\n return False\n print_status(\"数据库 'books.db' 存在\", True)\n # 检查数据库内容\n try:", | |
| "detail": "test_setup", | |
| "documentation": {} | |
| }, | |
| { | |
| "label": "main", | |
| "kind": 2, | |
| "importPath": "test_setup", | |
| "description": "test_setup", | |
| "peekOfCode": "def main():\n \"\"\"主函数\"\"\"\n print(\"=\" * 70)\n print(\"🔍 交互式英语学习平台 - 系统设置检查\")\n print(\"=\" * 70)\n print()\n checks = [\n (\"Python版本\", check_python_version),\n (\"Python依赖\", check_dependencies),\n (\"必要文件\", check_files),", | |
| "detail": "test_setup", | |
| "documentation": {} | |
| } | |
| ] |