- Integración de 93 capítulos del manual completo - Componente MarkdownViewer para renderizar archivos .md - Navegación jerárquica completa (ManualIndex) - Sistema de búsqueda mejorado - Página ManualViewer con navegación anterior/siguiente - Scripts de verificación del manual - Puerto configurado en 8096 - Configuración de despliegue (Vercel, Netlify, GitHub Pages) - Todos los problemas detectados corregidos
322 lines
14 KiB
Python
322 lines
14 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Script exhaustivo para buscar TODAS las referencias a archivos multimedia
|
|
y verificar si existen en el sistema
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
from pathlib import Path
|
|
from typing import List, Dict, Tuple
|
|
import csv
|
|
|
|
BASE_DIR = Path("/home/planetazuzu/protocolo-r-pido")
|
|
MANUAL_DIR = BASE_DIR / "manual-tes" / "TES_Manual_Digital"
|
|
|
|
def buscar_todas_referencias_multimedia(archivo: Path) -> List[Dict]:
|
|
"""Busca TODAS las referencias a medios en un archivo"""
|
|
referencias = []
|
|
|
|
try:
|
|
with open(archivo, 'r', encoding='utf-8') as f:
|
|
contenido = f.read()
|
|
lineas = contenido.split('\n')
|
|
|
|
for num_linea, linea in enumerate(lineas, 1):
|
|
# 1. Patrones de imágenes: 
|
|
patron_imagen = r'!\[([^\]]*)\]\(([^\)]+\.(jpg|jpeg|png|gif|svg|bmp|webp))\)'
|
|
matches = re.findall(patron_imagen, linea, re.IGNORECASE)
|
|
for texto_alt, ruta, ext in matches:
|
|
referencias.append({
|
|
'archivo_md': archivo.name,
|
|
'ruta_md': str(archivo.relative_to(BASE_DIR)),
|
|
'linea': num_linea,
|
|
'tipo': 'imagen',
|
|
'ruta_referenciada': ruta.strip(),
|
|
'extension': ext.lower(),
|
|
'texto_alt': texto_alt,
|
|
'contexto': linea.strip()[:100]
|
|
})
|
|
|
|
# 2. Referencias a videos en enlaces: [texto](video.mp4)
|
|
patron_video_enlace = r'\[([^\]]*)\]\(([^\)]+\.(mp4|avi|mov|wmv|mkv|webm|flv))\)'
|
|
matches = re.findall(patron_video_enlace, linea, re.IGNORECASE)
|
|
for texto, ruta, ext in matches:
|
|
referencias.append({
|
|
'archivo_md': archivo.name,
|
|
'ruta_md': str(archivo.relative_to(BASE_DIR)),
|
|
'linea': num_linea,
|
|
'tipo': 'video',
|
|
'ruta_referenciada': ruta.strip(),
|
|
'extension': ext.lower(),
|
|
'texto_alt': texto,
|
|
'contexto': linea.strip()[:100]
|
|
})
|
|
|
|
# 3. Referencias directas a videos (sin enlace)
|
|
patron_video_directo = r'([^\s]+\.(mp4|avi|mov|wmv|mkv|webm|flv))'
|
|
matches = re.findall(patron_video_directo, linea, re.IGNORECASE)
|
|
for ruta, ext in matches:
|
|
# Evitar falsos positivos en URLs
|
|
if not ruta.startswith('http'):
|
|
referencias.append({
|
|
'archivo_md': archivo.name,
|
|
'ruta_md': str(archivo.relative_to(BASE_DIR)),
|
|
'linea': num_linea,
|
|
'tipo': 'video',
|
|
'ruta_referenciada': ruta.strip(),
|
|
'extension': ext.lower(),
|
|
'texto_alt': '',
|
|
'contexto': linea.strip()[:100]
|
|
})
|
|
|
|
# 4. Enlaces a PDF/DOC/PPT
|
|
patron_documentos = r'\[([^\]]*)\]\(([^\)]+\.(pdf|doc|docx|ppt|pptx|xls|xlsx))\)'
|
|
matches = re.findall(patron_documentos, linea, re.IGNORECASE)
|
|
for texto, ruta, ext in matches:
|
|
referencias.append({
|
|
'archivo_md': archivo.name,
|
|
'ruta_md': str(archivo.relative_to(BASE_DIR)),
|
|
'linea': num_linea,
|
|
'tipo': 'documento',
|
|
'ruta_referenciada': ruta.strip(),
|
|
'extension': ext.lower(),
|
|
'texto_alt': texto,
|
|
'contexto': linea.strip()[:100]
|
|
})
|
|
|
|
# 5. Referencias a anexos/adjuntos/figuras
|
|
patron_anexo = r'(ver|Ver|VER|consultar|Consultar|CONSULTAR|adjunto|Adjunto|ADJUNTO|anexo|Anexo|ANEXO|figura|Figura|FIGURA|imagen|Imagen|IMAGEN)\s+[A-Z]?\d+[\.\)]?\s*[:\-]?\s*([^\s,\.\)]+\.(jpg|jpeg|png|gif|svg|pdf|doc|docx))'
|
|
matches = re.findall(patron_anexo, linea, re.IGNORECASE)
|
|
for palabra, ruta, ext in matches:
|
|
referencias.append({
|
|
'archivo_md': archivo.name,
|
|
'ruta_md': str(archivo.relative_to(BASE_DIR)),
|
|
'linea': num_linea,
|
|
'tipo': 'referencia_textual',
|
|
'ruta_referenciada': ruta.strip(),
|
|
'extension': ext.lower(),
|
|
'texto_alt': palabra,
|
|
'contexto': linea.strip()[:100]
|
|
})
|
|
|
|
# 6. Rutas relativas que podrían ser archivos
|
|
patron_rutas = r'\(([\.\/][^\)]+\.(jpg|jpeg|png|gif|svg|pdf|doc|docx|mp4|avi|mov))\)'
|
|
matches = re.findall(patron_rutas, linea, re.IGNORECASE)
|
|
for ruta, ext in matches:
|
|
referencias.append({
|
|
'archivo_md': archivo.name,
|
|
'ruta_md': str(archivo.relative_to(BASE_DIR)),
|
|
'linea': num_linea,
|
|
'tipo': 'ruta_relativa',
|
|
'ruta_referenciada': ruta.strip(),
|
|
'extension': ext.lower(),
|
|
'texto_alt': '',
|
|
'contexto': linea.strip()[:100]
|
|
})
|
|
|
|
# 7. Referencias a carpetas de assets/imágenes
|
|
patron_assets = r'(assets|images|img|imagenes|media|multimedia|videos|docs|documentos)/([^\s\)]+\.(jpg|jpeg|png|gif|svg|pdf|mp4|avi|mov))'
|
|
matches = re.findall(patron_assets, linea, re.IGNORECASE)
|
|
for carpeta, nombre_archivo, ext in matches:
|
|
# Construir ruta completa
|
|
ruta_completa = f"{carpeta}/{nombre_archivo}"
|
|
referencias.append({
|
|
'archivo_md': archivo.name,
|
|
'ruta_md': str(archivo.relative_to(BASE_DIR)),
|
|
'linea': num_linea,
|
|
'tipo': 'ruta_assets',
|
|
'ruta_referenciada': ruta_completa,
|
|
'extension': ext.lower(),
|
|
'texto_alt': '',
|
|
'contexto': linea.strip()[:100]
|
|
})
|
|
|
|
except Exception as e:
|
|
print(f"Error procesando {archivo}: {e}")
|
|
|
|
return referencias
|
|
|
|
def verificar_existencia_archivo(ruta_referenciada: str, archivo_origen: Path) -> Tuple[bool, str]:
|
|
"""Verifica si un archivo existe y devuelve la ruta encontrada"""
|
|
# Limpiar la ruta
|
|
ruta = ruta_referenciada.strip()
|
|
|
|
# Si es URL, no verificar
|
|
if ruta.startswith('http://') or ruta.startswith('https://'):
|
|
return True, "URL externa"
|
|
|
|
# Si es ruta absoluta
|
|
if os.path.isabs(ruta):
|
|
if os.path.exists(ruta):
|
|
return True, ruta
|
|
return False, ruta
|
|
|
|
# Si es ruta relativa
|
|
archivo_dir = archivo_origen.parent
|
|
|
|
# Intentar resolver desde el directorio del archivo
|
|
ruta_completa = (archivo_dir / ruta).resolve()
|
|
if ruta_completa.exists():
|
|
return True, str(ruta_completa.relative_to(BASE_DIR))
|
|
|
|
# Intentar desde la raíz del proyecto
|
|
ruta_desde_raiz = BASE_DIR / ruta.lstrip('/')
|
|
if ruta_desde_raiz.exists():
|
|
return True, str(ruta_desde_raiz.relative_to(BASE_DIR))
|
|
|
|
# Buscar en ubicaciones comunes
|
|
ubicaciones_comunes = [
|
|
BASE_DIR / "public" / ruta,
|
|
BASE_DIR / "src" / "assets" / ruta,
|
|
BASE_DIR / "assets" / ruta,
|
|
BASE_DIR / "images" / ruta,
|
|
BASE_DIR / "docs" / ruta,
|
|
MANUAL_DIR / ruta,
|
|
BASE_DIR / ruta,
|
|
]
|
|
|
|
for ubicacion in ubicaciones_comunes:
|
|
if ubicacion.exists():
|
|
return True, str(ubicacion.relative_to(BASE_DIR))
|
|
|
|
return False, ruta
|
|
|
|
def obtener_todos_archivos_md() -> List[Path]:
|
|
"""Obtiene todos los archivos .md del manual"""
|
|
archivos = []
|
|
for bloque_dir in MANUAL_DIR.iterdir():
|
|
if bloque_dir.is_dir() and bloque_dir.name.startswith("BLOQUE_"):
|
|
for archivo in bloque_dir.glob("*.md"):
|
|
archivos.append(archivo)
|
|
return sorted(archivos)
|
|
|
|
def generar_reporte_completo():
|
|
"""Genera reporte completo de multimedia faltante"""
|
|
print("Buscando referencias a archivos multimedia...")
|
|
archivos_md = obtener_todos_archivos_md()
|
|
|
|
todas_referencias = []
|
|
|
|
for archivo in archivos_md:
|
|
referencias = buscar_todas_referencias_multimedia(archivo)
|
|
todas_referencias.extend(referencias)
|
|
|
|
print(f"Encontradas {len(todas_referencias)} referencias a medios")
|
|
|
|
# Verificar existencia
|
|
print("Verificando existencia de archivos...")
|
|
resultados = []
|
|
|
|
for ref in todas_referencias:
|
|
existe, ruta_encontrada = verificar_existencia_archivo(ref['ruta_referenciada'], Path(BASE_DIR / ref['ruta_md']))
|
|
|
|
resultados.append({
|
|
'archivo_md': ref['archivo_md'],
|
|
'ruta_md': ref['ruta_md'],
|
|
'linea': ref['linea'],
|
|
'tipo': ref['tipo'],
|
|
'ruta_referenciada': ref['ruta_referenciada'],
|
|
'extension': ref['extension'],
|
|
'existe': 'Sí' if existe else 'No',
|
|
'ruta_encontrada': ruta_encontrada if existe else 'N/A',
|
|
'contexto': ref['contexto']
|
|
})
|
|
|
|
# Separar existentes y faltantes
|
|
referencias_faltantes = [r for r in resultados if r['existe'] == 'No']
|
|
referencias_existentes = [r for r in resultados if r['existe'] == 'Sí']
|
|
|
|
# Generar CSV
|
|
print("Generando archivo CSV...")
|
|
csv_path = BASE_DIR / "REFERENCIAS_MULTIMEDIA_COMPLETO.csv"
|
|
with open(csv_path, 'w', newline='', encoding='utf-8') as f:
|
|
writer = csv.DictWriter(f, fieldnames=[
|
|
'Archivo MD', 'Ruta MD', 'Línea', 'Tipo', 'Ruta Referenciada',
|
|
'Extensión', 'Existe', 'Ruta Encontrada', 'Contexto'
|
|
])
|
|
writer.writeheader()
|
|
writer.writerows(resultados)
|
|
|
|
# Generar reporte Markdown
|
|
reporte_md = []
|
|
reporte_md.append("# REPORTE COMPLETO: REFERENCIAS A ARCHIVOS MULTIMEDIA\n")
|
|
reporte_md.append(f"**Fecha:** {__import__('datetime').datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
reporte_md.append("---\n")
|
|
|
|
reporte_md.append("## 📊 RESUMEN EJECUTIVO\n")
|
|
reporte_md.append(f"- **Total de referencias encontradas:** {len(resultados)}\n")
|
|
reporte_md.append(f"- **Archivos existentes:** {len(referencias_existentes)}\n")
|
|
reporte_md.append(f"- **Archivos faltantes:** {len(referencias_faltantes)}\n")
|
|
reporte_md.append(f"- **Porcentaje de completitud:** {(len(referencias_existentes)/len(resultados)*100 if resultados else 0):.1f}%\n")
|
|
reporte_md.append("---\n")
|
|
|
|
if referencias_faltantes:
|
|
reporte_md.append("## ❌ ARCHIVOS MULTIMEDIA FALTANTES\n")
|
|
reporte_md.append(f"**Total:** {len(referencias_faltantes)} referencias a archivos que no existen\n\n")
|
|
reporte_md.append("| Archivo MD | Línea | Tipo | Ruta Referenciada | Extensión | Contexto |\n")
|
|
reporte_md.append("|------------|-------|------|------------------|-----------|----------|\n")
|
|
|
|
for ref in referencias_faltantes:
|
|
reporte_md.append(f"| `{ref['archivo_md']}` | {ref['linea']} | {ref['tipo']} | `{ref['ruta_referenciada']}` | {ref['extension']} | {ref['contexto'][:50]}... |\n")
|
|
else:
|
|
reporte_md.append("## ✅ NO SE ENCONTRARON ARCHIVOS MULTIMEDIA FALTANTES\n")
|
|
reporte_md.append("Todas las referencias a archivos multimedia apuntan a archivos existentes.\n")
|
|
|
|
reporte_md.append("\n---\n")
|
|
|
|
if referencias_existentes:
|
|
reporte_md.append("## ✅ ARCHIVOS MULTIMEDIA EXISTENTES\n")
|
|
reporte_md.append(f"**Total:** {len(referencias_existentes)} referencias a archivos existentes\n\n")
|
|
reporte_md.append("| Archivo MD | Tipo | Ruta Referenciada | Ruta Encontrada |\n")
|
|
reporte_md.append("|------------|------|-------------------|-----------------|\n")
|
|
|
|
# Mostrar solo primeros 20 para no hacer el reporte muy largo
|
|
for ref in referencias_existentes[:20]:
|
|
reporte_md.append(f"| `{ref['archivo_md']}` | {ref['tipo']} | `{ref['ruta_referenciada']}` | `{ref['ruta_encontrada']}` |\n")
|
|
|
|
if len(referencias_existentes) > 20:
|
|
reporte_md.append(f"\n*... y {len(referencias_existentes) - 20} referencias más (ver CSV completo)*\n")
|
|
|
|
reporte_md.append("\n---\n")
|
|
reporte_md.append("## 📋 ESTADÍSTICAS POR TIPO\n")
|
|
|
|
por_tipo = {}
|
|
por_tipo_faltantes = {}
|
|
|
|
for ref in resultados:
|
|
tipo = ref['tipo']
|
|
por_tipo[tipo] = por_tipo.get(tipo, 0) + 1
|
|
if ref['existe'] == 'No':
|
|
por_tipo_faltantes[tipo] = por_tipo_faltantes.get(tipo, 0) + 1
|
|
|
|
reporte_md.append("| Tipo | Total | Faltantes | Existentes |\n")
|
|
reporte_md.append("|------|-------|-----------|------------|\n")
|
|
|
|
for tipo in sorted(por_tipo.keys()):
|
|
total = por_tipo[tipo]
|
|
faltantes = por_tipo_faltantes.get(tipo, 0)
|
|
existentes = total - faltantes
|
|
reporte_md.append(f"| {tipo} | {total} | {faltantes} | {existentes} |\n")
|
|
|
|
reporte_md.append("\n---\n")
|
|
reporte_md.append("## 📄 ARCHIVOS GENERADOS\n")
|
|
reporte_md.append(f"- **CSV completo:** `REFERENCIAS_MULTIMEDIA_COMPLETO.csv`\n")
|
|
reporte_md.append(f"- **Reporte Markdown:** Este archivo\n")
|
|
reporte_md.append("\nEl archivo CSV contiene todas las referencias encontradas con detalles completos.\n")
|
|
|
|
# Guardar reporte
|
|
reporte_path = BASE_DIR / "REPORTE_MULTIMEDIA_COMPLETO.md"
|
|
with open(reporte_path, 'w', encoding='utf-8') as f:
|
|
f.write('\n'.join(reporte_md))
|
|
|
|
print(f"\n✅ Reporte generado: {reporte_path}")
|
|
print(f"✅ CSV generado: {csv_path}")
|
|
print(f"\n📊 Resumen:")
|
|
print(f" - Referencias encontradas: {len(resultados)}")
|
|
print(f" - Archivos faltantes: {len(referencias_faltantes)}")
|
|
print(f" - Archivos existentes: {len(referencias_existentes)}")
|
|
|
|
if __name__ == "__main__":
|
|
generar_reporte_completo()
|