teqo-backup/scripts/extract-city-report-snapshot.mjs
Francisco Solla 321ac7b872
Some checks failed
Deploy / preflight (push) Has been cancelled
Deploy / verify (push) Has been cancelled
Deploy / deploy staging (teqo-staging) (push) Has been cancelled
Deploy / requeue when main advanced (push) Has been cancelled
Deploy / deploy production (teqo-1313) (push) Has been cancelled
feat(relatorio-cidade): foca o relatório na mobilização do 2º turno
A federal foi decidida em 04/10; o documento de cidade passa a servir
inteiro à eleição de Lula. Saem conta eleitoral/cadeira de Solla,
concorrentes e pré-candidatos, emendas e entregas (sem fetch do Portal
da Transparência no builder), acervo de falas, panorama regional e
sinais/demandas; demografia, economia e transporte ficam como contexto
da visita.

A página 1 vira só mobilização: KPIs oficiais do artefato TSE 2026
(Lula/Δ, X₁/X₂, Solla), papel determinístico da cidade, quem mobiliza,
movimento local do 2º turno, riscos e o painel "O que pedir e falar"
(pedido Lula 13; guardrail de votos em jogo no lugar do defeso).

O checklist cai de 10 para 8 itens (emendas_web e quem_investe saem;
relacao_campo/disputa_local reancorados no 2º turno); listas
preCandidates/investments/emendasIndicators saem do contrato (arquivos
antigos seguem aceitos em replay). O snapshot enxuga para rede +
conjuntura + demografia (cityReportTerritory.mjs removido).

Prova: unit 5.232 verdes, int do snapshot sem PII e sem os grupos
aposentados, lint/format/typecheck/knip limpos e rebuild da fixture de
Cruz das Almas (9 seções, 16 páginas contra 21, página 1 fechando sem
fallback).
2026-10-10 16:57:20 -03:00

83 lines
2.9 KiB
JavaScript

/**
* Snapshot extractor for the city report (C163).
*
* Runs where production lives (homeserver), read-only by connection options,
* and writes a JSON snapshot consumed by `scripts/build-city-report.mjs`.
* Nothing is persisted in the database and the script never writes to it.
*
* Usage (from the homeserver, with `~/stack/teqo-1313.env` loaded):
* CITY_REPORT_CONFIRM=1 NODE_OPTIONS="--no-deprecation --import=tsx/esm --import=./scripts/seed-loader.mjs" \
* node scripts/extract-city-report-snapshot.mjs --municipality=<slug> --out=data/relatorios-cidade/<slug>.snapshot.json
*/
import { execFileSync } from 'node:child_process'
import { mkdir, writeFile } from 'node:fs/promises'
import { dirname, join, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { databaseLabel } from './lib/cityReportDatabase.mjs'
import { dieWithLabel, loadCliEnv, parseEqualsFlags } from './lib/cli.mjs'
import { enterReadOnlySession, resolveCampaignActor } from './lib/readOnlyExtract.mjs'
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
const LABEL = 'extract-city-report-snapshot'
const die = dieWithLabel(LABEL)
const codeSha = () => {
try {
return execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd: ROOT }).toString().trim()
} catch {
return null
}
}
loadCliEnv()
const { flags } = parseEqualsFlags(process.argv.slice(2))
const slug = typeof flags.municipality === 'string' ? flags.municipality : null
const out = typeof flags.out === 'string' ? flags.out : null
if (!slug || !out) {
die(
'Uso: --municipality=<slug> --out=<arquivo.json>\n' +
' Env: CITY_REPORT_CONFIRM=1 (obrigatório), DATABASE_URL (read-only é forçado), PORTAL da vida real.',
)
}
const rawDatabaseUrl = enterReadOnlySession({
confirmEnv: 'CITY_REPORT_CONFIRM',
label: LABEL,
die,
context: `município ${slug}`,
})
const { default: configPromise } = await import('../src/payload.config.ts')
const { getPayload } = await import('payload')
const { composeCityReportSnapshot } = await import('./cityReportSnapshot.mjs')
const payload = await getPayload({ config: configPromise })
try {
const actor = await resolveCampaignActor(payload, { die })
console.log(`[${LABEL}] ator: ${actor.name ?? actor.id} (${actor.role})`)
const snapshot = await composeCityReportSnapshot({
payload,
actor,
slug,
readAt: new Date().toISOString(),
codeSha: codeSha(),
database: databaseLabel(rawDatabaseUrl),
})
const outPath = resolve(ROOT, out)
await mkdir(dirname(outPath), { recursive: true })
await writeFile(outPath, `${JSON.stringify(snapshot, null, 2)}\n`)
console.log(
`[${LABEL}] snapshot de ${snapshot.municipality.name} → ${out} ` +
`(lideranças=${snapshot.leaderships.totalCount} assessores=${snapshot.advisors.length})`,
)
} catch (error) {
die(error instanceof Error ? error.message : String(error))
}
process.exit(0)