hospital-management/appointments_data.py
Marwan Alwali 99858b4075 update
2025-08-20 16:06:10 +03:00

996 lines
41 KiB
Python

import os
import django
# Set up Django environment
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hospital_management.settings')
django.setup()
import random
from datetime import datetime, date, time, timedelta
from django.utils import timezone as django_timezone
from django.contrib.auth import get_user_model
from appointments.models import (
AppointmentRequest, SlotAvailability, WaitingQueue, QueueEntry,
TelemedicineSession, AppointmentTemplate
)
from patients.models import PatientProfile
from core.models import Tenant
from hr.models import Employee, Department
import uuid
User = get_user_model()
# 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(
employee_profile__tenant=tenant,
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,
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_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 queue entries
print("\n6. 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" - 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,
'entries': entries
}
if __name__ == "__main__":
main()