#!/usr/bin/env python3 # -*- coding: utf-8 -*- """从 weekly-reports/2026/*.md(标准格式)重建 SQLite 周报数据。 对已存在的 (year,week,project) 记录执行 UPSERT(先删后插),保证与归档文件一致。 """ import glob import os import re import sqlite3 BASE = "/home/yangxuan/.openclaw/workspace-resume/weekly-reports" ARCHIVE_DIR = os.path.join(BASE, "2026") DB = os.path.join(BASE, "weekly_reports.db") WEEKDAY_MAP = {"周一": 1, "周二": 2, "周三": 3, "周四": 4, "周五": 5, "周六": 6, "周日": 7} def parse_md(path): """解析标准格式 Markdown,返回 dict""" with open(path, encoding="utf-8") as f: text = f.read() title_m = re.search(r"^#\s*((?:G5|G6))\s*开发周报\s*\(2026-W(\d{2})\)", text, re.M) project = title_m.group(1) if title_m else "G5" week = int(title_m.group(2)) if title_m else None start = end = None m = re.search(r"\*\*日期范围:\*\*\s*(\d{4}-\d{2}-\d{2})\s*~\s*(\d{4}-\d{2}-\d{2})", text) if m: start, end = m.group(1), m.group(2) main_task = "" m = re.search(r"主要任务:\s*(.+)", text) if m: main_task = m.group(1).strip() days = [] cur_weekday = None probs = [] in_prob = False for line in text.splitlines(): dm = re.match(r"^(周一|周二|周三|周四|周五|周六|周日)((\d{4}-\d{2}-\d{2}))", line) if dm: cur_weekday = WEEKDAY_MAP[dm.group(1)] days.append({"weekday": cur_weekday, "date": dm.group(2), "items": []}) in_prob = False continue if line.strip().startswith("## 存在问题"): in_prob = True continue if line.strip().startswith("## "): in_prob = False continue if line.strip().startswith("- ") or line.strip().startswith(" - "): item = re.sub(r"^\s*-\s*", "", line).strip() if not item: continue if in_prob and item not in ("无", "暂无"): probs.append(item) elif days: days[-1]["items"].append(item) return {"project": project, "week": week, "start": start, "end": end, "main_task": main_task, "days": days, "problems": probs} def main(): conn = sqlite3.connect(DB) cur = conn.cursor() files = sorted(glob.glob(os.path.join(ARCHIVE_DIR, "2026-W*-周报.md"))) total = 0 # 清空 2026 年全部周报(含级联明细/问题),用归档文件全量重建,保证一致 cur.execute("DELETE FROM weekly_report_daily WHERE report_id IN (SELECT id FROM weekly_reports WHERE year=2026)") cur.execute("DELETE FROM weekly_report_problems WHERE report_id IN (SELECT id FROM weekly_reports WHERE year=2026)") cur.execute("DELETE FROM weekly_reports WHERE year=2026") for path in files: data = parse_md(path) if data["week"] is None or not data["start"]: print(f"⏭ 跳过(无法解析):{os.path.basename(path)}") continue wnum = data["week"] # 先删除已有 (year, week, project) 记录(含级联明细/问题) cur.execute( "DELETE FROM weekly_reports WHERE year=? AND week_number=? AND project=?", (2026, wnum, data["project"])) cur.execute( """INSERT INTO weekly_reports (year, week_number, start_date, end_date, project, main_task, has_problems, file_path, word_count) VALUES (?,?,?,?,?,?,?,?,?)""", (2026, wnum, data["start"], data["end"], data["project"], data["main_task"], len(data["problems"]) > 0, path, len(data["main_task"]))) rid = cur.lastrowid for d in data["days"]: cur.execute( "INSERT INTO weekly_report_daily (report_id, day_of_week, work_date, work_items) VALUES (?,?,?,?)", (rid, d["weekday"], d["date"], "\n".join(d["items"]))) for p in data["problems"]: cur.execute( "INSERT INTO weekly_report_problems (report_id, problem_description) VALUES (?,?)", (rid, p)) total += 1 print(f"✅ W{wnum:02d} ({data['project']}) | {data['start']}~{data['end']} | " f"天{len(data['days'])} 问题{len(data['problems'])}") conn.commit() print(f"\n完成:共入库 {total} 份周报") # 统计 print("\n=== 数据库现况 ===") for row in cur.execute( "SELECT week_number, project, start_date, end_date, main_task, has_problems FROM weekly_reports WHERE year=2026 ORDER BY week_number"): print(f" W{row[0]:02d} | {row[1]} | {row[2]}~{row[3]} | 问题{row[5]} | {row[4][:28]}") conn.close() if __name__ == "__main__": main()