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 ) 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) provider = random.choice([p for p in providers if p.tenant == patient.tenant]) # 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 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) 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)}") # 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 } if __name__ == "__main__": main()