Updates
This commit is contained in:
1
ETB-API/sla_oncall/serializers/__init__.py
Normal file
1
ETB-API/sla_oncall/serializers/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Serializers for SLA & On-Call Management
|
||||
Binary file not shown.
BIN
ETB-API/sla_oncall/serializers/__pycache__/sla.cpython-312.pyc
Normal file
BIN
ETB-API/sla_oncall/serializers/__pycache__/sla.cpython-312.pyc
Normal file
Binary file not shown.
281
ETB-API/sla_oncall/serializers/sla.py
Normal file
281
ETB-API/sla_oncall/serializers/sla.py
Normal file
@@ -0,0 +1,281 @@
|
||||
"""
|
||||
Serializers for SLA & On-Call Management
|
||||
"""
|
||||
from datetime import timedelta
|
||||
from rest_framework import serializers
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from sla_oncall.models import (
|
||||
BusinessHours,
|
||||
SLADefinition,
|
||||
EscalationPolicy,
|
||||
OnCallRotation,
|
||||
OnCallAssignment,
|
||||
SLAInstance,
|
||||
EscalationInstance,
|
||||
NotificationTemplate,
|
||||
)
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class BusinessHoursSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for BusinessHours model"""
|
||||
|
||||
class Meta:
|
||||
model = BusinessHours
|
||||
fields = [
|
||||
'id', 'name', 'description', 'timezone',
|
||||
'weekday_start', 'weekday_end', 'weekend_start', 'weekend_end',
|
||||
'day_overrides', 'holiday_calendar',
|
||||
'is_active', 'is_default',
|
||||
'created_by', 'created_at', 'updated_at'
|
||||
]
|
||||
read_only_fields = ['id', 'created_at', 'updated_at']
|
||||
|
||||
def validate(self, data):
|
||||
"""Validate business hours configuration"""
|
||||
if data.get('weekday_start') >= data.get('weekday_end'):
|
||||
raise serializers.ValidationError(
|
||||
"Weekday start time must be before weekday end time"
|
||||
)
|
||||
|
||||
if data.get('weekend_start') >= data.get('weekend_end'):
|
||||
raise serializers.ValidationError(
|
||||
"Weekend start time must be before weekend end time"
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class SLADefinitionSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for SLADefinition model"""
|
||||
|
||||
business_hours_name = serializers.CharField(
|
||||
source='business_hours.name',
|
||||
read_only=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = SLADefinition
|
||||
fields = [
|
||||
'id', 'name', 'description', 'sla_type',
|
||||
'incident_categories', 'incident_severities', 'incident_priorities',
|
||||
'target_duration_minutes', 'business_hours_only', 'business_hours',
|
||||
'business_hours_name',
|
||||
'escalation_enabled', 'escalation_threshold_percent',
|
||||
'is_active', 'is_default',
|
||||
'created_by', 'created_at', 'updated_at'
|
||||
]
|
||||
read_only_fields = ['id', 'created_at', 'updated_at']
|
||||
|
||||
def validate_escalation_threshold_percent(self, value):
|
||||
"""Validate escalation threshold percentage"""
|
||||
if not (0 <= value <= 100):
|
||||
raise serializers.ValidationError(
|
||||
"Escalation threshold must be between 0 and 100 percent"
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
class EscalationPolicySerializer(serializers.ModelSerializer):
|
||||
"""Serializer for EscalationPolicy model"""
|
||||
|
||||
class Meta:
|
||||
model = EscalationPolicy
|
||||
fields = [
|
||||
'id', 'name', 'description', 'escalation_type', 'trigger_condition',
|
||||
'incident_severities', 'incident_categories',
|
||||
'trigger_delay_minutes', 'escalation_steps',
|
||||
'notification_channels', 'notification_templates',
|
||||
'is_active', 'is_default',
|
||||
'created_by', 'created_at', 'updated_at'
|
||||
]
|
||||
read_only_fields = ['id', 'created_at', 'updated_at']
|
||||
|
||||
|
||||
class OnCallRotationSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for OnCallRotation model"""
|
||||
|
||||
current_oncall = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = OnCallRotation
|
||||
fields = [
|
||||
'id', 'name', 'description', 'rotation_type', 'status',
|
||||
'team_name', 'team_description',
|
||||
'schedule_config', 'timezone',
|
||||
'external_system', 'external_system_id', 'integration_config',
|
||||
'current_oncall',
|
||||
'created_by', 'created_at', 'updated_at'
|
||||
]
|
||||
read_only_fields = ['id', 'created_at', 'updated_at', 'current_oncall']
|
||||
|
||||
def get_current_oncall(self, obj):
|
||||
"""Get current on-call person"""
|
||||
current_assignment = obj.get_current_oncall()
|
||||
if current_assignment:
|
||||
return {
|
||||
'user_id': current_assignment.user.id,
|
||||
'username': current_assignment.user.username,
|
||||
'start_time': current_assignment.start_time,
|
||||
'end_time': current_assignment.end_time
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
class OnCallAssignmentSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for OnCallAssignment model"""
|
||||
|
||||
user_name = serializers.CharField(source='user.username', read_only=True)
|
||||
user_email = serializers.CharField(source='user.email', read_only=True)
|
||||
rotation_name = serializers.CharField(source='rotation.name', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = OnCallAssignment
|
||||
fields = [
|
||||
'id', 'rotation', 'rotation_name', 'user', 'user_name', 'user_email',
|
||||
'start_time', 'end_time', 'status',
|
||||
'handoff_notes', 'handed_off_from', 'handoff_time',
|
||||
'incidents_handled', 'response_time_avg',
|
||||
'created_at', 'updated_at'
|
||||
]
|
||||
read_only_fields = [
|
||||
'id', 'created_at', 'updated_at', 'user_name', 'user_email', 'rotation_name'
|
||||
]
|
||||
|
||||
def validate(self, data):
|
||||
"""Validate assignment times"""
|
||||
if data.get('start_time') >= data.get('end_time'):
|
||||
raise serializers.ValidationError(
|
||||
"Start time must be before end time"
|
||||
)
|
||||
return data
|
||||
|
||||
|
||||
class SLAInstanceSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for SLAInstance model"""
|
||||
|
||||
incident_title = serializers.CharField(source='incident.title', read_only=True)
|
||||
sla_definition_name = serializers.CharField(source='sla_definition.name', read_only=True)
|
||||
is_breached = serializers.BooleanField(read_only=True)
|
||||
time_remaining = serializers.DurationField(read_only=True)
|
||||
breach_time = serializers.DurationField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = SLAInstance
|
||||
fields = [
|
||||
'id', 'sla_definition', 'sla_definition_name', 'incident', 'incident_title',
|
||||
'status', 'target_time',
|
||||
'started_at', 'met_at', 'breached_at',
|
||||
'escalation_policy', 'escalation_triggered', 'escalation_triggered_at',
|
||||
'escalation_level', 'response_time', 'resolution_time',
|
||||
'is_breached', 'time_remaining', 'breach_time',
|
||||
'created_at', 'updated_at'
|
||||
]
|
||||
read_only_fields = [
|
||||
'id', 'created_at', 'updated_at', 'started_at',
|
||||
'is_breached', 'time_remaining', 'breach_time',
|
||||
'incident_title', 'sla_definition_name'
|
||||
]
|
||||
|
||||
|
||||
class EscalationInstanceSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for EscalationInstance model"""
|
||||
|
||||
incident_title = serializers.CharField(source='incident.title', read_only=True)
|
||||
escalation_policy_name = serializers.CharField(source='escalation_policy.name', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = EscalationInstance
|
||||
fields = [
|
||||
'id', 'escalation_policy', 'escalation_policy_name',
|
||||
'incident', 'incident_title', 'sla_instance',
|
||||
'status', 'escalation_level', 'current_step',
|
||||
'triggered_at', 'acknowledged_at', 'resolved_at',
|
||||
'notifications_sent', 'actions_taken',
|
||||
'created_at', 'updated_at'
|
||||
]
|
||||
read_only_fields = [
|
||||
'id', 'created_at', 'updated_at',
|
||||
'incident_title', 'escalation_policy_name'
|
||||
]
|
||||
|
||||
|
||||
class NotificationTemplateSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for NotificationTemplate model"""
|
||||
|
||||
class Meta:
|
||||
model = NotificationTemplate
|
||||
fields = [
|
||||
'id', 'name', 'template_type', 'channel_type',
|
||||
'subject_template', 'body_template', 'variables',
|
||||
'is_active', 'is_default',
|
||||
'created_by', 'created_at', 'updated_at'
|
||||
]
|
||||
read_only_fields = ['id', 'created_at', 'updated_at']
|
||||
|
||||
|
||||
class SLAInstanceCreateSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for creating SLA instances"""
|
||||
|
||||
class Meta:
|
||||
model = SLAInstance
|
||||
fields = [
|
||||
'sla_definition', 'incident', 'escalation_policy'
|
||||
]
|
||||
|
||||
def create(self, validated_data):
|
||||
"""Create SLA instance with calculated target time"""
|
||||
sla_instance = super().create(validated_data)
|
||||
|
||||
# Calculate target time based on SLA definition
|
||||
sla_definition = sla_instance.sla_definition
|
||||
incident = sla_instance.incident
|
||||
|
||||
if sla_definition.business_hours_only and sla_definition.business_hours:
|
||||
# Calculate target time considering business hours
|
||||
target_time = sla_definition.business_hours.calculate_target_time(
|
||||
incident.created_at,
|
||||
sla_definition.target_duration_minutes
|
||||
)
|
||||
else:
|
||||
# Simple time calculation
|
||||
target_time = incident.created_at + timedelta(
|
||||
minutes=sla_definition.target_duration_minutes
|
||||
)
|
||||
|
||||
sla_instance.target_time = target_time
|
||||
sla_instance.save()
|
||||
|
||||
return sla_instance
|
||||
|
||||
|
||||
class OnCallAssignmentCreateSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for creating on-call assignments"""
|
||||
|
||||
class Meta:
|
||||
model = OnCallAssignment
|
||||
fields = [
|
||||
'rotation', 'user', 'start_time', 'end_time', 'handoff_notes'
|
||||
]
|
||||
|
||||
def create(self, validated_data):
|
||||
"""Create on-call assignment with validation"""
|
||||
assignment = super().create(validated_data)
|
||||
|
||||
# Check for overlapping assignments
|
||||
overlapping = OnCallAssignment.objects.filter(
|
||||
rotation=assignment.rotation,
|
||||
user=assignment.user,
|
||||
status__in=['SCHEDULED', 'ACTIVE'],
|
||||
start_time__lt=assignment.end_time,
|
||||
end_time__gt=assignment.start_time
|
||||
).exclude(id=assignment.id)
|
||||
|
||||
if overlapping.exists():
|
||||
raise serializers.ValidationError(
|
||||
"This assignment overlaps with existing assignments for the same user"
|
||||
)
|
||||
|
||||
return assignment
|
||||
Reference in New Issue
Block a user