sidra-sql 1.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sidra_sql/__init__.py +15 -0
- sidra_sql/cli.py +631 -0
- sidra_sql/config.py +137 -0
- sidra_sql/database.py +793 -0
- sidra_sql/exporter.py +171 -0
- sidra_sql/models.py +230 -0
- sidra_sql/plugin_manager.py +200 -0
- sidra_sql/runner.py +76 -0
- sidra_sql/scaffold.py +248 -0
- sidra_sql/sidra.py +362 -0
- sidra_sql/storage.py +156 -0
- sidra_sql/toml_runner.py +374 -0
- sidra_sql/transform_runner.py +153 -0
- sidra_sql/utils.py +106 -0
- sidra_sql/validator.py +222 -0
- sidra_sql-1.3.0.dist-info/METADATA +629 -0
- sidra_sql-1.3.0.dist-info/RECORD +20 -0
- sidra_sql-1.3.0.dist-info/WHEEL +4 -0
- sidra_sql-1.3.0.dist-info/entry_points.txt +2 -0
- sidra_sql-1.3.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sidra-sql
|
|
3
|
+
Version: 1.3.0
|
|
4
|
+
Summary: Tabelas de dados agregados do IBGE
|
|
5
|
+
Project-URL: Homepage, https://github.com/Quantilica/sidra-sql
|
|
6
|
+
Project-URL: Repository, https://github.com/Quantilica/sidra-sql
|
|
7
|
+
Project-URL: Issues, https://github.com/Quantilica/sidra-sql/issues
|
|
8
|
+
Author-email: "Komesu, D.K." <daniel@dkko.me>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: brazil,etl,ibge,open-data,postgresql,sidra
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Database
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Requires-Dist: httpx>=0.28.1
|
|
23
|
+
Requires-Dist: orjson>=3.11.7
|
|
24
|
+
Requires-Dist: platformdirs>=4.9.6
|
|
25
|
+
Requires-Dist: psycopg[binary]>=3.2.9
|
|
26
|
+
Requires-Dist: rich>=13.0.0
|
|
27
|
+
Requires-Dist: sidra-fetcher>=0.7.2
|
|
28
|
+
Requires-Dist: sqlalchemy>=2.0.41
|
|
29
|
+
Requires-Dist: typer>=0.24.1
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# sidra-sql: Pipeline ETL para dados do IBGE/SIDRA em PostgreSQL
|
|
33
|
+
|
|
34
|
+
 
