Files
creator/backend/tests/test_pdf_convert.py
2026-07-08 23:46:35 +02:00

116 lines
4.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""PDF→Text-Konvertierung: pymupdf4llm primär, pdftotext-Fallback, mtime-Cache."""
import os
import time
import fitz # PyMuPDF
import pytest
import blocks as blx
def _mini_pdf(path, text="Approximationsalgorithmen sind wichtig."):
doc = fitz.open()
page = doc.new_page()
page.insert_text((72, 72), text, fontsize=12)
doc.save(str(path))
doc.close()
def test_convert_writes_markdown_txt(tmp_path):
_mini_pdf(tmp_path / "skript.pdf")
blx._convert_pdfs(tmp_path)
out = (tmp_path / "skript.txt").read_text(encoding="utf-8")
assert "Approximationsalgorithmen" in out
def test_cache_skips_fresh_txt(tmp_path):
_mini_pdf(tmp_path / "a.pdf")
(tmp_path / ".pdf-txt-norm").write_text(str(blx._PDF_NORM_VERSION), encoding="utf-8")
marker = tmp_path / "a.txt"
marker.write_text("MARKER", encoding="utf-8")
now = time.time() + 60
os.utime(marker, (now, now))
blx._convert_pdfs(tmp_path)
assert marker.read_text(encoding="utf-8") == "MARKER" # nicht neu konvertiert
def test_norm_version_erzwingt_rekonvertierung(tmp_path, monkeypatch):
"""Fehlender/alter .pdf-txt-norm-Marker ignoriert den mtime-Cache einmalig."""
_mini_pdf(tmp_path / "a.pdf")
stale = tmp_path / "a.txt"
stale.write_text("ALT", encoding="utf-8")
now = time.time() + 60
os.utime(stale, (now, now))
monkeypatch.setattr(blx, "_pdf_markdown", lambda p: None)
monkeypatch.setattr(blx, "_pdf_plaintext", lambda p: "neu konvertiert")
blx._convert_pdfs(tmp_path)
assert stale.read_text(encoding="utf-8") == "neu konvertiert"
assert (tmp_path / ".pdf-txt-norm").read_text(encoding="utf-8") == str(blx._PDF_NORM_VERSION)
stale.write_text("BLEIBT", encoding="utf-8")
os.utime(stale, (now, now))
blx._convert_pdfs(tmp_path) # Marker aktuell → Cache greift wieder
assert stale.read_text(encoding="utf-8") == "BLEIBT"
def test_entzerre_pdf_woerter():
"""Small-Caps-Splits mergen; Variablen-Schutzfall und unbelegte Paare bleiben."""
# Regel A inkl. Kette
assert blx._entzerre_pdf_woerter("Das H ITTING S ET Problem") == "Das HITTING SET Problem"
assert blx._entzerre_pdf_woerter("V ERTEX C OVER ist schwer") == "VERTEX COVER ist schwer"
# Schutzfall: Großlauf klein fortgesetzt → L ist Variable
assert blx._entzerre_pdf_woerter("die Sprache L NP-vollständig ist") == "die Sprache L NP-vollständig ist"
# Regel B: Paar nur mit Frequenz-Beleg (≥3× ungespalten im Dokument)
belegt = "N P ist zentral. " + "NP NP NP."
assert blx._entzerre_pdf_woerter(belegt).startswith("NP ist zentral.")
assert blx._entzerre_pdf_woerter("C Y bleibt getrennt") == "C Y bleibt getrennt"
# kein Match mitten im Wort
assert blx._entzerre_pdf_woerter("HALTTM IST hier") == "HALTTM IST hier"
def test_fallback_to_pdftotext(tmp_path, monkeypatch):
_mini_pdf(tmp_path / "b.pdf")
monkeypatch.setattr(blx, "_pdf_markdown", lambda p: None)
monkeypatch.setattr(blx, "_pdf_plaintext", lambda p: "fallback")
blx._convert_pdfs(tmp_path)
assert (tmp_path / "b.txt").read_text(encoding="utf-8") == "fallback"
def test_ocr_languages_from_tessdata(tmp_path, monkeypatch):
import pymupdf
monkeypatch.setattr(pymupdf, "get_tessdata", lambda: str(tmp_path))
assert blx._ocr_languages() is None # keine Sprachdaten → OCR aus
(tmp_path / "eng.traineddata").touch()
assert blx._ocr_languages() == "eng"
(tmp_path / "deu.traineddata").touch()
assert blx._ocr_languages() == "deu+eng"
monkeypatch.setattr(pymupdf, "get_tessdata", lambda: (_ for _ in ()).throw(RuntimeError()))
assert blx._ocr_languages() is None
def test_fidelity_guard_prefers_faithful_plaintext():
plain = "Definition. P = {L ⊆ Σ | A ∈ L} und ≤ sowie häufig über. " * 20
# Markdown verlor die Formeln (Symbole weg) → plain gewinnt
md_lossy = "Definition. und sowie h¨aufig ¨uber. " * 20
text, tool = blx._pick_conversion(md_lossy, plain)
assert tool == "pdftotext"
# Markdown treu (Symbole + Länge da) → md gewinnt
md_ok = "# Def\n" + plain
text, tool = blx._pick_conversion(md_ok, plain)
assert tool == "pymupdf4llm"
# nur eine Quelle verfügbar
assert blx._pick_conversion(None, plain)[1] == "pdftotext"
assert blx._pick_conversion(md_ok, None)[1] == "pymupdf4llm"
assert blx._pick_conversion(None, None) is None
def test_entzerre_hyphen_space():
"""Regel C: GROSSLAUF + Leerzeichen vor Bindestrich-Kompositum („NP -vollständig",
Makro-Spacing) wird gemerged; echter Gedankenstrich („X - y") bleibt."""
assert blx._entzerre_pdf_woerter("die NP -vollständigkeit gilt") == "die NP-vollständigkeit gilt"
assert blx._entzerre_pdf_woerter("Begriffe der NP -schwere und NP -vollständigkeit") == \
"Begriffe der NP-schwere und NP-vollständigkeit"
assert blx._entzerre_pdf_woerter("Term X - y bleibt") == "Term X - y bleibt"
assert blx._entzerre_pdf_woerter("Liste:\n-punkt eins") == "Liste:\n-punkt eins"