Initial commit
This commit is contained in:
327
workers/app/tasks/sla_tasks.py
Normal file
327
workers/app/tasks/sla_tasks.py
Normal file
@@ -0,0 +1,327 @@
|
||||
"""
|
||||
SLA Tasks - ServiceManagerWeb Workers
|
||||
|
||||
Tareas para monitoreo y gestión de SLAs
|
||||
"""
|
||||
|
||||
from celery import current_task
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Dict, Any, 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
|
||||
|
||||
settings = get_settings()
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
@celery_app.task(
|
||||
bind=True,
|
||||
time_limit=300,
|
||||
soft_time_limit=240
|
||||
)
|
||||
def check_sla_violations(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Check for SLA violations and send alerts.
|
||||
|
||||
This task runs every 5 minutes to check for:
|
||||
- Response SLA violations
|
||||
- Resolution SLA violations
|
||||
- SLA warnings (approaching deadline)
|
||||
|
||||
Returns:
|
||||
Dict with check results
|
||||
"""
|
||||
task_logger = logger.bind(
|
||||
task_id=self.request.id,
|
||||
task_name=self.name
|
||||
)
|
||||
|
||||
task_logger.info("Starting SLA violations check")
|
||||
|
||||
if not settings.SLA_CHECK_ENABLED:
|
||||
task_logger.info("SLA check disabled, skipping")
|
||||
return {"status": "disabled"}
|
||||
|
||||
try:
|
||||
current_time = datetime.utcnow()
|
||||
results = {
|
||||
"checked_at": current_time.isoformat(),
|
||||
"response_violations": [],
|
||||
"resolution_violations": [],
|
||||
"warnings": [],
|
||||
"notifications_sent": 0
|
||||
}
|
||||
|
||||
# TODO: Implement actual database queries
|
||||
# For now, simulate some checks
|
||||
|
||||
# Mock violations for development
|
||||
mock_violations = [
|
||||
{
|
||||
"ticket_id": "mock-ticket-1",
|
||||
"ticket_number": "TKT-2024-000001",
|
||||
"subject": "Problema urgente de conexión",
|
||||
"priority": "HIGH",
|
||||
"sla_type": "response",
|
||||
"due_at": (current_time - timedelta(minutes=30)).isoformat(),
|
||||
"assigned_to_email": "agent@example.com",
|
||||
"created_by_email": "cliente@example.com",
|
||||
"tenant_id": "mock-tenant-1"
|
||||
}
|
||||
]
|
||||
|
||||
# Process violations
|
||||
for violation in mock_violations:
|
||||
task_logger.info(
|
||||
"Processing SLA violation",
|
||||
ticket_id=violation["ticket_id"],
|
||||
sla_type=violation["sla_type"]
|
||||
)
|
||||
|
||||
if violation["sla_type"] == "response":
|
||||
results["response_violations"].append(violation)
|
||||
|
||||
# Send notification to assigned agent
|
||||
if violation["assigned_to_email"]:
|
||||
send_templated_email_task.apply_async(kwargs={
|
||||
"to_email": violation["assigned_to_email"],
|
||||
"template_name": "sla_response_violation",
|
||||
"context": {
|
||||
"ticket_number": violation["ticket_number"],
|
||||
"subject": violation["subject"],
|
||||
"priority": violation["priority"],
|
||||
"due_at": violation["due_at"],
|
||||
"ticket_url": f"https://admin.servicemanager.local/tickets/{violation['ticket_id']}"
|
||||
},
|
||||
"tenant_id": violation["tenant_id"],
|
||||
"correlation_id": self.request.id
|
||||
})
|
||||
results["notifications_sent"] += 1
|
||||
|
||||
elif violation["sla_type"] == "resolution":
|
||||
results["resolution_violations"].append(violation)
|
||||
|
||||
# Send escalation notification
|
||||
send_templated_email_task.apply_async(kwargs={
|
||||
"to_email": "manager@example.com", # TODO: Get from tenant config
|
||||
"template_name": "sla_resolution_violation",
|
||||
"context": {
|
||||
"ticket_number": violation["ticket_number"],
|
||||
"subject": violation["subject"],
|
||||
"priority": violation["priority"],
|
||||
"assigned_to": violation["assigned_to_email"],
|
||||
"ticket_url": f"https://admin.servicemanager.local/tickets/{violation['ticket_id']}"
|
||||
},
|
||||
"tenant_id": violation["tenant_id"],
|
||||
"correlation_id": self.request.id
|
||||
})
|
||||
results["notifications_sent"] += 1
|
||||
|
||||
# TODO: Check for SLA warnings (approaching deadline)
|
||||
|
||||
task_logger.info(
|
||||
"SLA violations check completed",
|
||||
response_violations=len(results["response_violations"]),
|
||||
resolution_violations=len(results["resolution_violations"]),
|
||||
warnings=len(results["warnings"]),
|
||||
notifications_sent=results["notifications_sent"]
|
||||
)
|
||||
|
||||
return results
|
||||
|
||||
except Exception as exc:
|
||||
task_logger.error(
|
||||
"SLA violations check failed",
|
||||
error=str(exc),
|
||||
exc_info=True
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
@celery_app.task(
|
||||
bind=True,
|
||||
time_limit=600,
|
||||
soft_time_limit=540
|
||||
)
|
||||
def calculate_sla_metrics(self, tenant_id: str, date_from: str, date_to: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Calculate SLA metrics for a tenant and date range.
|
||||
|
||||
Args:
|
||||
tenant_id: Tenant ID
|
||||
date_from: Start date (ISO format)
|
||||
date_to: End date (ISO format)
|
||||
|
||||
Returns:
|
||||
Dict with SLA metrics
|
||||
"""
|
||||
task_logger = logger.bind(
|
||||
task_id=self.request.id,
|
||||
task_name=self.name,
|
||||
tenant_id=tenant_id
|
||||
)
|
||||
|
||||
task_logger.info(
|
||||
"Starting SLA metrics calculation",
|
||||
date_from=date_from,
|
||||
date_to=date_to
|
||||
)
|
||||
|
||||
try:
|
||||
# TODO: Implement actual database queries
|
||||
# For now, return mock metrics
|
||||
|
||||
metrics = {
|
||||
"tenant_id": tenant_id,
|
||||
"date_from": date_from,
|
||||
"date_to": date_to,
|
||||
"calculated_at": datetime.utcnow().isoformat(),
|
||||
"response_sla": {
|
||||
"target_hours": 2,
|
||||
"met_count": 45,
|
||||
"total_count": 50,
|
||||
"percentage": 90.0,
|
||||
"avg_response_time_hours": 1.8
|
||||
},
|
||||
"resolution_sla": {
|
||||
"target_hours": 24,
|
||||
"met_count": 42,
|
||||
"total_count": 48,
|
||||
"percentage": 87.5,
|
||||
"avg_resolution_time_hours": 22.5
|
||||
},
|
||||
"by_priority": {
|
||||
"LOW": {
|
||||
"response_sla_percentage": 95.0,
|
||||
"resolution_sla_percentage": 90.0
|
||||
},
|
||||
"MEDIUM": {
|
||||
"response_sla_percentage": 88.0,
|
||||
"resolution_sla_percentage": 85.0
|
||||
},
|
||||
"HIGH": {
|
||||
"response_sla_percentage": 92.0,
|
||||
"resolution_sla_percentage": 88.0
|
||||
},
|
||||
"URGENT": {
|
||||
"response_sla_percentage": 85.0,
|
||||
"resolution_sla_percentage": 80.0
|
||||
}
|
||||
},
|
||||
"trends": {
|
||||
"response_sla_trend": "+2.5%",
|
||||
"resolution_sla_trend": "-1.2%"
|
||||
}
|
||||
}
|
||||
|
||||
task_logger.info(
|
||||
"SLA metrics calculation completed",
|
||||
response_sla_percentage=metrics["response_sla"]["percentage"],
|
||||
resolution_sla_percentage=metrics["resolution_sla"]["percentage"]
|
||||
)
|
||||
|
||||
return metrics
|
||||
|
||||
except Exception as exc:
|
||||
task_logger.error(
|
||||
"SLA metrics calculation failed",
|
||||
error=str(exc),
|
||||
exc_info=True
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
@celery_app.task(
|
||||
bind=True,
|
||||
time_limit=300,
|
||||
soft_time_limit=240
|
||||
)
|
||||
def send_sla_warnings(self, tenant_id: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
Send SLA warning notifications for tickets approaching deadline.
|
||||
|
||||
Args:
|
||||
tenant_id: Optional tenant ID to filter by
|
||||
|
||||
Returns:
|
||||
Dict with warning results
|
||||
"""
|
||||
task_logger = logger.bind(
|
||||
task_id=self.request.id,
|
||||
task_name=self.name,
|
||||
tenant_id=tenant_id
|
||||
)
|
||||
|
||||
task_logger.info("Starting SLA warnings check")
|
||||
|
||||
try:
|
||||
current_time = datetime.utcnow()
|
||||
warning_threshold = settings.SLA_WARNING_THRESHOLD # 80% of SLA time
|
||||
|
||||
# TODO: Query database for tickets approaching SLA deadlines
|
||||
|
||||
# Mock warnings
|
||||
warnings = [
|
||||
{
|
||||
"ticket_id": "mock-ticket-2",
|
||||
"ticket_number": "TKT-2024-000002",
|
||||
"subject": "Consulta técnica",
|
||||
"priority": "MEDIUM",
|
||||
"sla_type": "response",
|
||||
"due_at": (current_time + timedelta(minutes=30)).isoformat(),
|
||||
"time_remaining_percent": 15.0,
|
||||
"assigned_to_email": "agent@example.com",
|
||||
"tenant_id": "mock-tenant-1"
|
||||
}
|
||||
]
|
||||
|
||||
notifications_sent = 0
|
||||
|
||||
for warning in warnings:
|
||||
if warning["time_remaining_percent"] <= (1 - warning_threshold) * 100:
|
||||
task_logger.info(
|
||||
"Sending SLA warning",
|
||||
ticket_id=warning["ticket_id"],
|
||||
time_remaining_percent=warning["time_remaining_percent"]
|
||||
)
|
||||
|
||||
send_templated_email_task.apply_async(kwargs={
|
||||
"to_email": warning["assigned_to_email"],
|
||||
"template_name": "sla_warning",
|
||||
"context": {
|
||||
"ticket_number": warning["ticket_number"],
|
||||
"subject": warning["subject"],
|
||||
"priority": warning["priority"],
|
||||
"sla_type": warning["sla_type"],
|
||||
"due_at": warning["due_at"],
|
||||
"time_remaining_percent": warning["time_remaining_percent"],
|
||||
"ticket_url": f"https://admin.servicemanager.local/tickets/{warning['ticket_id']}"
|
||||
},
|
||||
"tenant_id": warning["tenant_id"],
|
||||
"correlation_id": self.request.id
|
||||
})
|
||||
notifications_sent += 1
|
||||
|
||||
task_logger.info(
|
||||
"SLA warnings check completed",
|
||||
warnings_found=len(warnings),
|
||||
notifications_sent=notifications_sent
|
||||
)
|
||||
|
||||
return {
|
||||
"warnings_found": len(warnings),
|
||||
"notifications_sent": notifications_sent,
|
||||
"warnings": warnings
|
||||
}
|
||||
|
||||
except Exception as exc:
|
||||
task_logger.error(
|
||||
"SLA warnings check failed",
|
||||
error=str(exc),
|
||||
exc_info=True
|
||||
)
|
||||
raise
|
||||
Reference in New Issue
Block a user