diora-web/tui/tools/parity/compare_blocks.py
mrwnslz edc9c6d4c3 TUI: EPUB wie der Browser lesen (HTML5, Rohdaten) und Zeichenpositionen je Block
Blocktext, Block-Index und Zeichenoffsets stimmen nun bei sechs echten Büchern exakt mit
parseEpub des Web-Readers überein. Grundlage für einen echten Fortschrittsanteil
(fraction_for_anchor) statt 0.0. Parser html5lib statt lxml, Rohinhalt statt
ebooklib-get_content (formatiert um), UTF-8 wie JSZip.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 12:59:03 +01:00

38 lines
1.7 KiB
Python

"""Compare the TUI's block list with the web reader's (web_blocks.cjs output).
python compare_blocks.py web.json [name-part ...]
What must match is the number and tag of the blocks (they make the position anchors
shared by all clients). Text differences are reported too: they are cosmetic, but they
show where the TUI's HTML parser (lxml) reads a book differently from Chrome's.
Exit status 1 when a book's block list differs in length or tags.
"""
import json
import os
import sys
from pathlib import Path
from diora_tui.blocks import load_flat_book
def main(ref_file, only):
ref = json.load(open(ref_file, encoding="utf-8"))
failed = False
for file, web in ref.items():
if only and not any(o in file for o in only):
continue
book = load_flat_book(Path(file))
wb, tb = web["blocks"], book.blocks
structure = len(wb) == len(tb) and all(w["tag"] == t.tag for w, t in zip(wb, tb))
texts = [i for i, (w, t) in enumerate(zip(wb, tb)) if w["text"] != t.text]
chars = [i for i, (w, t) in enumerate(zip(wb, tb)) if (w["start"], w["len"]) != (t.char_start, t.char_len)]
total_ok = web["total"] == book.total_chars
failed |= not structure
print(f"{'OK ' if structure else 'FAIL'} {os.path.basename(file)[:50]}: web {len(wb)} / tui {len(tb)} blocks, "
f"{len(texts)} with other text" + (f" (first: {texts[0]})" if texts else "") +
f"; chars {'equal' if not chars and total_ok else f'{len(chars)} blocks differ, total web {web['total']} / tui {book.total_chars}'}")
return 1 if failed else 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1], sys.argv[2:]))