HH/apps/organizations/management/commands/assign_staff_managers.py
2026-02-22 08:35:53 +03:00

123 lines
5.0 KiB
Python

"""
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