457 lines
18 KiB
Python
457 lines
18 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, timedelta, timezone
|
|
from django.contrib.auth.hashers import make_password
|
|
from django.utils import timezone as django_timezone
|
|
from accounts.models import User, TwoFactorDevice, SocialAccount, UserSession, PasswordHistory
|
|
from core.models import Tenant
|
|
import uuid
|
|
import secrets
|
|
|
|
# Saudi-specific data constants
|
|
SAUDI_FIRST_NAMES_MALE = [
|
|
'Mohammed', 'Abdullah', 'Ahmed', 'Omar', 'Ali', 'Hassan', 'Khalid', 'Faisal',
|
|
'Saad', 'Fahd', 'Bandar', 'Turki', 'Nasser', 'Saud', 'Abdulrahman',
|
|
'Abdulaziz', 'Salman', 'Waleed', 'Majid', 'Rayan', 'Yazeed', 'Mansour',
|
|
'Osama', 'Tariq', 'Adel', 'Nawaf', 'Sultan', 'Mishaal', 'Badr', 'Ziad'
|
|
]
|
|
|
|
SAUDI_FIRST_NAMES_FEMALE = [
|
|
'Fatima', 'Aisha', 'Maryam', 'Khadija', 'Sarah', 'Noura', 'Hala', 'Reem',
|
|
'Lina', 'Dana', 'Rana', 'Nada', 'Layla', 'Amira', 'Zahra', 'Yasmin',
|
|
'Dina', 'Noor', 'Rahma', 'Salma', 'Lama', 'Ghada', 'Rania', 'Maha',
|
|
'Wedad', 'Najla', 'Shahd', 'Jood', 'Rand', 'Malak'
|
|
]
|
|
|
|
SAUDI_FAMILY_NAMES = [
|
|
'Al-Rashid', 'Al-Harbi', 'Al-Qahtani', 'Al-Dosari', 'Al-Otaibi', 'Al-Mutairi',
|
|
'Al-Shammari', 'Al-Zahrani', 'Al-Ghamdi', 'Al-Maliki', 'Al-Subai', 'Al-Jubayr',
|
|
'Al-Faisal', 'Al-Saud', 'Al-Thani', 'Al-Maktoum', 'Al-Sabah', 'Al-Khalifa',
|
|
'Bin-Laden', 'Al-Rajhi', 'Al-Sudairy', 'Al-Shaalan', 'Al-Kabeer', 'Al-Ajmi',
|
|
'Al-Anzi', 'Al-Dawsari', 'Al-Shamrani', 'Al-Balawi', 'Al-Juhani', 'Al-Sulami'
|
|
]
|
|
|
|
SAUDI_MIDDLE_NAMES = [
|
|
'bin Ahmed', 'bin Mohammed', 'bin Abdullah', 'bin Omar', 'bin Ali', 'bin Hassan',
|
|
'bin Khalid', 'bin Faisal', 'bin Saad', 'bin Fahd', 'bin Abdulaziz', 'bin Salman'
|
|
]
|
|
|
|
SAUDI_CITIES = [
|
|
'Riyadh', 'Jeddah', 'Mecca', 'Medina', 'Dammam', 'Khobar', 'Dhahran',
|
|
'Taif', 'Tabuk', 'Buraidah', 'Khamis Mushait', 'Hofuf', 'Mubarraz',
|
|
'Jubail', 'Yanbu', 'Abha', 'Najran', 'Jazan', 'Hail', 'Arar'
|
|
]
|
|
|
|
SAUDI_PROVINCES = [
|
|
'Riyadh Province', 'Makkah Province', 'Eastern Province', 'Asir Province',
|
|
'Jazan Province', 'Medina Province', 'Qassim Province', 'Tabuk Province',
|
|
'Hail Province', 'Northern Borders Province', 'Najran Province', 'Al Bahah Province'
|
|
]
|
|
|
|
SAUDI_JOB_TITLES = {
|
|
'PHYSICIAN': ['Consultant Physician', 'Senior Physician', 'Staff Physician', 'Resident Physician',
|
|
'Chief Medical Officer'],
|
|
'NURSE': ['Head Nurse', 'Senior Nurse', 'Staff Nurse', 'Charge Nurse', 'Clinical Nurse Specialist'],
|
|
'PHARMACIST': ['Clinical Pharmacist', 'Staff Pharmacist', 'Pharmacy Manager', 'Pharmaceutical Consultant'],
|
|
'ADMIN': ['Medical Director', 'Hospital Administrator', 'Department Manager', 'Operations Manager'],
|
|
'LAB_TECH': ['Senior Lab Technician', 'Medical Laboratory Scientist', 'Lab Supervisor'],
|
|
'RAD_TECH': ['Senior Radiologic Technologist', 'CT Technologist', 'MRI Technologist'],
|
|
'RADIOLOGIST': ['Consultant Radiologist', 'Senior Radiologist', 'Interventional Radiologist']
|
|
}
|
|
|
|
SAUDI_DEPARTMENTS = [
|
|
'Internal Medicine', 'Cardiology', 'Orthopedics', 'Neurology', 'Oncology',
|
|
'Pediatrics', 'Emergency Medicine', 'Radiology', 'Laboratory Medicine',
|
|
'Pharmacy', 'Surgery', 'Obstetrics and Gynecology', 'Dermatology',
|
|
'Ophthalmology', 'ENT', 'Anesthesiology', 'Pathology', 'Psychiatry'
|
|
]
|
|
|
|
# Saudi Medical License Formats
|
|
SAUDI_LICENSE_PREFIXES = ['MOH', 'SCFHS', 'SMLE', 'SFH']
|
|
|
|
|
|
def generate_saudi_phone():
|
|
"""Generate Saudi phone number"""
|
|
area_codes = ['11', '12', '13', '14', '16', '17'] # Major Saudi area codes
|
|
return f"+966-{random.choice(area_codes)}-{random.randint(100, 999)}-{random.randint(1000, 9999)}"
|
|
|
|
|
|
def generate_saudi_mobile():
|
|
"""Generate Saudi mobile number"""
|
|
mobile_prefixes = ['50', '53', '54', '55', '56', '57', '58', '59'] # Saudi mobile prefixes
|
|
return f"+966-{random.choice(mobile_prefixes)}-{random.randint(100, 999)}-{random.randint(1000, 9999)}"
|
|
|
|
|
|
def generate_saudi_license():
|
|
"""Generate Saudi medical license number"""
|
|
prefix = random.choice(SAUDI_LICENSE_PREFIXES)
|
|
return f"{prefix}-{random.randint(100000, 999999)}"
|
|
|
|
|
|
def generate_saudi_employee_id(tenant_name, role):
|
|
"""Generate Saudi employee ID"""
|
|
tenant_code = ''.join([c for c in tenant_name.upper() if c.isalpha()])[:3]
|
|
role_code = role[:3].upper()
|
|
return f"{tenant_code}-{role_code}-{random.randint(1000, 9999)}"
|
|
|
|
|
|
def create_saudi_users(tenants, users_per_tenant=50):
|
|
"""Create Saudi healthcare users"""
|
|
users = []
|
|
|
|
role_distribution = {
|
|
'PHYSICIAN': 0.15,
|
|
'NURSE': 0.25,
|
|
'PHARMACIST': 0.08,
|
|
'LAB_TECH': 0.10,
|
|
'RAD_TECH': 0.08,
|
|
'RADIOLOGIST': 0.05,
|
|
'ADMIN': 0.07,
|
|
'MEDICAL_ASSISTANT': 0.12,
|
|
'CLERICAL': 0.10
|
|
}
|
|
|
|
for tenant in tenants:
|
|
tenant_users = []
|
|
|
|
for role, percentage in role_distribution.items():
|
|
user_count = max(1, int(users_per_tenant * percentage))
|
|
|
|
for i in range(user_count):
|
|
# Determine gender for Arabic naming
|
|
is_male = random.choice([True, False])
|
|
first_name = random.choice(SAUDI_FIRST_NAMES_MALE if is_male else SAUDI_FIRST_NAMES_FEMALE)
|
|
last_name = random.choice(SAUDI_FAMILY_NAMES)
|
|
middle_name = random.choice(SAUDI_MIDDLE_NAMES) if random.choice([True, False]) else None
|
|
|
|
# Generate username
|
|
username = f"{first_name.lower()}.{last_name.lower().replace('-', '').replace('al', '')}"
|
|
counter = 1
|
|
original_username = username
|
|
while User.objects.filter(username=username).exists():
|
|
username = f"{original_username}{counter}"
|
|
counter += 1
|
|
|
|
# Generate email
|
|
email = f"{username}@{tenant.name.lower().replace(' ', '').replace('-', '')}.sa"
|
|
|
|
# Professional information
|
|
department = random.choice(SAUDI_DEPARTMENTS)
|
|
job_title = random.choice(SAUDI_JOB_TITLES.get(role, [f"{role.replace('_', ' ').title()}"]))
|
|
|
|
# License information for medical professionals
|
|
license_number = None
|
|
license_state = None
|
|
license_expiry = None
|
|
npi_number = None
|
|
|
|
if role in ['PHYSICIAN', 'NURSE', 'PHARMACIST', 'RADIOLOGIST']:
|
|
license_number = generate_saudi_license()
|
|
license_state = random.choice(SAUDI_PROVINCES)
|
|
license_expiry = django_timezone.now().date() + timedelta(days=random.randint(365, 1095))
|
|
if role == 'PHYSICIAN':
|
|
npi_number = f"SA{random.randint(1000000, 9999999)}"
|
|
|
|
user = User.objects.create(
|
|
username=username,
|
|
email=email,
|
|
first_name=first_name,
|
|
last_name=last_name,
|
|
middle_name=middle_name,
|
|
preferred_name=first_name if random.choice([True, False]) else None,
|
|
tenant=tenant,
|
|
|
|
# Contact information
|
|
phone_number=generate_saudi_phone(),
|
|
mobile_number=generate_saudi_mobile(),
|
|
|
|
# Professional information
|
|
employee_id=generate_saudi_employee_id(tenant.name, role),
|
|
department=department,
|
|
job_title=job_title,
|
|
role=role,
|
|
|
|
# License information
|
|
license_number=license_number,
|
|
license_state=license_state,
|
|
license_expiry=license_expiry,
|
|
npi_number=npi_number,
|
|
|
|
# Security settings
|
|
force_password_change=random.choice([True, False]),
|
|
password_expires_at=django_timezone.now() + timedelta(days=random.randint(90, 365)),
|
|
failed_login_attempts=random.randint(0, 2),
|
|
two_factor_enabled=random.choice([True, False]) if role in ['PHYSICIAN', 'ADMIN',
|
|
'PHARMACIST'] else False,
|
|
|
|
# Session settings
|
|
max_concurrent_sessions=random.choice([1, 2, 3, 5]),
|
|
session_timeout_minutes=random.choice([30, 60, 120, 240]),
|
|
|
|
# Preferences
|
|
user_timezone='Asia/Riyadh',
|
|
language=random.choice(['ar', 'en', 'ar_SA']),
|
|
theme=random.choice(['LIGHT', 'DARK', 'AUTO']),
|
|
|
|
# Status
|
|
is_verified=True,
|
|
is_approved=True,
|
|
approval_date=django_timezone.now() - timedelta(days=random.randint(1, 180)),
|
|
is_active=True,
|
|
is_staff=role in ['ADMIN', 'SUPER_ADMIN'],
|
|
is_superuser=role == 'SUPER_ADMIN',
|
|
|
|
# Metadata
|
|
created_at=django_timezone.now() - timedelta(days=random.randint(1, 365)),
|
|
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 30)),
|
|
last_password_change=django_timezone.now() - timedelta(days=random.randint(1, 90)),
|
|
date_joined=django_timezone.now() - timedelta(days=random.randint(1, 365))
|
|
)
|
|
|
|
# Set password
|
|
user.set_password('Hospital@123') # Default password
|
|
user.save()
|
|
|
|
users.append(user)
|
|
tenant_users.append(user)
|
|
|
|
# Set approval relationships
|
|
admin_users = [u for u in tenant_users if u.role in ['ADMIN', 'SUPER_ADMIN']]
|
|
if admin_users:
|
|
approver = random.choice(admin_users)
|
|
for user in tenant_users:
|
|
if user != approver and user.role != 'SUPER_ADMIN':
|
|
user.approved_by = approver
|
|
user.save()
|
|
|
|
print(f"Created {len(tenant_users)} users for {tenant.name}")
|
|
|
|
return users
|
|
|
|
|
|
def create_saudi_two_factor_devices(users):
|
|
"""Create two-factor authentication devices for Saudi users"""
|
|
devices = []
|
|
|
|
device_types = ['TOTP', 'SMS', 'EMAIL']
|
|
device_names = {
|
|
'TOTP': ['Google Authenticator', 'Microsoft Authenticator', 'Authy', 'LastPass Authenticator'],
|
|
'SMS': ['Primary Mobile', 'Work Mobile', 'Emergency Contact'],
|
|
'EMAIL': ['Work Email', 'Personal Email', 'Backup Email']
|
|
}
|
|
|
|
for user in users:
|
|
if user.two_factor_enabled:
|
|
# Create 1-3 devices per user
|
|
device_count = random.randint(1, 3)
|
|
|
|
for _ in range(device_count):
|
|
device_type = random.choice(device_types)
|
|
|
|
device_data = {
|
|
'user': user,
|
|
'device_id': uuid.uuid4(),
|
|
'name': random.choice(device_names[device_type]),
|
|
'device_type': device_type,
|
|
'is_active': True,
|
|
'is_verified': True,
|
|
'verified_at': django_timezone.now() - timedelta(days=random.randint(1, 30)),
|
|
'last_used_at': django_timezone.now() - timedelta(hours=random.randint(1, 168)),
|
|
'usage_count': random.randint(5, 100),
|
|
'created_at': django_timezone.now() - timedelta(days=random.randint(1, 60))
|
|
}
|
|
|
|
if device_type == 'TOTP':
|
|
device_data['secret_key'] = secrets.token_urlsafe(32)
|
|
elif device_type == 'SMS':
|
|
device_data['phone_number'] = user.mobile_number
|
|
elif device_type == 'EMAIL':
|
|
device_data['email_address'] = user.email
|
|
|
|
device = TwoFactorDevice.objects.create(**device_data)
|
|
devices.append(device)
|
|
|
|
print(f"Created {len(devices)} two-factor devices")
|
|
return devices
|
|
|
|
|
|
def create_saudi_social_accounts(users):
|
|
"""Create social authentication accounts for Saudi users"""
|
|
social_accounts = []
|
|
|
|
# Common providers in Saudi Arabia
|
|
providers = ['GOOGLE', 'MICROSOFT', 'APPLE', 'LINKEDIN']
|
|
|
|
for user in users:
|
|
# 30% chance of having social accounts
|
|
if random.choice([True, False, False, False]):
|
|
provider = random.choice(providers)
|
|
|
|
social_account = SocialAccount.objects.create(
|
|
user=user,
|
|
provider=provider,
|
|
provider_id=f"{provider.lower()}_{random.randint(100000000, 999999999)}",
|
|
provider_email=user.email,
|
|
display_name=user.get_full_name(),
|
|
profile_url=f"https://{provider.lower()}.com/profile/{user.username}",
|
|
avatar_url=f"https://{provider.lower()}.com/avatar/{user.username}.jpg",
|
|
access_token=secrets.token_urlsafe(64),
|
|
refresh_token=secrets.token_urlsafe(64),
|
|
token_expires_at=django_timezone.now() + timedelta(hours=1),
|
|
is_active=True,
|
|
created_at=django_timezone.now() - timedelta(days=random.randint(1, 180)),
|
|
last_login_at=django_timezone.now() - timedelta(hours=random.randint(1, 48))
|
|
)
|
|
social_accounts.append(social_account)
|
|
|
|
print(f"Created {len(social_accounts)} social accounts")
|
|
return social_accounts
|
|
|
|
|
|
def create_saudi_user_sessions(users):
|
|
"""Create user sessions for Saudi healthcare users"""
|
|
sessions = []
|
|
|
|
saudi_ips = [
|
|
'37.99.', '37.200.', '31.9.', '31.173.', '188.161.',
|
|
'185.84.', '188.245.', '217.9.', '82.205.', '5.63.'
|
|
]
|
|
|
|
browsers = [
|
|
'Chrome 120.0.0.0', 'Safari 17.1.2', 'Firefox 121.0.0', 'Edge 120.0.0.0',
|
|
'Chrome Mobile 120.0.0.0', 'Safari Mobile 17.1.2'
|
|
]
|
|
|
|
operating_systems = [
|
|
'Windows 11', 'Windows 10', 'macOS 14.0', 'iOS 17.1.2',
|
|
'Android 14', 'Ubuntu 22.04'
|
|
]
|
|
|
|
device_types = ['DESKTOP', 'MOBILE', 'TABLET']
|
|
login_methods = ['PASSWORD', 'TWO_FACTOR', 'SOCIAL', 'SSO']
|
|
|
|
for user in users:
|
|
# Create 1-5 sessions per user
|
|
session_count = random.randint(1, 5)
|
|
|
|
for i in range(session_count):
|
|
ip_prefix = random.choice(saudi_ips)
|
|
ip_address = f"{ip_prefix}{random.randint(1, 255)}.{random.randint(1, 255)}"
|
|
|
|
session_start = django_timezone.now() - timedelta(hours=random.randint(1, 720))
|
|
is_active = i == 0 and random.choice([True, True, False]) # Most recent session likely active
|
|
|
|
session = UserSession.objects.create(
|
|
user=user,
|
|
session_key=f"session_{secrets.token_urlsafe(20)}",
|
|
session_id=uuid.uuid4(),
|
|
ip_address=ip_address,
|
|
user_agent=f"Mozilla/5.0 (compatible; HospitalSystem/1.0; {random.choice(browsers)})",
|
|
device_type=random.choice(device_types),
|
|
browser=random.choice(browsers),
|
|
operating_system=random.choice(operating_systems),
|
|
country='Saudi Arabia',
|
|
region=random.choice(SAUDI_PROVINCES),
|
|
city=random.choice(SAUDI_CITIES),
|
|
is_active=is_active,
|
|
login_method=random.choice(login_methods),
|
|
created_at=session_start,
|
|
last_activity_at=session_start + timedelta(minutes=random.randint(1, 480)),
|
|
expires_at=session_start + timedelta(hours=user.session_timeout_minutes // 60),
|
|
ended_at=None if is_active else session_start + timedelta(hours=random.randint(1, 8))
|
|
)
|
|
sessions.append(session)
|
|
|
|
print(f"Created {len(sessions)} user sessions")
|
|
return sessions
|
|
|
|
|
|
def create_saudi_password_history(users):
|
|
"""Create password history for Saudi users"""
|
|
password_history = []
|
|
|
|
passwords = ['Hospital@123', 'Medical@456', 'Health@789', 'Saudi@2024', 'Secure@Pass']
|
|
|
|
for user in users:
|
|
# Create 1-5 password history entries per user
|
|
history_count = random.randint(1, 5)
|
|
|
|
for i in range(history_count):
|
|
password = random.choice(passwords)
|
|
|
|
history_entry = PasswordHistory.objects.create(
|
|
user=user,
|
|
password_hash=make_password(password),
|
|
created_at=django_timezone.now() - timedelta(days=random.randint(30 * i, 30 * (i + 1)))
|
|
)
|
|
password_history.append(history_entry)
|
|
|
|
print(f"Created {len(password_history)} password history entries")
|
|
return password_history
|
|
|
|
|
|
def main():
|
|
"""Main function to generate all Saudi accounts data"""
|
|
print("Starting Saudi Healthcare Accounts 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 users
|
|
print("\n1. Creating Saudi Healthcare Users...")
|
|
users = create_saudi_users(tenants, 40) # 40 users per tenant
|
|
|
|
# Create two-factor devices
|
|
print("\n2. Creating Two-Factor Authentication Devices...")
|
|
devices = create_saudi_two_factor_devices(users)
|
|
|
|
# Create social accounts
|
|
print("\n3. Creating Social Authentication Accounts...")
|
|
social_accounts = create_saudi_social_accounts(users)
|
|
|
|
# Create user sessions
|
|
print("\n4. Creating User Sessions...")
|
|
sessions = create_saudi_user_sessions(users)
|
|
|
|
# Create password history
|
|
print("\n5. Creating Password History...")
|
|
password_history = create_saudi_password_history(users)
|
|
|
|
print(f"\n✅ Saudi Healthcare Accounts Data Generation Complete!")
|
|
print(f"📊 Summary:")
|
|
print(f" - Users: {len(users)}")
|
|
print(f" - Two-Factor Devices: {len(devices)}")
|
|
print(f" - Social Accounts: {len(social_accounts)}")
|
|
print(f" - User Sessions: {len(sessions)}")
|
|
print(f" - Password History Entries: {len(password_history)}")
|
|
|
|
# Role distribution summary
|
|
role_counts = {}
|
|
for user in users:
|
|
role_counts[user.role] = role_counts.get(user.role, 0) + 1
|
|
|
|
print(f"\n👥 User Role Distribution:")
|
|
for role, count in sorted(role_counts.items()):
|
|
print(f" - {role.replace('_', ' ').title()}: {count}")
|
|
|
|
return {
|
|
'users': users,
|
|
'devices': devices,
|
|
'social_accounts': social_accounts,
|
|
'sessions': sessions,
|
|
'password_history': password_history
|
|
}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |