From 52755dfbcf772ed94bd41a94d8686bc8f999128d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E4=B8=80=E4=B8=81?= <1769123563@qq.com> Date: Tue, 18 Nov 2025 23:01:06 +0800 Subject: [PATCH] Fix the Error "'ReportTask' object has no attribute 'ir_file_path'" When Exporting PDF and Ensure that Pango Dependencies are not Missing --- Dockerfile | 6 ++- ReportEngine/flask_interface.py | 38 +++++++++++++++- ReportEngine/utils/dependency_check.py | 62 ++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 ReportEngine/utils/dependency_check.py diff --git a/Dockerfile b/Dockerfile index 0aaf310..292041a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ ENV PYTHONDONTWRITEBYTECODE=1 \ PATH="/root/.local/bin:${PATH}" \ PLAYWRIGHT_BROWSERS_PATH=/ms-playwright -# Install system dependencies required by scientific Python stack, Playwright, and Streamlit +# Install system dependencies required by scientific Python stack, Playwright, Streamlit, and WeasyPrint PDF RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ curl \ @@ -19,6 +19,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libgtk-3-0 \ libpango-1.0-0 \ libpangocairo-1.0-0 \ + libpangoft2-1.0-0 \ + libgdk-pixbuf2.0-0 \ + libffi-dev \ + libcairo2 \ libatk1.0-0 \ libatk-bridge2.0-0 \ libxcb1 \ diff --git a/ReportEngine/flask_interface.py b/ReportEngine/flask_interface.py index 79449f7..e6310b8 100644 --- a/ReportEngine/flask_interface.py +++ b/ReportEngine/flask_interface.py @@ -160,6 +160,14 @@ def initialize_report_engine(): try: report_agent = create_agent() logger.info("Report Engine初始化成功") + + # 检测 PDF 生成依赖(Pango) + try: + from .utils.dependency_check import log_dependency_status + log_dependency_status() + except Exception as dep_err: + logger.warning(f"依赖检测失败: {dep_err}") + return True except Exception as e: logger.exception(f"Report Engine初始化失败: {str(e)}") @@ -198,6 +206,8 @@ class ReportTask: self.report_file_name = "" self.state_file_path = "" self.state_file_relative_path = "" + self.ir_file_path = "" + self.ir_file_relative_path = "" # ====== 流式事件缓存与并发保护 ====== # 使用deque保存最近的事件,结合锁保证多线程下的安全访问 self.event_history: deque = deque(maxlen=1000) @@ -248,7 +258,9 @@ class ReportTask: 'report_file_name': self.report_file_name, 'report_file_path': self.report_file_relative_path or self.report_file_path, 'state_file_ready': bool(self.state_file_path), - 'state_file_path': self.state_file_relative_path or self.state_file_path + 'state_file_path': self.state_file_relative_path or self.state_file_path, + 'ir_file_ready': bool(self.ir_file_path), + 'ir_file_path': self.ir_file_relative_path or self.ir_file_path } def publish_event(self, event_type: str, payload: Dict[str, Any]) -> None: @@ -431,6 +443,8 @@ def run_report_generation(task: ReportTask, query: str, custom_template: str = " task.report_file_name = generation_result.get('report_filename', '') task.state_file_path = generation_result.get('state_filepath', '') task.state_file_relative_path = generation_result.get('state_relative_path', '') + task.ir_file_path = generation_result.get('ir_filepath', '') + task.ir_file_relative_path = generation_result.get('ir_relative_path', '') task.publish_event('html_ready', { 'message': 'HTML渲染完成,可刷新预览', 'report_file': task.report_file_relative_path or task.report_file_path, @@ -1027,6 +1041,17 @@ def export_pdf(task_id: str): Response: PDF文件流或错误信息 """ try: + # 检测 Pango 依赖 + from .utils.dependency_check import check_pango_available + pango_available, pango_message = check_pango_available() + if not pango_available: + return jsonify({ + 'success': False, + 'error': 'PDF 导出功能不可用:缺少 Pango 系统依赖', + 'details': '请查看 requirements.txt 文件中的 "===== PDF生成 =====" 部分了解如何安装 Pango', + 'system_message': pango_message + }), 503 + # 获取任务信息 task = tasks_registry.get(task_id) if not task: @@ -1104,6 +1129,17 @@ def export_pdf_from_ir(): Response: PDF文件流或错误信息 """ try: + # 检测 Pango 依赖 + from .utils.dependency_check import check_pango_available + pango_available, pango_message = check_pango_available() + if not pango_available: + return jsonify({ + 'success': False, + 'error': 'PDF 导出功能不可用:缺少 Pango 系统依赖', + 'details': '请查看 requirements.txt 文件中的 "===== PDF生成 =====" 部分了解如何安装 Pango', + 'system_message': pango_message + }), 503 + data = request.get_json() if not data or 'document_ir' not in data: diff --git a/ReportEngine/utils/dependency_check.py b/ReportEngine/utils/dependency_check.py new file mode 100644 index 0000000..bbb01a7 --- /dev/null +++ b/ReportEngine/utils/dependency_check.py @@ -0,0 +1,62 @@ +""" +检测系统依赖工具 +用于检测 PDF 生成所需的系统依赖 +""" +import sys +from loguru import logger + + +def check_pango_available(): + """ + 检测 Pango 库是否可用 + + Returns: + tuple: (is_available: bool, message: str) + """ + try: + # 尝试导入 weasyprint 并初始化 Pango + from weasyprint import HTML + from weasyprint.text.ffi import ffi, pango + + # 尝试调用 Pango 函数来确认库可用 + pango.pango_version() + + return True, "✓ Pango 依赖检测通过,PDF 导出功能可用" + except OSError as e: + # Pango 库未安装或无法加载 + error_msg = str(e) + if 'pango' in error_msg.lower(): + return False, ( + "⚠ Pango 依赖未安装或无法加载,PDF 导出功能将不可用(其他功能不受影响)\n" + " 请查看 requirements.txt 文件中的 PDF 生成部分,了解如何安装 Pango 依赖" + ) + return False, f"⚠ PDF 依赖加载失败: {error_msg}" + except ImportError as e: + # weasyprint 未安装 + return False, f"⚠ WeasyPrint 未安装: {e}" + except Exception as e: + # 其他未知错误 + return False, f"⚠ PDF 依赖检测失败: {e}" + + +def log_dependency_status(): + """ + 记录系统依赖状态到日志 + """ + is_available, message = check_pango_available() + + if is_available: + logger.success(message) + else: + logger.warning(message) + logger.info("提示:PDF 导出功能需要 Pango 库支持,但不影响系统其他功能的正常使用") + logger.info("安装说明请参考:requirements.txt 文件中的 '===== PDF生成 =====' 部分") + + return is_available + + +if __name__ == "__main__": + # 用于独立测试 + is_available, message = check_pango_available() + print(message) + sys.exit(0 if is_available else 1)