Files
service_manager/backend/scripts/generate_sla_test_data.py
icamarillo ceea67eb2b feat: Version 1.11.0 - Mejoras en auditoría, SLA, frontend y correcciones de sincronización
- Refactorización de endpoints de auditoría y helpers
- Mejoras en esquemas de auditoría (audit.py)
- Correcciones en endpoint SLA
- Actualizaciones en múltiples rutas del frontend interno:
  layout, tickets, usuarios, tenants, categorías, sistemas,
  SLA (at-risk, violations), auditoría (main + security), login, perfil
- Actualización de tailwind.config.js
- Eliminación de docs de versiones anteriores (CAMBIOS_v1.10.0, v1.8.0, OPTIMIZACIONES)
- Nuevos scripts de prueba: generate_security_test_data.py, generate_sla_test_data.py
- Script de prueba de sincronización crítica (test_critical_sync.ps1)
- README actualizado en scripts/
2026-02-20 10:53:53 -07:00

400 lines
16 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Script para generar datos de prueba de SLA Management.
Crea tickets con diferentes estados de SLA para probar el dashboard.
"""
import asyncio
import sys
from pathlib import Path
from datetime import datetime, timedelta, timezone
import uuid
import random
# Agregar el directorio raíz al path
sys.path.insert(0, str(Path(__file__).parent.parent))
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.core.database import AsyncSessionLocal
from app.models.ticket import Ticket, TicketStatus, TicketPriority
from app.models.category import Category
from app.models.user import User
from app.models.tenant import Tenant
from app.models.system import System
async def generate_sla_test_data():
"""Genera tickets de prueba con diferentes estados de SLA."""
async with AsyncSessionLocal() as db:
# Obtener tenant y usuarios
tenant_result = await db.execute(select(Tenant).limit(1))
tenant = tenant_result.scalar_one_or_none()
if not tenant:
print("❌ No se encontró ningún tenant. Ejecuta las migraciones primero.")
return
# Obtener usuarios
users_result = await db.execute(
select(User).where(User.tenant_id == tenant.id).limit(5)
)
users = list(users_result.scalars().all())
if not users:
print("❌ No se encontraron usuarios. Crea usuarios primero.")
return
creator = users[0]
agents = users if len(users) > 1 else [creator]
# Obtener o crear categorías
categories_result = await db.execute(
select(Category).where(Category.tenant_id == tenant.id)
)
categories = list(categories_result.scalars().all())
if not categories:
print("📁 Creando categorías de prueba...")
category_data = [
{"name": "Soporte Técnico", "sla_response_hours": 2, "sla_resolution_hours": 24, "color": "#3B82F6"},
{"name": "Facturación", "sla_response_hours": 4, "sla_resolution_hours": 48, "color": "#10B981"},
{"name": "Incidente Crítico", "sla_response_hours": 1, "sla_resolution_hours": 8, "color": "#EF4444"},
{"name": "Consulta General", "sla_response_hours": 8, "sla_resolution_hours": 72, "color": "#6B7280"},
]
for cat_data in category_data:
category = Category(
id=uuid.uuid4(),
tenant_id=tenant.id,
name=cat_data["name"],
description=f"Categoría de {cat_data['name']}",
color=cat_data["color"],
sla_response_hours=cat_data["sla_response_hours"],
sla_resolution_hours=cat_data["sla_resolution_hours"],
is_active=True
)
db.add(category)
categories.append(category)
await db.commit()
print(f" ✓ Creadas {len(categories)} categorías")
# Obtener o crear sistemas afectados
systems_result = await db.execute(
select(System).where(System.tenant_id == tenant.id)
)
systems = list(systems_result.scalars().all())
if not systems:
print("🖥️ Creando sistemas de prueba...")
system_names = ["Portal Web", "API REST", "Base de Datos", "Sistema de Pagos"]
for sys_name in system_names:
system = System(
id=uuid.uuid4(),
tenant_id=tenant.id,
name=sys_name,
description=f"Sistema {sys_name}",
is_active=True
)
db.add(system)
systems.append(system)
await db.commit()
print(f" ✓ Creados {len(systems)} sistemas")
print(f"✅ Usando tenant: {tenant.name}")
print(f"✅ Usuarios disponibles: {len(users)}")
print(f"✅ Categorías disponibles: {len(categories)}")
print()
now = datetime.now(timezone.utc)
tickets_created = 0
# Función auxiliar para crear ticket
def create_ticket(
subject: str,
description: str,
priority: TicketPriority,
status: TicketStatus,
category: Category,
created_hours_ago: int,
first_response_hours_after: int = None,
resolved_hours_after: int = None,
assigned: bool = True
):
nonlocal tickets_created
ticket_id = uuid.uuid4()
created_at = now - timedelta(hours=created_hours_ago)
# Calcular SLA deadlines basados en la categoría (sin timezone para la BD)
sla_response_due = (created_at + timedelta(hours=category.sla_response_hours)).replace(tzinfo=None)
sla_resolution_due = (created_at + timedelta(hours=category.sla_resolution_hours)).replace(tzinfo=None)
# Primera respuesta (si aplica)
first_response_at = None
if first_response_hours_after is not None:
first_response_at = (created_at + timedelta(hours=first_response_hours_after)).replace(tzinfo=None)
# Resolución (si aplica)
resolved_at = None
if resolved_hours_after is not None:
resolved_at = (created_at + timedelta(hours=resolved_hours_after)).replace(tzinfo=None)
ticket = Ticket(
id=ticket_id,
tenant_id=tenant.id,
ticket_number=f"TKT-{1000 + tickets_created}",
subject=subject,
description=description,
status=status,
priority=priority,
created_by=creator.id,
assigned_to=random.choice(agents).id if assigned else None,
category_id=category.id,
affected_system_id=random.choice(systems).id if systems else None,
sla_response_due=sla_response_due,
sla_resolution_due=sla_resolution_due,
first_response_at=first_response_at,
resolved_at=resolved_at,
created_at=created_at,
updated_at=resolved_at or first_response_at or created_at
)
db.add(ticket)
tickets_created += 1
return ticket
# ============================================
# 1. TICKETS CUMPLIENDO SLA RESPONSE (Verde)
# ============================================
print("✅ Generando tickets CUMPLIENDO Response SLA...")
for i in range(15):
category = random.choice(categories)
priority = random.choice([TicketPriority.LOW, TicketPriority.MEDIUM, TicketPriority.HIGH])
# Creado hace X horas, respondido ANTES del deadline
created_hours_ago = random.randint(24, 120)
response_time = random.uniform(0.5, category.sla_response_hours * 0.7) # 70% del SLA
status = random.choice([TicketStatus.IN_PROGRESS, TicketStatus.WAITING_CUSTOMER])
create_ticket(
subject=f"Ticket con respuesta a tiempo #{i+1}",
description=f"Este ticket fue respondido dentro del SLA de {category.name}",
priority=priority,
status=status,
category=category,
created_hours_ago=created_hours_ago,
first_response_hours_after=response_time,
assigned=True
)
print(f" ✓ Creados 15 tickets cumpliendo Response SLA")
# ============================================
# 2. TICKETS VIOLANDO SLA RESPONSE (Rojo)
# ============================================
print("🔴 Generando tickets VIOLANDO Response SLA...")
for i in range(8):
category = random.choice(categories)
priority = random.choice([TicketPriority.HIGH, TicketPriority.URGENT])
# Creado hace más tiempo que el SLA, SIN respuesta
created_hours_ago = category.sla_response_hours + random.randint(1, 10)
create_ticket(
subject=f"Ticket SIN respuesta - VIOLACIÓN #{i+1}",
description=f"Este ticket lleva {created_hours_ago}h sin respuesta (SLA: {category.sla_response_hours}h)",
priority=priority,
status=random.choice([TicketStatus.NEW, TicketStatus.TRIAGE]),
category=category,
created_hours_ago=created_hours_ago,
first_response_hours_after=None, # Sin respuesta!
assigned=random.choice([True, False])
)
print(f" ✓ Creados 8 tickets VIOLANDO Response SLA")
# ============================================
# 3. TICKETS EN RIESGO Response (Amarillo)
# ============================================
print("⚠️ Generando tickets EN RIESGO Response SLA...")
for i in range(10):
category = random.choice(categories)
priority = random.choice([TicketPriority.MEDIUM, TicketPriority.HIGH, TicketPriority.URGENT])
# Creado hace tiempo, cerca del deadline (80-95% consumido)
sla_hours = category.sla_response_hours
time_consumed = random.uniform(0.8, 0.95) * sla_hours
created_hours_ago = time_consumed
create_ticket(
subject=f"Ticket cerca de vencer respuesta #{i+1}",
description=f"Este ticket está al {int(time_consumed/sla_hours*100)}% del SLA de respuesta",
priority=priority,
status=random.choice([TicketStatus.TRIAGE, TicketStatus.NEW]),
category=category,
created_hours_ago=created_hours_ago,
first_response_hours_after=None, # Aún sin respuesta
assigned=True
)
print(f" ✓ Creados 10 tickets EN RIESGO Response SLA")
# ============================================
# 4. TICKETS CUMPLIENDO SLA RESOLUTION
# ============================================
print("✅ Generando tickets CUMPLIENDO Resolution SLA...")
for i in range(20):
category = random.choice(categories)
priority = random.choice([TicketPriority.LOW, TicketPriority.MEDIUM, TicketPriority.HIGH])
# Creado, respondido y resuelto dentro del SLA
created_hours_ago = random.randint(72, 240)
response_time = random.uniform(1, category.sla_response_hours * 0.5)
resolution_time = random.uniform(
response_time + 1,
category.sla_resolution_hours * 0.8
)
create_ticket(
subject=f"Ticket resuelto a tiempo #{i+1}",
description=f"Este ticket fue resuelto dentro del SLA de {category.name}",
priority=priority,
status=random.choice([TicketStatus.RESOLVED, TicketStatus.CLOSED]),
category=category,
created_hours_ago=created_hours_ago,
first_response_hours_after=response_time,
resolved_hours_after=resolution_time,
assigned=True
)
print(f" ✓ Creados 20 tickets cumpliendo Resolution SLA")
# ============================================
# 5. TICKETS VIOLANDO SLA RESOLUTION
# ============================================
print("🔴 Generando tickets VIOLANDO Resolution SLA...")
for i in range(6):
category = random.choice(categories)
priority = random.choice([TicketPriority.HIGH, TicketPriority.URGENT])
# Creado hace más del SLA de resolución, con respuesta pero sin resolver
created_hours_ago = category.sla_resolution_hours + random.randint(5, 48)
response_time = random.uniform(1, category.sla_response_hours * 0.5)
create_ticket(
subject=f"Ticket sin resolver - VIOLACIÓN #{i+1}",
description=f"Ticket lleva {created_hours_ago}h sin resolver (SLA: {category.sla_resolution_hours}h)",
priority=priority,
status=random.choice([TicketStatus.IN_PROGRESS, TicketStatus.WAITING_CUSTOMER]),
category=category,
created_hours_ago=created_hours_ago,
first_response_hours_after=response_time,
resolved_hours_after=None, # Sin resolver!
assigned=True
)
print(f" ✓ Creados 6 tickets VIOLANDO Resolution SLA")
# ============================================
# 6. TICKETS EN RIESGO Resolution
# ============================================
print("⚠️ Generando tickets EN RIESGO Resolution SLA...")
for i in range(12):
category = random.choice(categories)
priority = random.choice([TicketPriority.MEDIUM, TicketPriority.HIGH])
# Con respuesta, cerca del deadline de resolución
sla_hours = category.sla_resolution_hours
time_consumed = random.uniform(0.75, 0.95) * sla_hours
created_hours_ago = time_consumed
response_time = random.uniform(0.5, category.sla_response_hours * 0.5)
create_ticket(
subject=f"Ticket cerca de vencer resolución #{i+1}",
description=f"Este ticket está al {int(time_consumed/sla_hours*100)}% del SLA de resolución",
priority=priority,
status=TicketStatus.IN_PROGRESS,
category=category,
created_hours_ago=created_hours_ago,
first_response_hours_after=response_time,
resolved_hours_after=None,
assigned=True
)
print(f" ✓ Creados 12 tickets EN RIESGO Resolution SLA")
# Guardar todos los tickets
await db.commit()
print()
print("=" * 70)
print("✅ GENERACIÓN DE DATOS SLA COMPLETADA")
print(f" Total de tickets creados: {tickets_created}")
print()
print("📊 Distribución esperada:")
print(" ✅ Response cumplidos: 15 tickets")
print(" 🔴 Response violados: 8 tickets")
print(" ⚠️ Response en riesgo: 10 tickets")
print(" ✅ Resolution cumplidos: 20 tickets")
print(" 🔴 Resolution violados: 6 tickets")
print(" ⚠️ Resolution en riesgo: 12 tickets")
print()
print("🌐 Ve los resultados en:")
print(" Dashboard SLA: http://localhost:3001/sla")
print("=" * 70)
async def cleanup_sla_test_data():
"""Elimina tickets de prueba."""
async with AsyncSessionLocal() as db:
tenant_result = await db.execute(select(Tenant).limit(1))
tenant = tenant_result.scalar_one_or_none()
if not tenant:
print("❌ No se encontró ningún tenant.")
return
# Eliminar tickets que empiezan con TKT-
result = await db.execute(
select(Ticket).where(
Ticket.tenant_id == tenant.id,
Ticket.ticket_number.like('TKT-%')
)
)
tickets = result.scalars().all()
if not tickets:
print(" No hay tickets de prueba para eliminar.")
return
for ticket in tickets:
await db.delete(ticket)
await db.commit()
print(f"✅ Eliminados {len(tickets)} tickets de prueba")
if __name__ == "__main__":
import sys
if len(sys.argv) > 1 and sys.argv[1] == "cleanup":
print("🧹 Limpiando datos de prueba de SLA...")
print()
asyncio.run(cleanup_sla_test_data())
else:
print("🚀 Generando datos de prueba para SLA Management...")
print()
asyncio.run(generate_sla_test_data())
print()
print("💡 Para limpiar estos datos de prueba, ejecuta:")
print(" python scripts/generate_sla_test_data.py cleanup")