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

261 lines
10 KiB
Python

from django.contrib import admin
from django.utils.html import format_html
from django.urls import reverse
from django.utils.safestring import mark_safe
from .models import (
Postmortem, KnowledgeBaseArticle, IncidentRecommendation,
LearningPattern, KnowledgeBaseUsage, AutomatedPostmortemGeneration
)
@admin.register(Postmortem)
class PostmortemAdmin(admin.ModelAdmin):
list_display = [
'title', 'incident_link', 'status', 'severity', 'owner',
'completion_percentage', 'is_overdue', 'created_at'
]
list_filter = ['status', 'severity', 'is_automated', 'created_at']
search_fields = ['title', 'incident__title', 'owner__username']
readonly_fields = ['id', 'created_at', 'updated_at', 'completion_percentage', 'is_overdue']
fieldsets = (
('Basic Information', {
'fields': ('id', 'title', 'incident', 'status', 'severity')
}),
('Content', {
'fields': ('executive_summary', 'timeline', 'root_cause_analysis',
'impact_assessment', 'lessons_learned', 'action_items')
}),
('Automation', {
'fields': ('is_automated', 'generation_confidence', 'auto_generated_sections')
}),
('Workflow', {
'fields': ('owner', 'reviewers', 'approver', 'due_date')
}),
('Context', {
'fields': ('related_incidents', 'affected_services', 'affected_teams')
}),
('Timestamps', {
'fields': ('created_at', 'updated_at', 'published_at'),
'classes': ('collapse',)
})
)
filter_horizontal = ['reviewers', 'related_incidents']
def incident_link(self, obj):
if obj.incident:
url = reverse('admin:incident_intelligence_incident_change', args=[obj.incident.id])
return format_html('<a href="{}">{}</a>', url, obj.incident.title)
return '-'
incident_link.short_description = 'Incident'
def completion_percentage(self, obj):
percentage = obj.get_completion_percentage()
color = 'green' if percentage >= 80 else 'orange' if percentage >= 50 else 'red'
return format_html(
'<span style="color: {};">{:.1f}%</span>',
color, percentage
)
completion_percentage.short_description = 'Completion'
def is_overdue(self, obj):
if obj.is_overdue:
return format_html('<span style="color: red;">Yes</span>')
return format_html('<span style="color: green;">No</span>')
is_overdue.short_description = 'Overdue'
@admin.register(KnowledgeBaseArticle)
class KnowledgeBaseArticleAdmin(admin.ModelAdmin):
list_display = [
'title', 'article_type', 'category', 'status', 'view_count',
'author', 'is_due_for_review', 'created_at'
]
list_filter = ['article_type', 'category', 'status', 'difficulty_level', 'is_featured', 'created_at']
search_fields = ['title', 'content', 'summary', 'tags', 'author__username']
readonly_fields = ['id', 'created_at', 'updated_at', 'view_count', 'is_due_for_review']
fieldsets = (
('Basic Information', {
'fields': ('id', 'title', 'slug', 'article_type', 'category', 'subcategory')
}),
('Content', {
'fields': ('content', 'summary', 'tags', 'search_keywords')
}),
('Classification', {
'fields': ('related_services', 'related_components', 'difficulty_level')
}),
('Status & Workflow', {
'fields': ('status', 'is_featured', 'author', 'last_updated_by', 'maintainer')
}),
('Review Schedule', {
'fields': ('last_reviewed', 'next_review_due', 'is_due_for_review')
}),
('External Links', {
'fields': ('confluence_url', 'wiki_url', 'external_references'),
'classes': ('collapse',)
}),
('Relationships', {
'fields': ('related_incidents', 'source_postmortems'),
'classes': ('collapse',)
}),
('Statistics', {
'fields': ('view_count', 'created_at', 'updated_at'),
'classes': ('collapse',)
})
)
filter_horizontal = ['related_incidents', 'source_postmortems']
def is_due_for_review(self, obj):
if obj.is_due_for_review():
return format_html('<span style="color: red;">Yes</span>')
return format_html('<span style="color: green;">No</span>')
is_due_for_review.short_description = 'Due for Review'
@admin.register(IncidentRecommendation)
class IncidentRecommendationAdmin(admin.ModelAdmin):
list_display = [
'title', 'incident_link', 'recommendation_type', 'confidence_level',
'similarity_score', 'is_applied', 'created_at'
]
list_filter = ['recommendation_type', 'confidence_level', 'is_applied', 'created_at']
search_fields = ['title', 'description', 'incident__title', 'reasoning']
readonly_fields = ['id', 'created_at', 'updated_at']
fieldsets = (
('Basic Information', {
'fields': ('id', 'incident', 'recommendation_type', 'title', 'description')
}),
('Scores', {
'fields': ('similarity_score', 'confidence_level', 'confidence_score')
}),
('Related Objects', {
'fields': ('related_incident', 'knowledge_article', 'suggested_expert')
}),
('Recommendation Details', {
'fields': ('suggested_actions', 'expected_outcome', 'time_to_implement')
}),
('Usage Tracking', {
'fields': ('is_applied', 'applied_at', 'applied_by', 'effectiveness_rating')
}),
('AI Analysis', {
'fields': ('reasoning', 'matching_factors', 'model_version'),
'classes': ('collapse',)
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
})
)
def incident_link(self, obj):
if obj.incident:
url = reverse('admin:incident_intelligence_incident_change', args=[obj.incident.id])
return format_html('<a href="{}">{}</a>', url, obj.incident.title)
return '-'
incident_link.short_description = 'Incident'
@admin.register(LearningPattern)
class LearningPatternAdmin(admin.ModelAdmin):
list_display = [
'name', 'pattern_type', 'frequency', 'success_rate',
'confidence_score', 'is_validated', 'times_applied'
]
list_filter = ['pattern_type', 'is_validated', 'created_at']
search_fields = ['name', 'description', 'validated_by__username']
readonly_fields = ['id', 'created_at', 'updated_at']
fieldsets = (
('Basic Information', {
'fields': ('id', 'name', 'pattern_type', 'description')
}),
('Pattern Characteristics', {
'fields': ('frequency', 'success_rate', 'confidence_score')
}),
('Pattern Details', {
'fields': ('triggers', 'actions', 'outcomes')
}),
('Source Data', {
'fields': ('source_incidents', 'source_postmortems'),
'classes': ('collapse',)
}),
('Validation', {
'fields': ('is_validated', 'validated_by', 'validation_notes')
}),
('Usage Tracking', {
'fields': ('times_applied', 'last_applied')
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
})
)
filter_horizontal = ['source_incidents', 'source_postmortems']
@admin.register(KnowledgeBaseUsage)
class KnowledgeBaseUsageAdmin(admin.ModelAdmin):
list_display = [
'user', 'usage_type', 'article_link', 'recommendation_link',
'incident_link', 'created_at'
]
list_filter = ['usage_type', 'created_at']
search_fields = ['user__username', 'knowledge_article__title', 'recommendation__title']
readonly_fields = ['id', 'created_at']
def article_link(self, obj):
if obj.knowledge_article:
url = reverse('admin:knowledge_learning_knowledgebasearticle_change', args=[obj.knowledge_article.id])
return format_html('<a href="{}">{}</a>', url, obj.knowledge_article.title)
return '-'
article_link.short_description = 'Article'
def recommendation_link(self, obj):
if obj.recommendation:
url = reverse('admin:knowledge_learning_incidentrecommendation_change', args=[obj.recommendation.id])
return format_html('<a href="{}">{}</a>', url, obj.recommendation.title)
return '-'
recommendation_link.short_description = 'Recommendation'
def incident_link(self, obj):
if obj.incident:
url = reverse('admin:incident_intelligence_incident_change', args=[obj.incident.id])
return format_html('<a href="{}">{}</a>', url, obj.incident.title)
return '-'
incident_link.short_description = 'Incident'
@admin.register(AutomatedPostmortemGeneration)
class AutomatedPostmortemGenerationAdmin(admin.ModelAdmin):
list_display = [
'incident_link', 'status', 'generation_trigger', 'processing_time',
'model_version', 'started_at'
]
list_filter = ['status', 'generation_trigger', 'model_version', 'started_at']
search_fields = ['incident__title', 'error_message']
readonly_fields = ['id', 'started_at', 'completed_at']
fieldsets = (
('Basic Information', {
'fields': ('id', 'incident', 'status', 'generation_trigger')
}),
('Input Data', {
'fields': ('incident_data', 'timeline_data', 'log_data'),
'classes': ('collapse',)
}),
('Generation Results', {
'fields': ('generated_content', 'confidence_scores', 'quality_metrics', 'generated_postmortem'),
'classes': ('collapse',)
}),
('Processing Details', {
'fields': ('processing_time', 'model_version', 'error_message')
}),
('Timestamps', {
'fields': ('started_at', 'completed_at')
})
)
def incident_link(self, obj):
if obj.incident:
url = reverse('admin:incident_intelligence_incident_change', args=[obj.incident.id])
return format_html('<a href="{}">{}</a>', url, obj.incident.title)
return '-'
incident_link.short_description = 'Incident'