Spaces:
Running
Running
| from __future__ import annotations | |
| import re | |
| import unicodedata | |
| from pathlib import Path | |
| from typing import Any | |
| import yaml | |
| PROGRAM_OVERRIDES_PATH = Path("configs/program_overrides.yaml") | |
| def load_program_faculty_overrides(path: Path = PROGRAM_OVERRIDES_PATH) -> dict[str, str]: | |
| if not path.exists(): | |
| return MANUAL_PROGRAM_FACULTY | |
| with path.open("r", encoding="utf-8") as f: | |
| config = yaml.safe_load(f) or {} | |
| overrides = config.get("program_faculty_overrides") or {} | |
| if not isinstance(overrides, dict): | |
| return MANUAL_PROGRAM_FACULTY | |
| return {fold_text(key): str(value) for key, value in overrides.items()} | |
| def load_program_name_overrides(path: Path = PROGRAM_OVERRIDES_PATH) -> dict[str, str]: | |
| if not path.exists(): | |
| return {} | |
| with path.open("r", encoding="utf-8") as f: | |
| config = yaml.safe_load(f) or {} | |
| overrides = config.get("program_name_overrides") or {} | |
| if not isinstance(overrides, dict): | |
| return {} | |
| return {fold_text(key): str(value) for key, value in overrides.items()} | |
| MANUAL_PROGRAM_FACULTY = { | |
| "cong nghe giao duc": "Khoa Công nghệ Thông tin", | |
| "cong nghe thong tin": "Khoa Công nghệ Thông tin", | |
| "dia ly hoc": "Khoa Địa lý", | |
| "du lich": "Khoa Địa lý", | |
| "giao duc chinh tri": "Khoa Giáo dục Chính trị", | |
| "giao duc cong dan": "Khoa Giáo dục Chính trị", | |
| "giao duc dac biet": "Khoa Giáo dục Đặc biệt", | |
| "giao duc hoc": "Khoa Khoa học Giáo dục", | |
| "giao duc mam non trinh do cao dang va dai hoc": "Khoa Giáo dục Mầm non", | |
| "giao duc quoc phong an ninh": "Khoa Giáo dục Quốc phòng", | |
| "giao duc the chat": "Khoa Giáo dục Thể chất", | |
| "giao duc tieu hoc": "Khoa Giáo dục Tiểu học", | |
| "hoa hoc": "Khoa Hóa học", | |
| "ngon ngu han quoc": "Khoa Tiếng Hàn Quốc", | |
| "ngon ngu nhat": "Khoa Tiếng Nhật", | |
| "quan ly giao duc": "Khoa Khoa học Giáo dục", | |
| "sinh hoc ung dung": "Khoa Sinh học", | |
| "su pham cong nghe": "Khoa Vật lý", | |
| "su pham dia ly": "Khoa Địa lý", | |
| "su pham lich su dia ly": "Khoa Địa lý", | |
| "su pham hoa hoc": "Khoa Hóa học", | |
| "su pham sinh hoc": "Khoa Sinh học", | |
| "su pham tin hoc": "Khoa Công nghệ Thông tin", | |
| "su pham toan hoc": "Khoa Toán – Tin học", | |
| "su pham toan hoc tieng viet va song ngu viet anh": "Khoa Toán – Tin học", | |
| "su pham vat ly": "Khoa Vật lý", | |
| "tam ly hoc": "Khoa Tâm lý học", | |
| "tam ly hoc giao duc": "Khoa Tâm lý học", | |
| "toan ung dung": "Khoa Toán – Tin học", | |
| "vat ly hoc": "Khoa Vật lý", | |
| } | |
| def fold_text(text: str | None) -> str: | |
| text = str(text or "").lower().replace("đ", "d").replace("Đ", "D") | |
| decomposed = unicodedata.normalize("NFD", text) | |
| text = "".join(ch for ch in decomposed if unicodedata.category(ch) != "Mn") | |
| text = re.sub(r"[^a-z0-9]+", " ", text) | |
| return re.sub(r"\s+", " ", text).strip() | |
| FACULTY_NAME_ALIASES = { | |
| "khoa tam li hoc": "Khoa Tâm lý học", | |
| "khoa tam ly hoc": "Khoa Tâm lý học", | |
| } | |
| def clean_faculty_name(name: str | None) -> str: | |
| cleaned = re.sub(r"^\d+\.\s*", "", str(name or "")).strip() | |
| return FACULTY_NAME_ALIASES.get(fold_text(cleaned), cleaned) | |
| def resolve_faculty_name( | |
| candidate: str, | |
| faculty_records: list[dict[str, Any]], | |
| ) -> str: | |
| folded_candidate = fold_text(clean_faculty_name(candidate)) | |
| for faculty in faculty_records: | |
| faculty_name = clean_faculty_name(faculty.get("faculty_or_unit_name")) | |
| if fold_text(faculty_name) == folded_candidate: | |
| return faculty_name | |
| return clean_faculty_name(candidate) | |
| def enrich_program_faculty_names( | |
| program_records: list[dict[str, Any]], | |
| faculty_records: list[dict[str, Any]], | |
| ) -> list[dict[str, Any]]: | |
| """Bổ sung khoa phụ trách cho ngành khi sổ tay không ghi trực tiếp cạnh ngành.""" | |
| program_name_overrides = load_program_name_overrides() | |
| for program in program_records: | |
| program_key = fold_text(program.get("program_name")) | |
| canonical_name = program_name_overrides.get(program_key) | |
| if canonical_name and canonical_name != program.get("program_name"): | |
| program["raw_program_name"] = program.get("program_name") | |
| program["program_name"] = canonical_name | |
| program["program_name_source"] = "canonical_program_name_rule" | |
| if program.get("faculty_name"): | |
| program["faculty_name"] = clean_faculty_name(program.get("faculty_name")) | |
| program_faculty_overrides = load_program_faculty_overrides() | |
| known_by_program: dict[str, str] = {} | |
| for program in program_records: | |
| faculty_name = program.get("faculty_name") | |
| if faculty_name: | |
| known_by_program.setdefault( | |
| fold_text(program.get("program_name")), | |
| clean_faculty_name(faculty_name), | |
| ) | |
| for program in program_records: | |
| if program.get("faculty_name"): | |
| continue | |
| program_key = fold_text(program.get("program_name")) | |
| candidate = known_by_program.get(program_key) or program_faculty_overrides.get( | |
| program_key | |
| ) | |
| if not candidate: | |
| continue | |
| program["faculty_name"] = resolve_faculty_name(candidate, faculty_records) | |
| program["faculty_name_source"] = ( | |
| "matched_existing_program" | |
| if program_key in known_by_program | |
| else "manual_program_faculty_rule" | |
| ) | |
| return program_records | |