293 lines
9.2 KiB
Python
293 lines
9.2 KiB
Python
"""
|
|
Admin configuration for SLA & On-Call Management
|
|
"""
|
|
from django.contrib import admin
|
|
from django.utils.html import format_html
|
|
from django.utils import timezone
|
|
from datetime import timedelta
|
|
|
|
from .models import (
|
|
BusinessHours,
|
|
SLADefinition,
|
|
EscalationPolicy,
|
|
OnCallRotation,
|
|
OnCallAssignment,
|
|
SLAInstance,
|
|
EscalationInstance,
|
|
NotificationTemplate,
|
|
)
|
|
|
|
|
|
@admin.register(BusinessHours)
|
|
class BusinessHoursAdmin(admin.ModelAdmin):
|
|
list_display = ['name', 'timezone', 'weekday_start', 'weekday_end', 'is_active', 'is_default']
|
|
list_filter = ['is_active', 'is_default', 'timezone']
|
|
search_fields = ['name', 'description']
|
|
ordering = ['name']
|
|
|
|
fieldsets = (
|
|
('Basic Information', {
|
|
'fields': ('name', 'description', 'timezone')
|
|
}),
|
|
('Business Hours', {
|
|
'fields': ('weekday_start', 'weekday_end', 'weekend_start', 'weekend_end')
|
|
}),
|
|
('Overrides', {
|
|
'fields': ('day_overrides', 'holiday_calendar'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
('Status', {
|
|
'fields': ('is_active', 'is_default')
|
|
}),
|
|
('Metadata', {
|
|
'fields': ('created_by', 'created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
readonly_fields = ['created_at', 'updated_at']
|
|
|
|
|
|
@admin.register(SLADefinition)
|
|
class SLADefinitionAdmin(admin.ModelAdmin):
|
|
list_display = [
|
|
'name', 'sla_type', 'target_duration_minutes',
|
|
'business_hours_only', 'is_active', 'is_default'
|
|
]
|
|
list_filter = ['sla_type', 'business_hours_only', 'is_active', 'is_default']
|
|
search_fields = ['name', 'description']
|
|
ordering = ['name']
|
|
|
|
fieldsets = (
|
|
('Basic Information', {
|
|
'fields': ('name', 'description', 'sla_type')
|
|
}),
|
|
('Targeting Criteria', {
|
|
'fields': ('incident_categories', 'incident_severities', 'incident_priorities')
|
|
}),
|
|
('SLA Configuration', {
|
|
'fields': ('target_duration_minutes', 'business_hours_only', 'business_hours')
|
|
}),
|
|
('Escalation', {
|
|
'fields': ('escalation_enabled', 'escalation_threshold_percent')
|
|
}),
|
|
('Status', {
|
|
'fields': ('is_active', 'is_default')
|
|
}),
|
|
('Metadata', {
|
|
'fields': ('created_by', 'created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
readonly_fields = ['created_at', 'updated_at']
|
|
|
|
|
|
@admin.register(EscalationPolicy)
|
|
class EscalationPolicyAdmin(admin.ModelAdmin):
|
|
list_display = [
|
|
'name', 'escalation_type', 'trigger_condition',
|
|
'trigger_delay_minutes', 'is_active'
|
|
]
|
|
list_filter = ['escalation_type', 'trigger_condition', 'is_active']
|
|
search_fields = ['name', 'description']
|
|
ordering = ['name']
|
|
|
|
fieldsets = (
|
|
('Basic Information', {
|
|
'fields': ('name', 'description', 'escalation_type', 'trigger_condition')
|
|
}),
|
|
('Targeting', {
|
|
'fields': ('incident_severities', 'incident_categories')
|
|
}),
|
|
('Configuration', {
|
|
'fields': ('trigger_delay_minutes', 'escalation_steps')
|
|
}),
|
|
('Notifications', {
|
|
'fields': ('notification_channels', 'notification_templates'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
('Status', {
|
|
'fields': ('is_active', 'is_default')
|
|
}),
|
|
('Metadata', {
|
|
'fields': ('created_by', 'created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
readonly_fields = ['created_at', 'updated_at']
|
|
|
|
|
|
@admin.register(OnCallRotation)
|
|
class OnCallRotationAdmin(admin.ModelAdmin):
|
|
list_display = [
|
|
'name', 'rotation_type', 'team_name', 'external_system', 'status'
|
|
]
|
|
list_filter = ['rotation_type', 'external_system', 'status']
|
|
search_fields = ['name', 'team_name', 'description']
|
|
ordering = ['name']
|
|
|
|
fieldsets = (
|
|
('Basic Information', {
|
|
'fields': ('name', 'description', 'rotation_type', 'status')
|
|
}),
|
|
('Team Configuration', {
|
|
'fields': ('team_name', 'team_description')
|
|
}),
|
|
('Schedule', {
|
|
'fields': ('schedule_config', 'timezone')
|
|
}),
|
|
('External Integration', {
|
|
'fields': ('external_system', 'external_system_id', 'integration_config'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
('Metadata', {
|
|
'fields': ('created_by', 'created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
readonly_fields = ['created_at', 'updated_at']
|
|
|
|
|
|
@admin.register(OnCallAssignment)
|
|
class OnCallAssignmentAdmin(admin.ModelAdmin):
|
|
list_display = [
|
|
'user', 'rotation', 'start_time', 'end_time', 'status', 'incidents_handled'
|
|
]
|
|
list_filter = ['status', 'rotation', 'start_time']
|
|
search_fields = ['user__username', 'user__email', 'rotation__name']
|
|
ordering = ['-start_time']
|
|
|
|
fieldsets = (
|
|
('Assignment Details', {
|
|
'fields': ('rotation', 'user', 'start_time', 'end_time', 'status')
|
|
}),
|
|
('Handoff Information', {
|
|
'fields': ('handoff_notes', 'handed_off_from', 'handoff_time'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
('Performance', {
|
|
'fields': ('incidents_handled', 'response_time_avg')
|
|
}),
|
|
('Metadata', {
|
|
'fields': ('created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
readonly_fields = ['created_at', 'updated_at']
|
|
|
|
|
|
@admin.register(SLAInstance)
|
|
class SLAInstanceAdmin(admin.ModelAdmin):
|
|
list_display = [
|
|
'incident', 'sla_definition', 'status', 'target_time',
|
|
'escalation_triggered', 'time_remaining_display'
|
|
]
|
|
list_filter = ['status', 'escalation_triggered', 'sla_definition__sla_type']
|
|
search_fields = ['incident__title', 'sla_definition__name']
|
|
ordering = ['-created_at']
|
|
|
|
fieldsets = (
|
|
('SLA Details', {
|
|
'fields': ('sla_definition', 'incident', 'status', 'target_time')
|
|
}),
|
|
('Timing', {
|
|
'fields': ('started_at', 'met_at', 'breached_at')
|
|
}),
|
|
('Escalation', {
|
|
'fields': ('escalation_policy', 'escalation_triggered',
|
|
'escalation_triggered_at', 'escalation_level')
|
|
}),
|
|
('Performance', {
|
|
'fields': ('response_time', 'resolution_time')
|
|
}),
|
|
('Metadata', {
|
|
'fields': ('created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
readonly_fields = ['created_at', 'updated_at', 'started_at']
|
|
|
|
def time_remaining_display(self, obj):
|
|
"""Display time remaining with color coding"""
|
|
if obj.status == 'ACTIVE':
|
|
remaining = obj.time_remaining
|
|
if remaining > timedelta(hours=1):
|
|
color = 'green'
|
|
elif remaining > timedelta(minutes=15):
|
|
color = 'orange'
|
|
else:
|
|
color = 'red'
|
|
|
|
return format_html(
|
|
'<span style="color: {};">{}</span>',
|
|
color,
|
|
str(remaining).split('.')[0] if remaining else 'Expired'
|
|
)
|
|
return '-'
|
|
time_remaining_display.short_description = 'Time Remaining'
|
|
|
|
|
|
@admin.register(EscalationInstance)
|
|
class EscalationInstanceAdmin(admin.ModelAdmin):
|
|
list_display = [
|
|
'incident', 'escalation_policy', 'status', 'escalation_level',
|
|
'triggered_at', 'current_step'
|
|
]
|
|
list_filter = ['status', 'escalation_level', 'escalation_policy__escalation_type']
|
|
search_fields = ['incident__title', 'escalation_policy__name']
|
|
ordering = ['-created_at']
|
|
|
|
fieldsets = (
|
|
('Escalation Details', {
|
|
'fields': ('escalation_policy', 'incident', 'sla_instance', 'status')
|
|
}),
|
|
('Progress', {
|
|
'fields': ('escalation_level', 'current_step')
|
|
}),
|
|
('Timing', {
|
|
'fields': ('triggered_at', 'acknowledged_at', 'resolved_at')
|
|
}),
|
|
('Actions', {
|
|
'fields': ('notifications_sent', 'actions_taken'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
('Metadata', {
|
|
'fields': ('created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
readonly_fields = ['created_at', 'updated_at']
|
|
|
|
|
|
@admin.register(NotificationTemplate)
|
|
class NotificationTemplateAdmin(admin.ModelAdmin):
|
|
list_display = [
|
|
'name', 'template_type', 'channel_type', 'is_active', 'is_default'
|
|
]
|
|
list_filter = ['template_type', 'channel_type', 'is_active', 'is_default']
|
|
search_fields = ['name', 'subject_template']
|
|
ordering = ['template_type', 'channel_type', 'name']
|
|
|
|
fieldsets = (
|
|
('Basic Information', {
|
|
'fields': ('name', 'template_type', 'channel_type')
|
|
}),
|
|
('Template Content', {
|
|
'fields': ('subject_template', 'body_template', 'variables')
|
|
}),
|
|
('Status', {
|
|
'fields': ('is_active', 'is_default')
|
|
}),
|
|
('Metadata', {
|
|
'fields': ('created_by', 'created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
readonly_fields = ['created_at', 'updated_at'] |