- 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)
110 lines
4.4 KiB
TypeScript
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
|
|
}
|