""" Management command to set up SLA & On-Call Management with default configurations """ from django.core.management.base import BaseCommand from django.contrib.auth import get_user_model from datetime import time, timedelta from django.utils import timezone from sla_oncall.models import ( BusinessHours, SLADefinition, EscalationPolicy, OnCallRotation, OnCallAssignment, NotificationTemplate, ) User = get_user_model() class Command(BaseCommand): help = 'Set up SLA & On-Call Management with default configurations' def add_arguments(self, parser): parser.add_argument( '--force', action='store_true', help='Force recreation of existing configurations', ) def handle(self, *args, **options): self.stdout.write('Setting up SLA & On-Call Management...') force = options['force'] # Create default business hours self.create_default_business_hours(force) # Create default SLA definitions self.create_default_sla_definitions(force) # Create default escalation policies self.create_default_escalation_policies(force) # Create default notification templates self.create_default_notification_templates(force) # Create sample on-call rotation (if users exist) self.create_sample_oncall_rotation(force) self.stdout.write( self.style.SUCCESS('Successfully set up SLA & On-Call Management!') ) def create_default_business_hours(self, force): """Create default business hours configurations""" self.stdout.write('Creating default business hours...') default_hours, created = BusinessHours.objects.get_or_create( name='Standard Business Hours', defaults={ 'description': 'Standard 9-5 business hours, Monday to Friday', 'timezone': 'UTC', 'weekday_start': time(9, 0), 'weekday_end': time(17, 0), 'weekend_start': time(10, 0), 'weekend_end': time(16, 0), 'is_active': True, 'is_default': True, } ) if created or force: self.stdout.write(f' ✓ Created/Updated: {default_hours.name}') # 24/7 business hours twenty_four_seven, created = BusinessHours.objects.get_or_create( name='24/7 Operations', defaults={ 'description': '24/7 operations - always business hours', 'timezone': 'UTC', 'weekday_start': time(0, 0), 'weekday_end': time(23, 59), 'weekend_start': time(0, 0), 'weekend_end': time(23, 59), 'is_active': True, 'is_default': False, } ) if created or force: self.stdout.write(f' ✓ Created/Updated: {twenty_four_seven.name}') def create_default_sla_definitions(self, force): """Create default SLA definitions""" self.stdout.write('Creating default SLA definitions...') # Critical incidents SLA critical_sla, created = SLADefinition.objects.get_or_create( name='Critical Incident Response', defaults={ 'description': 'SLA for critical and emergency incidents', 'sla_type': 'RESPONSE_TIME', 'incident_severities': ['CRITICAL', 'EMERGENCY'], 'incident_priorities': ['P1'], 'target_duration_minutes': 15, 'business_hours_only': False, 'escalation_enabled': True, 'escalation_threshold_percent': 75.0, 'is_active': True, 'is_default': False, } ) if created or force: self.stdout.write(f' ✓ Created/Updated: {critical_sla.name}') # High priority incidents SLA high_sla, created = SLADefinition.objects.get_or_create( name='High Priority Response', defaults={ 'description': 'SLA for high priority incidents', 'sla_type': 'RESPONSE_TIME', 'incident_severities': ['HIGH'], 'incident_priorities': ['P2'], 'target_duration_minutes': 30, 'business_hours_only': False, 'escalation_enabled': True, 'escalation_threshold_percent': 80.0, 'is_active': True, 'is_default': False, } ) if created or force: self.stdout.write(f' ✓ Created/Updated: {high_sla.name}') # Medium priority incidents SLA medium_sla, created = SLADefinition.objects.get_or_create( name='Medium Priority Response', defaults={ 'description': 'SLA for medium priority incidents during business hours', 'sla_type': 'RESPONSE_TIME', 'incident_severities': ['MEDIUM'], 'incident_priorities': ['P3'], 'target_duration_minutes': 120, 'business_hours_only': True, 'escalation_enabled': True, 'escalation_threshold_percent': 85.0, 'is_active': True, 'is_default': False, } ) if created or force: self.stdout.write(f' ✓ Created/Updated: {medium_sla.name}') # Resolution time SLA resolution_sla, created = SLADefinition.objects.get_or_create( name='Critical Incident Resolution', defaults={ 'description': 'SLA for resolving critical incidents', 'sla_type': 'RESOLUTION_TIME', 'incident_severities': ['CRITICAL', 'EMERGENCY'], 'incident_priorities': ['P1'], 'target_duration_minutes': 240, # 4 hours 'business_hours_only': False, 'escalation_enabled': True, 'escalation_threshold_percent': 90.0, 'is_active': True, 'is_default': False, } ) if created or force: self.stdout.write(f' ✓ Created/Updated: {resolution_sla.name}') def create_default_escalation_policies(self, force): """Create default escalation policies""" self.stdout.write('Creating default escalation policies...') # Critical escalation policy critical_escalation, created = EscalationPolicy.objects.get_or_create( name='Critical Incident Escalation', defaults={ 'description': 'Escalation policy for critical and emergency incidents', 'escalation_type': 'TIME_BASED', 'trigger_condition': 'SLA_THRESHOLD', 'incident_severities': ['CRITICAL', 'EMERGENCY'], 'trigger_delay_minutes': 0, 'escalation_steps': [ { 'level': 1, 'delay_minutes': 5, 'actions': ['notify_oncall', 'notify_manager'], 'channels': ['email', 'sms'] }, { 'level': 2, 'delay_minutes': 15, 'actions': ['notify_director', 'page_oncall'], 'channels': ['email', 'sms', 'phone'] }, { 'level': 3, 'delay_minutes': 30, 'actions': ['notify_executive', 'escalate_to_vendor'], 'channels': ['email', 'phone', 'webhook'] } ], 'notification_channels': ['email', 'sms', 'phone'], 'is_active': True, 'is_default': False, } ) if created or force: self.stdout.write(f' ✓ Created/Updated: {critical_escalation.name}') # Standard escalation policy standard_escalation, created = EscalationPolicy.objects.get_or_create( name='Standard Escalation', defaults={ 'description': 'Standard escalation policy for most incidents', 'escalation_type': 'TIME_BASED', 'trigger_condition': 'SLA_THRESHOLD', 'incident_severities': ['LOW', 'MEDIUM', 'HIGH'], 'trigger_delay_minutes': 5, 'escalation_steps': [ { 'level': 1, 'delay_minutes': 15, 'actions': ['notify_oncall'], 'channels': ['email'] }, { 'level': 2, 'delay_minutes': 30, 'actions': ['notify_manager'], 'channels': ['email', 'sms'] } ], 'notification_channels': ['email', 'sms'], 'is_active': True, 'is_default': True, } ) if created or force: self.stdout.write(f' ✓ Created/Updated: {standard_escalation.name}') def create_default_notification_templates(self, force): """Create default notification templates""" self.stdout.write('Creating default notification templates...') # Email escalation template email_escalation, created = NotificationTemplate.objects.get_or_create( name='Email Escalation Alert', template_type='ESCALATION', channel_type='EMAIL', defaults={ 'subject_template': 'URGENT: Incident #{incident_id} Escalated - {incident_title}', 'body_template': ''' Incident #{incident_id} has been escalated to Level {escalation_level}. Details: - Title: {incident_title} - Severity: {incident_severity} - Status: {incident_status} - Created: {incident_created_at} - SLA Target: {sla_target_time} - Current On-Call: {current_oncall} Please respond immediately. View incident: {incident_url} ''', 'variables': [ 'incident_id', 'incident_title', 'incident_severity', 'incident_status', 'incident_created_at', 'sla_target_time', 'current_oncall', 'incident_url', 'escalation_level' ], 'is_active': True, 'is_default': True, } ) if created or force: self.stdout.write(f' ✓ Created/Updated: {email_escalation.name}') # SMS escalation template sms_escalation, created = NotificationTemplate.objects.get_or_create( name='SMS Escalation Alert', template_type='ESCALATION', channel_type='SMS', defaults={ 'subject_template': '', 'body_template': 'URGENT: Incident #{incident_id} escalated to L{escalation_level}. {incident_title}. Respond now.', 'variables': ['incident_id', 'incident_title', 'escalation_level'], 'is_active': True, 'is_default': True, } ) if created or force: self.stdout.write(f' ✓ Created/Updated: {sms_escalation.name}') # SLA breach template sla_breach, created = NotificationTemplate.objects.get_or_create( name='SLA Breach Alert', template_type='SLA_BREACH', channel_type='EMAIL', defaults={ 'subject_template': 'SLA BREACH: Incident #{incident_id} - {incident_title}', 'body_template': ''' SLA BREACH ALERT The SLA for incident #{incident_id} has been breached. Details: - Incident: {incident_title} - SLA Type: {sla_type} - Target Time: {sla_target_time} - Breach Time: {breach_time} - Breach Duration: {breach_duration} Immediate action required. ''', 'variables': [ 'incident_id', 'incident_title', 'sla_type', 'sla_target_time', 'breach_time', 'breach_duration' ], 'is_active': True, 'is_default': True, } ) if created or force: self.stdout.write(f' ✓ Created/Updated: {sla_breach.name}') def create_sample_oncall_rotation(self, force): """Create a sample on-call rotation if users exist""" self.stdout.write('Creating sample on-call rotation...') # Check if we have any users if not User.objects.exists(): self.stdout.write(' ⚠ No users found. Skipping on-call rotation creation.') return # Get first user as admin admin_user = User.objects.first() # Create sample rotation sample_rotation, created = OnCallRotation.objects.get_or_create( name='Primary On-Call Rotation', defaults={ 'description': 'Primary on-call rotation for incident response', 'rotation_type': 'WEEKLY', 'status': 'ACTIVE', 'team_name': 'Incident Response Team', 'team_description': 'Primary team responsible for incident response', 'schedule_config': { 'rotation_length_days': 7, 'handoff_time': '09:00', 'timezone': 'UTC' }, 'timezone': 'UTC', 'external_system': 'INTERNAL', 'created_by': admin_user, } ) if created or force: self.stdout.write(f' ✓ Created/Updated: {sample_rotation.name}') # Create a sample assignment for the next week if User.objects.count() >= 2: users = list(User.objects.all()[:2]) start_time = timezone.now() end_time = start_time + timedelta(days=7) assignment, created = OnCallAssignment.objects.get_or_create( rotation=sample_rotation, user=users[0], start_time=start_time, end_time=end_time, defaults={ 'status': 'ACTIVE', } ) if created: self.stdout.write(f' ✓ Created assignment for {users[0].username}') else: self.stdout.write(f' ⚠ Rotation already exists: {sample_rotation.name}')