Updates
This commit is contained in:
238
ETB-API/analytics_predictive_insights/signals.py
Normal file
238
ETB-API/analytics_predictive_insights/signals.py
Normal file
@@ -0,0 +1,238 @@
|
||||
"""
|
||||
Signals for analytics_predictive_insights app
|
||||
Handles automatic KPI calculations and analytics updates
|
||||
"""
|
||||
from django.db.models.signals import post_save, post_delete
|
||||
from django.dispatch import receiver
|
||||
from django.utils import timezone
|
||||
from django.db import models
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from incident_intelligence.models import Incident
|
||||
from .models import KPIMetric, KPIMeasurement, CostImpactAnalysis
|
||||
from .ml.anomaly_detection import AnomalyDetectionService
|
||||
from .ml.predictive_models import PredictiveModelService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@receiver(post_save, sender=Incident)
|
||||
def update_kpi_measurements_on_incident_change(sender, instance, created, **kwargs):
|
||||
"""Update KPI measurements when incidents are created or updated"""
|
||||
try:
|
||||
# Only process if incident is resolved or status changed
|
||||
if not created and not instance.is_resolved:
|
||||
return
|
||||
|
||||
# Get active KPI metrics that apply to this incident
|
||||
applicable_metrics = KPIMetric.objects.filter(
|
||||
is_active=True
|
||||
).filter(
|
||||
# Check if metric applies to this incident
|
||||
models.Q(incident_categories__contains=[instance.category]) |
|
||||
models.Q(incident_severities__contains=[instance.severity]) |
|
||||
models.Q(incident_priorities__contains=[instance.priority]) |
|
||||
models.Q(incident_categories__isnull=True) |
|
||||
models.Q(incident_severities__isnull=True) |
|
||||
models.Q(incident_priorities__isnull=True)
|
||||
)
|
||||
|
||||
for metric in applicable_metrics:
|
||||
# Calculate and update KPI measurement
|
||||
_calculate_kpi_measurement(metric, instance)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating KPI measurements for incident {instance.id}: {str(e)}")
|
||||
|
||||
|
||||
@receiver(post_save, sender=Incident)
|
||||
def trigger_anomaly_detection_on_incident(sender, instance, created, **kwargs):
|
||||
"""Trigger anomaly detection when new incidents are created"""
|
||||
try:
|
||||
if created:
|
||||
# Run anomaly detection for active models
|
||||
anomaly_service = AnomalyDetectionService()
|
||||
anomaly_service.run_anomaly_detection()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error running anomaly detection for incident {instance.id}: {str(e)}")
|
||||
|
||||
|
||||
@receiver(post_save, sender=CostImpactAnalysis)
|
||||
def update_cost_analytics_on_cost_change(sender, instance, created, **kwargs):
|
||||
"""Update cost analytics when cost analysis is created or updated"""
|
||||
try:
|
||||
# Trigger cost-related KPI updates
|
||||
cost_metrics = KPIMetric.objects.filter(
|
||||
is_active=True,
|
||||
metric_type='COST_IMPACT'
|
||||
)
|
||||
|
||||
for metric in cost_metrics:
|
||||
_calculate_kpi_measurement(metric, instance.incident)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating cost analytics for cost analysis {instance.id}: {str(e)}")
|
||||
|
||||
|
||||
def _calculate_kpi_measurement(metric, incident):
|
||||
"""Calculate KPI measurement for a specific metric and incident"""
|
||||
try:
|
||||
# Determine time window for calculation
|
||||
end_time = timezone.now()
|
||||
start_time = end_time - timedelta(hours=metric.time_window_hours)
|
||||
|
||||
# Get incidents in the time window that match the metric criteria
|
||||
incidents = Incident.objects.filter(
|
||||
created_at__gte=start_time,
|
||||
created_at__lte=end_time
|
||||
)
|
||||
|
||||
# Apply metric filters
|
||||
if metric.incident_categories:
|
||||
incidents = incidents.filter(category__in=metric.incident_categories)
|
||||
if metric.incident_severities:
|
||||
incidents = incidents.filter(severity__in=metric.incident_severities)
|
||||
if metric.incident_priorities:
|
||||
incidents = incidents.filter(priority__in=metric.incident_priorities)
|
||||
|
||||
# Calculate metric value based on type
|
||||
if metric.metric_type == 'MTTA':
|
||||
# Mean Time to Acknowledge
|
||||
acknowledged_incidents = incidents.filter(
|
||||
status__in=['IN_PROGRESS', 'RESOLVED', 'CLOSED']
|
||||
).exclude(assigned_to__isnull=True)
|
||||
|
||||
if acknowledged_incidents.exists():
|
||||
# Calculate average time to acknowledgment
|
||||
total_time = timedelta()
|
||||
count = 0
|
||||
|
||||
for inc in acknowledged_incidents:
|
||||
# This is simplified - in practice, you'd need to track acknowledgment time
|
||||
if inc.updated_at and inc.created_at:
|
||||
time_diff = inc.updated_at - inc.created_at
|
||||
total_time += time_diff
|
||||
count += 1
|
||||
|
||||
if count > 0:
|
||||
avg_time = total_time / count
|
||||
value = avg_time.total_seconds() / 60 # Convert to minutes
|
||||
unit = 'minutes'
|
||||
else:
|
||||
value = 0
|
||||
unit = 'minutes'
|
||||
else:
|
||||
value = 0
|
||||
unit = 'minutes'
|
||||
|
||||
elif metric.metric_type == 'MTTR':
|
||||
# Mean Time to Resolve
|
||||
resolved_incidents = incidents.filter(
|
||||
status__in=['RESOLVED', 'CLOSED'],
|
||||
resolved_at__isnull=False
|
||||
)
|
||||
|
||||
if resolved_incidents.exists():
|
||||
total_time = timedelta()
|
||||
count = 0
|
||||
|
||||
for inc in resolved_incidents:
|
||||
if inc.resolved_at and inc.created_at:
|
||||
time_diff = inc.resolved_at - inc.created_at
|
||||
total_time += time_diff
|
||||
count += 1
|
||||
|
||||
if count > 0:
|
||||
avg_time = total_time / count
|
||||
value = avg_time.total_seconds() / 3600 # Convert to hours
|
||||
unit = 'hours'
|
||||
else:
|
||||
value = 0
|
||||
unit = 'hours'
|
||||
else:
|
||||
value = 0
|
||||
unit = 'hours'
|
||||
|
||||
elif metric.metric_type == 'INCIDENT_COUNT':
|
||||
# Incident Count
|
||||
value = incidents.count()
|
||||
unit = 'count'
|
||||
|
||||
elif metric.metric_type == 'RESOLUTION_RATE':
|
||||
# Resolution Rate
|
||||
total_incidents = incidents.count()
|
||||
resolved_incidents = incidents.filter(
|
||||
status__in=['RESOLVED', 'CLOSED']
|
||||
).count()
|
||||
|
||||
if total_incidents > 0:
|
||||
value = (resolved_incidents / total_incidents) * 100
|
||||
unit = 'percentage'
|
||||
else:
|
||||
value = 0
|
||||
unit = 'percentage'
|
||||
|
||||
else:
|
||||
# Default calculation
|
||||
value = incidents.count()
|
||||
unit = 'count'
|
||||
|
||||
# Create or update KPI measurement
|
||||
measurement, created = KPIMeasurement.objects.get_or_create(
|
||||
metric=metric,
|
||||
measurement_period_start=start_time,
|
||||
measurement_period_end=end_time,
|
||||
defaults={
|
||||
'value': value,
|
||||
'unit': unit,
|
||||
'incident_count': incidents.count(),
|
||||
'sample_size': incidents.count()
|
||||
}
|
||||
)
|
||||
|
||||
if not created:
|
||||
measurement.value = value
|
||||
measurement.unit = unit
|
||||
measurement.incident_count = incidents.count()
|
||||
measurement.sample_size = incidents.count()
|
||||
measurement.save()
|
||||
|
||||
logger.info(f"Updated KPI measurement for {metric.name}: {value} {unit}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating KPI measurement for {metric.name}: {str(e)}")
|
||||
|
||||
|
||||
# Management command signals for scheduled tasks
|
||||
@receiver(post_save, sender=PredictiveModel)
|
||||
def schedule_model_training(sender, instance, created, **kwargs):
|
||||
"""Schedule model training when a new predictive model is created"""
|
||||
try:
|
||||
if created and instance.status == 'TRAINING':
|
||||
# In a real implementation, you would schedule a background task
|
||||
# For now, we'll just log the event
|
||||
logger.info(f"Scheduled training for model {instance.name}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error scheduling model training for {instance.name}: {str(e)}")
|
||||
|
||||
|
||||
@receiver(post_save, sender=PredictiveModel)
|
||||
def trigger_model_retraining(sender, instance, created, **kwargs):
|
||||
"""Trigger model retraining when performance drops below threshold"""
|
||||
try:
|
||||
if not created and instance.auto_retrain_enabled:
|
||||
# Check if model performance is below threshold
|
||||
if (instance.accuracy_score and
|
||||
instance.accuracy_score < instance.performance_threshold):
|
||||
|
||||
# Update status to retraining
|
||||
instance.status = 'RETRAINING'
|
||||
instance.save()
|
||||
|
||||
logger.info(f"Triggered retraining for model {instance.name} due to low performance")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error triggering model retraining for {instance.name}: {str(e)}")
|
||||
Reference in New Issue
Block a user