""" Patient Safety and Clinical Risk Management Models. This module handles safety flags, behavioral risk indicators, and clinical alerts for patient safety management. """ from django.db import models from django.utils.translation import gettext_lazy as _ from simple_history.models import HistoricalRecords from core.models import ( UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ) class PatientSafetyFlag(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """ Safety flags for patients with behavioral risks or medical alerts. Only editable by Senior Therapists and Administrators. """ class FlagType(models.TextChoices): AGGRESSION = 'AGGRESSION', _('Aggression Risk') ELOPEMENT = 'ELOPEMENT', _('Elopement Risk') SELF_HARM = 'SELF_HARM', _('Self-Harm Risk') ALLERGY = 'ALLERGY', _('Allergy Alert') MEDICAL = 'MEDICAL', _('Medical Alert') SEIZURE = 'SEIZURE', _('Seizure Risk') SENSORY = 'SENSORY', _('Sensory Sensitivity') COMMUNICATION = 'COMMUNICATION', _('Communication Needs') DIETARY = 'DIETARY', _('Dietary Restriction') OTHER = 'OTHER', _('Other') class Severity(models.TextChoices): LOW = 'LOW', _('Low') MEDIUM = 'MEDIUM', _('Medium') HIGH = 'HIGH', _('High') CRITICAL = 'CRITICAL', _('Critical') patient = models.ForeignKey( 'core.Patient', on_delete=models.CASCADE, related_name='safety_flags', verbose_name=_("Patient") ) flag_type = models.CharField( max_length=20, choices=FlagType.choices, verbose_name=_("Flag Type") ) severity = models.CharField( max_length=10, choices=Severity.choices, default=Severity.MEDIUM, verbose_name=_("Severity") ) title = models.CharField( max_length=200, verbose_name=_("Title"), help_text=_("Brief description of the safety concern") ) description = models.TextField( verbose_name=_("Description"), help_text=_("Detailed description of the safety concern and protocols") ) protocols = models.TextField( blank=True, verbose_name=_("Safety Protocols"), help_text=_("Specific protocols or procedures to follow") ) is_active = models.BooleanField( default=True, verbose_name=_("Is Active") ) created_by = models.ForeignKey( 'core.User', on_delete=models.SET_NULL, null=True, related_name='created_safety_flags', verbose_name=_("Created By"), help_text=_("Must be Senior Therapist or Administrator") ) deactivated_at = models.DateTimeField( null=True, blank=True, verbose_name=_("Deactivated At") ) deactivated_by = models.ForeignKey( 'core.User', on_delete=models.SET_NULL, null=True, blank=True, related_name='deactivated_safety_flags', verbose_name=_("Deactivated By") ) deactivation_reason = models.TextField( blank=True, verbose_name=_("Deactivation Reason") ) history = HistoricalRecords() class Meta: verbose_name = _("Patient Safety Flag") verbose_name_plural = _("Patient Safety Flags") ordering = ['-severity', '-created_at'] indexes = [ models.Index(fields=['patient', 'is_active']), models.Index(fields=['flag_type', 'severity']), models.Index(fields=['tenant', 'is_active']), ] def __str__(self): return f"{self.get_flag_type_display()} - {self.patient} ({self.get_severity_display()})" def get_severity_color(self): """Get Bootstrap color class for severity display.""" colors = { 'LOW': 'info', 'MEDIUM': 'warning', 'HIGH': 'danger', 'CRITICAL': 'dark', } return colors.get(self.severity, 'secondary') def get_icon(self): """Get icon class for flag type.""" icons = { 'AGGRESSION': 'bi-exclamation-triangle-fill', 'ELOPEMENT': 'bi-door-open-fill', 'SELF_HARM': 'bi-heart-pulse-fill', 'ALLERGY': 'bi-capsule-pill', 'MEDICAL': 'bi-hospital-fill', 'SEIZURE': 'bi-lightning-fill', 'SENSORY': 'bi-ear-fill', 'COMMUNICATION': 'bi-chat-dots-fill', 'DIETARY': 'bi-egg-fried', 'OTHER': 'bi-flag-fill', } return icons.get(self.flag_type, 'bi-flag-fill') def deactivate(self, user, reason=""): """Deactivate this safety flag.""" from django.utils import timezone self.is_active = False self.deactivated_at = timezone.now() self.deactivated_by = user self.deactivation_reason = reason self.save() @classmethod def get_active_flags_for_patient(cls, patient): """Get all active safety flags for a patient.""" return cls.objects.filter( patient=patient, is_active=True ).order_by('-severity', 'flag_type') @classmethod def has_critical_flags(cls, patient): """Check if patient has any critical safety flags.""" return cls.objects.filter( patient=patient, is_active=True, severity=cls.Severity.CRITICAL ).exists() class CrisisBehaviorProtocol(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """ Crisis behavior protocols and intervention strategies for patients. Linked to safety flags for comprehensive risk management. """ patient = models.ForeignKey( 'core.Patient', on_delete=models.CASCADE, related_name='crisis_protocols', verbose_name=_("Patient") ) safety_flag = models.ForeignKey( PatientSafetyFlag, on_delete=models.SET_NULL, null=True, blank=True, related_name='crisis_protocols', verbose_name=_("Related Safety Flag") ) trigger_description = models.TextField( verbose_name=_("Trigger Description"), help_text=_("What triggers this behavior?") ) warning_signs = models.TextField( verbose_name=_("Warning Signs"), help_text=_("Early warning signs to watch for") ) intervention_steps = models.TextField( verbose_name=_("Intervention Steps"), help_text=_("Step-by-step intervention protocol") ) de_escalation_techniques = models.TextField( blank=True, verbose_name=_("De-escalation Techniques") ) emergency_contacts = models.TextField( blank=True, verbose_name=_("Emergency Contacts"), help_text=_("Who to contact in case of crisis") ) medications = models.TextField( blank=True, verbose_name=_("Emergency Medications"), help_text=_("Any emergency medications or medical interventions") ) is_active = models.BooleanField( default=True, verbose_name=_("Is Active") ) last_reviewed = models.DateField( auto_now=True, verbose_name=_("Last Reviewed") ) reviewed_by = models.ForeignKey( 'core.User', on_delete=models.SET_NULL, null=True, related_name='reviewed_protocols', verbose_name=_("Reviewed By") ) history = HistoricalRecords() class Meta: verbose_name = _("Crisis Behavior Protocol") verbose_name_plural = _("Crisis Behavior Protocols") ordering = ['-last_reviewed'] indexes = [ models.Index(fields=['patient', 'is_active']), models.Index(fields=['tenant', 'is_active']), ] def __str__(self): return f"Crisis Protocol for {self.patient}" class PatientAllergy(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """ Structured allergy tracking for patients. Linked to safety flags for comprehensive medical alerts. """ class AllergyType(models.TextChoices): FOOD = 'FOOD', _('Food Allergy') MEDICATION = 'MEDICATION', _('Medication Allergy') ENVIRONMENTAL = 'ENVIRONMENTAL', _('Environmental Allergy') LATEX = 'LATEX', _('Latex Allergy') OTHER = 'OTHER', _('Other') class Severity(models.TextChoices): MILD = 'MILD', _('Mild') MODERATE = 'MODERATE', _('Moderate') SEVERE = 'SEVERE', _('Severe') ANAPHYLAXIS = 'ANAPHYLAXIS', _('Anaphylaxis Risk') patient = models.ForeignKey( 'core.Patient', on_delete=models.CASCADE, related_name='allergies', verbose_name=_("Patient") ) safety_flag = models.ForeignKey( PatientSafetyFlag, on_delete=models.SET_NULL, null=True, blank=True, related_name='allergies', verbose_name=_("Related Safety Flag") ) allergy_type = models.CharField( max_length=20, choices=AllergyType.choices, verbose_name=_("Allergy Type") ) allergen = models.CharField( max_length=200, verbose_name=_("Allergen"), help_text=_("Specific allergen (e.g., peanuts, penicillin)") ) severity = models.CharField( max_length=15, choices=Severity.choices, verbose_name=_("Severity") ) reaction_description = models.TextField( verbose_name=_("Reaction Description"), help_text=_("Describe the allergic reaction") ) treatment = models.TextField( blank=True, verbose_name=_("Treatment"), help_text=_("Treatment protocol (e.g., EpiPen, antihistamine)") ) verified_by_doctor = models.BooleanField( default=False, verbose_name=_("Verified by Doctor") ) verification_date = models.DateField( null=True, blank=True, verbose_name=_("Verification Date") ) is_active = models.BooleanField( default=True, verbose_name=_("Is Active") ) history = HistoricalRecords() class Meta: verbose_name = _("Patient Allergy") verbose_name_plural = _("Patient Allergies") ordering = ['-severity', 'allergen'] indexes = [ models.Index(fields=['patient', 'is_active']), models.Index(fields=['allergy_type', 'severity']), ] def __str__(self): return f"{self.allergen} - {self.patient} ({self.get_severity_display()})"