From 72ead04182726b67daeb58bf3a6b60bfd02e55c2 Mon Sep 17 00:00:00 2001 From: SXP-Simon Date: Wed, 25 Mar 2026 16:09:54 +0800 Subject: [PATCH] =?UTF-8?q?fix(pdf):=20=E4=BC=98=E5=8C=96=20PDF=20?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E7=94=9F=E6=88=90=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 7 + scripts/debug_all_pdf_themes.py | 235 +++++ scripts/mock_pdf_gen.py | 2 +- .../templates/format/pdf_template.html | 40 +- .../templates/hack/activity_chart_pdf.html | 2 +- .../templates/hack/pdf_template.html | 98 +- .../retro_futurism/activity_chart_pdf.html | 2 +- .../retro_futurism/pdf_template.html | 984 ++++++------------ .../templates/scrapbook/pdf_template.html | 7 + .../templates/simple/pdf_template.html | 114 +- .../spring_festival/activity_chart_pdf.html | 2 +- .../spring_festival/pdf_template.html | 34 +- 12 files changed, 811 insertions(+), 716 deletions(-) create mode 100644 scripts/debug_all_pdf_themes.py diff --git a/.gitignore b/.gitignore index 7cf5bb5..b360f37 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,10 @@ debug_simple.html debug_spring_festival.html debug_spring.html data/debug_data/avatar/cache.db +scripts/output_all/debug_format_test_group_*.pdf +scripts/output_all/debug_hack_test_group_*.pdf +scripts/output_all/debug_retro_futurism_test_group_*.pdf +scripts/output_all/debug_scrapbook_test_group_*.pdf +scripts/output_all/debug_simple_test_group_*.pdf +scripts/output_all/debug_spring_festival_test_group_*.pdf +scripts/data/avatar/cache.db diff --git a/scripts/debug_all_pdf_themes.py b/scripts/debug_all_pdf_themes.py new file mode 100644 index 0000000..641a2e3 --- /dev/null +++ b/scripts/debug_all_pdf_themes.py @@ -0,0 +1,235 @@ +import asyncio +import os +import sys +import types +from pathlib import Path + +# ========================================== +# 1. Environment Setup +# ========================================== +current_dir = os.path.dirname(os.path.abspath(__file__)) +plugin_root = os.path.abspath(os.path.join(current_dir, "..")) +sys.path.insert(0, plugin_root) + +# Mock astrbot.api +astrbot_api = types.ModuleType("astrbot.api") + + +class MockLogger: + def info(self, msg, *args, **kwargs): + print(f"[INFO] {msg}") + + def error(self, msg, *args, **kwargs): + print(f"[ERROR] {msg}") + + def warning(self, msg, *args, **kwargs): + print(f"[WARN] {msg}") + + def debug(self, msg, *args, **kwargs): + print(f"[DEBUG] {msg}") + + def log(self, level, msg, *args, **kwargs): + print(f"[LOG {level}] {msg}") + + def isEnabledFor(self, level): + return True + + +astrbot_api.logger = MockLogger() +astrbot_api.AstrBotConfig = dict +sys.modules["astrbot.api"] = astrbot_api + +# Mock astrbot.core.utils.astrbot_path +astrbot_core_utils = types.ModuleType("astrbot.core.utils") +astrbot_path = types.ModuleType("astrbot.core.utils.astrbot_path") +astrbot_path.get_astrbot_data_path = lambda: Path(".") +sys.modules["astrbot.core.utils"] = astrbot_core_utils +sys.modules["astrbot.core.utils.astrbot_path"] = astrbot_path + +# Now import plugin modules +from src.domain.entities.analysis_result import ( # noqa: E402 + ActivityVisualization, + EmojiStatistics, + GoldenQuote, + GroupStatistics, + SummaryTopic, + TokenUsage, + UserTitle, +) +from src.infrastructure.reporting.generators import ReportGenerator # noqa: E402 + + +# ========================================== +# 2. Mocks +# ========================================== +class MockConfigManager: + def __init__(self, theme="scrapbook"): + self.theme = theme + + def get_pdf_output_dir(self): + return os.path.join(current_dir, "output_all") + + def get_pdf_filename_format(self): + return f"debug_{self.theme}_{{group_id}}_{{date}}.pdf" + + def get_max_topics(self): + return 5 + + def get_max_user_titles(self): + return 8 + + def get_max_golden_quotes(self): + return 5 + + def get_report_template(self): + return self.theme + + def get_enable_user_card(self): + return True + + def get_t2i_max_concurrent(self): + return 2 + + @property + def playwright_available(self): + return True + + def get_browser_path(self): + return "" + + +async def mock_get_user_avatar(avatar_id: str, avatar_url_getter=None) -> str: + return f"https://q4.qlogo.cn/headimg_dl?dst_uin={avatar_id}&spec=640" + + +# ========================================== +# 3. Main Execution +# ========================================== +async def generate_theme(theme_name): + print(f"\n>>> Generating PDF for theme: {theme_name}") + config_manager = MockConfigManager(theme_name) + data_dir = Path(current_dir) / "data" + generator = ReportGenerator(config_manager, data_dir) + generator._get_user_avatar = mock_get_user_avatar + + stats = GroupStatistics( + message_count=1280, + total_characters=8500, + participant_count=42, + most_active_period="20:00-22:00", + emoji_count=156, + emoji_statistics=EmojiStatistics(face_count=100, mface_count=56), + activity_visualization=ActivityVisualization( + hourly_activity={i: (i * 5) % 65 for i in range(24)}, + daily_activity={"2026-03-25": 1280}, + ), + ) + + topics = [ + SummaryTopic( + topic="AstrBot新功能", + detail="大家对PDF生成功能的讨论非常热烈。", + contributors=["开发者", "测试员"], + ), + SummaryTopic( + topic="代码调试", + detail="关于Python异步编程的深入探讨。", + contributors=["大神"], + ), + SummaryTopic( + topic="周三摸鱼", + detail="今天群里大家都在发表情包。", + contributors=["全体群友"], + ), + ] + + user_titles = [ + UserTitle( + name="Simon", + user_id="10001", + title="极客之魂", + mbti="INTJ", + reason="完成了所有PDF模板的优化。", + ), + UserTitle( + name="Bot", + user_id="10002", + title="全能助手", + mbti="ENFJ", + reason="一直在努力工作。", + ), + ] + + stats.golden_quotes = [ + GoldenQuote( + sender="Simon", + content="这PDF现在看起来高端多了!", + reason="这是真的。", + user_id="10001", + ), + GoldenQuote( + sender="User", + content="能不能不分页?", + reason="用户反馈。", + user_id="10002", + ), + ] + stats.token_usage = TokenUsage( + prompt_tokens=2500, completion_tokens=1500, total_tokens=4000 + ) + + class MockDimension: + def __init__(self, name, percentage, comment, color): + self.name, self.percentage, self.comment, self.color = ( + name, + percentage, + comment, + color, + ) + + class MockChatQualityReview: + def __init__(self): + self.title = "今日群聊质量分析" + self.subtitle = "基于AI的深度神经网络评估" + self.dimensions = [ + MockDimension("讨论活跃度", 95, "大家讨论非常热烈", "#FF5722"), + MockDimension("情感温度", 88, "群内氛围融洽", "#E91E63"), + ] + self.summary = "整体聊天的质量很高,技术讨论和日常闲聊达到了完美的平衡。" + + analysis_result = { + "statistics": stats, + "topics": topics, + "user_titles": user_titles, + "chat_quality_review": MockChatQualityReview(), + "user_analysis": {"10001": {"nickname": "Simon"}, "10002": {"nickname": "Bot"}}, + "token_usage": stats.token_usage, + } + + try: + pdf_path = await generator.generate_pdf_report( + analysis_result, group_id="test_group" + ) + if pdf_path and os.path.exists(pdf_path): + print(f"[SUCCESS] {theme_name} PDF generated: {pdf_path}") + else: + print(f"[FAILURE] {theme_name} PDF generation failed or file not found.") + except Exception as e: + print(f"[ERROR] Failed to generate {theme_name}: {e}") + + +async def main(): + themes = [ + "scrapbook", + "hack", + "retro_futurism", + "simple", + "spring_festival", + "format", + ] + for theme in themes: + await generate_theme(theme) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/scripts/mock_pdf_gen.py b/scripts/mock_pdf_gen.py index 0742395..d46ec71 100644 --- a/scripts/mock_pdf_gen.py +++ b/scripts/mock_pdf_gen.py @@ -254,7 +254,7 @@ async def main(): self.dimensions = [ MockDimension("讨论活跃度", 85, "大家讨论非常热烈", "#FF5722"), MockDimension("知识共享度", 70, "分享了很多有用的技术信息", "#4CAF50"), - MockDimension("情感温度", 92, "群内氛围非常融洽", "#E91E63") + MockDimension("情感温度", 92, "群内氛围非常融洽", "#E91E63"), ] self.summary = "整体聊天的质量很高,技术讨论和日常闲聊达到了完美的平衡。" diff --git a/src/infrastructure/reporting/templates/format/pdf_template.html b/src/infrastructure/reporting/templates/format/pdf_template.html index eb57ba1..8d1e7ce 100644 --- a/src/infrastructure/reporting/templates/format/pdf_template.html +++ b/src/infrastructure/reporting/templates/format/pdf_template.html @@ -9,6 +9,11 @@ href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;600;700&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet"> @@ -745,138 +443,118 @@
-
-
-
-
-
System Report // {{current_date}}

