#!/usr/bin/env python3 """ 周报汇总工具 - 从归档的周报中提取指定时间范围的工作内容 用于月度、年度考核时快速生成工作总结 用法: python3 summarize_reports.py --week 2026-W31 python3 summarize_reports.py --month 2026-07 python3 summarize_reports.py --year 2026 python3 summarize_reports.py --range 2026-07-01 2026-09-30 """ import os import re import glob import argparse from datetime import datetime, timedelta from pathlib import Path # 周报归档目录 REPORTS_DIR = Path(__file__).parent def parse_week_number(week_str): """解析周数格式,如 2026-W31""" match = re.match(r'(\d{4})-W(\d{2})', week_str) if not match: return None year, week = int(match.group(1)), int(match.group(2)) # 计算该周的大致日期范围(简化处理,取第 1 周的周一) jan_first = datetime(year, 1, 1) week_start = jan_first + timedelta(weeks=week - 1) # 调整到最近的周一 week_start = week_start - timedelta(days=week_start.weekday()) week_end = week_start + timedelta(days=4) # 周五 return week_start, week_end def parse_month_range(month_str): """解析月份格式,如 2026-07""" match = re.match(r'(\d{4})-(\d{2})', month_str) if not match: return None year, month = int(match.group(1)), int(match.group(2)) month_start = datetime(year, month, 1) if month == 12: next_month = datetime(year + 1, 1, 1) else: next_month = datetime(year, month + 1, 1) month_end = next_month - timedelta(days=1) return month_start, month_end def parse_year_range(year_str): """解析年份格式,如 2026""" match = re.match(r'(\d{4})', year_str) if not match: return None year = int(match.group(1)) year_start = datetime(year, 1, 1) year_end = datetime(year, 12, 31) return year_start, year_end def parse_date_range(start_str, end_str): """解析日期范围,如 2026-07-01 2026-09-30""" try: start = datetime.strptime(start_str, '%Y-%m-%d') end = datetime.strptime(end_str, '%Y-%m-%d') return start, end except ValueError: return None def find_reports_in_range(start_date, end_date): """在指定日期范围内查找所有周报文件""" reports = [] # 遍历所有年份目录 for year_dir in REPORTS_DIR.iterdir(): if not year_dir.is_dir() or year_dir.name.startswith('.'): continue # 查找该年份下的所有周报文件 for report_file in glob.glob(str(year_dir / '*.md')): if report_file.endswith('README.md'): continue try: with open(report_file, 'r', encoding='utf-8') as f: content = f.read() # 尝试从文件内容中提取日期范围 date_match = re.search(r'(\d{4}-\d{2}-\d{2})\s*~\s*(\d{4}-\d{2}-\d{2})', content) if date_match: file_start = datetime.strptime(date_match.group(1), '%Y-%m-%d') file_end = datetime.strptime(date_match.group(2), '%Y-%m-%d') # 检查是否在查询范围内 if file_start <= end_date and file_end >= start_date: reports.append({ 'file': report_file, 'start': file_start, 'end': file_end, 'content': content }) except Exception as e: print(f"读取文件失败 {report_file}: {e}") continue # 按日期排序 reports.sort(key=lambda x: x['start']) return reports def extract_work_content(report_content): """从周报内容中提取工作内容""" sections = { 'project': '', 'task': '', 'daily_work': {}, 'problems': '' } lines = report_content.split('\n') current_section = None current_day = None for line in lines: # 项目名称 if line.startswith('项目名称:'): sections['project'] = line.replace('项目名称:', '').strip() # 主要任务 elif line.startswith('主要任务:'): sections['task'] = line.replace('主要任务:', '').strip() # 本周工作内容 elif '本周工作内容' in line: current_section = 'daily_work' # 存在问题 elif line.startswith('存在问题'): current_section = 'problems' current_day = None # 日期行(周一、周二等) elif current_section == 'daily_work' and re.match(r'^周 [一二三四五六日](\d{4}-\d{2}-\d{2})', line): match = re.match(r'^周 [一二三四五六日]((\d{4}-\d{2}-\d{2}))', line) current_day = match.group(1) sections['daily_work'][current_day] = [] # 工作内容项 elif current_section == 'daily_work' and line.strip().startswith('- '): if current_day: sections['daily_work'][current_day].append(line.strip()) # 问题内容 elif current_section == 'problems' and line.strip() and not line.startswith('存在问题'): sections['problems'] += line + '\n' return sections def generate_summary(reports, summary_type, date_range): """生成汇总报告""" output = [] # 汇总头部 start_str = date_range[0].strftime('%Y-%m-%d') end_str = date_range[1].strftime('%Y-%m-%d') output.append(f"# 工作总结报告") output.append(f"\n**时间范围:** {start_str} ~ {end_str}") output.append(f"**生成时间:** {datetime.now().strftime('%Y-%m-%d %H:%M')}") output.append(f"**包含周报数:** {len(reports)} 周") output.append("") # 按项目分组 projects = {} all_problems = [] all_daily_work = {} for report in reports: sections = extract_work_content(report['content']) project = sections['project'] or '未指定项目' if project not in projects: projects[project] = { 'tasks': [], 'weeks': [] } if sections['task']: projects[project]['tasks'].append({ 'week': f"{report['start'].strftime('%Y-W%W')}", 'task': sections['task'] }) projects[project]['weeks'].append(report['start'].strftime('%Y-W%W')) # 汇总问题 if sections['problems'].strip(): all_problems.append({ 'week': report['start'].strftime('%Y-W%W'), 'problems': sections['problems'].strip() }) # 汇总每日工作 for day, work_items in sections['daily_work'].items(): if day not in all_daily_work: all_daily_work[day] = [] all_daily_work[day].extend(work_items) # 输出项目汇总 output.append("## 📊 项目汇总\n") for project, data in projects.items(): output.append(f"### {project}") weeks = sorted(set(data['weeks'])) output.append(f"**参与周次:** {', '.join(weeks)}") output.append(f"**主要任务:**") for item in data['tasks']: output.append(f"- {item['week']}: {item['task']}") output.append("") # 输出存在问题汇总 if all_problems: output.append("## ⚠️ 存在问题汇总\n") for item in all_problems: output.append(f"### {item['week']}") output.append(item['problems']) output.append("") # 输出所有周报原文(可选) output.append("\n---\n") output.append("## 📋 原始周报记录\n") for report in reports: week_str = report['start'].strftime('%Y-W%W') output.append(f"\n### {week_str} ({report['start'].strftime('%Y-%m-%d')} ~ {report['end'].strftime('%Y-%m-%d')})") output.append(f"文件:{report['file']}\n") # 提取周报的前 20 行作为摘要 preview_lines = report['content'].split('\n')[:20] output.append('\n'.join(preview_lines)) output.append("\n... (更多内容见原文件)\n") return '\n'.join(output) def main(): parser = argparse.ArgumentParser(description='周报汇总工具') group = parser.add_mutually_exclusive_group(required=True) group.add_argument('--week', type=str, help='指定周数,如 2026-W31') group.add_argument('--month', type=str, help='指定月份,如 2026-07') group.add_argument('--year', type=str, help='指定年份,如 2026') group.add_argument('--range', type=str, nargs=2, metavar=('START', 'END'), help='指定日期范围,如 2026-07-01 2026-09-30') args = parser.parse_args() # 解析日期范围 date_range = None summary_type = None if args.week: date_range = parse_week_number(args.week) summary_type = 'week' elif args.month: date_range = parse_month_range(args.month) summary_type = 'month' elif args.year: date_range = parse_year_range(args.year) summary_type = 'year' elif args.range: date_range = parse_date_range(args.range[0], args.range[1]) summary_type = 'range' if not date_range: print("❌ 日期格式错误") return 1 print(f"🔍 查找范围:{date_range[0].strftime('%Y-%m-%d')} ~ {date_range[1].strftime('%Y-%m-%d')}") # 查找周报 reports = find_reports_in_range(date_range[0], date_range[1]) if not reports: print("❌ 未找到符合条件的周报") return 1 print(f"✅ 找到 {len(reports)} 份周报") # 生成汇总 summary = generate_summary(reports, summary_type, date_range) # 输出到文件 output_file = f"summary_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md" with open(output_file, 'w', encoding='utf-8') as f: f.write(summary) print(f"📄 汇总报告已生成:{output_file}") print("\n" + "="*50) print(summary[:2000]) # 预览前 2000 字符 if len(summary) > 2000: print(f"\n... (共 {len(summary)} 字符,完整内容见 {output_file})") return 0 if __name__ == '__main__': exit(main())