hospital-management/appointments_data.py
Marwan Alwali 263292f6be update
2025-11-04 00:50:06 +03:00

1528 lines
65 KiB
Python

import os
import django
# Set up Django environment
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hospital_management.settings')
django.setup()
# Django setup will be handled by manage.py when running this script
# Import Django-related modules after Django is set up
import random
from datetime import datetime, date, time, timedelta
from django.utils import timezone as django_timezone
from django.contrib.auth import get_user_model
from appointments.models import (
AppointmentRequest, SlotAvailability, WaitingQueue, QueueEntry,
TelemedicineSession, AppointmentTemplate, WaitingList, WaitingListContactLog,
QueueConfiguration, QueueMetrics
)
from patients.models import PatientProfile
from core.models import Tenant
from hr.models import Employee, Department
import uuid
from accounts.models import User
# Saudi-specific healthcare data
SAUDI_SPECIALTIES = [
'FAMILY_MEDICINE', 'INTERNAL_MEDICINE', 'PEDIATRICS', 'CARDIOLOGY',
'DERMATOLOGY', 'ENDOCRINOLOGY', 'GASTROENTEROLOGY', 'NEUROLOGY',
'ONCOLOGY', 'ORTHOPEDICS', 'PSYCHIATRY', 'RADIOLOGY', 'SURGERY',
'UROLOGY', 'GYNECOLOGY', 'OPHTHALMOLOGY', 'ENT', 'EMERGENCY'
]
APPOINTMENT_TYPES = [
'CONSULTATION', 'FOLLOW_UP', 'PROCEDURE', 'SURGERY', 'DIAGNOSTIC',
'THERAPY', 'VACCINATION', 'SCREENING', 'EMERGENCY', 'TELEMEDICINE'
]
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'
]
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'
]
TELEMEDICINE_PLATFORMS = ['ZOOM', 'TEAMS', 'WEBEX', 'DOXY', 'CUSTOM']
def create_mock_providers(tenants):
"""Create mock healthcare providers for demonstration"""
providers = []
mock_providers_data = [
{'first_name': 'Ahmad', 'last_name': 'Al-Rashid', 'role': 'PHYSICIAN', 'specialty': 'CARDIOLOGY'},
{'first_name': 'Fatima', 'last_name': 'Al-Ghamdi', 'role': 'PHYSICIAN', 'specialty': 'PEDIATRICS'},
{'first_name': 'Mohammed', 'last_name': 'Al-Otaibi', 'role': 'PHYSICIAN', 'specialty': 'FAMILY_MEDICINE'},
{'first_name': 'Sarah', 'last_name': 'Al-Harbi', 'role': 'PHYSICIAN', 'specialty': 'INTERNAL_MEDICINE'},
{'first_name': 'Khalid', 'last_name': 'Al-Mutairi', 'role': 'SURGEON', 'specialty': 'SURGERY'},
{'first_name': 'Nora', 'last_name': 'Al-Zahrani', 'role': 'PHYSICIAN', 'specialty': 'RADIOLOGY'},
{'first_name': 'Omar', 'last_name': 'Al-Dawsari', 'role': 'PHYSICIAN', 'specialty': 'EMERGENCY'},
{'first_name': 'Layla', 'last_name': 'Al-Subai', 'role': 'PHYSICIAN', 'specialty': 'GYNECOLOGY'},
]
for tenant in tenants:
for provider_data in mock_providers_data:
try:
# Check if user already exists
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'],
role=provider_data['role'],
tenant=tenant,
is_active=True,
password='temp_password_123' # Should be changed in production
)
providers.append(user)
print(f"Created mock provider: {user.get_full_name()}")
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 get_providers_for_tenant(tenants):
"""Get healthcare providers using multiple fallback strategies"""
providers = []
# First try: Look for users with clinical employee profiles
for tenant in tenants:
tenant_providers = User.objects.filter(
employee_profile__tenant=tenant,
employee_profile__department__department_type='CLINICAL',
employee_profile__employment_status='ACTIVE'
).select_related('employee_profile__department')
providers.extend(list(tenant_providers))
# Second try: If no clinical providers found, look for any employees with medical roles
if not providers:
for tenant in tenants:
tenant_providers = User.objects.filter(
tenant=tenant,
employee_profile__role__in=['PHYSICIAN', 'NURSE_PRACTITIONER', 'PHYSICIAN_ASSISTANT', 'RADIOLOGIST', 'SURGEON']
).select_related('employee_profile')
providers.extend(list(tenant_providers))
# Third try: If still no providers, look for any active users with medical-sounding roles
if not providers:
for tenant in tenants:
tenant_providers = User.objects.filter(
tenant=tenant,
is_active=True,
employee_profile__role__icontains='PHYSICIAN'
)
providers.extend(list(tenant_providers))
# Fourth try: Create some mock providers if none exist
if not providers:
print("No existing providers found. Creating mock providers...")
providers = create_mock_providers(tenants)
return providers
def create_appointment_templates(tenants):
"""Create appointment templates for common appointment types"""
templates = []
template_configs = [
{
'name': 'Family Medicine Consultation',
'appointment_type': 'CONSULTATION',
'specialty': 'FAMILY_MEDICINE',
'duration_minutes': 30,
'advance_booking_days': 30,
'minimum_notice_hours': 24,
'pre_instructions': 'Please bring your medical history and current medications list.',
},
{
'name': 'Cardiology Follow-up',
'appointment_type': 'FOLLOW_UP',
'specialty': 'CARDIOLOGY',
'duration_minutes': 45,
'advance_booking_days': 60,
'minimum_notice_hours': 48,
'authorization_required': True,
'pre_instructions': 'Please bring your latest ECG and cardiac test results.',
},
{
'name': 'Pediatric Wellness Visit',
'appointment_type': 'SCREENING',
'specialty': 'PEDIATRICS',
'duration_minutes': 45,
'advance_booking_days': 90,
'minimum_notice_hours': 24,
'pre_instructions': 'Please bring your child\'s vaccination record.',
},
{
'name': 'Surgical Consultation',
'appointment_type': 'CONSULTATION',
'specialty': 'SURGERY',
'duration_minutes': 60,
'advance_booking_days': 45,
'minimum_notice_hours': 72,
'authorization_required': True,
'pre_instructions': 'Please bring all relevant imaging studies and lab results.',
},
{
'name': 'Telemedicine Consultation',
'appointment_type': 'TELEMEDICINE',
'specialty': 'FAMILY_MEDICINE',
'duration_minutes': 20,
'advance_booking_days': 14,
'minimum_notice_hours': 4,
'pre_instructions': 'Please ensure you have a stable internet connection and privacy.',
},
{
'name': 'Emergency Consultation',
'appointment_type': 'EMERGENCY',
'specialty': 'EMERGENCY',
'duration_minutes': 60,
'advance_booking_days': 1,
'minimum_notice_hours': 1,
'pre_instructions': 'Please come immediately if experiencing emergency symptoms.',
},
{
'name': 'Diagnostic Imaging',
'appointment_type': 'DIAGNOSTIC',
'specialty': 'RADIOLOGY',
'duration_minutes': 30,
'advance_booking_days': 21,
'minimum_notice_hours': 24,
'pre_instructions': 'Please follow preparation instructions provided.',
},
{
'name': 'Physical Therapy Session',
'appointment_type': 'THERAPY',
'specialty': 'ORTHOPEDICS',
'duration_minutes': 60,
'advance_booking_days': 30,
'minimum_notice_hours': 24,
'pre_instructions': 'Please wear comfortable clothing suitable for exercise.',
},
]
for tenant in tenants:
for config in template_configs:
try:
template = AppointmentTemplate.objects.create(
tenant=tenant,
name=config['name'],
description=f"Standard {config['name'].lower()} appointment template",
appointment_type=config['appointment_type'],
specialty=config['specialty'],
duration_minutes=config['duration_minutes'],
advance_booking_days=config['advance_booking_days'],
minimum_notice_hours=config['minimum_notice_hours'],
insurance_verification_required=config.get('insurance_verification_required', False),
authorization_required=config.get('authorization_required', False),
pre_appointment_instructions=config['pre_instructions'],
post_appointment_instructions="Please follow up as instructed by your healthcare provider.",
required_forms=[
"Patient Registration Form",
"Medical History Form",
"Insurance Information Form"
],
is_active=True,
created_at=django_timezone.now() - timedelta(days=random.randint(30, 180)),
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']} for {tenant.name}: {e}")
continue
print(f"Created {len(templates)} appointment templates")
return templates
def create_waiting_queues(tenants):
"""Create waiting queues for different departments and specialties"""
queues = []
queue_configs = [
{
'name': 'Emergency Department Queue',
'queue_type': 'EMERGENCY',
'specialty': 'EMERGENCY',
'location': 'Emergency Wing',
'max_queue_size': 100,
'average_service_time': 45,
'priority_weights': {'EMERGENCY': 10, 'STAT': 8, 'URGENT': 5, 'ROUTINE': 1}
},
{
'name': 'Family Medicine Queue',
'queue_type': 'SPECIALTY',
'specialty': 'FAMILY_MEDICINE',
'location': 'Outpatient Clinic - Wing A',
'max_queue_size': 50,
'average_service_time': 30,
'priority_weights': {'STAT': 8, 'URGENT': 5, 'ROUTINE': 1}
},
{
'name': 'Cardiology Queue',
'queue_type': 'SPECIALTY',
'specialty': 'CARDIOLOGY',
'location': 'Main Building - Floor 2',
'max_queue_size': 30,
'average_service_time': 45,
'priority_weights': {'STAT': 8, 'URGENT': 5, 'ROUTINE': 1}
},
{
'name': 'Pediatrics Queue',
'queue_type': 'SPECIALTY',
'specialty': 'PEDIATRICS',
'location': 'Pediatric Wing',
'max_queue_size': 40,
'average_service_time': 35,
'priority_weights': {'STAT': 8, 'URGENT': 5, 'ROUTINE': 1}
},
{
'name': 'Surgery Consultation Queue',
'queue_type': 'SPECIALTY',
'specialty': 'SURGERY',
'location': 'Main Building - Floor 3',
'max_queue_size': 25,
'average_service_time': 60,
'priority_weights': {'STAT': 8, 'URGENT': 5, 'ROUTINE': 1}
},
{
'name': 'Radiology Queue',
'queue_type': 'PROCEDURE',
'specialty': 'RADIOLOGY',
'location': 'Radiology Department',
'max_queue_size': 35,
'average_service_time': 25,
'priority_weights': {'STAT': 8, 'URGENT': 5, 'ROUTINE': 1}
}
]
# Operating hours for queues (Saudi working hours)
operating_hours = {
'sunday': {'start': '08:00', 'end': '20:00'},
'monday': {'start': '08:00', 'end': '20:00'},
'tuesday': {'start': '08:00', 'end': '20:00'},
'wednesday': {'start': '08:00', 'end': '20:00'},
'thursday': {'start': '08:00', 'end': '20:00'},
'friday': {'start': '14:00', 'end': '20:00'}, # After Friday prayers
'saturday': {'start': '08:00', 'end': '16:00'} # Half day
}
for tenant in tenants:
for config in queue_configs:
try:
queue = WaitingQueue.objects.create(
tenant=tenant,
name=config['name'],
description=f"Patient waiting queue for {config['specialty'].replace('_', ' ').title()}",
queue_type=config['queue_type'],
specialty=config['specialty'],
location=config['location'],
max_queue_size=config['max_queue_size'],
average_service_time_minutes=config['average_service_time'],
priority_weights=config['priority_weights'],
is_active=True,
is_accepting_patients=True,
operating_hours=operating_hours,
created_at=django_timezone.now() - timedelta(days=random.randint(30, 180)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 30))
)
queues.append(queue)
except Exception as e:
print(f"Error creating queue {config['name']} for {tenant.name}: {e}")
continue
print(f"Created {len(queues)} waiting queues")
return queues
def create_slot_availability(tenants, days_ahead=30):
"""Create provider availability slots"""
slots = []
# Get healthcare providers using the improved strategy
providers = get_providers_for_tenant(tenants)
if not providers:
print("No clinical providers found. Skipping slot creation.")
return slots
print(f"Found {len(providers)} providers for slot creation")
# Time slots configuration
time_slots = {
'MORNING': [
(time(8, 0), time(8, 30)),
(time(8, 30), time(9, 0)),
(time(9, 0), time(9, 30)),
(time(9, 30), time(10, 0)),
(time(10, 0), time(10, 30)),
(time(10, 30), time(11, 0)),
(time(11, 0), time(11, 30)),
(time(11, 30), time(12, 0)),
],
'AFTERNOON': [
(time(14, 0), time(14, 30)),
(time(14, 30), time(15, 0)),
(time(15, 0), time(15, 30)),
(time(15, 30), time(16, 0)),
(time(16, 0), time(16, 30)),
(time(16, 30), time(17, 0)),
(time(17, 0), time(17, 30)),
(time(17, 30), time(18, 0)),
],
'EVENING': [
(time(19, 0), time(19, 30)),
(time(19, 30), time(20, 0)),
]
}
start_date = django_timezone.now().date()
for provider in providers:
tenant = provider.tenant
# Get department if available, otherwise use a default
department = None
if hasattr(provider, 'employee_profile') and provider.employee_profile:
department = provider.employee_profile.department
# Determine specialty based on role or department
specialty_mapping = {
'CARDIOLOGY': 'CARDIOLOGY',
'PEDIATRICS': 'PEDIATRICS',
'SURGERY': 'SURGERY',
'RADIOLOGY': 'RADIOLOGY',
'EMERGENCY': 'EMERGENCY',
'OBSTETRICS': 'GYNECOLOGY',
'ORTHOPEDICS': 'ORTHOPEDICS'
}
if department and hasattr(department, 'department_code'):
specialty = specialty_mapping.get(department.department_code, 'FAMILY_MEDICINE')
elif hasattr(provider, 'role') and provider.role:
if 'SURGEON' in provider.role:
specialty = 'SURGERY'
elif 'RADIOLOGY' in provider.role:
specialty = 'RADIOLOGY'
else:
specialty = 'FAMILY_MEDICINE'
else:
specialty = 'FAMILY_MEDICINE'
# Create slots for the next days_ahead days
for day_offset in range(days_ahead):
slot_date = start_date + timedelta(days=day_offset)
weekday = slot_date.weekday()
# Skip Fridays for most providers (except emergency)
if weekday == 4 and specialty != 'EMERGENCY':
continue
# Determine working periods for the day
working_periods = ['MORNING']
if weekday != 5: # Not Saturday (half day)
working_periods.append('AFTERNOON')
if specialty == 'EMERGENCY':
working_periods.append('EVENING')
# Random availability (not all providers work every day)
if random.random() < 0.8: # 80% chance provider is available
for period in working_periods:
# Select some slots from the period
available_slots = random.sample(
time_slots[period],
random.randint(2, min(6, len(time_slots[period])))
)
for start_time, end_time in available_slots:
duration = int((datetime.combine(date.today(), end_time) -
datetime.combine(date.today(), start_time)).total_seconds() / 60)
# Determine slot configuration
max_appointments = 1
if specialty in ['FAMILY_MEDICINE', 'PEDIATRICS']:
max_appointments = random.choice([1, 2])
availability_type = 'REGULAR'
if period == 'EVENING':
availability_type = 'EXTENDED'
if specialty == 'EMERGENCY':
availability_type = 'EMERGENCY'
supports_telemedicine = random.choice([True, False]) if specialty != 'SURGERY' else False
# Get location from department or use default
location = 'Main Building - Floor 1'
if department and hasattr(department, 'location') and department.location:
location = department.location
else:
location = random.choice(SAUDI_LOCATIONS)
try:
slot = SlotAvailability.objects.create(
tenant=tenant,
provider=provider,
date=slot_date,
start_time=start_time,
end_time=end_time,
duration_minutes=duration,
availability_type=availability_type,
max_appointments=max_appointments,
booked_appointments=0,
location=location,
room_number=f"Room {random.randint(101, 350)}",
specialty=specialty,
appointment_types=random.sample(APPOINTMENT_TYPES, random.randint(3, 6)),
supports_telemedicine=supports_telemedicine,
telemedicine_only=False,
is_active=True,
is_blocked=False,
created_at=django_timezone.now() - timedelta(days=random.randint(1, 30)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 7))
)
slots.append(slot)
except Exception as e:
print(f"Error creating slot for {provider.get_full_name()}: {e}")
continue
print(f"Created {len(slots)} availability slots")
return slots
def create_appointment_requests(tenants, slots, days_back=14, appointments_per_day=50):
"""Create appointment requests with various statuses"""
appointments = []
# Get patients
patients = list(PatientProfile.objects.filter(tenant__in=tenants))
if not patients:
print("No patients found. Skipping appointment creation.")
return appointments
# Get providers using the same improved logic
providers = get_providers_for_tenant(tenants)
if not providers:
print("No providers found. Skipping appointment creation.")
return appointments
print(f"Found {len(providers)} providers for appointment creation")
start_date = django_timezone.now().date() - timedelta(days=days_back)
for day_offset in range(days_back + 30): # Past and future appointments
appointment_date = start_date + timedelta(days=day_offset)
# Create appointments for this day
daily_appointments = random.randint(int(appointments_per_day * 0.7), int(appointments_per_day * 1.3))
for _ in range(daily_appointments):
patient = random.choice(patients)
# Get providers for this patient's tenant, with fallback
tenant_providers = [p for p in providers if p.tenant == patient.tenant]
if not tenant_providers:
# If no providers for this tenant, skip this appointment
print(f"Warning: No providers found for tenant {patient.tenant.name}, skipping appointment for {patient.get_full_name()}")
continue
provider = random.choice(tenant_providers)
# Select appointment type and specialty
appointment_type = random.choices(
APPOINTMENT_TYPES,
weights=[20, 25, 10, 5, 15, 8, 5, 10, 3, 4] # Weighted by frequency
)[0]
specialty = random.choice(SAUDI_SPECIALTIES)
# Set priority based on appointment type
if appointment_type == 'EMERGENCY':
priority = 'EMERGENCY'
urgency_score = random.randint(8, 10)
elif appointment_type in ['SURGERY', 'PROCEDURE']:
priority = random.choices(['URGENT', 'STAT'], weights=[70, 30])[0]
urgency_score = random.randint(5, 8)
else:
priority = random.choices(['ROUTINE', 'URGENT'], weights=[85, 15])[0]
urgency_score = random.randint(1, 5)
# Determine status based on appointment date
if appointment_date < django_timezone.now().date():
# Past appointments
status = random.choices(
['COMPLETED', 'NO_SHOW', 'CANCELLED'],
weights=[75, 15, 10]
)[0]
elif appointment_date == django_timezone.now().date():
# Today's appointments
status = random.choices(
['CONFIRMED', 'CHECKED_IN', 'IN_PROGRESS', 'COMPLETED'],
weights=[40, 30, 20, 10]
)[0]
else:
# Future appointments
status = random.choices(
['SCHEDULED', 'CONFIRMED'],
weights=[40, 60]
)[0]
# Generate appointment time
preferred_time = time(
random.randint(8, 17),
random.choice([0, 15, 30, 45])
)
duration = 30
if appointment_type in ['SURGERY', 'PROCEDURE']:
duration = random.randint(60, 180)
elif appointment_type == 'CONSULTATION':
duration = random.randint(30, 60)
# Schedule datetime
scheduled_datetime = None
scheduled_end_datetime = None
if status != 'PENDING':
scheduled_datetime = django_timezone.make_aware(
datetime.combine(appointment_date, preferred_time)
)
scheduled_end_datetime = scheduled_datetime + timedelta(minutes=duration)
# Telemedicine configuration
is_telemedicine = appointment_type == 'TELEMEDICINE' or (
appointment_type in ['CONSULTATION', 'FOLLOW_UP'] and random.random() < 0.2
)
telemedicine_platform = None
meeting_url = None
meeting_id = None
if is_telemedicine:
telemedicine_platform = random.choice(TELEMEDICINE_PLATFORMS)
meeting_url = f"https://{telemedicine_platform.lower()}.com/j/{random.randint(100000000, 999999999)}"
meeting_id = str(random.randint(100000000, 999999999))
# Check-in and completion times for completed appointments
checked_in_at = None
completed_at = None
actual_duration = None
if status in ['CHECKED_IN', 'IN_PROGRESS', 'COMPLETED'] and scheduled_datetime:
# Random check-in time (usually 10-30 minutes before appointment)
checkin_offset = random.randint(-30, 5) # Can be late too
checked_in_at = scheduled_datetime + timedelta(minutes=checkin_offset)
if status == 'COMPLETED':
# Random completion time
actual_duration = random.randint(int(duration * 0.8), int(duration * 1.2))
completed_at = scheduled_datetime + timedelta(minutes=actual_duration)
# Cancellation information
cancelled_at = None
cancellation_reason = None
if status == 'CANCELLED':
cancelled_at = scheduled_datetime - timedelta(
hours=random.randint(1, 72)) if scheduled_datetime else django_timezone.now()
cancellation_reason = random.choice([
'Patient requested cancellation',
'Provider unavailable',
'Emergency situation',
'Insurance issues',
'Patient illness'
])
try:
appointment = AppointmentRequest.objects.create(
tenant=patient.tenant,
patient=patient,
provider=provider,
appointment_type=appointment_type,
specialty=specialty,
preferred_date=appointment_date,
preferred_time=preferred_time,
duration_minutes=duration,
flexible_scheduling=random.choice([True, False]),
earliest_acceptable_date=appointment_date - timedelta(days=random.randint(0, 3)),
latest_acceptable_date=appointment_date + timedelta(days=random.randint(0, 7)),
acceptable_times=['morning', 'afternoon'] if random.choice([True, False]) else [],
priority=priority,
urgency_score=urgency_score,
chief_complaint=random.choice(SAUDI_CHIEF_COMPLAINTS),
clinical_notes=f"Patient presenting with {random.choice(SAUDI_CHIEF_COMPLAINTS).lower()}",
referring_provider=f"Dr. {random.choice(['Ahmed', 'Fatima', 'Mohammed', 'Sara'])} Al-{random.choice(['Rashid', 'Ghamdi', 'Otaibi'])}" if random.choice(
[True, False]) else None,
insurance_verified=random.choice([True, False]),
authorization_required=appointment_type in ['SURGERY', 'PROCEDURE'],
authorization_number=f"AUTH-{random.randint(100000, 999999)}" if appointment_type in ['SURGERY',
'PROCEDURE'] else None,
status=status,
scheduled_datetime=scheduled_datetime,
scheduled_end_datetime=scheduled_end_datetime,
location=random.choice(SAUDI_LOCATIONS),
room_number=f"Room {random.randint(101, 350)}",
is_telemedicine=is_telemedicine,
telemedicine_platform=telemedicine_platform,
meeting_url=meeting_url,
meeting_id=meeting_id,
meeting_password=f"pwd{random.randint(1000, 9999)}" if is_telemedicine else None,
checked_in_at=checked_in_at,
completed_at=completed_at,
actual_duration_minutes=actual_duration,
cancelled_at=cancelled_at,
cancellation_reason=cancellation_reason,
reminder_preferences={
'sms': True,
'email': random.choice([True, False]),
'phone': False
},
special_requirements=random.choice([
None, None, None, # Most appointments don't have special requirements
'Wheelchair accessible',
'Interpreter needed',
'Elderly patient assistance',
'Child-friendly environment'
]),
interpreter_needed=random.choice([True, False]) if random.random() < 0.1 else False,
interpreter_language=random.choice(
['Urdu', 'Hindi', 'Bengali', 'English']) if random.random() < 0.1 else None,
created_at=django_timezone.now() - timedelta(days=random.randint(1, 30)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 7))
)
appointments.append(appointment)
except Exception as e:
print(f"Error creating appointment for {patient.get_full_name()}: {e}")
continue
print(f"Created {len(appointments)} appointment requests")
return appointments
def create_telemedicine_sessions(appointments):
"""Create telemedicine sessions for telemedicine appointments"""
sessions = []
telemedicine_appointments = [apt for apt in appointments if apt.is_telemedicine and apt.scheduled_datetime]
for appointment in telemedicine_appointments:
# Determine session status
if appointment.status == 'COMPLETED':
session_status = 'COMPLETED'
elif appointment.status in ['IN_PROGRESS']:
session_status = 'IN_PROGRESS'
elif appointment.status in ['CONFIRMED', 'CHECKED_IN']:
session_status = 'READY'
elif appointment.status == 'CANCELLED':
session_status = 'CANCELLED'
else:
session_status = 'SCHEDULED'
# Generate session timing
actual_start = None
actual_end = None
provider_joined_at = None
patient_joined_at = None
if session_status in ['COMPLETED', 'IN_PROGRESS']:
# Provider usually joins first
provider_joined_at = appointment.scheduled_datetime + timedelta(minutes=random.randint(-5, 2))
patient_joined_at = provider_joined_at + timedelta(minutes=random.randint(1, 8))
actual_start = max(provider_joined_at, patient_joined_at)
if session_status == 'COMPLETED':
duration = appointment.actual_duration_minutes or appointment.duration_minutes
actual_end = actual_start + timedelta(minutes=duration)
# Connection quality and technical issues
connection_quality = random.choices(
['EXCELLENT', 'GOOD', 'FAIR', 'POOR'],
weights=[40, 35, 20, 5]
)[0]
technical_issues = None
if connection_quality in ['FAIR', 'POOR']:
technical_issues = random.choice([
'Intermittent audio issues',
'Video quality degradation',
'Connection drops',
'Echo and feedback problems',
'Bandwidth limitations'
])
# Recording information
recording_enabled = random.choice([True, False]) if random.random() < 0.3 else False
recording_consent = recording_enabled
recording_url = f"https://recordings.{appointment.telemedicine_platform.lower()}.com/{uuid.uuid4()}" if recording_enabled and session_status == 'COMPLETED' else None
recording_duration = appointment.actual_duration_minutes if recording_enabled and session_status == 'COMPLETED' else None
try:
session = TelemedicineSession.objects.create(
appointment=appointment,
platform=appointment.telemedicine_platform,
meeting_url=appointment.meeting_url,
meeting_id=appointment.meeting_id,
meeting_password=appointment.meeting_password,
waiting_room_enabled=True,
recording_enabled=recording_enabled,
recording_consent=recording_consent,
encryption_enabled=True,
password_required=appointment.meeting_password is not None,
status=session_status,
scheduled_start=appointment.scheduled_datetime,
scheduled_end=appointment.scheduled_end_datetime,
actual_start=actual_start,
actual_end=actual_end,
provider_joined_at=provider_joined_at,
patient_joined_at=patient_joined_at,
connection_quality=connection_quality,
technical_issues=technical_issues,
recording_url=recording_url,
recording_duration_minutes=recording_duration,
session_notes=f"Telemedicine session for {appointment.chief_complaint}" if session_status == 'COMPLETED' else None,
created_at=django_timezone.now() - timedelta(days=random.randint(0, 7)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 3))
)
sessions.append(session)
except Exception as e:
print(f"Error creating telemedicine session for appointment {appointment.request_id}: {e}")
continue
print(f"Created {len(sessions)} telemedicine sessions")
return sessions
def create_waiting_list_entries(tenants, num_entries_per_tenant=100):
"""Create waiting list entries with realistic clinical scenarios"""
waiting_list_entries = []
# Get patients and providers
patients = list(PatientProfile.objects.filter(tenant__in=tenants))
providers = get_providers_for_tenant(tenants)
departments = list(Department.objects.filter(tenant__in=tenants))
if not patients:
print("No patients found. Skipping waiting list creation.")
return waiting_list_entries
if not departments:
print("No departments found. Skipping waiting list creation.")
return waiting_list_entries
# Clinical scenarios for waiting list entries
clinical_scenarios = [
{
'specialty': 'CARDIOLOGY',
'appointment_type': 'CONSULTATION',
'clinical_indication': 'Chest pain evaluation and cardiac assessment',
'diagnosis_codes': ['R07.9', 'I25.10'],
'priority': 'URGENT',
'urgency_score': random.randint(6, 8),
'referral_urgency': 'URGENT'
},
{
'specialty': 'ORTHOPEDICS',
'appointment_type': 'FOLLOW_UP',
'clinical_indication': 'Post-operative knee replacement follow-up',
'diagnosis_codes': ['Z47.1', 'M17.9'],
'priority': 'ROUTINE',
'urgency_score': random.randint(2, 4),
'referral_urgency': 'ROUTINE'
},
{
'specialty': 'PEDIATRICS',
'appointment_type': 'SCREENING',
'clinical_indication': 'Well-child visit and developmental assessment',
'diagnosis_codes': ['Z00.129'],
'priority': 'ROUTINE',
'urgency_score': random.randint(1, 3),
'referral_urgency': 'ROUTINE'
},
{
'specialty': 'DERMATOLOGY',
'appointment_type': 'PROCEDURE',
'clinical_indication': 'Suspicious skin lesion requiring biopsy',
'diagnosis_codes': ['D48.5', 'L98.9'],
'priority': 'URGENT',
'urgency_score': random.randint(5, 7),
'referral_urgency': 'URGENT'
},
{
'specialty': 'GYNECOLOGY',
'appointment_type': 'CONSULTATION',
'clinical_indication': 'Abnormal uterine bleeding evaluation',
'diagnosis_codes': ['N93.9', 'N85.00'],
'priority': 'URGENT',
'urgency_score': random.randint(6, 8),
'referral_urgency': 'URGENT'
},
{
'specialty': 'NEUROLOGY',
'appointment_type': 'DIAGNOSTIC',
'clinical_indication': 'Headache evaluation and neurological assessment',
'diagnosis_codes': ['R51', 'G44.209'],
'priority': 'STAT',
'urgency_score': random.randint(7, 9),
'referral_urgency': 'STAT'
},
{
'specialty': 'ENDOCRINOLOGY',
'appointment_type': 'FOLLOW_UP',
'clinical_indication': 'Diabetes management and glycemic control',
'diagnosis_codes': ['E11.9', 'Z79.4'],
'priority': 'ROUTINE',
'urgency_score': random.randint(2, 5),
'referral_urgency': 'ROUTINE'
},
{
'specialty': 'SURGERY',
'appointment_type': 'CONSULTATION',
'clinical_indication': 'Gallbladder disease requiring surgical evaluation',
'diagnosis_codes': ['K80.20', 'R10.11'],
'priority': 'URGENT',
'urgency_score': random.randint(5, 8),
'referral_urgency': 'URGENT'
},
{
'specialty': 'PSYCHIATRY',
'appointment_type': 'THERAPY',
'clinical_indication': 'Depression and anxiety management',
'diagnosis_codes': ['F32.9', 'F41.9'],
'priority': 'ROUTINE',
'urgency_score': random.randint(3, 6),
'referral_urgency': 'ROUTINE'
},
{
'specialty': 'UROLOGY',
'appointment_type': 'DIAGNOSTIC',
'clinical_indication': 'Elevated PSA and prostate evaluation',
'diagnosis_codes': ['R97.20', 'N40.0'],
'priority': 'URGENT',
'urgency_score': random.randint(5, 7),
'referral_urgency': 'URGENT'
}
]
for tenant in tenants:
tenant_patients = [p for p in patients if p.tenant == tenant]
tenant_providers = [p for p in providers if p.tenant == tenant]
tenant_departments = [d for d in departments if d.tenant == tenant]
if not tenant_patients or not tenant_departments:
continue
for _ in range(num_entries_per_tenant):
patient = random.choice(tenant_patients)
scenario = random.choice(clinical_scenarios)
# Select appropriate department
suitable_departments = [
d for d in tenant_departments
if hasattr(d, 'department_type') and d.department_type == 'CLINICAL'
]
department = random.choice(suitable_departments) if suitable_departments else random.choice(tenant_departments)
# Provider assignment (optional, 70% have preferred provider)
provider = None
if random.random() < 0.7 and tenant_providers:
provider = random.choice(tenant_providers)
# Status distribution (weighted towards active entries)
status_weights = {
'ACTIVE': 60,
'CONTACTED': 15,
'OFFERED': 10,
'SCHEDULED': 8,
'CANCELLED': 4,
'EXPIRED': 2,
'TRANSFERRED': 1
}
status = random.choices(
list(status_weights.keys()),
weights=list(status_weights.values())
)[0]
# Contact information
contact_method = random.choice(['PHONE', 'EMAIL', 'SMS'])
contact_phone = f"+966{random.randint(500000000, 599999999)}" if contact_method == 'PHONE' else None
contact_email = f"{patient.email}" if contact_method == 'EMAIL' else None
# Timing and scheduling preferences
created_at = django_timezone.now() - timedelta(days=random.randint(1, 90))
preferred_date = created_at.date() + timedelta(days=random.randint(7, 60)) if random.random() < 0.8 else None
preferred_time = time(random.randint(8, 16), random.choice([0, 30])) if preferred_date else None
# Contact history
contact_attempts = 0
last_contacted = None
appointments_offered = 0
appointments_declined = 0
last_offer_date = None
if status in ['CONTACTED', 'OFFERED', 'SCHEDULED']:
contact_attempts = random.randint(1, 3)
last_contacted = created_at + timedelta(days=random.randint(1, 30))
if status in ['OFFERED', 'SCHEDULED']:
appointments_offered = random.randint(1, 2)
last_offer_date = last_contacted + timedelta(days=random.randint(1, 7))
if random.random() < 0.3: # 30% decline offers
appointments_declined = random.randint(1, appointments_offered)
# Authorization requirements (for procedures/surgery)
authorization_required = scenario['appointment_type'] in ['PROCEDURE', 'SURGERY']
authorization_status = 'NOT_REQUIRED'
authorization_number = None
if authorization_required:
authorization_status = random.choice(['PENDING', 'APPROVED', 'DENIED'])
if authorization_status == 'APPROVED':
authorization_number = f"AUTH-{random.randint(100000, 999999)}"
# Special requirements
requires_interpreter = random.random() < 0.05 # 5% need interpreter
interpreter_language = random.choice(['Arabic', 'English', 'Urdu', 'Hindi']) if requires_interpreter else None
accessibility_requirements = None
if random.random() < 0.1: # 10% have accessibility needs
accessibility_requirements = random.choice([
'Wheelchair accessible room required',
'Sign language interpreter needed',
'Hearing assistance devices needed',
'Visual assistance required'
])
transportation_needed = random.random() < 0.15 # 15% need transportation
# Referral information
referring_provider = f"Dr. {random.choice(['Ahmed', 'Fatima', 'Mohammed', 'Sara'])} Al-{random.choice(['Rashid', 'Ghamdi', 'Otaibi', 'Harbi'])}"
referral_date = created_at - timedelta(days=random.randint(1, 14))
# Notes
notes = random.choice([
None,
f"Patient prefers {scenario['specialty'].replace('_', ' ').title().lower()} specialist",
"Patient has multiple comorbidities",
"Requires extended appointment time",
"Patient has transportation limitations",
"Family member will accompany patient"
])
try:
waiting_entry = WaitingList.objects.create(
tenant=tenant,
patient=patient,
provider=provider,
department=department,
appointment_type=scenario['appointment_type'],
specialty=scenario['specialty'],
priority=scenario['priority'],
urgency_score=scenario['urgency_score'],
clinical_indication=scenario['clinical_indication'],
diagnosis_codes=scenario['diagnosis_codes'],
preferred_date=preferred_date,
preferred_time=preferred_time,
flexible_scheduling=random.choice([True, False]),
earliest_acceptable_date=preferred_date - timedelta(days=random.randint(0, 7)) if preferred_date else None,
latest_acceptable_date=preferred_date + timedelta(days=random.randint(7, 21)) if preferred_date else None,
acceptable_days=[0, 1, 2, 3, 4] if random.random() < 0.8 else [], # Monday-Friday
acceptable_times=['morning', 'afternoon'] if random.random() < 0.7 else [],
contact_method=contact_method,
contact_phone=contact_phone,
contact_email=contact_email,
status=status,
position=None, # Will be calculated later
estimated_wait_time=scenario['urgency_score'] * 7, # Days based on urgency
last_contacted=last_contacted,
contact_attempts=contact_attempts,
max_contact_attempts=3,
appointments_offered=appointments_offered,
appointments_declined=appointments_declined,
last_offer_date=last_offer_date,
requires_interpreter=requires_interpreter,
interpreter_language=interpreter_language,
accessibility_requirements=accessibility_requirements,
transportation_needed=transportation_needed,
insurance_verified=random.choice([True, False]),
authorization_required=authorization_required,
authorization_status=authorization_status,
authorization_number=authorization_number,
referring_provider=referring_provider,
referral_date=referral_date,
referral_urgency=scenario['referral_urgency'],
created_at=created_at,
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 7)),
notes=notes
)
waiting_list_entries.append(waiting_entry)
except Exception as e:
print(f"Error creating waiting list entry for {patient.get_full_name()}: {e}")
continue
# Update positions for active entries
for entry in waiting_list_entries:
if entry.status == 'ACTIVE':
try:
entry.position = entry.calculate_position()
entry.estimated_wait_time = entry.estimate_wait_time()
entry.save(update_fields=['position', 'estimated_wait_time'])
except Exception as e:
print(f"Error updating position for waiting list entry {entry.waiting_list_id}: {e}")
print(f"Created {len(waiting_list_entries)} waiting list entries")
return waiting_list_entries
def create_waiting_list_contact_logs(waiting_list_entries):
"""Create contact logs for waiting list entries"""
contact_logs = []
# Only create logs for entries that have been contacted
contacted_entries = [entry for entry in waiting_list_entries if entry.contact_attempts > 0]
for entry in contacted_entries:
# Create logs for each contact attempt
for attempt_num in range(1, entry.contact_attempts + 1):
contact_date = entry.last_contacted - timedelta(days=random.randint(0, 7) * (entry.contact_attempts - attempt_num))
# Contact method based on entry preferences
contact_method = entry.contact_method
if contact_method == 'PHONE':
contact_method = 'PHONE'
elif contact_method == 'EMAIL':
contact_method = 'EMAIL'
else:
contact_method = 'SMS'
# Contact outcomes
possible_outcomes = ['SUCCESSFUL', 'NO_ANSWER', 'BUSY', 'VOICEMAIL', 'EMAIL_SENT', 'SMS_SENT']
if attempt_num == entry.contact_attempts:
# Last attempt more likely to be successful
outcome_weights = [40, 20, 10, 10, 10, 10]
else:
outcome_weights = [20, 30, 15, 15, 10, 10]
contact_outcome = random.choices(possible_outcomes, weights=outcome_weights)[0]
# Patient responses (only for successful contacts)
patient_response = None
appointment_offered = False
offered_date = None
offered_time = None
if contact_outcome == 'SUCCESSFUL':
response_options = ['ACCEPTED', 'DECLINED', 'REQUESTED_DIFFERENT', 'WILL_CALL_BACK', 'NO_LONGER_NEEDED']
response_weights = [30, 20, 25, 15, 10]
patient_response = random.choices(response_options, weights=response_weights)[0]
# Offer appointment if appropriate
if patient_response in ['ACCEPTED', 'REQUESTED_DIFFERENT'] and random.random() < 0.7:
appointment_offered = True
offered_date = contact_date.date() + timedelta(days=random.randint(7, 21))
offered_time = time(random.randint(8, 16), random.choice([0, 30]))
# Notes based on outcome
notes_templates = {
'SUCCESSFUL': [
f"Spoke with patient regarding {entry.appointment_type.lower()} appointment",
"Patient confirmed interest in scheduling",
"Discussed clinical urgency and wait times",
"Patient requested specific time preferences"
],
'NO_ANSWER': [
"No answer after multiple rings",
"Call went to voicemail",
"Patient did not pick up"
],
'BUSY': [
"Line was busy",
"Patient indicated they were busy",
"Call disconnected due to network issues"
],
'VOICEMAIL': [
"Left detailed voicemail about appointment availability",
"Voicemail left with callback instructions",
"Message left explaining wait list status"
],
'EMAIL_SENT': [
"Email sent with appointment options",
"Detailed email with scheduling information",
"Follow-up email sent as requested"
],
'SMS_SENT': [
"SMS sent with appointment reminder",
"Text message with scheduling options",
"SMS follow-up sent"
]
}
notes = random.choice(notes_templates.get(contact_outcome, ["Contact attempt made"]))
# Next contact date for unsuccessful attempts
next_contact_date = None
if contact_outcome != 'SUCCESSFUL' and attempt_num < entry.max_contact_attempts:
next_contact_date = contact_date.date() + timedelta(days=random.randint(3, 7))
try:
contact_log = WaitingListContactLog.objects.create(
waiting_list_entry=entry,
contact_date=contact_date,
contact_method=contact_method,
contact_outcome=contact_outcome,
appointment_offered=appointment_offered,
offered_date=offered_date,
offered_time=offered_time,
patient_response=patient_response,
notes=notes,
next_contact_date=next_contact_date,
contacted_by=random.choice([entry.provider, None]) if entry.provider else None
)
contact_logs.append(contact_log)
except Exception as e:
print(f"Error creating contact log for waiting list entry {entry.waiting_list_id}: {e}")
continue
print(f"Created {len(contact_logs)} waiting list contact logs")
return contact_logs
def create_queue_entries(queues, appointments):
"""Create queue entries for appointments"""
entries = []
# Get appointments that should have queue entries (checked in, in progress, or recently completed)
relevant_appointments = [
apt for apt in appointments
if apt.status in ['CHECKED_IN', 'IN_PROGRESS', 'COMPLETED']
and apt.scheduled_datetime
and apt.scheduled_datetime >= django_timezone.now() - timedelta(days=1)
]
for appointment in relevant_appointments:
# Find appropriate queue for this appointment
suitable_queues = [
q for q in queues
if q.tenant == appointment.tenant
and (
q.specialty == appointment.specialty or (
q.queue_type == 'EMERGENCY' and appointment.priority == 'EMERGENCY'))
]
if not suitable_queues:
continue
queue = random.choice(suitable_queues)
# Determine queue entry status
if appointment.status == 'CHECKED_IN':
entry_status = 'WAITING'
elif appointment.status == 'IN_PROGRESS':
entry_status = 'IN_SERVICE'
else: # COMPLETED
entry_status = 'COMPLETED'
# Calculate priority score
priority_weights = queue.priority_weights
base_score = priority_weights.get(appointment.priority, 1)
urgency_bonus = appointment.urgency_score * 0.1
priority_score = base_score + urgency_bonus
# Queue position (random for now)
queue_position = random.randint(1, min(10, queue.max_queue_size))
# Timing information
joined_at = appointment.checked_in_at or appointment.scheduled_datetime
estimated_service_time = joined_at + timedelta(minutes=queue.average_service_time_minutes)
called_at = None
served_at = None
if entry_status in ['IN_SERVICE', 'COMPLETED']:
# Patient was called before being served
call_delay = random.randint(5, 30) # 5-30 minutes wait
called_at = joined_at + timedelta(minutes=call_delay)
if entry_status == 'COMPLETED':
serve_delay = random.randint(1, 10) # 1-10 minutes after being called
served_at = called_at + timedelta(minutes=serve_delay)
# Notification information
notification_sent = entry_status != 'WAITING'
notification_method = random.choice(['SMS', 'APP', 'PAGER']) if notification_sent else None
try:
entry = QueueEntry.objects.create(
queue=queue,
patient=appointment.patient,
appointment=appointment,
queue_position=queue_position,
priority_score=priority_score,
joined_at=joined_at,
estimated_service_time=estimated_service_time,
called_at=called_at,
served_at=served_at,
status=entry_status,
assigned_provider=appointment.provider,
notification_sent=notification_sent,
notification_method=notification_method,
notes=f"Queue entry for {appointment.appointment_type.lower()} appointment",
updated_at=django_timezone.now() - timedelta(minutes=random.randint(0, 60))
)
entries.append(entry)
except Exception as e:
print(f"Error creating queue entry for appointment {appointment.request_id}: {e}")
continue
print(f"Created {len(entries)} queue entries")
return entries
def create_queue_configurations(queues):
"""Create queue configurations for Phase 11 advanced queue management"""
configurations = []
for queue in queues:
try:
config, created = QueueConfiguration.objects.get_or_create(
queue=queue,
defaults={
'use_dynamic_positioning': True,
'priority_weight': 0.5,
'wait_time_weight': 0.3,
'appointment_time_weight': 0.2,
'enable_overflow_queue': queue.queue_type == 'EMERGENCY',
'overflow_threshold': int(queue.max_queue_size * 0.9),
'use_historical_data': True,
'default_service_time_minutes': queue.average_service_time_minutes,
'historical_data_days': 30,
'enable_websocket_updates': True,
'update_interval_seconds': 30,
'load_factor_normal_threshold': 0.5,
'load_factor_moderate_threshold': 0.75,
'load_factor_high_threshold': 0.9,
'auto_reposition_enabled': True,
'reposition_interval_minutes': 15,
'notify_on_position_change': True,
'position_change_threshold': 3
}
)
configurations.append(config)
if created:
print(f"Created queue configuration for {queue.name}")
else:
print(f"Queue configuration already exists for {queue.name}")
except Exception as e:
print(f"Error creating queue configuration for {queue.name}: {e}")
continue
print(f"Total queue configurations: {len(configurations)}")
return configurations
def create_queue_metrics(queues, days_back=7):
"""Create queue metrics for Phase 11 analytics"""
metrics = []
start_date = django_timezone.now().date() - timedelta(days=days_back)
for queue in queues:
for day_offset in range(days_back):
metric_date = start_date + timedelta(days=day_offset)
# Create hourly metrics for operating hours (8 AM to 8 PM)
for hour in range(8, 21):
try:
# Simulate realistic metrics
total_entries = random.randint(5, 30)
completed = int(total_entries * random.uniform(0.7, 0.9))
no_shows = int(total_entries * random.uniform(0.05, 0.15))
left_queue = total_entries - completed - no_shows
avg_wait = random.randint(10, 45)
max_wait = int(avg_wait * random.uniform(1.5, 2.5))
min_wait = int(avg_wait * random.uniform(0.3, 0.6))
avg_service = queue.average_service_time_minutes + random.randint(-10, 10)
peak_size = random.randint(5, min(20, queue.max_queue_size))
avg_size = int(peak_size * random.uniform(0.5, 0.8))
utilization = (completed / total_entries * 100) if total_entries > 0 else 0
no_show_rate = (no_shows / total_entries * 100) if total_entries > 0 else 0
metric = QueueMetrics.objects.create(
queue=queue,
date=metric_date,
hour=hour,
total_entries=total_entries,
completed_entries=completed,
no_shows=no_shows,
left_queue=left_queue,
average_wait_time_minutes=avg_wait,
max_wait_time_minutes=max_wait,
min_wait_time_minutes=min_wait,
average_service_time_minutes=avg_service,
peak_queue_size=peak_size,
average_queue_size=avg_size,
utilization_rate=utilization,
no_show_rate=no_show_rate
)
metrics.append(metric)
except Exception as e:
print(f"Error creating queue metric for {queue.name} on {metric_date} at {hour}:00: {e}")
continue
print(f"Created {len(metrics)} queue metrics entries")
return metrics
def main():
"""Main function to generate all appointments data"""
print("Starting Saudi Healthcare Appointments 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 appointment templates
print("\n1. Creating Appointment Templates...")
templates = create_appointment_templates(tenants)
# Create waiting queues
print("\n2. Creating Waiting Queues...")
queues = create_waiting_queues(tenants)
# Create slot availability
print("\n3. Creating Provider Availability Slots...")
slots = create_slot_availability(tenants, 30)
# Create appointment requests
print("\n4. Creating Appointment Requests...")
appointments = create_appointment_requests(tenants, slots, 14, 50)
# Create telemedicine sessions
print("\n5. Creating Telemedicine Sessions...")
sessions = create_telemedicine_sessions(appointments)
# Create waiting list entries
print("\n6. Creating Waiting List Entries...")
waiting_list_entries = create_waiting_list_entries(tenants, 100)
# Create waiting list contact logs
print("\n7. Creating Waiting List Contact Logs...")
contact_logs = create_waiting_list_contact_logs(waiting_list_entries)
# Create queue entries
print("\n8. Creating Queue Entries...")
entries = create_queue_entries(queues, appointments)
# Create queue configurations (Phase 11)
print("\n9. Creating Queue Configurations (Phase 11)...")
queue_configs = create_queue_configurations(queues)
# Create queue metrics (Phase 11)
print("\n10. Creating Queue Metrics (Phase 11)...")
queue_metrics = create_queue_metrics(queues, days_back=7)
print(f"\n✅ Saudi Healthcare Appointments Data Generation Complete!")
print(f" Summary:")
print(f" - Appointment Templates: {len(templates)}")
print(f" - Waiting Queues: {len(queues)}")
print(f" - Availability Slots: {len(slots)}")
print(f" - Appointment Requests: {len(appointments)}")
print(f" - Telemedicine Sessions: {len(sessions)}")
print(f" - Waiting List Entries: {len(waiting_list_entries)}")
print(f" - Waiting List Contact Logs: {len(contact_logs)}")
print(f" - Queue Entries: {len(entries)}")
print(f" - Queue Configurations (Phase 11): {len(queue_configs)}")
print(f" - Queue Metrics (Phase 11): {len(queue_metrics)}")
# Only show distributions if appointments exist
if appointments:
# Appointment status distribution
status_counts = {}
for appointment in appointments:
status_counts[appointment.status] = status_counts.get(appointment.status, 0) + 1
print(f"\n📊 Appointment Status Distribution:")
for status, count in status_counts.items():
print(f" - {status.title()}: {count}")
# Appointment type distribution
type_counts = {}
for appointment in appointments:
type_counts[appointment.appointment_type] = type_counts.get(appointment.appointment_type, 0) + 1
print(f"\n📊 Appointment Type Distribution:")
for apt_type, count in sorted(type_counts.items(), key=lambda x: x[1], reverse=True)[:10]:
print(f" - {apt_type.replace('_', ' ').title()}: {count}")
# Telemedicine statistics
telemedicine_count = len([apt for apt in appointments if apt.is_telemedicine])
print(
f"\n💻 Telemedicine Appointments: {telemedicine_count} ({telemedicine_count / len(appointments) * 100:.1f}%)")
else:
print(f"\n⚠️ No appointments were created. Check provider and patient data.")
return {
'templates': templates,
'queues': queues,
'slots': slots,
'appointments': appointments,
'sessions': sessions,
'waiting_list_entries': waiting_list_entries,
'contact_logs': contact_logs,
'entries': entries,
'queue_configs': queue_configs,
'queue_metrics': queue_metrics
}
if __name__ == "__main__":
main()