""" Notification Tasks - ServiceManagerWeb Workers Tareas para notificaciones y comunicaciones """ from celery import current_task from datetime import datetime, timedelta from typing import Dict, Any, List, Optional import structlog from app.celery import celery_app from app.core.config import get_settings from app.core.logging import get_logger from app.tasks.email_tasks import send_templated_email_task, send_bulk_email_task settings = get_settings() logger = get_logger(__name__) @celery_app.task( bind=True, time_limit=900, # 15 minutes soft_time_limit=780 # 13 minutes ) def send_daily_digest(self) -> Dict[str, Any]: """ Send daily digest emails to agents and managers. Includes: - New tickets assigned - SLA warnings - Performance summary - Pending tasks """ task_logger = logger.bind( task_id=self.request.id, task_name=self.name ) task_logger.info("Starting daily digest generation") if not settings.DIGEST_ENABLED: task_logger.info("Daily digest disabled, skipping") return {"status": "disabled"} try: current_time = datetime.utcnow() yesterday = current_time - timedelta(days=1) digest_results = { "generated_at": current_time.isoformat(), "period_start": yesterday.isoformat(), "period_end": current_time.isoformat(), "digests_sent": 0, "errors": [] } # TODO: Get active agents and managers from database mock_recipients = [ { "user_id": "user-1", "email": "agent1@example.com", "name": "Agent One", "role": "AGENT", "tenant_id": "tenant-1" }, { "user_id": "user-2", "email": "manager@example.com", "name": "Support Manager", "role": "SUPPORT_MANAGER", "tenant_id": "tenant-1" } ] for recipient in mock_recipients: try: task_logger.info( "Generating digest for user", user_id=recipient["user_id"], email=recipient["email"], role=recipient["role"] ) # TODO: Generate actual digest data from database digest_data = generate_digest_data( recipient["user_id"], recipient["role"], recipient["tenant_id"], yesterday, current_time ) # Send digest email send_templated_email_task.apply_async(kwargs={ "to_email": recipient["email"], "template_name": "daily_digest", "context": { "user_name": recipient["name"], "role": recipient["role"], "date": current_time.strftime("%Y-%m-%d"), **digest_data }, "tenant_id": recipient["tenant_id"], "correlation_id": self.request.id }) digest_results["digests_sent"] += 1 except Exception as e: error_msg = f"Failed to send digest to {recipient['email']}: {str(e)}" digest_results["errors"].append(error_msg) task_logger.error( "Digest generation failed for user", user_id=recipient["user_id"], error=str(e) ) task_logger.info( "Daily digest generation completed", digests_sent=digest_results["digests_sent"], errors=len(digest_results["errors"]) ) return digest_results except Exception as exc: task_logger.error( "Daily digest generation failed", error=str(exc), exc_info=True ) raise def generate_digest_data( user_id: str, role: str, tenant_id: str, period_start: datetime, period_end: datetime ) -> Dict[str, Any]: """ Generate digest data for a specific user. Args: user_id: User ID role: User role tenant_id: Tenant ID period_start: Start of digest period period_end: End of digest period Returns: Dict with digest data """ # TODO: Implement actual database queries # For now, return mock data base_data = { "summary": { "new_tickets": 5, "resolved_tickets": 7, "pending_tickets": 12, "overdue_tickets": 2 }, "sla_status": { "response_sla_met": 8, "response_sla_missed": 1, "resolution_sla_met": 6, "resolution_sla_missed": 2 } } if role == "AGENT": base_data.update({ "assigned_tickets": [ { "ticket_number": "TKT-2024-000001", "subject": "Problema de conexión", "priority": "HIGH", "created_at": "2024-01-15T10:00:00Z", "sla_due": "2024-01-15T12:00:00Z" } ], "urgent_tickets": 1, "performance": { "avg_response_time_hours": 1.5, "avg_resolution_time_hours": 18.2, "customer_satisfaction": 4.3 } }) elif role in ["SUPPORT_MANAGER", "ADMIN"]: base_data.update({ "team_summary": { "total_agents": 5, "active_agents": 4, "avg_load_per_agent": 6.2 }, "escalations": [ { "ticket_number": "TKT-2024-000002", "reason": "SLA violation", "assigned_to": "agent1@example.com" } ], "trends": { "ticket_volume_change": "+12%", "resolution_time_change": "-5%" } }) return base_data @celery_app.task( bind=True, time_limit=600, soft_time_limit=540 ) def send_ticket_notifications( self, ticket_id: str, event_type: str, tenant_id: str, context: Dict[str, Any], correlation_id: Optional[str] = None ) -> Dict[str, Any]: """ Send ticket-related notifications. Args: ticket_id: Ticket ID event_type: Type of event (created, assigned, updated, resolved, etc.) tenant_id: Tenant ID context: Context data for notifications correlation_id: Correlation ID Returns: Dict with notification results """ task_logger = logger.bind( task_id=self.request.id, task_name=self.name, ticket_id=ticket_id, event_type=event_type, tenant_id=tenant_id, correlation_id=correlation_id ) task_logger.info("Starting ticket notifications") try: notification_results = { "ticket_id": ticket_id, "event_type": event_type, "notifications_sent": 0, "notifications": [] } # Determine who should receive notifications based on event type recipients = get_notification_recipients(ticket_id, event_type, tenant_id) for recipient in recipients: try: template_name = f"ticket_{event_type}" # Send notification result = send_templated_email_task.apply_async(kwargs={ "to_email": recipient["email"], "template_name": template_name, "context": { **context, "recipient_name": recipient["name"], "recipient_role": recipient["role"] }, "tenant_id": tenant_id, "correlation_id": correlation_id or self.request.id }).get() notification_results["notifications"].append({ "recipient": recipient["email"], "template": template_name, "success": result.get("success", False), "error": result.get("error") }) if result.get("success"): notification_results["notifications_sent"] += 1 except Exception as e: task_logger.error( "Failed to send notification", recipient_email=recipient["email"], error=str(e) ) notification_results["notifications"].append({ "recipient": recipient["email"], "success": False, "error": str(e) }) task_logger.info( "Ticket notifications completed", notifications_sent=notification_results["notifications_sent"], total_recipients=len(recipients) ) return notification_results except Exception as exc: task_logger.error( "Ticket notifications failed", error=str(exc), exc_info=True ) raise def get_notification_recipients( ticket_id: str, event_type: str, tenant_id: str ) -> List[Dict[str, Any]]: """ Get list of users who should receive notifications for a ticket event. Args: ticket_id: Ticket ID event_type: Event type tenant_id: Tenant ID Returns: List of recipient dicts """ # TODO: Implement actual database queries # For now, return mock recipients based on event type recipients = [] if event_type == "created": # Notify assigned agent (if any) and customer recipients = [ {"email": "customer@example.com", "name": "Customer", "role": "CLIENT_USER"}, {"email": "agent@example.com", "name": "Agent", "role": "AGENT"} ] elif event_type == "assigned": # Notify assigned agent and customer recipients = [ {"email": "agent@example.com", "name": "Assigned Agent", "role": "AGENT"}, {"email": "customer@example.com", "name": "Customer", "role": "CLIENT_USER"} ] elif event_type == "updated": # Notify all participants recipients = [ {"email": "customer@example.com", "name": "Customer", "role": "CLIENT_USER"}, {"email": "agent@example.com", "name": "Agent", "role": "AGENT"} ] elif event_type == "resolved": # Notify customer for feedback recipients = [ {"email": "customer@example.com", "name": "Customer", "role": "CLIENT_USER"} ] elif event_type == "escalated": # Notify manager recipients = [ {"email": "manager@example.com", "name": "Manager", "role": "SUPPORT_MANAGER"} ] return recipients @celery_app.task( bind=True, time_limit=300, soft_time_limit=240 ) def send_system_alert( self, alert_type: str, message: str, severity: str = "INFO", tenant_id: Optional[str] = None, context: Optional[Dict[str, Any]] = None ) -> Dict[str, Any]: """ Send system alert to administrators. Args: alert_type: Type of alert (system_error, sla_violation, etc.) message: Alert message severity: Alert severity (INFO, WARNING, ERROR, CRITICAL) tenant_id: Optional tenant ID context: Additional context data Returns: Dict with alert results """ task_logger = logger.bind( task_id=self.request.id, task_name=self.name, alert_type=alert_type, severity=severity, tenant_id=tenant_id ) task_logger.info("Sending system alert", message=message) try: # TODO: Get administrators from configuration/database admin_emails = ["admin@example.com", "alerts@example.com"] alert_context = { "alert_type": alert_type, "message": message, "severity": severity, "timestamp": datetime.utcnow().isoformat(), "environment": settings.ENVIRONMENT, "tenant_id": tenant_id, **(context or {}) } notifications_sent = 0 for admin_email in admin_emails: try: send_templated_email_task.apply_async(kwargs={ "to_email": admin_email, "template_name": "system_alert", "context": alert_context, "tenant_id": tenant_id, "correlation_id": self.request.id }) notifications_sent += 1 except Exception as e: task_logger.error( "Failed to send alert to admin", admin_email=admin_email, error=str(e) ) task_logger.info( "System alert sent", notifications_sent=notifications_sent, total_admins=len(admin_emails) ) return { "alert_type": alert_type, "message": message, "severity": severity, "notifications_sent": notifications_sent, "sent_at": datetime.utcnow().isoformat() } except Exception as exc: task_logger.error( "System alert failed", error=str(exc), exc_info=True ) raise