from django.db import models from django.contrib.auth import get_user_model from django.core.validators import MinValueValidator, MaxValueValidator from django.utils import timezone import uuid User = get_user_model() class Postmortem(models.Model): """Automated postmortem generation and management""" STATUS_CHOICES = [ ('DRAFT', 'Draft'), ('IN_REVIEW', 'In Review'), ('APPROVED', 'Approved'), ('PUBLISHED', 'Published'), ('ARCHIVED', 'Archived'), ] SEVERITY_CHOICES = [ ('LOW', 'Low'), ('MEDIUM', 'Medium'), ('HIGH', 'High'), ('CRITICAL', 'Critical'), ] id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) title = models.CharField(max_length=200) incident = models.ForeignKey( 'incident_intelligence.Incident', on_delete=models.CASCADE, related_name='postmortems', help_text="Primary incident this postmortem is about" ) # Postmortem content executive_summary = models.TextField(help_text="High-level summary for executives") timeline = models.JSONField(default=list, help_text="Detailed timeline of events") root_cause_analysis = models.TextField(help_text="Analysis of root causes") impact_assessment = models.TextField(help_text="Assessment of business and technical impact") lessons_learned = models.TextField(help_text="Key lessons learned from the incident") action_items = models.JSONField(default=list, help_text="List of action items to prevent recurrence") # Automated generation is_automated = models.BooleanField(default=True, help_text="Whether this postmortem was auto-generated") generation_confidence = models.FloatField( validators=[MinValueValidator(0.0), MaxValueValidator(1.0)], null=True, blank=True, help_text="AI confidence in postmortem quality (0.0-1.0)" ) auto_generated_sections = models.JSONField( default=list, help_text="List of sections that were auto-generated" ) # Status and workflow status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='DRAFT') severity = models.CharField(max_length=20, choices=SEVERITY_CHOICES) # Ownership and review owner = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='owned_postmortems') reviewers = models.ManyToManyField(User, related_name='reviewed_postmortems', blank=True) approver = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='approved_postmortems') # Timestamps created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) published_at = models.DateTimeField(null=True, blank=True) due_date = models.DateTimeField(null=True, blank=True, help_text="When this postmortem should be completed") # Related incidents and context related_incidents = models.ManyToManyField( 'incident_intelligence.Incident', related_name='related_postmortems', blank=True, help_text="Other incidents related to this postmortem" ) affected_services = models.JSONField(default=list, help_text="Services affected by the incident") affected_teams = models.JSONField(default=list, help_text="Teams involved in the incident") class Meta: ordering = ['-created_at'] indexes = [ models.Index(fields=['status', 'severity']), models.Index(fields=['incident', 'status']), models.Index(fields=['created_at']), ] def __str__(self): return f"Postmortem: {self.title}" @property def is_overdue(self): """Check if postmortem is overdue""" if self.due_date and self.status not in ['APPROVED', 'PUBLISHED']: return timezone.now() > self.due_date return False def get_completion_percentage(self): """Calculate completion percentage based on filled sections""" sections = [ self.executive_summary, self.timeline, self.root_cause_analysis, self.impact_assessment, self.lessons_learned, self.action_items, ] filled_sections = sum(1 for section in sections if section) return (filled_sections / len(sections)) * 100 class KnowledgeBaseArticle(models.Model): """Knowledge base articles for incident resolution and learning""" ARTICLE_TYPE_CHOICES = [ ('RUNBOOK', 'Runbook'), ('TROUBLESHOOTING', 'Troubleshooting Guide'), ('BEST_PRACTICE', 'Best Practice'), ('LESSON_LEARNED', 'Lesson Learned'), ('PROCEDURE', 'Procedure'), ('REFERENCE', 'Reference'), ('WIKI', 'Wiki Article'), ] STATUS_CHOICES = [ ('DRAFT', 'Draft'), ('REVIEW', 'Under Review'), ('APPROVED', 'Approved'), ('PUBLISHED', 'Published'), ('DEPRECATED', 'Deprecated'), ] id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) title = models.CharField(max_length=200) slug = models.SlugField(unique=True, help_text="URL-friendly identifier") # Content content = models.TextField(help_text="Main article content") summary = models.TextField(help_text="Brief summary of the article") tags = models.JSONField(default=list, help_text="Tags for categorization and search") # Classification article_type = models.CharField(max_length=20, choices=ARTICLE_TYPE_CHOICES) category = models.CharField(max_length=100, help_text="Primary category") subcategory = models.CharField(max_length=100, blank=True, null=True) # Related services and components related_services = models.JSONField(default=list, help_text="Services this article relates to") related_components = models.JSONField(default=list, help_text="Components this article relates to") # Status and workflow status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='DRAFT') is_featured = models.BooleanField(default=False, help_text="Whether this is a featured article") view_count = models.PositiveIntegerField(default=0, help_text="Number of times this article has been viewed") # Ownership and maintenance author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='authored_articles') last_updated_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='updated_articles') maintainer = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='maintained_articles') # Timestamps created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) last_reviewed = models.DateTimeField(null=True, blank=True) next_review_due = models.DateTimeField(null=True, blank=True) # Related incidents and postmortems related_incidents = models.ManyToManyField( 'incident_intelligence.Incident', related_name='knowledge_articles', blank=True, help_text="Incidents that led to or are related to this article" ) source_postmortems = models.ManyToManyField( Postmortem, related_name='generated_articles', blank=True, help_text="Postmortems that generated this article" ) # External integrations confluence_url = models.URLField(blank=True, null=True, help_text="Link to Confluence page") wiki_url = models.URLField(blank=True, null=True, help_text="Link to wiki page") external_references = models.JSONField(default=list, help_text="External reference links") # Search and discovery search_keywords = models.JSONField(default=list, help_text="Keywords for search optimization") difficulty_level = models.CharField(max_length=20, choices=[ ('BEGINNER', 'Beginner'), ('INTERMEDIATE', 'Intermediate'), ('ADVANCED', 'Advanced'), ('EXPERT', 'Expert'), ], default='INTERMEDIATE') class Meta: ordering = ['-updated_at', '-created_at'] indexes = [ models.Index(fields=['article_type', 'status']), models.Index(fields=['category', 'subcategory']), models.Index(fields=['status', 'is_featured']), models.Index(fields=['created_at']), ] def __str__(self): return f"KB Article: {self.title}" def increment_view_count(self): """Increment the view count""" self.view_count += 1 self.save(update_fields=['view_count']) def is_due_for_review(self): """Check if article is due for review""" if self.next_review_due: return timezone.now() > self.next_review_due return False class IncidentRecommendation(models.Model): """Recommendation engine for suggesting similar incidents and solutions""" RECOMMENDATION_TYPE_CHOICES = [ ('SIMILAR_INCIDENT', 'Similar Incident'), ('SOLUTION', 'Solution/Resolution'), ('KNOWLEDGE_ARTICLE', 'Knowledge Article'), ('RUNBOOK', 'Runbook'), ('EXPERT', 'Expert/Team'), ('PREVENTION', 'Prevention Strategy'), ] CONFIDENCE_LEVEL_CHOICES = [ ('LOW', 'Low'), ('MEDIUM', 'Medium'), ('HIGH', 'High'), ('VERY_HIGH', 'Very High'), ] id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) incident = models.ForeignKey( 'incident_intelligence.Incident', on_delete=models.CASCADE, related_name='recommendations', help_text="Incident for which this recommendation is made" ) # Recommendation details recommendation_type = models.CharField(max_length=20, choices=RECOMMENDATION_TYPE_CHOICES) title = models.CharField(max_length=200) description = models.TextField(help_text="Description of the recommendation") # Similarity and confidence similarity_score = models.FloatField( validators=[MinValueValidator(0.0), MaxValueValidator(1.0)], help_text="Similarity score between current and recommended incident (0.0-1.0)" ) confidence_level = models.CharField(max_length=20, choices=CONFIDENCE_LEVEL_CHOICES) confidence_score = models.FloatField( validators=[MinValueValidator(0.0), MaxValueValidator(1.0)], help_text="AI confidence in this recommendation (0.0-1.0)" ) # Related objects related_incident = models.ForeignKey( 'incident_intelligence.Incident', on_delete=models.CASCADE, null=True, blank=True, related_name='recommended_for', help_text="Related incident that this recommendation is based on" ) knowledge_article = models.ForeignKey( KnowledgeBaseArticle, on_delete=models.CASCADE, null=True, blank=True, help_text="Related knowledge base article" ) suggested_expert = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, blank=True, help_text="Suggested expert who can help with this incident" ) # Recommendation content suggested_actions = models.JSONField(default=list, help_text="Suggested actions to take") expected_outcome = models.TextField(blank=True, null=True, help_text="Expected outcome of following this recommendation") time_to_implement = models.DurationField(null=True, blank=True, help_text="Estimated time to implement") # Usage tracking is_applied = models.BooleanField(default=False, help_text="Whether this recommendation was applied") applied_at = models.DateTimeField(null=True, blank=True) applied_by = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, blank=True, related_name='applied_recommendations' ) effectiveness_rating = models.PositiveIntegerField( null=True, blank=True, validators=[MinValueValidator(1), MaxValueValidator(5)], help_text="User rating of recommendation effectiveness (1-5)" ) # AI analysis reasoning = models.TextField(help_text="AI explanation for why this recommendation was made") matching_factors = models.JSONField(default=list, help_text="Factors that led to this recommendation") model_version = models.CharField(max_length=50, default='v1.0') # Timestamps created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ['-confidence_score', '-similarity_score'] indexes = [ models.Index(fields=['incident', 'recommendation_type']), models.Index(fields=['confidence_score', 'similarity_score']), models.Index(fields=['is_applied']), ] def __str__(self): return f"Recommendation: {self.title} for {self.incident.title}" class LearningPattern(models.Model): """Patterns learned from incidents and postmortems""" PATTERN_TYPE_CHOICES = [ ('ROOT_CAUSE', 'Root Cause Pattern'), ('RESOLUTION', 'Resolution Pattern'), ('PREVENTION', 'Prevention Pattern'), ('ESCALATION', 'Escalation Pattern'), ('COMMUNICATION', 'Communication Pattern'), ] id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField(max_length=200) pattern_type = models.CharField(max_length=20, choices=PATTERN_TYPE_CHOICES) description = models.TextField() # Pattern characteristics frequency = models.PositiveIntegerField(default=1, help_text="How many times this pattern has been observed") success_rate = models.FloatField( validators=[MinValueValidator(0.0), MaxValueValidator(1.0)], help_text="Success rate when this pattern is applied (0.0-1.0)" ) confidence_score = models.FloatField( validators=[MinValueValidator(0.0), MaxValueValidator(1.0)], help_text="Confidence in this pattern's validity (0.0-1.0)" ) # Pattern details triggers = models.JSONField(default=list, help_text="Conditions that trigger this pattern") actions = models.JSONField(default=list, help_text="Actions associated with this pattern") outcomes = models.JSONField(default=list, help_text="Expected outcomes of this pattern") # Related data source_incidents = models.ManyToManyField( 'incident_intelligence.Incident', related_name='learning_patterns', help_text="Incidents that contributed to this pattern" ) source_postmortems = models.ManyToManyField( Postmortem, related_name='learning_patterns', help_text="Postmortems that contributed to this pattern" ) # Pattern validation is_validated = models.BooleanField(default=False, help_text="Whether this pattern has been validated by experts") validated_by = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, blank=True, related_name='validated_patterns' ) validation_notes = models.TextField(blank=True, null=True) # Usage tracking times_applied = models.PositiveIntegerField(default=0, help_text="Number of times this pattern has been applied") last_applied = models.DateTimeField(null=True, blank=True) # Timestamps created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ['-confidence_score', '-frequency'] indexes = [ models.Index(fields=['pattern_type', 'confidence_score']), models.Index(fields=['is_validated']), ] def __str__(self): return f"Pattern: {self.name} ({self.pattern_type})" class KnowledgeBaseUsage(models.Model): """Track usage of knowledge base articles and recommendations""" USAGE_TYPE_CHOICES = [ ('VIEW', 'Article View'), ('APPLY', 'Recommendation Applied'), ('RATE', 'Rating Given'), ('SHARE', 'Shared'), ('BOOKMARK', 'Bookmarked'), ] id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='knowledge_usage') usage_type = models.CharField(max_length=20, choices=USAGE_TYPE_CHOICES) # Related objects knowledge_article = models.ForeignKey( KnowledgeBaseArticle, on_delete=models.CASCADE, null=True, blank=True, related_name='usage_logs' ) recommendation = models.ForeignKey( IncidentRecommendation, on_delete=models.CASCADE, null=True, blank=True, related_name='usage_logs' ) incident = models.ForeignKey( 'incident_intelligence.Incident', on_delete=models.CASCADE, null=True, blank=True, related_name='knowledge_usage' ) # Usage context context = models.JSONField(default=dict, help_text="Additional context about the usage") session_id = models.CharField(max_length=100, blank=True, null=True) # Timestamps created_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ['-created_at'] indexes = [ models.Index(fields=['user', 'usage_type']), models.Index(fields=['created_at']), ] def __str__(self): return f"{self.usage_type} by {self.user.username}" class AutomatedPostmortemGeneration(models.Model): """Track automated postmortem generation attempts and results""" STATUS_CHOICES = [ ('PENDING', 'Pending'), ('PROCESSING', 'Processing'), ('COMPLETED', 'Completed'), ('FAILED', 'Failed'), ('REVIEW_REQUIRED', 'Review Required'), ] id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) incident = models.ForeignKey( 'incident_intelligence.Incident', on_delete=models.CASCADE, related_name='postmortem_generations' ) # Generation details status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING') generation_trigger = models.CharField(max_length=50, help_text="What triggered the generation") # Input data incident_data = models.JSONField(help_text="Incident data used for generation") timeline_data = models.JSONField(help_text="Timeline data used for generation") log_data = models.JSONField(default=list, help_text="Log data used for generation") # Generation results generated_content = models.JSONField(null=True, blank=True, help_text="Generated postmortem content") confidence_scores = models.JSONField(default=dict, help_text="Confidence scores for different sections") quality_metrics = models.JSONField(default=dict, help_text="Quality metrics for generated content") # Postmortem relationship generated_postmortem = models.OneToOneField( Postmortem, on_delete=models.CASCADE, null=True, blank=True, related_name='generation_log' ) # Processing details processing_time = models.FloatField(null=True, blank=True, help_text="Time taken for generation in seconds") model_version = models.CharField(max_length=50, default='v1.0') error_message = models.TextField(blank=True, null=True) # Timestamps started_at = models.DateTimeField(auto_now_add=True) completed_at = models.DateTimeField(null=True, blank=True) class Meta: ordering = ['-started_at'] indexes = [ models.Index(fields=['status', 'started_at']), models.Index(fields=['incident', 'status']), ] def __str__(self): return f"Postmortem Generation for {self.incident.title} - {self.status}"