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

2379 lines
95 KiB
Python

import os
import sys
import django
import argparse
# Set up Django environment
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hospital_management.settings')
django.setup()
import random
from datetime import datetime, date, time, timedelta
from django.utils import timezone as django_timezone
from django.db import transaction
from emr.models import Encounter, VitalSigns, ProblemList, CarePlan, ClinicalNote, NoteTemplate, Icd10, ClinicalRecommendation, AllergyAlert, TreatmentProtocol, ClinicalGuideline, CriticalAlert, DiagnosticSuggestion
from patients.models import PatientProfile
from accounts.models import User
from hr.models import Employee
from appointments.models import AppointmentRequest
from inpatients.models import Admission
from core.models import Tenant
import uuid
from decimal import Decimal
# Optional import for ICD-10 XML parsing
try:
import xmlschema
XMLSCHEMA_AVAILABLE = True
except ImportError:
XMLSCHEMA_AVAILABLE = False
xmlschema = None
# Saudi-specific clinical data
SAUDI_ENCOUNTER_TYPES = [
'CONSULTATION', 'FOLLOW_UP', 'EMERGENCY', 'PROCEDURE', 'SURGERY',
'DIAGNOSTIC', 'THERAPY', 'SCREENING', 'VACCINATION', 'TELEMEDICINE'
]
SAUDI_ENCOUNTER_CLASSES = [
'OUTPATIENT', 'INPATIENT', 'EMERGENCY', 'OBSERVATION',
'DAY_SURGERY', 'TELEMEDICINE', 'HOME_VISIT'
]
SAUDI_CHIEF_COMPLAINTS = [
'Chest pain and shortness of breath',
'Abdominal pain and nausea',
'Headache and dizziness',
'Back pain and stiffness',
'Fever and cough',
'Joint pain and swelling',
'Fatigue and weakness',
'Skin rash and itching',
'Diabetes follow-up',
'Hypertension monitoring',
'Regular health checkup',
'Vaccination appointment',
'Pre-operative consultation',
'Post-operative follow-up',
'Pregnancy consultation',
'Child wellness visit',
'Mental health consultation',
'Physical therapy session',
'Diagnostic imaging',
'Laboratory test follow-up',
'Cardiac symptoms evaluation',
'Respiratory symptoms',
'Gastrointestinal complaints',
'Neurological symptoms',
'Endocrine disorders follow-up'
]
SAUDI_LOCATIONS = [
'Main Building - Floor 1',
'Main Building - Floor 2',
'Main Building - Floor 3',
'Emergency Wing',
'Outpatient Clinic - Wing A',
'Outpatient Clinic - Wing B',
'Surgical Suite - Floor 4',
'Radiology Department',
'Laboratory Building',
'Pediatric Wing',
'ICU - Floor 5',
'Cardiology Unit',
'Maternity Ward',
'Dialysis Center'
]
SAUDI_PROBLEM_TYPES = [
'DIAGNOSIS', 'SYMPTOM', 'FINDING', 'COMPLAINT',
'CONDITION', 'DISORDER', 'SYNDROME', 'INJURY',
'ALLERGY', 'INTOLERANCE', 'RISK_FACTOR'
]
SAUDI_COMMON_PROBLEMS = [
('Diabetes Mellitus Type 2', 'E11.9', 'ICD10'),
('Hypertension', 'I10', 'ICD10'),
('Hyperlipidemia', 'E78.5', 'ICD10'),
('Asthma', 'J45.9', 'ICD10'),
('Chronic Kidney Disease', 'N18.9', 'ICD10'),
('Osteoarthritis', 'M19.9', 'ICD10'),
('Depression', 'F32.9', 'ICD10'),
('Anxiety Disorder', 'F41.9', 'ICD10'),
('Obesity', 'E66.9', 'ICD10'),
('Gastroesophageal Reflux', 'K21.9', 'ICD10'),
('Migraine', 'G43.9', 'ICD10'),
('Allergic Rhinitis', 'J30.9', 'ICD10'),
('Hypothyroidism', 'E03.9', 'ICD10'),
('Coronary Artery Disease', 'I25.9', 'ICD10'),
('Chronic Obstructive Pulmonary Disease', 'J44.9', 'ICD10')
]
SAUDI_CARE_PLAN_TYPES = [
'COMPREHENSIVE', 'DISEASE_SPECIFIC', 'PREVENTIVE', 'CHRONIC_CARE',
'ACUTE_CARE', 'DISCHARGE', 'REHABILITATION', 'PALLIATIVE',
'MENTAL_HEALTH', 'MEDICATION', 'NUTRITION', 'EXERCISE'
]
SAUDI_NOTE_TYPES = [
'CONSULTATION', 'PROGRESS', 'DISCHARGE', 'PROCEDURE',
'SURGERY', 'EMERGENCY', 'NURSING', 'THERAPY',
'DIAGNOSTIC', 'MEDICATION_REVIEW', 'FOLLOW_UP'
]
def get_providers_for_emr(tenants):
"""Get healthcare providers for EMR data creation"""
providers = []
for tenant in tenants:
tenant_providers = User.objects.filter(
tenant=tenant,
is_active=True,
employee_profile__role__in=['PHYSICIAN', 'NURSE_PRACTITIONER', 'PHYSICIAN_ASSISTANT', 'RADIOLOGIST']
)
providers.extend(list(tenant_providers))
# Create mock providers if none exist
if not providers:
providers = create_mock_providers_for_emr(tenants)
return providers
def create_mock_providers_for_emr(tenants):
"""Create mock healthcare providers for EMR data"""
providers = []
mock_providers_data = [
{'first_name': 'Ahmad', 'last_name': 'Al-Rashid', 'role': 'PHYSICIAN'},
{'first_name': 'Fatima', 'last_name': 'Al-Ghamdi', 'role': 'PHYSICIAN'},
{'first_name': 'Mohammed', 'last_name': 'Al-Otaibi', 'role': 'PHYSICIAN'},
{'first_name': 'Sarah', 'last_name': 'Al-Harbi', 'role': 'PHYSICIAN'},
{'first_name': 'Khalid', 'last_name': 'Al-Mutairi', 'role': 'SURGEON'},
{'first_name': 'Nora', 'last_name': 'Al-Zahrani', 'role': 'NURSE_PRACTITIONER'},
{'first_name': 'Omar', 'last_name': 'Al-Dawsari', 'role': 'PHYSICIAN'},
{'first_name': 'Layla', 'last_name': 'Al-Subai', 'role': 'PHYSICIAN'},
]
for tenant in tenants:
for provider_data in mock_providers_data:
try:
email = f"{provider_data['first_name'].lower()}.{provider_data['last_name'].lower().replace('-', '')}@{tenant.domain}"
existing_user = User.objects.filter(email=email).first()
if not existing_user:
user = User.objects.create_user(
email=email,
first_name=provider_data['first_name'],
last_name=provider_data['last_name'],
employee_profile__role=provider_data['role'],
tenant=tenant,
is_active=True,
password='temp_password_123'
)
providers.append(user)
else:
providers.append(existing_user)
except Exception as e:
print(f"Error creating mock provider {provider_data['first_name']} {provider_data['last_name']}: {e}")
continue
return providers
def create_note_templates(tenants):
"""Create note templates for different specialties"""
templates = []
template_configs = [
{
'name': 'General Consultation Note',
'note_type': 'CONSULTATION',
'specialty': 'FAMILY_MEDICINE',
'content': """
CHIEF COMPLAINT: [Chief Complaint]
HISTORY OF PRESENT ILLNESS:
[History details]
PAST MEDICAL HISTORY:
[Past medical history]
MEDICATIONS:
[Current medications]
ALLERGIES:
[Known allergies]
SOCIAL HISTORY:
[Social history]
PHYSICAL EXAMINATION:
General: [General appearance]
Vital Signs: [Vital signs]
HEENT: [Head, eyes, ears, nose, throat]
Cardiovascular: [Heart examination]
Respiratory: [Lung examination]
Abdomen: [Abdominal examination]
Extremities: [Extremity examination]
Neurological: [Neurological examination]
ASSESSMENT:
[Clinical impression]
PLAN:
[Treatment plan]
[Provider Name], [Title]
[Date and Time]
""",
'structured_fields': [
'chief_complaint', 'history_present_illness', 'past_medical_history',
'medications', 'allergies', 'social_history', 'physical_exam',
'assessment', 'plan'
]
},
{
'name': 'Cardiology Consultation',
'note_type': 'CONSULTATION',
'specialty': 'CARDIOLOGY',
'content': """
CHIEF COMPLAINT: [Cardiac symptoms]
HISTORY OF PRESENT ILLNESS:
[Cardiac history details]
CARDIOVASCULAR RISK FACTORS:
[Risk factors assessment]
MEDICATIONS:
[Cardiac medications]
PHYSICAL EXAMINATION:
Blood Pressure: [BP measurements]
Heart Rate: [HR and rhythm]
Cardiovascular: [Detailed cardiac exam]
Peripheral Vascular: [Peripheral circulation]
ECG: [ECG findings]
LABORATORY RESULTS:
[Cardiac markers, lipids, etc.]
IMAGING:
[Echo, stress test, etc.]
ASSESSMENT:
[Cardiac diagnosis]
PLAN:
[Cardiac management plan]
[Cardiologist Name]
[Date and Time]
""",
'structured_fields': [
'chief_complaint', 'cardiac_history', 'risk_factors',
'cardiac_medications', 'cardiac_exam', 'ecg_findings',
'lab_results', 'imaging_results', 'cardiac_assessment', 'cardiac_plan'
]
},
{
'name': 'Emergency Department Note',
'note_type': 'EMERGENCY',
'specialty': 'EMERGENCY',
'content': """
TIME OF ARRIVAL: [Arrival time]
CHIEF COMPLAINT: [Emergency complaint]
TRIAGE LEVEL: [Triage priority]
HISTORY OF PRESENT ILLNESS:
[Emergency presentation details]
PAST MEDICAL HISTORY:
[Relevant medical history]
MEDICATIONS:
[Current medications]
ALLERGIES:
[Known allergies]
PHYSICAL EXAMINATION:
General Appearance: [Patient appearance]
Vital Signs: [Emergency vital signs]
Primary Survey: [ABCDE assessment]
Secondary Survey: [Detailed examination]
EMERGENCY PROCEDURES:
[Procedures performed]
LABORATORY/IMAGING:
[Emergency testing results]
ASSESSMENT:
[Emergency diagnosis]
DISPOSITION:
[Emergency department disposition]
[Emergency Physician Name]
[Date and Time]
""",
'structured_fields': [
'arrival_time', 'chief_complaint', 'triage_level',
'emergency_history', 'emergency_exam', 'procedures_performed',
'emergency_testing', 'emergency_assessment', 'disposition'
]
},
{
'name': 'Surgical Consultation',
'note_type': 'CONSULTATION',
'specialty': 'SURGERY',
'content': """
REASON FOR CONSULTATION: [Surgical consultation reason]
HISTORY OF PRESENT ILLNESS:
[Surgical problem history]
PAST SURGICAL HISTORY:
[Previous surgeries]
MEDICATIONS:
[Current medications]
ALLERGIES:
[Known allergies]
PHYSICAL EXAMINATION:
[Surgical examination findings]
IMAGING STUDIES:
[Relevant imaging]
LABORATORY RESULTS:
[Preoperative labs]
ASSESSMENT:
[Surgical assessment]
RECOMMENDATIONS:
[Surgical recommendations]
SURGICAL PLAN:
[Proposed surgical intervention]
RISKS AND BENEFITS DISCUSSED:
[Risk discussion]
[Surgeon Name]
[Date and Time]
""",
'structured_fields': [
'consultation_reason', 'surgical_history', 'past_surgeries',
'surgical_exam', 'imaging_studies', 'preop_labs',
'surgical_assessment', 'surgical_recommendations', 'surgical_plan'
]
},
{
'name': 'Progress Note',
'note_type': 'PROGRESS',
'specialty': 'INTERNAL_MEDICINE',
'content': """
SUBJECTIVE:
[Patient's reported symptoms and concerns]
OBJECTIVE:
Vital Signs: [Current vital signs]
Physical Examination: [Current physical findings]
Laboratory Results: [Recent lab results]
Imaging Studies: [Recent imaging]
ASSESSMENT:
[Current clinical assessment]
PLAN:
[Updated treatment plan]
[Provider Name]
[Date and Time]
""",
'structured_fields': [
'subjective', 'vital_signs', 'physical_exam',
'lab_results', 'imaging', 'assessment', 'plan'
]
}
]
for tenant in tenants:
for config in template_configs:
try:
template = NoteTemplate.objects.create(
tenant=tenant,
template_id=uuid.uuid4(),
name=config['name'],
description=f"Standard {config['name'].lower()} template",
note_type=config['note_type'],
specialty=config['specialty'],
template_content=config['content'].strip(),
structured_fields=config['structured_fields'],
is_active=True,
is_default=config['name'] == 'General Consultation Note',
usage_count=random.randint(0, 100),
version=1.0,
quality_indicators=[
'completeness', 'accuracy', 'timeliness',
'clarity', 'compliance'
],
compliance_requirements=[
'documentation_complete_within_24h',
'provider_signature_required',
'patient_privacy_maintained'
],
created_at=django_timezone.now() - timedelta(days=random.randint(30, 365)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 30))
)
templates.append(template)
except Exception as e:
print(f"Error creating template {config['name']}: {e}")
continue
print(f"Created {len(templates)} note templates")
return templates
def create_encounters(tenants, days_back=30):
"""Create patient encounters"""
encounters = []
# Get patients and providers
patients = list(PatientProfile.objects.filter(tenant__in=tenants))
if not patients:
print("No patients found. Skipping encounter creation.")
return encounters
providers = get_providers_for_emr(tenants)
if not providers:
print("No providers found. Skipping encounter creation.")
return encounters
# Get existing appointments to link with encounters
appointments = list(AppointmentRequest.objects.filter(
tenant__in=tenants,
status__in=['COMPLETED', 'IN_PROGRESS']
))
# Get existing admissions
admissions = list(Admission.objects.filter(tenant__in=tenants))
start_date = django_timezone.now().date() - timedelta(days=days_back)
# Create encounters for the past days_back days
for day_offset in range(days_back):
encounter_date = start_date + timedelta(days=day_offset)
daily_encounters = random.randint(20, 50) # Variable number of encounters per day
for _ in range(daily_encounters):
patient = random.choice(patients)
# Get providers for this patient's tenant
tenant_providers = [p for p in providers if p.tenant == patient.tenant]
if not tenant_providers:
print(f"Warning: No providers found for tenant {patient.tenant.name}. Skipping encounter for {patient.get_full_name()}")
continue
provider = random.choice(tenant_providers)
# Determine encounter type and class
encounter_type = random.choices(
SAUDI_ENCOUNTER_TYPES,
weights=[25, 20, 10, 8, 5, 12, 8, 7, 3, 2]
)[0]
if encounter_type == 'EMERGENCY':
encounter_class = 'EMERGENCY'
elif encounter_type in ['SURGERY', 'PROCEDURE']:
encounter_class = random.choice(['INPATIENT', 'DAY_SURGERY', 'OUTPATIENT'])
elif encounter_type == 'TELEMEDICINE':
encounter_class = 'TELEMEDICINE'
else:
encounter_class = random.choice(['OUTPATIENT', 'INPATIENT'])
# Generate encounter timing
start_hour = random.randint(8, 20)
start_minute = random.choice([0, 15, 30, 45])
start_datetime = django_timezone.make_aware(
datetime.combine(encounter_date, time(start_hour, start_minute))
)
duration_minutes = random.randint(15, 180)
if encounter_type == 'SURGERY':
duration_minutes = random.randint(60, 360)
elif encounter_type == 'EMERGENCY':
duration_minutes = random.randint(30, 240)
end_datetime = start_datetime + timedelta(minutes=duration_minutes)
# Determine status
if encounter_date < django_timezone.now().date():
status = random.choices(
['PLANNED','COMPLETED', 'CANCELLED'],
weights=[30,55, 15]
)[0]
elif encounter_date == django_timezone.now().date():
status = random.choices(
['IN_PROGRESS', 'COMPLETED'],
weights=[30, 70]
)[0]
else:
status = 'SCHEDULED'
# Priority and acuity
if encounter_type == 'EMERGENCY':
priority = random.choices(['EMERGENCY', 'URGENT'], weights=[60, 40])[0]
acuity_level = random.randint(2, 5)
else:
priority = random.choices(['ROUTINE', 'URGENT'], weights=[80, 20])[0]
acuity_level = random.randint(1, 3)
# Link to appointment if available
linked_appointment = None
if appointments:
suitable_appointments = [
apt for apt in appointments
if apt.patient == patient and
apt.scheduled_datetime and
apt.scheduled_datetime.date() == encounter_date
]
if suitable_appointments:
linked_appointment = random.choice(suitable_appointments)
# Link to admission if inpatient
linked_admission = None
if encounter_class == 'INPATIENT' and admissions:
suitable_admissions = [
adm for adm in admissions
if adm.patient == patient and
adm.admission_datetime <= start_datetime
]
if suitable_admissions:
linked_admission = random.choice(suitable_admissions)
# Documentation completion
documentation_complete = status == 'COMPLETED'
signed_off = documentation_complete and random.choice([True, False])
try:
encounter = Encounter.objects.create(
tenant=patient.tenant,
encounter_id=uuid.uuid4(),
patient=patient,
provider=provider,
encounter_type=encounter_type,
encounter_class=encounter_class,
start_datetime=start_datetime,
end_datetime=end_datetime if status in ['COMPLETED', 'CANCELLED'] else None,
status=status,
location=random.choice(SAUDI_LOCATIONS),
room_number=f"Room {random.randint(101, 450)}",
appointment=linked_appointment,
admission=linked_admission,
chief_complaint=random.choice(SAUDI_CHIEF_COMPLAINTS),
reason_for_visit=random.choice(SAUDI_CHIEF_COMPLAINTS),
priority=priority,
acuity_level=acuity_level,
documentation_complete=documentation_complete,
signed_off=signed_off,
signed_by=provider if signed_off else None,
signed_datetime=end_datetime if signed_off else None,
billable=random.choice([True, False]),
billing_codes=[
f"{random.randint(99200, 99499)}",
f"{random.randint(99200, 99499)}"
] if random.choice([True, False]) else [],
quality_measures={
'documentation_score': random.randint(70, 100),
'timeliness_score': random.randint(80, 100),
'completeness_score': random.randint(75, 100)
},
created_at=start_datetime - timedelta(minutes=random.randint(5, 60)),
updated_at=django_timezone.now() - timedelta(minutes=random.randint(0, 30)),
created_by=provider
)
encounters.append(encounter)
except Exception as e:
print(f"Error creating encounter for {patient.get_full_name()}: {e}")
continue
print(f"Created {len(encounters)} encounters")
return encounters
def create_vital_signs(encounters):
"""Create vital signs for encounters"""
vital_signs = []
providers = User.objects.filter(
employee_profile__role__in=['NURSE', 'NURSE_PRACTITIONER', 'PHYSICIAN', 'MEDICAL_ASSISTANT'],
is_active=True
)
if not providers:
# Use encounter providers as fallback
providers = list(set([enc.provider for enc in encounters]))
for encounter in encounters:
# Create 1-3 vital signs measurements per encounter
num_measurements = random.randint(1, 3)
for measurement_num in range(num_measurements):
# Time offset for multiple measurements
time_offset = measurement_num * random.randint(30, 120) # 30-120 minutes apart
measured_datetime = encounter.start_datetime + timedelta(minutes=time_offset)
# Generate realistic vital signs based on patient age
patient_age = (django_timezone.now().date() - encounter.patient.date_of_birth).days // 365
# Temperature (Celsius)
temperature = round(random.uniform(36.0, 39.5), 1)
if temperature > 38.5:
temperature_method = random.choice(['ORAL', 'RECTAL', 'TYMPANIC'])
else:
temperature_method = random.choice(['ORAL', 'AXILLARY', 'TYMPANIC'])
# Blood pressure
if patient_age < 18:
systolic_bp = random.randint(90, 120)
diastolic_bp = random.randint(50, 80)
elif patient_age < 65:
systolic_bp = random.randint(100, 140)
diastolic_bp = random.randint(60, 90)
else:
systolic_bp = random.randint(110, 160)
diastolic_bp = random.randint(70, 100)
bp_position = random.choice(['SITTING', 'LYING', 'STANDING'])
bp_cuff_size = 'REGULAR' if patient_age >= 18 else 'PEDIATRIC'
# Heart rate
if patient_age < 1:
heart_rate = random.randint(100, 160)
elif patient_age < 12:
heart_rate = random.randint(80, 120)
else:
heart_rate = random.randint(60, 100)
heart_rhythm = random.choices(
['REGULAR', 'REGULARLY_IRREGULAR', 'IRREGULARLY_IRREGULAR', 'IRREGULAR_UNSPECIFIED'],
weights=[85, 8, 5, 2]
)[0]
# Respiratory rate
if patient_age < 1:
respiratory_rate = random.randint(30, 60)
elif patient_age < 12:
respiratory_rate = random.randint(18, 30)
else:
respiratory_rate = random.randint(12, 20)
# Oxygen saturation
oxygen_saturation = random.randint(95, 100)
oxygen_delivery = 'ROOM_AIR'
oxygen_flow_rate = None
if oxygen_saturation < 95:
oxygen_delivery = random.choice(['NASAL_CANNULA', 'MASK', 'NON_REBREATHER'])
oxygen_flow_rate = random.randint(2, 15)
# Pain scale
pain_scale = random.randint(0, 10)
pain_location = None
pain_quality = None
if pain_scale > 0:
pain_location = random.choice([
'Head', 'Chest', 'Abdomen', 'Back', 'Extremities',
'Generalized', 'Neck', 'Pelvis'
])
pain_quality = random.choice([
'Sharp', 'Dull', 'Burning', 'Cramping',
'Stabbing', 'Throbbing', 'Aching'
])
# Weight and height
if patient_age < 18:
weight = round(random.uniform(3.0, 70.0), 1)
height = round(random.uniform(50.0, 180.0), 1)
else:
weight = round(random.uniform(45.0, 150.0), 1)
height = round(random.uniform(150.0, 200.0), 1)
# Head circumference for pediatric patients
head_circumference = None
if patient_age < 2:
head_circumference = round(random.uniform(30.0, 50.0), 1)
# Critical values detection
critical_values = []
if temperature > 39.0 or temperature < 35.0:
critical_values.append('temperature')
if systolic_bp > 180 or systolic_bp < 90:
critical_values.append('systolic_bp')
if heart_rate > 120 or heart_rate < 50:
critical_values.append('heart_rate')
if oxygen_saturation < 90:
critical_values.append('oxygen_saturation')
alerts_generated = len(critical_values) > 0
try:
vital_sign = VitalSigns.objects.create(
encounter=encounter,
patient=encounter.patient,
measurement_id=uuid.uuid4(),
measured_datetime=measured_datetime,
temperature=Decimal(str(temperature)),
temperature_method=temperature_method,
systolic_bp=systolic_bp,
diastolic_bp=diastolic_bp,
bp_position=bp_position,
bp_cuff_size=bp_cuff_size,
heart_rate=heart_rate,
heart_rhythm=heart_rhythm,
respiratory_rate=respiratory_rate,
oxygen_saturation=oxygen_saturation,
oxygen_delivery=oxygen_delivery,
oxygen_flow_rate=oxygen_flow_rate,
pain_scale=pain_scale,
pain_location=pain_location,
pain_quality=pain_quality,
weight=Decimal(str(weight)),
height=Decimal(str(height)),
head_circumference=Decimal(str(head_circumference)) if head_circumference else None,
device_used=random.choice(['MANUAL', 'AUTOMATED', 'MONITOR']),
device_calibrated=random.choice([True, False]),
measured_by=random.choice(providers),
verified_by=random.choice(providers) if random.choice([True, False]) else None,
critical_values=critical_values,
alerts_generated=alerts_generated,
notes=f"Routine vital signs measurement #{measurement_num + 1}" if measurement_num > 0 else None,
created_at=measured_datetime,
updated_at=measured_datetime + timedelta(minutes=random.randint(1, 10))
)
vital_signs.append(vital_sign)
except Exception as e:
print(f"Error creating vital signs for encounter {encounter.encounter_id}: {e}")
continue
print(f"Created {len(vital_signs)} vital signs records")
return vital_signs
def create_problem_lists(patients, providers):
"""Create problem lists for patients"""
problems = []
for patient in patients:
# Each patient has 1-5 problems
num_problems = random.randint(1, 5)
for _ in range(num_problems):
problem_data = random.choice(SAUDI_COMMON_PROBLEMS)
problem_name, problem_code, coding_system = problem_data
problem_type = random.choices(
SAUDI_PROBLEM_TYPES,
weights=[60, 20, 10, 5, 2, 2, 1, 1, 1, 1, 1]
)[0]
# Onset date (random date in the past)
onset_date = django_timezone.now().date() - timedelta(
days=random.randint(30, 3650) # 1 month to 10 years ago
)
# Severity
severity = random.choices(
['MILD', 'MODERATE', 'SEVERE', 'CRITICAL'],
weights=[40, 35, 20, 5]
)[0]
# Priority
priority = random.choices(
['HIGH', 'MEDIUM', 'LOW'],
weights=[20, 60, 20]
)[0]
# Status
status = random.choices(
['ACTIVE', 'RESOLVED', 'INACTIVE', 'REMISSION'],
weights=[50, 20, 10, 20]
)[0]
# Resolution date for resolved problems
resolution_date = None
resolution_notes = None
if status == 'RESOLVED':
resolution_date = onset_date + timedelta(
days=random.randint(7, 365)
)
resolution_notes = f"Problem resolved after treatment. Patient improved significantly."
# Provider assignments
tenant_providers = [p for p in providers if p.tenant == patient.tenant]
if not tenant_providers:
print(f"Warning: No providers found for tenant {patient.tenant.name}. Skipping problem for {patient.get_full_name()}")
continue
diagnosing_provider = random.choice(tenant_providers)
managing_provider = diagnosing_provider if random.choice([True, False]) else random.choice(tenant_providers)
# Fix timezone issue
verified_date = None
if random.choice([True, False]):
verified_date = django_timezone.now()
try:
problem = ProblemList.objects.create(
tenant=patient.tenant,
patient=patient,
problem_id=uuid.uuid4(),
problem_name=problem_name,
problem_code=problem_code,
coding_system=coding_system,
problem_type=problem_type,
onset_date=onset_date,
onset_description=f"Patient reports {problem_name.lower()} started gradually",
severity=severity,
priority=priority,
status=status,
resolution_date=resolution_date,
resolution_notes=resolution_notes,
diagnosing_provider=diagnosing_provider,
managing_provider=managing_provider,
body_site=random.choice([
'Chest', 'Abdomen', 'Head', 'Extremities', 'Back', 'Neck'
]) if random.choice([True, False]) else None,
laterality=random.choice(['LEFT', 'RIGHT', 'BILATERAL']) if random.choice([True, False]) else None,
clinical_notes=f"Patient diagnosed with {problem_name}. {severity.lower()} severity. Status: {status.lower()}.",
patient_concerns=f"Patient concerned about {problem_name.lower()} impact on daily activities",
treatment_goals=[
f"Manage {problem_name.lower()} symptoms",
"Improve quality of life",
"Prevent complications"
],
outcome_measures=[
"Symptom improvement",
"Functional status",
"Patient satisfaction"
],
verified=random.choice([True, False]),
verified_by=managing_provider if random.choice([True, False]) else None,
verified_date=verified_date, # Use timezone-aware datetime
created_at=django_timezone.make_aware(
datetime.combine(onset_date, time(random.randint(8, 17), random.randint(0, 59)))
),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 30)),
created_by=diagnosing_provider
)
problems.append(problem)
except Exception as e:
print(f"Error creating problem for {patient.get_full_name()}: {e}")
continue
print(f"Created {len(problems)} problem list entries")
return problems
def create_care_plans(patients, providers, problems):
"""Create care plans for patients"""
care_plans = []
for patient in patients:
# Each patient has 1-2 care plans
num_plans = random.randint(1, 2)
patient_problems = [p for p in problems if p.patient == patient]
if not patient_problems:
continue
for _ in range(num_plans):
plan_type = random.choice(SAUDI_CARE_PLAN_TYPES)
# Select related problems for this care plan
related_problems_list = random.sample(
patient_problems,
min(random.randint(1, 3), len(patient_problems))
)
# Generate care plan title and description
main_problem = related_problems_list[0].problem_name
title = f"{plan_type.replace('_', ' ').title()} Plan for {main_problem}"
description = f"Comprehensive {plan_type.lower().replace('_', ' ')} plan for managing {main_problem} and related conditions."
# Care plan category
category = random.choice([
'ASSESSMENT', 'TREATMENT', 'EDUCATION', 'COORDINATION',
'PREVENTION', 'LIFESTYLE', 'MEDICATION', 'FOLLOW_UP',
'EMERGENCY', 'SUPPORT'
])
# Timeline
start_date = django_timezone.now().date()
end_date = start_date + timedelta(days=random.randint(30, 365))
target_completion_date = end_date - timedelta(days=random.randint(0, 30))
# Status
status = random.choices(
['ACTIVE', 'COMPLETED', 'ON_HOLD', 'CANCELLED'],
weights=[60, 25, 10, 5]
)[0]
# Priority
priority = random.choices(
['LOW', 'ROUTINE', 'URGENT', 'STAT'],
weights=[25, 50, 20, 5]
)[0]
# Providers
tenant_providers = [p for p in providers if p.tenant == patient.tenant]
if not tenant_providers:
print(f"Warning: No providers found for tenant {patient.tenant.name}. Skipping care plan for {patient.get_full_name()}")
continue
primary_provider = random.choice(tenant_providers)
care_team_list = random.sample(
tenant_providers,
min(random.randint(2, 4), len(tenant_providers))
)
# Goals and objectives
goals = [
f"Manage {main_problem} effectively",
"Improve patient quality of life",
"Prevent disease progression",
"Optimize medication therapy"
]
objectives = [
f"Reduce {main_problem.lower()} symptoms by 50%",
"Achieve target vital signs",
"Improve functional status",
"Enhance patient understanding"
]
# Interventions and activities
interventions = [
"Medication management",
"Patient education",
"Lifestyle modifications",
"Regular monitoring"
]
activities = [
"Daily medication review",
"Weekly patient assessment",
"Monthly provider consultation",
"Quarterly outcome evaluation"
]
# Completion percentage
completion_percentage = random.randint(0, 100) if status in ['ACTIVE', 'COMPLETED'] else 0
if status == 'COMPLETED':
completion_percentage = 100
# Approval
approved = random.choice([True, False])
approved_date = django_timezone.now() if approved else None
try:
care_plan = CarePlan.objects.create(
tenant=patient.tenant,
patient=patient,
care_plan_id=uuid.uuid4(),
title=title,
description=description,
plan_type=plan_type,
category=category,
start_date=start_date,
end_date=end_date,
target_completion_date=target_completion_date,
status=status,
priority=priority,
primary_provider=primary_provider,
goals=goals,
objectives=objectives,
interventions=interventions,
activities=activities,
monitoring_parameters=[
"Vital signs",
"Symptom severity",
"Functional status",
"Medication adherence"
],
evaluation_criteria=[
"Symptom improvement",
"Quality of life measures",
"Patient satisfaction",
"Clinical outcomes"
],
patient_goals=[
"Feel better",
"Return to normal activities",
"Understand my condition",
"Take medications correctly"
],
patient_preferences=[
"Prefer oral medications",
"Morning appointments preferred",
"Family involvement desired"
],
patient_barriers=[
"Transportation challenges",
"Language barriers",
"Financial constraints"
] if random.choice([True, False]) else [],
resources_needed=[
"Medication",
"Medical equipment",
"Educational materials",
"Support services"
],
support_systems=[
"Family support",
"Healthcare team",
"Community resources"
],
progress_notes=f"Patient progressing well with {plan_type.lower().replace('_', ' ')} plan.",
last_reviewed=django_timezone.now().date() - timedelta(days=random.randint(0, 30)),
next_review_date=django_timezone.now().date() + timedelta(days=random.randint(7, 90)),
outcomes_achieved=[
"Symptom reduction achieved",
"Patient education completed"
] if completion_percentage > 50 else ["No outcomes achieved yet"],
completion_percentage=completion_percentage,
approved=approved,
approved_by=primary_provider if approved else None,
approved_date=approved_date, # Use timezone-aware datetime
created_at=django_timezone.make_aware(
datetime.combine(start_date, time(random.randint(8, 17), random.randint(0, 59)))
),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 7)),
created_by=primary_provider
)
# Set many-to-many relationships after creation
care_plan.care_team.set(care_team_list)
care_plan.related_problems.set(related_problems_list)
care_plans.append(care_plan)
except Exception as e:
print(f"Error creating care plan for {patient.get_full_name()}: {e}")
continue
print(f"Created {len(care_plans)} care plans")
return care_plans
def create_clinical_notes(encounters, templates):
"""Create clinical notes for encounters"""
clinical_notes = []
for encounter in encounters:
if encounter.status not in ['COMPLETED', 'IN_PROGRESS']:
continue
# Each encounter has 1-2 clinical notes
num_notes = random.randint(1, 2)
for note_num in range(num_notes):
# Select appropriate template
suitable_templates = [
t for t in templates
if t.tenant == encounter.tenant and (
t.note_type == encounter.encounter_type or
t.specialty == 'FAMILY_MEDICINE'
)
]
template = random.choice(suitable_templates) if suitable_templates else None
# Note type based on encounter
note_type = encounter.encounter_type if encounter.encounter_type in [nt for nt in
SAUDI_NOTE_TYPES] else 'CONSULTATION'
# Generate note title
titles = {
'CONSULTATION': f"Consultation Note - {encounter.chief_complaint}",
'EMERGENCY': f"Emergency Department Note - {encounter.chief_complaint}",
'PROGRESS': f"Progress Note - Day {note_num + 1}",
'SURGERY': f"Surgical Note - {encounter.chief_complaint}",
'PROCEDURE': f"Procedure Note - {encounter.chief_complaint}",
'DISCHARGE': f"Discharge Summary - {encounter.chief_complaint}"
}
title = titles.get(note_type, f"Clinical Note - {encounter.chief_complaint}")
# Generate note content
if template:
content = template.template_content
# Replace placeholders with actual data
content = content.replace('[Chief Complaint]', encounter.chief_complaint)
content = content.replace('[Provider Name]', encounter.provider.get_full_name())
content = content.replace('[Date and Time]', encounter.start_datetime.strftime('%Y-%m-%d %H:%M'))
content = content.replace('[History details]',
f"Patient presents with {encounter.chief_complaint.lower()}.")
content = content.replace('[Clinical impression]',
f"Impression consistent with {encounter.chief_complaint.lower()}.")
content = content.replace('[Treatment plan]',
"Continue current management plan and follow up as scheduled.")
else:
content = f"""
ENCOUNTER DATE: {encounter.start_datetime.strftime('%Y-%m-%d %H:%M')}
PATIENT: {encounter.patient.get_full_name()}
PROVIDER: {encounter.provider.get_full_name()}
CHIEF COMPLAINT: {encounter.chief_complaint}
HISTORY OF PRESENT ILLNESS:
Patient presents with {encounter.chief_complaint.lower()}. Symptoms have been present for several days.
PHYSICAL EXAMINATION:
Patient appears comfortable. Vital signs stable.
ASSESSMENT:
Clinical impression consistent with {encounter.chief_complaint.lower()}.
PLAN:
Continue current management. Follow up in clinic as needed.
Electronically signed by: {encounter.provider.get_full_name()}
Date: {encounter.start_datetime.strftime('%Y-%m-%d %H:%M')}
""".strip()
# Note timing
note_datetime = encounter.start_datetime + timedelta(minutes=random.randint(15, 120))
# Status and signing
if encounter.status == 'COMPLETED':
note_status = random.choices(
['SIGNED', 'PENDING_SIGNATURE', 'DRAFT'],
weights=[70, 20, 10]
)[0]
else:
note_status = 'DRAFT'
electronically_signed = note_status == 'SIGNED'
signed_datetime = note_datetime + timedelta(
minutes=random.randint(30, 180)) if electronically_signed else None
# Co-signers list
co_signers_list = []
if random.choice([True, False]): # 50% chance of co-signer
potential_cosigners = [
p for p in User.objects.filter(
tenant=encounter.tenant,
employee_profile__role__in=['PHYSICIAN', 'NURSE_PRACTITIONER'],
is_active=True
) if p != encounter.provider
]
if potential_cosigners:
co_signers_list = [random.choice(potential_cosigners)]
# Quality and compliance
quality_score = random.randint(75, 100)
compliance_flags = []
if quality_score < 85:
compliance_flags = ['documentation_incomplete', 'signature_pending']
try:
clinical_note = ClinicalNote.objects.create(
encounter=encounter,
patient=encounter.patient,
note_id=uuid.uuid4(),
note_type=note_type,
title=title,
content=content,
template=template,
structured_data={
'chief_complaint': encounter.chief_complaint,
'encounter_type': encounter.encounter_type,
'provider': encounter.provider.get_full_name(),
'location': encounter.location
},
author=encounter.provider,
status=note_status,
electronically_signed=electronically_signed,
signed_datetime=signed_datetime,
signature_method='ELECTRONIC' if electronically_signed else None,
quality_score=quality_score,
compliance_flags=compliance_flags,
note_datetime=note_datetime,
confidential=random.choice([True, False]),
restricted_access=random.choice([True, False]) if random.random() < 0.1 else False,
access_restrictions=['PROVIDER_ONLY'] if random.choice([True, False]) else [],
created_at=note_datetime,
updated_at=note_datetime + timedelta(minutes=random.randint(5, 60))
)
# Set many-to-many relationship after creation
clinical_note.co_signers.set(co_signers_list)
clinical_notes.append(clinical_note)
except Exception as e:
print(f"Error creating clinical note for encounter {encounter.encounter_id}: {e}")
continue
print(f"Created {len(clinical_notes)} clinical notes")
return clinical_notes
def create_icd10_codes(tenants):
"""Create ICD-10 codes for clinical reference"""
icd10_codes = []
# Sample ICD-10 codes with descriptions
icd10_data = [
# Chapter 1: Certain infectious and parasitic diseases
('A00', 'Cholera', 'Certain infectious and parasitic diseases', 'Cholera and other vibrio infections'),
('A01', 'Typhoid and paratyphoid fevers', 'Certain infectious and parasitic diseases', 'Typhoid and paratyphoid fevers'),
('A02', 'Other salmonella infections', 'Certain infectious and parasitic diseases', 'Other salmonella infections'),
('A03', 'Shigellosis', 'Certain infectious and parasitic diseases', 'Shigellosis'),
('A04', 'Other bacterial intestinal infections', 'Certain infectious and parasitic diseases', 'Other bacterial intestinal infections'),
# Chapter 2: Neoplasms
('C00', 'Malignant neoplasm of lip', 'Neoplasms', 'Malignant neoplasms of lip, oral cavity and pharynx'),
('C01', 'Malignant neoplasm of base of tongue', 'Neoplasms', 'Malignant neoplasms of lip, oral cavity and pharynx'),
('C02', 'Malignant neoplasm of other and unspecified parts of tongue', 'Neoplasms', 'Malignant neoplasms of lip, oral cavity and pharynx'),
('C03', 'Malignant neoplasm of gum', 'Neoplasms', 'Malignant neoplasms of lip, oral cavity and pharynx'),
('C04', 'Malignant neoplasm of floor of mouth', 'Neoplasms', 'Malignant neoplasms of lip, oral cavity and pharynx'),
# Chapter 9: Diseases of the circulatory system
('I00', 'Rheumatic fever without mention of heart involvement', 'Diseases of the circulatory system', 'Acute rheumatic fever'),
('I01', 'Rheumatic fever with heart involvement', 'Diseases of the circulatory system', 'Acute rheumatic fever'),
('I02', 'Rheumatic chorea', 'Diseases of the circulatory system', 'Acute rheumatic fever'),
('I05', 'Rheumatic mitral valve diseases', 'Diseases of the circulatory system', 'Chronic rheumatic heart diseases'),
('I06', 'Rheumatic aortic valve diseases', 'Diseases of the circulatory system', 'Chronic rheumatic heart diseases'),
# Chapter 10: Diseases of the respiratory system
('J00', 'Acute nasopharyngitis [common cold]', 'Diseases of the respiratory system', 'Acute upper respiratory infections'),
('J01', 'Acute sinusitis', 'Diseases of the respiratory system', 'Acute upper respiratory infections'),
('J02', 'Acute pharyngitis', 'Diseases of the respiratory system', 'Acute upper respiratory infections'),
('J03', 'Acute tonsillitis', 'Diseases of the respiratory system', 'Acute upper respiratory infections'),
('J04', 'Acute laryngitis and tracheitis', 'Diseases of the respiratory system', 'Acute upper respiratory infections'),
# Chapter 11: Diseases of the digestive system
('K00', 'Disorders of tooth development and eruption', 'Diseases of the digestive system', 'Disorders of teeth and supporting structures'),
('K01', 'Embedded and impacted teeth', 'Diseases of the digestive system', 'Disorders of teeth and supporting structures'),
('K02', 'Dental caries', 'Diseases of the digestive system', 'Disorders of teeth and supporting structures'),
('K03', 'Other diseases of hard tissues of teeth', 'Diseases of the digestive system', 'Disorders of teeth and supporting structures'),
('K04', 'Diseases of pulp and periapical tissues', 'Diseases of the digestive system', 'Disorders of teeth and supporting structures'),
# Chapter 14: Diseases of the genitourinary system
('N00', 'Acute nephritic syndrome', 'Diseases of the genitourinary system', 'Glomerular diseases'),
('N01', 'Rapidly progressive nephritic syndrome', 'Diseases of the genitourinary system', 'Glomerular diseases'),
('N02', 'Recurrent and persistent hematuria', 'Diseases of the genitourinary system', 'Glomerular diseases'),
('N03', 'Chronic nephritic syndrome', 'Diseases of the genitourinary system', 'Glomerular diseases'),
('N04', 'Nephrotic syndrome', 'Diseases of the genitourinary system', 'Glomerular diseases'),
# Chapter 18: Symptoms, signs and abnormal clinical and laboratory findings
('R00', 'Abnormalities of heart beat', 'Symptoms, signs and abnormal clinical and laboratory findings', 'Symptoms and signs involving the circulatory and respiratory systems'),
('R01', 'Cardiac murmurs and other cardiac sounds', 'Symptoms, signs and abnormal clinical and laboratory findings', 'Symptoms and signs involving the circulatory and respiratory systems'),
('R02', 'Gangrene, not elsewhere classified', 'Symptoms, signs and abnormal clinical and laboratory findings', 'Symptoms and signs involving the circulatory and respiratory systems'),
('R03', 'Abnormal blood-pressure reading, without diagnosis', 'Symptoms, signs and abnormal clinical and laboratory findings', 'Symptoms and signs involving the circulatory and respiratory systems'),
('R04', 'Haemorrhage from respiratory passages', 'Symptoms, signs and abnormal clinical and laboratory findings', 'Symptoms and signs involving the circulatory and respiratory systems'),
]
for tenant in tenants:
for code_data in icd10_data:
code, description, chapter, section = code_data
# Determine if it's a header (shorter codes are typically headers)
is_header = len(code) <= 3
# Create parent relationship for sub-codes
parent = None
if len(code) > 3:
parent_code = code[:3]
try:
parent = Icd10.objects.get(code=parent_code, tenant=tenant)
except Icd10.DoesNotExist:
parent = None
try:
icd10_code = Icd10.objects.create(
tenant=tenant,
code=code,
description=description,
chapter_name=chapter,
section_name=section,
parent=parent,
is_header=is_header,
created_at=django_timezone.now() - timedelta(days=random.randint(30, 365)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 30))
)
icd10_codes.append(icd10_code)
except Exception as e:
print(f"Error creating ICD-10 code {code}: {e}")
continue
print(f"Created {len(icd10_codes)} ICD-10 codes")
return icd10_codes
def create_clinical_recommendations(patients, providers, problems, encounters):
"""Create clinical recommendations for patients"""
recommendations = []
recommendation_templates = [
{
'category': 'PREVENTIVE',
'title': 'Annual Health Screening',
'description': 'Patient due for annual comprehensive health screening including blood work, imaging, and preventive counseling.',
'evidence_level': '1A',
'source': 'USPSTF Guidelines 2023',
'rationale': 'Regular screening improves early detection and prevention of chronic diseases.',
'priority': 'MEDIUM'
},
{
'category': 'DIAGNOSTIC',
'title': 'Cardiac Evaluation Recommended',
'description': 'Consider echocardiography and stress testing given patient history and risk factors.',
'evidence_level': '1B',
'source': 'ACC/AHA Guidelines 2022',
'rationale': 'Patient presents with cardiac symptoms requiring further evaluation.',
'priority': 'HIGH'
},
{
'category': 'TREATMENT',
'title': 'Optimize Diabetes Management',
'description': 'Consider adjusting diabetes regimen based on recent HbA1c and glucose readings.',
'evidence_level': '1A',
'source': 'ADA Standards of Care 2023',
'rationale': 'Current glycemic control may be suboptimal requiring treatment intensification.',
'priority': 'HIGH'
},
{
'category': 'MONITORING',
'title': 'Blood Pressure Monitoring',
'description': 'Increase frequency of blood pressure monitoring and consider 24-hour ambulatory monitoring.',
'evidence_level': '1B',
'source': 'JNC 8 Guidelines',
'rationale': 'Recent readings suggest need for closer monitoring and possible treatment adjustment.',
'priority': 'MEDIUM'
},
{
'category': 'LIFESTYLE',
'title': 'Weight Management Program',
'description': 'Recommend enrollment in structured weight management program with dietary counseling.',
'evidence_level': '1A',
'source': 'Obesity Guidelines 2023',
'rationale': 'Patient BMI indicates need for weight reduction to improve health outcomes.',
'priority': 'MEDIUM'
},
{
'category': 'MEDICATION',
'title': 'Statin Therapy Consideration',
'description': 'Consider initiating statin therapy based on cardiovascular risk assessment.',
'evidence_level': '1A',
'source': 'ACC/AHA Cholesterol Guidelines',
'rationale': 'Patient risk factors warrant preventive cardiovascular therapy.',
'priority': 'HIGH'
},
{
'category': 'FOLLOW_UP',
'title': 'Specialist Consultation',
'description': 'Refer to cardiology/endocrinology for comprehensive evaluation.',
'evidence_level': '2A',
'source': 'Clinical judgment',
'rationale': 'Complex medical condition requires specialist input.',
'priority': 'HIGH'
},
{
'category': 'EDUCATION',
'title': 'Diabetes Self-Management Education',
'description': 'Patient would benefit from structured diabetes education program.',
'evidence_level': '1A',
'source': 'ADA Standards',
'rationale': 'Education improves self-management and clinical outcomes.',
'priority': 'MEDIUM'
}
]
for patient in patients:
# Create 1-3 recommendations per patient
num_recommendations = random.randint(1, 3)
patient_problems = [p for p in problems if p.patient == patient]
patient_encounters = [e for e in encounters if e.patient == patient]
for _ in range(num_recommendations):
template = random.choice(recommendation_templates)
# Generate unique title based on patient context
if patient_problems:
main_problem = random.choice(patient_problems).problem_name
title = f"{template['title']} - {main_problem}"
else:
title = template['title']
# Status progression
status = random.choices(
['PENDING', 'ACTIVE', 'ACCEPTED', 'DEFERRED', 'COMPLETED'],
weights=[40, 30, 15, 10, 5]
)[0]
# Provider assignments
tenant_providers = [p for p in providers if p.tenant == patient.tenant]
if not tenant_providers:
print(f"Warning: No providers found for tenant {patient.tenant.name}. Skipping recommendation for {patient.get_full_name()}")
continue
created_by_provider = random.choice(tenant_providers)
accepted_by = None
accepted_at = None
if status in ['ACCEPTED', 'COMPLETED']:
accepted_by = random.choice(tenant_providers)
accepted_at = django_timezone.now() - timedelta(days=random.randint(1, 30))
deferred_by = None
deferred_at = None
if status == 'DEFERRED':
deferred_by = random.choice(tenant_providers)
deferred_at = django_timezone.now() - timedelta(days=random.randint(1, 14))
dismissed_by = None
dismissed_at = None
if status == 'DISMISSED':
dismissed_by = random.choice(tenant_providers)
dismissed_at = django_timezone.now() - timedelta(days=random.randint(1, 7))
# Expiration
expires_at = django_timezone.now() + timedelta(days=random.randint(30, 180))
# Related data
related_problems_list = random.sample(
patient_problems,
min(random.randint(0, 2), len(patient_problems))
) if patient_problems else []
related_encounter = random.choice(patient_encounters) if patient_encounters else None
try:
recommendation = ClinicalRecommendation.objects.create(
tenant=patient.tenant,
patient=patient,
recommendation_id=uuid.uuid4(),
title=title,
description=template['description'],
category=template['category'],
priority=template['priority'],
evidence_level=template['evidence_level'],
source=template['source'],
rationale=template['rationale'],
status=status,
accepted_by=accepted_by,
accepted_at=accepted_at,
deferred_by=deferred_by,
deferred_at=deferred_at,
dismissed_by=dismissed_by,
dismissed_at=dismissed_at,
expires_at=expires_at,
related_encounter=related_encounter,
created_by=created_by_provider,
created_at=django_timezone.now() - timedelta(days=random.randint(1, 60)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 7))
)
# Set many-to-many relationships
if related_problems_list:
recommendation.related_problems.set(related_problems_list)
recommendations.append(recommendation)
except Exception as e:
print(f"Error creating clinical recommendation for {patient.get_full_name()}: {e}")
continue
print(f"Created {len(recommendations)} clinical recommendations")
return recommendations
def create_allergy_alerts(patients, providers):
"""Create allergy alerts for patients"""
alerts = []
common_allergens = [
'Penicillin', 'Aspirin', 'Ibuprofen', 'Codeine', 'Morphine', 'Latex',
'Shellfish', 'Peanuts', 'Tree Nuts', 'Eggs', 'Milk', 'Soy',
'Wheat', 'Sulfa Drugs', 'Tetracycline', 'Erythromycin', 'Cephalosporins',
'NSAIDs', 'Contrast Dye', 'Local Anesthetics', 'Insulin', 'ACE Inhibitors'
]
reaction_types = [
'Anaphylaxis', 'Angioedema', 'Urticaria', 'Rash', 'Pruritus',
'Nausea/Vomiting', 'Diarrhea', 'Dyspnea', 'Wheezing', 'Hypotension',
'Tachycardia', 'Bronchospasm', 'Stevens-Johnson Syndrome', 'Toxic Epidermal Necrolysis'
]
severities = ['MILD', 'MODERATE', 'SEVERE', 'LIFE_THREATENING']
for patient in patients:
# 20% of patients have allergies
if random.random() > 0.2:
continue
# Each allergic patient has 1-3 allergies
num_allergies = random.randint(1, 3)
for _ in range(num_allergies):
allergen = random.choice(common_allergens)
# Ensure no duplicate allergens for same patient
existing_allergens = [alert.allergen for alert in alerts if alert.patient == patient]
while allergen in existing_allergens:
allergen = random.choice(common_allergens)
reaction_type = random.choice(reaction_types)
# Severity based on reaction type
if 'Anaphylaxis' in reaction_type or 'Stevens-Johnson' in reaction_type:
severity = random.choices(severities, weights=[0, 10, 30, 60])[0]
elif 'Angioedema' in reaction_type or 'Bronchospasm' in reaction_type:
severity = random.choices(severities, weights=[10, 30, 40, 20])[0]
else:
severity = random.choices(severities, weights=[40, 40, 15, 5])[0]
# Symptoms based on reaction type
symptoms = []
if 'Rash' in reaction_type or 'Urticaria' in reaction_type:
symptoms.extend(['Hives', 'Itching', 'Redness'])
if 'Anaphylaxis' in reaction_type:
symptoms.extend(['Difficulty breathing', 'Swelling of throat', 'Dizziness', 'Nausea'])
if 'Angioedema' in reaction_type:
symptoms.extend(['Facial swelling', 'Tongue swelling', 'Lip swelling'])
if 'Dyspnea' in reaction_type or 'Wheezing' in reaction_type:
symptoms.extend(['Shortness of breath', 'Wheezing', 'Chest tightness'])
symptoms = symptoms[:random.randint(1, 4)] if symptoms else ['Unknown symptoms']
# Onset timing
onset = random.choice([
'Immediate (< 1 hour)', 'Early (1-6 hours)', 'Delayed (6-24 hours)',
'Late (> 24 hours)', 'Unknown'
])
# Status
tenant_providers = [p for p in providers if p.tenant == patient.tenant]
if not tenant_providers:
print(f"Warning: No providers found for tenant {patient.tenant.name}. Skipping allergy alert for {patient.get_full_name()}")
continue
resolved = random.choice([True, False])
resolved_at = django_timezone.now() - timedelta(days=random.randint(1, 365)) if resolved else None
resolved_by = random.choice(tenant_providers) if resolved else None
try:
alert = AllergyAlert.objects.create(
tenant=patient.tenant,
patient=patient,
alert_id=uuid.uuid4(),
allergen=allergen,
reaction_type=reaction_type,
severity=severity,
symptoms=symptoms,
onset=onset,
resolved=resolved,
resolved_at=resolved_at,
resolved_by=resolved_by,
detected_at=django_timezone.now() - timedelta(days=random.randint(1, 3650)),
created_at=django_timezone.now() - timedelta(days=random.randint(1, 3650)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 30))
)
alerts.append(alert)
except Exception as e:
print(f"Error creating allergy alert for {patient.get_full_name()}: {e}")
continue
print(f"Created {len(alerts)} allergy alerts")
return alerts
def create_treatment_protocols(tenants, providers):
"""Create treatment protocols"""
protocols = []
protocol_templates = [
{
'name': 'Acute Coronary Syndrome Management',
'indication': 'Management of patients with suspected or confirmed acute coronary syndrome',
'goals': ['Rapid reperfusion', 'Pain control', 'Complication prevention'],
'interventions': ['ECG within 10 minutes', 'Cardiac enzymes', 'Antiplatelet therapy', 'Anticoagulation'],
'monitoring_parameters': ['ECG changes', 'Cardiac enzymes', 'Vital signs', 'Pain assessment'],
'success_rate': 85.0,
'average_duration': 7
},
{
'name': 'Diabetes Mellitus Type 2 Management',
'indication': 'Comprehensive management of type 2 diabetes mellitus',
'goals': ['Glycemic control (HbA1c < 7%)', 'Cardiovascular risk reduction', 'Prevention of complications'],
'interventions': ['Lifestyle modification', 'Oral hypoglycemics', 'Insulin therapy', 'Regular monitoring'],
'monitoring_parameters': ['HbA1c', 'Fasting glucose', 'Blood pressure', 'Lipid profile'],
'success_rate': 75.0,
'average_duration': 365
},
{
'name': 'Community Acquired Pneumonia Treatment',
'indication': 'Treatment of community-acquired pneumonia in adults',
'goals': ['Clinical cure', 'Prevention of complications', 'Appropriate antibiotic use'],
'interventions': ['Appropriate antibiotics', 'Supportive care', 'Vaccination assessment'],
'monitoring_parameters': ['Clinical improvement', 'Fever resolution', 'Oxygen saturation'],
'success_rate': 90.0,
'average_duration': 10
},
{
'name': 'Hypertension Management Protocol',
'indication': 'Management of essential hypertension',
'goals': ['BP control (< 130/80 mmHg)', 'Cardiovascular risk reduction', 'Organ protection'],
'interventions': ['Lifestyle modification', 'Antihypertensive therapy', 'Regular monitoring'],
'monitoring_parameters': ['Blood pressure', 'Electrolyte levels', 'Renal function'],
'success_rate': 70.0,
'average_duration': 180
},
{
'name': 'Acute Asthma Exacerbation Protocol',
'indication': 'Management of acute asthma exacerbations',
'goals': ['Rapid symptom relief', 'Prevention of respiratory failure', 'Hospitalization prevention'],
'interventions': ['Bronchodilators', 'Systemic corticosteroids', 'Oxygen therapy', 'Monitoring'],
'monitoring_parameters': ['Peak flow', 'Oxygen saturation', 'Respiratory rate', 'Clinical symptoms'],
'success_rate': 88.0,
'average_duration': 3
}
]
for tenant in tenants:
for template in protocol_templates:
# Get providers for this tenant
tenant_providers = [p for p in providers if p.tenant == tenant]
if not tenant_providers:
print(f"Warning: No providers found for tenant {tenant.name}. Skipping protocol {template['name']}")
continue
try:
protocol = TreatmentProtocol.objects.create(
tenant=tenant,
protocol_id=uuid.uuid4(),
name=template['name'],
description=f"Standardized protocol for {template['name'].lower()}",
indication=template['indication'],
goals=template['goals'],
interventions=template['interventions'],
monitoring_parameters=template['monitoring_parameters'],
success_rate=Decimal(str(template['success_rate'])),
average_duration=template['average_duration'],
is_active=True,
usage_count=random.randint(10, 200),
created_at=django_timezone.now() - timedelta(days=random.randint(30, 365)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 30)),
created_by=random.choice(tenant_providers)
)
protocols.append(protocol)
except Exception as e:
print(f"Error creating treatment protocol {template['name']}: {e}")
continue
print(f"Created {len(protocols)} treatment protocols")
return protocols
def create_clinical_guidelines(tenants):
"""Create clinical guidelines"""
guidelines = []
guideline_templates = [
{
'title': 'Management of Heart Failure',
'organization': 'American Heart Association',
'summary': 'Comprehensive guidelines for the diagnosis and management of heart failure',
'publication_date': date(2022, 4, 1),
'version': '2022',
'keywords': ['heart failure', 'cardiology', 'HFrEF', 'HFpEF'],
'specialties': ['Cardiology', 'Internal Medicine', 'Family Medicine']
},
{
'title': 'Standards of Medical Care in Diabetes',
'organization': 'American Diabetes Association',
'summary': 'Comprehensive guidelines for the diagnosis and management of diabetes mellitus',
'publication_date': date(2023, 1, 1),
'version': '2023',
'keywords': ['diabetes', 'glycemia', 'complications', 'prevention'],
'specialties': ['Endocrinology', 'Internal Medicine', 'Family Medicine']
},
{
'title': 'Guidelines for the Prevention, Detection, Evaluation, and Management of High Blood Pressure',
'organization': 'American College of Cardiology/American Heart Association',
'summary': 'Evidence-based guidelines for hypertension management',
'publication_date': date(2017, 11, 13),
'version': '2017',
'keywords': ['hypertension', 'blood pressure', 'cardiovascular', 'risk'],
'specialties': ['Cardiology', 'Internal Medicine', 'Nephrology']
},
{
'title': 'Global Initiative for Asthma (GINA) Report',
'organization': 'Global Initiative for Asthma',
'summary': 'Global strategy for asthma management and prevention',
'publication_date': date(2023, 5, 1),
'version': '2023',
'keywords': ['asthma', 'respiratory', 'inhalers', 'control'],
'specialties': ['Pulmonology', 'Allergy', 'Pediatrics']
},
{
'title': 'Prevention and Management of Osteoporosis',
'organization': 'World Health Organization',
'summary': 'Guidelines for osteoporosis prevention and treatment',
'publication_date': date(2022, 1, 1),
'version': '2022',
'keywords': ['osteoporosis', 'bone density', 'fracture', 'calcium'],
'specialties': ['Endocrinology', 'Rheumatology', 'Geriatrics']
}
]
for tenant in tenants:
for template in guideline_templates:
try:
guideline = ClinicalGuideline.objects.create(
tenant=tenant,
guideline_id=uuid.uuid4(),
title=template['title'],
organization=template['organization'],
summary=template['summary'],
url=f"https://example.com/guidelines/{template['title'].lower().replace(' ', '_')}",
publication_date=template['publication_date'],
last_updated=django_timezone.now().date() - timedelta(days=random.randint(0, 365)),
version=template['version'],
is_active=True,
keywords=template['keywords'],
specialties=template['specialties'],
created_at=django_timezone.now() - timedelta(days=random.randint(30, 365)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 30))
)
guidelines.append(guideline)
except Exception as e:
print(f"Error creating clinical guideline {template['title']}: {e}")
continue
print(f"Created {len(guidelines)} clinical guidelines")
return guidelines
def create_critical_alerts(patients, providers, encounters):
"""Create critical alerts for patients"""
alerts = []
alert_templates = [
{
'title': 'Critical Lab Value Alert',
'description': 'Potassium level critically elevated at 7.2 mEq/L',
'priority': 'CRITICAL',
'recommendation': 'Immediate treatment with calcium gluconate, insulin, and glucose. Transfer to ICU.'
},
{
'title': 'Acute Coronary Syndrome Alert',
'description': 'ST-elevation myocardial infarction detected on ECG',
'priority': 'CRITICAL',
'recommendation': 'Activate cardiac catheterization lab. Administer aspirin, heparin, and prepare for PCI.'
},
{
'title': 'Severe Hypoglycemia Alert',
'description': 'Blood glucose critically low at 35 mg/dL',
'priority': 'CRITICAL',
'recommendation': 'Administer IV dextrose immediately. Recheck glucose in 15 minutes.'
},
{
'title': 'Acute Stroke Alert',
'description': 'Suspected acute ischemic stroke with NIHSS score of 18',
'priority': 'URGENT',
'recommendation': 'Activate stroke team. Obtain CT brain immediately. Prepare for thrombolytic therapy.'
},
{
'title': 'Sepsis Alert',
'description': 'SIRS criteria met with suspected infection. Lactate elevated at 4.2 mmol/L',
'priority': 'URGENT',
'recommendation': 'Obtain blood cultures, start broad-spectrum antibiotics within 1 hour, fluid resuscitation.'
},
{
'title': 'Drug Interaction Alert',
'description': 'Critical drug interaction between warfarin and newly prescribed antibiotic',
'priority': 'HIGH',
'recommendation': 'Monitor INR closely. Consider dose adjustment or alternative antibiotic.'
},
{
'title': 'Allergy Alert - Severe Reaction',
'description': 'Patient with history of anaphylaxis to penicillin now prescribed amoxicillin',
'priority': 'HIGH',
'recommendation': 'Discontinue amoxicillin. Prescribe alternative antibiotic. Consider premedication if necessary.'
}
]
for patient in patients:
# 5% of patients have critical alerts
if random.random() > 0.05:
continue
# Each patient with alerts has 1-2 critical alerts
num_alerts = random.randint(1, 2)
patient_encounters = [e for e in encounters if e.patient == patient]
for _ in range(num_alerts):
template = random.choice(alert_templates)
# Status
tenant_providers = [p for p in providers if p.tenant == patient.tenant]
if not tenant_providers:
print(f"Warning: No providers found for tenant {patient.tenant.name}. Skipping critical alert for {patient.get_full_name()}")
continue
acknowledged = random.choice([True, False])
acknowledged_by = random.choice(tenant_providers) if acknowledged else None
acknowledged_at = django_timezone.now() - timedelta(hours=random.randint(1, 24)) if acknowledged else None
# Expiration
expires_at = django_timezone.now() + timedelta(hours=random.randint(24, 168))
# Related encounter
related_encounter = random.choice(patient_encounters) if patient_encounters else None
try:
alert = CriticalAlert.objects.create(
tenant=patient.tenant,
patient=patient,
alert_id=uuid.uuid4(),
title=template['title'],
description=template['description'],
priority=template['priority'],
recommendation=template['recommendation'],
acknowledged=acknowledged,
acknowledged_by=acknowledged_by,
acknowledged_at=acknowledged_at,
expires_at=expires_at,
related_encounter=related_encounter,
created_by=random.choice(tenant_providers),
created_at=django_timezone.now() - timedelta(hours=random.randint(1, 48)),
updated_at=django_timezone.now() - timedelta(minutes=random.randint(0, 60))
)
alerts.append(alert)
except Exception as e:
print(f"Error creating critical alert for {patient.get_full_name()}: {e}")
continue
print(f"Created {len(alerts)} critical alerts")
return alerts
def create_diagnostic_suggestions(patients, providers, encounters):
"""Create diagnostic suggestions for patients"""
suggestions = []
suggestion_templates = [
{
'test_name': 'Echocardiogram',
'test_code': 'ECHO',
'indication': 'Evaluate cardiac function and structure',
'confidence': 85.0
},
{
'test_name': 'Coronary Angiography',
'test_code': 'CATH',
'indication': 'Assess coronary artery disease',
'confidence': 78.0
},
{
'test_name': 'MRI Brain',
'test_code': 'MRI_BRAIN',
'indication': 'Evaluate neurological symptoms',
'confidence': 72.0
},
{
'test_name': 'CT Chest',
'test_code': 'CT_CHEST',
'indication': 'Assess pulmonary pathology',
'confidence': 80.0
},
{
'test_name': 'Colonoscopy',
'test_code': 'COLON',
'indication': 'Screen for colorectal cancer',
'confidence': 88.0
},
{
'test_name': 'DEXA Scan',
'test_code': 'DEXA',
'indication': 'Assess bone mineral density',
'confidence': 90.0
},
{
'test_name': 'Thyroid Function Tests',
'test_code': 'TFT',
'indication': 'Evaluate thyroid dysfunction',
'confidence': 75.0
},
{
'test_name': 'HbA1c',
'test_code': 'HBA1C',
'indication': 'Assess long-term glycemic control',
'confidence': 95.0
}
]
for patient in patients:
# 15% of patients have diagnostic suggestions
if random.random() > 0.15:
continue
# Each patient with suggestions has 1-3 suggestions
num_suggestions = random.randint(1, 3)
patient_encounters = [e for e in encounters if e.patient == patient]
for _ in range(num_suggestions):
template = random.choice(suggestion_templates)
# Status
status = random.choices(
['PENDING', 'ORDERED', 'COMPLETED'],
weights=[50, 30, 20]
)[0]
# Provider assignments
tenant_providers = [p for p in providers if p.tenant == patient.tenant]
if not tenant_providers:
print(f"Warning: No providers found for tenant {patient.tenant.name}. Skipping diagnostic suggestion for {patient.get_full_name()}")
continue
ordered_by = None
ordered_at = None
if status in ['ORDERED', 'COMPLETED']:
ordered_by = random.choice(tenant_providers)
ordered_at = django_timezone.now() - timedelta(days=random.randint(1, 14))
try:
suggestion = DiagnosticSuggestion.objects.create(
tenant=patient.tenant,
patient=patient,
suggestion_id=uuid.uuid4(),
test_name=template['test_name'],
test_code=template['test_code'],
indication=template['indication'],
confidence=Decimal(str(template['confidence'])),
status=status,
ordered_by=ordered_by,
ordered_at=ordered_at,
created_by=random.choice(tenant_providers),
created_at=django_timezone.now() - timedelta(days=random.randint(1, 30)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 7))
)
suggestions.append(suggestion)
except Exception as e:
print(f"Error creating diagnostic suggestion for {patient.get_full_name()}: {e}")
continue
print(f"Created {len(suggestions)} diagnostic suggestions")
return suggestions
# ============================================================================
# ICD-10 XML IMPORT FUNCTIONS (from import_icd10.py management command)
# ============================================================================
def _as_text(val):
"""Convert value to text string"""
if val is None:
return None
if isinstance(val, dict):
return val.get("#text") or val.get("@value") or val.get("value") or str(val)
return str(val)
def _ensure_list(maybe_list):
"""Ensure value is a list"""
if maybe_list is None:
return []
if isinstance(maybe_list, list):
return maybe_list
return [maybe_list]
def _find_first_with_key(data, key):
"""Depth-first search: return the first dict that directly contains `key`"""
if isinstance(data, dict):
if key in data:
return data
for v in data.values():
found = _find_first_with_key(v, key)
if found is not None:
return found
elif isinstance(data, list):
for item in data:
found = _find_first_with_key(item, key)
if found is not None:
return found
return None
def _collect_icd10_rows(chapters, tenants):
"""
Build Icd10 rows + parent links from chapters dict/list.
Expected structure: chapter -> section? -> diag (recursive)
"""
rows = []
parent_links = []
def import_diag(diag, chapter_name, section_name, parent_code, tenant):
code = _as_text(diag.get("name"))
desc = _as_text(diag.get("desc"))
if not code:
return
children = _ensure_list(diag.get("diag"))
is_header = bool(children) and not (desc and desc.strip())
rows.append(Icd10(
tenant=tenant,
code=code,
description=desc,
chapter_name=_as_text(chapter_name),
section_name=_as_text(section_name),
parent=None, # set later
is_header=is_header,
))
if parent_code:
parent_links.append((code, parent_code, tenant))
for child in children:
import_diag(child, chapter_name, section_name, parent_code=code, tenant=tenant)
# Normalize chapters to a list
chapters = _ensure_list(chapters)
# Process for each tenant
for tenant in tenants:
for ch in chapters:
ch_name = _as_text(ch.get("name"))
# Sections may be missing; diags may be directly under chapter
sections = _ensure_list(ch.get("section"))
if sections:
for sec in sections:
sec_name = _as_text(sec.get("name"))
for d in _ensure_list(sec.get("diag")):
import_diag(d, ch_name, sec_name, parent_code=None, tenant=tenant)
else:
# If no sections, look for diags at chapter level
for d in _ensure_list(ch.get("diag")):
import_diag(d, ch_name, None, parent_code=None, tenant=tenant)
return rows, parent_links
def import_icd10_from_xml(xsd_path, xml_path, tenants, truncate=False):
"""
Import ICD-10-CM codes from XML file.
Args:
xsd_path: Path to XSD schema file
xml_path: Path to XML data file
tenants: List of tenant objects to import codes for
truncate: Whether to delete existing codes first
Returns:
Number of codes imported
"""
if not XMLSCHEMA_AVAILABLE:
print("❌ Error: xmlschema library not installed.")
print(" Install it with: pip install xmlschema")
return 0
print(f"\n📥 Importing ICD-10 codes from XML...")
print(f" XSD: {xsd_path}")
print(f" XML: {xml_path}")
try:
xs = xmlschema.XMLSchema(xsd_path)
except Exception as e:
print(f"❌ Failed to load XSD: {e}")
return 0
try:
data = xs.to_dict(xml_path)
except Exception as e:
print(f"❌ Failed to parse XML: {e}")
return 0
# Unwrap root if single-key dict
if isinstance(data, dict) and len(data) == 1:
root_key, root_val = next(iter(data.items()))
root = root_val
else:
root = data
# Find container with "chapter" key
container_with_chapter = _find_first_with_key(root, "chapter")
if not container_with_chapter:
container_with_chapter = _find_first_with_key(root, "chapters")
if container_with_chapter and isinstance(container_with_chapter.get("chapters"), dict):
if "chapter" in container_with_chapter["chapters"]:
container_with_chapter = container_with_chapter["chapters"]
if not container_with_chapter or ("chapter" not in container_with_chapter):
preview_keys = list(root.keys()) if isinstance(root, dict) else type(root)
print(f"❌ Could not locate 'chapter' in XML. Top-level keys: {preview_keys}")
return 0
chapters = container_with_chapter.get("chapter")
if chapters is None:
print("❌ Found container for chapters, but 'chapter' is empty.")
return 0
# Optionally truncate
if truncate:
print("⚠️ Truncating existing ICD-10 data...")
Icd10.objects.all().delete()
# Collect rows + parent links
print("📊 Collecting ICD-10 rows...")
rows, parent_links = _collect_icd10_rows(chapters, tenants)
print(f"✅ Collected {len(rows)} rows. Inserting...")
BATCH = 1000
with transaction.atomic():
for i in range(0, len(rows), BATCH):
Icd10.objects.bulk_create(rows[i:i+BATCH], ignore_conflicts=True)
# Link parents
if parent_links:
print("🔗 Linking parent relationships...")
code_to_obj = {}
for tenant in tenants:
tenant_codes = {o.code: o for o in Icd10.objects.filter(tenant=tenant).only("id", "code")}
code_to_obj[tenant.id] = tenant_codes
updates = []
for child_code, parent_code, tenant in parent_links:
tenant_codes = code_to_obj.get(tenant.id, {})
child = tenant_codes.get(child_code)
parent = tenant_codes.get(parent_code)
if child and parent and child.parent_id != parent.id:
child.parent_id = parent.id
updates.append(child)
for i in range(0, len(updates), BATCH):
Icd10.objects.bulk_update(updates[i:i+BATCH], ["parent"])
print(f"✅ Linked {len(updates)} parent relations.")
print(f"✅ ICD-10 import completed: {len(rows)} codes imported")
return len(rows)
# ============================================================================
# MAIN DATA GENERATION FUNCTIONS
# ============================================================================
def parse_arguments():
"""Parse command-line arguments"""
parser = argparse.ArgumentParser(
description='Generate EMR sample data and optionally import ICD-10 codes from XML'
)
parser.add_argument(
'--import-icd10',
action='store_true',
help='Import full ICD-10 codes from XML files'
)
parser.add_argument(
'--xsd',
type=str,
help='Path to ICD-10 XSD schema file (required with --import-icd10)'
)
parser.add_argument(
'--xml',
type=str,
help='Path to ICD-10 XML data file (required with --import-icd10)'
)
parser.add_argument(
'--icd10-only',
action='store_true',
help='Only import ICD-10 codes, skip other EMR data generation'
)
parser.add_argument(
'--truncate',
action='store_true',
help='Delete existing ICD-10 codes before importing'
)
return parser.parse_args()
def main():
"""Main function to generate all EMR data"""
# Parse command-line arguments
args = parse_arguments()
# Validate ICD-10 import arguments
if args.import_icd10 or args.icd10_only:
if not args.xsd or not args.xml:
print("❌ Error: --xsd and --xml are required when using --import-icd10 or --icd10-only")
sys.exit(1)
if not XMLSCHEMA_AVAILABLE:
print("❌ Error: xmlschema library not installed.")
print(" Install it with: pip install xmlschema")
sys.exit(1)
# Get existing tenants
tenants = list(Tenant.objects.all())
if not tenants:
print("❌ No tenants found. Please run the core data generator first.")
return
# ICD-10 only mode
if args.icd10_only:
print("🏥 ICD-10 Import Mode (skipping other EMR data)")
import_icd10_from_xml(args.xsd, args.xml, tenants, truncate=args.truncate)
return
# Standard EMR data generation
print("Starting Saudi Healthcare EMR 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 note templates
print("\n1. Creating Note Templates...")
templates = create_note_templates(tenants)
# Create encounters
print("\n2. Creating Patient Encounters...")
encounters = create_encounters(tenants, days_back=30)
# Create vital signs
print("\n3. Creating Vital Signs...")
vital_signs = create_vital_signs(encounters)
# Get patients and providers for remaining models
patients = list(PatientProfile.objects.filter(tenant__in=tenants))
providers = get_providers_for_emr(tenants)
# Create problem lists
print("\n4. Creating Problem Lists...")
problems = create_problem_lists(patients, providers)
# Create care plans
print("\n5. Creating Care Plans...")
care_plans = create_care_plans(patients, providers, problems)
# Create clinical notes
print("\n6. Creating Clinical Notes...")
clinical_notes = create_clinical_notes(encounters, templates)
# Create ICD-10 codes (sample or full import)
print("\n7. Creating ICD-10 Codes...")
if args.import_icd10:
# Import full ICD-10 from XML
num_imported = import_icd10_from_xml(args.xsd, args.xml, tenants, truncate=args.truncate)
icd10_codes = list(Icd10.objects.filter(tenant__in=tenants))
else:
# Create sample ICD-10 codes
icd10_codes = create_icd10_codes(tenants)
# Create clinical recommendations
print("\n8. Creating Clinical Recommendations...")
clinical_recommendations = create_clinical_recommendations(patients, providers, problems, encounters)
# Create allergy alerts
print("\n9. Creating Allergy Alerts...")
allergy_alerts = create_allergy_alerts(patients, providers)
# Create treatment protocols
print("\n10. Creating Treatment Protocols...")
treatment_protocols = create_treatment_protocols(tenants, providers)
# Create clinical guidelines
print("\n11. Creating Clinical Guidelines...")
clinical_guidelines = create_clinical_guidelines(tenants)
# Create critical alerts
print("\n12. Creating Critical Alerts...")
critical_alerts = create_critical_alerts(patients, providers, encounters)
# Create diagnostic suggestions
print("\n13. Creating Diagnostic Suggestions...")
diagnostic_suggestions = create_diagnostic_suggestions(patients, providers, encounters)
print(f"\n✅ Saudi Healthcare EMR Data Generation Complete!")
print(f"📊 Summary:")
print(f" - Note Templates: {len(templates)}")
print(f" - Patient Encounters: {len(encounters)}")
print(f" - Vital Signs Records: {len(vital_signs)}")
print(f" - Problem List Entries: {len(problems)}")
print(f" - Care Plans: {len(care_plans)}")
print(f" - Clinical Notes: {len(clinical_notes)}")
print(f" - ICD-10 Codes: {len(icd10_codes)}")
print(f" - Clinical Recommendations: {len(clinical_recommendations)}")
print(f" - Allergy Alerts: {len(allergy_alerts)}")
print(f" - Treatment Protocols: {len(treatment_protocols)}")
print(f" - Clinical Guidelines: {len(clinical_guidelines)}")
print(f" - Critical Alerts: {len(critical_alerts)}")
print(f" - Diagnostic Suggestions: {len(diagnostic_suggestions)}")
# Statistics
if encounters:
encounter_type_counts = {}
for encounter in encounters:
encounter_type_counts[encounter.encounter_type] = encounter_type_counts.get(encounter.encounter_type, 0) + 1
print(f"\n📈 Encounter Type Distribution:")
for enc_type, count in sorted(encounter_type_counts.items(), key=lambda x: x[1], reverse=True)[:10]:
print(f" - {enc_type.replace('_', ' ').title()}: {count}")
if problems:
problem_status_counts = {}
for problem in problems:
problem_status_counts[problem.status] = problem_status_counts.get(problem.status, 0) + 1
print(f"\n📋 Problem Status Distribution:")
for status, count in problem_status_counts.items():
print(f" - {status.title()}: {count}")
return {
'templates': templates,
'encounters': encounters,
'vital_signs': vital_signs,
'problems': problems,
'care_plans': care_plans,
'clinical_notes': clinical_notes,
'icd10_codes': icd10_codes,
'clinical_recommendations': clinical_recommendations,
'allergy_alerts': allergy_alerts,
'treatment_protocols': treatment_protocols,
'clinical_guidelines': clinical_guidelines,
'critical_alerts': critical_alerts,
'diagnostic_suggestions': diagnostic_suggestions
}
if __name__ == "__main__":
main()