130 lines
3.9 KiB
Python
130 lines
3.9 KiB
Python
"""CLI: stack comments {extract, extract-ocr, stats}."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import fitz
|
|
from typer.testing import CliRunner
|
|
|
|
from cli.comments import app
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
def _seed(root: Path) -> Path:
|
|
cdir = root / "CMS-2024-0001" / "CMS-2024-0001-0001"
|
|
cdir.mkdir(parents=True)
|
|
doc = fitz.open()
|
|
doc.new_page().insert_text((50, 72), "Long body content " * 10)
|
|
doc.save(str(cdir / "attachment_1.pdf"))
|
|
doc.close()
|
|
return cdir
|
|
|
|
|
|
def test_extract_writes_combined(tmp_path: Path):
|
|
cdir = _seed(tmp_path)
|
|
result = runner.invoke(
|
|
app,
|
|
[
|
|
"extract",
|
|
"--root",
|
|
str(tmp_path),
|
|
"--no-bib", # skip bib.sqlite lookup in tests
|
|
"--workers",
|
|
"1",
|
|
],
|
|
)
|
|
assert result.exit_code == 0, result.output
|
|
assert (cdir / "combined.md").is_file()
|
|
assert "written" in result.output.lower()
|
|
|
|
|
|
def test_extract_ocr_empty_queue(tmp_path: Path):
|
|
# Implemented in #663 — an empty root reports a zero queue and exits
|
|
# cleanly without loading the OCR engine or the bib lookup.
|
|
result = runner.invoke(app, ["extract-ocr", "--root", str(tmp_path)])
|
|
assert result.exit_code == 0
|
|
assert "ocr queue: 0" in result.output
|
|
|
|
|
|
def test_stats_after_extract(tmp_path: Path):
|
|
_seed(tmp_path)
|
|
runner.invoke(
|
|
app,
|
|
["extract", "--root", str(tmp_path), "--no-bib", "--workers", "1"],
|
|
)
|
|
result = runner.invoke(app, ["stats", "--root", str(tmp_path)])
|
|
assert result.exit_code == 0, result.output
|
|
assert "1" in result.output # at least one comment counted
|
|
|
|
|
|
def test_extract_attaches_combined_md_as_note(tmp_path: Path, monkeypatch):
|
|
"""Extract writes combined.md and per-attachment sibling MDs to disk.
|
|
Only one Zotero-bound artifact: a single bib note titled 'Comment text'
|
|
whose content is the rendered-HTML of combined.md. No file attachments."""
|
|
from bib import connect
|
|
from bib.item import Source
|
|
|
|
bib_db = tmp_path / "bib.sqlite"
|
|
monkeypatch.setattr("conf.path", lambda _: bib_db)
|
|
|
|
cdir = _seed(tmp_path)
|
|
comment_id = cdir.name # CMS-2024-0001-0001
|
|
|
|
store = connect(str(bib_db))
|
|
item = Source(
|
|
title=f"Comment {comment_id}",
|
|
url=f"https://www.regulations.gov/comment/{comment_id}",
|
|
)
|
|
item_key = store.upsert(item)
|
|
store.close()
|
|
|
|
result = runner.invoke(app, ["extract", "--root", str(tmp_path), "--workers", "1"])
|
|
assert result.exit_code == 0, result.output
|
|
|
|
# Disk siblings still written (kept for non-Zotero corpus consumers)
|
|
assert (cdir / "combined.md").is_file()
|
|
assert (cdir / "attachment_1.pdf.md").is_file()
|
|
|
|
def md_attachments():
|
|
store = connect(str(bib_db))
|
|
rows = list(
|
|
store._con().execute(
|
|
"SELECT a.filename FROM attachments a "
|
|
"JOIN items i ON a.item_id=i.id "
|
|
"WHERE i.key=? AND a.filename LIKE '%.md'",
|
|
(item_key,),
|
|
)
|
|
)
|
|
store.close()
|
|
return rows
|
|
|
|
def notes():
|
|
store = connect(str(bib_db))
|
|
rows = list(
|
|
store._con().execute(
|
|
"SELECT n.title, n.content FROM notes n "
|
|
"JOIN items i ON n.item_id=i.id WHERE i.key=?",
|
|
(item_key,),
|
|
)
|
|
)
|
|
store.close()
|
|
return rows
|
|
|
|
# No file attachments — old behavior is gone.
|
|
assert md_attachments() == []
|
|
|
|
# Exactly one note, rendered HTML.
|
|
rows = notes()
|
|
assert len(rows) == 1
|
|
title, content = rows[0]
|
|
assert title == "Comment text"
|
|
assert "<h1>" in content or "<h2>" in content
|
|
assert "Long body content" in content # the seeded PDF text shows up
|
|
|
|
# Idempotent on re-run
|
|
runner.invoke(app, ["extract", "--root", str(tmp_path), "--workers", "1"])
|
|
assert len(notes()) == 1
|
|
assert md_attachments() == []
|