Implementar nueva lógica de estados de sincronización basada en ubicación de archivos
- Agregar nuevos estados: Recibido y Procesando
- Implementar función determine_sync_status() que verifica:
* D:\sftp\{nodo}.zip -> estado 'Recibido'
* D:\BackupSFTP\{nodo}.zip -> estado 'Procesando'
* Comparación de fechas para estado 'Actualizada'
- Actualizar get_node_status() y get_all_nodes() para usar nueva lógica
- Agregar script de pruebas test_sync_status.py
This commit is contained in:
@@ -9,6 +9,8 @@ class EstadoSincronizacion(str, Enum):
|
|||||||
ACTUALIZADA = "Actualizada"
|
ACTUALIZADA = "Actualizada"
|
||||||
ATENCION = "Atención"
|
ATENCION = "Atención"
|
||||||
ERROR = "Error"
|
ERROR = "Error"
|
||||||
|
RECIBIDO = "Recibido"
|
||||||
|
PROCESANDO = "Procesando"
|
||||||
|
|
||||||
|
|
||||||
class NodeStatus(BaseModel):
|
class NodeStatus(BaseModel):
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from fastapi import APIRouter, HTTPException, Path, Query
|
from fastapi import APIRouter, HTTPException, Path, Query
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from database.connection import db_connection
|
from database.connection import db_connection
|
||||||
from models.sync_models import (
|
from models.sync_models import (
|
||||||
@@ -42,6 +43,64 @@ def calculate_time_since_sync(last_sync: Optional[datetime]) -> Optional[str]:
|
|||||||
return f"{days} días"
|
return f"{days} días"
|
||||||
|
|
||||||
|
|
||||||
|
def determine_sync_status(node_name: str, last_restore_date: Optional[datetime] = None) -> str:
|
||||||
|
"""
|
||||||
|
Determina el estado de sincronización basado en la ubicación de los archivos ZIP del nodo.
|
||||||
|
|
||||||
|
Lógica:
|
||||||
|
- Si existe en D:\sftp -> "Recibido"
|
||||||
|
- Si existe en D:\BackupSFTP -> "Procesando"
|
||||||
|
- Si la fecha de última sincronización es mayor a la del archivo en D:\BackupSFTP -> "Actualizada"
|
||||||
|
- En otros casos sigue la lógica anterior basada en fechas
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
sftp_path = f"D:\\sftp\\{node_name}.zip"
|
||||||
|
backup_sftp_path = f"D:\\BackupSFTP\\{node_name}.zip"
|
||||||
|
|
||||||
|
# Verificar si existe en D:\sftp (prioridad más alta)
|
||||||
|
if os.path.exists(sftp_path):
|
||||||
|
return EstadoSincronizacion.RECIBIDO.value
|
||||||
|
|
||||||
|
# Verificar si existe en D:\BackupSFTP
|
||||||
|
if os.path.exists(backup_sftp_path):
|
||||||
|
# Si tenemos fecha de última restauración, comparar
|
||||||
|
if last_restore_date:
|
||||||
|
try:
|
||||||
|
# Obtener fecha de modificación del archivo
|
||||||
|
file_mod_time = datetime.fromtimestamp(os.path.getmtime(backup_sftp_path))
|
||||||
|
|
||||||
|
# Si la última sincronización es más reciente que el archivo
|
||||||
|
if last_restore_date > file_mod_time:
|
||||||
|
return EstadoSincronizacion.ACTUALIZADA.value
|
||||||
|
except OSError:
|
||||||
|
# Si hay error leyendo el archivo, asumir procesando
|
||||||
|
pass
|
||||||
|
|
||||||
|
return EstadoSincronizacion.PROCESANDO.value
|
||||||
|
|
||||||
|
# Si no existe en ninguna ruta SFTP, usar lógica basada en fechas de restauración
|
||||||
|
if last_restore_date is None:
|
||||||
|
return EstadoSincronizacion.ERROR.value
|
||||||
|
elif last_restore_date < datetime.now() - timedelta(days=7):
|
||||||
|
return EstadoSincronizacion.ERROR.value
|
||||||
|
elif last_restore_date < datetime.now() - timedelta(hours=24):
|
||||||
|
return EstadoSincronizacion.ATENCION.value
|
||||||
|
else:
|
||||||
|
return EstadoSincronizacion.ACTUALIZADA.value
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error determinando estado de sincronización para {node_name}: {str(e)}")
|
||||||
|
# En caso de error, usar lógica de fechas como fallback
|
||||||
|
if last_restore_date is None:
|
||||||
|
return EstadoSincronizacion.ERROR.value
|
||||||
|
elif last_restore_date < datetime.now() - timedelta(days=7):
|
||||||
|
return EstadoSincronizacion.ERROR.value
|
||||||
|
elif last_restore_date < datetime.now() - timedelta(hours=24):
|
||||||
|
return EstadoSincronizacion.ATENCION.value
|
||||||
|
else:
|
||||||
|
return EstadoSincronizacion.ACTUALIZADA.value
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{node_name}/status",
|
@router.get("/{node_name}/status",
|
||||||
response_model=NodeStatus,
|
response_model=NodeStatus,
|
||||||
summary="Obtener estado específico de un nodo",
|
summary="Obtener estado específico de un nodo",
|
||||||
@@ -108,15 +167,8 @@ async def get_node_status(
|
|||||||
last_restore_date = restore_info.get('last_restore_date')
|
last_restore_date = restore_info.get('last_restore_date')
|
||||||
size_mb = restore_info.get('size_mb', 0)
|
size_mb = restore_info.get('size_mb', 0)
|
||||||
|
|
||||||
# Determinar estado basado en fecha de restauración
|
# Determinar estado basado en ubicación de archivos y fecha de restauración
|
||||||
if last_restore_date is None:
|
estado = determine_sync_status(node_name, last_restore_date)
|
||||||
estado = 'Error'
|
|
||||||
elif last_restore_date < datetime.now() - timedelta(days=7):
|
|
||||||
estado = 'Error'
|
|
||||||
elif last_restore_date < datetime.now() - timedelta(hours=24):
|
|
||||||
estado = 'Atención'
|
|
||||||
else:
|
|
||||||
estado = 'Actualizada'
|
|
||||||
|
|
||||||
# Calcular tiempo desde última sincronización
|
# Calcular tiempo desde última sincronización
|
||||||
time_since_sync = calculate_time_since_sync(last_restore_date)
|
time_since_sync = calculate_time_since_sync(last_restore_date)
|
||||||
@@ -207,15 +259,8 @@ async def get_all_nodes(
|
|||||||
# Buscar por nombre de BD o nodo
|
# Buscar por nombre de BD o nodo
|
||||||
last_restore = restore_map.get(bd_name) or restore_map.get(nodo_name)
|
last_restore = restore_map.get(bd_name) or restore_map.get(nodo_name)
|
||||||
|
|
||||||
# Determinar estado basado en fecha de restauración
|
# Determinar estado basado en ubicación de archivos y fecha de restauración
|
||||||
if last_restore is None:
|
estado_sync = determine_sync_status(nodo_name, last_restore)
|
||||||
estado_sync = 'Error'
|
|
||||||
elif last_restore < datetime.now() - timedelta(days=7):
|
|
||||||
estado_sync = 'Error'
|
|
||||||
elif last_restore < datetime.now() - timedelta(hours=24):
|
|
||||||
estado_sync = 'Atención'
|
|
||||||
else:
|
|
||||||
estado_sync = 'Actualizada'
|
|
||||||
|
|
||||||
# Aplicar filtros
|
# Aplicar filtros
|
||||||
if estado and estado_sync != estado.value:
|
if estado and estado_sync != estado.value:
|
||||||
|
|||||||
126
test_sync_status.py
Normal file
126
test_sync_status.py
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Script de prueba para validar la nueva lógica de estados de sincronización
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Agregar el directorio padre al path para importar los módulos
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent / 'sync_api'))
|
||||||
|
|
||||||
|
from routers.nodes import determine_sync_status
|
||||||
|
from models.sync_models import EstadoSincronizacion
|
||||||
|
|
||||||
|
|
||||||
|
def create_test_files():
|
||||||
|
"""Crear archivos de prueba para validar la lógica"""
|
||||||
|
|
||||||
|
# Crear directorios de prueba si no existen
|
||||||
|
os.makedirs("D:\\sftp", exist_ok=True)
|
||||||
|
os.makedirs("D:\\BackupSFTP", exist_ok=True)
|
||||||
|
|
||||||
|
# Crear archivo de prueba en D:\sftp
|
||||||
|
with open("D:\\sftp\\NODO_TEST1.zip", "w") as f:
|
||||||
|
f.write("archivo de prueba")
|
||||||
|
|
||||||
|
# Crear archivo de prueba en D:\BackupSFTP
|
||||||
|
with open("D:\\BackupSFTP\\NODO_TEST2.zip", "w") as f:
|
||||||
|
f.write("archivo de prueba")
|
||||||
|
|
||||||
|
print("Archivos de prueba creados:")
|
||||||
|
print("- D:\\sftp\\NODO_TEST1.zip")
|
||||||
|
print("- D:\\BackupSFTP\\NODO_TEST2.zip")
|
||||||
|
|
||||||
|
|
||||||
|
def test_sync_status_logic():
|
||||||
|
"""Probar la lógica de determinación de estados"""
|
||||||
|
|
||||||
|
print("\n=== PRUEBAS DE LÓGICA DE ESTADOS ===\n")
|
||||||
|
|
||||||
|
# Prueba 1: Nodo con archivo en D:\sftp (debe ser "Recibido")
|
||||||
|
print("1. Prueba: Archivo en D:\\sftp")
|
||||||
|
status = determine_sync_status("NODO_TEST1")
|
||||||
|
print(f" Estado: {status}")
|
||||||
|
print(f" Esperado: {EstadoSincronizacion.RECIBIDO.value}")
|
||||||
|
print(f" ✓ Correcto" if status == EstadoSincronizacion.RECIBIDO.value else " ✗ Incorrecto")
|
||||||
|
|
||||||
|
# Prueba 2: Nodo con archivo en D:\BackupSFTP (debe ser "Procesando")
|
||||||
|
print("\n2. Prueba: Archivo en D:\\BackupSFTP")
|
||||||
|
status = determine_sync_status("NODO_TEST2")
|
||||||
|
print(f" Estado: {status}")
|
||||||
|
print(f" Esperado: {EstadoSincronizacion.PROCESANDO.value}")
|
||||||
|
print(f" ✓ Correcto" if status == EstadoSincronizacion.PROCESANDO.value else " ✗ Incorrecto")
|
||||||
|
|
||||||
|
# Prueba 3: Nodo sin archivo, con fecha reciente (debe ser "Actualizada")
|
||||||
|
print("\n3. Prueba: Sin archivo, fecha reciente")
|
||||||
|
recent_date = datetime.now() - timedelta(hours=1)
|
||||||
|
status = determine_sync_status("NODO_TEST3", recent_date)
|
||||||
|
print(f" Estado: {status}")
|
||||||
|
print(f" Esperado: {EstadoSincronizacion.ACTUALIZADA.value}")
|
||||||
|
print(f" ✓ Correcto" if status == EstadoSincronizacion.ACTUALIZADA.value else " ✗ Incorrecto")
|
||||||
|
|
||||||
|
# Prueba 4: Nodo sin archivo, fecha antigua (debe ser "Error")
|
||||||
|
print("\n4. Prueba: Sin archivo, fecha antigua")
|
||||||
|
old_date = datetime.now() - timedelta(days=10)
|
||||||
|
status = determine_sync_status("NODO_TEST4", old_date)
|
||||||
|
print(f" Estado: {status}")
|
||||||
|
print(f" Esperado: {EstadoSincronizacion.ERROR.value}")
|
||||||
|
print(f" ✓ Correcto" if status == EstadoSincronizacion.ERROR.value else " ✗ Incorrecto")
|
||||||
|
|
||||||
|
# Prueba 5: Nodo sin archivo, sin fecha (debe ser "Error")
|
||||||
|
print("\n5. Prueba: Sin archivo, sin fecha")
|
||||||
|
status = determine_sync_status("NODO_TEST5", None)
|
||||||
|
print(f" Estado: {status}")
|
||||||
|
print(f" Esperado: {EstadoSincronizacion.ERROR.value}")
|
||||||
|
print(f" ✓ Correcto" if status == EstadoSincronizacion.ERROR.value else " ✗ Incorrecto")
|
||||||
|
|
||||||
|
# Prueba 6: Archivo en BackupSFTP con fecha más reciente que el archivo (debe ser "Actualizada")
|
||||||
|
print("\n6. Prueba: Archivo en BackupSFTP, fecha más reciente")
|
||||||
|
future_date = datetime.now() + timedelta(hours=1) # Fecha futura para simular más reciente
|
||||||
|
status = determine_sync_status("NODO_TEST2", future_date)
|
||||||
|
print(f" Estado: {status}")
|
||||||
|
print(f" Esperado: {EstadoSincronizacion.ACTUALIZADA.value}")
|
||||||
|
print(f" ✓ Correcto" if status == EstadoSincronizacion.ACTUALIZADA.value else " ✗ Incorrecto")
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup_test_files():
|
||||||
|
"""Limpiar archivos de prueba"""
|
||||||
|
test_files = [
|
||||||
|
"D:\\sftp\\NODO_TEST1.zip",
|
||||||
|
"D:\\BackupSFTP\\NODO_TEST2.zip"
|
||||||
|
]
|
||||||
|
|
||||||
|
for file_path in test_files:
|
||||||
|
try:
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
os.remove(file_path)
|
||||||
|
print(f"Archivo eliminado: {file_path}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error eliminando {file_path}: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("=== SCRIPT DE PRUEBA PARA ESTADOS DE SINCRONIZACIÓN ===")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Solo crear archivos de prueba si los directorios existen
|
||||||
|
if os.path.exists("D:\\") and os.access("D:\\", os.W_OK):
|
||||||
|
create_test_files()
|
||||||
|
test_sync_status_logic()
|
||||||
|
|
||||||
|
# Preguntar si limpiar archivos de prueba
|
||||||
|
response = input("\n¿Deseas eliminar los archivos de prueba? (s/n): ")
|
||||||
|
if response.lower() in ['s', 'si', 'yes', 'y']:
|
||||||
|
cleanup_test_files()
|
||||||
|
else:
|
||||||
|
print("No se puede acceder al disco D: o no hay permisos de escritura")
|
||||||
|
print("Probando solo la lógica sin archivos reales...")
|
||||||
|
test_sync_status_logic()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error durante las pruebas: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
Reference in New Issue
Block a user