751 lines
35 KiB
Python
751 lines
35 KiB
Python
import os
|
|
import django
|
|
|
|
# Set up Django environment
|
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hospital_management.settings')
|
|
django.setup()
|
|
|
|
import random
|
|
from datetime import datetime, timedelta, date, time
|
|
from django.utils import timezone as django_timezone
|
|
from inpatients.models import Ward, Bed, Admission, Transfer, DischargeSummary, SurgerySchedule
|
|
from accounts.models import User
|
|
from patients.models import PatientProfile
|
|
from core.models import Tenant
|
|
import uuid
|
|
|
|
# Saudi-specific data constants
|
|
SAUDI_WARD_NAMES = [
|
|
'King Fahd Ward', 'King Abdulaziz Ward', 'Prince Sultan Ward', 'Princess Noura Ward',
|
|
'Al-Faisal Ward', 'Al-Rashid Ward', 'Al-Salam Ward', 'Al-Noor Ward',
|
|
'Al-Shifa Ward', 'Al-Amal Ward', 'Al-Rahma Ward', 'Al-Baraka Ward',
|
|
'Cardiac Care Unit', 'Intensive Care Unit', 'Pediatric ICU', 'Neonatal ICU',
|
|
'Medical Ward A', 'Medical Ward B', 'Surgical Ward A', 'Surgical Ward B',
|
|
'Orthopedic Ward', 'Maternity Ward', 'Pediatric Ward', 'Oncology Ward'
|
|
]
|
|
|
|
SAUDI_SPECIALTIES = ['GENERAL_MEDICINE','SURGERY','CARDIOLOGY','NEUROLOGY','ONCOLOGY',
|
|
'PEDIATRICS','OBSTETRICS','GYNECOLOGY','ORTHOPEDICS','PSYCHIATRY',
|
|
'EMERGENCY','CRITICAL_CARE','REHABILITATION',
|
|
]
|
|
|
|
SAUDI_BUILDINGS = [
|
|
'King Fahd Medical Tower', 'Prince Sultan Building', 'Al-Faisal Complex',
|
|
'Medical Center A', 'Medical Center B', 'Specialty Care Building',
|
|
'Emergency Services Building', 'Outpatient Complex', 'Research Center'
|
|
]
|
|
|
|
SAUDI_WINGS = [
|
|
'North Wing', 'South Wing', 'East Wing', 'West Wing', 'Central Wing',
|
|
'VIP Wing', 'Family Wing', 'Isolation Wing'
|
|
]
|
|
|
|
SAUDI_COMMON_DIAGNOSES = [
|
|
'Diabetes Mellitus Type 2', 'Hypertension', 'Coronary Artery Disease',
|
|
'Chronic Kidney Disease', 'Heart Failure', 'Pneumonia', 'Gastritis',
|
|
'Cholecystitis', 'Appendicitis', 'Urinary Tract Infection',
|
|
'Acute Myocardial Infarction', 'Stroke', 'Pulmonary Embolism',
|
|
'Deep Vein Thrombosis', 'Sepsis', 'Acute Renal Failure',
|
|
'Chronic Obstructive Pulmonary Disease', 'Asthma', 'Anemia'
|
|
]
|
|
|
|
SAUDI_SURGICAL_PROCEDURES = [
|
|
'Coronary Artery Bypass Graft', 'Percutaneous Coronary Intervention',
|
|
'Appendectomy', 'Cholecystectomy', 'Hernia Repair', 'Hip Replacement',
|
|
'Knee Replacement', 'Cataract Surgery', 'Thyroidectomy',
|
|
'Prostatectomy', 'Hysterectomy', 'Cesarean Section',
|
|
'Arthroscopy', 'Endoscopy', 'Colonoscopy', 'Angioplasty'
|
|
]
|
|
|
|
SAUDI_MEDICATIONS = [
|
|
'Metformin', 'Insulin', 'Lisinopril', 'Amlodipine', 'Atorvastatin',
|
|
'Aspirin', 'Warfarin', 'Furosemide', 'Omeprazole', 'Paracetamol',
|
|
'Amoxicillin', 'Ciprofloxacin', 'Prednisolone', 'Morphine', 'Tramadol'
|
|
]
|
|
|
|
|
|
def create_saudi_wards(tenants, wards_per_tenant=12):
|
|
"""Create Saudi hospital wards"""
|
|
wards = []
|
|
|
|
ward_types =['GENERAL','SURGICAL','ICU','CCU','NICU','PICU','EMERGENCY','MATERNITY','PEDIATRIC',
|
|
'ONCOLOGY','CARDIAC','ORTHOPEDIC','NEUROLOGY','PSYCHIATRY','REHABILITATION',
|
|
'ISOLATION','STEP_DOWN',
|
|
]
|
|
|
|
for tenant in tenants:
|
|
for i in range(wards_per_tenant):
|
|
ward_name = random.choice(SAUDI_WARD_NAMES)
|
|
ward_type = random.choice(ward_types)
|
|
specialty = random.choice(SAUDI_SPECIALTIES)
|
|
|
|
# Ensure unique ward names per tenant
|
|
counter = 1
|
|
original_name = ward_name
|
|
while Ward.objects.filter(tenant=tenant, name=ward_name).exists():
|
|
ward_name = f"{original_name} {counter}"
|
|
counter += 1
|
|
|
|
# Get nursing staff for the ward
|
|
nurses = User.objects.filter(
|
|
tenant=tenant,
|
|
employee_profile__role__in=['NURSE', 'NURSE_PRACTITIONER']
|
|
)
|
|
physicians = User.objects.filter(
|
|
tenant=tenant,
|
|
employee_profile__role='PHYSICIAN'
|
|
)
|
|
|
|
nurse_manager = random.choice(nurses) if nurses else None
|
|
attending_physicians = list(physicians[:random.randint(2, 5)]) if physicians else []
|
|
|
|
total_beds = random.randint(20, 60)
|
|
private_rooms = random.randint(5, 15)
|
|
semi_private_rooms = random.randint(8, 20)
|
|
shared_rooms = total_beds - private_rooms - (semi_private_rooms * 2)
|
|
|
|
# Calculate nurse to patient ratio as decimal (e.g., 1:4 = 0.25, 1:5 = 0.2)
|
|
ratio_options = [4, 5, 6, 8] # 1:4, 1:5, 1:6, 1:8
|
|
selected_ratio = random.choice(ratio_options)
|
|
nurse_patient_ratio = round(1.0 / selected_ratio, 3) # Convert to decimal
|
|
|
|
ward = Ward.objects.create(
|
|
tenant=tenant,
|
|
ward_id=uuid.uuid4(),
|
|
name=ward_name,
|
|
description=f"Specialized {specialty.lower()} ward providing comprehensive inpatient care",
|
|
ward_type=ward_type,
|
|
specialty=specialty,
|
|
total_beds=total_beds,
|
|
private_rooms=private_rooms,
|
|
semi_private_rooms=semi_private_rooms,
|
|
shared_rooms=max(0, shared_rooms),
|
|
building=random.choice(SAUDI_BUILDINGS),
|
|
floor=random.randint(1, 10),
|
|
wing=random.choice(SAUDI_WINGS),
|
|
nurse_manager=nurse_manager,
|
|
min_nurses_day=random.randint(8, 15),
|
|
min_nurses_night=random.randint(5, 10),
|
|
nurse_to_patient_ratio=nurse_patient_ratio, # Now using decimal value
|
|
equipment_list=[
|
|
'Cardiac Monitors', 'IV Pumps', 'Ventilators', 'Defibrillators',
|
|
'Oxygen Supply', 'Suction Units', 'Patient Beds', 'Wheelchairs'
|
|
],
|
|
special_features=[
|
|
'Prayer Room', 'Family Waiting Area', 'Isolation Rooms',
|
|
'Nurse Station', 'Clean Utility Room', 'Dirty Utility Room'
|
|
],
|
|
admission_criteria=f"Patients requiring {specialty.lower()} specialized care",
|
|
age_restrictions=random.choice([
|
|
'Adults only (18+)', 'Pediatric only (<18)', 'All ages', 'Adults (16+)'
|
|
]),
|
|
gender_restrictions=random.choice([
|
|
'Mixed gender', 'Male only', 'Female only', 'Separate sections'
|
|
]),
|
|
is_active=True,
|
|
is_accepting_admissions=random.choice([True, True, True, False]),
|
|
closure_reason=None if random.choice([True, True, True, False]) else 'Maintenance',
|
|
phone_number=f"+966-11-{random.randint(100, 999)}-{random.randint(1000, 9999)}",
|
|
extension=f"{random.randint(1000, 9999)}",
|
|
created_at=django_timezone.now() - timedelta(days=random.randint(30, 365)),
|
|
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 30))
|
|
)
|
|
|
|
# Set attending physicians many-to-many relationship
|
|
if attending_physicians:
|
|
ward.attending_physicians.set(attending_physicians)
|
|
|
|
wards.append(ward)
|
|
|
|
print(f"Created {wards_per_tenant} wards for {tenant.name}")
|
|
|
|
return wards
|
|
|
|
|
|
def create_saudi_beds(wards):
|
|
"""Create beds for Saudi hospital wards"""
|
|
beds = []
|
|
|
|
bed_types = [
|
|
'STANDARD','ICU','CARDIAC','ISOLATION','BARIATRIC','PEDIATRIC',
|
|
'NEONATAL','MATERNITY','PSYCHIATRIC','STRETCHER','RECLINER'
|
|
]
|
|
|
|
room_types = ['PRIVATE','SEMI_PRIVATE','SHARED','ICU','ISOLATION']
|
|
|
|
bed_statuses = ['AVAILABLE','OCCUPIED','RESERVED','MAINTENANCE','CLEANING','OUT_OF_ORDER','BLOCKED']
|
|
|
|
cleaning_levels = ['STANDARD','DEEP','ISOLATION','TERMINAL']
|
|
|
|
for ward in wards:
|
|
for bed_num in range(1, ward.total_beds + 1):
|
|
bed_number = f"{ward.name[:3].upper()}-{bed_num:03d}"
|
|
room_number = f"{ward.floor}{random.randint(10, 99)}"
|
|
|
|
bed_type = random.choice(bed_types)
|
|
room_type = random.choice(room_types)
|
|
status = random.choice(bed_statuses)
|
|
|
|
# Determine bed position
|
|
bed_positions = ['A', 'B', 'C', 'D'] if room_type[0] == 'shared' else ['A', 'B'] if room_type[
|
|
0] == 'semi_private' else [
|
|
'A']
|
|
bed_position = random.choice(bed_positions)
|
|
|
|
bed = Bed.objects.create(
|
|
ward=ward,
|
|
bed_number=bed_number,
|
|
room_number=room_number,
|
|
bed_type=bed_type,
|
|
room_type=room_type,
|
|
status=status,
|
|
occupied_since=django_timezone.now() - timedelta(
|
|
days=random.randint(1, 30)) if status == 'OCCUPIED' else None,
|
|
reserved_until=django_timezone.now() + timedelta(
|
|
hours=random.randint(1, 24)) if status == 'AVAILABLE' and random.choice([True, False]) else None,
|
|
equipment=[
|
|
'Cardiac Monitor', 'IV Pole', 'Oxygen Outlet', 'Suction Outlet',
|
|
'Nurse Call System', 'Bedside Table', 'Privacy Curtain'
|
|
],
|
|
features=[
|
|
'Adjustable Height', 'Side Rails', 'Trendelenburg Position',
|
|
'Reverse Trendelenburg', 'Patient Controls', 'LED Reading Light'
|
|
],
|
|
last_maintenance=django_timezone.now() - timedelta(days=random.randint(7, 90)),
|
|
next_maintenance=django_timezone.now() + timedelta(days=random.randint(30, 180)),
|
|
maintenance_notes="Routine maintenance completed" if random.choice([True, False]) else None,
|
|
last_cleaned=django_timezone.now() - timedelta(hours=random.randint(1, 24)),
|
|
cleaning_level=random.choice(cleaning_levels),
|
|
blocked_reason="Maintenance required" if status == 'blocked' else None,
|
|
blocked_until=django_timezone.now() + timedelta(
|
|
hours=random.randint(2, 48)) if status == 'blocked' else None,
|
|
bed_position=bed_position,
|
|
created_at=django_timezone.now() - timedelta(days=random.randint(1, 365)),
|
|
updated_at=django_timezone.now() - timedelta(hours=random.randint(1, 48))
|
|
)
|
|
beds.append(bed)
|
|
|
|
print(f"Created {len(beds)} beds across all wards")
|
|
return beds
|
|
|
|
|
|
def create_saudi_admissions(tenants, beds, admissions_per_tenant=100):
|
|
"""Create Saudi hospital admissions"""
|
|
admissions = []
|
|
|
|
admission_types = ['EMERGENCY','ELECTIVE','URGENT','OBSERVATION','DAY_SURGERY',
|
|
'TRANSFER','READMISSION','DIRECT',
|
|
]
|
|
|
|
admission_sources = ['EMERGENCY','OUTPATIENT','PHYSICIAN_OFFICE','TRANSFER',
|
|
'NURSING_HOME','HOME','AMBULATORY_SURGERY'
|
|
]
|
|
|
|
statuses = ['PENDING','ADMITTED','TRANSFERRED','DISCHARGED','DECEASED','LEFT_AMA','CANCELLED',]
|
|
priorities = ['ROUTINE','URGENT','EMERGENT','CRITICAL']
|
|
acuity_levels = ['1', '2', '3', '4', '5'] # 1 = highest acuity
|
|
|
|
isolation_types = ['CONTACT','DROPLET','AIRBORNE','PROTECTIVE','STRICT',]
|
|
|
|
code_statuses = ['FULL_CODE','DNR','DNI','DNR_DNI','COMFORT_CARE','LIMITED',]
|
|
|
|
for tenant in tenants:
|
|
# Get patients and staff for this tenant
|
|
patients = list(PatientProfile.objects.filter(tenant=tenant))
|
|
physicians = list(User.objects.filter(tenant=tenant, employee_profile__role='PHYSICIAN'))
|
|
nurses = list(User.objects.filter(tenant=tenant, employee_profile__role__in=['NURSE', 'NURSE_PRACTITIONER']))
|
|
tenant_beds = [bed for bed in beds if bed.ward.tenant == tenant]
|
|
|
|
if not patients or not physicians or not tenant_beds:
|
|
continue
|
|
|
|
for i in range(admissions_per_tenant):
|
|
patient = random.choice(patients)
|
|
admitting_physician = random.choice(physicians)
|
|
attending_physician = random.choice(physicians)
|
|
current_bed = random.choice(tenant_beds)
|
|
current_ward = current_bed.ward
|
|
|
|
admission_datetime = django_timezone.now() - timedelta(days=random.randint(0, 30))
|
|
status = random.choice(statuses)
|
|
|
|
# If discharged, set discharge datetime
|
|
discharge_datetime = None
|
|
if status == 'discharged':
|
|
discharge_datetime = admission_datetime + timedelta(days=random.randint(1, 14))
|
|
|
|
admission = Admission.objects.create(
|
|
tenant=tenant,
|
|
admission_id=uuid.uuid4(),
|
|
patient=patient,
|
|
admission_datetime=admission_datetime,
|
|
admission_type=random.choice(admission_types),
|
|
admission_source=random.choice(admission_sources),
|
|
chief_complaint=random.choice([
|
|
'Chest pain', 'Shortness of breath', 'Abdominal pain',
|
|
'Fever and chills', 'Weakness and fatigue', 'Nausea and vomiting',
|
|
'Headache', 'Dizziness', 'Joint pain', 'Back pain'
|
|
]),
|
|
admitting_diagnosis=random.choice(SAUDI_COMMON_DIAGNOSES),
|
|
secondary_diagnoses=[random.choice(SAUDI_COMMON_DIAGNOSES) for _ in range(random.randint(0, 3))],
|
|
admitting_physician=admitting_physician,
|
|
attending_physician=attending_physician,
|
|
current_ward=current_ward,
|
|
current_bed=current_bed,
|
|
status=status,
|
|
priority=random.choice(priorities),
|
|
acuity_level=random.choice(acuity_levels),
|
|
insurance_verified=random.choice([True, False]),
|
|
authorization_number=f"AUTH-{random.randint(100000, 999999)}" if random.choice([True, False]) else None,
|
|
estimated_length_of_stay=random.randint(2, 14),
|
|
discharge_planning_started=random.choice([True, False]),
|
|
anticipated_discharge_date=admission_datetime.date() + timedelta(days=random.randint(1, 10)),
|
|
discharge_datetime=discharge_datetime,
|
|
isolation_required=random.choice([True, False, False, False]),
|
|
isolation_type=random.choice(isolation_types) if random.choice([True, False]) else None,
|
|
special_needs=[
|
|
'Interpreter services', 'Dietary restrictions', 'Mobility assistance',
|
|
'Vision/hearing impairment', 'Cultural considerations'
|
|
] if random.choice([True, False]) else [],
|
|
allergies=[
|
|
'Penicillin', 'Sulfa drugs', 'Shellfish', 'Latex', 'Contrast dye'
|
|
] if random.choice([True, False]) else [],
|
|
alerts=[
|
|
'Fall risk', 'Suicide risk', 'Flight risk', 'Combative',
|
|
'Infection control', 'Allergy alert'
|
|
] if random.choice([True, False]) else [],
|
|
code_status=random.choice(code_statuses),
|
|
advance_directive=random.choice([True, False]),
|
|
admission_notes=f"Patient admitted with {random.choice(SAUDI_COMMON_DIAGNOSES).lower()} requiring inpatient management",
|
|
created_at=admission_datetime,
|
|
updated_at=admission_datetime + timedelta(hours=random.randint(1, 24))
|
|
)
|
|
|
|
# Set consulting physicians
|
|
consulting_physicians = random.sample(physicians, random.randint(0, 3))
|
|
if consulting_physicians:
|
|
admission.consulting_physicians.set(consulting_physicians)
|
|
|
|
# Update bed status if occupied
|
|
if status == 'active':
|
|
current_bed.status = 'occupied'
|
|
current_bed.current_patient = patient
|
|
current_bed.current_admission = admission
|
|
current_bed.occupied_since = admission_datetime
|
|
current_bed.save()
|
|
|
|
admissions.append(admission)
|
|
|
|
print(f"Created {admissions_per_tenant} admissions for {tenant.name}")
|
|
|
|
return admissions
|
|
|
|
|
|
def create_saudi_transfers(admissions):
|
|
"""Create transfer records for Saudi patients"""
|
|
transfers = []
|
|
|
|
transfer_types = ['WARD','BED','ROOM','UNIT','FACILITY',]
|
|
|
|
transfer_statuses = ['REQUESTED','APPROVED','SCHEDULED','IN_PROGRESS','COMPLETED','CANCELLED','DELAYED',]
|
|
transport_methods = ['WHEELCHAIR','STRETCHER','BED','AMBULATORY','AMBULANCE','OTHER',]
|
|
priorities = ['ROUTINE','URGENT','EMERGENT','STAT',]
|
|
|
|
# Create transfers for 30% of admissions
|
|
for admission in random.sample(admissions, int(len(admissions) * 0.3)):
|
|
transfer_type = random.choice(transfer_types)
|
|
|
|
# Get available beds in different wards
|
|
available_beds = Bed.objects.filter(
|
|
ward__tenant=admission.tenant,
|
|
status='AVAILABLE'
|
|
).exclude(ward=admission.current_ward)
|
|
|
|
if not available_beds:
|
|
continue
|
|
|
|
to_bed = random.choice(available_beds)
|
|
to_ward = to_bed.ward
|
|
|
|
# Get users who can request transfers (physicians, nurses, etc.)
|
|
requesting_users = User.objects.filter(
|
|
tenant=admission.tenant,
|
|
is_active=True,
|
|
employee_profile__role__in=['PHYSICIAN', 'NURSE', 'NURSE_PRACTITIONER', 'PHYSICIAN_ASSISTANT']
|
|
)
|
|
|
|
if not requesting_users.exists():
|
|
# Fallback to any active user from the tenant
|
|
requesting_users = User.objects.filter(tenant=admission.tenant, is_active=True)
|
|
|
|
if not requesting_users.exists():
|
|
continue # Skip if no users available
|
|
|
|
requested_by = random.choice(requesting_users)
|
|
|
|
requested_datetime = admission.admission_datetime + timedelta(hours=random.randint(1, 72))
|
|
status = random.choice(transfer_statuses)
|
|
|
|
scheduled_datetime = None
|
|
actual_datetime = None
|
|
|
|
if status in ['APPROVED', 'IN_PROGRESS', 'COMPLETED']:
|
|
scheduled_datetime = requested_datetime + timedelta(hours=random.randint(1, 12))
|
|
|
|
if status in ['IN_PROGRESS', 'COMPLETED']:
|
|
actual_datetime = scheduled_datetime + timedelta(minutes=random.randint(-30, 60))
|
|
|
|
# Get transport team members
|
|
transport_team_members = []
|
|
nurses = User.objects.filter(
|
|
tenant=admission.tenant,
|
|
employee_profile__role__in=['NURSE', 'NURSE_PRACTITIONER']
|
|
)
|
|
if nurses:
|
|
transport_team_members = random.sample(list(nurses), min(2, nurses.count()))
|
|
|
|
transfer = Transfer.objects.create(
|
|
transfer_id=uuid.uuid4(),
|
|
admission=admission,
|
|
patient=admission.patient,
|
|
transfer_type=transfer_type,
|
|
from_ward=admission.current_ward,
|
|
from_bed=admission.current_bed,
|
|
to_ward=to_ward,
|
|
to_bed=to_bed,
|
|
requested_by=requested_by, # Add the required field
|
|
requested_datetime=requested_datetime,
|
|
scheduled_datetime=scheduled_datetime,
|
|
actual_datetime=actual_datetime,
|
|
status=status,
|
|
reason=random.choice([
|
|
'Clinical deterioration', 'Need for higher level of care',
|
|
'Bed availability', 'Specialist consultation required',
|
|
'Patient preference', 'Isolation requirements'
|
|
]),
|
|
priority=random.choice(priorities),
|
|
transport_method=random.choice(transport_methods),
|
|
equipment_needed=['IV pole', 'Oxygen tank', 'Monitor'] if random.choice([True, False]) else [],
|
|
supplies_needed=['Medications', 'Patient chart', 'Personal items'] if random.choice([True, False]) else [],
|
|
patient_condition=random.choice(['stable', 'unstable', 'critical', 'improving']),
|
|
vital_signs={
|
|
'blood_pressure': f"{random.randint(90, 180)}/{random.randint(60, 100)}",
|
|
'heart_rate': random.randint(60, 120),
|
|
'respiratory_rate': random.randint(12, 24),
|
|
'temperature': round(random.uniform(36.0, 39.0), 1),
|
|
'oxygen_saturation': random.randint(92, 100)
|
|
},
|
|
handoff_report=f"Patient transferred for {random.choice(['specialized care', 'higher acuity monitoring', 'bed availability'])}",
|
|
medications_transferred=random.sample(SAUDI_MEDICATIONS, random.randint(2, 5)),
|
|
delay_reason="Bed not ready" if status == 'APPROVED' and random.choice([True, False]) else None,
|
|
complications="None noted" if status == 'COMPLETED' else None,
|
|
notes=f"Transfer completed successfully" if status == 'COMPLETED' else None,
|
|
created_at=requested_datetime,
|
|
updated_at=requested_datetime + timedelta(hours=random.randint(1, 24))
|
|
)
|
|
|
|
# Set transport team using .set() method for many-to-many relationship
|
|
if transport_team_members:
|
|
transfer.transport_team.set(transport_team_members)
|
|
|
|
# Update admission current location if transfer completed
|
|
if status == 'COMPLETED':
|
|
admission.current_ward = to_ward
|
|
admission.current_bed = to_bed
|
|
admission.save()
|
|
|
|
# Update bed statuses
|
|
transfer.from_bed.status = 'AVAILABLE'
|
|
transfer.from_bed.current_patient = None
|
|
transfer.from_bed.current_admission = None
|
|
transfer.from_bed.occupied_since = None
|
|
transfer.from_bed.save()
|
|
|
|
to_bed.status = 'OCCUPIED'
|
|
to_bed.current_patient = admission.patient
|
|
to_bed.current_admission = admission
|
|
to_bed.occupied_since = actual_datetime
|
|
to_bed.save()
|
|
|
|
transfers.append(transfer)
|
|
|
|
print(f"Created {len(transfers)} transfers")
|
|
return transfers
|
|
|
|
|
|
def create_saudi_discharge_summaries(admissions):
|
|
"""Create discharge summaries for discharged Saudi patients"""
|
|
summaries = []
|
|
|
|
discharged_admissions = [adm for adm in admissions if adm.status == 'DISCHARGED' and adm.discharge_datetime]
|
|
|
|
discharge_dispositions = ['HOME','HOME_HEALTH','NURSING_HOME','REHAB_FACILITY','HOSPICE','TRANSFER','DECEASED','LEFT_AMA','OTHER',]
|
|
|
|
for admission in discharged_admissions:
|
|
length_of_stay = (admission.discharge_datetime.date() - admission.admission_datetime.date()).days
|
|
|
|
summary = DischargeSummary.objects.create(
|
|
admission=admission,
|
|
summary_id=uuid.uuid4(),
|
|
discharge_date=admission.discharge_datetime.date(),
|
|
discharge_time=admission.discharge_datetime.time(),
|
|
length_of_stay=length_of_stay,
|
|
admission_diagnosis=admission.admitting_diagnosis,
|
|
final_diagnosis=random.choice(SAUDI_COMMON_DIAGNOSES),
|
|
secondary_diagnoses=admission.secondary_diagnoses or [],
|
|
procedures_performed=random.sample(SAUDI_SURGICAL_PROCEDURES, random.randint(0, 3)),
|
|
hospital_course=f"Patient admitted with {admission.admitting_diagnosis.lower()} and responded well to treatment. No significant complications during stay.",
|
|
complications="None" if random.choice(
|
|
[True, True, False]) else "Minor wound infection, resolved with antibiotics",
|
|
discharge_medications=random.sample(SAUDI_MEDICATIONS, random.randint(3, 8)),
|
|
medication_changes=[
|
|
"Started on new antihypertensive",
|
|
"Increased insulin dose",
|
|
"Discontinued antibiotic"
|
|
] if random.choice([True, False]) else [],
|
|
activity_restrictions=random.choice([
|
|
"No lifting >10 lbs for 2 weeks",
|
|
"Bedrest x 24 hours",
|
|
"No driving until follow-up",
|
|
"Activity as tolerated"
|
|
]),
|
|
diet_instructions=random.choice([
|
|
"Diabetic diet", "Low sodium diet", "Cardiac diet",
|
|
"Regular diet", "Soft mechanical diet"
|
|
]),
|
|
wound_care="Keep incision clean and dry. Change dressing daily." if random.choice([True, False]) else None,
|
|
special_instructions=[
|
|
"Monitor blood pressure daily",
|
|
"Check blood sugar twice daily",
|
|
"Weigh yourself daily"
|
|
] if random.choice([True, False]) else [],
|
|
follow_up_appointments=[
|
|
{
|
|
'provider': 'Primary Care Physician',
|
|
'date': (admission.discharge_datetime.date() + timedelta(days=7)).isoformat(),
|
|
'time': '10:00 AM'
|
|
},
|
|
{
|
|
'provider': 'Cardiologist',
|
|
'date': (admission.discharge_datetime.date() + timedelta(days=14)).isoformat(),
|
|
'time': '2:00 PM'
|
|
}
|
|
],
|
|
follow_up_instructions="Follow up with primary care physician in 1 week and specialist in 2 weeks",
|
|
warning_signs=[
|
|
"Fever >38.5°C", "Increased shortness of breath",
|
|
"Chest pain", "Swelling in legs", "Unusual bleeding"
|
|
],
|
|
when_to_call="Call physician or return to emergency department if experiencing any warning signs",
|
|
discharge_disposition=random.choice(discharge_dispositions),
|
|
discharge_location="Home" if random.choice([True, True, False]) else "Rehabilitation facility",
|
|
transportation_arranged=True,
|
|
transportation_method=random.choice(['private_vehicle', 'taxi', 'ambulance', 'hospital_transport']),
|
|
durable_medical_equipment=['Walker', 'Hospital bed', 'Oxygen concentrator'] if random.choice(
|
|
[True, False]) else [],
|
|
supplies_provided=['Wound care supplies', 'Medications', 'Educational materials'] if random.choice(
|
|
[True, False]) else [],
|
|
education_provided=[
|
|
'Disease management', 'Medication administration',
|
|
'Signs and symptoms to report', 'Activity restrictions'
|
|
],
|
|
education_materials=['Discharge instructions', 'Medication list', 'Educational brochures'],
|
|
patient_understanding='Good' if random.choice([True, True, False]) else 'Fair',
|
|
social_worker_involved=random.choice([True, False]),
|
|
case_manager_involved=random.choice([True, False]),
|
|
readmission_risk=random.choice(['low', 'moderate', 'high']),
|
|
patient_satisfaction=random.randint(1, 5), # Changed to numeric scale (1-5)
|
|
discharging_physician=admission.attending_physician,
|
|
summary_completed=True,
|
|
summary_signed=True,
|
|
patient_copy_provided=True,
|
|
created_at=admission.discharge_datetime,
|
|
updated_at=admission.discharge_datetime + timedelta(hours=2)
|
|
)
|
|
summaries.append(summary)
|
|
|
|
print(f"Created {len(summaries)} discharge summaries")
|
|
return summaries
|
|
|
|
|
|
def create_saudi_surgery_schedules(tenants, surgeries_per_tenant=30):
|
|
"""Create surgery schedules for Saudi patients"""
|
|
surgeries = []
|
|
|
|
surgery_types = ['ELECTIVE','URGENT','EMERGENT','TRAUMA','TRANSPLANT',
|
|
'CARDIAC','NEUROSURGERY','ORTHOPEDIC','GENERAL','OTHER',]
|
|
|
|
anesthesia_types = ['GENERAL','REGIONAL','LOCAL','SPINAL','EPIDURAL','MAC','SEDATION','OTHER',]
|
|
|
|
surgery_statuses = ['SCHEDULED','CONFIRMED','PREP','IN_PROGRESS','COMPLETED','CANCELLED','POSTPONED','DELAYED',]
|
|
|
|
priorities = ['ROUTINE','URGENT','EMERGENT','STAT',]
|
|
|
|
for tenant in tenants:
|
|
# Get required staff and patients
|
|
patients = list(PatientProfile.objects.filter(tenant=tenant))
|
|
surgeons = list(User.objects.filter(tenant=tenant, employee_profile__role='PHYSICIAN'))
|
|
anesthesiologists = list(User.objects.filter(tenant=tenant, employee_profile__role='PHYSICIAN'))
|
|
nurses = list(User.objects.filter(tenant=tenant, employee_profile__role__in=['NURSE', 'NURSE_PRACTITIONER']))
|
|
admissions = list(Admission.objects.filter(tenant=tenant, status='active'))
|
|
|
|
if not patients or not surgeons or not nurses or not admissions:
|
|
print(f"Insufficient data for tenant {tenant.name}: patients={len(patients)}, surgeons={len(surgeons)}, nurses={len(nurses)}, admissions={len(admissions)}")
|
|
continue
|
|
|
|
for i in range(min(surgeries_per_tenant, len(admissions))): # Don't exceed available admissions
|
|
# Always use an admission - required field
|
|
admission = random.choice(admissions)
|
|
patient = admission.patient # Use patient from the admission
|
|
procedure = random.choice(SAUDI_SURGICAL_PROCEDURES)
|
|
|
|
# Generate surgery date (within next 30 days)
|
|
surgery_date = (django_timezone.now() + timedelta(days=random.randint(0, 30))).date()
|
|
start_time = time(hour=random.randint(7, 16), minute=random.choice([0, 30]))
|
|
estimated_duration = random.randint(60, 480) # 1-8 hours
|
|
|
|
status = random.choice(surgery_statuses)[0]
|
|
|
|
# Actual times if surgery completed
|
|
actual_start_time = None
|
|
actual_end_time = None
|
|
actual_duration = None
|
|
|
|
if status in ['IN_PROGRESS', 'COMPLETED']:
|
|
actual_start_time = datetime.combine(surgery_date, start_time)
|
|
if status == 'COMPLETED':
|
|
actual_duration = estimated_duration + random.randint(-30, 60)
|
|
actual_end_time = actual_start_time + timedelta(minutes=actual_duration)
|
|
|
|
surgery = SurgerySchedule.objects.create(
|
|
tenant=tenant,
|
|
surgery_id=uuid.uuid4(),
|
|
patient=patient,
|
|
admission=admission, # Always provide admission - required field
|
|
procedure_name=procedure,
|
|
procedure_code=f"CPT-{random.randint(10000, 99999)}",
|
|
surgery_type=random.choice(surgery_types),
|
|
scheduled_date=surgery_date,
|
|
scheduled_start_time=start_time,
|
|
estimated_duration_minutes=estimated_duration,
|
|
operating_room=f"OR-{random.randint(1, 12)}",
|
|
or_block_time=f"{start_time} - {(datetime.combine(surgery_date, start_time) + timedelta(minutes=estimated_duration)).time()}",
|
|
primary_surgeon=random.choice(surgeons),
|
|
anesthesiologist=random.choice(anesthesiologists),
|
|
scrub_nurse=random.choice(nurses),
|
|
circulating_nurse=random.choice(nurses),
|
|
anesthesia_type=random.choice(anesthesia_types),
|
|
preop_diagnosis=random.choice(SAUDI_COMMON_DIAGNOSES),
|
|
preop_orders=[
|
|
'NPO after midnight', 'Pre-op antibiotics', 'Type and crossmatch',
|
|
'Pre-op labs', 'Consent signed'
|
|
],
|
|
consent_obtained=True,
|
|
consent_date=surgery_date - timedelta(days=random.randint(0, 7)),
|
|
special_equipment=['Laparoscopic equipment', 'C-arm', 'Microscope'] if random.choice(
|
|
[True, False]) else [],
|
|
implants_needed=['Orthopedic implants', 'Cardiac devices'] if random.choice([True, False]) else [],
|
|
blood_products=['Packed RBCs', 'FFP', 'Platelets'] if random.choice([True, False]) else [],
|
|
status=status,
|
|
actual_start_time=actual_start_time,
|
|
actual_end_time=actual_end_time,
|
|
actual_duration_minutes=actual_duration,
|
|
postop_diagnosis=random.choice(SAUDI_COMMON_DIAGNOSES) if status == 'COMPLETED' else None,
|
|
procedure_performed=procedure if status == 'COMPLETED' else None,
|
|
complications="None" if status == 'COMPLETED' and random.choice([True, True, False]) else None,
|
|
recovery_location=random.choice(
|
|
['PACU', 'ICU', 'WARD', 'SAME_DAY']) if status == 'COMPLETED' else None,
|
|
priority=random.choice(priorities),
|
|
surgery_notes=f"Successful {procedure.lower()} performed without complications" if status == 'COMPLETED' else None,
|
|
created_at=django_timezone.now() - timedelta(days=random.randint(1, 30)),
|
|
updated_at=django_timezone.now() - timedelta(hours=random.randint(1, 24))
|
|
)
|
|
|
|
# Set assistant surgeons using .set() method for many-to-many relationship
|
|
assistant_surgeons = random.sample(surgeons, random.randint(0, 2))
|
|
if assistant_surgeons:
|
|
surgery.assistant_surgeons.set(assistant_surgeons)
|
|
|
|
surgeries.append(surgery)
|
|
|
|
print(f"Created {min(surgeries_per_tenant, len(admissions))} surgeries for {tenant.name}")
|
|
|
|
return surgeries
|
|
|
|
|
|
def main():
|
|
"""Main function to generate all Saudi inpatients data"""
|
|
print("Starting Saudi Healthcare Inpatients Data Generation...")
|
|
|
|
# Get existing tenants
|
|
tenants = list(Tenant.objects.all())
|
|
if not tenants:
|
|
print("❌ No tenants found. Please run the core data generator first.")
|
|
return
|
|
|
|
# Create wards
|
|
print("\n1. Creating Saudi Hospital Wards...")
|
|
wards = create_saudi_wards(tenants, 10)
|
|
|
|
# Create beds
|
|
print("\n2. Creating Hospital Beds...")
|
|
beds = create_saudi_beds(wards)
|
|
|
|
# Create admissions
|
|
print("\n3. Creating Patient Admissions...")
|
|
admissions = create_saudi_admissions(tenants, beds, 80)
|
|
|
|
# Create transfers
|
|
print("\n4. Creating Patient Transfers...")
|
|
transfers = create_saudi_transfers(admissions)
|
|
|
|
# Create discharge summaries
|
|
print("\n5. Creating Discharge Summaries...")
|
|
summaries = create_saudi_discharge_summaries(admissions)
|
|
|
|
# Create surgery schedules
|
|
print("\n6. Creating Surgery Schedules...")
|
|
surgeries = create_saudi_surgery_schedules(tenants, 25)
|
|
|
|
print(f"\n✅ Saudi Healthcare Inpatients Data Generation Complete!")
|
|
print(f"📊 Summary:")
|
|
print(f" - Wards: {len(wards)}")
|
|
print(f" - Beds: {len(beds)}")
|
|
print(f" - Admissions: {len(admissions)}")
|
|
print(f" - Transfers: {len(transfers)}")
|
|
print(f" - Discharge Summaries: {len(summaries)}")
|
|
print(f" - Surgery Schedules: {len(surgeries)}")
|
|
|
|
# Status distribution
|
|
admission_statuses = {}
|
|
for admission in admissions:
|
|
admission_statuses[admission.status] = admission_statuses.get(admission.status, 0) + 1
|
|
|
|
print(f"\n🏥 Admission Status Distribution:")
|
|
for status, count in admission_statuses.items():
|
|
print(f" - {status.title()}: {count}")
|
|
|
|
# Nurse-to-patient ratio summary
|
|
print(f"\n👩⚕️ Nurse-to-Patient Ratios Used:")
|
|
ratios_used = set()
|
|
for ward in wards:
|
|
ratio_value = ward.nurse_to_patient_ratio
|
|
# Convert back to ratio format for display
|
|
patients_per_nurse = int(1 / ratio_value) if ratio_value > 0 else 0
|
|
ratios_used.add(f"1:{patients_per_nurse}")
|
|
|
|
for ratio in sorted(ratios_used):
|
|
print(f" - {ratio}")
|
|
|
|
return {
|
|
'wards': wards,
|
|
'beds': beds,
|
|
'admissions': admissions,
|
|
'transfers': transfers,
|
|
'summaries': summaries,
|
|
'surgeries': surgeries
|
|
}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |