Files
service_manager/backend/app/api/schemas/reports.py
icamarillo bd21207aae v1.15.1 - modulo de reportes implementado
- 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
2026-02-26 08:51:46 -07:00

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)