"""Notification service for pipeline completion alerts.""" import asyncio from datetime import datetime from typing import Dict, Any, Optional, List from enum import Enum class NotificationType(Enum): """Types of notifications.""" COMPLETION = "completion" ERROR = "error" PROGRESS = "progress" SYSTEM = "system" class NotificationService: """Handles various types of notifications for pipeline events.""" def __init__(self): self.enabled = True self.notification_history: List[Dict[str, Any]] = [] async def send_completion_notification( self, job_id: str, result: Dict[str, Any], notification_config: Dict[str, Any] = None ) -> bool: """Send completion notification. Args: job_id: Pipeline job ID result: Pipeline result data notification_config: Notification configuration Returns: True if notification sent successfully """ if not self.enabled: return False try: notification_data = { "type": NotificationType.COMPLETION.value, "job_id": job_id, "timestamp": datetime.utcnow().isoformat(), "video_title": result.get("video_metadata", {}).get("title", "Unknown Video"), "processing_time": result.get("processing_time_seconds"), "quality_score": result.get("quality_score"), "summary_preview": self._get_summary_preview(result.get("summary")), "success": True } # Store in history self._add_to_history(notification_data) # In a real implementation, this would send emails, webhooks, etc. # For now, we'll just log the notification await self._log_notification(notification_data) return True except Exception as e: print(f"Failed to send completion notification: {e}") return False async def send_error_notification( self, job_id: str, error: Dict[str, Any], notification_config: Dict[str, Any] = None ) -> bool: """Send error notification. Args: job_id: Pipeline job ID error: Error information notification_config: Notification configuration Returns: True if notification sent successfully """ if not self.enabled: return False try: notification_data = { "type": NotificationType.ERROR.value, "job_id": job_id, "timestamp": datetime.utcnow().isoformat(), "error_message": error.get("message", "Unknown error"), "error_type": error.get("type", "UnknownError"), "retry_count": error.get("retry_count", 0), "stage": error.get("stage", "unknown"), "success": False } # Store in history self._add_to_history(notification_data) await self._log_notification(notification_data) return True except Exception as e: print(f"Failed to send error notification: {e}") return False async def send_progress_notification( self, job_id: str, progress: Dict[str, Any], notification_config: Dict[str, Any] = None ) -> bool: """Send progress notification (typically only for major milestones). Args: job_id: Pipeline job ID progress: Progress information notification_config: Notification configuration Returns: True if notification sent successfully """ if not self.enabled: return False # Only send progress notifications for major milestones milestone_stages = ["extracting_transcript", "generating_summary", "completed"] current_stage = progress.get("stage", "") if current_stage not in milestone_stages: return True # Skip non-milestone progress updates try: notification_data = { "type": NotificationType.PROGRESS.value, "job_id": job_id, "timestamp": datetime.utcnow().isoformat(), "stage": current_stage, "percentage": progress.get("percentage", 0), "message": progress.get("message", "Processing..."), "milestone": True } # Store in history (but don't clutter with too many progress updates) if current_stage in ["generating_summary", "completed"]: self._add_to_history(notification_data) await self._log_notification(notification_data) return True except Exception as e: print(f"Failed to send progress notification: {e}") return False async def send_system_notification( self, message: str, notification_type: str = "info", metadata: Dict[str, Any] = None ) -> bool: """Send system-level notification. Args: message: Notification message notification_type: Type of system notification (info, warning, error) metadata: Additional metadata Returns: True if notification sent successfully """ if not self.enabled: return False try: notification_data = { "type": NotificationType.SYSTEM.value, "timestamp": datetime.utcnow().isoformat(), "message": message, "notification_type": notification_type, "metadata": metadata or {} } self._add_to_history(notification_data) await self._log_notification(notification_data) return True except Exception as e: print(f"Failed to send system notification: {e}") return False def _get_summary_preview(self, summary: Optional[str]) -> Optional[str]: """Get a preview of the summary for notifications. Args: summary: Full summary text Returns: Preview text or None """ if not summary: return None # Return first 100 characters with ellipsis if len(summary) <= 100: return summary else: return summary[:97] + "..." def _add_to_history(self, notification_data: Dict[str, Any]): """Add notification to history. Args: notification_data: Notification data to store """ self.notification_history.append(notification_data) # Keep only last 1000 notifications to prevent memory bloat if len(self.notification_history) > 1000: self.notification_history = self.notification_history[-1000:] async def _log_notification(self, notification_data: Dict[str, Any]): """Log notification for debugging/monitoring. Args: notification_data: Notification data to log """ notification_type = notification_data.get("type", "unknown") job_id = notification_data.get("job_id", "system") message = notification_data.get("message", "") print(f"[NOTIFICATION] [{notification_type.upper()}] Job {job_id}: {message}") # In production, this would integrate with logging service # Could also send to external services like Slack, Discord, email, etc. def get_notification_history( self, limit: int = 50, notification_type: Optional[str] = None ) -> List[Dict[str, Any]]: """Get notification history. Args: limit: Maximum number of notifications to return notification_type: Filter by notification type Returns: List of notification records """ history = self.notification_history # Filter by type if specified if notification_type: history = [ n for n in history if n.get("type") == notification_type ] # Return most recent first return list(reversed(history[-limit:])) def get_notification_stats(self) -> Dict[str, Any]: """Get notification statistics. Returns: Notification statistics """ total_notifications = len(self.notification_history) type_counts = {} for notification in self.notification_history: notification_type = notification.get("type", "unknown") type_counts[notification_type] = type_counts.get(notification_type, 0) + 1 return { "total_notifications": total_notifications, "notifications_by_type": type_counts, "enabled": self.enabled } def enable_notifications(self): """Enable notification sending.""" self.enabled = True def disable_notifications(self): """Disable notification sending.""" self.enabled = False def clear_history(self): """Clear notification history.""" self.notification_history.clear()