351 lines
13 KiB
Python
351 lines
13 KiB
Python
"""
|
|
Executive Summary models - AI-powered executive dashboards and reports
|
|
|
|
This module implements:
|
|
- Executive metrics (KPI snapshots for trending)
|
|
- Executive reports (AI-generated weekly/monthly summaries)
|
|
- Predictive insights (AI-detected patterns and risks)
|
|
- AI recommendations (suggested actions)
|
|
"""
|
|
|
|
from django.db import models
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from apps.core.models import TimeStampedModel, UUIDModel
|
|
|
|
|
|
class ExecutiveMetric(UUIDModel, TimeStampedModel):
|
|
"""
|
|
Executive Metric - Daily KPI snapshots for trending and analysis.
|
|
|
|
Stores aggregated metrics across all hospitals for executive overview.
|
|
Used for trend analysis and dashboard display.
|
|
"""
|
|
|
|
METRIC_TYPES = [
|
|
("complaints_total", _("Total Complaints")),
|
|
("complaints_critical", _("Critical Complaints")),
|
|
("complaints_overdue", _("Overdue Complaints")),
|
|
("complaints_resolution_time", _("Avg Resolution Time (hours)")),
|
|
("surveys_total", _("Total Surveys")),
|
|
("surveys_satisfaction", _("Satisfaction Rate %")),
|
|
("surveys_nps", _("NPS Score")),
|
|
("surveys_response_rate", _("Response Rate %")),
|
|
("actions_total", _("Total Actions")),
|
|
("actions_open", _("Open Actions")),
|
|
("actions_overdue", _("Overdue Actions")),
|
|
("actions_closed", _("Closed Actions")),
|
|
("observations_total", _("Total Observations")),
|
|
("observations_critical", _("Critical Observations")),
|
|
("inquiries_total", _("Total Inquiries")),
|
|
("inquiries_resolved", _("Resolved Inquiries")),
|
|
("call_center_total", _("Call Center Interactions")),
|
|
("call_center_satisfaction", _("Call Center Satisfaction %")),
|
|
("physician_avg_rating", _("Avg Physician Rating")),
|
|
]
|
|
|
|
# Date for this metric snapshot
|
|
metric_date = models.DateField(db_index=True, help_text="Date of this metric snapshot")
|
|
|
|
# Metric type
|
|
metric_type = models.CharField(max_length=50, choices=METRIC_TYPES, db_index=True)
|
|
|
|
# Metric value
|
|
metric_value = models.DecimalField(max_digits=10, decimal_places=2)
|
|
|
|
# Variance from previous period
|
|
variance = models.DecimalField(
|
|
max_digits=10, decimal_places=2, null=True, blank=True, help_text="Variance from previous period"
|
|
)
|
|
|
|
# Variance direction
|
|
variance_direction = models.CharField(
|
|
max_length=10,
|
|
choices=[("up", "Up"), ("down", "Down"), ("neutral", "Neutral")],
|
|
default="neutral",
|
|
help_text="Direction of variance",
|
|
)
|
|
|
|
# Hospital (null = system-wide aggregate)
|
|
hospital = models.ForeignKey(
|
|
"organizations.Hospital", on_delete=models.CASCADE, null=True, blank=True, related_name="executive_metrics"
|
|
)
|
|
|
|
# Metadata
|
|
metadata = models.JSONField(default=dict, blank=True)
|
|
|
|
class Meta:
|
|
ordering = ["-metric_date", "metric_type"]
|
|
indexes = [
|
|
models.Index(fields=["metric_date", "metric_type"]),
|
|
models.Index(fields=["hospital", "metric_date"]),
|
|
]
|
|
verbose_name = _("Executive Metric")
|
|
verbose_name_plural = _("Executive Metrics")
|
|
|
|
def __str__(self):
|
|
hospital_name = f" - {self.hospital.name}" if self.hospital else " (All)"
|
|
return f"{self.get_metric_type_display()}: {self.metric_value}{hospital_name} ({self.metric_date})"
|
|
|
|
|
|
class ExecutiveReport(UUIDModel, TimeStampedModel):
|
|
"""
|
|
Executive Report - AI-generated weekly/monthly narrative summaries.
|
|
|
|
Contains natural language summaries generated by AI analyzing
|
|
performance data across all hospitals.
|
|
"""
|
|
|
|
REPORT_TYPES = [
|
|
("weekly", _("Weekly Summary")),
|
|
("monthly", _("Monthly Summary")),
|
|
("quarterly", _("Quarterly Summary")),
|
|
("custom", _("Custom Report")),
|
|
]
|
|
|
|
STATUS_CHOICES = [
|
|
("pending", _("Pending")),
|
|
("generating", _("Generating")),
|
|
("completed", _("Completed")),
|
|
("failed", _("Failed")),
|
|
]
|
|
|
|
# Report metadata
|
|
report_type = models.CharField(max_length=20, choices=REPORT_TYPES, db_index=True)
|
|
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="pending", db_index=True)
|
|
|
|
# Date range
|
|
start_date = models.DateField()
|
|
end_date = models.DateField()
|
|
|
|
# AI-generated narratives (bilingual)
|
|
narrative_en = models.TextField(blank=True, help_text="English narrative summary")
|
|
narrative_ar = models.TextField(blank=True, help_text="Arabic narrative summary")
|
|
|
|
# Key highlights
|
|
highlights_en = models.JSONField(default=list, blank=True, help_text="Key highlights (English)")
|
|
highlights_ar = models.JSONField(default=list, blank=True, help_text="Key highlights (Arabic)")
|
|
|
|
# Key concerns
|
|
concerns_en = models.JSONField(default=list, blank=True, help_text="Key concerns (English)")
|
|
concerns_ar = models.JSONField(default=list, blank=True, help_text="Key concerns (Arabic)")
|
|
|
|
# Summary metrics snapshot
|
|
metrics_snapshot = models.JSONField(default=dict, blank=True, help_text="Key metrics at report time")
|
|
|
|
# AI metadata
|
|
ai_model = models.CharField(max_length=100, blank=True, help_text="AI model used for generation")
|
|
generation_time_ms = models.IntegerField(null=True, blank=True, help_text="Generation time in milliseconds")
|
|
|
|
# PDF report file (if generated)
|
|
pdf_file = models.FileField(
|
|
upload_to="executive_reports/pdfs/%Y/%m/", null=True, blank=True, help_text="Generated PDF report"
|
|
)
|
|
|
|
# Error message if failed
|
|
error_message = models.TextField(blank=True, help_text="Error message if generation failed")
|
|
|
|
class Meta:
|
|
ordering = ["-created_at"]
|
|
indexes = [
|
|
models.Index(fields=["report_type", "-created_at"]),
|
|
models.Index(fields=["status", "-created_at"]),
|
|
]
|
|
verbose_name = _("Executive Report")
|
|
verbose_name_plural = _("Executive Reports")
|
|
|
|
def __str__(self):
|
|
return f"{self.get_report_type_display()} ({self.start_date} to {self.end_date}) - {self.get_status_display()}"
|
|
|
|
|
|
class PredictiveInsight(UUIDModel, TimeStampedModel):
|
|
"""
|
|
Predictive Insight - AI-detected patterns, anomalies, and early warnings.
|
|
|
|
Automatically generated insights identifying:
|
|
- Emerging complaint patterns
|
|
- Declining satisfaction trends
|
|
- SLA breach risks
|
|
- Performance anomalies
|
|
- Resource bottlenecks
|
|
"""
|
|
|
|
INSIGHT_TYPES = [
|
|
("trend_change", _("Trend Change")),
|
|
("anomaly", _("Anomaly Detected")),
|
|
("risk_warning", _("Risk Warning")),
|
|
("sla_breach_risk", _("SLA Breach Risk")),
|
|
("performance_drop", _("Performance Drop")),
|
|
("volume_spike", _("Volume Spike")),
|
|
("satisfaction_decline", _("Satisfaction Decline")),
|
|
("positive_trend", _("Positive Trend")),
|
|
]
|
|
|
|
SEVERITY_LEVELS = [
|
|
("low", _("Low")),
|
|
("medium", _("Medium")),
|
|
("high", _("High")),
|
|
("critical", _("Critical")),
|
|
]
|
|
|
|
STATUS_CHOICES = [
|
|
("new", _("New")),
|
|
("acknowledged", _("Acknowledged")),
|
|
("resolved", _("Resolved")),
|
|
("dismissed", _("Dismissed")),
|
|
]
|
|
|
|
# Insight details
|
|
insight_type = models.CharField(max_length=30, choices=INSIGHT_TYPES, db_index=True)
|
|
severity = models.CharField(max_length=10, choices=SEVERITY_LEVELS, default="medium", db_index=True)
|
|
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="new", db_index=True)
|
|
|
|
# Title and description (bilingual)
|
|
title_en = models.CharField(max_length=300)
|
|
title_ar = models.CharField(max_length=300, blank=True)
|
|
description_en = models.TextField()
|
|
description_ar = models.TextField(blank=True)
|
|
|
|
# AI-generated recommendation
|
|
recommendation_en = models.TextField(blank=True)
|
|
recommendation_ar = models.TextField(blank=True)
|
|
|
|
# Affected entities
|
|
hospital = models.ForeignKey(
|
|
"organizations.Hospital", on_delete=models.SET_NULL, null=True, blank=True, related_name="predictive_insights"
|
|
)
|
|
department = models.ForeignKey(
|
|
"organizations.Department", on_delete=models.SET_NULL, null=True, blank=True, related_name="predictive_insights"
|
|
)
|
|
|
|
# Metric affected
|
|
metric_type = models.CharField(max_length=50, blank=True, help_text="Related metric type")
|
|
|
|
# Related entity (for SLA breach predictions, etc.)
|
|
entity_type = models.CharField(
|
|
max_length=50, blank=True, help_text="Type of related entity (e.g., complaint, px_action)"
|
|
)
|
|
entity_id = models.CharField(max_length=100, blank=True, help_text="ID of related entity")
|
|
|
|
# Predicted values
|
|
current_value = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
|
|
predicted_value = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
|
|
confidence_score = models.DecimalField(
|
|
max_digits=5, decimal_places=2, null=True, blank=True, help_text="AI confidence score (0-100)"
|
|
)
|
|
|
|
# Time-related
|
|
predicted_date = models.DateField(null=True, blank=True, help_text="When this is expected to occur")
|
|
|
|
# Acknowledgement
|
|
acknowledged_by = models.ForeignKey(
|
|
"accounts.User", on_delete=models.SET_NULL, null=True, blank=True, related_name="acknowledged_insights"
|
|
)
|
|
acknowledged_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
# AI metadata
|
|
ai_model = models.CharField(max_length=100, blank=True)
|
|
detection_metadata = models.JSONField(default=dict, blank=True)
|
|
|
|
class Meta:
|
|
ordering = ["-severity", "-created_at"]
|
|
indexes = [
|
|
models.Index(fields=["status", "severity", "-created_at"]),
|
|
models.Index(fields=["hospital", "status"]),
|
|
]
|
|
verbose_name = _("Predictive Insight")
|
|
verbose_name_plural = _("Predictive Insights")
|
|
|
|
def __str__(self):
|
|
return f"[{self.get_severity_display()}] {self.title_en}"
|
|
|
|
|
|
class AIRecommendation(UUIDModel, TimeStampedModel):
|
|
"""
|
|
AI Recommendation - Suggested actions based on data analysis.
|
|
|
|
AI-generated recommendations for:
|
|
- Process improvements
|
|
- Resource allocation
|
|
- Training needs
|
|
- Policy changes
|
|
- Preventive measures
|
|
"""
|
|
|
|
CATEGORY_CHOICES = [
|
|
("process_improvement", _("Process Improvement")),
|
|
("resource_allocation", _("Resource Allocation")),
|
|
("training", _("Training")),
|
|
("policy_change", _("Policy Change")),
|
|
("preventive_action", _("Preventive Action")),
|
|
("performance_optimization", _("Performance Optimization")),
|
|
("communication", _("Communication")),
|
|
("quality_assurance", _("Quality Assurance")),
|
|
]
|
|
|
|
PRIORITY_LEVELS = [
|
|
("low", _("Low")),
|
|
("medium", _("Medium")),
|
|
("high", _("High")),
|
|
("urgent", _("Urgent")),
|
|
]
|
|
|
|
STATUS_CHOICES = [
|
|
("new", _("New")),
|
|
("under_review", _("Under Review")),
|
|
("approved", _("Approved")),
|
|
("implemented", _("Implemented")),
|
|
("rejected", _("Rejected")),
|
|
]
|
|
|
|
# Recommendation details
|
|
category = models.CharField(max_length=30, choices=CATEGORY_CHOICES, db_index=True)
|
|
priority = models.CharField(max_length=10, choices=PRIORITY_LEVELS, default="medium", db_index=True)
|
|
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="new", db_index=True)
|
|
|
|
# Title and description (bilingual)
|
|
title_en = models.CharField(max_length=300)
|
|
title_ar = models.CharField(max_length=300, blank=True)
|
|
description_en = models.TextField()
|
|
description_ar = models.TextField(blank=True)
|
|
|
|
# Expected impact
|
|
expected_impact_en = models.TextField(blank=True, help_text="Expected impact if implemented (English)")
|
|
expected_impact_ar = models.TextField(blank=True, help_text="Expected impact if implemented (Arabic)")
|
|
|
|
# Related data
|
|
hospital = models.ForeignKey(
|
|
"organizations.Hospital", on_delete=models.SET_NULL, null=True, blank=True, related_name="ai_recommend"
|
|
)
|
|
department = models.ForeignKey(
|
|
"organizations.Department", on_delete=models.SET_NULL, null=True, blank=True, related_name="ai_recommend"
|
|
)
|
|
|
|
# Related insight (if generated from one)
|
|
related_insight = models.ForeignKey(
|
|
PredictiveInsight, on_delete=models.SET_NULL, null=True, blank=True, related_name="recommendations"
|
|
)
|
|
|
|
# Implementation tracking
|
|
implemented_by = models.ForeignKey(
|
|
"accounts.User", on_delete=models.SET_NULL, null=True, blank=True, related_name="implemented_recommendations"
|
|
)
|
|
implemented_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
# AI metadata
|
|
confidence_score = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
|
|
ai_model = models.CharField(max_length=100, blank=True)
|
|
generation_metadata = models.JSONField(default=dict, blank=True)
|
|
|
|
class Meta:
|
|
ordering = ["-priority", "-created_at"]
|
|
indexes = [
|
|
models.Index(fields=["status", "priority", "-created_at"]),
|
|
models.Index(fields=["hospital", "status"]),
|
|
]
|
|
verbose_name = _("AI Recommendation")
|
|
verbose_name_plural = _("AI Recommendations")
|
|
|
|
def __str__(self):
|
|
return f"[{self.get_priority_display()}] {self.title_en}"
|