teqo/src/lib/municipalityNameAliases.ts
Francisco Ignácio Fontoura Solla 4806c417c8 Engineering Pass 3: consolidate patterns and lock them with guards.
Fold duplicated campanha access, loaders, list/filter/form shells, and scripts into shared modules; reorganize utilities by domain; stabilize e2e; and document prevention-first audit workflow so the same smells cannot creep back in.
2026-07-29 06:11:29 -03:00

63 lines
2.5 KiB
TypeScript

import { bahiaMunicipalities } from '@/lib/bahiaTerritories'
import { normalizeSearchPhrase } from '@/lib/wordStartFilter'
/**
* THE municipality-name resolution fold (Pass 3 P3-H): variant spellings seen
* in the wild (TSE open-data NM_MUNICIPIO across 2014/2018/2022, hand-typed
* CSVs) → canonical `bahiaTerritories` name. This table used to live inside
* the TSE pipeline, so the supporter import rejected 4 of the 5 spellings the
* campaign had already reconciled — a latent bug for any CSV digitized from
* TSE documents.
*
* Keys are raw spellings (data); both resolution paths normalize with
* `normalizeSearchPhrase` (the strictly strongest normalizer: accent-fold,
* case-fold, punctuation-to-space), so "DIAS D ÁVILA" and "Dias d'Ávila" hit
* the same entry.
*/
export const MUNICIPALITY_NAME_ALIASES: ReadonlyArray<
readonly [variant: string, canonical: string]
> = [
// TSE: CAMACÃ → canonical Camacan
['CAMACÃ', 'Camacan'],
// TSE: "DIAS D ÁVILA" (space, no apostrophe) → Dias d'Ávila
['DIAS D ÁVILA', "Dias d'Ávila"],
// TSE: MUQUÉM DO SÃO FRANCISCO → Muquém de São Francisco
['MUQUÉM DO SÃO FRANCISCO', 'Muquém de São Francisco'],
// TSE: SANTA TEREZINHA → Santa Teresinha
['SANTA TEREZINHA', 'Santa Teresinha'],
// TSE 2018 votacao_candidato_munzona: QUINJINGUE (typo) → canonical Quijingue
['QUINJINGUE', 'Quijingue'],
]
const canonicalMunicipalityBySearchValue = new Map(
bahiaMunicipalities.map((name) => [normalizeSearchPhrase(name), name] as const),
)
for (const [, canonical] of MUNICIPALITY_NAME_ALIASES) {
if (![...canonicalMunicipalityBySearchValue.values()].includes(canonical)) {
throw new Error(`Municipality alias points to unknown canonical municipality: ${canonical}`)
}
}
const aliasBySearchValue = new Map(
MUNICIPALITY_NAME_ALIASES.map(
([variant, canonical]) => [normalizeSearchPhrase(variant), canonical] as const,
),
)
/**
* Resolve any known spelling (canonical or curated variant) to the canonical
* municipality name. Returns null when unknown — each caller keeps its own
* error policy (the TSE pipeline throws, the supporter import row-fails).
*/
export const resolveMunicipalityName = (value: string | undefined | null): string | null => {
if (typeof value !== 'string') return null
const trimmed = value.trim()
if (!trimmed) return null
const searchValue = normalizeSearchPhrase(trimmed)
return (
canonicalMunicipalityBySearchValue.get(searchValue) ??
aliasBySearchValue.get(searchValue) ??
null
)
}