feat: data_models 扩展 MixedParagraph/MixedSheet(段落容器)

This commit is contained in:
lhl
2026-08-09 04:04:14 +08:00
parent 0f137b945b
commit 539945be67
4 changed files with 47 additions and 4 deletions
+1
View File
@@ -33,3 +33,4 @@
| 2026-08-09 03:30 | 整体迭代 | 里程碑2 最终评审修正(must-fix x2):① find_header_row 结果未传入 extract_table(恒 header_row=0,标题行置上的 sheet 会损坏表头)→ excel_parser.py 结构化路径改用 find_header_row(filled) 并 <0 回落 0;② cell_formatting 死代码(CellValue.formatting 恒 None,取消线/背景色到不了下游)→ extract_table 新增可选 formatting_map 参数,excel_parser 遍历 ws.iter_rows 构建 fmt_map 传入。tests/test_excel_parser.py 新增 2 用例(标题行表头定位、strikethrough 回填);pytest 全量 41 passed | src/genesis/parsers/excel_parser.py, src/genesis/parsers/table_extractor.py, tests/test_excel_parser.py | deepseek-v4-flash-free | | 2026-08-09 03:30 | 整体迭代 | 里程碑2 最终评审修正(must-fix x2):① find_header_row 结果未传入 extract_table(恒 header_row=0,标题行置上的 sheet 会损坏表头)→ excel_parser.py 结构化路径改用 find_header_row(filled) 并 <0 回落 0;② cell_formatting 死代码(CellValue.formatting 恒 None,取消线/背景色到不了下游)→ extract_table 新增可选 formatting_map 参数,excel_parser 遍历 ws.iter_rows 构建 fmt_map 传入。tests/test_excel_parser.py 新增 2 用例(标题行表头定位、strikethrough 回填);pytest 全量 41 passed | src/genesis/parsers/excel_parser.py, src/genesis/parsers/table_extractor.py, tests/test_excel_parser.py | deepseek-v4-flash-free |
| 2026-08-09 03:59 | Agent 实现 | 里程碑2.5 Task9 实现:段落分割器 split_paragraphsMIXED 完整段落解析第一步,纯函数)。新建 src/genesis/parsers/paragraph_splitter.py_is_blank_row 行空白判定 + split_paragraphs(matrix)→list[(start,end)] 含端点 0-based,以全空行为界返回段落区间,空矩阵→[])与 tests/test_paragraph_splitter.py5 用例:空矩阵/单段落无空行/中间空行分割/尾部空行不产生多余段落/前导空行从首个非空行开始);TDD 验证 REDModuleNotFoundError: No module named 'genesis.parsers.paragraph_splitter')→ GREEN5 passed);pytest 全量 46 passed41 baseline + 5 新增);不依赖 openpyxl 与 data_models;提交 34ef28b | src/genesis/parsers/paragraph_splitter.py, tests/test_paragraph_splitter.py | deepseek-v4-flash-free | | 2026-08-09 03:59 | Agent 实现 | 里程碑2.5 Task9 实现:段落分割器 split_paragraphsMIXED 完整段落解析第一步,纯函数)。新建 src/genesis/parsers/paragraph_splitter.py_is_blank_row 行空白判定 + split_paragraphs(matrix)→list[(start,end)] 含端点 0-based,以全空行为界返回段落区间,空矩阵→[])与 tests/test_paragraph_splitter.py5 用例:空矩阵/单段落无空行/中间空行分割/尾部空行不产生多余段落/前导空行从首个非空行开始);TDD 验证 REDModuleNotFoundError: No module named 'genesis.parsers.paragraph_splitter')→ GREEN5 passed);pytest 全量 46 passed41 baseline + 5 新增);不依赖 openpyxl 与 data_models;提交 34ef28b | src/genesis/parsers/paragraph_splitter.py, tests/test_paragraph_splitter.py | deepseek-v4-flash-free |
| 2026-08-09 04:20 | Agent 实现 | 里程碑2.5 Task10 实现:data_models 扩展 MixedParagraph/MixedSheet 段落容器,并新增 ExcelParseResult.mixed 字段(默认空列表)。TDD:先追加 3 个测试(字段存在性与默认行为)确认 REDImportError: cannot import name 'MixedParagraph'),再在 data_models.py 末尾追加两个 dataclass(顶部 from typing 加 Literal、dataclasses 加 field),excel_parser.py 更新导入并为 ExcelParseResult 加 mixed 字段,确认 GREEN3 passed),全量 pytest 49 passed46 baseline + 3)。未修改既有类与字段,仅追加。 | src/genesis/data_models.py, src/genesis/parsers/excel_parser.py, tests/test_excel_parser.py | deepseek-v4-flash-free |
+20 -3
View File
@@ -1,8 +1,8 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass, field
from enum import Enum from enum import Enum
from typing import Any from typing import Any, Literal
class SheetType(Enum): class SheetType(Enum):
@@ -222,4 +222,21 @@ class StructuredSource:
rule_docs: list[RuleDocument] rule_docs: list[RuleDocument]
image_analyses: list[ImageAnalysis] image_analyses: list[ImageAnalysis]
existing_system: ExistingSystemInfo | None existing_system: ExistingSystemInfo | None
comments: list[CellComment] comments: list[CellComment]
@dataclass
class MixedParagraph:
"""混合 sheet 的一个段落(表格或自由文本)"""
kind: Literal["table", "free_text"]
matrix: list[list[Any]] | None = None # 该段原始矩阵(调试/重现)
table: ExcelTable | None = None # kind="table" 时填充
text: str | None = None # kind="free_text" 时填充(段全文)
source_range: tuple[int, int] | None = None # (first_row, last_row) 矩阵 0-based
@dataclass
class MixedSheet:
"""混合 sheet 的段落集合"""
name: str
paragraphs: list[MixedParagraph] = field(default_factory=list)
+2 -1
View File
@@ -3,7 +3,7 @@ from __future__ import annotations
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from genesis.data_models import CellComment, ExcelTable from genesis.data_models import CellComment, ExcelTable, MixedSheet
from genesis.parsers.excel_reader import open_workbook, sheet_matrix from genesis.parsers.excel_reader import open_workbook, sheet_matrix
from genesis.parsers.sheet_detector import detect_sheet_type from genesis.parsers.sheet_detector import detect_sheet_type
from genesis.parsers.sheet_nature import SheetNature, classify_sheet, find_header_row from genesis.parsers.sheet_nature import SheetNature, classify_sheet, find_header_row
@@ -19,6 +19,7 @@ class ExcelParseResult:
tables: list[ExcelTable] = field(default_factory=list) tables: list[ExcelTable] = field(default_factory=list)
comments: list[CellComment] = field(default_factory=list) comments: list[CellComment] = field(default_factory=list)
skipped: list[str] = field(default_factory=list) skipped: list[str] = field(default_factory=list)
mixed: list[MixedSheet] = field(default_factory=list)
class ExcelParser: class ExcelParser:
+24
View File
@@ -57,3 +57,27 @@ def test_parse_attaches_strikethrough_formatting(tmp_path):
cv = result.tables[0].rows[0]["機能ID"] cv = result.tables[0].rows[0]["機能ID"]
assert cv.formatting is not None assert cv.formatting is not None
assert cv.formatting.strikethrough is True assert cv.formatting.strikethrough is True
from genesis.data_models import MixedParagraph, MixedSheet
from genesis.parsers.excel_parser import ExcelParseResult
def test_excel_parse_result_has_mixed_default():
r = ExcelParseResult(file_name="f.xlsx")
assert r.mixed == []
def test_mixed_paragraph_defaults():
p = MixedParagraph(kind="table")
assert p.table is None
assert p.text is None
assert p.source_range is None
def test_mixed_sheet_holds_paragraphs():
p1 = MixedParagraph(kind="table")
p2 = MixedParagraph(kind="free_text", text="备注")
ms = MixedSheet(name="混合", paragraphs=[p1, p2])
assert ms.name == "混合"
assert [p.kind for p in ms.paragraphs] == ["table", "free_text"]