pgx
PGX — Lightweight Postgres for AI agents. Written in Rust. Thousands of isolated databases in one runtime. 🐘
翻譯準備中
Painel público de indicadores oficiais de violência e segurança pública no Estado do Rio de Janeiro: séries históricas desde 1991, municípios, áreas policiais, mapas e comparações por mandato.
翻譯準備中
資料Python · TypeScriptGitHub @Tomoyo-Computer-Science-Research-Unit上線中營運者未確認
試用過了嗎? 點一下就好,不需要登入。
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Painel público de indicadores oficiais de violência e segurança pública no Rio de Janeiro, série do ISP desde 1991.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗This project has been created as part of the 42 curriculum by c3c4d4.
Painel público de dados sobre violência e segurança pública por estado, da Tomoyo: Rio de Janeiro (ISP), São Paulo (SSP-SP) e as outras 25 UFs pelo Sinesp VDE do Ministério da Justiça, desde 2015.
O projeto baixa as bases do ISP, normaliza as séries e publica um site estático: indicadores acumulados, séries desde 1991, mapa, rankings, mudanças recentes e comparação por mandato. Vítimas não são identificadas e endereços não são publicados. O visual é o de tomoyo.systems: tinta sobre papel, Shippori Mincho e Archivo, tema claro e escuro.
Site em produção: https://g0v.tomoyo.systems/perdeu-playboy/ (perdeu-playboy.online redireciona para lá).
São Paulo em https://g0v.tomoyo.systems/perdeu-playboy/sp/; cada UF publicada mora em /perdeu-playboy/<uf>/;
o Brasil inteiro, em /perdeu-playboy/br/.
A lista única é frontend/lib/ufs.json: sigla, código IBGE, nome, capital, fonte (isp, ssp ou sinesp),
ano inicial e se a UF é publicada. O ETL, o seletor de UF do cabeçalho (tecla U) e o build-site.sh
leem o mesmo arquivo. As 27 estão publicadas desde 2026-10-16. Uma UF servida pelo Sinesp precisa de "published": true, dos
mandatos de governador e de prefeito da capital em backend/app/services/governor_performance.py
(o teste exige a sequência sem buracos desde o início da série) e do snapshot:
cd backend && .venv/bin/python -m app.services.sinesp_repository --uf MG # baixa o VDE e extrai a UF
.venv/bin/python -m app.etl.export_static_frontend --uf MG # frontend/data/mg/
Um build serve todas as UFs: o Rio na raiz, as demais em /<uf>/ (app/[uf]/, com
generateStaticParams). Os snapshots não entram no bundle: frontend/scripts/stage-data.mjs copia
frontend/data/<uf>/ para public/data/<uf>/ antes do next build e do next dev, e DataGate busca o
da UF da página com fetch (?v=<build>, porque o nginx guarda JSON por 7 dias). O Distrito Federal tem
um município só (Brasília): mapa de uma forma, painel lateral já preenchido. Tiroteios só existe no Rio,
onde há fonte.
O Brasil é a primeira entrada de ufs.json (sigla BR, fonte brasil): primeiro item do seletor, rota
/br/, as mesmas telas. O país ocupa o lugar do estado e as 27 UFs o dos municípios (séries, rankings,
mapa das UFs, mudanças); Mandatos compara presidentes, e não há faixa de prefeito. No backend o Brasil
não é uma UF: published_ufs() o deixa de fora e python -m app.uf o imprime por último, porque o
snapshot nacional lê os extratos do Sinesp que as rodadas das UFs refazem.
cd backend && .venv/bin/python -m app.services.brasil_repository # extrai o que falta (o Rio) e lista os buracos
.venv/bin/python -m app.etl.export_static_frontend --uf BR # frontend/data/br/
Fonte principal: Instituto de Segurança Pública do Rio de Janeiro, ISP Dados Abertos.
| Arquivo | Nível | Cobertura no site |
|---|---|---|
DOMensalEstadoDesde1991.csv | Estado | desde 1991-01 |
BaseDPEvolucaoMensalCisp.csv | Área integrada (CISP) | desde 2003-01 |
BaseMunicipioMensal.csv | Município | desde 2014-01 |
ArmasApreendidasEvolucaoCisp.csv | CISP (agregado para município e estado) | desde 2007-01 |
Relacao_RISPxAISPxCISP.csv | Divisão territorial | – |
Alguns indicadores começam depois: lesão corporal seguida de morte (1999), morte por intervenção de agente do Estado (1998), estupro (2003), feminicídio (2024-10). Esses meses ficam sem dado no snapshot, nunca zero.
População, para taxas por 100 mil habitantes, vem do IBGE/SIDRA por ano: censos de 1991, 2000, 2010 e 2022, contagem de 2007 e estimativas anuais (tabela 6579). Anos sem publicação (1992–1999 e 2023) são interpolados linearmente e marcados como tal. Geometrias: malhas do IBGE (municípios) e Data.Rio (bairros, com população do Censo 2022 por bairro).
| Fonte | Formato | Cobertura |
|---|---|---|
SSP-SP, dados mensais (v1/OcorrenciasMensais/RecuperaDadosMensaisAgrupados) | JSON, uma chamada por território, ano e grupo (criminal, produtividade) | estado e 93 DPs da capital desde 2001-01; 645 municípios desde 2023-01 no snapshot de 2026-10-14 |
SSP-SP, violência contra a mulher (v1/ViolenciaMulher/RecuperaDadosPorAno) | JSON | feminicídio estadual desde 2018-01 |
Sinesp VDE/MJSP (bancovde-AAAA.xlsx) | XLSX anual, todas as UFs | morte por intervenção de agente do Estado, só total estadual, desde 2015 |
O download respeita uma chamada por segundo; as respostas ficam em data/raw/ssp_sp/monthly/ (ignoradas)
e o consolidado data/raw/ssp_sp/ssp_monthly.csv.gz é versionado, então a rodada diária só baixa o ano
corrente (e o anterior até março). python -m app.services.ssp_repository --download faz tudo.
Lacuna conhecida: a coleta municipal de 2001 a 2022 (cerca de 28 mil chamadas, 8 horas a 1 req/s) não
foi feita; o check_sources --uf SP do servidor baixa até 3.000 chamadas por rodada, do ano mais recente
para o mais antigo, e o snapshot ganha esses anos em cerca de dez rodadas.
Homicídio doloso e latrocínio contam vítimas; as demais naturezas, ocorrências. Roubo (outros) é o total de
roubos menos carga e banco (não equivale ao roubo de rua do ISP). Lesão corporal seguida de morte começa em
2016-09; letalidade violenta e crime geral só existem para o estado, desde 2016-09. Não há mapa por DP:
a SSP não publica os limites, então o mapa de SP é só por município.
O Sinesp VDE cobre as 27 UFs desde 2015, mês a mês (com lacunas: o Espírito Santo só manda homicídios a partir de 2016-01; roubo de veículo, de carga e armas apreendidas começam depois de 2015 em AC, ES, GO, MS, PR, RO, RR, SE e TO). Homicídio doloso, latrocínio, lesão corporal seguida de morte e feminicídio vêm por município; morte por intervenção, estupro, roubo de veículo, roubo de carga e armas apreendidas só com o total estadual. Não há roubo a pessoa nem recorte por delegacia.
O total nacional é a soma das 27 UFs no Sinesp VDE, todas pelo mesmo arquivo. Rio de Janeiro e São
Paulo também entram pelo Sinesp: o ISP soma roubo de rua, que o Sinesp não tem, e a SSP-SP conta
ocorrências onde o Sinesp conta vítimas, então somar as fontes estaduais com o Sinesp não daria uma
série nacional. Os números desses dois estados em /br/ diferem dos de / e /sp/.
Regra da soma: um indicador num mês tem total nacional só quando as 27 UFs têm linha para ele naquele mês. Faltando uma, o mês fica sem dado, nunca com a soma das outras 26; zero enviado pela UF conta como zero. Cada UF tem o corte de sempre (o último mês com homicídio no estado), então o último mês nacional é o da UF mais atrasada. No snapshot de 2026-10-16:
| Indicador | Total nacional | Por quê |
|---|---|---|
| homicídio, latrocínio, lesão seguida de morte, feminicídio, letalidade | desde 2016-01 | o Espírito Santo não mandou 2015 |
| morte por intervenção, estupro | desde 2015-01 | – |
| roubo de veículo, armas apreendidas | desde 2021-01 | AC, ES, GO, PB, PR, RO, RR e TO começam depois de 2015 ou têm meses sem envio |
| roubo de carga, crime geral | desde 2021-01, sem 2023, 2024 e 2025-02/03 | SE começa em 2021; MS não mandou esses meses |
População do Brasil e das UFs: SIDRA, tabelas 6579 (estimativas), 4714 (Censo 2022) e 200 (Censo
2010), níveis n1 e n3; 2023 interpolado. Mapa: malha do país com divisão por UF
(api/v3/malhas/paises/BR?intrarregiao=UF&qualidade=minima).
Presidentes desde a posse anterior ao início da série: Dilma Rousseff (1/1/2011, afastada em 12/5/2016), Michel Temer interino (12/5 a 30/8/2016) e titular (31/8/2016), Jair Bolsonaro (2019), Lula (2023). Dilma fica sem base de comparação: a série nacional começa depois da posse.
O SIM/DataSUS (óbitos por agressão, desde 1979) estenderia a comparação até Figueiredo, mas é anual e o site inteiro (acumulado no ano, soma de 12 meses, Mandatos) é mensal; ficou de fora.
Indicadores publicados: homicidio_doloso, lesao_corp_morte, latrocinio, letalidade_violenta,
morte_interv_policial, feminicidio, roubo_rua, roubo_veiculo, roubo_carga, estupro,
apreensao_armas. crime_geral é derivado (letalidade violenta + roubo de rua + roubo de veículo + roubo de
carga + estupro) e só existe num mês quando todos os componentes têm dado.
Tudo cabe numa tela de 1280×720 ou mais, sem rolagem de página; abaixo disso a página rola, o
layout não cede. Teclas 1–7 trocam de tela, T tema, F efeitos de impressão, setas e M/V/R
por tela. A mira e os elementos quebráveis são os do tomoyo.systems; o mon com sangue é inquebrável.
/ Panorama: acumulado do ano por indicador contra os mesmos meses do ano anterior, mínimo e máximo históricos
no mesmo período, sparkline anual, gráfico anual com faixas de mandato e pequenos múltiplos de todos os
indicadores. Recorte por estado, município ou área integrada./series Séries: anual, mensal ou soma móvel de 12 meses; ocorrências ou por 100 mil; intervalo de anos;
tabela ano a ano; CSV. Permalinks em ?indicator=&type=&name=&view=&metric=&from=&to=./mapa Mapa: coropleto de municípios e, ao clicar no Rio, de bairros agregados por CISP. Escala por posição./rankings, /mudancas, /mandatos, /metodologia, /glossario, /fontes./tiroteios Tiroteios: ocorrências, mortos e feridos registrados pelo Instituto Fogo Cruzado desde julho
de 2016, por estado, município e bairro do Rio, com ranking do recorte. Fonte colaborativa, separada do ISP;
entra no build só com credenciais (ver Deploy)./status Status: última checagem e último build no servidor, mês mais recente, cada arquivo do ISP
(data de modificação no servidor deles, tamanho, SHA-256, contrato) e as últimas rodadas. Lê o
status.json que o build-site.sh escreve a cada rodada.backend/app/
uf.py UF corrente e lista de UFs (lê frontend/lib/ufs.json)
etl/export_static_frontend.py gera o snapshot de uma UF (--uf) em frontend/data/<uf>/
services/ssp_repository.py SSP-SP: download a 1 req/s, consolidado, mesma interface do ISP
services/sinesp_repository.py Sinesp VDE: download dos XLSX anuais, extrato por UF
services/brasil_repository.py Brasil: soma das 27 UFs do Sinesp, UFs como territórios
services/isp_repository.py lê e normaliza os CSVs do ISP (sem dado ≠ zero, nomes reparados)
services/population_repository.py população anual do IBGE, com interpolação marcada
services/governor_performance.py mandatos e comparação descritiva
etl/check_sources.py contrato de cada CSV do ISP (colunas, chaves, intervalos, atraso)
etl/write_status.py status.json servido pelo site
frontend/
data/<uf>/ snapshot de cada UF (versionado); scripts/stage-data.mjs copia para public/data/
lib/ufs.json lista única de UFs
lib/uf.ts textos e nomes por UF (Método, Glossário, avisos, seletor), UF do caminho
data/<uf>/static-data.generated.json metadados, séries do estado, população, mandatos, fontes (~230 KB)
data/<uf>/static-series/<ind>.generated.json séries por município e CISP de um indicador,
carregadas só quando o recorte pede; crime_geral já vem somado
data/<uf>/static-geo.generated.json geometrias, carregadas só no mapa (~2,3 MB no Rio)
components/DataGate.tsx busca o núcleo da UF da página e só então renderiza a tela
lib/static-api.ts toda a leitura e os cálculos (acumulados, anuais, rankings, mapa)
app/, components/ Next.js App Router, Tailwind com tokens em CSS, Recharts
data/raw/ caches dos arquivos oficiais
O site em produção não depende de backend ativo. As séries do snapshot são esparsas, no formato
{"from": "AAAA-MM", "values": [...]}, com null para meses não apurados.
Contrato dos arquivos oficiais: python -m app.etl.check_sources baixa o que falta e confere, por
CSV, colunas obrigatórias, chave única, ano e mês dentro do intervalo, valores não negativos e o
último mês apurado (atraso acima de quatro meses é aviso). Sai com código 1 quando algo quebra; o
servidor então avisa por e-mail e mantém o snapshot anterior.
Requisitos: Python 3.10+ (CI usa 3.12), Node 22+.
cp .env.example .env
cd backend && uv venv --python 3.12 .venv && uv pip install --python .venv/bin/python -e ".[dev]"
cd ../frontend && npm ci
Regerar o snapshot (baixa os CSVs do ISP e as tabelas do IBGE na primeira vez; ~2 min):
cd backend && .venv/bin/python -m app.etl.export_static_frontend --uf RJ
.venv/bin/python -m app.services.ssp_repository --download # SP: só o que falta, 1 req/s
.venv/bin/python -m app.etl.export_static_frontend --uf SP
Rodar o site:
cd frontend && npm run dev
make lint # flake8 + mypy
make test # pytest
cd frontend && npm run typecheck && npm run build && npm run test:visual # RJ, SP, MG, DF, AM, BA, BR
O CI (.github/workflows/ci.yml) roda os testes do backend e os testes visuais do frontend com Playwright.
O site vive em g0v.tomoyo.systems/perdeu-playboy, no servidor principal da Tomoyo (droplet
mail.tomoyo.systems na DigitalOcean), como um nginx estático atrás do Traefik, ao lado dos outros
serviços. A landing, a configuração do nginx e o cron de todos os projetos ficam no repositório
g0v; aqui só o que publica este site.
Fluxo normal, sem intervenção, todo dia às 06:23 de Brasília no próprio servidor: o cron
(/etc/cron.d/g0v-build-site) roda deploy/g0v/build-site.sh (instalado em /root/g0v/build-site.sh,
clone em /root/g0v/src, log em /root/g0v/build-site.log). O script puxa main e, para cada UF
publicada em frontend/lib/ufs.json, baixa de novo os arquivos oficiais, confere o contrato
(check_sources --uf) e regenera o snapshot (export_static_frontend --uf); falha numa UF deixa só
aquela com o snapshot commitado. O Brasil roda por último, com os extratos das UFs já refeitos. Se o commit ou os dados mudaram desde o último build (ignorando
generated_at), faz um next build só, com todas as UFs, e sincroniza out/ com
/root/g0v/site/perdeu-playboy (o Rio na raiz, as demais em /<uf>/, os snapshots em /data/<uf>/).
São 30 s numa máquina de 12 núcleos; o build por UF levava 25 s cada, 11 minutos para 27 UFs. Cada rodada vira
uma linha em /root/g0v/runs.jsonl e um status.json por UF (/perdeu-playboy/status.json,
/perdeu-playboy/sp/status.json); contrato, ETL ou build quebrados mandam e-mail para
[email protected] pelo mailserver da própria máquina. Para publicar na hora, depois de um push
em main:
ssh [email protected] 'FORCE=1 SKIP_DATA=1 /root/g0v/build-site.sh'
O workflow update-data.yml faz só a parte dos dados do Rio, às 05:53: regenera o snapshot e commita
em main quando mudou, para manter o repositório em dia.
Build manual:
cd frontend && NEXT_PUBLIC_BASE_PATH=/perdeu-playboy npm run build
Deploy a partir desta máquina, com build local (serve para testar mudanças não commitadas):
scripts/deploy-g0v.sh
O script resolve o droplet pelo doctl e sobe o build para /root/g0v/site/perdeu-playboy.
O certificado vem do Traefik (Let's Encrypt via DNS Cloudflare) e o DNS é o wildcard *.tomoyo.systems.
Fogo Cruzado é opcional: com FOGOCRUZADO_EMAIL, FOGOCRUZADO_PASSWORD e ENABLE_FOGOCRUZADO=true
no ambiente (no servidor, em /root/g0v/.env-perdeu), o ETL baixa as ocorrências e o snapshot ganha a
seção fogo_cruzado; sem isso o export é idêntico.
O projeto Vercel antigo (perdeu-playboy.online) só redireciona (deploy/vercel-redirect).
AI was used to scaffold the ETL, the parsers and the deploy script; the result was reviewed against the official sources and tested.
/br/ diferem dos de / e /sp/ porque vêm do Sinesp, não das fontes estaduais.