- Actualizadas 93 rutas en manual-index.ts para apuntar a /manual/ - Simplificado ManualViewer para usar rutas directas del índice - Agregados scripts de limpieza y actualización de rutas - Documentación completa de la limpieza e integración - 93 archivos del manual organizados en public/manual/ - Backup excluido del repositorio (muy pesado)
288 lines
11 KiB
Python
Executable file
288 lines
11 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""
|
|
Script de Limpieza e Integración del Manual TES Digital
|
|
|
|
OBJETIVO:
|
|
1. Identificar los 93 archivos .md válidos según manual-index.ts
|
|
2. Crear backup completo
|
|
3. Eliminar archivos obsoletos/duplicados
|
|
4. Asegurar que los 93 archivos estén en public/manual/ con estructura correcta
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import shutil
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Set, List, Dict
|
|
from datetime import datetime
|
|
|
|
# Configuración
|
|
PROJECT_ROOT = Path(__file__).parent.parent
|
|
MANUAL_INDEX_PATH = PROJECT_ROOT / "src/data/manual-index.ts"
|
|
BACKUP_DIR = PROJECT_ROOT / "backup_manual_pre_limpieza"
|
|
MANUAL_SOURCE_DIR = PROJECT_ROOT / "manual-tes/TES_Manual_Digital"
|
|
MANUAL_PUBLIC_DIR = PROJECT_ROOT / "public/manual"
|
|
|
|
# Patrones para archivos obsoletos
|
|
PATRONES_OBSOLETOS = [
|
|
r".*\.(tmp|backup|old|bak)\.md$",
|
|
r".*prueba.*\.md$",
|
|
r".*test.*\.md$",
|
|
r".*ejemplo.*\.md$",
|
|
r".*temp.*\.md$",
|
|
]
|
|
|
|
def extraer_rutas_validas_del_indice() -> Set[str]:
|
|
"""Extrae las rutas de archivos válidos del manual-index.ts"""
|
|
rutas_validas = set()
|
|
|
|
if not MANUAL_INDEX_PATH.exists():
|
|
print(f"❌ ERROR: No se encuentra {MANUAL_INDEX_PATH}")
|
|
return rutas_validas
|
|
|
|
contenido = MANUAL_INDEX_PATH.read_text(encoding='utf-8')
|
|
|
|
# Buscar todas las rutas en el formato: rutaArchivo: "ruta/a/archivo.md"
|
|
patron = r'rutaArchivo:\s*"([^"]+)"'
|
|
matches = re.findall(patron, contenido)
|
|
|
|
for match in matches:
|
|
# Normalizar ruta relativa al proyecto
|
|
ruta_completa = PROJECT_ROOT / match
|
|
if ruta_completa.exists():
|
|
rutas_validas.add(str(ruta_completa))
|
|
else:
|
|
print(f"⚠️ ADVERTENCIA: Archivo del índice no encontrado: {match}")
|
|
|
|
print(f"✅ Encontradas {len(rutas_validas)} rutas válidas en el índice")
|
|
return rutas_validas
|
|
|
|
def encontrar_todos_los_md(excluir_node_modules: bool = True) -> List[Path]:
|
|
"""Encuentra todos los archivos .md en el proyecto"""
|
|
archivos_md = []
|
|
|
|
for root, dirs, files in os.walk(PROJECT_ROOT):
|
|
# Excluir node_modules y otros directorios
|
|
if excluir_node_modules and 'node_modules' in root:
|
|
continue
|
|
if '.git' in root:
|
|
continue
|
|
if 'backup' in root.lower():
|
|
continue
|
|
|
|
for file in files:
|
|
if file.endswith('.md'):
|
|
archivo_path = Path(root) / file
|
|
archivos_md.append(archivo_path)
|
|
|
|
return archivos_md
|
|
|
|
def es_archivo_obsoleto(archivo: Path, rutas_validas: Set[str]) -> bool:
|
|
"""Determina si un archivo es obsoleto"""
|
|
ruta_str = str(archivo)
|
|
|
|
# Si está en las rutas válidas, NO es obsoleto
|
|
if ruta_str in rutas_validas:
|
|
return False
|
|
|
|
# Verificar patrones obsoletos
|
|
nombre_archivo = archivo.name.lower()
|
|
for patron in PATRONES_OBSOLETOS:
|
|
if re.match(patron, nombre_archivo):
|
|
return True
|
|
|
|
# Archivos fuera de la estructura principal del manual
|
|
if 'manual-tes' not in ruta_str and 'public/manual' not in ruta_str:
|
|
# Pero mantener documentación del proyecto (README, etc.)
|
|
if archivo.name.upper() in ['README.MD', 'CHANGELOG.MD', 'LICENSE.MD', 'SECURITY.MD']:
|
|
return False
|
|
# Mantener archivos de documentación en la raíz o docs/
|
|
if archivo.parent.name in ['docs', ''] or archivo.parent == PROJECT_ROOT:
|
|
# Solo si son archivos de documentación del proyecto
|
|
if any(keyword in archivo.name.upper() for keyword in ['PLAN', 'GUIA', 'INSTRUCCION', 'PROBLEMA', 'CORRECCION']):
|
|
return False
|
|
|
|
# Archivos duplicados en diferentes ubicaciones (mantener solo los de TES_Manual_Digital)
|
|
if 'manual-tes' in ruta_str:
|
|
if 'TES_Manual_Digital' not in ruta_str:
|
|
# Archivos fuera de TES_Manual_Digital pero dentro de manual-tes
|
|
return True
|
|
|
|
return False
|
|
|
|
def crear_backup(archivos_a_backup: List[Path]) -> bool:
|
|
"""Crea un backup de todos los archivos .md antes de limpiar"""
|
|
print(f"\n📦 Creando backup en {BACKUP_DIR}...")
|
|
|
|
try:
|
|
BACKUP_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
backup_log = BACKUP_DIR / f"backup_log_{timestamp}.txt"
|
|
|
|
with open(backup_log, 'w', encoding='utf-8') as log:
|
|
log.write(f"Backup creado: {timestamp}\n")
|
|
log.write(f"Total archivos: {len(archivos_a_backup)}\n\n")
|
|
|
|
for archivo in archivos_a_backup:
|
|
try:
|
|
# Mantener estructura relativa
|
|
ruta_relativa = archivo.relative_to(PROJECT_ROOT)
|
|
destino = BACKUP_DIR / ruta_relativa
|
|
destino.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(archivo, destino)
|
|
log.write(f"✅ {ruta_relativa}\n")
|
|
except Exception as e:
|
|
log.write(f"❌ ERROR copiando {archivo}: {e}\n")
|
|
|
|
print(f"✅ Backup completado: {len(archivos_a_backup)} archivos")
|
|
print(f"📄 Log guardado en: {backup_log}")
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"❌ ERROR creando backup: {e}")
|
|
return False
|
|
|
|
def copiar_archivos_validos_a_public(rutas_validas: Set[str]) -> Dict[str, bool]:
|
|
"""Copia los archivos válidos a public/manual/ con estructura correcta"""
|
|
resultados = {}
|
|
|
|
print(f"\n📋 Copiando archivos válidos a {MANUAL_PUBLIC_DIR}...")
|
|
|
|
MANUAL_PUBLIC_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
for ruta_str in rutas_validas:
|
|
archivo_origen = Path(ruta_str)
|
|
|
|
if not archivo_origen.exists():
|
|
resultados[ruta_str] = False
|
|
print(f"⚠️ No encontrado: {archivo_origen}")
|
|
continue
|
|
|
|
# Extraer estructura de carpetas desde la ruta
|
|
# Ejemplo: manual-tes/TES_Manual_Digital/BLOQUE_0_FUNDAMENTOS/archivo.md
|
|
partes = archivo_origen.parts
|
|
|
|
# Buscar BLOQUE_X en la ruta
|
|
bloque_dir = None
|
|
for parte in partes:
|
|
if parte.startswith('BLOQUE_'):
|
|
bloque_dir = parte
|
|
break
|
|
|
|
if not bloque_dir:
|
|
print(f"⚠️ No se pudo determinar bloque para: {archivo_origen}")
|
|
resultados[ruta_str] = False
|
|
continue
|
|
|
|
# Crear estructura en public/manual/
|
|
destino_dir = MANUAL_PUBLIC_DIR / bloque_dir
|
|
destino_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
destino_archivo = destino_dir / archivo_origen.name
|
|
|
|
try:
|
|
shutil.copy2(archivo_origen, destino_archivo)
|
|
resultados[ruta_str] = True
|
|
print(f"✅ Copiado: {archivo_origen.name} → {destino_dir.name}/")
|
|
except Exception as e:
|
|
resultados[ruta_str] = False
|
|
print(f"❌ ERROR copiando {archivo_origen.name}: {e}")
|
|
|
|
exitosos = sum(1 for v in resultados.values() if v)
|
|
print(f"\n✅ Copiados {exitosos}/{len(rutas_validas)} archivos a public/manual/")
|
|
|
|
return resultados
|
|
|
|
def generar_reporte_limpieza(archivos_obsoletos: List[Path], archivos_validos: List[Path]) -> Path:
|
|
"""Genera un reporte detallado de la limpieza"""
|
|
reporte_path = PROJECT_ROOT / "REPORTE_LIMPIEZA_MANUAL.md"
|
|
|
|
with open(reporte_path, 'w', encoding='utf-8') as f:
|
|
f.write("# 📋 Reporte de Limpieza del Manual TES Digital\n\n")
|
|
f.write(f"**Fecha:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
|
|
|
|
f.write("## 📊 Resumen\n\n")
|
|
f.write(f"- **Archivos válidos encontrados:** {len(archivos_validos)}\n")
|
|
f.write(f"- **Archivos obsoletos identificados:** {len(archivos_obsoletos)}\n")
|
|
f.write(f"- **Total archivos .md en proyecto:** {len(archivos_validos) + len(archivos_obsoletos)}\n\n")
|
|
|
|
f.write("## ✅ Archivos Válidos (93 esperados)\n\n")
|
|
for archivo in sorted(archivos_validos):
|
|
f.write(f"- `{archivo.relative_to(PROJECT_ROOT)}`\n")
|
|
|
|
f.write("\n## 🗑️ Archivos Obsoletos Identificados\n\n")
|
|
f.write("**NOTA:** Estos archivos pueden ser eliminados de forma segura.\n\n")
|
|
for archivo in sorted(archivos_obsoletos):
|
|
f.write(f"- `{archivo.relative_to(PROJECT_ROOT)}`\n")
|
|
|
|
print(f"\n📄 Reporte generado: {reporte_path}")
|
|
return reporte_path
|
|
|
|
def main():
|
|
"""Función principal"""
|
|
print("=" * 70)
|
|
print("🧹 LIMPIEZA E INTEGRACIÓN DEL MANUAL TES DIGITAL")
|
|
print("=" * 70)
|
|
|
|
# Paso 1: Extraer rutas válidas del índice
|
|
print("\n[1/6] Extrayendo rutas válidas del índice...")
|
|
rutas_validas = extraer_rutas_validas_del_indice()
|
|
|
|
if len(rutas_validas) != 93:
|
|
print(f"⚠️ ADVERTENCIA: Se esperaban 93 archivos, se encontraron {len(rutas_validas)}")
|
|
|
|
# Paso 2: Encontrar todos los archivos .md
|
|
print("\n[2/6] Buscando todos los archivos .md...")
|
|
todos_los_md = encontrar_todos_los_md()
|
|
print(f"✅ Encontrados {len(todos_los_md)} archivos .md en total")
|
|
|
|
# Paso 3: Clasificar archivos
|
|
print("\n[3/6] Clasificando archivos (válidos vs obsoletos)...")
|
|
archivos_validos = []
|
|
archivos_obsoletos = []
|
|
|
|
for archivo in todos_los_md:
|
|
if str(archivo) in rutas_validas:
|
|
archivos_validos.append(archivo)
|
|
elif es_archivo_obsoleto(archivo, rutas_validas):
|
|
archivos_obsoletos.append(archivo)
|
|
# Los demás se mantienen (documentación del proyecto, etc.)
|
|
|
|
print(f"✅ Archivos válidos: {len(archivos_validos)}")
|
|
print(f"🗑️ Archivos obsoletos identificados: {len(archivos_obsoletos)}")
|
|
|
|
# Paso 4: Crear backup
|
|
print("\n[4/6] Creando backup...")
|
|
if not crear_backup(todos_los_md):
|
|
print("❌ ERROR: No se pudo crear el backup. Abortando.")
|
|
return
|
|
|
|
# Paso 5: Copiar archivos válidos a public/manual/
|
|
print("\n[5/6] Copiando archivos válidos a public/manual/...")
|
|
resultados_copia = copiar_archivos_validos_a_public(rutas_validas)
|
|
|
|
# Paso 6: Generar reporte
|
|
print("\n[6/6] Generando reporte...")
|
|
reporte = generar_reporte_limpieza(archivos_obsoletos, archivos_validos)
|
|
|
|
# Resumen final
|
|
print("\n" + "=" * 70)
|
|
print("✅ LIMPIEZA COMPLETADA")
|
|
print("=" * 70)
|
|
print(f"\n📊 Resumen:")
|
|
print(f" - Archivos válidos: {len(archivos_validos)}")
|
|
print(f" - Archivos obsoletos identificados: {len(archivos_obsoletos)}")
|
|
print(f" - Archivos copiados a public/manual/: {sum(1 for v in resultados_copia.values() if v)}")
|
|
print(f"\n📦 Backup guardado en: {BACKUP_DIR}")
|
|
print(f"📄 Reporte guardado en: {reporte}")
|
|
print(f"\n⚠️ PRÓXIMOS PASOS:")
|
|
print(f" 1. Revisar el reporte: {reporte}")
|
|
print(f" 2. Verificar que public/manual/ tenga los 93 archivos")
|
|
print(f" 3. Si todo está bien, ejecutar eliminación de obsoletos")
|
|
print(f" 4. Probar la aplicación")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|