272 lines
9.3 KiB
Python
272 lines
9.3 KiB
Python
"""
|
|
Base data generator class for Saudi healthcare data.
|
|
Provides common functionality and patterns for all data generators.
|
|
"""
|
|
|
|
import os
|
|
import django
|
|
from datetime import datetime, timedelta
|
|
from django.utils import timezone as django_timezone
|
|
from django.db import transaction
|
|
|
|
from .constants import *
|
|
from .generators import *
|
|
from .helpers import *
|
|
|
|
|
|
class BaseDataGenerator:
|
|
"""Base class for Saudi healthcare data generators"""
|
|
|
|
def __init__(self, tenant=None):
|
|
"""Initialize the data generator"""
|
|
self.tenant = tenant
|
|
self.progress_tracker = ProgressTracker()
|
|
|
|
def setup_django(self):
|
|
"""Setup Django environment if not already done"""
|
|
if not hasattr(django, 'apps') or not django.apps.apps_ready:
|
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hospital_management.settings')
|
|
django.setup()
|
|
|
|
def validate_dependencies(self):
|
|
"""Validate that required dependencies exist"""
|
|
return validate_dependencies()
|
|
|
|
def get_tenants(self):
|
|
"""Get tenants for data generation"""
|
|
from core.models import Tenant
|
|
|
|
if self.tenant:
|
|
return [self.tenant]
|
|
|
|
tenants = list(Tenant.objects.filter(is_active=True))
|
|
if not tenants:
|
|
raise ValueError("No active tenants found")
|
|
|
|
return tenants
|
|
|
|
def get_tenant_data(self, tenant):
|
|
"""Get common tenant data needed for generation"""
|
|
return {
|
|
'users': get_tenant_users(tenant),
|
|
'providers': get_tenant_providers(tenant),
|
|
'patients': get_tenant_patients(tenant),
|
|
'departments': get_tenant_departments(tenant)
|
|
}
|
|
|
|
def safe_bulk_create(self, Model, objects, **kwargs):
|
|
"""Safe bulk create with error handling"""
|
|
return safe_bulk_create(Model, objects, **kwargs)
|
|
|
|
def safe_create(self, Model, **kwargs):
|
|
"""Safe create with error handling"""
|
|
return safe_create(Model, **kwargs)
|
|
|
|
def print_progress(self, message=""):
|
|
"""Print current progress"""
|
|
self.progress_tracker.print_progress(message)
|
|
|
|
def increment_progress(self, count=1):
|
|
"""Increment progress counter"""
|
|
self.progress_tracker.increment(count)
|
|
|
|
def generate_summary(self, results):
|
|
"""Generate summary of generation results"""
|
|
total_created = sum(results.values()) if isinstance(results, dict) else len(results)
|
|
return {
|
|
'total_created': total_created,
|
|
'details': results,
|
|
'timestamp': django_timezone.now(),
|
|
'tenant': self.tenant.name if self.tenant else 'All Tenants'
|
|
}
|
|
|
|
def run_generation(self, **kwargs):
|
|
"""Main generation method - to be implemented by subclasses"""
|
|
raise NotImplementedError("Subclasses must implement run_generation method")
|
|
|
|
|
|
class SaudiHealthcareDataGenerator(BaseDataGenerator):
|
|
"""Base class for Saudi healthcare data generators with common Saudi-specific functionality"""
|
|
|
|
def __init__(self, tenant=None):
|
|
super().__init__(tenant)
|
|
self.setup_django()
|
|
|
|
def generate_saudi_name(self, gender=None):
|
|
"""Generate a Saudi name"""
|
|
return generate_saudi_name(gender)
|
|
|
|
def generate_saudi_address(self):
|
|
"""Generate a Saudi address"""
|
|
return generate_saudi_address()
|
|
|
|
def generate_saudi_phone(self):
|
|
"""Generate a Saudi phone number"""
|
|
return generate_saudi_phone()
|
|
|
|
def generate_saudi_mobile(self):
|
|
"""Generate a Saudi mobile number"""
|
|
return generate_saudi_mobile_e164()
|
|
|
|
def generate_birth_date(self, min_age=1, max_age=85):
|
|
"""Generate birth date within age range"""
|
|
return generate_birth_date(min_age, max_age)
|
|
|
|
def generate_hire_date(self, max_years_ago=20):
|
|
"""Generate hire date within reasonable range"""
|
|
return generate_hire_date(max_years_ago)
|
|
|
|
def generate_future_date(self, days_ahead=365):
|
|
"""Generate future date"""
|
|
return generate_future_date(days_ahead)
|
|
|
|
def generate_past_date(self, days_back=365):
|
|
"""Generate past date"""
|
|
return generate_past_date(days_back)
|
|
|
|
def generate_vital_signs(self, patient_age=None):
|
|
"""Generate realistic vital signs"""
|
|
return generate_vital_signs(patient_age)
|
|
|
|
def pick_job_title(self, department):
|
|
"""Pick appropriate job title for department"""
|
|
return pick_job_title_for_department(department)
|
|
|
|
def infer_role(self, job_title):
|
|
"""Infer role from job title"""
|
|
return infer_role_from_title(job_title)
|
|
|
|
def tenant_unique_username(self, tenant, base_username):
|
|
"""Generate tenant-unique username"""
|
|
return tenant_scoped_unique_username(tenant, base_username)
|
|
|
|
def generate_lab_values(self, test_type='QUANTITATIVE', reference_range=None):
|
|
"""Generate lab values"""
|
|
return generate_lab_values(test_type, reference_range)
|
|
|
|
|
|
class DataGenerationOrchestrator:
|
|
"""Orchestrates the execution of multiple data generators with dependency management"""
|
|
|
|
def __init__(self):
|
|
self.generators = {}
|
|
self.execution_order = [
|
|
'core', # Tenants
|
|
'accounts', # Users
|
|
'hr', # Employees/Departments
|
|
'patients', # Patients
|
|
# Clinical modules (can run in parallel)
|
|
'emr',
|
|
'lab',
|
|
'radiology',
|
|
'pharmacy',
|
|
'appointments',
|
|
'billing',
|
|
'inpatients',
|
|
'inventory',
|
|
'facility_management'
|
|
]
|
|
|
|
def register_generator(self, name, generator_class, **kwargs):
|
|
"""Register a data generator"""
|
|
self.generators[name] = {
|
|
'class': generator_class,
|
|
'kwargs': kwargs,
|
|
'instance': None
|
|
}
|
|
|
|
def get_dependencies(self, generator_name):
|
|
"""Get dependencies for a generator"""
|
|
dependencies = {
|
|
'core': [],
|
|
'accounts': ['core'],
|
|
'hr': ['core', 'accounts'],
|
|
'patients': ['core'],
|
|
'emr': ['core', 'accounts', 'hr', 'patients'],
|
|
'lab': ['core', 'accounts', 'hr', 'patients'],
|
|
'radiology': ['core', 'accounts', 'hr', 'patients'],
|
|
'pharmacy': ['core', 'accounts', 'hr', 'patients'],
|
|
'appointments': ['core', 'accounts', 'hr', 'patients'],
|
|
'billing': ['core', 'accounts', 'patients'],
|
|
'inpatients': ['core', 'accounts', 'hr', 'patients'],
|
|
'inventory': ['core'],
|
|
'facility_management': ['core']
|
|
}
|
|
return dependencies.get(generator_name, [])
|
|
|
|
def validate_dependencies(self, generator_name, completed_generators):
|
|
"""Validate that dependencies are satisfied"""
|
|
dependencies = self.get_dependencies(generator_name)
|
|
missing = [dep for dep in dependencies if dep not in completed_generators]
|
|
return len(missing) == 0, missing
|
|
|
|
def run_generator(self, name, **kwargs):
|
|
"""Run a specific generator"""
|
|
if name not in self.generators:
|
|
print(f"Generator {name} not registered")
|
|
return None
|
|
|
|
generator_config = self.generators[name]
|
|
generator_class = generator_config['class']
|
|
generator_kwargs = {**generator_config['kwargs'], **kwargs}
|
|
|
|
try:
|
|
generator = generator_class(**generator_kwargs)
|
|
results = generator.run_generation()
|
|
return results
|
|
except Exception as e:
|
|
print(f"Error running generator {name}: {e}")
|
|
return None
|
|
|
|
def run_all(self, generators_to_run=None, **kwargs):
|
|
"""Run all generators in dependency order"""
|
|
if generators_to_run is None:
|
|
generators_to_run = self.execution_order
|
|
|
|
completed = set()
|
|
results = {}
|
|
|
|
for generator_name in generators_to_run:
|
|
if generator_name not in self.generators:
|
|
print(f"Skipping {generator_name} - not registered")
|
|
continue
|
|
|
|
# Validate dependencies
|
|
valid, missing = self.validate_dependencies(generator_name, completed)
|
|
if not valid:
|
|
print(f"Skipping {generator_name} - missing dependencies: {missing}")
|
|
continue
|
|
|
|
print(f"\n🚀 Running {generator_name} generator...")
|
|
result = self.run_generator(generator_name, **kwargs)
|
|
|
|
if result is not None:
|
|
results[generator_name] = result
|
|
completed.add(generator_name)
|
|
print(f"✅ {generator_name} completed")
|
|
else:
|
|
print(f"❌ {generator_name} failed")
|
|
|
|
return results
|
|
|
|
def get_available_generators(self):
|
|
"""Get list of available generators"""
|
|
return list(self.generators.keys())
|
|
|
|
def get_execution_plan(self, generators_to_run=None):
|
|
"""Get execution plan with dependencies"""
|
|
if generators_to_run is None:
|
|
generators_to_run = self.execution_order
|
|
|
|
plan = []
|
|
for generator_name in generators_to_run:
|
|
if generator_name in self.generators:
|
|
dependencies = self.get_dependencies(generator_name)
|
|
plan.append({
|
|
'name': generator_name,
|
|
'dependencies': dependencies,
|
|
'class': self.generators[generator_name]['class'].__name__
|
|
})
|
|
|
|
return plan
|