"""Helpers for identifying official immigration source domains.""" from __future__ import annotations from urllib.parse import urlparse OFFICIAL_DOMAIN_HINTS: dict[str, list[str]] = { "AU": ["homeaffairs.gov.au", "immi.homeaffairs.gov.au"], "CA": ["canada.ca", "cic.gc.ca"], "DE": ["make-it-in-germany.com", "bamf.de", "auswaertiges-amt.de"], "DK": ["nyidanmark.dk"], "ES": ["inclusion.gob.es", "exteriores.gob.es"], "FI": ["migri.fi"], "FR": ["france-visas.gouv.fr", "service-public.fr"], "GB": ["gov.uk"], "IE": ["irishimmigration.ie", "enterprise.gov.ie"], "JP": ["isa.go.jp", "mofa.go.jp"], "NL": ["ind.nl"], "NO": ["udi.no"], "NZ": ["immigration.govt.nz"], "PT": ["aima.gov.pt", "eportugal.gov.pt", "vistos.mne.gov.pt"], "SE": ["migrationsverket.se"], "SG": ["ica.gov.sg", "mom.gov.sg"], "US": ["uscis.gov", "travel.state.gov"], } COUNTRY_ALIASES: dict[str, str] = { "australia": "AU", "canada": "CA", "denmark": "DK", "finland": "FI", "france": "FR", "germany": "DE", "ireland": "IE", "japan": "JP", "netherlands": "NL", "new zealand": "NZ", "norway": "NO", "portugal": "PT", "singapore": "SG", "spain": "ES", "sweden": "SE", "uk": "GB", "united kingdom": "GB", "united states": "US", "usa": "US", } UNOFFICIAL_CONTEXT_TERMS = ( "blog", "forum", "reddit", "quora", "lawfirm", "law-firm", "immigrationlaw", "relocation", "movingto", ) def domain_from_url(url: str) -> str: parsed = urlparse(url or "") host = parsed.netloc.lower().split("@")[-1].split(":")[0] if host.startswith("www."): host = host[4:] return host def is_domain_match(domain: str, official_domain: str) -> bool: normalized = official_domain.lower() return domain == normalized or domain.endswith(f".{normalized}") def hints_for_country(country: str | None) -> list[str]: if not country: return [] normalized = country.strip().upper() return OFFICIAL_DOMAIN_HINTS.get(normalized, []) def infer_country_codes(text: str, country: str | None = None) -> list[str]: codes: list[str] = [] if country: normalized = country.strip().upper() if normalized in OFFICIAL_DOMAIN_HINTS: codes.append(normalized) lowered = (text or "").lower() for alias, code in COUNTRY_ALIASES.items(): if alias in lowered and code not in codes: codes.append(code) return codes def infer_official_domains( text: str, country: str | None = None, *, max_domains: int = 8, ) -> list[str]: domains: list[str] = [] for code in infer_country_codes(text, country): for domain in OFFICIAL_DOMAIN_HINTS.get(code, []): if domain not in domains: domains.append(domain) if len(domains) >= max_domains: return domains return domains def classify_source(url: str) -> dict[str, object]: domain = domain_from_url(url) known_official = [ official_domain for domains in OFFICIAL_DOMAIN_HINTS.values() for official_domain in domains if is_domain_match(domain, official_domain) ] if known_official: return { "domain": domain, "type": "official_government", "is_official": True, "reason": f"Matches official domain hint {known_official[0]}", } if domain.endswith(".gov") or ".gov." in domain or domain.endswith(".gouv.fr"): return { "domain": domain, "type": "government", "is_official": True, "reason": "Government domain pattern", } if "embassy" in domain or "consulate" in domain: return { "domain": domain, "type": "embassy_or_consulate", "is_official": True, "reason": "Embassy or consulate domain pattern", } if any(term in domain for term in UNOFFICIAL_CONTEXT_TERMS): return { "domain": domain, "type": "unofficial_context", "is_official": False, "reason": "Likely blog, forum, legal-service, or relocation context", } return { "domain": domain, "type": "unknown", "is_official": False, "reason": "No official-domain signal detected", }