- extract_attachment now dispatches PDF/DOCX/TXT/HTML; DOCX via python-docx, text-like via plain read with cheap HTML strip. - combine.py writes one combined.md per comment dir with YAML frontmatter (per-attachment status + chars) and per-section body. - extract_comment is idempotent (existing combined.md is a no-op unless force=True). - parse_combined() splits frontmatter ↔ body for downstream consumers. Walker + DuckDB view land in the next batch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
45 lines
1.2 KiB
Python
45 lines
1.2 KiB
Python
"""DOCX extraction via python-docx."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import docx
|
|
|
|
from rex.comments import extract_attachment
|
|
|
|
|
|
def _make_docx(path: Path, paragraphs: list[str]) -> Path:
|
|
d = docx.Document()
|
|
for p in paragraphs:
|
|
d.add_paragraph(p)
|
|
d.save(str(path))
|
|
return path
|
|
|
|
|
|
def test_extract_docx_with_text(tmp_path: Path):
|
|
p = _make_docx(
|
|
tmp_path / "a.docx",
|
|
["First paragraph of comment letter.", "Second paragraph with details."],
|
|
)
|
|
result = extract_attachment(p)
|
|
assert result.status == "ok"
|
|
assert "First paragraph" in result.text
|
|
assert "Second paragraph" in result.text
|
|
|
|
|
|
def test_extract_docx_empty_marked_ocr_needed(tmp_path: Path):
|
|
p = _make_docx(tmp_path / "empty.docx", [])
|
|
result = extract_attachment(p)
|
|
# No paragraphs ⇒ 0 chars ⇒ ocr_needed (we treat empty docx the same as
|
|
# an image-only pdf — body lives somewhere we can't read it).
|
|
assert result.status == "ocr_needed"
|
|
assert result.chars <= 50
|
|
|
|
|
|
def test_extract_docx_corrupted_marked_failed(tmp_path: Path):
|
|
bad = tmp_path / "bad.docx"
|
|
bad.write_bytes(b"this is not a docx zip")
|
|
result = extract_attachment(bad)
|
|
assert result.status == "failed"
|