Daily
Analysis
Export

-
-
- - - -
-
- - - -
-
- - - -
-

- Daily activity analysis report, generated based on message statistics and AI analysis + Activity analysis report, generated based on message statistics and AI analysis

-
+
-
Messages
{{message_count}}
-
累计消息数量
-
- Participants + Users
{{participant_count}}
-
参与者计数
-
- Characters + Chars
{{total_characters}}
-
文本总字数
-
Emoji
{{emoji_count}}
-
表情符号统计
- -
-
-
- - - -
-
Peak Activity Window
-
{{most_active_period}}
-
活跃序列 SCAN // 消息流强度 {{message_count}}
+
+
+ + + +
STABILITY_METRICS.LOG
+
+
PEAK WINDOW SCAN
+
{{most_active_period}}
+
+ INTENSITY: {{message_count}} MSGS/DAY + STATUS: OPERATIONAL
- -
-
-
- {{hourly_chart_html|safe}} -
-
+
+ {{hourly_chart_html|safe}}
- + {% if topics_html %}
{{topics_html|safe}}
+ {% endif %} - - {% endfor %} diff --git a/src/infrastructure/reporting/templates/spring_festival/pdf_template.html b/src/infrastructure/reporting/templates/spring_festival/pdf_template.html index f1915c2..6fe538b 100644 --- a/src/infrastructure/reporting/templates/spring_festival/pdf_template.html +++ b/src/infrastructure/reporting/templates/spring_festival/pdf_template.html @@ -42,11 +42,12 @@ } .layout { - max-width: 900px; + width: 100%; + max-width: 190mm; margin: 0 auto; - padding: 40px; - border: 10px solid var(--red-festive); - min-height: 100vh; + padding: 10mm; + border: 10mm solid var(--red-festive); + min-height: 297mm; position: relative; } @@ -266,6 +267,14 @@ padding-top: 20px; font-size: 0.8rem; color: var(--red-festive); + display: flex; + flex-direction: column; + gap: 8px; + } + + .footer-repo a { + color: var(--red-festive); + text-decoration: underline; } .avoid-break { @@ -317,12 +326,25 @@ {{titles_html | safe}}
- + + {% if chat_quality_html %} +
智睿点评 // Chat Quality
+ {{ chat_quality_html | safe }} + {% endif %} +
每日金句回响
{{quotes_html | safe}}