Judge / backend /scripts /parse_rulebook.py
GonzaViss's picture
feat(rag): re-chunk rulebook by rule number for precise retrieval
b248094
Raw
History Blame Contribute Delete
2.94 kB
"""
Convierte el PDF del reglamento de Riftbound a Markdown estructurado.
Detecta headers por tamaño de fuente relativo al body text:
- > 2.0x body → H1 (título principal)
- > 1.4x body → H2 (sección)
- > 1.2x body → H3 (subsección)
- else → párrafo
"""
import re
import pymupdf
from pathlib import Path
from statistics import mode
_RULE_BOUNDARY = re.compile(r"^\d{3,}\.")
def _extract_spans(doc: pymupdf.Document) -> list[dict]:
spans = []
for page in doc:
blocks = page.get_text("dict")["blocks"]
for block in blocks:
if block["type"] != 0: # solo bloques de texto
continue
for line in block["lines"]:
for span in line["spans"]:
text = span["text"].strip()
if text:
spans.append({"text": text, "size": round(span["size"], 1)})
return spans
def _detect_body_size(spans: list[dict]) -> float:
sizes = [s["size"] for s in spans]
try:
return mode(sizes)
except Exception:
return min(sizes)
def _classify(size: float, body_size: float) -> str:
ratio = size / body_size
if ratio > 2.0:
return "h1"
if ratio > 1.4:
return "h2"
if ratio > 1.2:
return "h3"
return "body"
def _spans_to_markdown(spans: list[dict], body_size: float) -> str:
lines: list[str] = []
current_body: list[str] = []
def flush_body():
if current_body:
lines.append(" ".join(current_body))
lines.append("")
current_body.clear()
for span in spans:
kind = _classify(span["size"], body_size)
if kind == "body":
if current_body and _RULE_BOUNDARY.match(span["text"]):
flush_body()
current_body.append(span["text"])
else:
flush_body()
prefix = {"h1": "#", "h2": "##", "h3": "###"}[kind]
lines.append(f"{prefix} {span['text']}")
lines.append("")
flush_body()
# Colapsar líneas en blanco múltiples
result: list[str] = []
prev_blank = False
for line in lines:
is_blank = line.strip() == ""
if is_blank and prev_blank:
continue
result.append(line)
prev_blank = is_blank
return "\n".join(result).strip()
def parse_rulebook(pdf_path: str | Path) -> str:
doc = pymupdf.open(str(pdf_path))
spans = _extract_spans(doc)
body_size = _detect_body_size(spans)
return _spans_to_markdown(spans, body_size)
if __name__ == "__main__":
import sys
pdf = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("data/raw/rulebook.pdf")
output = Path("data/processed/rulebook.md")
output.parent.mkdir(parents=True, exist_ok=True)
result = parse_rulebook(pdf)
output.write_text(result, encoding="utf-8")
print(f"Generado: {output} ({len(result)} chars)")