This commit is contained in:
Iliyan Angelov
2025-09-19 11:58:53 +03:00
parent 306b20e24a
commit 6b247e5b9f
11423 changed files with 1500615 additions and 778 deletions

View File

@@ -0,0 +1 @@
# Management commands

View File

@@ -0,0 +1,216 @@
"""
Management command to calculate KPI measurements
"""
from django.core.management.base import BaseCommand, CommandError
from django.utils import timezone
from datetime import timedelta
from analytics_predictive_insights.models import KPIMetric, KPIMeasurement
from incident_intelligence.models import Incident
class Command(BaseCommand):
"""Calculate KPI measurements for all active metrics"""
help = 'Calculate KPI measurements for all active metrics'
def add_arguments(self, parser):
parser.add_argument(
'--metric-id',
type=str,
help='Calculate KPI for a specific metric ID only'
)
parser.add_argument(
'--time-window',
type=int,
default=24,
help='Time window in hours for KPI calculation (default: 24)'
)
parser.add_argument(
'--force',
action='store_true',
help='Force recalculation even if recent measurement exists'
)
def handle(self, *args, **options):
"""Handle the command execution"""
metric_id = options.get('metric_id')
time_window = options.get('time_window', 24)
force = options.get('force', False)
try:
if metric_id:
metrics = KPIMetric.objects.filter(id=metric_id, is_active=True)
if not metrics.exists():
raise CommandError(f'No active metric found with ID: {metric_id}')
else:
metrics = KPIMetric.objects.filter(is_active=True)
self.stdout.write(f'Calculating KPIs for {metrics.count()} metrics...')
total_calculated = 0
for metric in metrics:
try:
calculated = self._calculate_metric(metric, time_window, force)
if calculated:
total_calculated += 1
self.stdout.write(
self.style.SUCCESS(f'✓ Calculated KPI for {metric.name}')
)
else:
self.stdout.write(
self.style.WARNING(f'⚠ Skipped KPI for {metric.name} (recent measurement exists)')
)
except Exception as e:
self.stdout.write(
self.style.ERROR(f'✗ Error calculating KPI for {metric.name}: {str(e)}')
)
self.stdout.write(
self.style.SUCCESS(f'Successfully calculated {total_calculated} KPIs')
)
except Exception as e:
raise CommandError(f'Error executing command: {str(e)}')
def _calculate_metric(self, metric, time_window_hours, force=False):
"""Calculate KPI measurement for a specific metric"""
end_time = timezone.now()
start_time = end_time - timedelta(hours=time_window_hours)
# Check if recent measurement exists
if not force:
recent_measurement = KPIMeasurement.objects.filter(
metric=metric,
calculated_at__gte=end_time - timedelta(hours=1)
).first()
if recent_measurement:
return False
# Get incidents in the time window
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':
value, unit = self._calculate_mtta(incidents)
elif metric.metric_type == 'MTTR':
value, unit = self._calculate_mttr(incidents)
elif metric.metric_type == 'INCIDENT_COUNT':
value, unit = incidents.count(), 'count'
elif metric.metric_type == 'RESOLUTION_RATE':
value, unit = self._calculate_resolution_rate(incidents)
elif metric.metric_type == 'AVAILABILITY':
value, unit = self._calculate_availability(incidents)
else:
value, unit = incidents.count(), 'count'
# Create or update 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()
return True
def _calculate_mtta(self, incidents):
"""Calculate Mean Time to Acknowledge"""
acknowledged_incidents = incidents.filter(
status__in=['IN_PROGRESS', 'RESOLVED', 'CLOSED']
).exclude(assigned_to__isnull=True)
if not acknowledged_incidents.exists():
return 0, 'minutes'
total_time = timedelta()
count = 0
for incident in acknowledged_incidents:
# Simplified calculation - in practice, you'd track acknowledgment time
if incident.updated_at and incident.created_at:
time_diff = incident.updated_at - incident.created_at
total_time += time_diff
count += 1
if count > 0:
avg_time = total_time / count
return avg_time.total_seconds() / 60, 'minutes' # Convert to minutes
return 0, 'minutes'
def _calculate_mttr(self, incidents):
"""Calculate Mean Time to Resolve"""
resolved_incidents = incidents.filter(
status__in=['RESOLVED', 'CLOSED'],
resolved_at__isnull=False
)
if not resolved_incidents.exists():
return 0, 'hours'
total_time = timedelta()
count = 0
for incident in resolved_incidents:
if incident.resolved_at and incident.created_at:
time_diff = incident.resolved_at - incident.created_at
total_time += time_diff
count += 1
if count > 0:
avg_time = total_time / count
return avg_time.total_seconds() / 3600, 'hours' # Convert to hours
return 0, 'hours'
def _calculate_resolution_rate(self, incidents):
"""Calculate resolution rate"""
total_incidents = incidents.count()
if total_incidents == 0:
return 0, 'percentage'
resolved_incidents = incidents.filter(
status__in=['RESOLVED', 'CLOSED']
).count()
rate = (resolved_incidents / total_incidents) * 100
return rate, 'percentage'
def _calculate_availability(self, incidents):
"""Calculate service availability"""
# Simplified availability calculation
# In practice, you'd need more sophisticated uptime tracking
total_incidents = incidents.count()
if total_incidents == 0:
return 100, 'percentage'
# Assume availability decreases with incident count
# This is a simplified calculation
availability = max(0, 100 - (total_incidents * 0.1))
return availability, 'percentage'

