Initial commit
This commit is contained in:
469
workers/app/tasks/notification_tasks.py
Normal file
469
workers/app/tasks/notification_tasks.py
Normal file
@@ -0,0 +1,469 @@
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user