from __future__ import annotations import re from apps.jobs.services.normalization import normalize_token SKILL_ALIASES: dict[str, tuple[str, ...]] = { "microsoft 365": ("m365", "office 365", "o365"), "microsoft teams": ("ms teams",), "entra id": ("azure active directory", "azure ad"), "intune": ("microsoft intune", "endpoint manager"), "autopilot": ("windows autopilot",), "group policy": ("group policy object", "gpo"), "dynamics 365": ("microsoft dynamics 365", "d365"), "microsoft copilot": ("copilot for microsoft 365", "m365 copilot"), "microsoft defender": ("defender for endpoint", "microsoft defender for endpoint"), "microsoft sentinel": ("azure sentinel", "sentinel siem"), "conditional access": ("voorwaardelijke toegang",), "networking": ("network", "netwerk", "lan", "wan"), "routing": ("routering",), "switching": ("network switches", "switches"), "sd-wan": ("sd wan",), "firewalls": ("firewall",), "fortinet": ("fortigate",), "palo alto": ("palo alto networks",), "wi-fi": ("wifi", "wireless"), "backup and restore": ("backup", "back-up", "restore"), "disaster recovery": ("business continuity", "bcp", "dr plan"), "monitoring": ("infrastructure monitoring", "system monitoring"), "observability": ("telemetry", "tracing"), "high availability": ("hoogbeschikbaarheid", "ha architecture"), "vmware": ("vsphere", "esxi"), "ci/cd": ("continuous integration", "continuous delivery", "ci cd"), "deployment": ("deployments", "software deployment", "uitrol"), "reliability": ("platform reliability", "site reliability", "sre"), "scalability": ("scalable", "schaalbaarheid"), "voip": ("voice over ip", "telefonie", "telephony", "3cx", "innovaphone"), "teams telephony": ("teams phone", "teams telefonie", "teams voice"), "onsite support": ("on-site support", "support op locatie", "field support"), "second line support": ("second-line support", "2nd line", "tweedelijnssupport"), "installations": ("installatie", "installaties", "roll-out", "rollout"), "migrations": ("migratie", "migraties"), "technical documentation": ("technische documentatie",), "customer support": ("klantondersteuning", "user support"), "incident management": ("incidentbeheer", "incident response", "escalations"), "problem management": ("probleembeheer", "root cause analysis"), "change management": ("wijzigingsbeheer", "organizational change"), "itil": ("itil 4", "it service management"), "servicenow": ("service now",), "jira service management": ("jira service desk", "jsm"), "digital workplace": ("digitale werkplek",), "m365 governance": ("microsoft 365 governance", "office 365 governance"), "document management": ("documentbeheer", "document management system", "dms"), "data governance": ("data governance", "datagovernance"), "user adoption": ("gebruikersadoptie", "technology adoption"), "training and workshops": ("user training", "workshops", "training geven"), "customer experience": ("customer satisfaction", "csat", "nps"), } def contains_term(text: str, term: str, *, allow_plural: bool = False) -> bool: normalized_term = normalize_token(term) if not normalized_term: return False pattern = re.escape(normalized_term).replace(r"\ ", r"[\s/_-]+") if allow_plural and normalized_term[-1].isalpha() and not normalized_term.endswith("s"): pattern += "s?" return re.search(rf"(? tuple[str, ...]: normalized = normalize_token(skill) return (normalized, *SKILL_ALIASES.get(normalized, ())) def skill_is_present(skill: str, text: str) -> bool: return any(contains_term(text, candidate) for candidate in skill_terms(skill)) def canonical_skill_key(value: str, catalog_keys: set[str]) -> str | None: normalized = normalize_token(value) if normalized in catalog_keys: return normalized for key in catalog_keys: if normalized in {normalize_token(term) for term in SKILL_ALIASES.get(key, ())}: return key return None