teqo/src/lib/bahiaElectionAggregates.ts
Francisco Ignácio Fontoura Solla 0ef4ddac1d E10 /simplify: remove superficie morta, unifica o parser de enum repetivel e reusa o ranking A11
- campoVotes (sem leitor) e classification.fieldHeadroom (duplicava o fator field) removidos
- formatTerritorialClassWhy passa a ser mesmo a fonte unica do slice(0,2) e do fallback
  "sem serie", eliminando o sentinela de string vazia nas duas superficies de detalhe
- classe territorial vira um so componente com layout table|card, como os vizinhos
- parseExhaustiveEnumParam em campaignListUrl cobre trend e class (o includes era morto:
  allParamValues ja deduplica)
- bloco central caminha o computeVoteRankByYear memoizado do A11 em vez de reordenar
  os 435 slugs por conta propria
- comentarios corrigidos onde afirmavam o contrario do codigo (CampaignCellTooltip e a
  regra do sem_base por superficie)
2026-07-25 22:33:16 -03:00

110 lines
4.4 KiB
TypeScript

import artifactJson from '@/lib/electionAggregates/bahia-federal-baseline.json'
/**
* Committed per-municipality election baseline (immutable TSE 2014/2018/2022):
* Solla nominal votes + federal T1 valid votes, pre-cut by the same
* `municipalityCatalog` geography the runtime uses (Salvador split by zone,
* every other municipality whole — Camaçari included).
*
* v2 (E8 "conta da cadeira"): adds the curated campo-parties federal nominal
* votes, federal T1 turnout/blank/null tally, and 2022 majoritarian
* (presidente/governador #13) votes+tally used for "teto do campo",
* "share intracampo" and roll-off. 2022-only for the majoritarian slice by
* design (E8's formulas anchor the field ceiling and roll-off to the single
* most recent majoritarian race) — the DB has majoritária seeded for
* 2014/2018 too (`pnpm db:seed:tse`, since 2026-07-24), but it isn't cut into
* this artifact; a future consumer that needs it extends `majoritarian2022`
* into a `majoritarianByYear` map.
*
* Generated by `pnpm build:election-aggregates` from the locally seeded TSE
* data — never computed at build or request time (see the script header for
* the rationale). A snapshot test pins artifact keys ↔ catalog slugs.
*/
/** Turnout/valid/blank/null tally cell (`municipalityElectoralBaseline.ts`'s `OfficeTallyCell` is this same type). */
export type FederalBaselineTallyCell = {
comparecimento: number
votosValidos: number
votosBranco: number
votosNulo: number
}
/** One 2022 majoritarian office (presidente/governador #13): votes + tally. */
type MajoritarianOfficeCell = FederalBaselineTallyCell & {
votes: number
}
export type MunicipalityFederalBaseline = {
/** TSE year (as string) → Solla nominal votes inside the municipality geography. */
votesByYear: Record<string, number>
/** TSE year (as string) → federal T1 valid votes inside the municipality geography. */
validVotesByYear: Record<string, number>
/** TSE year (as string) → curated campo-parties (`campoParties.ts`) federal T1 nominal votes. */
campoFederalVotesByYear: Record<string, number>
/** TSE year (as string) → federal T1 turnout/valid/blank/null tally. */
federalTallyByYear: Record<string, FederalBaselineTallyCell>
/** 2022-only majoritarian tickets (absent for older artifacts / unknown slugs). */
majoritarian2022?: {
president: MajoritarianOfficeCell
governor: MajoritarianOfficeCell
}
}
type FederalBaselineArtifact = {
provenance: string
candidateNumber: number
candidateName: string
years: number[]
municipalities: Record<string, MunicipalityFederalBaseline>
}
const artifact = artifactJson as FederalBaselineArtifact
export const FEDERAL_BASELINE_CANDIDATE_NUMBER = artifact.candidateNumber
export const federalBaselineMunicipalitySlugs = (): string[] => Object.keys(artifact.municipalities)
const EMPTY_BASELINE: MunicipalityFederalBaseline = {
votesByYear: {},
validVotesByYear: {},
campoFederalVotesByYear: {},
federalTallyByYear: {},
}
/**
* Baseline for one catalog municipality slug. Unknown slugs return an empty
* baseline (renders as 0 votes) — the artifact↔catalog snapshot test is the
* guard against silent drift.
*/
export const getMunicipalityFederalBaseline = (slug: string): MunicipalityFederalBaseline =>
artifact.municipalities[slug] ?? EMPTY_BASELINE
/** Statewide sums of the artifact for one year — the denominators of every relative reading. */
type StatewideFederalTotals = {
/** Candidate's own nominal votes across every catalog municipality. */
ownVotes: number
/** Federal T1 valid votes across every catalog municipality. */
validVotes: number
}
const statewideTotalsCache = new Map<number, StatewideFederalTotals>()
/**
* The artifact stores no statewide rollup, so every relative metric (A11's
* share of own vote, E10's LQ) needs the same sums over the 435 slugs. Cached
* at module scope — the artifact is immutable for the process lifetime.
*/
export const getStatewideFederalTotals = (year: number): StatewideFederalTotals => {
const cached = statewideTotalsCache.get(year)
if (cached) return cached
const yearKey = String(year)
const totals: StatewideFederalTotals = { ownVotes: 0, validVotes: 0 }
for (const baseline of Object.values(artifact.municipalities)) {
totals.ownVotes += baseline.votesByYear[yearKey] ?? 0
totals.validVotes += baseline.validVotesByYear[yearKey] ?? 0
}
statewideTotalsCache.set(year, totals)
return totals
}