From 6cd1288b181dd4338c091a81c42c38bc3cd2a218 Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 15 Jun 2026 14:53:01 +0800 Subject: [PATCH] fix: preserve source body for missing summaries --- scripts/import_course_summaries.py | 65 ++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/scripts/import_course_summaries.py b/scripts/import_course_summaries.py index e5a847d..2041402 100644 --- a/scripts/import_course_summaries.py +++ b/scripts/import_course_summaries.py @@ -13,6 +13,7 @@ sys.path.insert(0, str(ROOT)) from app.data import ( # noqa: E402 append_operation_log, + course_summary_to_class_record_line, course_summary_semantic_key, create_course_summary_review_task, normalize_course_summary, @@ -192,7 +193,59 @@ def parse_missing_table(path: Path) -> list[dict]: return rows -def import_missing_tasks(missing_table: Path, tasks_path: Path, dry_run: bool) -> int: +def time_range_from_text(text: str) -> str: + match = re.search(r"(\d{1,2})[::](\d{2})\s*[--–—]\s*(\d{1,2})[::](\d{2})", text) + if not match: + return "" + start_hour, start_minute, end_hour, end_minute = map(int, match.groups()) + start = start_hour * 60 + start_minute + end = end_hour * 60 + end_minute + if end <= start: + return "" + return f"{start_hour:02d}:{start_minute:02d}-{end_hour:02d}:{end_minute:02d}" + + +def duration_from_time_range(time_range: str) -> tuple[str, int | None]: + match = re.fullmatch(r"(\d{2}):(\d{2})-(\d{2}):(\d{2})", time_range) + if not match: + return "", None + start_hour, start_minute, end_hour, end_minute = map(int, match.groups()) + minutes = end_hour * 60 + end_minute - (start_hour * 60 + start_minute) + if minutes <= 0: + return "", None + return f"{minutes // 60}小时{minutes % 60}分", minutes + + +def source_summary_for_missing_row(source: Path, row: dict) -> dict: + relative_path = str(row.get("来源文件") or "").strip() + if not relative_path: + return {} + source_path = source / relative_path + if not source_path.exists(): + return {} + + target_date = str(row.get("日期") or "").replace(".", "-") + best: dict = {} + for entry in iter_markdown_entries(source_path): + if entry.get("date_iso") != target_date: + continue + time_range = str(entry.get("time_range") or "") or time_range_from_text(str(entry.get("body") or "")) + duration, minutes = duration_from_time_range(time_range) + candidate = { + "title": entry.get("title", ""), + "body": entry.get("body", ""), + "time_range": time_range, + "duration": duration, + "duration_minutes": minutes, + } + if time_range: + return candidate + if not best: + best = candidate + return best + + +def import_missing_tasks(source: Path, missing_table: Path, tasks_path: Path, dry_run: bool) -> int: rows = parse_missing_table(missing_table) if not rows: return 0 @@ -220,12 +273,18 @@ def import_missing_tasks(missing_table: Path, tasks_path: Path, dry_run: bool) - "teacher_trusted": False, "remark": row.get("备注", ""), } + summary.update({key: value for key, value in source_summary_for_missing_row(source, row).items() if value not in ("", None)}) + proposed_line = "" + try: + proposed_line = course_summary_to_class_record_line(summary) + except ValueError: + pass created += 1 if not dry_run: create_course_summary_review_task( tasks_path, summary, - "", + proposed_line, ["历史 classnotes缺失导入,默认只进入审核,不自动扣课时"], saved_path=row.get("来源文件", ""), ) @@ -240,7 +299,7 @@ def main() -> None: missing_table = args.missing_table or (source / "classnotes缺失.txt") scanned, copied = copy_markdown_files(source, target, args.dry_run) imported, skipped = rebuild_state_from_markdown(target if target.exists() else source, args.state, args.dry_run) - review_tasks = import_missing_tasks(missing_table, args.tasks, args.dry_run) + review_tasks = import_missing_tasks(source, missing_table, args.tasks, args.dry_run) if not args.dry_run: append_operation_log( args.operation_logs,