""" Management command to assign managers (report_to) to staff members. This command assigns department heads as managers for staff in their department. For staff without a department head, it assigns the first available manager. """ from django.core.management.base import BaseCommand from django.db import transaction from apps.organizations.models import Staff, Department class Command(BaseCommand): help = 'Assign managers (report_to) to staff members who do not have one assigned' def add_arguments(self, parser): parser.add_argument( '--dry-run', action='store_true', dest='dry_run', default=False, help='Show what would be done without making changes', ) parser.add_argument( '--hospital-id', dest='hospital_id', default=None, help='Only process staff for a specific hospital ID', ) def handle(self, *args, **options): dry_run = options['dry_run'] hospital_id = options['hospital_id'] # Get staff without managers staff_queryset = Staff.objects.filter(report_to__isnull=True, status='active') if hospital_id: staff_queryset = staff_queryset.filter(hospital_id=hospital_id) staff_without_managers = staff_queryset.select_related('department', 'hospital') if not staff_without_managers.exists(): self.stdout.write(self.style.SUCCESS('All staff members already have managers assigned.')) return self.stdout.write(f'Found {staff_without_managers.count()} staff members without managers.') assigned_count = 0 skipped_count = 0 for staff in staff_without_managers: manager = self._find_manager_for_staff(staff) if manager: if dry_run: self.stdout.write(f' [DRY RUN] Would assign: {staff} -> manager: {manager}') else: staff.report_to = manager staff.save(update_fields=['report_to']) self.stdout.write(self.style.SUCCESS(f' Assigned: {staff} -> manager: {manager}')) assigned_count += 1 else: self.stdout.write(self.style.WARNING(f' No manager found for: {staff} (Dept: {staff.department})')) skipped_count += 1 if dry_run: self.stdout.write(self.style.WARNING(f'\n[DRY RUN] Would assign {assigned_count} managers, skip {skipped_count}')) else: self.stdout.write(self.style.SUCCESS(f'\nAssigned managers to {assigned_count} staff members.')) if skipped_count > 0: self.stdout.write(self.style.WARNING(f'Could not find managers for {skipped_count} staff members.')) def _find_manager_for_staff(self, staff): """Find an appropriate manager for a staff member.""" # Strategy 1: Find another staff member in the same department who has people reporting to them if staff.department: dept_managers = Staff.objects.filter( department=staff.department, status='active', direct_reports__isnull=False ).exclude(id=staff.id).distinct() if dept_managers.exists(): return dept_managers.first() # Strategy 2: Find any staff member with a higher job title in the same department # Look for staff with "Manager", "Director", "Head", "Chief", "Supervisor" in job title manager_titles = ['manager', 'director', 'head', 'chief', 'supervisor', 'lead', 'senior'] for title in manager_titles: potential_managers = Staff.objects.filter( department=staff.department, status='active', job_title__icontains=title ).exclude(id=staff.id) if potential_managers.exists(): return potential_managers.first() # Strategy 3: Find any manager in the same hospital hospital_managers = Staff.objects.filter( hospital=staff.hospital, status='active', direct_reports__isnull=False ).exclude(id=staff.id).distinct() if hospital_managers.exists(): return hospital_managers.first() # Strategy 4: Find any senior staff in the same hospital manager_titles = ['manager', 'director', 'head', 'chief', 'supervisor', 'lead'] for title in manager_titles: potential_managers = Staff.objects.filter( hospital=staff.hospital, status='active', job_title__icontains=title ).exclude(id=staff.id) if potential_managers.exists(): return potential_managers.first() return None