""" Management command to seed staff data with bilingual support (English and Arabic) """ import random from django.core.management.base import BaseCommand, CommandError from django.db import transaction from apps.accounts.models import User from apps.organizations.models import Hospital, Department, Staff # Saudi names data - Paired to ensure English and Arabic correspond NAMES_MALE = [ {'en': 'Mohammed', 'ar': 'محمد'}, {'en': 'Ahmed', 'ar': 'أحمد'}, {'en': 'Abdullah', 'ar': 'عبدالله'}, {'en': 'Khalid', 'ar': 'خالد'}, {'en': 'Saud', 'ar': 'سعود'}, {'en': 'Fahd', 'ar': 'فهد'}, {'en': 'Abdulaziz', 'ar': 'عبدالعزيز'}, {'en': 'Sultan', 'ar': 'سلطان'}, {'en': 'Faisal', 'ar': 'فيصل'}, {'en': 'Omar', 'ar': 'عمر'}, {'en': 'Turki', 'ar': 'تركي'}, {'en': 'Nasser', 'ar': 'ناصر'}, {'en': 'Mishari', 'ar': 'مشاري'}, {'en': 'Abdulrahman', 'ar': 'عبدالرحمن'}, {'en': 'Yousef', 'ar': 'يوسف'}, {'en': 'Ali', 'ar': 'علي'} ] NAMES_FEMALE = [ {'en': 'Fatimah', 'ar': 'فاطمة'}, {'en': 'Aisha', 'ar': 'عائشة'}, {'en': 'Maryam', 'ar': 'مريم'}, {'en': 'Noura', 'ar': 'نورة'}, {'en': 'Sarah', 'ar': 'سارة'}, {'en': 'Hind', 'ar': 'هند'}, {'en': 'Latifa', 'ar': 'لطيفة'}, {'en': 'Mona', 'ar': 'منى'}, {'en': 'Reem', 'ar': 'ريم'}, {'en': 'Jawaher', 'ar': 'جواهر'}, {'en': 'Lina', 'ar': 'لينا'}, {'en': 'Dua', 'ar': 'دعاء'}, {'en': 'Maha', 'ar': 'مها'}, {'en': 'Jumanah', 'ar': 'جمانة'}, {'en': 'Rahaf', 'ar': 'رحاب'}, {'en': 'Samar', 'ar': 'سمر'} ] LAST_NAMES = [ {'en': 'Al-Otaibi', 'ar': 'العتيبي'}, {'en': 'Al-Dosari', 'ar': 'الدوسري'}, {'en': 'Al-Qahtani', 'ar': 'القحطاني'}, {'en': 'Al-Shammari', 'ar': 'الشمري'}, {'en': 'Al-Harbi', 'ar': 'الحربي'}, {'en': 'Al-Mutairi', 'ar': 'المطيري'}, {'en': 'Al-Anazi', 'ar': 'العنزي'}, {'en': 'Al-Zahrani', 'ar': 'الزهراني'}, {'en': 'Al-Ghamdi', 'ar': 'الغامدي'}, {'en': 'Al-Shehri', 'ar': 'الشهري'}, {'en': 'Al-Salem', 'ar': 'السالم'}, {'en': 'Al-Fahad', 'ar': 'الفالح'} ] # Specializations for physicians PHYSICIAN_SPECIALIZATIONS = [ 'Internal Medicine', 'General Surgery', 'Pediatrics', 'Obstetrics & Gynecology', 'Cardiology', 'Orthopedics', 'Neurology', 'Dermatology', 'Ophthalmology', 'ENT', 'Urology', 'Nephrology', 'Gastroenterology', 'Pulmonology', 'Endocrinology', 'Rheumatology', 'Hematology', 'Oncology', 'Psychiatry' ] # Job titles for nurses NURSE_JOB_TITLES = [ 'Registered Nurse', 'Senior Nurse', 'Nurse Practitioner', 'ICU Nurse', 'Emergency Nurse', 'Pediatric Nurse', 'Operating Room Nurse', 'Head Nurse' ] # Job titles for admin staff ADMIN_JOB_TITLES = [ 'Medical Receptionist', 'Administrative Assistant', 'Medical Records Clerk', 'Hospital Administrator', 'Department Secretary', 'Patient Services Representative', 'Front Desk Officer', 'Billing Specialist' ] class Command(BaseCommand): help = 'Seed staff data with bilingual support (English and Arabic)' def add_arguments(self, parser): parser.add_argument( '--hospital-code', type=str, help='Target hospital code (default: all hospitals)' ) parser.add_argument( '--count', type=int, default=10, help='Number of staff to create per type (default: 10)' ) parser.add_argument( '--physicians', type=int, default=10, help='Number of physicians to create (default: 10)' ) parser.add_argument( '--nurses', type=int, default=15, help='Number of nurses to create (default: 15)' ) parser.add_argument( '--admin-staff', type=int, default=5, help='Number of admin staff to create (default: 5)' ) parser.add_argument( '--create-users', action='store_true', help='Create user accounts for staff' ) parser.add_argument( '--clear', action='store_true', help='Clear existing staff first' ) parser.add_argument( '--dry-run', action='store_true', help='Preview without making changes' ) def handle(self, *args, **options): hospital_code = options['hospital_code'] count = options['count'] physicians_count = options['physicians'] nurses_count = options['nurses'] admin_staff_count = options['admin_staff'] create_users = options['create_users'] clear_existing = options['clear'] dry_run = options['dry_run'] self.stdout.write(f"\n{'='*60}") self.stdout.write("Staff Data Seeding Command") self.stdout.write(f"{'='*60}\n") with transaction.atomic(): # Get hospitals if hospital_code: hospitals = Hospital.objects.filter(code=hospital_code) if not hospitals.exists(): self.stdout.write( self.style.ERROR(f"Hospital with code '{hospital_code}' not found") ) return else: hospitals = Hospital.objects.filter(status='active') if not hospitals.exists(): self.stdout.write( self.style.ERROR("No hospitals found. Please create hospitals first.") ) return self.stdout.write( self.style.SUCCESS(f"Found {hospitals.count()} hospital(s) to seed staff") ) # Display configuration self.stdout.write("\nConfiguration:") self.stdout.write(f" Physicians per hospital: {physicians_count}") self.stdout.write(f" Nurses per hospital: {nurses_count}") self.stdout.write(f" Admin staff per hospital: {admin_staff_count}") self.stdout.write(f" Total staff per hospital: {physicians_count + nurses_count + admin_staff_count}") self.stdout.write(f" Create user accounts: {create_users}") self.stdout.write(f" Clear existing: {clear_existing}") self.stdout.write(f" Dry run: {dry_run}") # Get all departments for assignment all_departments = Department.objects.filter(status='active') if not all_departments.exists(): self.stdout.write( self.style.WARNING("\nNo departments found. Staff will be created without departments.") ) # Clear existing staff if requested if clear_existing: if dry_run: self.stdout.write( self.style.WARNING(f"\nWould delete {Staff.objects.count()} existing staff") ) else: deleted_count = Staff.objects.count() Staff.objects.all().delete() self.stdout.write( self.style.SUCCESS(f"\n✓ Deleted {deleted_count} existing staff") ) # Track created staff created_staff = [] # Seed physicians physicians = self.create_staff_type( hospitals=hospitals, departments=all_departments, staff_type=Staff.StaffType.PHYSICIAN, count=physicians_count, job_titles=PHYSICIAN_SPECIALIZATIONS, create_users=create_users, dry_run=dry_run ) created_staff.extend(physicians) # Seed nurses nurses = self.create_staff_type( hospitals=hospitals, departments=all_departments, staff_type=Staff.StaffType.NURSE, count=nurses_count, job_titles=NURSE_JOB_TITLES, create_users=create_users, dry_run=dry_run ) created_staff.extend(nurses) # Seed admin staff admins = self.create_staff_type( hospitals=hospitals, departments=all_departments, staff_type=Staff.StaffType.ADMIN, count=admin_staff_count, job_titles=ADMIN_JOB_TITLES, create_users=create_users, dry_run=dry_run ) created_staff.extend(admins) # Summary self.stdout.write("\n" + "="*60) self.stdout.write("Summary:") self.stdout.write(f" Physicians created: {len(physicians)}") self.stdout.write(f" Nurses created: {len(nurses)}") self.stdout.write(f" Admin staff created: {len(admins)}") self.stdout.write(f" Total staff created: {len(created_staff)}") self.stdout.write("="*60 + "\n") if dry_run: self.stdout.write(self.style.WARNING("DRY RUN: No changes were made\n")) else: self.stdout.write(self.style.SUCCESS("Staff seeding completed successfully!\n")) def create_staff_type(self, hospitals, departments, staff_type, count, job_titles, create_users, dry_run): """Create staff of a specific type""" created = [] staff_type_display = dict(Staff.StaffType.choices).get(staff_type, staff_type) self.stdout.write(f"\nSeeding {staff_type_display}...") for hospital in hospitals: # Get departments for this hospital hospital_depts = [d for d in departments if d.hospital == hospital] for i in range(count): # Generate names (paired to ensure English and Arabic correspond) if staff_type == Staff.StaffType.NURSE: # More female nurses (70%) if random.random() < 0.7: first_name = random.choice(NAMES_FEMALE) else: first_name = random.choice(NAMES_MALE) else: # Physicians and admin staff are mostly male (60%) if random.random() < 0.6: first_name = random.choice(NAMES_MALE) else: first_name = random.choice(NAMES_FEMALE) last_name = random.choice(LAST_NAMES) job_title = random.choice(job_titles) # Generate employee ID employee_id = self.generate_employee_id(hospital.code, staff_type) # Generate license number for physicians license_number = None if staff_type == Staff.StaffType.PHYSICIAN: license_number = self.generate_license_number() # Select department (optional) department = random.choice(hospital_depts) if hospital_depts and random.random() > 0.1 else None # Specialization for all staff types (required field) if staff_type == Staff.StaffType.PHYSICIAN: specialization = job_title elif staff_type == Staff.StaffType.NURSE: specialization = random.choice(['General Nursing', 'Patient Care', 'Clinical Nursing', 'Nursing']) elif staff_type == Staff.StaffType.ADMIN: specialization = random.choice(['Administration', 'Hospital Operations', 'Medical Administration', 'Healthcare Admin']) else: specialization = 'General Staff' if dry_run: self.stdout.write( f" Would create: {first_name['en']} {last_name['en']} ({first_name['ar']} {last_name['ar']}) - {job_title}" ) created.append({'type': staff_type_display, 'name': f"{first_name['en']} {last_name['en']}"}) else: # Create staff staff = Staff.objects.create( first_name=first_name['en'], last_name=last_name['en'], first_name_ar=first_name['ar'], last_name_ar=last_name['ar'], staff_type=staff_type, job_title=job_title, license_number=license_number, specialization=specialization, employee_id=employee_id, hospital=hospital, department=department, status='active' ) # Create user account if requested if create_users: self.create_user_for_staff(staff) created.append(staff) self.stdout.write( self.style.SUCCESS(f" ✓ Created {len([s for s in created if isinstance(s, Staff)])} {staff_type_display}") ) return created def generate_employee_id(self, hospital_code, staff_type): """Generate unique employee ID""" prefix = { Staff.StaffType.PHYSICIAN: 'DR', Staff.StaffType.NURSE: 'RN', Staff.StaffType.ADMIN: 'ADM', Staff.StaffType.OTHER: 'STF' }.get(staff_type, 'STF') random_num = random.randint(10000, 99999) return f"{prefix}-{hospital_code}-{random_num}" def generate_license_number(self): """Generate unique license number""" random_num = random.randint(1000000, 9999999) return f"MOH-LIC-{random_num}" def create_user_for_staff(self, staff): """Create a user account for staff""" username = self.generate_username(staff) # Check if user already exists if User.objects.filter(username=username).exists(): return # Generate email email = f"{staff.first_name.lower()}.{staff.last_name.lower()}@{staff.hospital.code.lower()}.sa" # Check if email exists if User.objects.filter(email=email).exists(): email = f"{username}@{staff.hospital.code.lower()}.sa" # Create user user = User.objects.create_user( username=username, email=email, first_name=staff.first_name, last_name=staff.last_name, password='password123', # Default password employee_id=staff.employee_id, hospital=staff.hospital, department=staff.department, language='ar' if random.random() < 0.5 else 'en', # Random language preference is_staff=True, ) # Link staff to user staff.user = user staff.save(update_fields=['user']) self.stdout.write( self.style.SUCCESS(f" ✓ Created user: {username}") ) def generate_username(self, staff): """Generate unique username""" base = f"{staff.first_name.lower()}.{staff.last_name.lower()}" username = base # Add suffix if username exists counter = 1 while User.objects.filter(username=username).exists(): username = f"{base}{counter}" counter += 1 return username