"""Weekly work report synthesizer - reads daily work logs and generates weekly analysis.""" import logging import os import sys from datetime import datetime, timezone, timedelta sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from tools.config import load_secrets, get_output_dir from tools.llm import DeepSeekClient logger = logging.getLogger(__name__) TZ_BEIJING = timezone(timedelta(hours=8)) PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) WORK_WEEKLY_SYSTEM_PROMPT = """你是一个私人工作日志分析师。你的任务是阅读用户过去一周的每日工作日志,生成一篇有深度的周度工作分析。 ## 核心原则 1. **概述先行**:给出本周工作的整体面貌——主要精力投向了哪些领域、总体节奏如何。 2. **按主题组织,不按时间罗列**:不要"周一做了X、周二做了Y"。要提炼跨日的工作主线——哪些项目贯穿了整周?哪些任务是单日完成的? 3. **识别模式和趋势**:本周的工作节奏有什么特点?是否出现了新的工作类型?哪些工作在重复发生?是否有一些事在全周的工作日志中反复出现但没有被解决? 4. **产出导向**:本周有什么可量化的产出?写了什么材料?参加了什么会议?完成了什么节点? 5. **前瞻建议**:基于本周工作日志,下周需要关注什么?有什么待办被遗漏了? ## 输出格式 纯 Markdown,不要代码块包裹。 ### 1. 本周工作概览 一段话概括本周工作的底色——主要在忙什么、节奏如何、核心成果。 ### 2. 工作统计 | 维度 | 数值 | |:---|:---| | 有工作记录的天数 | X/7 | | 工作 Memos 总数 | X 条 | | 涉及标签 | #工作 #会议 ... | | 单日最高工作量 | 周X · X条 | ### 3. 深入分析 按主题组织: - 持续性的工作主线(跨日出现的项目/主题) - 本周重要节点(会议、截止日期、关键决策) - 工作节奏评价(是否有某天特别忙?是否有连续高压?) - 新出现的工作类型或关注点 - 被遗漏或被推迟的事项 ### 4. 下周关注 基于本周工作轨迹,列出下周需要重点关注的事项。具体到事项+时间节点。 --- 注意: - 如果某天没有工作日志,在统计中注明,但分析中跳过。 - 分析要有观点——不只是"做了什么",更要回答"这意味着什么"和"接下来该做什么"。 - 篇幅不设上限。""" def read_week_work_logs(week_start, week_end): """Read all daily work logs from week_start to week_end.""" all_texts = [] daily_root = get_output_dir("daily") current = week_start while current <= week_end: date_str = current.strftime("%Y-%m-%d") work_file = os.path.join(daily_root, date_str, "work", f"工作日志_{date_str}.md") if os.path.exists(work_file): with open(work_file, "r", encoding="utf-8") as f: text = f.read() all_texts.append((date_str, text)) logger.info(f" ✓ {date_str}: {len(text)} chars") else: logger.info(f" ✗ {date_str}: no work log") current += timedelta(days=1) return all_texts def extract_weekly_work_stats(all_texts): """Extract statistics from daily work logs.""" total_memos = 0 all_tags = set() highest_day = ("", 0) days_with_data = 0 for date_str, text in all_texts: if "今日无工作" in text: continue days_with_data += 1 # Count memos (each ### header with content) import re memo_count = len(re.findall(r'^###\s+\d{2}:\d{2}', text, re.MULTILINE)) total_memos += memo_count if memo_count > highest_day[1]: highest_day = (date_str, memo_count) # Extract tags tag_matches = re.findall(r'#(\w+)', text) work_tags = {"工作", "会议", "项目", "出差", "培训", "汇报", "材料", "接待", "调研"} for tag in tag_matches: if tag in work_tags: all_tags.add(tag) return { "days_with_data": days_with_data, "total_memos": total_memos, "top_day": highest_day, "tags": sorted(all_tags), } def generate_weekly_work_report(): """Main entry: generate weekly work report.""" now = datetime.now(TZ_BEIJING) yesterday = now - timedelta(days=1) week_end = yesterday.replace(hour=23, minute=59, second=59) week_start = (week_end - timedelta(days=6)).replace(hour=0, minute=0, second=0) logger.info(f"Week range: {week_start.strftime('%Y-%m-%d')} → {week_end.strftime('%Y-%m-%d')}") # Step 1: Read daily work logs all_texts = read_week_work_logs(week_start, week_end) if not all_texts: logger.warning("No work logs found this week") return None # Step 2: Extract stats stats = extract_weekly_work_stats(all_texts) # Step 3: Build context context_parts = [] context_parts.append(f"## 本周工作统计\n") context_parts.append(f"- 有工作记录的天数:{stats['days_with_data']}/7") context_parts.append(f"- 工作 Memos 总数:{stats['total_memos']} 条") context_parts.append(f"- 涉及标签:{' '.join('#' + t for t in stats['tags'])}") if stats['top_day'][0]: context_parts.append(f"- 单日最高:{stats['top_day'][0]} · {stats['top_day'][1]}条") context_parts.append("") context_parts.append("## 每日工作日志原文\n") for date_str, text in all_texts: context_parts.append(f"### {date_str}\n") context_parts.append(text[:3500]) # Trim each day context_parts.append("\n---\n") full_context = "\n".join(context_parts) # Step 4: Call DeepSeek API secrets = load_secrets() client = DeepSeekClient(secrets["deepseek_api_key"]) logger.info(f"Sending to DeepSeek... ({len(full_context)} chars)") result = client.chat( system_prompt=WORK_WEEKLY_SYSTEM_PROMPT, user_message=full_context, temperature=0.5, ) # Step 5: Assemble and write year = week_start.strftime("%Y") week_num = week_start.isocalendar()[1] week_start_str = week_start.strftime("%m%d") week_end_str = week_end.strftime("%m%d") filename = f"工作周报_W{week_num}_{week_start_str}-{week_end_str}.md" header = f"""# 工作周报 · {week_start.strftime('%Y.%m.%d')} — {week_end.strftime('%Y.%m.%d')} > 基于 {stats['days_with_data']}/7 天工作日志生成 · 依托 DeepSeek API 分析 """ final_report = header + result output_dir = os.path.join(get_output_dir("weekly"), "work") os.makedirs(output_dir, exist_ok=True) output_path = os.path.join(output_dir, filename) with open(output_path, "w", encoding="utf-8") as f: f.write(final_report) logger.info(f"Report saved to {output_path}") return output_path if __name__ == "__main__": logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") path = generate_weekly_work_report() if path: print(f"DONE: {path}") else: print("FAILED: No report generated") sys.exit(1)