2025-08-12 13:33:25 +03:00

1679 lines
46 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
class Ward(models.Model):
"""
Hospital ward model for organizing patient care areas.
"""
# 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=20,
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=[
('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'),
],
help_text='Type of ward'
)
specialty = models.CharField(
max_length=100,
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'),
],
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=[
('NONE', 'No Restrictions'),
('MALE_ONLY', 'Male Only'),
('FEMALE_ONLY', 'Female Only'),
],
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 f"{self.name} ({self.ward_id})"
@property
def occupancy_rate(self):
"""
Calculate current occupancy rate.
"""
occupied_beds = self.beds.filter(status='OCCUPIED').count()
if self.total_beds > 0:
return (occupied_beds / self.total_beds) * 100
return 0
@property
def available_beds(self):
"""
Get count of available beds.
"""
return self.beds.filter(status='AVAILABLE').count()
class Bed(models.Model):
"""
Hospital bed model for tracking individual bed status and assignments.
"""
# 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=[
('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'),
],
default='STANDARD',
help_text='Type of bed'
)
room_type = models.CharField(
max_length=20,
choices=[
('PRIVATE', 'Private Room'),
('SEMI_PRIVATE', 'Semi-Private'),
('SHARED', 'Shared Room'),
('ICU', 'ICU Room'),
('ISOLATION', 'Isolation Room'),
],
help_text='Type of room'
)
# Bed Status
status = models.CharField(
max_length=20,
choices=[
('AVAILABLE', 'Available'),
('OCCUPIED', 'Occupied'),
('RESERVED', 'Reserved'),
('MAINTENANCE', 'Under Maintenance'),
('CLEANING', 'Being Cleaned'),
('OUT_OF_ORDER', 'Out of Order'),
('BLOCKED', 'Blocked'),
],
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=[
('STANDARD', 'Standard Cleaning'),
('DEEP', 'Deep Cleaning'),
('ISOLATION', 'Isolation Cleaning'),
('TERMINAL', 'Terminal Cleaning'),
],
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=[
('WINDOW', 'Window Side'),
('DOOR', 'Door Side'),
('CENTER', 'Center'),
('CORNER', 'Corner'),
],
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.
"""
# 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=[
('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'),
],
help_text='Type of admission'
)
admission_source = models.CharField(
max_length=30,
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'),
],
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=[
('PENDING', 'Pending Admission'),
('ADMITTED', 'Admitted'),
('TRANSFERRED', 'Transferred'),
('DISCHARGED', 'Discharged'),
('DECEASED', 'Deceased'),
('LEFT_AMA', 'Left Against Medical Advice'),
('CANCELLED', 'Cancelled'),
],
default='PENDING',
help_text='Current admission status'
)
# Priority and Acuity
priority = models.CharField(
max_length=20,
choices=[
('ROUTINE', 'Routine'),
('URGENT', 'Urgent'),
('EMERGENT', 'Emergent'),
('CRITICAL', 'Critical'),
],
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=[
('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'),
],
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=[
('CONTACT', 'Contact Precautions'),
('DROPLET', 'Droplet Precautions'),
('AIRBORNE', 'Airborne Precautions'),
('PROTECTIVE', 'Protective Isolation'),
('STRICT', 'Strict Isolation'),
],
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=[
('FULL_CODE', 'Full Code'),
('DNR', 'Do Not Resuscitate'),
('DNI', 'Do Not Intubate'),
('DNR_DNI', 'DNR/DNI'),
('COMFORT_CARE', 'Comfort Care Only'),
('LIMITED', 'Limited Code'),
],
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 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=[
('WARD', 'Ward Transfer'),
('BED', 'Bed Transfer'),
('ROOM', 'Room Transfer'),
('UNIT', 'Unit Transfer'),
('FACILITY', 'Facility Transfer'),
],
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=[
('REQUESTED', 'Requested'),
('APPROVED', 'Approved'),
('SCHEDULED', 'Scheduled'),
('IN_PROGRESS', 'In Progress'),
('COMPLETED', 'Completed'),
('CANCELLED', 'Cancelled'),
('DELAYED', 'Delayed'),
],
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=[
('ROUTINE', 'Routine'),
('URGENT', 'Urgent'),
('EMERGENT', 'Emergent'),
('STAT', 'STAT'),
],
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=[
('WHEELCHAIR', 'Wheelchair'),
('STRETCHER', 'Stretcher'),
('BED', 'Hospital Bed'),
('AMBULATORY', 'Walking'),
('AMBULANCE', 'Ambulance'),
('OTHER', 'Other'),
],
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=[
('STABLE', 'Stable'),
('UNSTABLE', 'Unstable'),
('CRITICAL', 'Critical'),
('IMPROVING', 'Improving'),
('DETERIORATING', 'Deteriorating'),
],
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.
"""
# 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=[
('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'),
],
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=[
('PRIVATE', 'Private Vehicle'),
('TAXI', 'Taxi'),
('AMBULANCE', 'Ambulance'),
('MEDICAL_TRANSPORT', 'Medical Transport'),
('PUBLIC_TRANSPORT', 'Public Transportation'),
('WALKING', 'Walking'),
('OTHER', 'Other'),
],
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=[
('EXCELLENT', 'Excellent'),
('GOOD', 'Good'),
('FAIR', 'Fair'),
('POOR', 'Poor'),
],
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=[
('LOW', 'Low Risk'),
('MODERATE', 'Moderate Risk'),
('HIGH', 'High Risk'),
('VERY_HIGH', 'Very High Risk'),
],
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.
"""
# 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=[
('ELECTIVE', 'Elective'),
('URGENT', 'Urgent'),
('EMERGENT', 'Emergent'),
('TRAUMA', 'Trauma'),
('TRANSPLANT', 'Transplant'),
('CARDIAC', 'Cardiac'),
('NEUROSURGERY', 'Neurosurgery'),
('ORTHOPEDIC', 'Orthopedic'),
('GENERAL', 'General Surgery'),
('OTHER', 'Other'),
],
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=[
('GENERAL', 'General Anesthesia'),
('REGIONAL', 'Regional Anesthesia'),
('LOCAL', 'Local Anesthesia'),
('SPINAL', 'Spinal Anesthesia'),
('EPIDURAL', 'Epidural Anesthesia'),
('MAC', 'Monitored Anesthesia Care'),
('SEDATION', 'Conscious Sedation'),
('OTHER', 'Other'),
],
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=[
('SCHEDULED', 'Scheduled'),
('CONFIRMED', 'Confirmed'),
('PREP', 'Pre-operative Prep'),
('IN_PROGRESS', 'In Progress'),
('COMPLETED', 'Completed'),
('CANCELLED', 'Cancelled'),
('POSTPONED', 'Postponed'),
('DELAYED', 'Delayed'),
],
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=[
('PACU', 'Post-Anesthesia Care Unit'),
('ICU', 'Intensive Care Unit'),
('WARD', 'Regular Ward'),
('SAME_DAY', 'Same Day Surgery'),
('HOME', 'Home'),
('OTHER', 'Other'),
],
blank=True,
null=True,
help_text='Post-operative recovery location'
)
# Priority and Urgency
priority = models.CharField(
max_length=20,
choices=[
('ROUTINE', 'Routine'),
('URGENT', 'Urgent'),
('EMERGENT', 'Emergent'),
('STAT', 'STAT'),
],
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