HH/apps/organizations/management/commands/set_department_arabic_names.py

176 lines
7.0 KiB
Python

"""
Populate the `name_ar` (Arabic name) field on Department rows from a
hard-coded English -> Arabic mapping.
The mapping covers every department name present in the database. Keys are
normalized (case-insensitive, trailing "department" stripped, whitespace
collapsed) so that "Surgery", "Surgery Department" and "surgery department"
all resolve to the same Arabic value.
Arabic terms are cross-referenced with
apps/complaints/management/commands/arabic_dept_mapping.py to keep the
project's Arabic vocabulary consistent.
Usage:
python manage.py set_department_arabic_names # fill empties
python manage.py set_department_arabic_names --dry-run # preview only
python manage.py set_department_arabic_names --overwrite # re-translate all
python manage.py set_department_arabic_names --hospital-code HH-N
"""
import re
from django.core.management.base import BaseCommand
from django.db import transaction
from apps.organizations.models import Department
ENGLISH_TO_ARABIC = {
"laboratory": "قسم المختبر",
"icu": "وحدة العناية المركزة",
"security": "قسم الأمن",
"maintenance": "قسم الصيانة",
"housekeeping": "قسم النظافة",
"medical reports": "قسم التقارير الطبية",
"reception": "الاستقبال",
"administration": "الإدارة",
"surgery": "قسم الجراحة",
"cardiology": "قسم القلب",
"orthopedics": "قسم جراحة العظام",
"orthopedic": "قسم جراحة العظام",
"nursing": "قسم التمريض",
"pharmacy": "قسم الصيدلية",
"pediatrics": "قسم الأطفال",
"pediatric": "قسم الأطفال",
"emergency": "قسم الطوارئ",
"anesthesia": "قسم التخدير",
"biomedical": "قسم الهندسة الطبية الحيوية",
"contact center": "قسم مركز الاتصال",
"critical care": "قسم العناية المركزة",
"dental": "قسم الأسنان",
"dermatology": "قسم الجلدية",
"emergency administrative": "إدارة الطوارئ",
"emergency medicine": "قسم طب الطوارئ",
"executive administration": "الإدارة التنفيذية",
"facility management & maintenance": "قسم إدارة المرافق والصيانة",
"financial collection & claims": "قسم المالية والتحصيل والمطالبات",
"food services": "قسم خدمات الطعام",
"hr": "قسم الموارد البشرية",
"housekeeping & hospitality": "قسم النظافة والضيافة",
"ivf": "قسم أطفال الأنابيب",
"infection control": "قسم مكافحة العدوى",
"information technology": "قسم تقنية المعلومات",
"inpatient": "قسم التنويم",
"internal medicine": "قسم الباطنية",
"laundry": "قسم الغسيل",
"medical administration": "الإدارة الطبية",
"medical ancillary services": "قسم الخدمات الطبية المساندة",
"medical approvals": "قسم الموافقات الطبية",
"medical records": "قسم السجلات الطبية",
"oncology": "قسم الأورام",
"obstetrics & gynecology": "قسم النساء والولادة",
"operating rooms": "قسم غرف العمليات",
"operating rooms (or)": "قسم غرف العمليات",
"ophthalmology": "قسم العيون",
"outpatient": "قسم العيادات الخارجية",
"patient affairs": "قسم علاقات المرضى",
"patient experience": "قسم تجربة المريض",
"radiology": "قسم الأشعة",
"support services": "قسم الخدمات المساندة",
}
_DEPT_SUFFIX_RE = re.compile(r"\s+department$", re.IGNORECASE)
_WS_RE = re.compile(r"\s+")
def _normalize_en(value: str) -> str:
"""Normalize an English department name for mapping lookup."""
if not value:
return ""
value = _DEPT_SUFFIX_RE.sub("", str(value).strip())
value = _WS_RE.sub(" ", value).strip()
return value.lower()
class Command(BaseCommand):
help = "Populate Department.name_ar from a hard-coded English -> Arabic mapping"
def add_arguments(self, parser):
parser.add_argument(
"--dry-run",
action="store_true",
help="Preview changes without writing to the database",
)
parser.add_argument(
"--overwrite",
action="store_true",
help="Re-translate even departments that already have name_ar set",
)
parser.add_argument(
"--hospital-code",
type=str,
help="Limit to a single hospital code (default: all hospitals)",
)
def handle(self, *args, **options):
dry_run = options["dry_run"]
overwrite = options["overwrite"]
hospital_code = options["hospital_code"]
qs = Department.objects.select_related("hospital").order_by("hospital__code", "name_en", "name")
if hospital_code:
qs = qs.filter(hospital__code=hospital_code)
if not overwrite:
qs = qs.filter(name_ar="")
self.stdout.write(self.style.MIGRATE_HEADING(
f"Department Arabic names (hospital={hospital_code or 'ALL'}, "
f"overwrite={overwrite}, dry_run={dry_run})"
))
updated = 0
skipped = 0
unmapped = []
with transaction.atomic():
for dept in qs.iterator():
key = _normalize_en(dept.name_en) or _normalize_en(dept.name)
arabic = ENGLISH_TO_ARABIC.get(key)
if not arabic:
unmapped.append((dept, key))
skipped += 1
continue
if dept.name_ar == arabic:
skipped += 1
continue
label = f"[{dept.hospital.code if dept.hospital else '-'}] {dept.code}: " \
f"{dept.name_en or dept.name!r}"
if dry_run:
self.stdout.write(f" ~ {label} -> {arabic}")
else:
dept.name_ar = arabic
dept.save(update_fields=["name_ar"])
self.stdout.write(self.style.SUCCESS(f" + {label} -> {arabic}"))
updated += 1
self.stdout.write("")
self.stdout.write(self.style.SUCCESS(
f"Done: {updated} {'would be ' if dry_run else ''}updated, {skipped} skipped"
))
if unmapped:
self.stdout.write(self.style.WARNING(
f"\n{len(unmapped)} department(s) had no mapping (left unchanged):"
))
for dept, key in unmapped:
self.stdout.write(self.style.WARNING(
f" - [{dept.hospital.code if dept.hospital else '-'}] {dept.code}: "
f"{dept.name_en or dept.name!r} (key={key!r})"
))
self.stdout.write(self.style.WARNING(
"\nAdd the missing keys to ENGLISH_TO_ARABIC and re-run."
))