Files
stack/tests/rex/comments/test_extract_epub.py
kert 77fd48eb31 feat(rex,pfs): EPUB attachments readable by the corpus indexer (F2)
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.
2026-09-09 21:11:04 -04:00

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")