1285 lines
47 KiB
Python
1285 lines
47 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, date, time, timedelta
|
|
from django.utils import timezone as django_timezone
|
|
from emr.models import Encounter, VitalSigns, ProblemList, CarePlan, ClinicalNote, NoteTemplate
|
|
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
|
|
|
|
|
|
# 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', 'RISK_FACTOR', 'ALLERGY',
|
|
'MEDICATION_INTOLERANCE', 'FAMILY_HISTORY', 'SOCIAL_HISTORY'
|
|
]
|
|
|
|
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 = [
|
|
'TREATMENT', 'PREVENTION', 'REHABILITATION', 'CHRONIC_CARE',
|
|
'ACUTE_CARE', 'SURGICAL', 'MEDICATION_MANAGEMENT', 'LIFESTYLE'
|
|
]
|
|
|
|
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)
|
|
provider = random.choice([p for p in providers if p.tenant == patient.tenant])
|
|
|
|
# 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(
|
|
['COMPLETED', 'CANCELLED'],
|
|
weights=[85, 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 = 'ADULT' if patient_age >= 18 else random.choice(['PEDIATRIC', 'INFANT'])
|
|
|
|
# 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', 'IRREGULAR', 'BRADYCARDIA', 'TACHYCARDIA'],
|
|
weights=[85, 10, 3, 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]
|
|
)[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', 'CHRONIC'],
|
|
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
|
|
diagnosing_provider = random.choice([p for p in providers if p.tenant == patient.tenant])
|
|
managing_provider = diagnosing_provider if random.choice([True, False]) else random.choice(
|
|
[p for p in providers if p.tenant == patient.tenant])
|
|
|
|
# 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([
|
|
'MEDICAL', 'SURGICAL', 'NURSING', 'REHABILITATION',
|
|
'PREVENTIVE', 'CHRONIC_DISEASE', 'ACUTE_CARE'
|
|
])
|
|
|
|
# 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(
|
|
['HIGH', 'MEDIUM', 'LOW'],
|
|
weights=[25, 50, 25]
|
|
)[0]
|
|
|
|
# Providers
|
|
primary_provider = random.choice([p for p in providers if p.tenant == patient.tenant])
|
|
care_team_list = random.sample(
|
|
[p for p in providers if p.tenant == patient.tenant],
|
|
min(random.randint(2, 4), len([p for p in providers if p.tenant == patient.tenant]))
|
|
)
|
|
|
|
# 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 [],
|
|
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 main():
|
|
"""Main function to generate all EMR data"""
|
|
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)
|
|
|
|
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)}")
|
|
|
|
# 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
|
|
}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |