295 lines
9.6 KiB
Python
295 lines
9.6 KiB
Python
"""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() |