107 lines
4.7 KiB
Python
107 lines
4.7 KiB
Python
#!/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()
|