""" 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)