- Fix error de sintaxis SQL en cálculo de tickets 'at risk' - Fix error de timezone (offset-naive vs offset-aware datetimes) - Implementado sistema completo de SLA Management - Agregados endpoints: /sla/dashboard, /sla/violations, /sla/at-risk - Creadas vistas frontend para dashboard, violaciones y tickets en riesgo - Actualizado sistema de Celery para monitoreo automático de SLAs - Mejorada configuración de categorías con tiempos SLA personalizables - Corregidos problemas de proxy en configuración de Vite - Agregado troubleshooting guide en README Archivos principales modificados: - backend/app/api/v1/endpoints/sla.py (nuevo) - backend/app/api/schemas/sla.py (nuevo) - frontend-internal/src/routes/sla/ (nuevo módulo completo) - workers/app/tasks/sla_tasks.py (queries async mejoradas) Documentación: docs/changelog-2026-02-17.md
254 lines
6.3 KiB
Python
254 lines
6.3 KiB
Python
"""
|
|
SLA Schemas - ServiceManagerWeb
|
|
|
|
Schemas para el sistema de gestión de SLAs
|
|
"""
|
|
|
|
from pydantic import BaseModel, ConfigDict
|
|
from typing import Optional, List, Dict, Any
|
|
from datetime import datetime
|
|
from enum import Enum
|
|
import uuid
|
|
|
|
|
|
class SLATypeEnum(str, Enum):
|
|
"""Tipos de SLA"""
|
|
RESPONSE = "response"
|
|
RESOLUTION = "resolution"
|
|
|
|
|
|
class SLAStatusEnum(str, Enum):
|
|
"""Estados de cumplimiento SLA"""
|
|
MET = "met" # Cumplido
|
|
VIOLATED = "violated" # Violado
|
|
AT_RISK = "at_risk" # En riesgo (80%+ del tiempo)
|
|
PENDING = "pending" # Pendiente (ticket aún abierto)
|
|
|
|
|
|
# ===================================
|
|
# DASHBOARD SCHEMAS
|
|
# ===================================
|
|
|
|
class SLAComplianceMetrics(BaseModel):
|
|
"""Métricas de cumplimiento SLA"""
|
|
target_hours: int
|
|
met_count: int
|
|
violated_count: int
|
|
at_risk_count: int
|
|
total_count: int
|
|
compliance_percentage: float
|
|
avg_time_hours: Optional[float] = None
|
|
|
|
|
|
class SLADashboardResponse(BaseModel):
|
|
"""Response del dashboard principal de SLA"""
|
|
tenant_id: uuid.UUID
|
|
period_start: datetime
|
|
period_end: datetime
|
|
generated_at: datetime
|
|
|
|
# Métricas generales
|
|
response_sla: SLAComplianceMetrics
|
|
resolution_sla: SLAComplianceMetrics
|
|
|
|
# Contadores rápidos
|
|
active_violations: int
|
|
at_risk_tickets: int
|
|
total_tickets_period: int
|
|
|
|
# Breakdown por categoría (top 5)
|
|
by_category: List[Dict[str, Any]]
|
|
|
|
# Breakdown por prioridad
|
|
by_priority: Dict[str, Dict[str, float]]
|
|
|
|
# Tendencias (comparación con período anterior)
|
|
trends: Dict[str, str]
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# ===================================
|
|
# VIOLATIONS SCHEMAS
|
|
# ===================================
|
|
|
|
class TicketBasicInfo(BaseModel):
|
|
"""Información básica del ticket"""
|
|
id: uuid.UUID
|
|
ticket_number: str
|
|
subject: str
|
|
priority: str
|
|
status: str
|
|
|
|
|
|
class UserBasicInfo(BaseModel):
|
|
"""Información básica del usuario"""
|
|
id: uuid.UUID
|
|
first_name: str
|
|
last_name: str
|
|
email: str
|
|
|
|
|
|
class CategoryBasicInfo(BaseModel):
|
|
"""Información básica de categoría"""
|
|
id: uuid.UUID
|
|
name: str
|
|
sla_response_hours: int
|
|
sla_resolution_hours: int
|
|
|
|
|
|
class SLAViolationResponse(BaseModel):
|
|
"""Detalle de una violación SLA"""
|
|
ticket: TicketBasicInfo
|
|
category: Optional[CategoryBasicInfo] = None
|
|
created_by: UserBasicInfo
|
|
assigned_to: Optional[UserBasicInfo] = None
|
|
|
|
sla_type: SLATypeEnum
|
|
sla_due_at: datetime
|
|
violated_at: datetime
|
|
hours_overdue: float
|
|
|
|
# Contexto adicional
|
|
first_response_at: Optional[datetime] = None
|
|
resolved_at: Optional[datetime] = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class SLAViolationsListResponse(BaseModel):
|
|
"""Lista paginada de violaciones"""
|
|
violations: List[SLAViolationResponse]
|
|
total: int
|
|
page: int
|
|
per_page: int
|
|
total_pages: int
|
|
|
|
|
|
# ===================================
|
|
# TICKETS AT RISK
|
|
# ===================================
|
|
|
|
class SLATicketAtRisk(BaseModel):
|
|
"""Ticket que está en riesgo de violar SLA"""
|
|
ticket: TicketBasicInfo
|
|
category: Optional[CategoryBasicInfo] = None
|
|
assigned_to: Optional[UserBasicInfo] = None
|
|
|
|
sla_type: SLATypeEnum
|
|
sla_due_at: datetime
|
|
time_remaining_hours: float
|
|
risk_percentage: float # 0-100, qué % del tiempo ha pasado
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class SLAAtRiskListResponse(BaseModel):
|
|
"""Lista de tickets en riesgo"""
|
|
tickets: List[SLATicketAtRisk]
|
|
total: int
|
|
|
|
|
|
# ===================================
|
|
# METRICS & REPORTS SCHEMAS
|
|
# ===================================
|
|
|
|
class SLAMetricsByCategory(BaseModel):
|
|
"""Métricas SLA por categoría"""
|
|
category_id: uuid.UUID
|
|
category_name: str
|
|
response_sla_compliance: float
|
|
resolution_sla_compliance: float
|
|
total_tickets: int
|
|
response_violations: int
|
|
resolution_violations: int
|
|
avg_response_time_hours: Optional[float]
|
|
avg_resolution_time_hours: Optional[float]
|
|
|
|
|
|
class SLAMetricsByAgent(BaseModel):
|
|
"""Métricas SLA por agente"""
|
|
agent_id: uuid.UUID
|
|
agent_name: str
|
|
tickets_assigned: int
|
|
response_sla_met: int
|
|
resolution_sla_met: int
|
|
response_compliance: float
|
|
resolution_compliance: float
|
|
avg_response_time_hours: Optional[float]
|
|
avg_resolution_time_hours: Optional[float]
|
|
|
|
|
|
class SLAMetricsByPriority(BaseModel):
|
|
"""Métricas SLA por prioridad"""
|
|
priority: str
|
|
total_tickets: int
|
|
response_sla_compliance: float
|
|
resolution_sla_compliance: float
|
|
avg_response_time_hours: Optional[float]
|
|
avg_resolution_time_hours: Optional[float]
|
|
|
|
|
|
class SLADetailedMetricsResponse(BaseModel):
|
|
"""Response de métricas detalladas"""
|
|
tenant_id: uuid.UUID
|
|
date_from: datetime
|
|
date_to: datetime
|
|
group_by: str # 'category', 'agent', 'priority'
|
|
|
|
by_category: Optional[List[SLAMetricsByCategory]] = None
|
|
by_agent: Optional[List[SLAMetricsByAgent]] = None
|
|
by_priority: Optional[List[SLAMetricsByPriority]] = None
|
|
|
|
generated_at: datetime
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# ===================================
|
|
# HISTORICAL TRENDS
|
|
# ===================================
|
|
|
|
class SLADailyTrend(BaseModel):
|
|
"""Tendencia diaria de SLA"""
|
|
date: str # YYYY-MM-DD
|
|
response_compliance: float
|
|
resolution_compliance: float
|
|
total_tickets: int
|
|
violations: int
|
|
|
|
|
|
class SLATrendsResponse(BaseModel):
|
|
"""Response de tendencias históricas"""
|
|
tenant_id: uuid.UUID
|
|
days: int
|
|
daily_trends: List[SLADailyTrend]
|
|
|
|
# Promedios del período
|
|
avg_response_compliance: float
|
|
avg_resolution_compliance: float
|
|
total_tickets: int
|
|
total_violations: int
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# ===================================
|
|
# CONFIGURATION
|
|
# ===================================
|
|
|
|
class SLAConfigByCategoryResponse(BaseModel):
|
|
"""Configuración SLA por categoría"""
|
|
category_id: uuid.UUID
|
|
category_name: str
|
|
sla_response_hours: int
|
|
sla_resolution_hours: int
|
|
warning_threshold_percentage: int # % del tiempo para alertar
|
|
is_active: bool
|
|
|
|
|
|
class SLAConfigListResponse(BaseModel):
|
|
"""Lista de configuraciones SLA"""
|
|
tenant_id: uuid.UUID
|
|
categories: List[SLAConfigByCategoryResponse]
|