feat: RuleDocParser 规则文档 Markdown 化与分类

This commit is contained in:
lhl
2026-08-10 10:14:15 +08:00
parent 00fcbf18c3
commit 9ab11da87d
3 changed files with 154 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
from __future__ import annotations
import hashlib
from pathlib import Path
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 _is_list_item(text: str, style_name: str) -> bool:
"""双通道列表检测:List 样式或文本前缀(spec §3.3)。"""
if "List" in style_name:
return True
return text.startswith(LIST_PREFIXES)
def _table_to_markdown(table) -> list[str]:
"""docx 表格 → GFM 表格行。"""
lines: list[str] = []
for r_idx, row in enumerate(table.rows):
cells = [cell.text.replace("|", "\\|").strip() for cell in row.cells]
lines.append("| " + " | ".join(cells) + " |")
if r_idx == 0:
lines.append("| " + " | ".join(["---"] * len(cells)) + " |")
return lines
class RuleDocParser:
"""规则文档 docx 解析:Markdown 化 + 分类。"""
def parse(self, path: str | Path, category: str = "write") -> RuleDocument:
doc = Document(str(path))
md: list[str] = []
# 按文档顺序遍历段落与表格(body 级子元素)
for child in doc.element.body.iterchildren():
tag = child.tag
if tag == qn("w:p"):
from docx.text.paragraph import Paragraph
para = Paragraph(child, doc)
text = para.text
style_name = para.style.name if para.style else "Normal"
stripped = text.strip()
if not stripped:
md.append("")
elif style_name.startswith("Heading"):
md.append("#" * heading_level(style_name) + " " + stripped)
elif _is_list_item(stripped, style_name):
md.append("- " + stripped.lstrip("・-•").strip())
else:
md.append(stripped)
elif tag == qn("w:tbl"):
from docx.table import Table
md.extend(_table_to_markdown(Table(child, doc)))
md.append("")
content = "\n".join(md).strip()
return RuleDocument(
file_name=Path(path).name,
category=category,
markdown_content=content,
source_path=str(path),
file_type="word",
hash=hashlib.sha256(Path(path).read_bytes()).hexdigest(),
)