2025-10-06 15:25:37 +03:00

1423 lines
41 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.
"""
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
def mark_maintenance(self, notes=None):
"""
Mark bed for maintenance.
"""
self.status = 'MAINTENANCE'
self.last_maintenance = timezone.now()
if notes:
self.maintenance_notes = notes
self.save()
def mark_cleaned(self, user, cleaning_level='STANDARD'):
"""
Mark bed as cleaned.
"""
self.status = 'AVAILABLE'
self.last_cleaned = timezone.now()
self.cleaned_by = user
self.cleaning_level = cleaning_level
self.save()
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.
"""
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'
MORGUE = 'MORGUE', 'Morgue'
OTHER = 'OTHER', 'Other'
class TransportationMethod(models.TextChoices):
PRIVATE = 'PRIVATE', 'Private Vehicle'
TAXI = 'TAXI', 'Taxi'
AMBULANCE = 'AMBULANCE', 'Ambulance'
MEDICAL_TRANSPORT = 'MEDICAL_TRANSPORT', 'Medical Transport'
PUBLIC_TRANSPORT = 'PUBLIC_TRANSPORT', 'Public Transportation'
WALKING = 'WALKING', 'Walking'
OTHER = 'OTHER', 'Other'
class PatientUnderstanding(models.TextChoices):
EXCELLENT = 'EXCELLENT', 'Excellent'
GOOD = 'GOOD', 'Good'
FAIR = 'FAIR', 'Fair'
POOR = 'POOR', 'Poor'
class ReadmissionRisk(models.TextChoices):
LOW = 'LOW', 'Low Risk'
MODERATE = 'MODERATE', 'Moderate Risk'
HIGH = 'HIGH', 'High Risk'
VERY_HIGH = '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=DischargeDisposition.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=TransportationMethod.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=PatientUnderstanding.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=ReadmissionRisk.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.
"""
class TransferType(models.TextChoices):
WARD = 'WARD', 'Ward Transfer'
BED = 'BED', 'Bed Transfer'
ROOM = 'ROOM', 'Room Transfer'
UNIT = 'UNIT', 'Unit Transfer'
FACILITY = 'FACILITY', 'Facility Transfer'
class TransferStatus(models.TextChoices):
REQUESTED = 'REQUESTED', 'Requested'
APPROVED = 'APPROVED', 'Approved'
SCHEDULED = 'SCHEDULED', 'Scheduled'
IN_PROGRESS = 'IN_PROGRESS', 'In Progress'
COMPLETED = 'COMPLETED', 'Completed'
CANCELLED = 'CANCELLED', 'Cancelled'
DELAYED = 'DELAYED', 'Delayed'
REJECTED = 'REJECTED', 'Rejected'
class TransferPriority(models.TextChoices):
ROUTINE = 'ROUTINE', 'Routine'
URGENT = 'URGENT', 'Urgent'
EMERGENT = 'EMERGENT', 'Emergent'
STAT = 'STAT', 'STAT'
class TransportMethod(models.TextChoices):
WHEELCHAIR = 'WHEELCHAIR', 'Wheelchair'
STRETCHER = 'STRETCHER', 'Stretcher'
BED = 'BED', 'Hospital Bed'
AMBULATORY = 'AMBULATORY', 'Walking'
AMBULANCE = 'AMBULANCE', 'Ambulance'
OTHER = 'OTHER', 'Other'
# Named to avoid collision with any general PatientCondition enum you may already have
class TransferPatientCondition(models.TextChoices):
STABLE = 'STABLE', 'Stable'
UNSTABLE = 'UNSTABLE', 'Unstable'
CRITICAL = 'CRITICAL', 'Critical'
IMPROVING = 'IMPROVING', 'Improving'
DETERIORATING = '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=TransferType.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=TransferStatus.choices,
default=TransferStatus.REQUESTED,
help_text='Transfer status'
)
# Transfer Reason and Priority
reason = models.TextField(
help_text='Reason for transfer'
)
priority = models.CharField(
max_length=20,
choices=TransferPriority.choices,
default=TransferPriority.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=TransportMethod.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=TransferPatientCondition.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