Marwan Alwali 610e165e17 update
2025-09-04 19:19:52 +03:00

1725 lines
47 KiB
Python

"""
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.
"""
WARD_TYPE_CHOICES = [
('GENERAL', 'General Medical'),
('SURGICAL', 'Surgical'),
('ICU', 'Intensive Care Unit'),
('CCU', 'Cardiac Care Unit'),
('NICU', 'Neonatal ICU'),
('PICU', 'Pediatric ICU'),
('EMERGENCY', 'Emergency'),
('MATERNITY', 'Maternity'),
('PEDIATRIC', 'Pediatric'),
('ONCOLOGY', 'Oncology'),
('CARDIAC', 'Cardiac'),
('ORTHOPEDIC', 'Orthopedic'),
('NEUROLOGY', 'Neurology'),
('PSYCHIATRY', 'Psychiatry'),
('REHABILITATION', 'Rehabilitation'),
('ISOLATION', 'Isolation'),
('STEP_DOWN', 'Step Down'),
('OTHER', 'Other'),
]
SPECIALITY_CHOICES = [
('GENERAL_MEDICINE', 'General Medicine'),
('SURGERY', 'Surgery'),
('CARDIOLOGY', 'Cardiology'),
('NEUROLOGY', 'Neurology'),
('ONCOLOGY', 'Oncology'),
('PEDIATRICS', 'Pediatrics'),
('OBSTETRICS', 'Obstetrics'),
('GYNECOLOGY', 'Gynecology'),
('ORTHOPEDICS', 'Orthopedics'),
('PSYCHIATRY', 'Psychiatry'),
('EMERGENCY', 'Emergency Medicine'),
('CRITICAL_CARE', 'Critical Care'),
('REHABILITATION', 'Rehabilitation'),
('OTHER', 'Other'),
]
GENDER_RESTRICTIONS_CHOICES = [
('NONE', 'No Restrictions'),
('MALE_ONLY', 'Male 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=WARD_TYPE_CHOICES,
help_text='Type of ward'
)
specialty = models.CharField(
max_length=100,
choices=SPECIALITY_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.CharField(
max_length=100,
help_text='Building name or identifier'
)
floor = models.CharField(
max_length=20,
help_text='Floor number or identifier'
)
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=GENDER_RESTRICTIONS_CHOICES,
default='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.
"""
BED_TYPE_CHOICES = [
('STANDARD', 'Standard Bed'),
('ICU', 'ICU Bed'),
('CARDIAC', 'Cardiac Monitoring'),
('ISOLATION', 'Isolation Bed'),
('BARIATRIC', 'Bariatric Bed'),
('PEDIATRIC', 'Pediatric Bed'),
('NEONATAL', 'Neonatal Bed'),
('MATERNITY', 'Maternity Bed'),
('PSYCHIATRIC', 'Psychiatric Bed'),
('STRETCHER', 'Stretcher/Gurney'),
('RECLINER', 'Recliner Chair'),
('OTHER', 'Other'),
]
ROOM_TYPE_CHOICES = [
('PRIVATE', 'Private Room'),
('SEMI_PRIVATE', 'Semi-Private'),
('SHARED', 'Shared Room'),
('ICU', 'ICU Room'),
('ISOLATION', 'Isolation Room'),
]
STATUS_CHOICES = [
('AVAILABLE', 'Available'),
('OCCUPIED', 'Occupied'),
('RESERVED', 'Reserved'),
('MAINTENANCE', 'Under Maintenance'),
('CLEANING', 'Being Cleaned'),
('OUT_OF_ORDER', 'Out of Order'),
('BLOCKED', 'Blocked'),
]
CLEANING_LEVEL_CHOICES = [
('STANDARD', 'Standard Cleaning'),
('DEEP', 'Deep Cleaning'),
('ISOLATION', 'Isolation Cleaning'),
('TERMINAL', 'Terminal Cleaning'),
]
BED_POSITION_CHOICES = [
('A', 'A'),
('B', 'B'),
('C', 'C'),
('D', 'D'),
('E', 'E'),
('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 and Configuration
bed_type = models.CharField(
max_length=30,
choices=BED_TYPE_CHOICES,
default='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_active_out_of_service = models.BooleanField(
default=True,
)
room_type = models.CharField(
max_length=20,
choices=ROOM_TYPE_CHOICES,
help_text='Type of room'
)
# Bed Status
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='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.JSONField(
default=list,
blank=True,
help_text='Equipment available with this bed'
)
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=CLEANING_LEVEL_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=BED_POSITION_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_patient']),
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.
"""
ADMISSION_TYPE_CHOICES = [
('EMERGENCY', 'Emergency Admission'),
('ELECTIVE', 'Elective Admission'),
('URGENT', 'Urgent Admission'),
('OBSERVATION', 'Observation'),
('DAY_SURGERY', 'Day Surgery'),
('TRANSFER', 'Transfer from Another Facility'),
('READMISSION', 'Readmission'),
('DIRECT', 'Direct Admission'),
('OTHER', 'Other'),
]
ADMISSION_SOURCE_CHOICES = [
('EMERGENCY', 'Emergency Department'),
('OUTPATIENT', 'Outpatient Clinic'),
('PHYSICIAN_OFFICE', 'Physician Office'),
('TRANSFER', 'Transfer from Another Hospital'),
('NURSING_HOME', 'Nursing Home'),
('HOME', 'Home'),
('AMBULATORY_SURGERY', 'Ambulatory Surgery'),
('OTHER_FACILITY', 'Other Healthcare Facility'),
('OTHER', 'Other'),
]
STATUS_CHOICES = [
('PENDING', 'Pending Admission'),
('ADMITTED', 'Admitted'),
('TRANSFERRED', 'Transferred'),
('DISCHARGED', 'Discharged'),
('DECEASED', 'Deceased'),
('LEFT_AMA', 'Left Against Medical Advice'),
('CANCELLED', 'Cancelled'),
]
PRIORITY_CHOICES = [
('ROUTINE', 'Routine'),
('URGENT', 'Urgent'),
('EMERGENT', 'Emergent'),
('CRITICAL', 'Critical'),
]
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'),
('DECEASED', 'Deceased'),
('LEFT_AMA', 'Left Against Medical Advice'),
('OTHER', 'Other'),
]
ISOLATION_TYPE_CHOICES = [
('CONTACT', 'Contact Precautions'),
('DROPLET', 'Droplet Precautions'),
('AIRBORNE', 'Airborne Precautions'),
('PROTECTIVE', 'Protective Isolation'),
('STRICT', 'Strict Isolation'),
]
CODE_STATUS_CHOICES = [
('FULL_CODE', 'Full Code'),
('DNR', 'Do Not Resuscitate'),
('DNI', 'Do Not Intubate'),
('DNR_DNI', 'DNR/DNI'),
('COMFORT_CARE', 'Comfort Care Only'),
('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=ADMISSION_TYPE_CHOICES,
help_text='Type of admission'
)
admission_source = models.CharField(
max_length=30,
choices=ADMISSION_SOURCE_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=STATUS_CHOICES,
default='PENDING',
help_text='Current admission status'
)
# Priority and Acuity
priority = models.CharField(
max_length=20,
choices=PRIORITY_CHOICES,
default='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=DISCHARGE_DISPOSITION_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=ISOLATION_TYPE_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=CODE_STATUS_CHOICES,
default='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 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 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'),
('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 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
operating_room = models.CharField(
max_length=20,
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