328 lines
15 KiB
Python
328 lines
15 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""
|
||
|
|
Búsqueda EXHAUSTIVA de referencias a archivos multimedia
|
||
|
|
Incluye búsqueda de patrones sutiles y referencias textuales
|
||
|
|
"""
|
||
|
|
|
||
|
|
import os
|
||
|
|
import re
|
||
|
|
from pathlib import Path
|
||
|
|
from typing import List, Dict
|
||
|
|
import csv
|
||
|
|
|
||
|
|
BASE_DIR = Path("/home/planetazuzu/protocolo-r-pido")
|
||
|
|
MANUAL_DIR = BASE_DIR / "manual-tes" / "TES_Manual_Digital"
|
||
|
|
|
||
|
|
def buscar_referencias_exhaustivas(archivo: Path) -> List[Dict]:
|
||
|
|
"""Búsqueda exhaustiva de TODAS las posibles referencias a medios"""
|
||
|
|
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. Imágenes Markdown estándar: 
|
||
|
|
patron1 = r'!\[([^\]]*)\]\(([^\)]+)\)'
|
||
|
|
matches = re.findall(patron1, linea)
|
||
|
|
for texto_alt, ruta in matches:
|
||
|
|
# Verificar si parece ser una imagen
|
||
|
|
if any(ext in ruta.lower() for ext in ['.jpg', '.jpeg', '.png', '.gif', '.svg', '.bmp', '.webp']):
|
||
|
|
referencias.append({
|
||
|
|
'archivo_md': archivo.name,
|
||
|
|
'ruta_md': str(archivo.relative_to(BASE_DIR)),
|
||
|
|
'linea': num_linea,
|
||
|
|
'tipo': 'imagen_markdown',
|
||
|
|
'ruta_referenciada': ruta.strip(),
|
||
|
|
'extension': Path(ruta).suffix.lower(),
|
||
|
|
'texto_alt': texto_alt,
|
||
|
|
'contexto': linea.strip()[:150]
|
||
|
|
})
|
||
|
|
|
||
|
|
# 2. Enlaces a archivos multimedia: [texto](archivo.ext)
|
||
|
|
patron2 = r'\[([^\]]*)\]\(([^\)]+\.(jpg|jpeg|png|gif|svg|bmp|webp|pdf|doc|docx|ppt|pptx|xls|xlsx|mp4|avi|mov|wmv|mkv|webm|flv))\)'
|
||
|
|
matches = re.findall(patron2, linea, re.IGNORECASE)
|
||
|
|
for texto, ruta, ext in matches:
|
||
|
|
tipo = 'documento' if ext.lower() in ['pdf', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx'] else \
|
||
|
|
'video' if ext.lower() in ['mp4', 'avi', 'mov', 'wmv', 'mkv', 'webm', 'flv'] else 'imagen'
|
||
|
|
referencias.append({
|
||
|
|
'archivo_md': archivo.name,
|
||
|
|
'ruta_md': str(archivo.relative_to(BASE_DIR)),
|
||
|
|
'linea': num_linea,
|
||
|
|
'tipo': f'{tipo}_enlace',
|
||
|
|
'ruta_referenciada': ruta.strip(),
|
||
|
|
'extension': ext.lower(),
|
||
|
|
'texto_alt': texto,
|
||
|
|
'contexto': linea.strip()[:150]
|
||
|
|
})
|
||
|
|
|
||
|
|
# 3. Referencias directas a archivos (sin markdown)
|
||
|
|
patron3 = r'\b([^\s\(\)]+\.(jpg|jpeg|png|gif|svg|bmp|webp|pdf|doc|docx|ppt|pptx|mp4|avi|mov|wmv))\b'
|
||
|
|
matches = re.findall(patron3, linea, re.IGNORECASE)
|
||
|
|
for ruta, ext in matches:
|
||
|
|
# Evitar URLs y rutas de código
|
||
|
|
if not ruta.startswith('http') and not ruta.startswith('//') and '.' in ruta:
|
||
|
|
tipo = 'documento' if ext.lower() in ['pdf', 'doc', 'docx', 'ppt', 'pptx'] else \
|
||
|
|
'video' if ext.lower() in ['mp4', 'avi', 'mov', 'wmv'] else 'imagen'
|
||
|
|
referencias.append({
|
||
|
|
'archivo_md': archivo.name,
|
||
|
|
'ruta_md': str(archivo.relative_to(BASE_DIR)),
|
||
|
|
'linea': num_linea,
|
||
|
|
'tipo': f'{tipo}_directo',
|
||
|
|
'ruta_referenciada': ruta.strip(),
|
||
|
|
'extension': ext.lower(),
|
||
|
|
'texto_alt': '',
|
||
|
|
'contexto': linea.strip()[:150]
|
||
|
|
})
|
||
|
|
|
||
|
|
# 4. Referencias textuales: "ver figura X", "anexo Y", etc.
|
||
|
|
patron4 = r'(ver|Ver|VER|consultar|Consultar|CONSULTAR|adjunto|Adjunto|ADJUNTO|anexo|Anexo|ANEXO|figura|Figura|FIGURA|imagen|Imagen|IMAGEN|gráfico|Gráfico|GRÁFICO|diagrama|Diagrama|DIAGRAMA|tabla|Tabla|TABLA)\s+[A-Z]?\d+[\.\)]?\s*[:\-]?\s*([^\s,\.\)]+\.(jpg|jpeg|png|gif|svg|pdf|doc|docx))'
|
||
|
|
matches = re.findall(patron4, 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()[:150]
|
||
|
|
})
|
||
|
|
|
||
|
|
# 5. Rutas relativas con ../ o ./
|
||
|
|
patron5 = r'\(([\.\/][^\)]+\.(jpg|jpeg|png|gif|svg|pdf|doc|docx|mp4|avi|mov))\)'
|
||
|
|
matches = re.findall(patron5, linea, re.IGNORECASE)
|
||
|
|
for ruta, ext in matches:
|
||
|
|
tipo = 'documento' if ext.lower() in ['pdf', 'doc', 'docx'] else \
|
||
|
|
'video' if ext.lower() in ['mp4', 'avi', 'mov'] else 'imagen'
|
||
|
|
referencias.append({
|
||
|
|
'archivo_md': archivo.name,
|
||
|
|
'ruta_md': str(archivo.relative_to(BASE_DIR)),
|
||
|
|
'linea': num_linea,
|
||
|
|
'tipo': f'{tipo}_relativa',
|
||
|
|
'ruta_referenciada': ruta.strip(),
|
||
|
|
'extension': ext.lower(),
|
||
|
|
'texto_alt': '',
|
||
|
|
'contexto': linea.strip()[:150]
|
||
|
|
})
|
||
|
|
|
||
|
|
# 6. Referencias a carpetas comunes
|
||
|
|
patron6 = r'(assets|images|img|imagenes|media|multimedia|videos|docs|documentos|public|static)/[^\s\)]+\.(jpg|jpeg|png|gif|svg|pdf|mp4|avi|mov)'
|
||
|
|
matches = re.findall(patron6, linea, re.IGNORECASE)
|
||
|
|
for carpeta, ext in matches:
|
||
|
|
# Extraer nombre de archivo
|
||
|
|
match_archivo = re.search(rf'{carpeta}/([^\s\)]+\.{ext})', linea, re.IGNORECASE)
|
||
|
|
if match_archivo:
|
||
|
|
nombre_archivo = match_archivo.group(1)
|
||
|
|
ruta_completa = f"{carpeta}/{nombre_archivo}"
|
||
|
|
tipo = 'documento' if ext.lower() == 'pdf' else \
|
||
|
|
'video' if ext.lower() in ['mp4', 'avi', 'mov'] else 'imagen'
|
||
|
|
referencias.append({
|
||
|
|
'archivo_md': archivo.name,
|
||
|
|
'ruta_md': str(archivo.relative_to(BASE_DIR)),
|
||
|
|
'linea': num_linea,
|
||
|
|
'tipo': f'{tipo}_carpeta',
|
||
|
|
'ruta_referenciada': ruta_completa,
|
||
|
|
'extension': ext.lower(),
|
||
|
|
'texto_alt': carpeta,
|
||
|
|
'contexto': linea.strip()[:150]
|
||
|
|
})
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
print(f"Error procesando {archivo}: {e}")
|
||
|
|
|
||
|
|
return referencias
|
||
|
|
|
||
|
|
def verificar_existencia(ruta: str, archivo_origen: Path) -> tuple:
|
||
|
|
"""Verifica si un archivo existe"""
|
||
|
|
ruta = ruta.strip()
|
||
|
|
|
||
|
|
# URLs externas
|
||
|
|
if ruta.startswith(('http://', 'https://', '//')):
|
||
|
|
return True, "URL externa"
|
||
|
|
|
||
|
|
# Ruta absoluta
|
||
|
|
if os.path.isabs(ruta):
|
||
|
|
return os.path.exists(ruta), ruta
|
||
|
|
|
||
|
|
# Ruta relativa desde el archivo origen
|
||
|
|
archivo_dir = archivo_origen.parent
|
||
|
|
ruta_resuelta = (archivo_dir / ruta).resolve()
|
||
|
|
|
||
|
|
if ruta_resuelta.exists() and ruta_resuelta.is_file():
|
||
|
|
return True, str(ruta_resuelta.relative_to(BASE_DIR))
|
||
|
|
|
||
|
|
# Buscar desde raíz del proyecto
|
||
|
|
ruta_desde_raiz = BASE_DIR / ruta.lstrip('/')
|
||
|
|
if ruta_desde_raiz.exists() and ruta_desde_raiz.is_file():
|
||
|
|
return True, str(ruta_desde_raiz.relative_to(BASE_DIR))
|
||
|
|
|
||
|
|
# Buscar en ubicaciones comunes
|
||
|
|
ubicaciones = [
|
||
|
|
BASE_DIR / "public" / ruta,
|
||
|
|
BASE_DIR / "src" / "assets" / ruta,
|
||
|
|
BASE_DIR / "assets" / ruta,
|
||
|
|
BASE_DIR / "images" / ruta,
|
||
|
|
BASE_DIR / "docs" / ruta,
|
||
|
|
MANUAL_DIR / ruta,
|
||
|
|
]
|
||
|
|
|
||
|
|
for ubicacion in ubicaciones:
|
||
|
|
if ubicacion.exists() and ubicacion.is_file():
|
||
|
|
return True, str(ubicacion.relative_to(BASE_DIR))
|
||
|
|
|
||
|
|
return False, ruta
|
||
|
|
|
||
|
|
def obtener_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 main():
|
||
|
|
print("🔍 Búsqueda EXHAUSTIVA de referencias a archivos multimedia...")
|
||
|
|
archivos_md = obtener_archivos_md()
|
||
|
|
print(f" Analizando {len(archivos_md)} archivos .md...")
|
||
|
|
|
||
|
|
todas_referencias = []
|
||
|
|
for archivo in archivos_md:
|
||
|
|
refs = buscar_referencias_exhaustivas(archivo)
|
||
|
|
todas_referencias.extend(refs)
|
||
|
|
|
||
|
|
print(f" Encontradas {len(todas_referencias)} referencias potenciales")
|
||
|
|
|
||
|
|
# Verificar existencia
|
||
|
|
print(" Verificando existencia de archivos...")
|
||
|
|
resultados = []
|
||
|
|
|
||
|
|
for ref in todas_referencias:
|
||
|
|
existe, ruta_encontrada = verificar_existencia(ref['ruta_referenciada'], Path(BASE_DIR / ref['ruta_md']))
|
||
|
|
|
||
|
|
resultados.append({
|
||
|
|
'Archivo MD': ref['archivo_md'],
|
||
|
|
'Ruta MD': ref['ruta_md'],
|
||
|
|
'Línea': ref['linea'],
|
||
|
|
'Tipo': ref['tipo'],
|
||
|
|
'Ruta Referenciada': ref['ruta_referenciada'],
|
||
|
|
'Extensión': ref['extension'],
|
||
|
|
'Existe': 'Sí' if existe else 'No',
|
||
|
|
'Ruta Encontrada': ruta_encontrada if existe else 'N/A',
|
||
|
|
'Contexto': ref['contexto']
|
||
|
|
})
|
||
|
|
|
||
|
|
# Separar faltantes
|
||
|
|
faltantes = [r for r in resultados if r['Existe'] == 'No']
|
||
|
|
existentes = [r for r in resultados if r['Existe'] == 'Sí']
|
||
|
|
|
||
|
|
# Generar CSV
|
||
|
|
csv_path = BASE_DIR / "REFERENCIAS_MULTIMEDIA_COMPLETO.csv"
|
||
|
|
with open(csv_path, 'w', newline='', encoding='utf-8') as f:
|
||
|
|
if resultados:
|
||
|
|
writer = csv.DictWriter(f, fieldnames=resultados[0].keys())
|
||
|
|
writer.writeheader()
|
||
|
|
writer.writerows(resultados)
|
||
|
|
else:
|
||
|
|
f.write("Archivo MD,Ruta MD,Línea,Tipo,Ruta Referenciada,Extensión,Existe,Ruta Encontrada,Contexto\n")
|
||
|
|
|
||
|
|
# Generar reporte Markdown
|
||
|
|
reporte = []
|
||
|
|
reporte.append("# REPORTE EXHAUSTIVO: REFERENCIAS A ARCHIVOS MULTIMEDIA\n")
|
||
|
|
reporte.append(f"**Fecha:** {__import__('datetime').datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
||
|
|
reporte.append("---\n")
|
||
|
|
|
||
|
|
reporte.append("## 📊 RESUMEN EJECUTIVO\n")
|
||
|
|
reporte.append(f"- **Total de archivos .md analizados:** {len(archivos_md)}\n")
|
||
|
|
reporte.append(f"- **Total de referencias encontradas:** {len(resultados)}\n")
|
||
|
|
reporte.append(f"- **Archivos existentes:** {len(existentes)}\n")
|
||
|
|
reporte.append(f"- **Archivos faltantes:** {len(faltantes)}\n")
|
||
|
|
|
||
|
|
if resultados:
|
||
|
|
reporte.append(f"- **Porcentaje de completitud:** {(len(existentes)/len(resultados)*100):.1f}%\n")
|
||
|
|
else:
|
||
|
|
reporte.append("- **Porcentaje de completitud:** N/A (no se encontraron referencias)\n")
|
||
|
|
|
||
|
|
reporte.append("---\n")
|
||
|
|
|
||
|
|
if faltantes:
|
||
|
|
reporte.append("## ❌ ARCHIVOS MULTIMEDIA FALTANTES\n")
|
||
|
|
reporte.append(f"**Total:** {len(faltantes)} referencias a archivos que NO existen\n\n")
|
||
|
|
reporte.append("| Archivo MD | Línea | Tipo | Ruta Referenciada | Extensión | Contexto |\n")
|
||
|
|
reporte.append("|------------|-------|------|-------------------|-----------|----------|\n")
|
||
|
|
|
||
|
|
for ref in faltantes:
|
||
|
|
contexto_corto = ref['Contexto'][:80].replace('|', '\\|')
|
||
|
|
reporte.append(f"| `{ref['Archivo MD']}` | {ref['Línea']} | {ref['Tipo']} | `{ref['Ruta Referenciada']}` | {ref['Extensión']} | {contexto_corto}... |\n")
|
||
|
|
else:
|
||
|
|
reporte.append("## ✅ NO SE ENCONTRARON ARCHIVOS MULTIMEDIA FALTANTES\n")
|
||
|
|
if len(resultados) == 0:
|
||
|
|
reporte.append("\n**Resultado:** No se encontraron referencias a archivos multimedia en ningún archivo .md del proyecto.\n")
|
||
|
|
reporte.append("\nEsto indica que:\n")
|
||
|
|
reporte.append("- Los archivos .md no contienen referencias a imágenes, videos o documentos externos\n")
|
||
|
|
reporte.append("- El contenido es principalmente texto con formato Markdown\n")
|
||
|
|
reporte.append("- No hay dependencias de archivos multimedia que deban ser creados\n")
|
||
|
|
else:
|
||
|
|
reporte.append("\nTodas las referencias encontradas apuntan a archivos existentes.\n")
|
||
|
|
|
||
|
|
reporte.append("\n---\n")
|
||
|
|
|
||
|
|
if existentes and len(existentes) > 0:
|
||
|
|
reporte.append("## ✅ ARCHIVOS MULTIMEDIA EXISTENTES\n")
|
||
|
|
reporte.append(f"**Total:** {len(existentes)} referencias a archivos que existen\n\n")
|
||
|
|
reporte.append("| Archivo MD | Tipo | Ruta Referenciada | Ruta Encontrada |\n")
|
||
|
|
reporte.append("|------------|------|-------------------|-----------------|\n")
|
||
|
|
|
||
|
|
for ref in existentes[:30]:
|
||
|
|
reporte.append(f"| `{ref['Archivo MD']}` | {ref['Tipo']} | `{ref['Ruta Referenciada']}` | `{ref['Ruta Encontrada']}` |\n")
|
||
|
|
|
||
|
|
if len(existentes) > 30:
|
||
|
|
reporte.append(f"\n*... y {len(existentes) - 30} referencias más (ver CSV completo)*\n")
|
||
|
|
|
||
|
|
reporte.append("\n---\n")
|
||
|
|
|
||
|
|
if resultados:
|
||
|
|
reporte.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.append("| Tipo | Total | Faltantes | Existentes |\n")
|
||
|
|
reporte.append("|------|-------|-----------|------------|\n")
|
||
|
|
|
||
|
|
for tipo in sorted(por_tipo.keys()):
|
||
|
|
total = por_tipo[tipo]
|
||
|
|
faltantes_tipo = por_tipo_faltantes.get(tipo, 0)
|
||
|
|
existentes_tipo = total - faltantes_tipo
|
||
|
|
reporte.append(f"| {tipo} | {total} | {faltantes_tipo} | {existentes_tipo} |\n")
|
||
|
|
|
||
|
|
reporte.append("\n---\n")
|
||
|
|
reporte.append("## 📄 ARCHIVOS GENERADOS\n")
|
||
|
|
reporte.append(f"- **CSV completo:** `REFERENCIAS_MULTIMEDIA_COMPLETO.csv`\n")
|
||
|
|
reporte.append(f"- **Reporte Markdown:** Este archivo\n")
|
||
|
|
reporte.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))
|
||
|
|
|
||
|
|
print(f"\n✅ Reporte generado: {reporte_path}")
|
||
|
|
print(f"✅ CSV generado: {csv_path}")
|
||
|
|
print(f"\n📊 Resumen final:")
|
||
|
|
print(f" - Archivos analizados: {len(archivos_md)}")
|
||
|
|
print(f" - Referencias encontradas: {len(resultados)}")
|
||
|
|
print(f" - Archivos faltantes: {len(faltantes)}")
|
||
|
|
print(f" - Archivos existentes: {len(existentes)}")
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|