HH/apps/analytics/models.py
2026-04-08 17:13:35 +03:00

127 lines
3.8 KiB
Python

"""
Analytics models - KPIs, metrics, and dashboards
This module implements analytics capabilities:
- KPI definitions and tracking
- Metric aggregation
- Dashboard data
- Trend analysis
"""
from django.db import models
from apps.core.models import TimeStampedModel, UUIDModel
class KPI(UUIDModel, TimeStampedModel):
"""
KPI (Key Performance Indicator) definition.
Defines metrics to track across the system.
"""
name = models.CharField(max_length=200, unique=True)
name_ar = models.CharField(max_length=200, blank=True)
description = models.TextField(blank=True)
# Category
category = models.CharField(
max_length=100,
choices=[
("patient_satisfaction", "Patient Satisfaction"),
("complaint_management", "Complaint Management"),
("action_management", "Action Management"),
("sla_compliance", "SLA Compliance"),
("survey_response", "Survey Response"),
("operational", "Operational"),
],
db_index=True,
)
# Measurement
unit = models.CharField(max_length=50, help_text="Unit of measurement (%, count, hours, etc.)")
calculation_method = models.TextField(help_text="Description of how this KPI is calculated")
# Thresholds
target_value = models.DecimalField(
max_digits=10, decimal_places=2, null=True, blank=True, help_text="Target value for this KPI"
)
warning_threshold = models.DecimalField(
max_digits=10, decimal_places=2, null=True, blank=True, help_text="Warning threshold"
)
critical_threshold = models.DecimalField(
max_digits=10, decimal_places=2, null=True, blank=True, help_text="Critical threshold"
)
# Configuration
is_active = models.BooleanField(default=True)
class Meta:
ordering = ["category", "name"]
verbose_name = "KPI"
verbose_name_plural = "KPIs"
def __str__(self):
return self.name
class KPIValue(UUIDModel, TimeStampedModel):
"""
KPI value - actual measurement at a point in time.
"""
kpi = models.ForeignKey(KPI, on_delete=models.CASCADE, related_name="values")
# Scope
hospital = models.ForeignKey(
"organizations.Hospital", on_delete=models.CASCADE, null=True, blank=True, related_name="kpi_values"
)
department = models.ForeignKey(
"organizations.Department", on_delete=models.CASCADE, null=True, blank=True, related_name="kpi_values"
)
# Value
value = models.DecimalField(max_digits=10, decimal_places=2)
# Time period
period_start = models.DateTimeField(db_index=True)
period_end = models.DateTimeField(db_index=True)
period_type = models.CharField(
max_length=20,
choices=[
("hourly", "Hourly"),
("daily", "Daily"),
("weekly", "Weekly"),
("monthly", "Monthly"),
("quarterly", "Quarterly"),
("yearly", "Yearly"),
],
default="daily",
)
# Status
status = models.CharField(
max_length=20,
choices=[
("on_target", "On Target"),
("warning", "Warning"),
("critical", "Critical"),
],
db_index=True,
)
# Metadata
metadata = models.JSONField(default=dict, blank=True, help_text="Additional calculation details")
class Meta:
ordering = ["-period_end"]
indexes = [
models.Index(fields=["kpi", "-period_end"]),
models.Index(fields=["hospital", "kpi", "-period_end"]),
models.Index(fields=["hospital", "department", "-period_end"]),
]
def __str__(self):
scope = self.hospital.name if self.hospital else "Global"
return f"{self.kpi.name} - {scope} - {self.period_end.strftime('%Y-%m-%d')}: {self.value}"