Files
ETB/ETB-API/analytics_predictive_insights/serializers/analytics.py
Iliyan Angelov 6b247e5b9f Updates
2025-09-19 11:58:53 +03:00

405 lines
17 KiB
Python

"""
Analytics & Predictive Insights serializers for Enterprise Incident Management API
Provides comprehensive serialization for KPIs, metrics, predictive models, and insights
"""
from rest_framework import serializers
from django.contrib.auth import get_user_model
from ..models import (
KPIMetric, KPIMeasurement, IncidentRecurrenceAnalysis, PredictiveModel,
AnomalyDetection, CostImpactAnalysis, DashboardConfiguration,
HeatmapData, PredictiveInsight
)
User = get_user_model()
class KPIMetricSerializer(serializers.ModelSerializer):
"""Serializer for KPI metrics"""
created_by_username = serializers.CharField(source='created_by.username', read_only=True)
measurement_count = serializers.SerializerMethodField()
latest_measurement = serializers.SerializerMethodField()
class Meta:
model = KPIMetric
fields = [
'id', 'name', 'description', 'metric_type', 'aggregation_type',
'incident_categories', 'incident_severities', 'incident_priorities',
'calculation_formula', 'time_window_hours', 'is_active',
'is_system_metric', 'created_by_username', 'created_at', 'updated_at',
'measurement_count', 'latest_measurement'
]
read_only_fields = ['id', 'created_at', 'updated_at']
def get_measurement_count(self, obj):
"""Get the number of measurements for this metric"""
return obj.measurements.count()
def get_latest_measurement(self, obj):
"""Get the latest measurement for this metric"""
latest = obj.measurements.first()
if latest:
return {
'value': str(latest.value),
'unit': latest.unit,
'calculated_at': latest.calculated_at,
'incident_count': latest.incident_count
}
return None
class KPIMeasurementSerializer(serializers.ModelSerializer):
"""Serializer for KPI measurements"""
metric_name = serializers.CharField(source='metric.name', read_only=True)
metric_type = serializers.CharField(source='metric.metric_type', read_only=True)
class Meta:
model = KPIMeasurement
fields = [
'id', 'metric', 'metric_name', 'metric_type', 'value', 'unit',
'measurement_period_start', 'measurement_period_end',
'incident_count', 'sample_size', 'metadata', 'calculated_at'
]
read_only_fields = ['id', 'calculated_at']
class IncidentRecurrenceAnalysisSerializer(serializers.ModelSerializer):
"""Serializer for incident recurrence analysis"""
primary_incident_title = serializers.CharField(source='primary_incident.title', read_only=True)
primary_incident_severity = serializers.CharField(source='primary_incident.severity', read_only=True)
recurring_incident_count = serializers.SerializerMethodField()
recurring_incident_titles = serializers.SerializerMethodField()
class Meta:
model = IncidentRecurrenceAnalysis
fields = [
'id', 'primary_incident', 'primary_incident_title', 'primary_incident_severity',
'recurring_incidents', 'recurring_incident_count', 'recurring_incident_titles',
'recurrence_type', 'confidence_score', 'recurrence_rate',
'common_keywords', 'common_categories', 'time_pattern',
'total_affected_users', 'total_downtime_hours', 'estimated_cost_impact',
'prevention_recommendations', 'automation_opportunities',
'is_resolved', 'resolution_actions', 'created_at', 'updated_at', 'model_version'
]
read_only_fields = ['id', 'created_at', 'updated_at']
def get_recurring_incident_count(self, obj):
"""Get the number of recurring incidents"""
return obj.recurring_incidents.count()
def get_recurring_incident_titles(self, obj):
"""Get titles of recurring incidents"""
return [incident.title for incident in obj.recurring_incidents.all()]
class PredictiveModelSerializer(serializers.ModelSerializer):
"""Serializer for predictive models"""
created_by_username = serializers.CharField(source='created_by.username', read_only=True)
insight_count = serializers.SerializerMethodField()
anomaly_detection_count = serializers.SerializerMethodField()
performance_summary = serializers.SerializerMethodField()
class Meta:
model = PredictiveModel
fields = [
'id', 'name', 'description', 'model_type', 'algorithm_type',
'model_config', 'feature_columns', 'target_column',
'training_data_period_days', 'min_training_samples',
'accuracy_score', 'precision_score', 'recall_score', 'f1_score',
'status', 'version', 'model_file_path',
'last_trained_at', 'training_duration_seconds', 'training_samples_count',
'auto_retrain_enabled', 'retrain_frequency_days', 'performance_threshold',
'created_by_username', 'created_at', 'updated_at',
'insight_count', 'anomaly_detection_count', 'performance_summary'
]
read_only_fields = ['id', 'created_at', 'updated_at']
def get_insight_count(self, obj):
"""Get the number of insights generated by this model"""
return obj.insights.count()
def get_anomaly_detection_count(self, obj):
"""Get the number of anomaly detections by this model"""
return obj.anomaly_detections.count()
def get_performance_summary(self, obj):
"""Get a summary of model performance metrics"""
return {
'accuracy': obj.accuracy_score,
'precision': obj.precision_score,
'recall': obj.recall_score,
'f1_score': obj.f1_score,
'overall_health': self._calculate_overall_health(obj)
}
def _calculate_overall_health(self, obj):
"""Calculate overall model health score"""
if not all([obj.accuracy_score, obj.precision_score, obj.recall_score, obj.f1_score]):
return 'Unknown'
avg_score = (obj.accuracy_score + obj.precision_score + obj.recall_score + obj.f1_score) / 4
if avg_score >= 0.9:
return 'Excellent'
elif avg_score >= 0.8:
return 'Good'
elif avg_score >= 0.7:
return 'Fair'
else:
return 'Poor'
class AnomalyDetectionSerializer(serializers.ModelSerializer):
"""Serializer for anomaly detection results"""
model_name = serializers.CharField(source='model.name', read_only=True)
model_type = serializers.CharField(source='model.model_type', read_only=True)
resolved_by_username = serializers.CharField(source='resolved_by.username', read_only=True)
related_incident_count = serializers.SerializerMethodField()
related_incident_titles = serializers.SerializerMethodField()
time_since_detection = serializers.SerializerMethodField()
class Meta:
model = AnomalyDetection
fields = [
'id', 'model', 'model_name', 'model_type',
'anomaly_type', 'severity', 'status',
'confidence_score', 'anomaly_score', 'threshold_used',
'detected_at', 'time_window_start', 'time_window_end',
'related_incidents', 'related_incident_count', 'related_incident_titles',
'affected_services', 'affected_metrics',
'description', 'root_cause_analysis', 'impact_assessment',
'actions_taken', 'resolved_at', 'resolved_by', 'resolved_by_username',
'metadata', 'time_since_detection'
]
read_only_fields = ['id', 'detected_at']
def get_related_incident_count(self, obj):
"""Get the number of related incidents"""
return obj.related_incidents.count()
def get_related_incident_titles(self, obj):
"""Get titles of related incidents"""
return [incident.title for incident in obj.related_incidents.all()]
def get_time_since_detection(self, obj):
"""Get time elapsed since anomaly detection"""
from django.utils import timezone
return timezone.now() - obj.detected_at
class CostImpactAnalysisSerializer(serializers.ModelSerializer):
"""Serializer for cost impact analysis"""
incident_title = serializers.CharField(source='incident.title', read_only=True)
incident_severity = serializers.CharField(source='incident.severity', read_only=True)
validated_by_username = serializers.CharField(source='validated_by.username', read_only=True)
cost_per_hour = serializers.SerializerMethodField()
cost_per_user = serializers.SerializerMethodField()
class Meta:
model = CostImpactAnalysis
fields = [
'id', 'incident', 'incident_title', 'incident_severity',
'cost_type', 'cost_amount', 'currency',
'calculation_method', 'calculation_details',
'downtime_hours', 'affected_users', 'revenue_impact',
'business_unit', 'service_tier',
'is_validated', 'validated_by', 'validated_by_username',
'validated_at', 'validation_notes',
'created_at', 'updated_at',
'cost_per_hour', 'cost_per_user'
]
read_only_fields = ['id', 'created_at', 'updated_at']
def get_cost_per_hour(self, obj):
"""Calculate cost per hour of downtime"""
if obj.downtime_hours and obj.downtime_hours > 0:
return float(obj.cost_amount / obj.downtime_hours)
return None
def get_cost_per_user(self, obj):
"""Calculate cost per affected user"""
if obj.affected_users and obj.affected_users > 0:
return float(obj.cost_amount / obj.affected_users)
return None
class DashboardConfigurationSerializer(serializers.ModelSerializer):
"""Serializer for dashboard configurations"""
created_by_username = serializers.CharField(source='created_by.username', read_only=True)
allowed_user_count = serializers.SerializerMethodField()
allowed_usernames = serializers.SerializerMethodField()
widget_count = serializers.SerializerMethodField()
class Meta:
model = DashboardConfiguration
fields = [
'id', 'name', 'description', 'dashboard_type',
'layout_config', 'widget_configs', 'widget_count',
'is_public', 'allowed_users', 'allowed_user_count', 'allowed_usernames',
'allowed_roles', 'auto_refresh_enabled', 'refresh_interval_seconds',
'is_active', 'created_by_username', 'created_at', 'updated_at'
]
read_only_fields = ['id', 'created_at', 'updated_at']
def get_allowed_user_count(self, obj):
"""Get the number of allowed users"""
return obj.allowed_users.count()
def get_allowed_usernames(self, obj):
"""Get usernames of allowed users"""
return [user.username for user in obj.allowed_users.all()]
def get_widget_count(self, obj):
"""Get the number of widgets in this dashboard"""
return len(obj.widget_configs) if obj.widget_configs else 0
class HeatmapDataSerializer(serializers.ModelSerializer):
"""Serializer for heatmap data"""
data_point_count = serializers.SerializerMethodField()
time_span_hours = serializers.SerializerMethodField()
class Meta:
model = HeatmapData
fields = [
'id', 'name', 'heatmap_type', 'time_period_start', 'time_period_end',
'time_granularity', 'data_points', 'data_point_count',
'color_scheme', 'aggregation_method', 'time_span_hours',
'created_at', 'updated_at'
]
read_only_fields = ['id', 'created_at', 'updated_at']
def get_data_point_count(self, obj):
"""Get the number of data points"""
return len(obj.data_points) if obj.data_points else 0
def get_time_span_hours(self, obj):
"""Get the time span in hours"""
from django.utils import timezone
delta = obj.time_period_end - obj.time_period_start
return delta.total_seconds() / 3600
class PredictiveInsightSerializer(serializers.ModelSerializer):
"""Serializer for predictive insights"""
model_name = serializers.CharField(source='model.name', read_only=True)
model_type = serializers.CharField(source='model.model_type', read_only=True)
acknowledged_by_username = serializers.CharField(source='acknowledged_by.username', read_only=True)
related_incident_count = serializers.SerializerMethodField()
related_incident_titles = serializers.SerializerMethodField()
time_until_expiry = serializers.SerializerMethodField()
is_expired = serializers.SerializerMethodField()
class Meta:
model = PredictiveInsight
fields = [
'id', 'model', 'model_name', 'model_type',
'insight_type', 'title', 'description',
'confidence_level', 'confidence_score',
'predicted_value', 'prediction_horizon', 'prediction_date',
'input_features', 'supporting_evidence',
'related_incidents', 'related_incident_count', 'related_incident_titles',
'affected_services', 'recommendations', 'risk_assessment',
'is_acknowledged', 'acknowledged_by', 'acknowledged_by_username',
'acknowledged_at', 'is_validated', 'actual_value', 'validation_accuracy',
'generated_at', 'expires_at', 'time_until_expiry', 'is_expired'
]
read_only_fields = ['id', 'generated_at']
def get_related_incident_count(self, obj):
"""Get the number of related incidents"""
return obj.related_incidents.count()
def get_related_incident_titles(self, obj):
"""Get titles of related incidents"""
return [incident.title for incident in obj.related_incidents.all()]
def get_time_until_expiry(self, obj):
"""Get time until insight expires"""
from django.utils import timezone
return obj.expires_at - timezone.now()
def get_is_expired(self, obj):
"""Check if insight has expired"""
return obj.is_expired
# Summary and aggregation serializers
class KPISummarySerializer(serializers.Serializer):
"""Serializer for KPI summary data"""
metric_type = serializers.CharField()
metric_name = serializers.CharField()
current_value = serializers.DecimalField(max_digits=15, decimal_places=4)
unit = serializers.CharField()
trend = serializers.CharField() # 'up', 'down', 'stable'
trend_percentage = serializers.DecimalField(max_digits=5, decimal_places=2)
period_start = serializers.DateTimeField()
period_end = serializers.DateTimeField()
incident_count = serializers.IntegerField()
target_value = serializers.DecimalField(max_digits=15, decimal_places=4, allow_null=True)
target_met = serializers.BooleanField()
class AnomalySummarySerializer(serializers.Serializer):
"""Serializer for anomaly summary data"""
total_anomalies = serializers.IntegerField()
critical_anomalies = serializers.IntegerField()
high_anomalies = serializers.IntegerField()
medium_anomalies = serializers.IntegerField()
low_anomalies = serializers.IntegerField()
unresolved_anomalies = serializers.IntegerField()
false_positive_rate = serializers.DecimalField(max_digits=5, decimal_places=2)
average_resolution_time = serializers.DurationField()
class CostSummarySerializer(serializers.Serializer):
"""Serializer for cost summary data"""
total_cost = serializers.DecimalField(max_digits=15, decimal_places=2)
currency = serializers.CharField()
downtime_cost = serializers.DecimalField(max_digits=15, decimal_places=2)
lost_revenue = serializers.DecimalField(max_digits=15, decimal_places=2)
penalty_cost = serializers.DecimalField(max_digits=15, decimal_places=2)
resource_cost = serializers.DecimalField(max_digits=15, decimal_places=2)
total_downtime_hours = serializers.DecimalField(max_digits=10, decimal_places=2)
total_affected_users = serializers.IntegerField()
cost_per_hour = serializers.DecimalField(max_digits=10, decimal_places=2)
cost_per_user = serializers.DecimalField(max_digits=10, decimal_places=2)
class PredictiveInsightSummarySerializer(serializers.Serializer):
"""Serializer for predictive insight summary data"""
total_insights = serializers.IntegerField()
high_confidence_insights = serializers.IntegerField()
medium_confidence_insights = serializers.IntegerField()
low_confidence_insights = serializers.IntegerField()
acknowledged_insights = serializers.IntegerField()
validated_insights = serializers.IntegerField()
expired_insights = serializers.IntegerField()
average_accuracy = serializers.DecimalField(max_digits=5, decimal_places=2)
active_models = serializers.IntegerField()
class DashboardDataSerializer(serializers.Serializer):
"""Serializer for complete dashboard data"""
kpi_summary = KPISummarySerializer(many=True)
anomaly_summary = AnomalySummarySerializer()
cost_summary = CostSummarySerializer()
insight_summary = PredictiveInsightSummarySerializer()
recent_anomalies = AnomalyDetectionSerializer(many=True)
recent_insights = PredictiveInsightSerializer(many=True)
heatmap_data = HeatmapDataSerializer(many=True)
last_updated = serializers.DateTimeField()