|
|
35
|
+
|
|
36
|
+
**Pipeline ETL robusto para baixar, normalizar e carregar tabelas agregadas do SIDRA/IBGE em PostgreSQL.**
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
Trabalhar com dados do IBGE é uma tarefa que todo analista e cientista de dados brasileiro conhece bem — e sabe que não é simples. A API SIDRA disponibiliza um acervo imenso de séries estatísticas (PIB municipal, população, inflação, agropecuária e muito mais), mas transformar esses dados brutos em um banco de dados relacional, limpo, normalizado e pronto para consulta é trabalhoso e cheio de armadilhas.
|
|
41
|
+
|
|
42
|
+
Este projeto resolve exatamente esse problema: um pipeline ETL completo, com controle de cache, downloads paralelos, carga em massa via protocolo COPY do PostgreSQL e um esquema de banco de dados cuidadosamente normalizado.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Por que usar este projeto?
|
|
47
|
+
|
|
48
|
+
- **Zero redundância:** nomes de arquivo determinísticos garantem que a mesma requisição nunca seja baixada duas vezes.
|
|
49
|
+
- **Desempenho real:** downloads multi-threaded + carga via `COPY` do PostgreSQL são ordens de magnitude mais rápidos que abordagens ingênuas.
|
|
50
|
+
- **Confiabilidade:** retry com backoff exponencial lida com instabilidades da API sem interromper o pipeline.
|
|
51
|
+
- **Declarativo:** cada pesquisa é descrita em um arquivo TOML — sem código Python para adicionar novas séries.
|
|
52
|
+
- **Transformações:** camada de transformação (TOML + SQL) gera tabelas planas e desnormalizadas, prontas para Power BI, Excel ou qualquer ferramenta analítica.
|
|
53
|
+
- **Banco normalizado:** dados separados em cinco tabelas relacionais com constraints de unicidade e índices otimizados para consultas analíticas.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Funcionalidades
|
|
58
|
+
|
|
59
|
+
| Funcionalidade | Detalhes |
|
|
60
|
+
|---|---|
|
|
61
|
+
| **Download paralelo** | Pool de threads configurável para baixar múltiplos períodos simultaneamente |
|
|
62
|
+
| **Cache inteligente** | Filenames determinísticos — cache-hit evita requisições duplicadas à API |
|
|
63
|
+
| **Retry com backoff** | Até 5 tentativas com delay exponencial (5s, 10s, 20s…) em falhas de rede |
|
|
64
|
+
| **Carga em massa** | Protocolo COPY nativo do PostgreSQL via `psycopg3` para inserção em alta performance |
|
|
65
|
+
| **Versionamento de revisões** | Soft-versioning na própria fato: cada revisão do IBGE é retida, no máximo uma linha ativa por chave (índice único parcial) — habilita snapshots as-of |
|
|
66
|
+
| **Recarga idempotente** | Detecção-de-mudança na carga — re-execução sem dados novos é no-op (zero churn) |
|
|
67
|
+
| **Export CSV + as-of** | `export` grava as saídas em CSV via `COPY`; `--as-of DATE` reconstrói o vintage publicado na data |
|
|
68
|
+
| **Skip inteligente de carga** | Tabela `arquivo_carregado` rastreia arquivos já carregados; re-execuções pulam o trabalho sem repetir I/O |
|
|
69
|
+
| **Normalização completa** | Localidades, dimensões (variável × classificação) e fatos em tabelas separadas |
|
|
70
|
+
| **Suporte a 6 classificações** | Produto cartesiano de até 6 níveis de classificação por variável |
|
|
71
|
+
| **Metadados persistidos** | Agregados, periodicidade e metadados JSON salvos no banco para consulta |
|
|
72
|
+
| **Transformações SQL** | Gera tabelas planas (ou views) prontas para análise, definidas por pares TOML + SQL |
|
|
73
|
+
| **Logging detalhado** | Dual-channel (arquivo rotativo + console) com rastreamento de cada etapa |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Arquitetura
|
|
78
|
+
|
|
79
|
+
O projeto segue uma arquitetura baseada em plugins. O motor core `sidra-sql` gerencia e orquestra a execução de pipelines que são distribuídos via repositórios Git independentes.
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
83
|
+
│ Plugins Independentes (GitHub) │
|
|
84
|
+
│ manifest.toml + fetch.toml + transform.toml │
|
|
85
|
+
│ (declaração das tabelas a baixar) │
|
|
86
|
+
└──────────────────────────┬──────────────────────────────────┘
|
|
87
|
+
│ instalado e lido via CLI
|
|
88
|
+
┌──────────────────────────▼──────────────────────────────────┐
|
|
89
|
+
│ sidra-sql plugin install <url> │
|
|
90
|
+
│ sidra-sql run <plugin-alias> <pipeline-id> │
|
|
91
|
+
│ │
|
|
92
|
+
│ toml_runner.py │
|
|
93
|
+
│ TomlScript: download → metadata → load │
|
|
94
|
+
└──────┬───────────────────┬──────────────────┬───────────────┘
|
|
95
|
+
│ │ │
|
|
96
|
+
┌──────▼──────┐ ┌────────▼───────┐ ┌──────▼──────────────┐
|
|
97
|
+
│ sidra.py │ │ database.py │ │ storage.py │
|
|
98
|
+
│ (Fetcher) │ │ (load, upsert, │ │ (filesystem, cache, │
|
|
99
|
+
│ API client │ │ DDL builders) │ │ filename hashing) │
|
|
100
|
+
└──────┬──────┘ └────────┬───────┘ └──────┬──────────────┘
|
|
101
|
+
│ │ │
|
|
102
|
+
┌──────▼──────┐ ┌────────▼───────┐ ┌──────▼──────┐
|
|
103
|
+
│ SIDRA API │ │ PostgreSQL │ │ Sistema de │
|
|
104
|
+
│ (IBGE) │ │ (ibge_sidra) │ │ arquivos │
|
|
105
|
+
└─────────────┘ └────────┬───────┘ └─────────────┘
|
|
106
|
+
│
|
|
107
|
+
┌───────────────────▼────────────────────────┐
|
|
108
|
+
│ transform_runner.py │
|
|
109
|
+
│ (executa o SQL da transformação) │
|
|
110
|
+
└───────────────────┬────────────────────────┘
|
|
111
|
+
│
|
|
112
|
+
┌───────────▼───────────┐
|
|
113
|
+
│ PostgreSQL │
|
|
114
|
+
│ (analytics schema) │
|
|
115
|
+
│ tabelas prontas para │
|
|
116
|
+
│ Power BI / Excel │
|
|
117
|
+
└───────────────────────┘
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Princípios de design:**
|
|
121
|
+
|
|
122
|
+
- **Desacoplado:** os pipelines vivem em repositórios próprios; o motor apenas clona e executa os manifestos TOML.
|
|
123
|
+
- **Determinismo:** o mesmo conjunto de parâmetros sempre gera o mesmo nome de arquivo — re-execuções são seguras e baratas.
|
|
124
|
+
- **Dois passos de carga:** o primeiro escaneamento coleta chaves únicas de localidades e dimensões; o segundo transmite os dados via COPY, evitando acúmulo em memória.
|
|
125
|
+
- **Declarativo:** tanto a carga (scripts TOML) quanto a transformação (TOML + SQL) são definidas por arquivos de configuração.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Esquema do Banco de Dados
|
|
130
|
+
|
|
131
|
+
O banco é organizado em cinco tabelas no schema `ibge_sidra` (configurável):
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
┌─────────────────┐ ┌──────────────────────────────────────────┐
|
|
135
|
+
│ tabela_sidra │ │ dados (fatos) │
|
|
136
|
+
│─────────────────│ │──────────────────────────────────────────│
|
|
137
|
+
│ id (PK) │◄──────│ tabela_sidra_id (FK) │
|
|
138
|
+
│ nome │ │ localidade_id (FK) ──────────────────────┼──►┌─────────────────┐
|
|
139
|
+
│ periodicidade │ │ dimensao_id (FK) ────────────────────────┼──►│ localidade │
|
|
140
|
+
│ metadados (JSON)│ │ periodo_id (FK) ─────────────────────────┼──►│─────────────────│
|
|
141
|
+
│ ultima_atualizac│ │ v (valor como texto) │ │ id (PK) │
|
|
142
|
+
└─────────────────┘ │ modificacao (date) │ │ nc (nível id) │
|
|
143
|
+
│ ativo (boolean) │ │ nn (nível nome)│
|
|
144
|
+
└──────────────────────────────────────────┘ │ d1c (unidade id)│
|
|
145
|
+
│ d1n (unidade nom│
|
|
146
|
+
┌──────────────────────────────┐ └─────────────────┘
|
|
147
|
+
│ periodo │◄────(periodo_id)
|
|
148
|
+
│──────────────────────────────│
|
|
149
|
+
│ id (PK) │ ┌──────────────────────────────────────────┐
|
|
150
|
+
│ codigo (ex: "202301") │ │ dimensao │
|
|
151
|
+
│ ano, mes, trimestre, semestre│ │──────────────────────────────────────────│
|
|
152
|
+
│ data_inicio, data_fim │ │ id (PK) │
|
|
153
|
+
└──────────────────────────────┘ │ mc,mn (unidade de medida id/nome) │
|
|
154
|
+
│ d2c,d2n (variável id/nome) │
|
|
155
|
+
│ d4c–d9c (ids das classificações, ≤6) │
|
|
156
|
+
│ d4n–d9n (nomes das classificações) │
|
|
157
|
+
└──────────────────────────────────────────┘
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Versionamento de revisões (vintage storage)
|
|
161
|
+
|
|
162
|
+
O IBGE revisa valores ao longo do tempo. A tabela `dados` preserva esse histórico: cada revisão distinta de uma chave lógica vira uma linha, e **no máximo uma** fica `ativo = TRUE` (a mais recente). A invariante é garantida fisicamente por um **índice único parcial**:
|
|
163
|
+
|
|
164
|
+
```sql
|
|
165
|
+
CREATE UNIQUE INDEX uq_dados_ativo
|
|
166
|
+
ON dados (tabela_sidra_id, localidade_id, dimensao_id, periodo_id)
|
|
167
|
+
WHERE ativo;
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
A carga aplica **detecção-de-mudança** (deactivate-then-insert por diferença de valor), processando cada `modificacao` em ordem crescente:
|
|
171
|
+
|
|
172
|
+
- **Carga inicial / valor novo:** insere a linha como `ativo = TRUE`.
|
|
173
|
+
- **Recarga idêntica:** no-op — nenhuma linha é tocada (zero churn).
|
|
174
|
+
- **Revisão de valor:** a linha ativa anterior vira `ativo = FALSE` (retida) e o novo valor entra como `ativo = TRUE`.
|
|
175
|
+
|
|
176
|
+
Assim, **"verdade atual"** = `WHERE ativo`; **"trilha de revisão"** = todas as linhas da chave por `modificacao`. Isso é o que torna o `sidra-sql export --as-of <data>` possível: por chave, a linha de maior `modificacao <= data` é o valor publicado naquela data.
|
|
177
|
+
|
|
178
|
+
> **Migração:** bancos criados antes desta mudança tinham uma constraint única cheia (`uq_dados`) e descartavam revisões. A migração para o índice parcial é idempotente e roda automaticamente no início de cada `sidra-sql run` (`ensure_vintage_schema`). Para reconstruir a trilha de vintages a partir do cache de arquivos JSON `@modificacao` já baixados, rode `--force-load`.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Pipelines Padrão (Plugin Oficial)
|
|
183
|
+
|
|
184
|
+
O `sidra-sql` vem pré-configurado com o catálogo oficial de pipelines de referência (hospedado em [Quantilica/sidra-pipelines](https://github.com/Quantilica/sidra-pipelines)). Estas pipelines são instaladas automaticamente com o alias `std` na primeira execução do CLI.
|
|
185
|
+
|
|
186
|
+
| Comando | Pesquisa | Tabelas SIDRA |
|
|
187
|
+
|---|---|---|
|
|
188
|
+
| `sidra-sql run std pib_municipal` | **PIB dos Municípios** | 5938 |
|
|
189
|
+
| `sidra-sql run std estimativa_populacao` | **Estimativas de População** | 6579 |
|
|
190
|
+
| `sidra-sql run std censo_populacao` | **Censo Demográfico** | 200 |
|
|
191
|
+
| `sidra-sql run std contagem_populacao` | **Contagem de População** | 305, 793 |
|
|
192
|
+
| `sidra-sql run std ipca` | **IPCA** | 1692, 1693, 58, 61, 655, 656, 2938, 1419, 7060 |
|
|
193
|
+
| `sidra-sql run std ipca15` | **IPCA-15** | 1646, 1387, 1705, 7062 |
|
|
194
|
+
| `sidra-sql run std inpc` | **INPC** | 1686, 1690, 22, 23, 653, 654, 2951, 1100, 7063 |
|
|
195
|
+
| `sidra-sql run std ppm_rebanhos` | **PPM — Rebanhos** | 73, 3939 |
|
|
196
|
+
| `sidra-sql run std ppm_producao` | **PPM — Produção animal** | 74, 3940 |
|
|
197
|
+
| `sidra-sql run std ppm_exploracao` | **PPM — Aquicultura e exploração** | 94, 95 |
|
|
198
|
+
| `sidra-sql run std pam_lavouras_temporarias` | **PAM — Lavouras temporárias** | 839, 1000, 1001, 1002, 1612 |
|
|
199
|
+
| `sidra-sql run std pam_lavouras_permanentes` | **PAM — Lavouras permanentes** | 1613 |
|
|
200
|
+
| `sidra-sql run std pevs_producao` | **PEVS — Produção florestal** | 289, 291 |
|
|
201
|
+
| `sidra-sql run std pevs_area_florestal` | **PEVS — Área florestal** | 5930 |
|
|
202
|
+
|
|
203
|
+
Para criar suas próprias pipelines e distribuí-las como plugin, consulte o **[Guia de Criação de Pipelines](CREATING_PIPELINES.md)**.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Pré-requisitos
|
|
208
|
+
|
|
209
|
+
- **Python 3.13+**
|
|
210
|
+
- **PostgreSQL 14+** (com usuário e banco de dados criados)
|
|
211
|
+
- Acesso à internet para consultar a API SIDRA do IBGE
|
|
212
|
+
- Biblioteca [`sidra-fetcher`](https://github.com/Quantilica/sidra-fetcher) (instalada automaticamente via `pyproject.toml`)
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Instalação
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
pip install sidra-sql
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Com [uv](https://github.com/astral-sh/uv):
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
uv add sidra-sql
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**Dependências principais:**
|
|
229
|
+
|
|
230
|
+
| Pacote | Uso |
|
|
231
|
+
|---|---|
|
|
232
|
+
| [`sidra-fetcher`](https://github.com/Quantilica/sidra-fetcher) | Cliente HTTP para a API SIDRA do IBGE |
|
|
233
|
+
| `psycopg[binary] >= 3.2.9` | Adaptador PostgreSQL com extensões C |
|
|
234
|
+
| `sqlalchemy >= 2.0.41` | ORM e geração de SQL |
|
|
235
|
+
| `orjson >= 3.11.7` | Serialização JSON de alta performance |
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Configuração
|
|
240
|
+
|
|
241
|
+
Crie o arquivo `config.ini` na raiz do projeto:
|
|
242
|
+
|
|
243
|
+
```ini
|
|
244
|
+
[storage]
|
|
245
|
+
# Diretório onde os arquivos JSON baixados serão armazenados
|
|
246
|
+
data_dir = data
|
|
247
|
+
|
|
248
|
+
[database]
|
|
249
|
+
user = postgres
|
|
250
|
+
password = sua_senha
|
|
251
|
+
host = localhost
|
|
252
|
+
port = 5432
|
|
253
|
+
dbname = dados
|
|
254
|
+
schema = ibge_sidra
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
> **Nota:** O schema `ibge_sidra` será criado automaticamente na primeira execução, incluindo todas as tabelas, índices e constraints.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Uso
|
|
262
|
+
|
|
263
|
+
O sistema gerencia pipelines através de uma interface de linha de comando (CLI). Como as pipelines são plugins externos (repositórios git), o primeiro passo é instalar o plugin desejado.
|
|
264
|
+
|
|
265
|
+
### 1. Gerenciar Plugins
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
# Instalar um plugin via URL do Git
|
|
269
|
+
sidra-sql plugin install https://github.com/Quantilica/sidra-pipeline-pam.git --alias pam
|
|
270
|
+
|
|
271
|
+
# Listar os plugins instalados e suas pipelines disponíveis
|
|
272
|
+
sidra-sql plugin list
|
|
273
|
+
|
|
274
|
+
# Atualizar um plugin instalado
|
|
275
|
+
sidra-sql plugin update pam
|
|
276
|
+
|
|
277
|
+
# Remover um plugin
|
|
278
|
+
sidra-sql plugin remove pam
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### 2. Criar e Desenvolver Plugins
|
|
282
|
+
|
|
283
|
+
O CLI inclui comandos para criar e iterar sobre plugins localmente, sem precisar escrever os arquivos manualmente.
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
# Criar a estrutura completa de um novo plugin
|
|
287
|
+
sidra-sql plugin scaffold meu-plugin --description "Meus dados do IBGE"
|
|
288
|
+
|
|
289
|
+
# Adicionar um pipeline a um plugin existente (rodar dentro do diretório do plugin)
|
|
290
|
+
cd meu-plugin
|
|
291
|
+
sidra-sql plugin add-pipeline nova_serie --description "Nova série de dados"
|
|
292
|
+
|
|
293
|
+
# Adicionar um pipeline com caminho aninhado
|
|
294
|
+
sidra-sql plugin add-pipeline ipca_municipios --path "precos/ipca" --description "IPCA municipal"
|
|
295
|
+
|
|
296
|
+
# Validar a estrutura do plugin antes de publicar
|
|
297
|
+
sidra-sql plugin validate
|
|
298
|
+
|
|
299
|
+
# Validar um plugin já instalado pelo alias
|
|
300
|
+
sidra-sql plugin validate std
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
O comando `scaffold` cria:
|
|
304
|
+
```
|
|
305
|
+
meu-plugin/
|
|
306
|
+
├── .gitignore
|
|
307
|
+
├── README.md
|
|
308
|
+
├── manifest.toml
|
|
309
|
+
└── meu_plugin/
|
|
310
|
+
├── fetch.toml ← template comentado com referências ao SIDRA
|
|
311
|
+
├── transform.toml ← declara as saídas (uma ou mais por pipeline)
|
|
312
|
+
└── meu_plugin.sql ← SQL da saída (um arquivo .sql por entrada [[table]])
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Para o fluxo completo de criação de plugins, veja o **[Guia de Criação de Pipelines](CREATING_PIPELINES.md)**.
|
|
316
|
+
|
|
317
|
+
### 3. Executar Pipelines
|
|
318
|
+
|
|
319
|
+
Use o comando `run`, especificando o alias do plugin e o id do pipeline (mostrados via `sidra-sql plugin list`):
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
# Baixa os dados e executa a transformação do pipeline 'lavouras_temporarias' do plugin 'pam'
|
|
323
|
+
sidra-sql run pam lavouras_temporarias
|
|
324
|
+
|
|
325
|
+
# Executa todos os pipelines de um plugin
|
|
326
|
+
sidra-sql run pam
|
|
327
|
+
|
|
328
|
+
# Força a atualização de metadados (re-baixa metadados da API mesmo com cache local)
|
|
329
|
+
sidra-sql run pam lavouras_temporarias --force-metadata
|
|
330
|
+
|
|
331
|
+
# Força o recarregamento de todos os arquivos no banco, ignorando o registro de arquivos já carregados
|
|
332
|
+
sidra-sql run pam lavouras_temporarias --force-load
|
|
333
|
+
|
|
334
|
+
# Combinação: metadados frescos + recarga completa
|
|
335
|
+
sidra-sql run pam lavouras_temporarias --force-metadata --force-load
|
|
336
|
+
|
|
337
|
+
# Executar apenas a etapa de transformação (sem fetch nem recursão)
|
|
338
|
+
sidra-sql transform pam lavouras_temporarias
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
> **`--force-load`** ignora a tabela `arquivo_carregado` e reprocessa todos os arquivos presentes em disco. Útil quando o schema do banco foi alterado ou quando é necessário reconstruir os dados a partir dos arquivos JSON locais já baixados. Não re-baixa arquivos da API — para isso use `--force-metadata` em conjunto.
|
|
342
|
+
|
|
343
|
+
### 4. Exportar para CSV
|
|
344
|
+
|
|
345
|
+
O comando `export` grava as saídas (`analytics.*`) de um pipeline em arquivos CSV, via protocolo `COPY` nativo do PostgreSQL (sem dependência de Polars/pandas).
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
# Exporta as saídas materializadas do pipeline para CSV (verdade atual)
|
|
349
|
+
sidra-sql export pam lavouras_temporarias
|
|
350
|
+
|
|
351
|
+
# Diretório de saída (default: ./export)
|
|
352
|
+
sidra-sql export pam lavouras_temporarias --output-dir ./csv
|
|
353
|
+
|
|
354
|
+
# Exporta todas as saídas de todos os pipelines do plugin
|
|
355
|
+
sidra-sql export pam
|
|
356
|
+
|
|
357
|
+
# Snapshot as-of: reconstrói as saídas como o IBGE as publicava na data
|
|
358
|
+
sidra-sql export pam lavouras_temporarias --as-of 2024-03-01
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
- **Sem `--as-of`** lê as tabelas/views já materializadas (`analytics.<nome>`). Se uma saída ainda não existe, o comando avisa e sugere rodar `sidra-sql run`. Gera `<nome>.csv`.
|
|
362
|
+
- **Com `--as-of DATE`** reconstrói cada saída a partir do histórico de revisões da tabela `dados`, reusando o próprio `.sql` do transform sem alterá-lo (uma view temporária `dados` expõe, por chave, a linha de maior `modificacao <= DATE`). Não toca as tabelas `analytics.*` persistidas. Gera `<nome>@YYYYMMDD.csv`.
|
|
363
|
+
|
|
364
|
+
> O snapshot as-of só enxerga revisões já capturadas no banco (ver **Versionamento de revisões** abaixo). Para reconstruir vintages a partir do cache de arquivos JSON já baixados, rode `sidra-sql run <plugin> <pipeline> --force-load`.
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Formato TOML
|
|
369
|
+
|
|
370
|
+
Cada arquivo TOML contém uma lista de entradas `[[tabelas]]`. Cada entrada corresponde a uma chamada à API SIDRA:
|
|
371
|
+
|
|
372
|
+
```toml
|
|
373
|
+
[[tabelas]]
|
|
374
|
+
tabela_sidra = "5938" # ID da tabela no SIDRA
|
|
375
|
+
variables = ["37", "498"] # IDs das variáveis ("allxp" para todas)
|
|
376
|
+
territories = {6 = ["all"]} # nível territorial → lista de IDs
|
|
377
|
+
|
|
378
|
+
[tabelas.classifications] # classificações e categorias (opcional)
|
|
379
|
+
315 = [] # lista vazia = todas as categorias
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
**Níveis territoriais comuns:**
|
|
383
|
+
|
|
384
|
+
| Código | Descrição |
|
|
385
|
+
|---|---|
|
|
386
|
+
| `1` | Brasil |
|
|
387
|
+
| `2` | Grandes Regiões |
|
|
388
|
+
| `3` | Unidades da Federação |
|
|
389
|
+
| `6` | Municípios |
|
|
390
|
+
| `7` | Regiões Metropolitanas |
|
|
391
|
+
| `71` | Regiões Metropolitanas e RIDEs |
|
|
392
|
+
|
|
393
|
+
### Flags especiais
|
|
394
|
+
|
|
395
|
+
**`unnest_classifications = true`**
|
|
396
|
+
|
|
397
|
+
Busca os metadados da tabela em tempo de execução e gera uma requisição para cada combinação de classificação × categoria:
|
|
398
|
+
|
|
399
|
+
```toml
|
|
400
|
+
[[tabelas]]
|
|
401
|
+
tabela_sidra = "1613"
|
|
402
|
+
variables = ["allxp"]
|
|
403
|
+
territories = {6 = []}
|
|
404
|
+
unnest_classifications = true
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**`split_variables = true`**
|
|
408
|
+
|
|
409
|
+
Emite uma requisição separada para cada variável listada em `variables`:
|
|
410
|
+
|
|
411
|
+
```toml
|
|
412
|
+
[[tabelas]]
|
|
413
|
+
tabela_sidra = "1002"
|
|
414
|
+
variables = ["109", "216", "214", "112"]
|
|
415
|
+
split_variables = true
|
|
416
|
+
territories = {6 = []}
|
|
417
|
+
classifications = {81 = ["allxt"]}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Adicionar uma nova série
|
|
421
|
+
|
|
422
|
+
Para aprender a criar o seu próprio repositório de pipelines compatível com este motor, veja a documentação dedicada:
|
|
423
|
+
👉 **[Guia: Como Criar Pipelines (Plugins)](CREATING_PIPELINES.md)**
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Transformações
|
|
428
|
+
|
|
429
|
+
Após a carga dos dados brutos no banco normalizado, a camada de transformação gera tabelas planas e desnormalizadas, prontas para consumo por ferramentas analíticas (Power BI, Excel, Metabase, etc.).
|
|
430
|
+
|
|
431
|
+
Cada pipeline declara suas saídas em um único `transform.toml` no seu diretório:
|
|
432
|
+
|
|
433
|
+
- **`transform.toml`** — uma ou mais entradas `[[table]]`, cada uma especificando o nome da tabela/view de destino, schema, estratégia e o arquivo `.sql` correspondente
|
|
434
|
+
- **`<saída>.sql`** — query SELECT que produz os dados denormalizados (um arquivo por entrada `[[table]]`)
|
|
435
|
+
|
|
436
|
+
Um pipeline pode produzir múltiplas saídas (ex.: uma tabela detalhada + uma view agregada) declarando múltiplos blocos `[[table]]` no mesmo `transform.toml`. Cada saída é materializada na ordem do array, em sua própria transação — se uma falhar, as anteriores persistem.
|
|
437
|
+
|
|
438
|
+
### Executar uma transformação
|
|
439
|
+
|
|
440
|
+
A execução via CLI `sidra-sql run <plugin> <pipeline>` já orquestra de forma inteligente a extração e em seguida a transformação de acordo com o `manifest.toml` do plugin.
|
|
441
|
+
|
|
442
|
+
### Formato TOML da transformação
|
|
443
|
+
|
|
444
|
+
```toml
|
|
445
|
+
[[table]]
|
|
446
|
+
name = "ipca" # Nome da tabela de destino
|
|
447
|
+
schema = "analytics" # Schema de destino (criado automaticamente)
|
|
448
|
+
strategy = "replace" # Estratégia de materialização ("replace" ou "view")
|
|
449
|
+
sql = "ipca.sql" # Arquivo SQL (relativo a transform.toml)
|
|
450
|
+
description = "IPCA - variação e peso mensal por categoria e localidade"
|
|
451
|
+
primary_key = ["periodo", "localidade_id", "variavel", "categoria"] # Opcional: define PK após carga
|
|
452
|
+
indexes = [
|
|
453
|
+
{ name = "idx_ipca_periodo", columns = ["periodo"] },
|
|
454
|
+
{ name = "idx_ipca_localidade", columns = ["localidade"] },
|
|
455
|
+
]
|
|
456
|
+
|
|
457
|
+
# Saída adicional opcional — outra tabela/view do mesmo pipeline:
|
|
458
|
+
[[table]]
|
|
459
|
+
name = "ipca_resumo_anual"
|
|
460
|
+
schema = "analytics"
|
|
461
|
+
strategy = "view"
|
|
462
|
+
sql = "ipca_resumo.sql"
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
**Estratégias disponíveis:**
|
|
466
|
+
|
|
467
|
+
| Estratégia | Comportamento | Quando usar |
|
|
468
|
+
|---|---|---|
|
|
469
|
+
| `replace` | `DROP` + `CREATE AS` + `PK/Indexes` | Import em Power BI / Excel (refresh completo) |
|
|
470
|
+
| `view` | `CREATE OR REPLACE VIEW` | Conexões live (zero storage, sempre atualizado) |
|
|
471
|
+
|
|
472
|
+
### SQL da transformação
|
|
473
|
+
|
|
474
|
+
O arquivo `.sql` contém um SELECT puro. Os nomes de tabela (`dados`, `dimensao`, `localidade`, `periodo`) são resolvidos pelo `search_path` configurado em `config.ini` — não use prefixo de schema:
|
|
475
|
+
|
|
476
|
+
```sql
|
|
477
|
+
SELECT
|
|
478
|
+
p.codigo AS periodo,
|
|
479
|
+
p.ano,
|
|
480
|
+
p.mes,
|
|
481
|
+
l.d1c AS localidade_id,
|
|
482
|
+
l.d1n AS localidade,
|
|
483
|
+
dim.d2n AS variavel,
|
|
484
|
+
dim.d4n AS categoria,
|
|
485
|
+
CASE WHEN d.v ~ '^-?[0-9]' THEN d.v::numeric END AS valor
|
|
486
|
+
FROM dados d
|
|
487
|
+
JOIN periodo p ON d.periodo_id = p.id
|
|
488
|
+
JOIN dimensao dim ON d.dimensao_id = dim.id
|
|
489
|
+
JOIN localidade l ON d.localidade_id = l.id
|
|
490
|
+
WHERE d.tabela_sidra_id IN ('7060', '1419')
|
|
491
|
+
AND d.ativo = true
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
Valores não numéricos do SIDRA (`"..."`, `"-"`, `"X"`) são convertidos em `NULL` pelo guard `CASE WHEN d.v ~ '^-?[0-9]'`.
|
|
495
|
+
|
|
496
|
+
### Adicionar uma nova transformação
|
|
497
|
+
|
|
498
|
+
Para detalhes sobre como adicionar ou editar transformações dentro do seu próprio plugin, veja o **[Guia: Como Criar Pipelines](CREATING_PIPELINES.md)**.
|
|
499
|
+
|
|
500
|
+
### Transformações incluídas
|
|
501
|
+
|
|
502
|
+
| Pipeline | Tabela de destino | Descrição |
|
|
503
|
+
|---|---|---|
|
|
504
|
+
| `pipelines/snpc/ipca/` | `analytics.ipca` | IPCA completo |
|
|
505
|
+
| `pipelines/snpc/inpc/` | `analytics.inpc` | INPC completo |
|
|
506
|
+
| `pipelines/snpc/ipca15/` | `analytics.ipca15` | IPCA-15 completo |
|
|
507
|
+
| `pipelines/pib_munic/` | `analytics.pib_municipal` | PIB dos Municípios |
|
|
508
|
+
| `pipelines/populacao/estimapop/` | `analytics.estimativa_populacao` | Estimativas de população |
|
|
509
|
+
| `pipelines/populacao/censo_populacao/` | `analytics.censo_populacao` | Censo Demográfico |
|
|
510
|
+
| `pipelines/populacao/contagem_populacao/` | `analytics.contagem_populacao` | Contagem da População |
|
|
511
|
+
| `pipelines/ppm/rebanhos/` | `analytics.ppm_rebanhos` | Efetivo dos rebanhos |
|
|
512
|
+
| `pipelines/ppm/producao/` | `analytics.ppm_producao` | Produção de origem animal |
|
|
513
|
+
| `pipelines/ppm/exploracao/` | `analytics.ppm_exploracao` | Aquicultura e exploração |
|
|
514
|
+
| `pipelines/pam/lavouras_permanentes/` | `analytics.pam_lavouras_permanentes` | Lavouras permanentes |
|
|
515
|
+
| `pipelines/pam/lavouras_temporarias/` | `analytics.pam_lavouras_temporarias` | Lavouras temporárias |
|
|
516
|
+
| `pipelines/pevs/producao/` | `analytics.pevs_producao` | Extração vegetal e silvicultura |
|
|
517
|
+
| `pipelines/pevs/area_florestal/` | `analytics.pevs_area_florestal` | Área de florestas plantadas |
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## Fluxo de Dados
|
|
522
|
+
|
|
523
|
+
```
|
|
524
|
+
API SIDRA (IBGE)
|
|
525
|
+
│
|
|
526
|
+
│ GET /agregados/{tabela}/periodos/{período}/...
|
|
527
|
+
▼
|
|
528
|
+
┌────────────────────────────────────────────────────┐
|
|
529
|
+
│ Fetcher (sidra.py) │
|
|
530
|
+
│ ┌─────────────────────────────────────────────┐ │
|
|
531
|
+
│ │ ThreadPoolExecutor (max_workers=4) │ │
|
|
532
|
+
│ │ → download de cada período em paralelo │ │
|
|
533
|
+
│ │ → retry com backoff (5 tentativas, base 5s)│ │
|
|
534
|
+
│ └──────────────────┬──────────────────────────┘ │
|
|
535
|
+
└─────────────────────┼──────────────────────────────┘
|
|
536
|
+
│
|
|
537
|
+
▼
|
|
538
|
+
┌────────────────────────┐
|
|
539
|
+
│ Storage (storage.py) │
|
|
540
|
+
│ data/t-{id}/ │
|
|
541
|
+
│ ├── arquivo1.json │ ← nome determinístico
|
|
542
|
+
│ ├── arquivo2.json │ (tabela+período+terr+
|
|
543
|
+
│ └── ... │ vars+classif+mod)
|
|
544
|
+
└───────────┬────────────┘
|
|
545
|
+
│
|
|
546
|
+
▼
|
|
547
|
+
┌────────────────────────────────────────┐
|
|
548
|
+
│ load_dados (database.py) │
|
|
549
|
+
│ │
|
|
550
|
+
│ Passo 1: scan JSON │
|
|
551
|
+
│ → coleta chaves únicas de │
|
|
552
|
+
│ localidades e dimensões │
|
|
553
|
+
│ → upsert em lotes de 5.000 linhas │
|
|
554
|
+
│ → constrói lookup dicts em memória │
|
|
555
|
+
│ Passo 2: scan JSON novamente │
|
|
556
|
+
│ → resolve IDs via lookup │
|
|
557
|
+
│ → usa data de modificação da API │
|
|
558
|
+
│ → stream via COPY para staging table │
|
|
559
|
+
│ → merge por vintage (modificacao): │
|
|
560
|
+
│ deactivate-then-insert por valor │
|
|
561
|
+
└───────────────┬────────────────────────┘
|
|
562
|
+
│
|
|
563
|
+
▼
|
|
564
|
+
┌──────────────────┐
|
|
565
|
+
│ PostgreSQL │
|
|
566
|
+
│ │
|
|
567
|
+
│ tabela_sidra │
|
|
568
|
+
│ localidade │
|
|
569
|
+
│ dimensao │
|
|
570
|
+
│ dados │
|
|
571
|
+
└──────────────────┘
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
---
|
|
575
|
+
|
|
576
|
+
## Desenvolvimento
|
|
577
|
+
|
|
578
|
+
```bash
|
|
579
|
+
git clone https://github.com/Quantilica/sidra-sql.git
|
|
580
|
+
cd sidra-sql
|
|
581
|
+
uv sync --dev
|
|
582
|
+
uv run pytest
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
A suíte de testes cobre:
|
|
586
|
+
|
|
587
|
+
| Arquivo | O que testa |
|
|
588
|
+
|---|---|
|
|
589
|
+
| `tests/test_config.py` | Carregamento de config, setup de logging |
|
|
590
|
+
| `tests/test_storage.py` | Geração de nomes, leitura/escrita, caminhos de metadados |
|
|
591
|
+
| `tests/test_base.py` | Cache de metadados, deduplicação, download com filepaths |
|
|
592
|
+
| `tests/test_sidra.py` | Retry logic, unnesting de classificações, context manager |
|
|
593
|
+
| `tests/test_database.py` | Limpeza de dados, criação de engine, semântica de carga (vintage merge) |
|
|
594
|
+
| `tests/test_exporter.py` | Builders de SQL do export (COPY, view as-of), parsing de saídas |
|
|
595
|
+
| `tests/test_utils.py` | Produto cartesiano de dimensões, resolução de unidade |
|
|
596
|
+
|
|
597
|
+
---
|
|
598
|
+
|
|
599
|
+
## Estrutura do Repositório
|
|
600
|
+
|
|
601
|
+
```
|
|
602
|
+
sidra-sql/
|
|
603
|
+
├── src/sidra_sql/
|
|
604
|
+
│ ├── __init__.py
|
|
605
|
+
│ ├── cli.py # Interface de Linha de Comando (Typer)
|
|
606
|
+
│ ├── plugin_manager.py # Gerenciamento de plugins, Git e manifests
|
|
607
|
+
│ ├── scaffold.py # Geração de plugins e pipelines (scaffold, add-pipeline)
|
|
608
|
+
│ ├── validator.py # Validação de estrutura de plugins
|
|
609
|
+
│ ├── toml_runner.py # TomlScript — orquestra o pipeline ETL de extração
|
|
610
|
+
│ ├── transform_runner.py # TransformRunner — materializa TOML+SQL analíticos
|
|
611
|
+
│ ├── exporter.py # Exporter — export CSV das saídas + snapshots as-of
|
|
612
|
+
│ ├── config.py # Leitura de config.ini
|
|
613
|
+
│ ├── database.py # SQLAlchemy, carga, DDL/DCL
|
|
614
|
+
│ ├── models.py # ORM models (tabelas, localidades, dimensões, dados)
|
|
615
|
+
│ ├── sidra.py # Cliente da API SIDRA com retry e cache
|
|
616
|
+
│ ├── storage.py # Filesystem: leitura, escrita, filenames
|
|
617
|
+
│ └── utils.py # Produto cartesiano de dimensões
|
|
618
|
+
├── tests/
|
|
619
|
+
├── config.ini # Configurações (não versionado)
|
|
620
|
+
├── pyproject.toml # Metadados e dependências do projeto
|
|
621
|
+
├── README.md
|
|
622
|
+
└── CREATING_PIPELINES.md # Guia para criação de plugins
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
## Licença
|
|
628
|
+
|
|
629
|
+
MIT — veja [LICENSE](LICENSE).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
sidra_sql/__init__.py,sha256=AsWaOaA0TOIWEw5eMoQl_9rOom0M0Z8mPNxLHKxl3zM,390
|
|
2
|
+
sidra_sql/cli.py,sha256=g0vqXXStGE0q72B5EVv5weMOENTSo_s4SLt5nyZK_0c,20104
|
|
3
|
+
sidra_sql/config.py,sha256=71ODLM_v9EHrQOi9rx-_zAHQ5HTy9zmzgp8z2Vvjwxs,4053
|
|
4
|
+
sidra_sql/database.py,sha256=X9_IrkTAfoxevFPKzwvzmP5QvcVqCZUO0tvOOdUOu1M,27160
|
|
5
|
+
sidra_sql/exporter.py,sha256=roiDvfwr6f44JM308-T8tNcYYoVc9aOcTRuuc0bSUJ8,6387
|
|
6
|
+
sidra_sql/models.py,sha256=WsUFiBldI-hKONLRrZbKoUAoeiYnG_pjYu37i0pqx5M,7519
|
|
7
|
+
sidra_sql/plugin_manager.py,sha256=cV0Bt_VjkW4Y9oB2wyJHFjnGgUl1tbtv-9vCbBfYjmc,6785
|
|
8
|
+
sidra_sql/runner.py,sha256=yQ3Xx-RU7mf72oC7lXQszSpWbmzCDBakiJrvCyZu9R0,2374
|
|
9
|
+
sidra_sql/scaffold.py,sha256=BkdOUKXN5vfRx595RzbeXdiP4Afxg7qV60NsFY1xrOw,8926
|
|
10
|
+
sidra_sql/sidra.py,sha256=kfhtRcSKfNYGEkzUfIwzvu3b_cerBHdYnk7FvbD5oVo,13641
|
|
11
|
+
sidra_sql/storage.py,sha256=No7SWSLQSi4YGFopn-kTP4oVsYqKcS-8frGxqYA3ZlM,6018
|
|
12
|
+
sidra_sql/toml_runner.py,sha256=Hh6OpEpdkSllphwdPd-Ob-AmOqG6CFmK5jEpIazThMg,14075
|
|
13
|
+
sidra_sql/transform_runner.py,sha256=slV5k8XSPdi1NEoJg8whG8vGTA9PTjlW46uTaGTFuBs,5280
|
|
14
|
+
sidra_sql/utils.py,sha256=Y3G1X1JwWuVG6jSY5mtPKHY-9NgaVR-CI_6On6SJWJ8,3495
|
|
15
|
+
sidra_sql/validator.py,sha256=558XeyPA7YwQx8yZYAo29DPlUAMaUGcTfihN0HKJk0c,7070
|
|
16
|
+
sidra_sql-1.3.0.dist-info/METADATA,sha256=5HANlLd_wdfCwDMD8IaXSEohB397nI5OV3R_12IMPQw,32853
|
|
17
|
+
sidra_sql-1.3.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
18
|
+
sidra_sql-1.3.0.dist-info/entry_points.txt,sha256=w7u07NGGQ8izmgvFLJ2SRt9hryVOAMZTbnzmBy8URR0,49
|
|
19
|
+
sidra_sql-1.3.0.dist-info/licenses/LICENSE,sha256=TugvnTQpZyvRbaauokRh_OlHs5J4CHwUv11YhJnHwas,1066
|
|
20
|
+
sidra_sql-1.3.0.dist-info/RECORD,,
|