159 lines
4.0 KiB
Python
159 lines
4.0 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']),
|
|
]
|
|
|
|
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}"
|