fix(documents): preserve DOCX table content
This commit is contained in:
@@ -210,6 +210,8 @@ def _extract_docx(path: Path) -> str:
|
||||
"""Extract text from DOCX using python-docx."""
|
||||
try:
|
||||
from docx import Document as DocxDocument
|
||||
from docx.table import Table
|
||||
from docx.text.paragraph import Paragraph
|
||||
except ImportError:
|
||||
return "[error: python-docx not installed]"
|
||||
try:
|
||||
@@ -217,10 +219,30 @@ def _extract_docx(path: Path) -> str:
|
||||
return error
|
||||
doc = DocxDocument(path)
|
||||
collector = _TextCollector(_MAX_TEXT_LENGTH)
|
||||
for paragraph in doc.paragraphs:
|
||||
text = paragraph.text.strip()
|
||||
for block in doc.iter_inner_content():
|
||||
if isinstance(block, Paragraph):
|
||||
text = block.text.strip()
|
||||
if text and not collector.add(text, separator="\n\n"):
|
||||
break
|
||||
continue
|
||||
if not isinstance(block, Table):
|
||||
continue
|
||||
first_row = True
|
||||
for row in block.rows:
|
||||
cells: list[str] = []
|
||||
seen_cells: set[int] = set()
|
||||
for cell in row.cells:
|
||||
cell_id = id(cell._tc)
|
||||
if cell_id in seen_cells:
|
||||
continue
|
||||
seen_cells.add(cell_id)
|
||||
cells.append(" ".join(cell.text.split()))
|
||||
if not any(cells):
|
||||
continue
|
||||
separator = "\n\n" if first_row else "\n"
|
||||
first_row = False
|
||||
if not collector.add("\t".join(cells), separator=separator):
|
||||
return collector.render()
|
||||
return collector.render()
|
||||
except Exception as e:
|
||||
logger.exception("Failed to extract DOCX {}", path)
|
||||
|
||||
@@ -93,6 +93,26 @@ def test_drain_pending_path_preserves_document_text(tmp_path: Path) -> None:
|
||||
assert "summarize" in result
|
||||
|
||||
|
||||
def test_drain_pending_path_preserves_docx_table_text(tmp_path: Path) -> None:
|
||||
"""Uploaded Word forms must retain content stored in table cells."""
|
||||
from docx import Document
|
||||
|
||||
doc = Document()
|
||||
table = doc.add_table(rows=2, cols=2)
|
||||
table.cell(0, 0).text = "Applicant"
|
||||
table.cell(0, 1).text = "Ada Lovelace"
|
||||
table.cell(1, 0).text = "Research area"
|
||||
table.cell(1, 1).text = "Analytical engines"
|
||||
docx_path = tmp_path / "application.docx"
|
||||
doc.save(docx_path)
|
||||
|
||||
content, image_only = extract_documents("summarize", [str(docx_path)])
|
||||
|
||||
assert image_only == []
|
||||
assert "Applicant\tAda Lovelace" in content
|
||||
assert "Research area\tAnalytical engines" in content
|
||||
|
||||
|
||||
def test_drain_pending_path_without_extract_loses_document(tmp_path: Path) -> None:
|
||||
"""Demonstrates the BUG: if _drain_pending calls _build_user_content
|
||||
directly without extract_documents, document content is lost."""
|
||||
|
||||
@@ -174,6 +174,29 @@ class TestExtractText:
|
||||
assert "This is paragraph one." in result
|
||||
assert "This is paragraph two." in result
|
||||
|
||||
def test_extract_text_docx_preserves_paragraph_and_table_order(self, tmp_path: Path):
|
||||
"""DOCX forms commonly keep nearly all meaningful content in tables."""
|
||||
from docx import Document
|
||||
|
||||
docx_file = tmp_path / "form.docx"
|
||||
doc = Document()
|
||||
doc.add_paragraph("Applicant details")
|
||||
table = doc.add_table(rows=2, cols=2)
|
||||
table.cell(0, 0).text = "Name"
|
||||
table.cell(0, 1).text = "Ada Lovelace"
|
||||
table.cell(1, 0).text = "Project"
|
||||
table.cell(1, 1).text = "Analytical Engine"
|
||||
doc.add_paragraph("End of form")
|
||||
doc.save(docx_file)
|
||||
|
||||
result = extract_text(docx_file)
|
||||
|
||||
assert result is not None
|
||||
assert "Name\tAda Lovelace" in result
|
||||
assert "Project\tAnalytical Engine" in result
|
||||
assert result.index("Applicant details") < result.index("Name\tAda Lovelace")
|
||||
assert result.index("Analytical Engine") < result.index("End of form")
|
||||
|
||||
def test_extract_text_docx_empty(self, tmp_path: Path):
|
||||
"""Test extracting text from an empty .docx file."""
|
||||
from docx import Document
|
||||
|
||||
Reference in New Issue
Block a user