agdar/referrals/models.py
2025-11-02 14:35:35 +03:00

261 lines
7.5 KiB
Python

"""
Referrals models for the Tenhal Multidisciplinary Healthcare Platform.
This module handles inter-discipline and external referrals.
"""
from django.db import models
from django.utils.translation import gettext_lazy as _
from core.models import (
UUIDPrimaryKeyMixin,
TimeStampedMixin,
TenantOwnedMixin,
)
class Referral(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin):
"""
Inter-discipline and external referrals.
Tracks referrals between clinics and to external providers.
"""
class Discipline(models.TextChoices):
MEDICAL = 'MEDICAL', _('Medical')
NURSING = 'NURSING', _('Nursing')
OT = 'OT', _('Occupational Therapy')
SLP = 'SLP', _('Speech-Language Pathology')
ABA = 'ABA', _('Applied Behavior Analysis')
EXTERNAL = 'EXTERNAL', _('External Provider')
class Urgency(models.TextChoices):
ROUTINE = 'ROUTINE', _('Routine')
URGENT = 'URGENT', _('Urgent')
EMERGENCY = 'EMERGENCY', _('Emergency')
class Status(models.TextChoices):
PENDING = 'PENDING', _('Pending')
ACCEPTED = 'ACCEPTED', _('Accepted')
REJECTED = 'REJECTED', _('Rejected')
COMPLETED = 'COMPLETED', _('Completed')
CANCELLED = 'CANCELLED', _('Cancelled')
# Core Relationships
patient = models.ForeignKey(
'core.Patient',
on_delete=models.CASCADE,
related_name='referrals',
verbose_name=_("Patient")
)
# From (Source)
from_clinic = models.ForeignKey(
'core.Clinic',
on_delete=models.CASCADE,
related_name='referrals_from',
verbose_name=_("From Clinic")
)
from_discipline = models.CharField(
max_length=20,
choices=Discipline.choices,
verbose_name=_("From Discipline")
)
from_provider = models.ForeignKey(
'core.User',
on_delete=models.SET_NULL,
null=True,
related_name='referrals_made',
verbose_name=_("From Provider")
)
# To (Destination) - Internal or External
to_clinic = models.ForeignKey(
'core.Clinic',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='referrals_to',
verbose_name=_("To Clinic")
)
to_discipline = models.CharField(
max_length=20,
choices=Discipline.choices,
blank=True,
verbose_name=_("To Discipline")
)
to_provider = models.ForeignKey(
'core.User',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='referrals_received',
verbose_name=_("To Provider")
)
external_provider_name = models.CharField(
max_length=200,
blank=True,
help_text=_("Name of external provider if referral is external"),
verbose_name=_("External Provider Name")
)
external_provider_contact = models.CharField(
max_length=200,
blank=True,
verbose_name=_("External Provider Contact")
)
# Referral Details
reason = models.TextField(
verbose_name=_("Reason for Referral")
)
urgency = models.CharField(
max_length=20,
choices=Urgency.choices,
default=Urgency.ROUTINE,
verbose_name=_("Urgency")
)
status = models.CharField(
max_length=20,
choices=Status.choices,
default=Status.PENDING,
verbose_name=_("Status")
)
# Timestamps
responded_at = models.DateTimeField(
null=True,
blank=True,
verbose_name=_("Responded At")
)
completed_at = models.DateTimeField(
null=True,
blank=True,
verbose_name=_("Completed At")
)
# Additional Information
notes = models.TextField(
blank=True,
verbose_name=_("Notes")
)
response_notes = models.TextField(
blank=True,
verbose_name=_("Response Notes")
)
# Attachments (clinical documents, reports, etc.)
clinical_summary = models.TextField(
blank=True,
help_text=_("Summary of clinical findings to share with receiving provider"),
verbose_name=_("Clinical Summary")
)
class Meta:
verbose_name = _("Referral")
verbose_name_plural = _("Referrals")
ordering = ['-created_at']
indexes = [
models.Index(fields=['patient', 'status']),
models.Index(fields=['from_clinic', 'created_at']),
models.Index(fields=['to_clinic', 'status']),
models.Index(fields=['status', 'urgency']),
models.Index(fields=['tenant', 'created_at']),
]
def __str__(self):
if self.to_clinic:
return f"Referral: {self.from_clinic.name_en}{self.to_clinic.name_en} - {self.patient}"
else:
return f"Referral: {self.from_clinic.name_en}{self.external_provider_name} - {self.patient}"
@property
def is_internal(self):
"""Check if referral is internal (within the same facility)."""
return self.to_clinic is not None
@property
def is_external(self):
"""Check if referral is external."""
return self.to_clinic is None and bool(self.external_provider_name)
@property
def is_pending(self):
"""Check if referral is pending."""
return self.status == self.Status.PENDING
@property
def response_time(self):
"""Calculate response time in hours."""
if self.responded_at:
delta = self.responded_at - self.created_at
return round(delta.total_seconds() / 3600, 2)
return None
class ReferralAutoRule(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin):
"""
Automatic referral rules based on diagnoses or conditions.
E.g., ASD diagnosis → auto-suggest referral to Pediatrician
"""
name = models.CharField(
max_length=200,
verbose_name=_("Rule Name")
)
description = models.TextField(
blank=True,
verbose_name=_("Description")
)
# Trigger Conditions
trigger_clinic = models.ForeignKey(
'core.Clinic',
on_delete=models.CASCADE,
related_name='referral_auto_rules',
verbose_name=_("Trigger Clinic")
)
trigger_keywords = models.JSONField(
default=list,
help_text=_("Keywords in diagnosis/assessment that trigger this rule"),
verbose_name=_("Trigger Keywords")
)
# Target
target_clinic = models.ForeignKey(
'core.Clinic',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='referral_auto_rules_target',
verbose_name=_("Target Clinic")
)
target_external_provider = models.CharField(
max_length=200,
blank=True,
verbose_name=_("Target External Provider")
)
# Rule Configuration
urgency = models.CharField(
max_length=20,
choices=Referral.Urgency.choices,
default=Referral.Urgency.ROUTINE,
verbose_name=_("Default Urgency")
)
auto_create = models.BooleanField(
default=False,
help_text=_("Automatically create referral (vs. just suggest)"),
verbose_name=_("Auto Create")
)
is_active = models.BooleanField(
default=True,
verbose_name=_("Is Active")
)
class Meta:
verbose_name = _("Referral Auto Rule")
verbose_name_plural = _("Referral Auto Rules")
ordering = ['trigger_clinic', 'name']
def __str__(self):
return f"{self.name} - {self.trigger_clinic.name_en}"