""" Inpatients app models for hospital management system. Provides ward management, bed allocation, admissions, transfers, and discharge planning. """ import uuid from django.db import models from django.core.validators import RegexValidator, MinValueValidator, MaxValueValidator from django.utils import timezone from django.conf import settings from datetime import timedelta, datetime, time import json from kombu.transport.redis import PRIORITY_STEPS class Ward(models.Model): """ Hospital ward model for organizing patient care areas. """ class WardType(models.TextChoices): GENERAL = 'GENERAL', 'General Medical' SURGICAL = 'SURGICAL', 'Surgical' ICU = 'ICU', 'Intensive Care Unit' CCU = 'CCU', 'Cardiac Care Unit' NICU = 'NICU', 'Neonatal ICU' PICU = 'PICU', 'Pediatric ICU' EMERGENCY = 'EMERGENCY', 'Emergency' MATERNITY = 'MATERNITY', 'Maternity' PEDIATRIC = 'PEDIATRIC', 'Pediatric' ONCOLOGY = 'ONCOLOGY', 'Oncology' CARDIAC = 'CARDIAC', 'Cardiac' ORTHOPEDIC = 'ORTHOPEDIC', 'Orthopedic' NEUROLOGY = 'NEUROLOGY', 'Neurology' PSYCHIATRY = 'PSYCHIATRY', 'Psychiatry' REHABILITATION = 'REHABILITATION', 'Rehabilitation' ISOLATION = 'ISOLATION', 'Isolation' STEP_DOWN = 'STEP_DOWN', 'Step Down' OTHER = 'OTHER', 'Other' class WardSpecialty(models.TextChoices): # use WardSpeciality if you prefer UK spelling GENERAL_MEDICINE = 'GENERAL_MEDICINE', 'General Medicine' SURGERY = 'SURGERY', 'Surgery' CARDIOLOGY = 'CARDIOLOGY', 'Cardiology' NEUROLOGY = 'NEUROLOGY', 'Neurology' ONCOLOGY = 'ONCOLOGY', 'Oncology' PEDIATRICS = 'PEDIATRICS', 'Pediatrics' OBSTETRICS = 'OBSTETRICS', 'Obstetrics' GYNECOLOGY = 'GYNECOLOGY', 'Gynecology' ORTHOPEDICS = 'ORTHOPEDICS', 'Orthopedics' PSYCHIATRY = 'PSYCHIATRY', 'Psychiatry' EMERGENCY = 'EMERGENCY', 'Emergency Medicine' CRITICAL_CARE = 'CRITICAL_CARE', 'Critical Care' REHABILITATION = 'REHABILITATION', 'Rehabilitation' OTHER = 'OTHER', 'Other' class GenderRestriction(models.TextChoices): NONE = 'NONE', 'No Restrictions' MALE_ONLY = 'MALE_ONLY', 'Male Only' FEMALE_ONLY = 'FEMALE_ONLY', 'Female Only' # Tenant relationship tenant = models.ForeignKey( 'core.Tenant', on_delete=models.CASCADE, related_name='wards', help_text='Organization tenant' ) # Ward Information ward_id = models.CharField( max_length=50, help_text='Unique ward identifier' ) name = models.CharField( max_length=200, help_text='Ward name' ) description = models.TextField( blank=True, null=True, help_text='Ward description' ) # Ward Type and Specialty ward_type = models.CharField( max_length=50, choices=WardType.choices, help_text='Type of ward' ) specialty = models.CharField( max_length=100, choices=WardSpecialty.choices, help_text='Medical specialty' ) # Capacity and Configuration total_beds = models.PositiveIntegerField( help_text='Total number of beds in ward' ) private_rooms = models.PositiveIntegerField( default=0, help_text='Number of private rooms' ) semi_private_rooms = models.PositiveIntegerField( default=0, help_text='Number of semi-private rooms' ) shared_rooms = models.PositiveIntegerField( default=0, help_text='Number of shared rooms' ) # Location Information building = models.ForeignKey('facility_management.Building', on_delete=models.CASCADE, related_name='wards') floor = models.ForeignKey('facility_management.Floor', on_delete=models.CASCADE, related_name='wards_floor') wing = models.CharField( max_length=50, blank=True, null=True, help_text='Wing or section' ) # Staffing Information nurse_manager = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='managed_wards', help_text='Nurse manager for this ward' ) attending_physicians = models.ManyToManyField( settings.AUTH_USER_MODEL, related_name='attending_wards', blank=True, help_text='Attending physicians for this ward' ) # Staffing Requirements min_nurses_day = models.PositiveIntegerField( default=1, help_text='Minimum nurses required for day shift' ) min_nurses_night = models.PositiveIntegerField( default=1, help_text='Minimum nurses required for night shift' ) nurse_to_patient_ratio = models.FloatField( default=1.0, help_text='Nurse to patient ratio' ) # Equipment and Features equipment_list = models.JSONField( default=list, blank=True, help_text='Available equipment in ward' ) special_features = models.JSONField( default=list, blank=True, help_text='Special features and capabilities' ) # Admission Criteria admission_criteria = models.TextField( blank=True, null=True, help_text='Admission criteria for this ward' ) age_restrictions = models.JSONField( default=dict, blank=True, help_text='Age restrictions (min_age, max_age)' ) gender_restrictions = models.CharField( max_length=20, choices=GenderRestriction.choices, default=GenderRestriction.NONE, help_text='Gender restrictions' ) # Status and Operations is_active = models.BooleanField( default=True, help_text='Ward is active and operational' ) is_accepting_admissions = models.BooleanField( default=True, help_text='Ward is accepting new admissions' ) closure_reason = models.TextField( blank=True, null=True, help_text='Reason for closure if not accepting admissions' ) # Contact Information phone_number = models.CharField( max_length=20, blank=True, null=True, help_text='Ward phone number' ) extension = models.CharField( max_length=10, blank=True, null=True, help_text='Phone extension' ) # Metadata created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='created_wards', help_text='User who created the ward' ) class Meta: db_table = 'inpatients_ward' verbose_name = 'Ward' verbose_name_plural = 'Wards' ordering = ['building', 'floor', 'name'] indexes = [ models.Index(fields=['tenant', 'ward_type']), models.Index(fields=['specialty']), models.Index(fields=['is_active', 'is_accepting_admissions']), ] unique_together = ['tenant', 'ward_id'] def __str__(self): return self.name @property def occupancy_rate(self): """ Calculate current occupancy rate. """ occupied_beds = self.beds.filter(status='OCCUPIED').count() if self.total_beds == 0: return 0.0 return 100 * (occupied_beds / self.total_beds) @property def available_beds(self): """ Get count of available beds. """ return self.beds.filter(status='AVAILABLE').count() @property def occupied_beds(self): return self.beds.filter(status='OCCUPIED').count() class Bed(models.Model): """ Hospital bed model for tracking individual bed status and assignments. """ class BedType(models.TextChoices): STANDARD = 'STANDARD', 'Standard Bed' ICU = 'ICU', 'ICU Bed' CARDIAC = 'CARDIAC', 'Cardiac Monitoring' ISOLATION = 'ISOLATION', 'Isolation Bed' BARIATRIC = 'BARIATRIC', 'Bariatric Bed' PEDIATRIC = 'PEDIATRIC', 'Pediatric Bed' NEONATAL = 'NEONATAL', 'Neonatal Bed' MATERNITY = 'MATERNITY', 'Maternity Bed' PSYCHIATRIC = 'PSYCHIATRIC', 'Psychiatric Bed' STRETCHER = 'STRETCHER', 'Stretcher/Gurney' RECLINER = 'RECLINER', 'Recliner Chair' OTHER = 'OTHER', 'Other' class RoomType(models.TextChoices): PRIVATE = 'PRIVATE', 'Private Room' SEMI_PRIVATE = 'SEMI_PRIVATE', 'Semi-Private' SHARED = 'SHARED', 'Shared Room' ICU = 'ICU', 'ICU Room' ISOLATION = 'ISOLATION', 'Isolation Room' class BedStatus(models.TextChoices): AVAILABLE = 'AVAILABLE', 'Available' OCCUPIED = 'OCCUPIED', 'Occupied' RESERVED = 'RESERVED', 'Reserved' MAINTENANCE = 'MAINTENANCE', 'Under Maintenance' CLEANING = 'CLEANING', 'Being Cleaned' OUT_OF_ORDER = 'OUT_OF_ORDER', 'Out of Order' BLOCKED = 'BLOCKED', 'Blocked' class CleaningLevel(models.TextChoices): STANDARD = 'STANDARD', 'Standard Cleaning' DEEP = 'DEEP', 'Deep Cleaning' ISOLATION = 'ISOLATION', 'Isolation Cleaning' TERMINAL = 'TERMINAL', 'Terminal Cleaning' class BedPosition(models.TextChoices): A = 'A', 'A' B = 'B', 'B' C = 'C', 'C' D = 'D', 'D' E = 'E', 'E' F = 'F', 'F' # Ward relationship ward = models.ForeignKey( Ward, on_delete=models.CASCADE, related_name='beds', help_text='Ward containing this bed' ) # Bed Information bed_number = models.CharField( max_length=20, help_text='Bed number or identifier' ) room_number = models.CharField( max_length=20, help_text='Room number' ) bed_type = models.CharField( max_length=30, choices=BedType.choices, default=BedType.STANDARD, help_text='Type of bed' ) is_operational = models.BooleanField( default=True, help_text='Operational status' ) is_active = models.BooleanField( default=True, help_text='Active status' ) is_out_of_service = models.BooleanField( default=False, ) room_type = models.CharField( max_length=20, choices=RoomType.choices, help_text='Type of room' ) # Bed Status status = models.CharField( max_length=20, choices=BedStatus.choices, default=BedStatus.AVAILABLE, help_text='Current bed status' ) # Current Assignment # current_patient = models.ForeignKey( # 'patients.PatientProfile', # on_delete=models.SET_NULL, # null=True, # blank=True, # related_name='current_bed', # help_text='Currently assigned patient' # ) current_admission = models.ForeignKey( 'Admission', on_delete=models.SET_NULL, null=True, blank=True, related_name='assigned_bed', help_text='Current admission using this bed' ) # Assignment Timing occupied_since = models.DateTimeField( blank=True, null=True, help_text='When bed became occupied' ) reserved_until = models.DateTimeField( blank=True, null=True, help_text='Reservation expiry time' ) # Equipment and Features equipment = models.ManyToManyField('facility_management.Asset', related_name='beds_assets', blank=True) features = models.JSONField( default=list, blank=True, help_text='Special features of this bed' ) # Maintenance Information last_maintenance = models.DateTimeField( blank=True, null=True, help_text='Last maintenance date' ) next_maintenance = models.DateTimeField( blank=True, null=True, help_text='Next scheduled maintenance' ) maintenance_notes = models.TextField( blank=True, null=True, help_text='Maintenance notes and history' ) # Cleaning Information last_cleaned = models.DateTimeField( blank=True, null=True, help_text='Last cleaning timestamp' ) cleaned_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='cleaned_beds', help_text='Staff member who last cleaned bed' ) cleaning_level = models.CharField( max_length=20, choices=CleaningLevel.choices, blank=True, null=True, help_text='Level of last cleaning' ) # Blocking Information blocked_reason = models.TextField( blank=True, null=True, help_text='Reason for blocking bed' ) blocked_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='blocked_beds', help_text='User who blocked the bed' ) blocked_until = models.DateTimeField( blank=True, null=True, help_text='Blocked until this time' ) # Location Details bed_position = models.CharField( max_length=20, choices=BedPosition.choices, blank=True, null=True, help_text='Position within room' ) # Metadata created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='created_beds', help_text='User who created the bed record' ) class Meta: db_table = 'inpatients_bed' verbose_name = 'Bed' verbose_name_plural = 'Beds' ordering = ['ward', 'room_number', 'bed_number'] indexes = [ models.Index(fields=['ward', 'status']), models.Index(fields=['current_admission']), models.Index(fields=['bed_type', 'room_type']), models.Index(fields=['status']), ] unique_together = ['ward', 'bed_number'] def __str__(self): return f"{self.ward.name} - Room {self.room_number}, Bed {self.bed_number}" @property def is_available(self): """ Check if bed is available for assignment. """ return self.status == 'AVAILABLE' @property def occupancy_duration(self): """ Calculate how long bed has been occupied. """ if self.occupied_since: return timezone.now() - self.occupied_since return None class Admission(models.Model): """ Patient admission model for tracking inpatient stays. """ class AdmissionType(models.TextChoices): EMERGENCY = 'EMERGENCY', 'Emergency Admission' ELECTIVE = 'ELECTIVE', 'Elective Admission' URGENT = 'URGENT', 'Urgent Admission' OBSERVATION = 'OBSERVATION', 'Observation' DAY_SURGERY = 'DAY_SURGERY', 'Day Surgery' TRANSFER = 'TRANSFER', 'Transfer from Another Facility' READMISSION = 'READMISSION', 'Readmission' DIRECT = 'DIRECT', 'Direct Admission' OTHER = 'OTHER', 'Other' class AdmissionSource(models.TextChoices): EMERGENCY = 'EMERGENCY', 'Emergency Department' OUTPATIENT = 'OUTPATIENT', 'Outpatient Clinic' PHYSICIAN_OFFICE = 'PHYSICIAN_OFFICE', 'Physician Office' TRANSFER = 'TRANSFER', 'Transfer from Another Hospital' NURSING_HOME = 'NURSING_HOME', 'Nursing Home' HOME = 'HOME', 'Home' AMBULATORY_SURGERY = 'AMBULATORY_SURGERY', 'Ambulatory Surgery' OTHER_FACILITY = 'OTHER_FACILITY', 'Other Healthcare Facility' OTHER = 'OTHER', 'Other' class AdmissionStatus(models.TextChoices): PENDING = 'PENDING', 'Pending Admission' ADMITTED = 'ADMITTED', 'Admitted' TRANSFERRED = 'TRANSFERRED', 'Transferred' DISCHARGED = 'DISCHARGED', 'Discharged' DECEASED = 'DECEASED', 'Deceased' LEFT_AMA = 'LEFT_AMA', 'Left Against Medical Advice' CANCELLED = 'CANCELLED', 'Cancelled' class AdmissionPriority(models.TextChoices): ROUTINE = 'ROUTINE', 'Routine' URGENT = 'URGENT', 'Urgent' EMERGENT = 'EMERGENT', 'Emergent' CRITICAL = 'CRITICAL', 'Critical' class DischargeDisposition(models.TextChoices): HOME = 'HOME', 'Home' HOME_HEALTH = 'HOME_HEALTH', 'Home with Health Services' NURSING_HOME = 'NURSING_HOME', 'Nursing Home' REHAB_FACILITY = 'REHAB_FACILITY', 'Rehabilitation Facility' HOSPICE = 'HOSPICE', 'Hospice' TRANSFER = 'TRANSFER', 'Transfer to Another Hospital' DECEASED = 'DECEASED', 'Deceased' LEFT_AMA = 'LEFT_AMA', 'Left Against Medical Advice' OTHER = 'OTHER', 'Other' class IsolationType(models.TextChoices): CONTACT = 'CONTACT', 'Contact Precautions' DROPLET = 'DROPLET', 'Droplet Precautions' AIRBORNE = 'AIRBORNE', 'Airborne Precautions' PROTECTIVE = 'PROTECTIVE', 'Protective Isolation' STRICT = 'STRICT', 'Strict Isolation' class CodeStatus(models.TextChoices): FULL_CODE = 'FULL_CODE', 'Full Code' DNR = 'DNR', 'Do Not Resuscitate' DNI = 'DNI', 'Do Not Intubate' DNR_DNI = 'DNR_DNI', 'DNR/DNI' COMFORT_CARE = 'COMFORT_CARE', 'Comfort Care Only' LIMITED = 'LIMITED', 'Limited Code' # Tenant relationship tenant = models.ForeignKey( 'core.Tenant', on_delete=models.CASCADE, related_name='admissions', help_text='Organization tenant' ) # Admission Information admission_id = models.UUIDField( default=uuid.uuid4, unique=True, editable=False, help_text='Unique admission identifier' ) # Patient Information patient = models.ForeignKey( 'patients.PatientProfile', on_delete=models.CASCADE, related_name='admissions', help_text='Admitted patient' ) # Admission Details admission_datetime = models.DateTimeField( help_text='Date and time of admission' ) admission_type = models.CharField( max_length=30, choices=AdmissionType.choices, help_text='Type of admission' ) admission_source = models.CharField( max_length=30, choices=AdmissionSource.choices, help_text='Source of admission' ) # Clinical Information chief_complaint = models.TextField( help_text='Chief complaint or reason for admission' ) admitting_diagnosis = models.TextField( help_text='Admitting diagnosis' ) secondary_diagnoses = models.JSONField( default=list, blank=True, help_text='Secondary diagnoses' ) # Provider Information admitting_physician = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='admitted_patients', help_text='Admitting physician' ) attending_physician = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='attending_patients', help_text='Attending physician' ) consulting_physicians = models.ManyToManyField( settings.AUTH_USER_MODEL, related_name='consulting_patients', blank=True, help_text='Consulting physicians' ) # Ward and Bed Assignment current_ward = models.ForeignKey( Ward, on_delete=models.CASCADE, related_name='current_admissions', help_text='Current ward assignment' ) current_bed = models.ForeignKey( Bed, on_delete=models.SET_NULL, null=True, blank=True, related_name='current_admissions', help_text='Current bed assignment' ) # Admission Status status = models.CharField( max_length=20, choices=AdmissionStatus.choices, default=AdmissionStatus.PENDING, help_text='Current admission status' ) # Priority and Acuity priority = models.CharField( max_length=20, choices=AdmissionPriority.choices, default=AdmissionPriority.ROUTINE, help_text='Admission priority' ) acuity_level = models.PositiveIntegerField( default=1, validators=[MinValueValidator(1), MaxValueValidator(5)], help_text='Patient acuity level (1-5, 5 being highest)' ) # Insurance and Financial insurance_verified = models.BooleanField( default=False, help_text='Insurance coverage verified' ) authorization_number = models.CharField( max_length=100, blank=True, null=True, help_text='Insurance authorization number' ) estimated_length_of_stay = models.PositiveIntegerField( blank=True, null=True, help_text='Estimated length of stay in days' ) # Discharge Planning discharge_planning_started = models.BooleanField( default=False, help_text='Discharge planning has been initiated' ) discharge_planner = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='discharge_planning_cases', help_text='Assigned discharge planner' ) anticipated_discharge_date = models.DateField( blank=True, null=True, help_text='Anticipated discharge date' ) # Discharge Information discharge_datetime = models.DateTimeField( blank=True, null=True, help_text='Actual discharge date and time' ) discharge_disposition = models.CharField( max_length=30, choices=DischargeDisposition.choices, blank=True, null=True, help_text='Discharge disposition' ) # Special Requirements isolation_required = models.BooleanField( default=False, help_text='Patient requires isolation precautions' ) isolation_type = models.CharField( max_length=30, choices=IsolationType.choices, blank=True, null=True, help_text='Type of isolation required' ) special_needs = models.JSONField( default=list, blank=True, help_text='Special care needs and requirements' ) # Allergies and Alerts allergies = models.JSONField( default=list, blank=True, help_text='Patient allergies' ) alerts = models.JSONField( default=list, blank=True, help_text='Clinical alerts and warnings' ) # Code Status code_status = models.CharField( max_length=20, choices=CodeStatus.choices, default=CodeStatus.FULL_CODE, help_text='Code status' ) # Advance Directives advance_directive = models.BooleanField( default=False, help_text='Advance directive on file' ) healthcare_proxy = models.CharField( max_length=200, blank=True, null=True, help_text='Healthcare proxy information' ) # Notes admission_notes = models.TextField( blank=True, null=True, help_text='Admission notes' ) # Metadata created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='created_admissions', help_text='User who created the admission' ) class Meta: db_table = 'inpatients_admission' verbose_name = 'Admission' verbose_name_plural = 'Admissions' ordering = ['-admission_datetime'] indexes = [ models.Index(fields=['tenant', 'status']), models.Index(fields=['patient', 'status']), models.Index(fields=['current_ward']), models.Index(fields=['admission_datetime']), models.Index(fields=['attending_physician']), ] def __str__(self): return f"{self.patient.get_full_name()} - {self.admission_datetime.strftime('%Y-%m-%d')}" @property def length_of_stay(self): """ Calculate current or total length of stay. """ end_time = self.discharge_datetime or timezone.now() return (end_time - self.admission_datetime).days @property def is_active(self): """ Check if admission is currently active. """ return self.status in ['PENDING', 'ADMITTED', 'TRANSFERRED'] class DischargeSummary(models.Model): """ Discharge summary model for documenting patient discharge. """ DISCHARGE_DISPOSITION_CHOICES = [ ('HOME', 'Home'), ('HOME_HEALTH', 'Home with Health Services'), ('NURSING_HOME', 'Nursing Home'), ('REHAB_FACILITY', 'Rehabilitation Facility'), ('HOSPICE', 'Hospice'), ('TRANSFER', 'Transfer to Another Hospital'), ('MORGUE', 'Morgue'), ('OTHER', 'Other'), ] TRANSPORTATION_METHOD_CHOICES = [ ('PRIVATE', 'Private Vehicle'), ('TAXI', 'Taxi'), ('AMBULANCE', 'Ambulance'), ('MEDICAL_TRANSPORT', 'Medical Transport'), ('PUBLIC_TRANSPORT', 'Public Transportation'), ('WALKING', 'Walking'), ('OTHER', 'Other'), ] PATIENT_UNDERSTANDING_CHOICES = [ ('EXCELLENT', 'Excellent'), ('GOOD', 'Good'), ('FAIR', 'Fair'), ('POOR', 'Poor'), ] READMISSION_RISK_CHOICES=[ ('LOW', 'Low Risk'), ('MODERATE', 'Moderate Risk'), ('HIGH', 'High Risk'), ('VERY_HIGH', 'Very High Risk'), ] # Admission relationship admission = models.OneToOneField( Admission, on_delete=models.CASCADE, related_name='discharge_summary', help_text='Associated admission' ) # Summary Information summary_id = models.UUIDField( default=uuid.uuid4, unique=True, editable=False, help_text='Unique summary identifier' ) # Discharge Details discharge_date = models.DateField( help_text='Discharge date' ) discharge_time = models.TimeField( help_text='Discharge time' ) length_of_stay = models.PositiveIntegerField( help_text='Total length of stay in days' ) # Clinical Summary admission_diagnosis = models.TextField( help_text='Admission diagnosis' ) final_diagnosis = models.TextField( help_text='Final/discharge diagnosis' ) secondary_diagnoses = models.JSONField( default=list, blank=True, help_text='Secondary diagnoses' ) procedures_performed = models.JSONField( default=list, blank=True, help_text='Procedures performed during stay' ) # Hospital Course hospital_course = models.TextField( help_text='Summary of hospital course' ) complications = models.TextField( blank=True, null=True, help_text='Complications during stay' ) # Discharge Medications discharge_medications = models.JSONField( default=list, blank=True, help_text='Medications at discharge' ) medication_changes = models.TextField( blank=True, null=True, help_text='Changes to medications' ) # Discharge Instructions activity_restrictions = models.TextField( blank=True, null=True, help_text='Activity restrictions' ) diet_instructions = models.TextField( blank=True, null=True, help_text='Diet instructions' ) wound_care = models.TextField( blank=True, null=True, help_text='Wound care instructions' ) special_instructions = models.TextField( blank=True, null=True, help_text='Special discharge instructions' ) # Follow-up Care follow_up_appointments = models.JSONField( default=list, blank=True, help_text='Scheduled follow-up appointments' ) follow_up_instructions = models.TextField( blank=True, null=True, help_text='Follow-up care instructions' ) # Warning Signs warning_signs = models.TextField( blank=True, null=True, help_text='Warning signs to watch for' ) when_to_call = models.TextField( blank=True, null=True, help_text='When to call healthcare provider' ) # Discharge Disposition discharge_disposition = models.CharField( max_length=30, choices=DISCHARGE_DISPOSITION_CHOICES, help_text='Discharge disposition' ) discharge_location = models.CharField( max_length=200, blank=True, null=True, help_text='Specific discharge location' ) # Transportation transportation_arranged = models.BooleanField( default=False, help_text='Transportation arranged for discharge' ) transportation_method = models.CharField( max_length=30, choices=TRANSPORTATION_METHOD_CHOICES, blank=True, null=True, help_text='Method of transportation' ) # Equipment and Supplies durable_medical_equipment = models.JSONField( default=list, blank=True, help_text='Durable medical equipment provided' ) supplies_provided = models.JSONField( default=list, blank=True, help_text='Medical supplies provided' ) # Patient Education education_provided = models.JSONField( default=list, blank=True, help_text='Patient education topics covered' ) education_materials = models.JSONField( default=list, blank=True, help_text='Educational materials provided' ) patient_understanding = models.CharField( max_length=20, choices=PATIENT_UNDERSTANDING_CHOICES, blank=True, null=True, help_text='Patient understanding of instructions' ) # Discharge Planning discharge_planner = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='planned_discharges', help_text='Discharge planner' ) social_worker_involved = models.BooleanField( default=False, help_text='Social worker involved in discharge planning' ) case_manager_involved = models.BooleanField( default=False, help_text='Case manager involved in discharge planning' ) # Quality Measures readmission_risk = models.CharField( max_length=20, choices=READMISSION_RISK_CHOICES, blank=True, null=True, help_text='Risk of readmission' ) patient_satisfaction = models.PositiveIntegerField( blank=True, null=True, validators=[MinValueValidator(1), MaxValueValidator(10)], help_text='Patient satisfaction score (1-10)' ) # Provider Information discharging_physician = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='physician_discharges', help_text='Discharging physician' ) primary_nurse = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='primary_nurse_discharges', help_text='Primary nurse' ) # Documentation summary_completed = models.BooleanField( default=False, help_text='Discharge summary completed' ) summary_signed = models.BooleanField( default=False, help_text='Discharge summary signed' ) patient_copy_provided = models.BooleanField( default=False, help_text='Copy provided to patient' ) # Metadata created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='created_discharge_summaries', help_text='User who created the summary' ) class Meta: db_table = 'inpatients_discharge_summary' verbose_name = 'Discharge Summary' verbose_name_plural = 'Discharge Summaries' ordering = ['-discharge_date'] indexes = [ models.Index(fields=['admission']), models.Index(fields=['discharge_date']), models.Index(fields=['discharging_physician']), ] def __str__(self): return f"Discharge Summary - {self.admission.patient.get_full_name()} ({self.discharge_date})" class Transfer(models.Model): """ Patient transfer model for tracking ward/bed changes. """ TRANSFER_TYPE_CHOICES = [ ('WARD', 'Ward Transfer'), ('BED', 'Bed Transfer'), ('ROOM', 'Room Transfer'), ('UNIT', 'Unit Transfer'), ('FACILITY', 'Facility Transfer'), ] STATUS_CHOICES = [ ('REQUESTED', 'Requested'), ('APPROVED', 'Approved'), ('SCHEDULED', 'Scheduled'), ('IN_PROGRESS', 'In Progress'), ('COMPLETED', 'Completed'), ('CANCELLED', 'Cancelled'), ('DELAYED', 'Delayed'), ] PRIORITY_CHOICES = [ ('ROUTINE', 'Routine'), ('URGENT', 'Urgent'), ('EMERGENT', 'Emergent'), ('STAT', 'STAT'), ] TRANSPORT_METHOD_CHOICES = [ ('WHEELCHAIR', 'Wheelchair'), ('STRETCHER', 'Stretcher'), ('BED', 'Hospital Bed'), ('AMBULATORY', 'Walking'), ('AMBULANCE', 'Ambulance'), ('OTHER', 'Other'), ] PATIENT_CONDITION_CHOICES = [ ('STABLE', 'Stable'), ('UNSTABLE', 'Unstable'), ('CRITICAL', 'Critical'), ('IMPROVING', 'Improving'), ('DETERIORATING', 'Deteriorating'), ] # Transfer Information transfer_id = models.UUIDField( default=uuid.uuid4, unique=True, editable=False, help_text='Unique transfer identifier' ) # Admission and Patient admission = models.ForeignKey( Admission, on_delete=models.CASCADE, related_name='transfers', help_text='Associated admission' ) patient = models.ForeignKey( 'patients.PatientProfile', on_delete=models.CASCADE, related_name='transfers', help_text='Patient being transferred' ) # Transfer Details transfer_type = models.CharField( max_length=20, choices=TRANSFER_TYPE_CHOICES, help_text='Type of transfer' ) # Source Location from_ward = models.ForeignKey( Ward, on_delete=models.CASCADE, related_name='transfers_from', help_text='Source ward' ) from_bed = models.ForeignKey( Bed, on_delete=models.SET_NULL, null=True, blank=True, related_name='transfers_from', help_text='Source bed' ) # Destination Location to_ward = models.ForeignKey( Ward, on_delete=models.CASCADE, related_name='transfers_to', help_text='Destination ward' ) to_bed = models.ForeignKey( Bed, on_delete=models.SET_NULL, null=True, blank=True, related_name='transfers_to', help_text='Destination bed' ) # Transfer Timing requested_datetime = models.DateTimeField( auto_now_add=True, help_text='When transfer was requested' ) scheduled_datetime = models.DateTimeField( blank=True, null=True, help_text='Scheduled transfer time' ) actual_datetime = models.DateTimeField( blank=True, null=True, help_text='Actual transfer time' ) # Transfer Status status = models.CharField( max_length=20, choices=STATUS_CHOICES, default='REQUESTED', help_text='Transfer status' ) # Transfer Reason and Priority reason = models.TextField( help_text='Reason for transfer' ) priority = models.CharField( max_length=20, choices=PRIORITY_CHOICES, default='ROUTINE', help_text='Transfer priority' ) # Staff Involved requested_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='requested_transfers', help_text='Staff member who requested transfer' ) approved_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='approved_transfers', help_text='Staff member who approved transfer' ) completed_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='completed_transfers', help_text='Staff member who completed transfer' ) # Transport Information transport_method = models.CharField( max_length=20, choices=TRANSPORT_METHOD_CHOICES, blank=True, null=True, help_text='Method of transport' ) transport_team = models.ManyToManyField( settings.AUTH_USER_MODEL, related_name='transport_assignments', blank=True, help_text='Transport team members' ) # Equipment and Supplies equipment_needed = models.JSONField( default=list, blank=True, help_text='Equipment needed for transfer' ) supplies_needed = models.JSONField( default=list, blank=True, help_text='Supplies needed for transfer' ) # Clinical Information patient_condition = models.CharField( max_length=20, choices=PATIENT_CONDITION_CHOICES, help_text='Patient condition at time of transfer' ) vital_signs = models.JSONField( default=dict, blank=True, help_text='Vital signs at transfer' ) # Handoff Information handoff_report = models.TextField( blank=True, null=True, help_text='Nursing handoff report' ) medications_transferred = models.JSONField( default=list, blank=True, help_text='Medications transferred with patient' ) # Delays and Issues delay_reason = models.TextField( blank=True, null=True, help_text='Reason for any delays' ) complications = models.TextField( blank=True, null=True, help_text='Any complications during transfer' ) # Notes notes = models.TextField( blank=True, null=True, help_text='Additional transfer notes' ) # Metadata created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'inpatients_transfer' verbose_name = 'Transfer' verbose_name_plural = 'Transfers' ordering = ['-requested_datetime'] indexes = [ models.Index(fields=['admission', 'status']), models.Index(fields=['patient']), models.Index(fields=['from_ward', 'to_ward']), models.Index(fields=['requested_datetime']), models.Index(fields=['priority']), ] def __str__(self): return f"{self.patient.get_full_name()} - {self.from_ward.name} to {self.to_ward.name}" @property def transfer_duration(self): """ Calculate transfer duration if completed. """ if self.actual_datetime and self.requested_datetime: return self.actual_datetime - self.requested_datetime return None class SurgerySchedule(models.Model): """ Surgery schedule model for tracking surgical procedures. """ SURGERY_TYPE_CHOICES = [ ('ELECTIVE', 'Elective'), ('URGENT', 'Urgent'), ('EMERGENT', 'Emergent'), ('TRAUMA', 'Trauma'), ('TRANSPLANT', 'Transplant'), ('CARDIAC', 'Cardiac'), ('NEUROSURGERY', 'Neurosurgery'), ('ORTHOPEDIC', 'Orthopedic'), ('GENERAL', 'General Surgery'), ('OTHER', 'Other'), ] ANESTHESIA_TYPE_CHOICES = [ ('GENERAL', 'General Anesthesia'), ('REGIONAL', 'Regional Anesthesia'), ('LOCAL', 'Local Anesthesia'), ('SPINAL', 'Spinal Anesthesia'), ('EPIDURAL', 'Epidural Anesthesia'), ('MAC', 'Monitored Anesthesia Care'), ('SEDATION', 'Conscious Sedation'), ('OTHER', 'Other'), ] STATUS_CHOICES = [ ('SCHEDULED', 'Scheduled'), ('CONFIRMED', 'Confirmed'), ('PREP', 'Pre-operative Prep'), ('IN_PROGRESS', 'In Progress'), ('COMPLETED', 'Completed'), ('CANCELLED', 'Cancelled'), ('POSTPONED', 'Postponed'), ('DELAYED', 'Delayed'), ] RECOVERY_LOCATION_CHOICES = [ ('PACU', 'Post-Anesthesia Care Unit'), ('ICU', 'Intensive Care Unit'), ('WARD', 'Regular Ward'), ('SAME_DAY', 'Same Day Surgery'), ('HOME', 'Home'), ('OTHER', 'Other'), ] PRIORITY_CHOICES = [ ('ROUTINE', 'Routine'), ('URGENT', 'Urgent'), ('EMERGENT', 'Emergent'), ('STAT', 'STAT'), ] # Tenant relationship tenant = models.ForeignKey( 'core.Tenant', on_delete=models.CASCADE, related_name='surgery_schedules', help_text='Organization tenant' ) # Surgery Information surgery_id = models.UUIDField( default=uuid.uuid4, unique=True, editable=False, help_text='Unique surgery identifier' ) # Patient and Admission patient = models.ForeignKey( 'patients.PatientProfile', on_delete=models.CASCADE, related_name='surgeries', help_text='Patient undergoing surgery' ) admission = models.ForeignKey( Admission, on_delete=models.CASCADE, related_name='surgeries', help_text='Associated admission' ) # Surgery Details procedure_name = models.CharField( max_length=200, help_text='Name of surgical procedure' ) procedure_code = models.CharField( max_length=20, blank=True, null=True, help_text='CPT or ICD procedure code' ) surgery_type = models.CharField( max_length=30, choices=SURGERY_TYPE_CHOICES, help_text='Type of surgery' ) # Scheduling Information scheduled_date = models.DateField( help_text='Scheduled surgery date' ) scheduled_start_time = models.TimeField( help_text='Scheduled start time' ) estimated_duration_minutes = models.PositiveIntegerField( help_text='Estimated duration in minutes' ) operating_room = models.ForeignKey( 'operating_theatre.OperatingRoom', on_delete=models.SET_NULL, related_name='surgery_operations', blank=True, null=True, help_text='Operating room assignment', ) or_block_time = models.CharField( max_length=50, blank=True, null=True, help_text='OR block time assignment' ) # Surgical Team primary_surgeon = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='primary_surgeries', help_text='Primary surgeon' ) assistant_surgeons = models.ManyToManyField( settings.AUTH_USER_MODEL, related_name='assistant_surgeries', blank=True, help_text='Assistant surgeons' ) anesthesiologist = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='inpatient_anesthesia_cases', help_text='Anesthesiologist' ) scrub_nurse = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='inpatient_scrub_cases', help_text='Scrub nurse' ) circulating_nurse = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='inpatient_circulating_cases', help_text='Circulating nurse' ) # Anesthesia Information anesthesia_type = models.CharField( max_length=30, choices=ANESTHESIA_TYPE_CHOICES, blank=True, null=True, help_text='Type of anesthesia' ) # Pre-operative Information preop_diagnosis = models.TextField( help_text='Pre-operative diagnosis' ) preop_orders = models.JSONField( default=list, blank=True, help_text='Pre-operative orders' ) consent_obtained = models.BooleanField( default=False, help_text='Surgical consent obtained' ) consent_date = models.DateTimeField( blank=True, null=True, help_text='Date consent was obtained' ) # Equipment and Supplies special_equipment = models.JSONField( default=list, blank=True, help_text='Special equipment needed' ) implants_needed = models.JSONField( default=list, blank=True, help_text='Implants or devices needed' ) blood_products = models.JSONField( default=list, blank=True, help_text='Blood products needed' ) # Surgery Status status = models.CharField( max_length=20, choices=STATUS_CHOICES, default='SCHEDULED', help_text='Surgery status' ) # Actual Timing actual_start_time = models.DateTimeField( blank=True, null=True, help_text='Actual start time' ) actual_end_time = models.DateTimeField( blank=True, null=True, help_text='Actual end time' ) actual_duration_minutes = models.PositiveIntegerField( blank=True, null=True, help_text='Actual duration in minutes' ) # Post-operative Information postop_diagnosis = models.TextField( blank=True, null=True, help_text='Post-operative diagnosis' ) procedure_performed = models.TextField( blank=True, null=True, help_text='Actual procedure performed' ) complications = models.TextField( blank=True, null=True, help_text='Intraoperative complications' ) # Recovery Information recovery_location = models.CharField( max_length=50, choices=RECOVERY_LOCATION_CHOICES, blank=True, null=True, help_text='Post-operative recovery location' ) # Priority and Urgency priority = models.CharField( max_length=20, choices=PRIORITY_CHOICES, default='ROUTINE', help_text='Surgery priority' ) # Notes surgery_notes = models.TextField( blank=True, null=True, help_text='Surgery notes' ) # Metadata created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='created_surgeries', help_text='User who created the surgery schedule' ) class Meta: db_table = 'inpatients_surgery_schedule' verbose_name = 'Surgery Schedule' verbose_name_plural = 'Surgery Schedules' ordering = ['scheduled_date', 'scheduled_start_time'] indexes = [ models.Index(fields=['tenant', 'status']), models.Index(fields=['patient']), models.Index(fields=['admission']), models.Index(fields=['scheduled_date']), models.Index(fields=['primary_surgeon']), models.Index(fields=['operating_room']), ] def __str__(self): return f"{self.procedure_name} - {self.patient.get_full_name()} ({self.scheduled_date})" @property def is_today(self): """ Check if surgery is scheduled for today. """ return self.scheduled_date == timezone.now().date() @property def duration_variance(self): """ Calculate variance between estimated and actual duration. """ if self.actual_duration_minutes: return self.actual_duration_minutes - self.estimated_duration_minutes return None