#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Cata previa de Maludocs: qué entendería la Biblioteca de un PDF, antes de subirlo. Uso: python3 maludocs_cata.py "ruta/al/documento.pdf" [--todo] Por página: la entidad detectada, cuántos hechos sacó y una muestra. Al final, lo que conviene corregir en el documento antes de cargarlo (páginas sin texto, sin entidad, sin hechos, y números sin unidad). Con --todo lista todos los hechos. """ import re import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent)) import fitz # noqa: E402 import maludocs as indice # noqa: E402 (solo usa hechos_de_pagina) def _junta_espaciado(linea): grupos = re.split(r" {2,}", linea.strip()) salida = [] for grupo in grupos: trozos = grupo.split(" ") if (len(trozos) >= 3 and all(len(t) <= 2 for t in trozos) and sum(len(t) == 1 for t in trozos) >= 0.7 * len(trozos)): grupo = "".join(trozos) else: # «L A PREGUNTA» → «LA PREGUNTA», «0 2» → «02»: dos o más sueltos seguidos se juntan grupo = re.sub(r"\b(?:[A-Za-zÁÉÍÓÚÑ0-9] ){1,}[A-Za-zÁÉÍÓÚÑ0-9]\b(?! ?[x×] ?\d)", lambda m: m.group(0).replace(" ", ""), grupo) salida.append(grupo) return " ".join(salida) def paginas_de(ruta): with fitz.open(ruta) as d: for p in d: lineas = [_junta_espaciado(l) for l in p.get_text().splitlines()] yield re.sub(r"\n{3,}", "\n\n", "\n".join(lineas)).strip() def main(): if len(sys.argv) < 2: print(__doc__) return 2 ruta, todo = sys.argv[1], "--todo" in sys.argv avisos, total, debiles = [], 0, 0 for n, texto in enumerate(paginas_de(ruta), 1): if len(texto) < 20: print(f"pág. {n}: SIN TEXTO (imagen o vacía)") avisos.append(f"pág. {n} no tiene texto: si es una lámina con datos, ponlos como texto, no como imagen") continue hechos, entidad, titulo = indice.hechos_de_pagina(texto, n) fuertes = [h for h in hechos if h["origen"] != "debil"] total += len(fuertes); debiles += len(hechos) - len(fuertes) print(f"pág. {n}: {entidad or '(sin entidad)'} · {len(fuertes)} hechos" + (f" · {len(hechos) - len(fuertes)} débiles" if len(hechos) > len(fuertes) else "")) for h in (hechos if todo else hechos[:5]): sec = f" [{h['seccion']}]" if h.get("seccion") else "" ent = f" — {h['entidad'][:50]}" if h["origen"] == "segmento" else "" deb = " (débil: sin unidad, solo el modelo lo usará)" if h["origen"] == "debil" else "" print(f" {h['etiqueta'][:40]}{sec}: {h['valor_txt'][:40]}{ent}{deb}") if not entidad: avisos.append(f"pág. {n} sin entidad clara: pon en la primera línea de qué producto o cosa habla") limpio = re.sub(r"\b(19|20)\d{2}\b|\bp[aá]gina \d+ de \d+|\b\d{1,2} de [a-z]+\b", " ", texto, flags=re.I) sueltos = [m.group(0) for m in re.finditer(r"\b\d+(?:[.,]\d+)?\b(?!\s*(?:%|kg|g|cm|mm|m3|m³|micras|mils|pulg|und|unid|unidades|u|l|lt|litros|soles|usd|de|x|×|·))", limpio)] if not fuertes and len(sueltos) >= 3: avisos.append(f"pág. {n} tiene números pero ningún dato reconocible: usa «Rótulo» y debajo «valor unidad», o «Rótulo: valor»") if len(sueltos) > 8 and len(hechos) < len(sueltos) / 4: avisos.append(f"pág. {n}: muchos números sin unidad ni rótulo ({len(sueltos)}); Maludocs solo confía en «rótulo → valor con unidad»") print(f"\n{total} hechos en total" + (f" · {debiles} débiles (se indexan, pero no se contestan por código)" if debiles else "")) if avisos: print("\nAntes de subirlo:") for a in avisos: print(" -", a) else: print("\nListo para cargar.") return 0 if __name__ == "__main__": sys.exit(main())