- 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
633 lines
32 KiB
Python
633 lines
32 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Script para generar reportes completos para conversión a app digital
|
|
Genera 3 reportes:
|
|
1. Archivos .md faltantes del índice
|
|
2. Medios/multimedia faltantes
|
|
3. Recomendaciones para estructura de app
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
from pathlib import Path
|
|
from collections import defaultdict
|
|
from typing import Dict, List, Set, Tuple
|
|
|
|
BASE_DIR = Path("/home/planetazuzu/protocolo-r-pido")
|
|
MANUAL_DIR = BASE_DIR / "manual-tes" / "TES_Manual_Digital"
|
|
|
|
# Mapeo del índice esperado (del INDICE_COMPLETO_MANUAL_TES.md)
|
|
INDICE_ESPERADO = {
|
|
"1.1.1": {"nombre": "Fundamentos de Emergencias", "bloque": 0, "parte": 1},
|
|
"1.2.1": {"nombre": "Constantes Vitales", "bloque": 1, "parte": 1},
|
|
"1.2.2": {"nombre": "ABCDE Operativo", "bloque": 1, "parte": 1},
|
|
"1.2.3": {"nombre": "Glasgow Operativo", "bloque": 1, "parte": 1},
|
|
"1.2.4": {"nombre": "Triage START", "bloque": 1, "parte": 1},
|
|
"2.1.1": {"nombre": "Acceso Vascular Básico", "bloque": 4, "parte": 2},
|
|
"2.1.2": {"nombre": "Reconocimiento PCR", "bloque": 4, "parte": 2},
|
|
"2.1.3": {"nombre": "RCP Adultos", "bloque": 4, "parte": 2},
|
|
"2.1.4": {"nombre": "RCP Pediatría", "bloque": 4, "parte": 2},
|
|
"2.1.5": {"nombre": "RCP Lactantes", "bloque": 4, "parte": 2},
|
|
"2.1.6": {"nombre": "Uso DESA", "bloque": 4, "parte": 2},
|
|
"2.1.7": {"nombre": "RCP Dos Intervinientes", "bloque": 4, "parte": 2},
|
|
"2.1.8": {"nombre": "OVACE Adultos", "bloque": 4, "parte": 2},
|
|
"2.1.9": {"nombre": "OVACE Pediatría", "bloque": 4, "parte": 2},
|
|
"2.1.10": {"nombre": "OVACE Lactantes", "bloque": 4, "parte": 2},
|
|
"2.1.11": {"nombre": "Posición Lateral de Seguridad", "bloque": 4, "parte": 2},
|
|
"2.2.1": {"nombre": "Medicina de Emergencias Aplicada", "bloque": 9, "parte": 2},
|
|
"3.1.1": {"nombre": "Anatomía Operativa", "bloque": 2, "parte": 3},
|
|
"3.1.2": {"nombre": "Inmovilización Manual", "bloque": 2, "parte": 3},
|
|
"3.1.3": {"nombre": "Collarín Cervical", "bloque": 2, "parte": 3},
|
|
"3.1.4": {"nombre": "Camilla Cuchara", "bloque": 2, "parte": 3},
|
|
"3.1.5": {"nombre": "Tablero Espinal", "bloque": 2, "parte": 3},
|
|
"3.1.6": {"nombre": "Colchón Vacío", "bloque": 2, "parte": 3},
|
|
"3.1.7": {"nombre": "Extricación y Movimientos en Bloque", "bloque": 2, "parte": 3},
|
|
"3.1.8": {"nombre": "Transferencias y Movilización", "bloque": 2, "parte": 3},
|
|
"3.1.9": {"nombre": "Errores Críticos", "bloque": 2, "parte": 3},
|
|
"3.1.10": {"nombre": "Férulas", "bloque": 2, "parte": 3},
|
|
"3.1.11": {"nombre": "Cinturón Pélvico", "bloque": 2, "parte": 3},
|
|
"3.1.12": {"nombre": "Férula de Tracción", "bloque": 2, "parte": 3},
|
|
"3.1.13": {"nombre": "Camillas y Sillas de Evacuación", "bloque": 2, "parte": 3},
|
|
"3.1.14": {"nombre": "Inventario de Material", "bloque": 2, "parte": 3},
|
|
"3.2.1": {"nombre": "Oxigenoterapia Básica", "bloque": 3, "parte": 3},
|
|
"3.2.2": {"nombre": "Oxigenoterapia - Fundamentos", "bloque": 3, "parte": 3},
|
|
"3.2.3": {"nombre": "Dispositivos de Oxigenoterapia", "bloque": 3, "parte": 3},
|
|
"3.2.4": {"nombre": "Ventilación con Bolsa-Mascarilla", "bloque": 3, "parte": 3},
|
|
"3.2.5": {"nombre": "Aspiración", "bloque": 3, "parte": 3},
|
|
"3.2.6": {"nombre": "Cánula Orofaringea", "bloque": 3, "parte": 3},
|
|
"3.2.7": {"nombre": "BVM (Bolsa Válvula Mascarilla)", "bloque": 3, "parte": 3},
|
|
"3.2.8": {"nombre": "Cánulas", "bloque": 3, "parte": 3},
|
|
"3.2.9": {"nombre": "Organización del Maletín", "bloque": 3, "parte": 3},
|
|
"3.2.10": {"nombre": "Control de Hemorragias", "bloque": 3, "parte": 3},
|
|
"3.2.11": {"nombre": "Quemaduras", "bloque": 3, "parte": 3},
|
|
"3.2.12": {"nombre": "Heridas y Vendajes", "bloque": 3, "parte": 3},
|
|
"3.2.13": {"nombre": "Exposición y Aislamiento Térmico", "bloque": 3, "parte": 3},
|
|
"3.2.14": {"nombre": "Monitorización Básica", "bloque": 3, "parte": 3},
|
|
"3.2.15": {"nombre": "Glucometro", "bloque": 3, "parte": 3},
|
|
"3.2.16": {"nombre": "Termometría", "bloque": 3, "parte": 3},
|
|
"3.2.17": {"nombre": "Confort y Dolor", "bloque": 3, "parte": 3},
|
|
"3.2.18": {"nombre": "Bioseguridad y Descontaminación", "bloque": 3, "parte": 3},
|
|
"3.2.19": {"nombre": "Gestión de Material en Escena", "bloque": 3, "parte": 3},
|
|
"3.2.20": {"nombre": "Comunicación Operativa", "bloque": 3, "parte": 3},
|
|
"3.2.21": {"nombre": "Señalización e Iluminación", "bloque": 3, "parte": 3},
|
|
"3.2.22": {"nombre": "Documentación Operativa", "bloque": 3, "parte": 3},
|
|
"3.2.23": {"nombre": "Cierre Bloque 3", "bloque": 3, "parte": 3},
|
|
"3.2.24": {"nombre": "Inventario Material Sanitario", "bloque": 3, "parte": 3},
|
|
"3.2.25": {"nombre": "Maletín de Curas", "bloque": 3, "parte": 3},
|
|
"3.2.26": {"nombre": "Bolsa de Monitorización", "bloque": 3, "parte": 3},
|
|
"3.2.27": {"nombre": "Inventario Global", "bloque": 3, "parte": 3},
|
|
"3.2.28": {"nombre": "Checklist Maestro", "bloque": 3, "parte": 3},
|
|
"4.1.1": {"nombre": "Principios de Administración de Fármacos", "bloque": 6, "parte": 4},
|
|
"4.1.2": {"nombre": "Vademécum Operativo", "bloque": 6, "parte": 4},
|
|
"4.1.3": {"nombre": "Oxígeno - Administración y Seguridad", "bloque": 6, "parte": 4},
|
|
"4.1.4": {"nombre": "Adrenalina - Uso en Anafilaxia y RCP", "bloque": 6, "parte": 4},
|
|
"4.1.5": {"nombre": "Aspirina - Uso en SCA", "bloque": 6, "parte": 4},
|
|
"4.1.6": {"nombre": "Glucagón - Uso en Hipoglucemia", "bloque": 6, "parte": 4},
|
|
"4.1.7": {"nombre": "Salbutamol - Uso en Crisis Asmática", "bloque": 6, "parte": 4},
|
|
"4.1.8": {"nombre": "Abreviaturas y Terminología Farmacológica", "bloque": 6, "parte": 4},
|
|
"5.1.1": {"nombre": "Introducción a Protocolos Transtelefónicos", "bloque": 5, "parte": 5},
|
|
"5.1.2": {"nombre": "Protocolos de Emergencias Específicas", "bloque": 5, "parte": 5},
|
|
"5.1.3": {"nombre": "PCR Transtelefónica", "bloque": 5, "parte": 5},
|
|
"5.1.4": {"nombre": "OVACE Transtelefónica", "bloque": 5, "parte": 5},
|
|
"5.1.5": {"nombre": "SCA Transtelefónico", "bloque": 5, "parte": 5},
|
|
"5.1.6": {"nombre": "ICTUS Transtelefónico", "bloque": 5, "parte": 5},
|
|
"5.1.7": {"nombre": "Anafilaxia Transtelefónica", "bloque": 5, "parte": 5},
|
|
"5.1.8": {"nombre": "Crisis Asmática Transtelefónica", "bloque": 5, "parte": 5},
|
|
"5.1.9": {"nombre": "Hipoglucemia Transtelefónica", "bloque": 5, "parte": 5},
|
|
"5.1.10": {"nombre": "Comunicación con Coordinador", "bloque": 5, "parte": 5},
|
|
"5.2.1": {"nombre": "Introducción a Gestión Operativa", "bloque": 8, "parte": 5},
|
|
"5.2.2": {"nombre": "Documentación Clínica Prehospitalaria", "bloque": 8, "parte": 5},
|
|
"5.2.3": {"nombre": "Coordinación y Comunicación Operativa", "bloque": 8, "parte": 5},
|
|
"5.2.4": {"nombre": "Gestión de Recursos y Material", "bloque": 8, "parte": 5},
|
|
"5.2.5": {"nombre": "Calidad y Mejora Continua", "bloque": 8, "parte": 5},
|
|
"6.1.1": {"nombre": "Fundamentos de Conducción en Urgencias", "bloque": 7, "parte": 6},
|
|
"6.1.2": {"nombre": "Uso de Luces y Sirena", "bloque": 7, "parte": 6},
|
|
"6.1.3": {"nombre": "Técnicas de Conducción en Emergencias", "bloque": 7, "parte": 6},
|
|
"6.1.4": {"nombre": "Seguridad Vial y Prevención de Accidentes", "bloque": 7, "parte": 6},
|
|
"6.1.5": {"nombre": "Gestión de Rutas y Navegación", "bloque": 7, "parte": 6},
|
|
"6.1.6": {"nombre": "Protocolos de Seguridad en Escena", "bloque": 7, "parte": 6},
|
|
"7.1.1": {"nombre": "Situaciones Especiales", "bloque": 10, "parte": 7},
|
|
"7.2.1": {"nombre": "Protocolos de Trauma", "bloque": 11, "parte": 7},
|
|
"8.1.1": {"nombre": "Marco Legal, Ético y Profesional del TES", "bloque": 12, "parte": 8},
|
|
"8.2.1": {"nombre": "Comunicación y Relación con el Paciente", "bloque": 13, "parte": 8},
|
|
"8.3.1": {"nombre": "Seguridad Personal y Salud del TES", "bloque": 14, "parte": 8},
|
|
}
|
|
|
|
BLOQUE_TO_FOLDER = {
|
|
0: "BLOQUE_0_FUNDAMENTOS",
|
|
1: "BLOQUE_1_PROCEDIMIENTOS_BASICOS",
|
|
2: "BLOQUE_2_MATERIAL_E_INMOVILIZACION",
|
|
3: "BLOQUE_3_MATERIAL_SANITARIO_Y_OXIGENOTERAPIA",
|
|
4: "BLOQUE_4_SOPORTE_VITAL_BASICO_Y_RCP",
|
|
5: "BLOQUE_5_PROTOCOLOS_TRANSTELEFONICOS",
|
|
6: "BLOQUE_6_FARMACOLOGIA",
|
|
7: "BLOQUE_7_CONDUCCION_Y_SEGURIDAD_VIAL",
|
|
8: "BLOQUE_8_GESTION_OPERATIVA_Y_DOCUMENTACION",
|
|
9: "BLOQUE_9_MEDICINA_EMERGENCIAS_APLICADA",
|
|
10: "BLOQUE_10_SITUACIONES_ESPECIALES",
|
|
11: "BLOQUE_11_PROTOCOLOS_TRAUMA",
|
|
12: "BLOQUE_12_MARCO_LEGAL_ETICO_PROFESIONAL",
|
|
13: "BLOQUE_13_COMUNICACION_RELACION_PACIENTE",
|
|
14: "BLOQUE_14_SEGURIDAD_PERSONAL_SALUD_TES",
|
|
}
|
|
|
|
def obtener_archivos_md_existentes() -> Dict[str, Path]:
|
|
"""Obtiene todos los archivos .md existentes mapeados por nombre"""
|
|
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[archivo.name] = archivo
|
|
return archivos
|
|
|
|
def buscar_referencias_multimedia(archivo: Path) -> List[Dict]:
|
|
"""Busca referencias a medios/multimedia 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):
|
|
# Buscar imágenes: 
|
|
patron_imagen = r'!\[([^\]]*)\]\(([^\)]+)\)'
|
|
matches = re.findall(patron_imagen, linea)
|
|
for texto_alt, ruta in matches:
|
|
referencias.append({
|
|
'tipo': 'imagen',
|
|
'texto_alt': texto_alt,
|
|
'ruta': ruta,
|
|
'linea': num_linea,
|
|
'archivo': archivo.name,
|
|
'ruta_completa': str(archivo)
|
|
})
|
|
|
|
# Buscar enlaces a archivos multimedia
|
|
patron_multimedia = r'\[([^\]]+)\]\(([^\)]+\.(jpg|jpeg|png|gif|svg|pdf|mp4|mp3|avi|mov|wav))\)'
|
|
matches = re.findall(patron_multimedia, linea, re.IGNORECASE)
|
|
for texto, ruta, ext in matches:
|
|
referencias.append({
|
|
'tipo': 'multimedia',
|
|
'texto': texto,
|
|
'ruta': ruta,
|
|
'extension': ext,
|
|
'linea': num_linea,
|
|
'archivo': archivo.name,
|
|
'ruta_completa': str(archivo)
|
|
})
|
|
|
|
# Buscar referencias a videos
|
|
if 'video' in linea.lower() or 'youtube' in linea.lower() or 'vimeo' in linea.lower():
|
|
referencias.append({
|
|
'tipo': 'video_referencia',
|
|
'linea': num_linea,
|
|
'contenido': linea.strip(),
|
|
'archivo': archivo.name,
|
|
'ruta_completa': str(archivo)
|
|
})
|
|
except Exception as e:
|
|
print(f"Error leyendo {archivo}: {e}")
|
|
|
|
return referencias
|
|
|
|
def verificar_existencia_medio(ruta: str, archivo_origen: Path) -> bool:
|
|
"""Verifica si un archivo multimedia existe"""
|
|
# Si es ruta absoluta
|
|
if os.path.isabs(ruta):
|
|
return os.path.exists(ruta)
|
|
|
|
# Si es ruta relativa
|
|
archivo_dir = archivo_origen.parent
|
|
ruta_completa = (archivo_dir / ruta).resolve()
|
|
|
|
# Verificar si existe
|
|
if ruta_completa.exists():
|
|
return True
|
|
|
|
# Buscar en otras ubicaciones comunes
|
|
posibles_rutas = [
|
|
MANUAL_DIR / ruta,
|
|
BASE_DIR / ruta,
|
|
BASE_DIR / "public" / ruta,
|
|
BASE_DIR / "src" / "assets" / ruta,
|
|
]
|
|
|
|
return any(os.path.exists(r) for r in posibles_rutas)
|
|
|
|
def generar_reporte_1_archivos_faltantes():
|
|
"""Genera reporte 1: Archivos .md faltantes del índice"""
|
|
archivos_existentes = obtener_archivos_md_existentes()
|
|
|
|
# Mapear archivos esperados según el índice
|
|
archivos_esperados = {}
|
|
for codigo, info in INDICE_ESPERADO.items():
|
|
bloque_num = info["bloque"]
|
|
folder_name = BLOQUE_TO_FOLDER[bloque_num]
|
|
# Generar nombre esperado basado en el patrón observado
|
|
nombre_base = info["nombre"].upper().replace(" ", "_").replace("-", "_")
|
|
nombre_archivo = f"BLOQUE_{bloque_num:02d}_{codigo.split('.')[-1]}_{nombre_base}.md"
|
|
archivos_esperados[codigo] = {
|
|
'nombre': info["nombre"],
|
|
'archivo_esperado': nombre_archivo,
|
|
'bloque': bloque_num,
|
|
'folder': folder_name
|
|
}
|
|
|
|
# Verificar qué archivos existen realmente
|
|
archivos_encontrados = []
|
|
archivos_faltantes = []
|
|
|
|
# Buscar archivos por bloque y nombre aproximado
|
|
for codigo, info_esperado in archivos_esperados.items():
|
|
bloque_num = info_esperado['bloque']
|
|
folder_name = info_esperado['folder']
|
|
bloque_dir = MANUAL_DIR / folder_name
|
|
|
|
encontrado = False
|
|
archivo_real = None
|
|
|
|
if bloque_dir.exists():
|
|
# Buscar archivos que coincidan con el patrón
|
|
nombre_buscar = info_esperado['nombre'].upper().replace(" ", "_")
|
|
for archivo in bloque_dir.glob("*.md"):
|
|
if nombre_buscar in archivo.name.upper() or f"BLOQUE_{bloque_num:02d}" in archivo.name:
|
|
encontrado = True
|
|
archivo_real = archivo.name
|
|
break
|
|
|
|
if encontrado:
|
|
archivos_encontrados.append({
|
|
'codigo': codigo,
|
|
'nombre': info_esperado['nombre'],
|
|
'archivo_real': archivo_real,
|
|
'bloque': bloque_num
|
|
})
|
|
else:
|
|
archivos_faltantes.append({
|
|
'codigo': codigo,
|
|
'nombre': info_esperado['nombre'],
|
|
'archivo_esperado': info_esperado['archivo_esperado'],
|
|
'bloque': bloque_num,
|
|
'folder': folder_name
|
|
})
|
|
|
|
# Generar reporte
|
|
reporte = []
|
|
reporte.append("# REPORTE 1: ARCHIVOS .MD FALTANTES DEL ÍNDICE\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 capítulos según índice:** {len(INDICE_ESPERADO)}\n")
|
|
reporte.append(f"- **Archivos encontrados:** {len(archivos_encontrados)}\n")
|
|
reporte.append(f"- **Archivos faltantes:** {len(archivos_faltantes)}\n")
|
|
reporte.append(f"- **Porcentaje de completitud:** {(len(archivos_encontrados)/len(INDICE_ESPERADO)*100):.1f}%\n")
|
|
reporte.append("---\n")
|
|
|
|
if archivos_faltantes:
|
|
reporte.append("## ❌ ARCHIVOS FALTANTES\n")
|
|
reporte.append(f"**Total:** {len(archivos_faltantes)} archivos\n\n")
|
|
|
|
por_parte = defaultdict(list)
|
|
for archivo in archivos_faltantes:
|
|
parte_num = archivo['codigo'].split('.')[0]
|
|
por_parte[parte_num].append(archivo)
|
|
|
|
for parte_num in sorted(por_parte.keys(), key=int):
|
|
reporte.append(f"\n### Parte {parte_num}\n")
|
|
for archivo in sorted(por_parte[parte_num], key=lambda x: x['codigo']):
|
|
reporte.append(f"- **{archivo['codigo']}** - {archivo['nombre']}")
|
|
reporte.append(f" - Bloque: {archivo['bloque']}")
|
|
reporte.append(f" - Carpeta esperada: `{archivo['folder']}/`")
|
|
reporte.append(f" - Archivo esperado: `{archivo['archivo_esperado']}`\n")
|
|
else:
|
|
reporte.append("## ✅ TODOS LOS ARCHIVOS ESTÁN PRESENTES\n")
|
|
reporte.append("Todos los 93 capítulos del índice tienen archivos .md correspondientes.\n")
|
|
|
|
reporte.append("\n---\n")
|
|
reporte.append("## 📋 LISTADO COMPLETO DE ARCHIVOS ENCONTRADOS\n")
|
|
reporte.append(f"**Total:** {len(archivos_encontrados)} archivos\n\n")
|
|
|
|
por_parte_encontrados = defaultdict(list)
|
|
for archivo in archivos_encontrados:
|
|
parte_num = archivo['codigo'].split('.')[0]
|
|
por_parte_encontrados[parte_num].append(archivo)
|
|
|
|
for parte_num in sorted(por_parte_encontrados.keys(), key=int):
|
|
reporte.append(f"\n### Parte {parte_num}\n")
|
|
for archivo in sorted(por_parte_encontrados[parte_num], key=lambda x: x['codigo']):
|
|
reporte.append(f"- ✅ `{archivo['codigo']}` - {archivo['nombre']}")
|
|
reporte.append(f" - Archivo: `{archivo['archivo_real']}`\n")
|
|
|
|
return "\n".join(reporte), len(archivos_faltantes)
|
|
|
|
def generar_reporte_2_multimedia_faltante():
|
|
"""Genera reporte 2: Medios/multimedia faltantes"""
|
|
archivos_md = obtener_archivos_md_existentes()
|
|
todas_referencias = []
|
|
medios_faltantes = []
|
|
|
|
for nombre_archivo, ruta_archivo in archivos_md.items():
|
|
referencias = buscar_referencias_multimedia(ruta_archivo)
|
|
todas_referencias.extend(referencias)
|
|
|
|
for ref in referencias:
|
|
if ref['tipo'] in ['imagen', 'multimedia']:
|
|
if not verificar_existencia_medio(ref['ruta'], ruta_archivo):
|
|
medios_faltantes.append(ref)
|
|
|
|
# Generar reporte
|
|
reporte = []
|
|
reporte.append("# REPORTE 2: MEDIOS/MULTIMEDIA FALTANTES\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 referencias a medios encontradas:** {len(todas_referencias)}\n")
|
|
reporte.append(f"- **Medios faltantes:** {len(medios_faltantes)}\n")
|
|
reporte.append(f"- **Medios existentes:** {len(todas_referencias) - len(medios_faltantes)}\n")
|
|
reporte.append("---\n")
|
|
|
|
if medios_faltantes:
|
|
reporte.append("## ❌ MEDIOS FALTANTES\n")
|
|
reporte.append(f"**Total:** {len(medios_faltantes)} referencias a medios que no existen\n\n")
|
|
|
|
por_tipo = defaultdict(list)
|
|
for medio in medios_faltantes:
|
|
por_tipo[medio['tipo']].append(medio)
|
|
|
|
for tipo in sorted(por_tipo.keys()):
|
|
reporte.append(f"\n### {tipo.upper()}\n")
|
|
for medio in por_tipo[tipo]:
|
|
reporte.append(f"- **Archivo:** `{medio['archivo']}`")
|
|
reporte.append(f" - **Línea:** {medio['linea']}")
|
|
reporte.append(f" - **Ruta referenciada:** `{medio['ruta']}`")
|
|
if 'texto_alt' in medio:
|
|
reporte.append(f" - **Texto alternativo:** {medio['texto_alt']}")
|
|
reporte.append(f" - **Ubicación:** `{medio['ruta_completa']}`\n")
|
|
else:
|
|
reporte.append("## ✅ NO SE ENCONTRARON REFERENCIAS A MEDIOS FALTANTES\n")
|
|
reporte.append("No se encontraron referencias a imágenes, videos u otros medios multimedia en los archivos.\n")
|
|
|
|
reporte.append("\n---\n")
|
|
reporte.append("## 📋 TODAS LAS REFERENCIAS A MEDIOS ENCONTRADAS\n")
|
|
reporte.append(f"**Total:** {len(todas_referencias)} referencias\n\n")
|
|
|
|
if todas_referencias:
|
|
por_archivo = defaultdict(list)
|
|
for ref in todas_referencias:
|
|
por_archivo[ref['archivo']].append(ref)
|
|
|
|
for archivo in sorted(por_archivo.keys()):
|
|
reporte.append(f"\n### `{archivo}`\n")
|
|
for ref in por_archivo[archivo]:
|
|
reporte.append(f"- **Línea {ref['linea']}:** {ref['tipo']}")
|
|
if 'ruta' in ref:
|
|
reporte.append(f" - Ruta: `{ref['ruta']}`")
|
|
reporte.append("")
|
|
else:
|
|
reporte.append("No se encontraron referencias a medios multimedia en ningún archivo.\n")
|
|
|
|
return "\n".join(reporte), len(medios_faltantes)
|
|
|
|
def generar_reporte_3_recomendaciones_app():
|
|
"""Genera reporte 3: Recomendaciones para estructura de app"""
|
|
reporte = []
|
|
reporte.append("# REPORTE 3: RECOMENDACIONES PARA ESTRUCTURA DE APP\n")
|
|
reporte.append(f"**Fecha:** {__import__('datetime').datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
reporte.append("---\n")
|
|
|
|
reporte.append("## 📱 ESTRUCTURA DE CARPETAS IDEAL PARA APP\n")
|
|
reporte.append("\n### Propuesta de Estructura:\n")
|
|
reporte.append("```\n")
|
|
reporte.append("app-manual-tes/\n")
|
|
reporte.append("├── contenido/\n")
|
|
reporte.append("│ ├── parte-i-fundamentos/\n")
|
|
reporte.append("│ │ ├── bloque-0-fundamentos/\n")
|
|
reporte.append("│ │ │ ├── 1.1.1-fundamentos-emergencias.md\n")
|
|
reporte.append("│ │ │ └── metadata.json\n")
|
|
reporte.append("│ │ └── bloque-1-procedimientos-basicos/\n")
|
|
reporte.append("│ │ ├── 1.2.1-constantes-vitales.md\n")
|
|
reporte.append("│ │ ├── 1.2.2-abcde-operativo.md\n")
|
|
reporte.append("│ │ ├── 1.2.3-glasgow-operativo.md\n")
|
|
reporte.append("│ │ ├── 1.2.4-triage-start.md\n")
|
|
reporte.append("│ │ └── metadata.json\n")
|
|
reporte.append("│ ├── parte-ii-soporte-vital/\n")
|
|
reporte.append("│ │ ├── bloque-4-rcp/\n")
|
|
reporte.append("│ │ └── bloque-9-medicina-emergencias/\n")
|
|
reporte.append("│ ├── parte-iii-material/\n")
|
|
reporte.append("│ │ ├── bloque-2-inmovilizacion/\n")
|
|
reporte.append("│ │ └── bloque-3-oxigenoterapia/\n")
|
|
reporte.append("│ ├── parte-iv-farmacologia/\n")
|
|
reporte.append("│ │ └── bloque-6-farmacologia/\n")
|
|
reporte.append("│ ├── parte-v-protocolos/\n")
|
|
reporte.append("│ │ ├── bloque-5-transtelefonicos/\n")
|
|
reporte.append("│ │ └── bloque-8-gestion/\n")
|
|
reporte.append("│ ├── parte-vi-conduccion/\n")
|
|
reporte.append("│ │ └── bloque-7-conduccion/\n")
|
|
reporte.append("│ ├── parte-vii-situaciones-especiales/\n")
|
|
reporte.append("│ │ ├── bloque-10-situaciones-especiales/\n")
|
|
reporte.append("│ │ └── bloque-11-trauma/\n")
|
|
reporte.append("│ └── parte-viii-habilidades/\n")
|
|
reporte.append("│ ├── bloque-12-marco-legal/\n")
|
|
reporte.append("│ ├── bloque-13-comunicacion/\n")
|
|
reporte.append("│ └── bloque-14-seguridad/\n")
|
|
reporte.append("├── assets/\n")
|
|
reporte.append("│ ├── imagenes/\n")
|
|
reporte.append("│ ├── videos/\n")
|
|
reporte.append("│ └── iconos/\n")
|
|
reporte.append("├── data/\n")
|
|
reporte.append("│ ├── indice.json\n")
|
|
reporte.append("│ └── metadata-global.json\n")
|
|
reporte.append("└── public/\n")
|
|
reporte.append(" └── (archivos estáticos)\n")
|
|
reporte.append("```\n")
|
|
reporte.append("\n---\n")
|
|
|
|
reporte.append("## 📋 METADATOS NECESARIOS EN CADA .MD\n")
|
|
reporte.append("\n### Front Matter Propuesto (YAML):\n")
|
|
reporte.append("```yaml\n")
|
|
reporte.append("---\n")
|
|
reporte.append("id: '1.1.1'\n")
|
|
reporte.append("titulo: 'Fundamentos de Emergencias'\n")
|
|
reporte.append("subtitulo: 'Marco conceptual y operativo'\n")
|
|
reporte.append("parte: 1\n")
|
|
reporte.append("bloque: 0\n")
|
|
reporte.append("bloque_nombre: 'Fundamentos de Emergencias Prehospitalarias'\n")
|
|
reporte.append("parte_nombre: 'Fundamentos y Evaluación Inicial'\n")
|
|
reporte.append("nivel_dificultad: 'basico'\n")
|
|
reporte.append("importancia: 'alta'\n")
|
|
reporte.append("palabras_clave:\n")
|
|
reporte.append(" - 'emergencias'\n")
|
|
reporte.append(" - 'soporte vital'\n")
|
|
reporte.append(" - 'cadena supervivencia'\n")
|
|
reporte.append("tipo_contenido: 'formativo'\n")
|
|
reporte.append("tiempo_lectura: 15\n")
|
|
reporte.append("version: '1.0'\n")
|
|
reporte.append("fecha_actualizacion: '2024-12-13'\n")
|
|
reporte.append("autor: 'Manual TES Digital'\n")
|
|
reporte.append("navegacion:\n")
|
|
reporte.append(" anterior: null\n")
|
|
reporte.append(" siguiente: '1.2.1'\n")
|
|
reporte.append(" relacionados:\n")
|
|
reporte.append(" - '1.2.1'\n")
|
|
reporte.append(" - '2.1.1'\n")
|
|
reporte.append("---\n")
|
|
reporte.append("```\n")
|
|
reporte.append("\n### Campos Explicados:\n")
|
|
reporte.append("- **id**: Identificador único del capítulo (formato X.Y.Z)\n")
|
|
reporte.append("- **titulo**: Título principal del capítulo\n")
|
|
reporte.append("- **subtitulo**: Descripción breve\n")
|
|
reporte.append("- **parte/bloque**: Números para organización jerárquica\n")
|
|
reporte.append("- **nivel_dificultad**: 'basico', 'intermedio', 'avanzado'\n")
|
|
reporte.append("- **importancia**: 'alta', 'media', 'baja'\n")
|
|
reporte.append("- **palabras_clave**: Array de términos para búsqueda\n")
|
|
reporte.append("- **tipo_contenido**: 'formativo', 'operativo', 'referencia'\n")
|
|
reporte.append("- **tiempo_lectura**: Minutos estimados\n")
|
|
reporte.append("- **navegacion**: Enlaces a capítulos relacionados\n")
|
|
reporte.append("\n---\n")
|
|
|
|
reporte.append("## 🧭 SISTEMA DE NAVEGACIÓN RECOMENDADO\n")
|
|
reporte.append("\n### 1. Menú Lateral Jerárquico\n")
|
|
reporte.append("```\n")
|
|
reporte.append("📚 Manual TES Digital\n")
|
|
reporte.append("├── 📖 Parte I: Fundamentos\n")
|
|
reporte.append("│ ├── 🔹 Bloque 0: Fundamentos\n")
|
|
reporte.append("│ │ └── 1.1.1 Fundamentos de Emergencias\n")
|
|
reporte.append("│ └── 🔹 Bloque 1: Procedimientos Básicos\n")
|
|
reporte.append("│ ├── 1.2.1 Constantes Vitales\n")
|
|
reporte.append("│ ├── 1.2.2 ABCDE Operativo\n")
|
|
reporte.append("│ ├── 1.2.3 Glasgow Operativo\n")
|
|
reporte.append("│ └── 1.2.4 Triage START\n")
|
|
reporte.append("├── 💉 Parte II: Soporte Vital\n")
|
|
reporte.append("│ ├── 🔹 Bloque 4: RCP\n")
|
|
reporte.append("│ └── 🔹 Bloque 9: Medicina Emergencias\n")
|
|
reporte.append("└── ... (resto de partes)\n")
|
|
reporte.append("```\n")
|
|
reporte.append("\n**Características:**\n")
|
|
reporte.append("- Expandible/colapsable por niveles\n")
|
|
reporte.append("- Indicador visual del capítulo actual\n")
|
|
reporte.append("- Búsqueda rápida integrada\n")
|
|
reporte.append("- Favoritos/marcadores\n")
|
|
reporte.append("\n### 2. Breadcrumbs (Migas de Pan)\n")
|
|
reporte.append("```\n")
|
|
reporte.append("Inicio > Parte I > Bloque 1 > 1.2.1 Constantes Vitales\n")
|
|
reporte.append("```\n")
|
|
reporte.append("\n**Funcionalidad:**\n")
|
|
reporte.append("- Navegación rápida a niveles superiores\n")
|
|
reporte.append("- Contexto visual de ubicación\n")
|
|
reporte.append("- Click en cualquier nivel para ir directamente\n")
|
|
reporte.append("\n### 3. Navegación Anterior/Siguiente\n")
|
|
reporte.append("```\n")
|
|
reporte.append("← Capítulo Anterior: 1.1.1 Fundamentos\n")
|
|
reporte.append("Capítulo Siguiente: 1.2.2 ABCDE Operativo →\n")
|
|
reporte.append("```\n")
|
|
reporte.append("\n**Funcionalidad:**\n")
|
|
reporte.append("- Botones flotantes o en footer\n")
|
|
reporte.append("- Navegación secuencial lógica\n")
|
|
reporte.append("- Atajos de teclado (← →)\n")
|
|
reporte.append("\n### 4. Índice Rápido\n")
|
|
reporte.append("**Características:**\n")
|
|
reporte.append("- Modal o sidebar deslizable\n")
|
|
reporte.append("- Búsqueda instantánea\n")
|
|
reporte.append("- Filtros por parte/bloque\n")
|
|
reporte.append("- Acceso desde cualquier página\n")
|
|
reporte.append("\n---\n")
|
|
|
|
reporte.append("## 🔍 FUNCIONALIDADES ADICIONALES RECOMENDADAS\n")
|
|
reporte.append("\n### Búsqueda Avanzada\n")
|
|
reporte.append("- Búsqueda por texto completo\n")
|
|
reporte.append("- Filtros por parte, bloque, tipo de contenido\n")
|
|
reporte.append("- Búsqueda por palabras clave\n")
|
|
reporte.append("- Historial de búsquedas\n")
|
|
reporte.append("\n### Modo Offline\n")
|
|
reporte.append("- Cache de contenido para acceso offline\n")
|
|
reporte.append("- Service Worker para PWA\n")
|
|
reporte.append("- Sincronización cuando hay conexión\n")
|
|
reporte.append("\n### Personalización\n")
|
|
reporte.append("- Modo oscuro/claro\n")
|
|
reporte.append("- Tamaño de fuente ajustable\n")
|
|
reporte.append("- Favoritos personalizados\n")
|
|
reporte.append("- Notas/annotaciones por capítulo\n")
|
|
reporte.append("\n### Tests/Autoevaluaciones\n")
|
|
reporte.append("- Preguntas por capítulo\n")
|
|
reporte.append("- Tests por bloque/parte\n")
|
|
reporte.append("- Historial de resultados\n")
|
|
reporte.append("- Modo estudio vs modo examen\n")
|
|
reporte.append("\n---\n")
|
|
|
|
reporte.append("## 📐 ESTRUCTURA DE DATOS JSON RECOMENDADA\n")
|
|
reporte.append("\n### indice.json\n")
|
|
reporte.append("```json\n")
|
|
reporte.append("{\n")
|
|
reporte.append(" \"version\": \"1.0\",\n")
|
|
reporte.append(" \"fecha\": \"2024-12-15\",\n")
|
|
reporte.append(" \"partes\": [\n")
|
|
reporte.append(" {\n")
|
|
reporte.append(" \"id\": 1,\n")
|
|
reporte.append(" \"nombre\": \"Fundamentos y Evaluación Inicial\",\n")
|
|
reporte.append(" \"bloques\": [\n")
|
|
reporte.append(" {\n")
|
|
reporte.append(" \"id\": 0,\n")
|
|
reporte.append(" \"nombre\": \"Fundamentos de Emergencias\",\n")
|
|
reporte.append(" \"capitulos\": [\n")
|
|
reporte.append(" {\n")
|
|
reporte.append(" \"id\": \"1.1.1\",\n")
|
|
reporte.append(" \"titulo\": \"Fundamentos de Emergencias\",\n")
|
|
reporte.append(" \"ruta\": \"parte-i-fundamentos/bloque-0-fundamentos/1.1.1-fundamentos-emergencias.md\"\n")
|
|
reporte.append(" }\n")
|
|
reporte.append(" ]\n")
|
|
reporte.append(" }\n")
|
|
reporte.append(" ]\n")
|
|
reporte.append(" }\n")
|
|
reporte.append(" ]\n")
|
|
reporte.append("}\n")
|
|
reporte.append("```\n")
|
|
reporte.append("\n---\n")
|
|
|
|
reporte.append("## ✅ CHECKLIST DE IMPLEMENTACIÓN\n")
|
|
reporte.append("\n### Fase 1: Estructura Base\n")
|
|
reporte.append("- [ ] Crear estructura de carpetas propuesta\n")
|
|
reporte.append("- [ ] Migrar archivos .md a nueva estructura\n")
|
|
reporte.append("- [ ] Agregar front matter a todos los archivos\n")
|
|
reporte.append("- [ ] Generar indice.json desde estructura\n")
|
|
reporte.append("\n### Fase 2: Navegación\n")
|
|
reporte.append("- [ ] Implementar menú lateral jerárquico\n")
|
|
reporte.append("- [ ] Agregar breadcrumbs\n")
|
|
reporte.append("- [ ] Implementar navegación anterior/siguiente\n")
|
|
reporte.append("- [ ] Crear índice rápido con búsqueda\n")
|
|
reporte.append("\n### Fase 3: Funcionalidades\n")
|
|
reporte.append("- [ ] Implementar búsqueda avanzada\n")
|
|
reporte.append("- [ ] Agregar modo offline (PWA)\n")
|
|
reporte.append("- [ ] Implementar personalización\n")
|
|
reporte.append("- [ ] Crear sistema de tests/autoevaluaciones\n")
|
|
reporte.append("\n---\n")
|
|
|
|
return "\n".join(reporte)
|
|
|
|
if __name__ == "__main__":
|
|
print("Generando reportes para conversión a app digital...")
|
|
|
|
# Reporte 1
|
|
print("\n1. Generando reporte de archivos faltantes...")
|
|
reporte1, num_faltantes = generar_reporte_1_archivos_faltantes()
|
|
with open(BASE_DIR / "REPORTE_1_ARCHIVOS_FALTANTES.md", "w", encoding="utf-8") as f:
|
|
f.write(reporte1)
|
|
print(f" ✅ Reporte 1 generado: {num_faltantes} archivos faltantes")
|
|
|
|
# Reporte 2
|
|
print("\n2. Generando reporte de medios faltantes...")
|
|
reporte2, num_medios_faltantes = generar_reporte_2_multimedia_faltante()
|
|
with open(BASE_DIR / "REPORTE_2_MEDIOS_FALTANTES.md", "w", encoding="utf-8") as f:
|
|
f.write(reporte2)
|
|
print(f" ✅ Reporte 2 generado: {num_medios_faltantes} medios faltantes")
|
|
|
|
# Reporte 3
|
|
print("\n3. Generando reporte de recomendaciones...")
|
|
reporte3 = generar_reporte_3_recomendaciones_app()
|
|
with open(BASE_DIR / "REPORTE_3_RECOMENDACIONES_APP.md", "w", encoding="utf-8") as f:
|
|
f.write(reporte3)
|
|
print(f" ✅ Reporte 3 generado")
|
|
|
|
print("\n✅ Todos los reportes generados exitosamente!")
|