View File

@@ -0,0 +1,63 @@
"""
Management command to run anomaly detection
"""
from django.core.management.base import BaseCommand, CommandError
from analytics_predictive_insights.models import PredictiveModel
from analytics_predictive_insights.ml.anomaly_detection import AnomalyDetectionService
class Command(BaseCommand):
"""Run anomaly detection using active models"""
help = 'Run anomaly detection using all active anomaly detection models'
def add_arguments(self, parser):
parser.add_argument(
'--model-id',
type=str,
help='Run anomaly detection for a specific model ID only'
)
parser.add_argument(
'--time-window',
type=int,
default=24,
help='Time window in hours for anomaly detection (default: 24)'
)
def handle(self, *args, **options):
"""Handle the command execution"""
model_id = options.get('model_id')
time_window = options.get('time_window', 24)
try:
# Initialize anomaly detection service
anomaly_service = AnomalyDetectionService()
self.stdout.write('Starting anomaly detection...')
# Run anomaly detection
total_anomalies = anomaly_service.run_anomaly_detection(model_id)
if total_anomalies > 0:
self.stdout.write(
self.style.SUCCESS(f'✓ Detected {total_anomalies} anomalies')
)
else:
self.stdout.write(
self.style.WARNING('⚠ No anomalies detected')
)
# Get summary
summary = anomaly_service.get_anomaly_summary(time_window)
self.stdout.write('\nAnomaly Summary:')
self.stdout.write(f' Total anomalies: {summary["total_anomalies"]}')
self.stdout.write(f' Critical: {summary["critical_anomalies"]}')
self.stdout.write(f' High: {summary["high_anomalies"]}')
self.stdout.write(f' Medium: {summary["medium_anomalies"]}')
self.stdout.write(f' Low: {summary["low_anomalies"]}')
self.stdout.write(f' Unresolved: {summary["unresolved_anomalies"]}')
self.stdout.write(f' False positive rate: {summary["false_positive_rate"]:.2f}%')
except Exception as e:
raise CommandError(f'Error running anomaly detection: {str(e)}')

View File

@@ -0,0 +1,108 @@
"""
Management command to train predictive models
"""
from django.core.management.base import BaseCommand, CommandError
from analytics_predictive_insights.models import PredictiveModel
from analytics_predictive_insights.ml.predictive_models import PredictiveModelService
class Command(BaseCommand):
"""Train predictive models"""
help = 'Train predictive models that are in training status'
def add_arguments(self, parser):
parser.add_argument(
'--model-id',
type=str,
help='Train a specific model ID only'
)
parser.add_argument(
'--force',
action='store_true',
help='Force retraining of active models'
)
def handle(self, *args, **options):
"""Handle the command execution"""
model_id = options.get('model_id')
force = options.get('force', False)
try:
# Initialize predictive model service
model_service = PredictiveModelService()
# Get models to train
if model_id:
models = PredictiveModel.objects.filter(id=model_id)
if not models.exists():
raise CommandError(f'No model found with ID: {model_id}')
else:
if force:
models = PredictiveModel.objects.filter(
model_type__in=[
'INCIDENT_PREDICTION',
'SEVERITY_PREDICTION',
'RESOLUTION_TIME_PREDICTION',
'COST_PREDICTION'
]
)
else:
models = PredictiveModel.objects.filter(status='TRAINING')
self.stdout.write(f'Training {models.count()} models...')
total_trained = 0
total_failed = 0
for model in models:
try:
self.stdout.write(f'Training model: {model.name}...')
result = model_service.train_model(str(model.id))
if result['success']:
total_trained += 1
self.stdout.write(
self.style.SUCCESS(f'✓ Successfully trained {model.name}')
)
# Display metrics
if 'metrics' in result:
metrics = result['metrics']
self.stdout.write(f' Accuracy: {metrics.get("accuracy", "N/A")}')
self.stdout.write(f' Precision: {metrics.get("precision", "N/A")}')
self.stdout.write(f' Recall: {metrics.get("recall", "N/A")}')
self.stdout.write(f' F1 Score: {metrics.get("f1_score", "N/A")}')
self.stdout.write(f' R2 Score: {metrics.get("r2_score", "N/A")}')
self.stdout.write(f' Training samples: {result.get("training_samples", "N/A")}')
self.stdout.write(f' Training duration: {result.get("training_duration", "N/A")} seconds')
else:
total_failed += 1
self.stdout.write(
self.style.ERROR(f'✗ Failed to train {model.name}: {result.get("error", "Unknown error")}')
)
except Exception as e:
total_failed += 1
self.stdout.write(
self.style.ERROR(f'✗ Error training {model.name}: {str(e)}')
)
self.stdout.write('\nTraining Summary:')
self.stdout.write(f' Successfully trained: {total_trained}')
self.stdout.write(f' Failed: {total_failed}')
if total_trained > 0:
self.stdout.write(
self.style.SUCCESS(f'✓ Training completed successfully')
)
else:
self.stdout.write(
self.style.WARNING('⚠ No models were successfully trained')
)
except Exception as e:
raise CommandError(f'Error executing command: {str(e)}')