extract_attachment returned "unsupported" for .epub, so CPT 2021/2022 and CPT Changes 2023 had no text at all, and 2018/2019/2024 indexed only from their PDF siblings. Added rex.comments.epub_text — stdlib only, shared with pfs.cpt_epub — that resolves an EPUB's own reading order via META-INF/container.xml -> the OPF's manifest + spine (falling back to sorted .xhtml/.html names when container.xml is missing), and strips tags/entities into plain text. extract_attachment's new .epub branch returns the same ExtractResult shape the PDF branch does; no change needed in llm.source._attachment_sections, which already tries every bib attachment regardless of extension. pfs.cpt_epub used to hard-code "OPS/" as the content-file prefix in three places; it now resolves the same way via epub_text.content_root, so a differently-templated EPUB would still locate its content instead of silently parsing to nothing.
56 lines
1.8 KiB
Python
56 lines
1.8 KiB
Python
"""F2: extract_attachment's .epub branch."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import zipfile
|
|
from pathlib import Path
|
|
|
|
from rex.comments import extract_attachment
|
|
|
|
|
|
def _build_epub(path: Path, body: str) -> None:
|
|
with zipfile.ZipFile(path, "w") as zf:
|
|
zf.writestr("mimetype", "application/epub+zip")
|
|
zf.writestr("OPS/Chapter01.xhtml", f"<html><body>{body}</body></html>")
|
|
|
|
|
|
def test_epub_extracted_ok(tmp_path: Path):
|
|
p = tmp_path / "book.epub"
|
|
_build_epub(p, "<p>" + "A CPT codebook chapter with real text. " * 5 + "</p>")
|
|
result = extract_attachment(p)
|
|
assert result.status == "ok"
|
|
assert "CPT codebook chapter" in result.text
|
|
assert result.chars == len(result.text)
|
|
|
|
|
|
def test_epub_below_threshold_marked_ocr_needed(tmp_path: Path):
|
|
p = tmp_path / "book.epub"
|
|
_build_epub(p, "<p>short</p>")
|
|
result = extract_attachment(p)
|
|
assert result.status == "ocr_needed"
|
|
|
|
|
|
def test_epub_malformed_zip_marked_failed(tmp_path: Path):
|
|
p = tmp_path / "book.epub"
|
|
p.write_bytes(b"not a real zip file")
|
|
result = extract_attachment(p)
|
|
assert result.status == "failed"
|
|
assert result.text == ""
|
|
|
|
|
|
def test_epub_extracted_in_reading_order(tmp_path: Path):
|
|
p = tmp_path / "book.epub"
|
|
with zipfile.ZipFile(p, "w") as zf:
|
|
zf.writestr("mimetype", "application/epub+zip")
|
|
zf.writestr(
|
|
"OPS/Chapter01.xhtml",
|
|
"<html><body><p>First chapter content here, plenty of text.</p></body></html>",
|
|
)
|
|
zf.writestr(
|
|
"OPS/Chapter02.xhtml",
|
|
"<html><body><p>Second chapter content here, plenty of text.</p></body></html>",
|
|
)
|
|
result = extract_attachment(p)
|
|
assert result.status == "ok"
|
|
assert result.text.index("First chapter") < result.text.index("Second chapter")
|