docs: Phase3 计划修订(Task1 前缀 test:、_heading_level 提取共享 helper、兜底分支覆盖)

This commit is contained in:
lhl
2026-08-10 09:09:40 +08:00
parent 129c75bb06
commit 7bc9175ebe
@@ -40,8 +40,6 @@
- [ ] **Step 1: 新建 docx_helpers.py**
```python
from pathlib import Path
from docx import Document
@@ -90,7 +88,7 @@ Expected: `ok`(工作目录为项目根 `D:\00_project\Genesis`
```bash
git add tests/docx_helpers.py
git commit -m "chore: docx 测试基建(新建/落盘/规则文档快捷构造)"
git commit -m "test: docx 测试基建(新建/落盘/规则文档快捷构造)"
```
---
@@ -102,7 +100,7 @@ git commit -m "chore: docx 测试基建(新建/落盘/规则文档快捷构造
- Test: `tests/test_word_template_parser.py`
**Interfaces:**
- Consumes: `genesis.data_models.ChapterMarker` / `genesis.data_models.ParsedTemplate`
- Consumes: `genesis.data_models.ChapterMarker` / `genesis.data_models.ParsedTemplate``genesis.parsers._word_common.heading_level`**本任务新建**共享 helperTask 3 复用)
- Produces:
- `class WordTemplateParser`,方法 `parse(path: str | Path) -> ParsedTemplate`
- 内部正则 `PLACEHOLDER_RE``r"\{\{([a-z][a-z0-9_]*)(?::([a-z][a-z0-9_]*))?\}\}"`(两组:group1=键名,group2=可选章节名)
@@ -116,11 +114,24 @@ Create `tests/test_word_template_parser.py`:
```python
from genesis.data_models import ChapterMarker, ParsedTemplate
from genesis.parsers._word_common import heading_level
from genesis.parsers.word_template_parser import WordTemplateParser
from tests.docx_helpers import new_document, save_document
def test_heading_level_parses_numeric_suffix():
assert heading_level("Heading 1") == 1
assert heading_level("Heading 2") == 2
assert heading_level("Heading 3") == 3
def test_heading_level_fallback_on_invalid():
# 兜底分支(非数字 / 无后缀)→ 1,分支覆盖必须命中
assert heading_level("Heading X") == 1
assert heading_level("Heading") == 1
def test_parse_extracts_heading_levels(tmp_path):
doc = new_document()
doc.add_heading("1. はじめに", level=1)
@@ -210,9 +221,25 @@ def test_parse_styles_collected(tmp_path):
- [ ] **Step 2: 运行测试确认失败**
Run: `python -m pytest tests/test_word_template_parser.py -v`
Expected: FAIL / ERROR`ModuleNotFoundError: No module named 'genesis.parsers.word_template_parser'`
Expected: FAIL / ERROR`ModuleNotFoundError: No module named 'genesis.parsers.word_template_parser'` / `_word_common`
- [ ] **Step 3: 写最小实现**
- [ ] **Step 3: 写最小实现(含共享 helper**
Create `src/genesis/parsers/_word_common.py`(Task 2/3 共享,避免重复定义):
```python
"""Word 解析共享小工具(WordTemplateParser / RuleDocParser 复用)。"""
from __future__ import annotations
def heading_level(style_name: str) -> int:
"""从 Heading N 样式名解析大纲级别;非数字/无后缀兜底 1。"""
try:
return int(style_name.split()[-1])
except (ValueError, IndexError):
return 1
```
Create `src/genesis/parsers/word_template_parser.py`:
@@ -226,19 +253,12 @@ from docx import Document
from docx.oxml.ns import qn
from genesis.data_models import ChapterMarker, ParsedTemplate
from genesis.parsers._word_common import heading_level
# 统一占位符正则:{{键名}} 或 {{键名:章节名}}spec §3.2
PLACEHOLDER_RE = re.compile(r"\{\{([a-z][a-z0-9_]*)(?::([a-z][a-z0-9_]*))?\}\}")
def _heading_level(style_name: str) -> int:
"""从 Heading N 样式名解析大纲级别;非数字兜底 1。"""
try:
return int(style_name.split()[-1])
except (ValueError, IndexError):
return 1
class WordTemplateParser:
"""概要设计模板 docx 解析:章构成 / 占位符 / 样式名提取。"""
@@ -258,7 +278,7 @@ class WordTemplateParser:
if style_name.startswith("Heading"):
sections.append(ChapterMarker(
type="heading", name=text, level=_heading_level(style_name)
type="heading", name=text, level=heading_level(style_name)
))
for m in PLACEHOLDER_RE.finditer(text):
@@ -286,17 +306,17 @@ class WordTemplateParser:
- [ ] **Step 4: 运行测试确认通过**
Run: `python -m pytest tests/test_word_template_parser.py -v`
Expected: 6 passed
Expected: 8 passed6 解析用例 + 2 共享 helper 兜底分支用例)
- [ ] **Step 5: 全量回归**
Run: `python -m pytest -q`
Expected: 138 passed / 100.00%132 基线 + 6 新增),fail_under=99 达标
Expected: 140 passed / 100.00%132 基线 + 8 新增),fail_under=99 达标branch 模式)
- [ ] **Step 6: 提交**
```bash
git add tests/test_word_template_parser.py src/genesis/parsers/word_template_parser.py
git add tests/test_word_template_parser.py src/genesis/parsers/_word_common.py src/genesis/parsers/word_template_parser.py
git commit -m "feat: WordTemplateParser 章构成/占位符/样式名提取"
```
@@ -309,7 +329,7 @@ git commit -m "feat: WordTemplateParser 章构成/占位符/样式名提取"
- Test: `tests/test_rule_doc_parser.py`
**Interfaces:**
- Consumes: `genesis.data_models.RuleDocument``tests/docx_helpers.make_rule_doc`
- Consumes: `genesis.data_models.RuleDocument``genesis.parsers._word_common.heading_level`Task 2 共享 helper);`tests.docx_helpers.make_rule_doc`
- Produces:
- `class RuleDocParser`,方法 `parse(path: str | Path, category: str = "write") -> RuleDocument`
- Markdown 化:Heading N → `#`×N`・`/`-`/`•` 前缀文本或 List Bullet/Number 样式 → `- ` 项;普通段落 → 原文;表格 → GFM(表头+分隔行+数据行);空段 → 空行
@@ -422,18 +442,12 @@ from docx import Document
from docx.oxml.ns import qn
from genesis.data_models import RuleDocument
from genesis.parsers._word_common import heading_level
# 列表项前缀(真实样本为 Normal 样式 + ・ 前缀,样式名通道不足以命中)
LIST_PREFIXES = ("", "-", "")
def _heading_level(style_name: str) -> int:
try:
return int(style_name.split()[-1])
except (ValueError, IndexError):
return 1
def _is_list_item(text: str, style_name: str) -> bool:
"""双通道列表检测:List 样式或文本前缀(spec §3.3)。"""
if "List" in style_name:
@@ -471,7 +485,7 @@ class RuleDocParser:
if not stripped:
md.append("")
elif style_name.startswith("Heading"):
md.append("#" * _heading_level(style_name) + " " + stripped)
md.append("#" * heading_level(style_name) + " " + stripped)
elif _is_list_item(stripped, style_name):
md.append("- " + stripped.lstrip("・-•").strip())
else:
@@ -502,7 +516,7 @@ Expected: 6 passed
- [ ] **Step 5: 全量回归**
Run: `python -m pytest -q`
Expected: 144 passed / 100.00%138 + 6 新增),fail_under=99 达标
Expected: 146 passed / 100.00%140 + 6 新增),fail_under=99 达标
- [ ] **Step 6: 提交**
@@ -699,7 +713,7 @@ Expected: 6 passed
- [ ] **Step 5: 全量回归**
Run: `python -m pytest -q`
Expected: 150 passed / 100.00%144 + 6 新增),fail_under=99 达标
Expected: 152 passed / 100.00%146 + 6 新增),fail_under=99 达标
- [ ] **Step 6: 提交**
@@ -797,7 +811,7 @@ Expected: 8 passed(现有 4 Excel + 新增 4 Word
- [ ] **Step 3: 全量回归(红线验证)**
Run: `python -m pytest -q`
Expected: 154 passed / 100.00%150 + 4 新增),fail_under=99 达标
Expected: 156 passed / 100.00%152 + 4 新增),fail_under=99 达标
- [ ] **Step 4: 提交**