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
This commit is contained in:
227
backend/app/api/schemas/reports.py
Normal file
227
backend/app/api/schemas/reports.py
Normal file
@@ -0,0 +1,227 @@
|
||||
"""
|
||||
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)
|
||||
Reference in New Issue
Block a user