pgx
AI 에이전트를 위한 경량 Postgres, Rust로 구현, 하나의 런타임에서 수천 개의 격리된 데이터베이스 제공
리우데자네이루 주의 폭력·안전 공식 지표를 연도별·시·경찰구역·지도 등으로 제공하는 공개 대시보드
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를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기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.