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

926 lines
42 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, date, time
from django.utils import timezone as django_timezone
from hr.models import Employee, Department, Schedule, ScheduleAssignment, TimeEntry, PerformanceReview, TrainingRecord
from accounts.models import User
from core.models import Tenant
from decimal import Decimal
import uuid
# Saudi-specific data constants
SAUDI_FIRST_NAMES_MALE = [
'Mohammed', 'Ahmed', 'Abdullah', 'Khalid', 'Omar', 'Ali', 'Hassan', 'Ibrahim',
'Yousef', 'Fahd', 'Faisal', 'Saud', 'Nasser', 'Abdulaziz', 'Abdulrahman',
'Majid', 'Saeed', 'Waleed', 'Tariq', 'Mansour', 'Sultan', 'Bandar',
'Turki', 'Nawaf', 'Rayan', 'Ziad', 'Adel', 'Salman', 'Fares', 'Amjad'
]
SAUDI_FIRST_NAMES_FEMALE = [
'Fatima', 'Aisha', 'Khadija', 'Maryam', 'Zahra', 'Noor', 'Layla', 'Sara',
'Hala', 'Noura', 'Reem', 'Lina', 'Dina', 'Rana', 'Jana', 'Maya',
'Amal', 'Hanan', 'Widad', 'Nada', 'Rawan', 'Ghada', 'Samar', 'Hind',
'Munira', 'Rahma', 'Najla', 'Dalal', 'Abeer', 'Manal'
]
SAUDI_LAST_NAMES = [
'Al-Rashid', 'Al-Otaibi', 'Al-Dosari', 'Al-Harbi', 'Al-Zahrani', 'Al-Ghamdi',
'Al-Qahtani', 'Al-Maliki', 'Al-Shammari', 'Al-Mutairi', 'Al-Subai', 'Al-Shamsi',
'Al-Faisal', 'Al-Saud', 'Al-Thani', 'Al-Fahd', 'Al-Sultan', 'Al-Nasser',
'Al-Mansour', 'Al-Khalil', 'Al-Ibrahim', 'Al-Hassan', 'Al-Ali', 'Al-Omar',
'Al-Ahmed', 'Al-Mohammed', 'Bin Laden', 'Bin Rashid', 'Bin Abdullah', 'Bin Sultan'
]
SAUDI_CITIES = [
'Riyadh', 'Jeddah', 'Mecca', 'Medina', 'Dammam', 'Khobar', 'Dhahran',
'Taif', 'Tabuk', 'Buraidah', 'Khamis Mushait', 'Hail', 'Hofuf', 'Najran',
'Jazan', 'Yanbu', 'Abha', 'Arar', 'Sakaka', 'Qatif'
]
SAUDI_DEPARTMENTS = [
('EMERGENCY', 'Emergency Department', 'Emergency medical services'),
('ICU', 'Intensive Care Unit', 'Critical care services'),
('CARDIOLOGY', 'Cardiology Department', 'Heart and cardiovascular care'),
('SURGERY', 'General Surgery', 'Surgical services'),
('ORTHOPEDICS', 'Orthopedics Department', 'Bone and joint care'),
('PEDIATRICS', 'Pediatrics Department', 'Children healthcare'),
('OBSTETRICS', 'Obstetrics & Gynecology', 'Women and maternity care'),
('RADIOLOGY', 'Radiology Department', 'Medical imaging services'),
('LABORATORY', 'Laboratory Services', 'Diagnostic testing'),
('PHARMACY', 'Pharmacy Department', 'Medication services'),
('NURSING', 'Nursing Services', 'Patient care services'),
('ADMINISTRATION', 'Administration', 'Hospital administration'),
('FINANCE', 'Finance Department', 'Financial management'),
('HR', 'Human Resources', 'Staff management'),
('IT', 'Information Technology', 'Technology services'),
('MAINTENANCE', 'Maintenance Services', 'Facility maintenance'),
('SECURITY', 'Security Department', 'Hospital security'),
('HOUSEKEEPING', 'Housekeeping Services', 'Cleaning services'),
('FOOD_SERVICE', 'Food Services', 'Dietary services'),
('SOCIAL_WORK', 'Social Work', 'Patient social services')
]
SAUDI_JOB_TITLES = {
'CLINICAL': [
'Chief Medical Officer', 'Medical Director', 'Department Head', 'Consultant Physician',
'Senior Physician', 'Physician', 'Resident Physician', 'Intern',
'Chief Nurse', 'Nurse Manager', 'Senior Nurse', 'Staff Nurse',
'Nurse Practitioner', 'Clinical Nurse Specialist', 'Charge Nurse',
'Pharmacist', 'Clinical Pharmacist', 'Pharmacy Technician',
'Radiologist', 'Radiology Technician', 'Medical Technologist',
'Laboratory Technician', 'Physical Therapist', 'Respiratory Therapist'
],
'ADMINISTRATIVE': [
'Chief Executive Officer', 'Chief Operating Officer', 'Administrator',
'Assistant Administrator', 'Department Manager', 'Supervisor',
'Administrative Assistant', 'Secretary', 'Clerk', 'Coordinator'
],
'SUPPORT': [
'Security Officer', 'Security Guard', 'Maintenance Technician',
'Housekeeping Supervisor', 'Housekeeper', 'Food Service Manager',
'Cook', 'Kitchen Assistant', 'Transport Aide', 'Receptionist'
]
}
SAUDI_TRAINING_PROGRAMS = [
'Basic Life Support (BLS)', 'Advanced Cardiac Life Support (ACLS)',
'Pediatric Advanced Life Support (PALS)', 'Infection Control',
'Patient Safety', 'Fire Safety', 'Emergency Procedures',
'HIPAA Compliance', 'Cultural Sensitivity', 'Arabic Language',
'Islamic Healthcare Ethics', 'Medication Administration',
'Wound Care Management', 'Electronic Health Records',
'Quality Improvement', 'Customer Service Excellence'
]
def create_saudi_departments(tenants):
"""Create Saudi hospital departments"""
departments = []
for tenant in tenants:
# Check for existing departments to avoid duplicates
existing_dept_codes = set(
Department.objects.filter(tenant=tenant).values_list('department_code', flat=True)
)
for dept_code, dept_name, dept_desc in SAUDI_DEPARTMENTS:
# Skip if department already exists for this tenant
if dept_code in existing_dept_codes:
print(f"Department {dept_code} already exists for {tenant.name}, skipping...")
continue
# Determine department type
if dept_code in ['EMERGENCY', 'ICU', 'CARDIOLOGY', 'SURGERY', 'ORTHOPEDICS',
'PEDIATRICS', 'OBSTETRICS', 'RADIOLOGY', 'LABORATORY', 'PHARMACY', 'NURSING']:
dept_type = 'CLINICAL'
elif dept_code in ['ADMINISTRATION', 'FINANCE', 'HR', 'IT']:
dept_type = 'ADMINISTRATIVE'
elif dept_code in ['MAINTENANCE', 'SECURITY', 'HOUSEKEEPING', 'FOOD_SERVICE']:
dept_type = 'SUPPORT'
else:
dept_type = 'ANCILLARY'
try:
department = Department.objects.create(
tenant=tenant,
department_code=dept_code,
name=dept_name,
description=dept_desc,
department_type=dept_type,
annual_budget=Decimal(str(random.randint(500000, 5000000))),
cost_center=f"CC-{dept_code}",
location=f"{random.choice(['Building A', 'Building B', 'Main Building'])}, Floor {random.randint(1, 5)}",
is_active=True,
notes=f"Primary {dept_name.lower()} for {tenant.name}",
created_at=django_timezone.now() - timedelta(days=random.randint(30, 365)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 30))
)
departments.append(department)
except Exception as e:
print(f"Error creating department {dept_code} for {tenant.name}: {e}")
continue
created_count = len([d for d in departments if d.tenant == tenant])
print(f"Created {created_count} new departments for {tenant.name}")
return departments
def create_saudi_employees(tenants, departments, employees_per_tenant=150):
"""Create Saudi hospital employees"""
employees = []
for tenant in tenants:
tenant_departments = [dept for dept in departments if dept.tenant == tenant]
if not tenant_departments:
print(f"No departments found for {tenant.name}, skipping employee creation...")
continue
# Check existing employee numbers to avoid duplicates
existing_emp_numbers = set(
Employee.objects.filter(tenant=tenant).values_list('employee_number', flat=True)
)
created_count = 0
for i in range(employees_per_tenant):
# Generate unique employee number
emp_number = f"EMP-{i + 1:04d}"
counter = 1
original_emp_number = emp_number
while emp_number in existing_emp_numbers:
emp_number = f"{original_emp_number}-{counter}"
counter += 1
# Generate employee details
gender = random.choice(['MALE', 'FEMALE'])
first_name = random.choice(SAUDI_FIRST_NAMES_MALE if gender == 'MALE' else SAUDI_FIRST_NAMES_FEMALE)
last_name = random.choice(SAUDI_LAST_NAMES)
middle_name = random.choice(SAUDI_FIRST_NAMES_MALE + SAUDI_FIRST_NAMES_FEMALE) if random.choice(
[True, False]) else None
# Select department and job title
department = random.choice(tenant_departments)
if department.department_type == 'CLINICAL':
job_titles = SAUDI_JOB_TITLES['CLINICAL']
elif department.department_type == 'ADMINISTRATIVE':
job_titles = SAUDI_JOB_TITLES['ADMINISTRATIVE']
else:
job_titles = SAUDI_JOB_TITLES['SUPPORT']
job_title = random.choice(job_titles)
# Determine employment type and status
employment_type = random.choices(
['FULL_TIME', 'PART_TIME', 'CONTRACT', 'CONTRACT'],
weights=[70, 15, 10, 5]
)[0]
employment_status = random.choices(
['ACTIVE', 'INACTIVE', 'LEAVE'],
weights=[85, 10, 5]
)[0]
# Generate dates
hire_date = django_timezone.now().date() - timedelta(days=random.randint(30, 2000))
date_of_birth = hire_date - timedelta(days=random.randint(8000, 15000)) # 22-41 years old at hire
# Generate salary information
if 'Chief' in job_title or 'Director' in job_title:
annual_salary = Decimal(str(random.randint(300000, 600000)))
hourly_rate = None
elif 'Manager' in job_title or 'Head' in job_title:
annual_salary = Decimal(str(random.randint(180000, 350000)))
hourly_rate = None
elif 'Senior' in job_title or 'Consultant' in job_title:
annual_salary = Decimal(str(random.randint(120000, 250000)))
hourly_rate = None
else:
annual_salary = None
hourly_rate = Decimal(str(random.randint(50, 150)))
# FTE and hours
if employment_type == 'FULL_TIME':
fte_percentage = Decimal('100.00')
standard_hours = Decimal('40.00')
elif employment_type == 'PART_TIME':
fte_percentage = Decimal(str(random.choice([50, 60, 75])))
standard_hours = Decimal(str(fte_percentage / 100 * 40))
else:
fte_percentage = Decimal('100.00')
standard_hours = Decimal('40.00')
# Contact information
city = random.choice(SAUDI_CITIES)
phone = f"+966-{random.randint(11, 13)}-{random.randint(100, 999)}-{random.randint(1000, 9999)}"
mobile = f"+966-5{random.randint(0, 9)}-{random.randint(100, 999)}-{random.randint(1000, 9999)}"
try:
# Create employee
employee = Employee.objects.create(
tenant=tenant,
employee_number=emp_number,
first_name=first_name,
last_name=last_name,
middle_name=middle_name,
preferred_name=first_name if random.choice([True, False]) else None,
email=f"{first_name.lower()}.{last_name.lower().replace('-', '').replace(' ', '')}@{tenant.name.lower().replace(' ', '')}.sa",
phone=phone,
mobile_phone=mobile,
address_line_1=f"{random.randint(1, 999)} {random.choice(['King Fahd Road', 'Prince Sultan Street', 'Al-Malik Road', 'Olaya Street'])}",
address_line_2=f"Apt {random.randint(1, 50)}" if random.choice([True, False]) else None,
city=city,
state=random.choice(['Riyadh Province', 'Makkah Province', 'Eastern Province', 'Asir Province']),
postal_code=f"{random.randint(10000, 99999)}",
country='Saudi Arabia',
date_of_birth=date_of_birth,
gender=gender,
marital_status=random.choice(['SINGLE', 'MARRIED', 'DIVORCED', 'WIDOWED']),
department=department,
job_title=job_title,
employment_type=employment_type,
employment_status=employment_status,
hire_date=hire_date,
termination_date=None if employment_status != 'TERMINATED' else hire_date + timedelta(
days=random.randint(30, 1000)),
standard_hours_per_week=standard_hours,
fte_percentage=fte_percentage,
hourly_rate=hourly_rate,
annual_salary=annual_salary,
license_number=f"LIC-{random.randint(100000, 999999)}" if 'Physician' in job_title or 'Nurse' in job_title else None,
license_expiry_date=django_timezone.now().date() + timedelta(
days=random.randint(30, 730)) if 'Physician' in job_title or 'Nurse' in job_title else None,
certifications=[
{'name': 'BLS Certification', 'number': f"BLS-{random.randint(10000, 99999)}",
'expiry': (django_timezone.now().date() + timedelta(days=365)).isoformat()},
{'name': 'ACLS Certification', 'number': f"ACLS-{random.randint(10000, 99999)}",
'expiry': (django_timezone.now().date() + timedelta(days=730)).isoformat()}
] if 'Physician' in job_title or 'Nurse' in job_title else [],
emergency_contact_name=f"{random.choice(SAUDI_FIRST_NAMES_MALE + SAUDI_FIRST_NAMES_FEMALE)} {random.choice(SAUDI_LAST_NAMES)}",
emergency_contact_relationship=random.choice(['Spouse', 'Parent', 'Sibling', 'Child']),
emergency_contact_phone=f"+966-5{random.randint(0, 9)}-{random.randint(100, 999)}-{random.randint(1000, 9999)}",
notes=f"Employee hired for {department.name} department",
created_at=django_timezone.now() - timedelta(days=random.randint(1, 100)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 30))
)
employees.append(employee)
existing_emp_numbers.add(emp_number)
created_count += 1
except Exception as e:
print(f"Error creating employee {emp_number} for {tenant.name}: {e}")
continue
print(f"Created {created_count} employees for {tenant.name}")
return employees
def assign_department_heads(departments, employees):
"""Assign department heads from senior employees"""
for department in departments:
# Skip if department already has a head
if department.department_head:
continue
# Find senior employees in this department
dept_employees = [emp for emp in employees if
emp.department == department and emp.employment_status == 'ACTIVE']
senior_employees = [emp for emp in dept_employees if
any(title in emp.job_title for title in ['Chief', 'Director', 'Head', 'Manager'])]
if senior_employees:
department.department_head = random.choice(senior_employees)
department.save()
print("Assigned department heads")
def assign_supervisors(employees):
"""Assign supervisors to employees based on hierarchy"""
for employee in employees:
if employee.employment_status != 'ACTIVE' or employee.supervisor:
continue
# Don't assign supervisors to top-level positions
if any(title in employee.job_title for title in ['Chief', 'Director']):
continue
# Find potential supervisors in the same department
potential_supervisors = [
emp for emp in employees
if emp.department == employee.department
and emp.employment_status == 'ACTIVE'
and emp != employee
and any(title in emp.job_title for title in ['Chief', 'Director', 'Head', 'Manager', 'Senior'])
]
if potential_supervisors:
employee.supervisor = random.choice(potential_supervisors)
employee.save()
print("Assigned employee supervisors")
def create_saudi_schedules(employees, schedules_per_employee=2):
"""Create work schedules for Saudi employees"""
schedules = []
schedule_patterns = {
'DAY_SHIFT': {
'sunday': {'start': '07:00', 'end': '19:00'},
'monday': {'start': '07:00', 'end': '19:00'},
'tuesday': {'start': '07:00', 'end': '19:00'},
'wednesday': {'start': '07:00', 'end': '19:00'},
'thursday': {'start': '07:00', 'end': '19:00'},
'friday': {'start': '07:00', 'end': '19:00'},
'saturday': {'start': '07:00', 'end': '19:00'},
},
# 'EVENING_SHIFT': {
# 'monday': {'start': '15:00', 'end': '23:00'},
# 'tuesday': {'start': '15:00', 'end': '23:00'},
# 'wednesday': {'start': '15:00', 'end': '23:00'},
# 'thursday': {'start': '15:00', 'end': '23:00'},
# 'friday': {'start': '13:00', 'end': '21:00'},
# 'saturday': {'start': '15:00', 'end': '23:00'},
# 'sunday': 'off'
# },
'NIGHT_SHIFT': {
'sunday': {'start': '19:00', 'end': '07:00'},
'monday': {'start': '19:00', 'end': '07:00'},
'tuesday': {'start': '19:00', 'end': '07:00'},
'wednesday': {'start': '19:00', 'end': '07:00'},
'thursday': {'start': '19:00', 'end': '07:00'},
'friday': {'start': '19:00', 'end': '07:00'},
'saturday': {'start': '19:00', 'end': '07:00'},
},
'ADMIN_HOURS': {
'sunday': {'start': '08:00', 'end': '17:00'},
'monday': {'start': '08:00', 'end': '17:00'},
'tuesday': {'start': '08:00', 'end': '17:00'},
'wednesday': {'start': '08:00', 'end': '17:00'},
'thursday': {'start': '08:00', 'end': '16:00'},
'friday': 'off',
'saturday': 'off',
}
}
clinical_employees = [emp for emp in employees if
emp.department and emp.department.department_type == 'CLINICAL' and emp.employment_status == 'ACTIVE']
admin_employees = [emp for emp in employees if
emp.department and emp.department.department_type in ['ADMINISTRATIVE',
'SUPPORT'] and emp.employment_status == 'ACTIVE']
# Create schedules for clinical staff (rotating shifts)
for employee in clinical_employees:
for i in range(schedules_per_employee):
schedule_type = random.choice(['REGULAR', 'ROTATING'])
pattern_name = random.choice(['DAY_SHIFT', 'EVENING_SHIFT', 'NIGHT_SHIFT'])
effective_date = django_timezone.now().date() - timedelta(days=random.randint(0, 180))
end_date = effective_date + timedelta(days=random.randint(90, 365)) if random.choice(
[True, False]) else None
try:
schedule = Schedule.objects.create(
employee=employee,
name=f"{employee.get_full_name()} - {pattern_name.replace('_', ' ').title()}",
description=f"{pattern_name.replace('_', ' ').title()} schedule for {employee.job_title}",
schedule_type=schedule_type,
effective_date=effective_date,
end_date=end_date,
schedule_pattern=schedule_patterns[pattern_name],
is_active=i == 0, # Only first schedule is active
notes=f"Standard {pattern_name.replace('_', ' ').lower()} for clinical staff",
created_at=django_timezone.now() - timedelta(days=random.randint(1, 90)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 30))
)
schedules.append(schedule)
except Exception as e:
print(f"Error creating schedule for {employee.get_full_name()}: {e}")
continue
# Create schedules for administrative staff (regular hours)
for employee in admin_employees:
schedule_type = 'REGULAR'
pattern_name = 'ADMIN_HOURS'
effective_date = django_timezone.now().date() - timedelta(days=random.randint(0, 180))
try:
schedule = Schedule.objects.create(
employee=employee,
name=f"{employee.get_full_name()} - Administrative Hours",
description="Standard administrative working hours",
schedule_type=schedule_type,
effective_date=effective_date,
end_date=None,
schedule_pattern=schedule_patterns[pattern_name],
is_active=True,
notes="Standard administrative schedule",
created_at=django_timezone.now() - timedelta(days=random.randint(1, 90)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 30))
)
schedules.append(schedule)
except Exception as e:
print(f"Error creating schedule for {employee.get_full_name()}: {e}")
continue
print(f"Created {len(schedules)} employee schedules")
return schedules
def create_schedule_assignments(schedules, days_back=30):
"""Create specific schedule assignments"""
assignments = []
shift_types = {
'DAY_SHIFT': 'DAY',
# 'EVENING_SHIFT': 'EVENING',
'NIGHT_SHIFT': 'NIGHT',
'ADMIN_HOURS': 'DAY'
}
for schedule in schedules:
if not schedule.is_active:
continue
# Create assignments for the last 30 days
start_date = django_timezone.now().date() - timedelta(days=days_back)
for day_offset in range(days_back):
assignment_date = start_date + timedelta(days=day_offset)
weekday = assignment_date.strftime('%A').lower()
# Skip if no work scheduled for this day
if weekday not in schedule.schedule_pattern or schedule.schedule_pattern[weekday] == 'off':
continue
day_schedule = schedule.schedule_pattern[weekday]
start_time = datetime.strptime(day_schedule['start'], '%H:%M').time()
end_time = datetime.strptime(day_schedule['end'], '%H:%M').time()
# Determine shift type based on schedule pattern
pattern_key = None
for pattern_name, pattern_data in [
('DAY_SHIFT', schedule.schedule_pattern),
# ('EVENING_SHIFT', schedule.schedule_pattern),
('NIGHT_SHIFT', schedule.schedule_pattern),
('ADMIN_HOURS', schedule.schedule_pattern)
]:
if pattern_data == schedule.schedule_pattern:
pattern_key = pattern_name
break
shift_type = shift_types.get(pattern_key, 'DAY')
# Random status (mostly completed for past dates)
if assignment_date < django_timezone.now().date():
status = random.choices(
['COMPLETED', 'NO_SHOW', 'CANCELLED'],
weights=[90, 5, 5]
)[0]
else:
status = random.choices(
['SCHEDULED', 'CONFIRMED'],
weights=[70, 30]
)[0]
try:
assignment = ScheduleAssignment.objects.create(
schedule=schedule,
assignment_date=assignment_date,
start_time=start_time,
end_time=end_time,
shift_type=shift_type,
department=schedule.employee.department,
location=schedule.employee.department.location if schedule.employee.department else None,
status=status,
break_minutes=15 if shift_type in ['DAY', 'EVENING'] else 30,
lunch_minutes=30 if shift_type in ['DAY', 'EVENING'] else 0,
notes=f"{shift_type.title()} shift assignment",
created_at=django_timezone.now() - timedelta(days=random.randint(0, 7)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 3))
)
assignments.append(assignment)
except Exception as e:
print(f"Error creating assignment for {schedule.employee.get_full_name()}: {e}")
continue
print(f"Created {len(assignments)} schedule assignments")
return assignments
def create_time_entries(employees, days_back=30):
"""Create time entries for employees"""
entries = []
active_employees = [emp for emp in employees if emp.employment_status == 'ACTIVE']
for employee in active_employees:
# Create time entries for working days
start_date = django_timezone.now().date() - timedelta(days=days_back)
for day_offset in range(days_back):
work_date = start_date + timedelta(days=day_offset)
# Skip weekends for administrative staff
if (employee.department and employee.department.department_type == 'ADMINISTRATIVE'
and work_date.weekday() in [4, 5]): # Friday, Saturday
continue
# Random chance of working (85% for clinical, 90% for admin)
work_probability = 0.85 if employee.department and employee.department.department_type == 'CLINICAL' else 0.90
if random.random() > work_probability:
continue
# Generate work times based on typical schedules
if employee.department and employee.department.department_type == 'CLINICAL':
# Clinical staff - various shifts
shift_options = [
(time(7, 0), time(15, 0)), # Day shift
(time(15, 0), time(23, 0)), # Evening shift
(time(23, 0), time(7, 0)), # Night shift (next day)
]
start_time, end_time = random.choice(shift_options)
else:
# Administrative staff - regular hours
start_time = time(8, 0)
end_time = time(17, 0) if work_date.weekday() != 4 else time(12, 0) # Half day Friday
# Add some variance to clock in/out times
clock_in_variance = random.randint(-15, 15) # ±15 minutes
clock_out_variance = random.randint(-15, 15)
clock_in = datetime.combine(work_date, start_time) + timedelta(minutes=clock_in_variance)
clock_out = datetime.combine(work_date, end_time) + timedelta(minutes=clock_out_variance)
# Handle overnight shifts
if end_time < start_time:
clock_out += timedelta(days=1)
# Break and lunch times
break_start = clock_in + timedelta(hours=2, minutes=random.randint(0, 60))
break_end = break_start + timedelta(minutes=15)
lunch_start = clock_in + timedelta(hours=4, minutes=random.randint(0, 60))
lunch_end = lunch_start + timedelta(minutes=30)
entry_type = random.choices(
['REGULAR', 'OVERTIME', 'HOLIDAY'],
weights=[85, 10, 5]
)[0]
status = random.choices(
['APPROVED', 'SUBMITTED', 'DRAFT'],
weights=[70, 20, 10]
)[0]
try:
entry = TimeEntry.objects.create(
employee=employee,
work_date=work_date,
clock_in_time=clock_in,
clock_out_time=clock_out,
break_start_time=break_start,
break_end_time=break_end,
lunch_start_time=lunch_start,
lunch_end_time=lunch_end,
entry_type=entry_type,
department=employee.department,
location=employee.department.location if employee.department else None,
status=status,
notes=f"{entry_type.title()} work shift",
created_at=django_timezone.now() - timedelta(days=random.randint(0, 7)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 3))
)
entries.append(entry)
except Exception as e:
print(f"Error creating time entry for {employee.get_full_name()}: {e}")
continue
print(f"Created {len(entries)} time entries")
return entries
def create_performance_reviews(employees):
"""Create performance reviews for employees"""
reviews = []
# Only create reviews for employees with 6+ months of service
eligible_employees = [
emp for emp in employees
if emp.employment_status == 'ACTIVE'
and (django_timezone.now().date() - emp.hire_date).days >= 180
]
competency_areas = [
'Clinical Skills', 'Communication', 'Teamwork', 'Professionalism',
'Quality of Work', 'Productivity', 'Problem Solving', 'Initiative',
'Reliability', 'Cultural Competency', 'Patient Care', 'Safety Compliance'
]
for employee in eligible_employees:
# Create 1-2 reviews per eligible employee
num_reviews = random.randint(1, 2)
for i in range(num_reviews):
# Review period (last 6-12 months)
review_period_months = random.randint(6, 12)
review_date = django_timezone.now().date() - timedelta(days=random.randint(0, 90))
period_start = review_date - timedelta(days=review_period_months * 30)
period_end = review_date - timedelta(days=30)
review_type = random.choices(
['ANNUAL', 'PROBATIONARY', 'MID_YEAR'],
weights=[60, 20, 20]
)[0]
# Generate overall rating (1-5, skewed toward higher ratings)
overall_rating = random.choices(
[2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0],
weights=[5, 10, 20, 25, 25, 10, 5]
)[0]
# Generate competency ratings
competency_ratings = {}
for competency in random.sample(competency_areas, random.randint(6, 10)):
rating = random.choices(
[2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0],
weights=[5, 10, 20, 25, 25, 10, 5]
)[0]
competency_ratings[competency] = rating
status = random.choices(
['COMPLETED', 'ACKNOWLEDGED', 'IN_PROGRESS'],
weights=[60, 30, 10]
)[0]
try:
review = PerformanceReview.objects.create(
employee=employee,
review_period_start=period_start,
review_period_end=period_end,
review_date=review_date,
review_type=review_type,
overall_rating=Decimal(str(overall_rating)),
competency_ratings=competency_ratings,
goals_achieved=f"{employee.first_name} successfully completed assigned patient care duties and contributed to department objectives.",
goals_not_achieved="Minor areas for improvement in documentation timeliness." if overall_rating < 4.0 else None,
future_goals="Continue professional development and maintain high standards of patient care.",
strengths=f"Strong {random.choice(['clinical skills', 'communication abilities', 'teamwork', 'dedication to patient care'])}.",
areas_for_improvement="Opportunities for growth in leadership and mentoring junior staff." if overall_rating < 4.5 else None,
development_plan="Recommended for advanced training programs and leadership development opportunities.",
training_recommendations=f"Suggested training: {random.choice(SAUDI_TRAINING_PROGRAMS)}",
employee_comments="Appreciate the feedback and committed to continuous improvement." if status == 'ACKNOWLEDGED' else None,
employee_signature_date=review_date + timedelta(
days=random.randint(1, 14)) if status == 'ACKNOWLEDGED' else None,
status=status,
notes=f"{review_type.title()} performance review completed",
created_at=django_timezone.now() - timedelta(days=random.randint(1, 60)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 30))
)
reviews.append(review)
except Exception as e:
print(f"Error creating performance review for {employee.get_full_name()}: {e}")
continue
print(f"Created {len(reviews)} performance reviews")
return reviews
def create_training_records(employees):
"""Create training records for employees"""
records = []
active_employees = [emp for emp in employees if emp.employment_status == 'ACTIVE']
training_by_role = {
'ALL': ['Orientation', 'Fire Safety', 'Emergency Procedures', 'HIPAA Compliance', 'Patient Safety'],
'CLINICAL': ['Basic Life Support (BLS)', 'Infection Control', 'Medication Administration',
'Wound Care Management'],
'PHYSICIAN': ['Advanced Cardiac Life Support (ACLS)', 'Pediatric Advanced Life Support (PALS)'],
'NURSE': ['IV Therapy', 'Pain Management', 'Electronic Health Records'],
'ADMIN': ['Customer Service Excellence', 'Quality Improvement', 'Arabic Language'],
'SUPPORT': ['Safety Training', 'Equipment Operation', 'Cultural Sensitivity']
}
for employee in active_employees:
# Mandatory training for all employees
mandatory_training = training_by_role['ALL'].copy()
# Role-specific training
if employee.department and employee.department.department_type == 'CLINICAL':
mandatory_training.extend(training_by_role['CLINICAL'])
if 'Physician' in employee.job_title:
mandatory_training.extend(training_by_role['PHYSICIAN'])
elif 'Nurse' in employee.job_title:
mandatory_training.extend(training_by_role['NURSE'])
elif employee.department and employee.department.department_type == 'ADMINISTRATIVE':
mandatory_training.extend(training_by_role['ADMIN'])
else:
mandatory_training.extend(training_by_role['SUPPORT'])
# Add some random additional training
all_training = list(set(mandatory_training + random.sample(SAUDI_TRAINING_PROGRAMS, random.randint(2, 5))))
for training_name in all_training:
# Training date (within employment period)
days_since_hire = (django_timezone.now().date() - employee.hire_date).days
training_date = employee.hire_date + timedelta(days=random.randint(0, days_since_hire))
# Completion date (usually same day or within a week)
completion_date = training_date + timedelta(days=random.randint(0, 7))
# Training type
if training_name in ['Orientation']:
training_type = 'ORIENTATION'
elif training_name in ['Fire Safety', 'Emergency Procedures', 'HIPAA Compliance', 'Patient Safety']:
training_type = 'MANDATORY'
elif 'Certification' in training_name or any(cert in training_name for cert in ['BLS', 'ACLS', 'PALS']):
training_type = 'CERTIFICATION'
elif training_name in ['Safety Training', 'Infection Control']:
training_type = 'SAFETY'
else:
training_type = 'CONTINUING_ED'
# Duration based on training type
if training_type == 'CERTIFICATION':
duration = Decimal(str(random.randint(8, 16))) # 8-16 hours
elif training_type == 'MANDATORY':
duration = Decimal(str(random.randint(2, 8))) # 2-8 hours
else:
duration = Decimal(str(random.randint(1, 4))) # 1-4 hours
# Expiry date for certifications
expiry_date = None
if training_type == 'CERTIFICATION':
expiry_date = completion_date + timedelta(days=random.randint(365, 730)) # 1-2 years
status = random.choices(
['COMPLETED', 'IN_PROGRESS', 'SCHEDULED'],
weights=[80, 15, 5]
)[0]
# Score and pass status
if status == 'COMPLETED':
score = Decimal(str(random.randint(75, 100)))
passed = score >= 70
else:
score = None
passed = False
try:
record = TrainingRecord.objects.create(
employee=employee,
training_name=training_name,
training_description=f"Comprehensive {training_name.lower()} training program",
training_type=training_type,
training_provider=random.choice([
'Saudi Healthcare Training Institute',
'Ministry of Health Training Center',
'King Fahd Medical Training Academy',
'Internal Training Department'
]),
instructor=f"Dr. {random.choice(SAUDI_FIRST_NAMES_MALE + SAUDI_FIRST_NAMES_FEMALE)} {random.choice(SAUDI_LAST_NAMES)}",
training_date=training_date,
completion_date=completion_date if status == 'COMPLETED' else None,
expiry_date=expiry_date,
duration_hours=duration,
credits_earned=duration if status == 'COMPLETED' else Decimal('0.00'),
status=status,
score=score,
passed=passed,
certificate_number=f"CERT-{random.randint(100000, 999999)}" if status == 'COMPLETED' and training_type == 'CERTIFICATION' else None,
certification_body='Saudi Healthcare Certification Board' if training_type == 'CERTIFICATION' else None,
training_cost=Decimal(str(random.randint(500, 5000))),
notes=f"{training_name} training completed successfully" if status == 'COMPLETED' else f"{training_name} training in progress",
created_at=django_timezone.now() - timedelta(days=random.randint(1, 30)),
updated_at=django_timezone.now() - timedelta(days=random.randint(0, 15))
)
records.append(record)
except Exception as e:
print(f"Error creating training record for {employee.get_full_name()}: {e}")
continue
print(f"Created {len(records)} training records")
return records
def main():
"""Main function to generate all Saudi HR data"""
print("Starting Saudi Healthcare HR 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 departments
print("\n1. Creating Saudi Hospital Departments...")
departments = create_saudi_departments(tenants)
# Create employees
print("\n2. Creating Saudi Hospital Employees...")
employees = create_saudi_employees(tenants, departments, 120)
# Assign department heads and supervisors
print("\n3. Assigning Department Heads and Supervisors...")
assign_department_heads(departments, employees)
assign_supervisors(employees)
# Create schedules
print("\n4. Creating Employee Work Schedules...")
schedules = create_saudi_schedules(employees, 2)
# Create schedule assignments
print("\n5. Creating Schedule Assignments...")
assignments = create_schedule_assignments(schedules, 30)
# Create time entries
print("\n6. Creating Employee Time Entries...")
time_entries = create_time_entries(employees, 30)
# Create performance reviews
print("\n7. Creating Performance Reviews...")
reviews = create_performance_reviews(employees)
# Create training records
print("\n8. Creating Training Records...")
training_records = create_training_records(employees)
print(f"\n✅ Saudi Healthcare HR Data Generation Complete!")
print(f"📊 Summary:")
print(f" - Departments: {len(departments)}")
print(f" - Employees: {len(employees)}")
print(f" - Schedules: {len(schedules)}")
print(f" - Schedule Assignments: {len(assignments)}")
print(f" - Time Entries: {len(time_entries)}")
print(f" - Performance Reviews: {len(reviews)}")
print(f" - Training Records: {len(training_records)}")
# Department distribution
dept_counts = {}
for employee in employees:
if employee.department:
dept_type = employee.department.department_type
dept_counts[dept_type] = dept_counts.get(dept_type, 0) + 1
print(f"\n🏥 Employee Distribution by Department Type:")
for dept_type, count in dept_counts.items():
print(f" - {dept_type.title()}: {count}")
# Employment status distribution
status_counts = {}
for employee in employees:
status_counts[employee.employment_status] = status_counts.get(employee.employment_status, 0) + 1
print(f"\n👥 Employee Status Distribution:")
for status, count in status_counts.items():
print(f" - {status.title()}: {count}")
return {
'departments': departments,
'employees': employees,
'schedules': schedules,
'assignments': assignments,
'time_entries': time_entries,
'reviews': reviews,
'training_records': training_records
}
if __name__ == "__main__":
main()