- Nuevo módulo de reportes: backend/app/api/v1/endpoints/reports.py - Schemas de reportes: backend/app/api/schemas/reports.py - Frontend: frontend-internal/src/routes/reports/ - Mejoras al módulo de auditoría (audit.py, audit_helpers.py) - Modelo de auditoría actualizado - Sidebar actualizado con enlace a reportes
228 lines
6.6 KiB
Python
228 lines
6.6 KiB
Python
"""
|
|
Reports Schemas - ServiceManagerWeb
|
|
|
|
Schemas de respuesta para el módulo de reportes y estadísticas.
|
|
"""
|
|
|
|
from pydantic import BaseModel, ConfigDict
|
|
from typing import Optional, List, Dict, Any
|
|
from datetime import datetime
|
|
|
|
|
|
# ===================================
|
|
# RESUMEN GENERAL
|
|
# ===================================
|
|
|
|
class TicketsByStatus(BaseModel):
|
|
"""Conteo de tickets agrupado por estado"""
|
|
new: int = 0
|
|
triage: int = 0
|
|
in_progress: int = 0
|
|
waiting_customer: int = 0
|
|
resolved: int = 0
|
|
closed: int = 0
|
|
reopened: int = 0
|
|
total: int = 0
|
|
|
|
|
|
class TicketsByPriority(BaseModel):
|
|
"""Conteo de tickets agrupado por prioridad"""
|
|
low: int = 0
|
|
medium: int = 0
|
|
high: int = 0
|
|
urgent: int = 0
|
|
total: int = 0
|
|
|
|
|
|
class ReportSummaryResponse(BaseModel):
|
|
"""Resumen ejecutivo del período seleccionado"""
|
|
period_start: datetime
|
|
period_end: datetime
|
|
generated_at: datetime
|
|
|
|
# Totales del período
|
|
total_tickets: int
|
|
open_tickets: int # Tickets sin resolver
|
|
resolved_tickets: int # Tickets resueltos o cerrados
|
|
avg_resolution_hours: Optional[float] # Promedio de horas para resolver
|
|
avg_first_response_hours: Optional[float] # Promedio de horas para primera respuesta
|
|
|
|
# Satisfacción del cliente
|
|
avg_rating: Optional[float] # Promedio de calificación (1-5)
|
|
total_rated: int # Cuántos tickets tienen calificación
|
|
|
|
# Desglose por estado y prioridad
|
|
by_status: TicketsByStatus
|
|
by_priority: TicketsByPriority
|
|
|
|
# Comparación vs período anterior
|
|
tickets_change_pct: Optional[float] # % cambio vs período anterior
|
|
resolution_change_pct: Optional[float] # % cambio en tasa de resolución
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# ===================================
|
|
# RENDIMIENTO POR AGENTE
|
|
# ===================================
|
|
|
|
class AgentReportRow(BaseModel):
|
|
"""Estadísticas de un agente específico"""
|
|
agent_id: str
|
|
agent_name: str
|
|
agent_email: str
|
|
total_assigned: int # Total asignados en el período
|
|
resolved: int # Cuántos resolvió
|
|
open: int # Cuántos siguen abiertos
|
|
resolution_rate: float # Porcentaje de resolución (0-100)
|
|
avg_resolution_hours: Optional[float] # Promedio de horas para resolver
|
|
avg_rating: Optional[float] # Calificación promedio (1-5)
|
|
total_rated: int # Cuántos tickets calificaron al agente
|
|
urgent_handled: int # Urgentes atendidos
|
|
|
|
|
|
class AgentReportResponse(BaseModel):
|
|
"""Reporte de rendimiento por agente"""
|
|
period_start: datetime
|
|
period_end: datetime
|
|
generated_at: datetime
|
|
agents: List[AgentReportRow]
|
|
total_agents: int
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# ===================================
|
|
# TICKETS POR CATEGORÍA
|
|
# ===================================
|
|
|
|
class CategoryReportRow(BaseModel):
|
|
"""Estadísticas de una categoría"""
|
|
category_id: str
|
|
category_name: str
|
|
total_tickets: int
|
|
open_tickets: int
|
|
resolved_tickets: int
|
|
avg_resolution_hours: Optional[float]
|
|
sla_response_hours: int # SLA configurado para respuesta
|
|
sla_resolution_hours: int # SLA configurado para resolución
|
|
sla_compliance_pct: float # % de tickets que cumplieron SLA de resolución
|
|
|
|
|
|
class CategoryReportResponse(BaseModel):
|
|
"""Reporte de tickets agrupado por categoría"""
|
|
period_start: datetime
|
|
period_end: datetime
|
|
generated_at: datetime
|
|
categories: List[CategoryReportRow]
|
|
uncategorized_count: int
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# ===================================
|
|
# TICKETS POR CLIENTE (TENANT)
|
|
# ===================================
|
|
|
|
class ClientReportRow(BaseModel):
|
|
"""Estadísticas de un cliente (tenant)"""
|
|
tenant_id: str
|
|
tenant_name: str
|
|
total_tickets: int
|
|
open_tickets: int
|
|
resolved_tickets: int
|
|
urgent_tickets: int
|
|
avg_resolution_hours: Optional[float]
|
|
avg_rating: Optional[float]
|
|
last_ticket_at: Optional[datetime]
|
|
|
|
|
|
class ClientReportResponse(BaseModel):
|
|
"""Reporte de tickets agrupado por cliente — solo ADMIN"""
|
|
period_start: datetime
|
|
period_end: datetime
|
|
generated_at: datetime
|
|
clients: List[ClientReportRow]
|
|
total_clients: int
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# ===================================
|
|
# TENDENCIAS (TICKETS EN EL TIEMPO)
|
|
# ===================================
|
|
|
|
class TrendDataPoint(BaseModel):
|
|
"""Un punto de datos en la línea de tendencia"""
|
|
date: str # Formato YYYY-MM-DD
|
|
created: int # Tickets creados ese día
|
|
resolved: int # Tickets resueltos ese día
|
|
net_open: int # Diferencia: creados - resueltos
|
|
|
|
|
|
class TrendsReportResponse(BaseModel):
|
|
"""Evolución de tickets día a día"""
|
|
period_start: datetime
|
|
period_end: datetime
|
|
generated_at: datetime
|
|
data_points: List[TrendDataPoint]
|
|
total_days: int
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# ===================================
|
|
# SATISFACCIÓN DEL CLIENTE (CSAT)
|
|
# ===================================
|
|
|
|
class CSATDistribution(BaseModel):
|
|
"""Distribución de calificaciones 1-5"""
|
|
rating_1: int = 0
|
|
rating_2: int = 0
|
|
rating_3: int = 0
|
|
rating_4: int = 0
|
|
rating_5: int = 0
|
|
|
|
|
|
class CSATReportResponse(BaseModel):
|
|
"""Reporte de satisfacción del cliente"""
|
|
period_start: datetime
|
|
period_end: datetime
|
|
generated_at: datetime
|
|
avg_rating: Optional[float]
|
|
total_rated: int
|
|
total_tickets: int
|
|
response_rate: float # % de tickets que recibieron calificación
|
|
distribution: CSATDistribution
|
|
by_category: List[Dict[str, Any]] # Promedio por categoría
|
|
by_agent: List[Dict[str, Any]] # Promedio por agente
|
|
recent_comments: List[Dict[str, Any]] = [] # Últimos comentarios de calificación
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# ===================================
|
|
# TICKETS POR SISTEMA AFECTADO
|
|
# ===================================
|
|
|
|
class SystemReportRow(BaseModel):
|
|
"""Estadísticas de un sistema afectado"""
|
|
system_id: str
|
|
system_name: str
|
|
total_tickets: int
|
|
open_tickets: int
|
|
resolved_tickets: int
|
|
urgent_tickets: int
|
|
avg_resolution_hours: Optional[float]
|
|
|
|
|
|
class SystemReportResponse(BaseModel):
|
|
"""Reporte de tickets agrupado por sistema afectado"""
|
|
period_start: datetime
|
|
period_end: datetime
|
|
generated_at: datetime
|
|
systems: List[SystemReportRow]
|
|
no_system_count: int # Tickets sin sistema asignado
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|