nia-etl-utils 0.1.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.
- nia_etl_utils/__init__.py +126 -0
- nia_etl_utils/database.py +246 -0
- nia_etl_utils/email_smtp.py +126 -0
- nia_etl_utils/env_config.py +43 -0
- nia_etl_utils/limpeza_pastas.py +137 -0
- nia_etl_utils/logger_config.py +146 -0
- nia_etl_utils/processa_csv.py +209 -0
- nia_etl_utils/processa_csv_paralelo.py +151 -0
- nia_etl_utils-0.1.0.dist-info/METADATA +594 -0
- nia_etl_utils-0.1.0.dist-info/RECORD +12 -0
- nia_etl_utils-0.1.0.dist-info/WHEEL +5 -0
- nia_etl_utils-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nia-etl-utils
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Utilitários compartilhados para pipelines ETL do NIA/MPRJ
|
|
5
|
+
Author-email: Nícolas Esmael <nicolas.esmael@mprj.mp.br>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Repository, https://gitlab-dti.mprj.mp.br/nia/etl-nia/nia-etl-utils
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: loguru>=0.7.0
|
|
11
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
12
|
+
Requires-Dist: cx-Oracle>=8.3.0
|
|
13
|
+
Requires-Dist: psycopg2-binary>=2.9.0
|
|
14
|
+
Requires-Dist: sqlalchemy>=2.0.0
|
|
15
|
+
Requires-Dist: pandas>=2.0.0
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
19
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
20
|
+
|
|
21
|
+
# nia-etl-utils
|
|
22
|
+
|
|
23
|
+
## ✨ Visão Geral
|
|
24
|
+
|
|
25
|
+
Biblioteca Python centralizada contendo **utilitários compartilhados** para pipelines ETL do NIA/MPRJ. Consolida funções reutilizáveis para configuração de ambiente, notificações por email, conexões de banco de dados, logging padronizado e processamento de dados.
|
|
26
|
+
|
|
27
|
+
Desenvolvida para **eliminar duplicação de código**, **padronizar boas práticas** e **facilitar manutenção** em todos os projetos de engenharia de dados do NIA.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 📂 Estrutura do Projeto
|
|
32
|
+
|
|
33
|
+
```plaintext
|
|
34
|
+
.
|
|
35
|
+
├── src/
|
|
36
|
+
│ └── nia_etl_utils/ # Pacote principal
|
|
37
|
+
│ ├── __init__.py # Exporta funções principais
|
|
38
|
+
│ ├── env_config.py # Gerenciamento de variáveis de ambiente
|
|
39
|
+
│ ├── email_smtp.py # Envio de emails via SMTP
|
|
40
|
+
│ ├── database.py # Conexões PostgreSQL e Oracle
|
|
41
|
+
│ ├── logger_config.py # Configuração de logging com Loguru
|
|
42
|
+
│ ├── processa_csv.py # Processamento e exportação de CSV
|
|
43
|
+
│ ├── processa_csv_paralelo.py # Processamento paralelo de CSV grandes
|
|
44
|
+
│ └── limpeza_pastas.py # Manipulação de arquivos e diretórios
|
|
45
|
+
│
|
|
46
|
+
├── tests/ # Testes unitários (~60+ testes)
|
|
47
|
+
│ ├── conftest.py # Fixtures compartilhadas
|
|
48
|
+
│ ├── test_env_config.py # Testes de variáveis de ambiente
|
|
49
|
+
│ ├── test_email_smtp.py # Testes de email (com mocks)
|
|
50
|
+
│ ├── test_database.py # Testes de conexões (com mocks)
|
|
51
|
+
│ ├── test_logger_config.py # Testes de logging
|
|
52
|
+
│ ├── test_processa_csv.py # Testes de processamento CSV
|
|
53
|
+
│ ├── test_processa_csv_paralelo.py # Testes de processamento paralelo
|
|
54
|
+
│ ├── test_limpeza_pastas.py # Testes de manipulação de arquivos
|
|
55
|
+
│ └── README.md # Documentação dos testes
|
|
56
|
+
│
|
|
57
|
+
├── .env.example # Template de variáveis de ambiente
|
|
58
|
+
├── .gitignore # Arquivos ignorados pelo Git
|
|
59
|
+
├── .gitlab-ci.yml # Pipeline CI/CD (testes + cobertura)
|
|
60
|
+
├── .python-version # Versão Python do projeto (3.13.3)
|
|
61
|
+
├── pyproject.toml # Configuração do pacote Python
|
|
62
|
+
├── requirements.txt # Dependências do projeto
|
|
63
|
+
├── run_tests.sh # Script helper para executar testes
|
|
64
|
+
└── README.md
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 🔧 Módulos Disponíveis
|
|
70
|
+
|
|
71
|
+
### 1️⃣ Configuração de Ambiente (`env_config.py`)
|
|
72
|
+
|
|
73
|
+
Gerenciamento robusto de variáveis de ambiente com validação e falha explícita.
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from nia_etl_utils import obter_variavel_env
|
|
77
|
+
|
|
78
|
+
# Variável obrigatória (falha com sys.exit(1) se não existir)
|
|
79
|
+
db_host = obter_variavel_env('DB_POSTGRESQL_HOST')
|
|
80
|
+
|
|
81
|
+
# Variável opcional com fallback
|
|
82
|
+
porta = obter_variavel_env('DB_PORT', default='5432')
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Características:**
|
|
86
|
+
- ✅ Falha rápida com `sys.exit(1)` quando variável obrigatória não existe
|
|
87
|
+
- ✅ Suporte a valores padrão opcionais
|
|
88
|
+
- ✅ Logs descritivos de erro
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### 2️⃣ Email SMTP (`email_smtp.py`)
|
|
93
|
+
|
|
94
|
+
Envio de emails com ou sem anexo, suportando destinatários configuráveis via env var.
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from nia_etl_utils import enviar_email_smtp
|
|
98
|
+
|
|
99
|
+
# Uso padrão (destinatários da env var EMAIL_DESTINATARIOS)
|
|
100
|
+
enviar_email_smtp(
|
|
101
|
+
corpo_do_email="Pipeline concluído com sucesso",
|
|
102
|
+
assunto="[PROD] ETL Finalizado"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Com destinatários específicos e anexo
|
|
106
|
+
enviar_email_smtp(
|
|
107
|
+
destinatarios=["diretor@mprj.mp.br"],
|
|
108
|
+
corpo_do_email="Relatório executivo anexo",
|
|
109
|
+
assunto="Relatório Mensal",
|
|
110
|
+
anexo="/tmp/relatorio.pdf"
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Características:**
|
|
115
|
+
- ✅ Destinatários configuráveis via `EMAIL_DESTINATARIOS`
|
|
116
|
+
- ✅ Suporte a anexos
|
|
117
|
+
- ✅ Falha explícita com `sys.exit(1)` em erros SMTP
|
|
118
|
+
- ✅ Validação de arquivos anexos
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### 3️⃣ Conexões de Banco (`database.py`)
|
|
123
|
+
|
|
124
|
+
Conexões padronizadas para PostgreSQL (psycopg2 + SQLAlchemy) e Oracle (cx_Oracle).
|
|
125
|
+
|
|
126
|
+
#### PostgreSQL
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from nia_etl_utils import conectar_postgresql_nia, fechar_conexao
|
|
130
|
+
|
|
131
|
+
# Conecta no PostgreSQL do NIA
|
|
132
|
+
cur, conn = conectar_postgresql_nia()
|
|
133
|
+
cur.execute("SELECT * FROM tabela")
|
|
134
|
+
resultados = cur.fetchall()
|
|
135
|
+
fechar_conexao(cur, conn)
|
|
136
|
+
|
|
137
|
+
# Engine SQLAlchemy (para pandas)
|
|
138
|
+
from nia_etl_utils import obter_engine_postgresql_nia
|
|
139
|
+
import pandas as pd
|
|
140
|
+
|
|
141
|
+
engine = obter_engine_postgresql_nia()
|
|
142
|
+
df = pd.read_sql("SELECT * FROM tabela", engine)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### Oracle
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from nia_etl_utils import conectar_oracle, fechar_conexao
|
|
149
|
+
|
|
150
|
+
# Conecta no Oracle
|
|
151
|
+
cur, conn = conectar_oracle()
|
|
152
|
+
cur.execute("SELECT * FROM tabela WHERE ROWNUM <= 10")
|
|
153
|
+
resultados = cur.fetchall()
|
|
154
|
+
fechar_conexao(cur, conn)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### Bancos Adicionais (Genérico)
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from nia_etl_utils import conectar_postgresql
|
|
161
|
+
|
|
162
|
+
# Conecta em qualquer PostgreSQL configurado com sufixo customizado
|
|
163
|
+
# Requer: DB_POSTGRESQL_HOST_SUFIXO, DB_POSTGRESQL_PORT_SUFIXO, etc
|
|
164
|
+
cur, conn = conectar_postgresql("_SUFIXO")
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Características:**
|
|
168
|
+
- ✅ Funções genéricas + wrappers de conveniência
|
|
169
|
+
- ✅ Suporte a múltiplos bancos PostgreSQL (via sufixos)
|
|
170
|
+
- ✅ Logs informativos de conexão
|
|
171
|
+
- ✅ Falha explícita com `sys.exit(1)` em erros de conexão
|
|
172
|
+
- ✅ `fechar_conexao()` segura (não falha se erro ao fechar)
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
### 4️⃣ Logging (`logger_config.py`)
|
|
177
|
+
|
|
178
|
+
Configuração padronizada do Loguru com rotação, retenção e níveis customizáveis.
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from nia_etl_utils import configurar_logger_padrao_nia
|
|
182
|
+
from loguru import logger
|
|
183
|
+
|
|
184
|
+
# Configuração rápida com padrões do NIA
|
|
185
|
+
caminho_log = configurar_logger_padrao_nia("ouvidorias_etl")
|
|
186
|
+
logger.info("Pipeline iniciado")
|
|
187
|
+
|
|
188
|
+
# Configuração customizada
|
|
189
|
+
from nia_etl_utils import configurar_logger
|
|
190
|
+
|
|
191
|
+
caminho_log = configurar_logger(
|
|
192
|
+
prefixo="meu_pipeline",
|
|
193
|
+
data_extracao="2025_01_19",
|
|
194
|
+
pasta_logs="/var/logs/nia",
|
|
195
|
+
rotation="50 MB",
|
|
196
|
+
retention="30 days",
|
|
197
|
+
level="INFO"
|
|
198
|
+
)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Características:**
|
|
202
|
+
- ✅ Rotação automática de arquivos por tamanho
|
|
203
|
+
- ✅ Retenção configurável (padrão: 7 dias em DEV, 30 dias em PROD)
|
|
204
|
+
- ✅ Formato padronizado com timestamp, nível, módulo, função e linha
|
|
205
|
+
- ✅ Logs organizados por pipeline e data
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
### 5️⃣ Processamento CSV (`processa_csv.py`)
|
|
210
|
+
|
|
211
|
+
Exportação de DataFrames para CSV com nomenclatura padronizada e validações.
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from nia_etl_utils import exportar_para_csv, extrair_e_exportar_csv
|
|
215
|
+
import pandas as pd
|
|
216
|
+
|
|
217
|
+
# Exportação simples
|
|
218
|
+
df = pd.DataFrame({"col1": [1, 2], "col2": [3, 4]})
|
|
219
|
+
caminho = exportar_para_csv(
|
|
220
|
+
df=df,
|
|
221
|
+
nome_arquivo="dados_clientes",
|
|
222
|
+
data_extracao="2025_01_19",
|
|
223
|
+
diretorio_base="/tmp/dados"
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# Extração + Exportação
|
|
227
|
+
def extrair_dados():
|
|
228
|
+
# ... lógica de extração ...
|
|
229
|
+
return pd.DataFrame({"dados": [1, 2, 3]})
|
|
230
|
+
|
|
231
|
+
caminho = extrair_e_exportar_csv(
|
|
232
|
+
nome_extracao="dados_vendas",
|
|
233
|
+
funcao_extracao=extrair_dados,
|
|
234
|
+
data_extracao="2025_01_19",
|
|
235
|
+
diretorio_base="/tmp/dados",
|
|
236
|
+
falhar_se_vazio=True # sys.exit(1) se DataFrame vazio
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# Múltiplas extrações em lote
|
|
240
|
+
from nia_etl_utils import exportar_multiplos_csv
|
|
241
|
+
|
|
242
|
+
extractions = [
|
|
243
|
+
{"nome": "clientes", "funcao": extrair_clientes},
|
|
244
|
+
{"nome": "vendas", "funcao": extrair_vendas}
|
|
245
|
+
]
|
|
246
|
+
|
|
247
|
+
resultados = exportar_multiplos_csv(
|
|
248
|
+
extractions=extractions,
|
|
249
|
+
data_extracao="2025_01_19",
|
|
250
|
+
diretorio_base="/tmp/dados"
|
|
251
|
+
)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Características:**
|
|
255
|
+
- ✅ Nomenclatura padronizada: `{nome}_{data}.csv`
|
|
256
|
+
- ✅ Criação automática de diretórios
|
|
257
|
+
- ✅ Logs com informações úteis (linhas, colunas, tamanho)
|
|
258
|
+
- ✅ Controle de falha em DataFrames vazios
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
### 6️⃣ Manipulação de Arquivos (`limpeza_pastas.py`)
|
|
263
|
+
|
|
264
|
+
Utilitários para limpeza e criação de diretórios.
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
from nia_etl_utils import limpar_pasta, remover_pasta_recursivamente, criar_pasta_se_nao_existir
|
|
268
|
+
|
|
269
|
+
# Limpa pasta (remove arquivos, mantém subdiretórios)
|
|
270
|
+
limpar_pasta("/tmp/dados")
|
|
271
|
+
|
|
272
|
+
# Remove pasta completa (arquivos + subdiretórios)
|
|
273
|
+
remover_pasta_recursivamente("/tmp/temporario")
|
|
274
|
+
|
|
275
|
+
# Cria pasta se não existir (incluindo pais)
|
|
276
|
+
criar_pasta_se_nao_existir("/dados/processados/2025/01")
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Características:**
|
|
280
|
+
- ✅ Uso de `pathlib.Path` (moderno e seguro)
|
|
281
|
+
- ✅ Validações de permissão
|
|
282
|
+
- ✅ Falha explícita com `sys.exit(1)` em erros
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
### 7️⃣ Processamento Paralelo de CSV (`processa_csv_paralelo.py`)
|
|
287
|
+
|
|
288
|
+
Processa arquivos CSV grandes em paralelo usando multiprocessing com chunks otimizados.
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from nia_etl_utils import processar_csv_paralelo
|
|
292
|
+
|
|
293
|
+
# Função de transformação customizada
|
|
294
|
+
def limpar_texto(texto):
|
|
295
|
+
return texto.strip().upper()
|
|
296
|
+
|
|
297
|
+
# Processa CSV grande em paralelo
|
|
298
|
+
processar_csv_paralelo(
|
|
299
|
+
caminho_entrada="dados_brutos.csv",
|
|
300
|
+
caminho_saida="dados_limpos.csv",
|
|
301
|
+
colunas_para_tratar=["nome", "descricao", "observacao"],
|
|
302
|
+
funcao_transformacao=limpar_texto,
|
|
303
|
+
remover_entrada=True # Remove arquivo original após processar
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# Com configurações customizadas
|
|
307
|
+
processar_csv_paralelo(
|
|
308
|
+
caminho_entrada="dados_gigantes.csv",
|
|
309
|
+
caminho_saida="dados_processados.csv",
|
|
310
|
+
colunas_para_tratar=["texto"],
|
|
311
|
+
funcao_transformacao=limpar_texto,
|
|
312
|
+
chunksize=5000, # Tamanho customizado de chunk
|
|
313
|
+
num_processos=4, # Número de processos paralelos
|
|
314
|
+
normalizar_colunas=False, # Mantém case original das colunas
|
|
315
|
+
remover_entrada=False # Preserva arquivo de entrada
|
|
316
|
+
)
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
**Características:**
|
|
320
|
+
- ✅ Processamento paralelo automático usando `multiprocessing.Pool`
|
|
321
|
+
- ✅ Chunksize calculado automaticamente baseado no tamanho do arquivo
|
|
322
|
+
- ✅ Heurística inteligente:
|
|
323
|
+
- Arquivos < 500MB: chunks de 10.000 linhas
|
|
324
|
+
- Arquivos 500MB-2GB: chunks de 5.000 linhas
|
|
325
|
+
- Arquivos 2-5GB: chunks de 2.000 linhas
|
|
326
|
+
- Arquivos > 5GB: chunks de 1.000 linhas
|
|
327
|
+
- ✅ Normalização opcional de nomes de colunas (lowercase)
|
|
328
|
+
- ✅ Remoção opcional do arquivo de entrada
|
|
329
|
+
- ✅ Logs informativos de progresso
|
|
330
|
+
- ✅ Suporta qualquer função de transformação customizada
|
|
331
|
+
|
|
332
|
+
**Quando usar:**
|
|
333
|
+
- 📊 Arquivos CSV com milhões de linhas
|
|
334
|
+
- 🔄 Transformações pesadas em texto (limpeza, normalização)
|
|
335
|
+
- ⚡ Necessidade de processar múltiplas colunas rapidamente
|
|
336
|
+
- 💾 Arquivos que não cabem confortavelmente na memória
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## 📦 Instalação
|
|
341
|
+
|
|
342
|
+
### Via GitLab (Recomendado)
|
|
343
|
+
|
|
344
|
+
```bash
|
|
345
|
+
# Instalar versão específica
|
|
346
|
+
pip install git+https://gitlab-dti.mprj.mp.br/nia/etl-nia/nia-etl-utils.git@v0.1.0
|
|
347
|
+
|
|
348
|
+
# Ou no requirements.txt
|
|
349
|
+
nia-etl-utils @ git+https://gitlab-dti.mprj.mp.br/nia/etl-nia/nia-etl-utils.git@v0.1.0
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Modo Desenvolvimento
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
git clone https://gitlab-dti.mprj.mp.br/nia/etl-nia/nia-etl-utils.git
|
|
356
|
+
cd nia-etl-utils
|
|
357
|
+
pip install -e ".[dev]"
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## ⚙️ Configuração
|
|
363
|
+
|
|
364
|
+
### 1. Criar arquivo `.env`
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
cp .env.example .env
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### 2. Configurar variáveis de ambiente
|
|
371
|
+
|
|
372
|
+
```env
|
|
373
|
+
# Email SMTP
|
|
374
|
+
MAIL_SMTP_SERVER=smtp.mprj.mp.br
|
|
375
|
+
MAIL_SMTP_PORT=587
|
|
376
|
+
MAIL_SENDER=etl@mprj.mp.br
|
|
377
|
+
EMAIL_DESTINATARIOS=equipe@mprj.mp.br,gestor@mprj.mp.br
|
|
378
|
+
|
|
379
|
+
# PostgreSQL - NIA
|
|
380
|
+
DB_POSTGRESQL_HOST=postgres-nia.mprj.mp.br
|
|
381
|
+
DB_POSTGRESQL_PORT=5432
|
|
382
|
+
DB_POSTGRESQL_DATABASE=nia_database
|
|
383
|
+
DB_POSTGRESQL_USER=usuario
|
|
384
|
+
DB_POSTGRESQL_PASSWORD=senha
|
|
385
|
+
|
|
386
|
+
# PostgreSQL - OpenGeo
|
|
387
|
+
DB_POSTGRESQL_HOST_OPENGEO=postgres-opengeo.mprj.mp.br
|
|
388
|
+
DB_POSTGRESQL_PORT_OPENGEO=5432
|
|
389
|
+
DB_POSTGRESQL_DATABASE_OPENGEO=opengeo_database
|
|
390
|
+
DB_POSTGRESQL_USER_OPENGEO=usuario
|
|
391
|
+
DB_POSTGRESQL_PASSWORD_OPENGEO=senha
|
|
392
|
+
|
|
393
|
+
# Oracle
|
|
394
|
+
DB_ORACLE_HOST=oracle.mprj.mp.br
|
|
395
|
+
DB_ORACLE_PORT=1521
|
|
396
|
+
DB_ORACLE_SERVICE_NAME=ORCL
|
|
397
|
+
DB_ORACLE_USER=usuario
|
|
398
|
+
DB_ORACLE_PASSWORD=senha
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## 🧪 Testes
|
|
404
|
+
|
|
405
|
+
### Executar Testes
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
# Todos os testes
|
|
409
|
+
pytest
|
|
410
|
+
|
|
411
|
+
# Com cobertura
|
|
412
|
+
pytest --cov=src/nia_etl_utils --cov-report=term-missing
|
|
413
|
+
|
|
414
|
+
# Ou usar o script helper
|
|
415
|
+
./run_tests.sh --coverage --verbose
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Cobertura Atual
|
|
419
|
+
|
|
420
|
+
- **~70 testes unitários** (incluindo testes de processamento paralelo)
|
|
421
|
+
- **~90% de cobertura** de código
|
|
422
|
+
- Testes com mocks (sem dependência de banco/SMTP real)
|
|
423
|
+
|
|
424
|
+
Veja `tests/README.md` para documentação completa dos testes.
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## 🚀 Exemplo de Uso Completo
|
|
429
|
+
|
|
430
|
+
```python
|
|
431
|
+
from nia_etl_utils import (
|
|
432
|
+
configurar_logger_padrao_nia,
|
|
433
|
+
obter_variavel_env,
|
|
434
|
+
conectar_postgresql_nia,
|
|
435
|
+
exportar_para_csv,
|
|
436
|
+
processar_csv_paralelo,
|
|
437
|
+
fechar_conexao
|
|
438
|
+
)
|
|
439
|
+
from loguru import logger
|
|
440
|
+
import pandas as pd
|
|
441
|
+
|
|
442
|
+
# 1. Configura logging
|
|
443
|
+
configurar_logger_padrao_nia("meu_pipeline")
|
|
444
|
+
|
|
445
|
+
# 2. Conecta no banco
|
|
446
|
+
logger.info("Iniciando conexão com banco de dados...")
|
|
447
|
+
cur, conn = conectar_postgresql_nia()
|
|
448
|
+
|
|
449
|
+
# 3. Extrai dados
|
|
450
|
+
logger.info("Extraindo dados...")
|
|
451
|
+
cur.execute("SELECT * FROM tabela WHERE data >= CURRENT_DATE - 7")
|
|
452
|
+
resultados = cur.fetchall()
|
|
453
|
+
colunas = [desc[0] for desc in cur.description]
|
|
454
|
+
df = pd.DataFrame(resultados, columns=colunas)
|
|
455
|
+
|
|
456
|
+
# 4. Fecha conexão
|
|
457
|
+
fechar_conexao(cur, conn)
|
|
458
|
+
logger.info(f"Extração concluída: {len(df)} registros")
|
|
459
|
+
|
|
460
|
+
# 5. Exporta CSV
|
|
461
|
+
from datetime import datetime
|
|
462
|
+
data_hoje = datetime.now().strftime("%Y_%m_%d")
|
|
463
|
+
|
|
464
|
+
caminho = exportar_para_csv(
|
|
465
|
+
df=df,
|
|
466
|
+
nome_arquivo="dados_extraidos",
|
|
467
|
+
data_extracao=data_hoje,
|
|
468
|
+
diretorio_base="/dados/processados"
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
# 6. Processa CSV em paralelo (se necessário)
|
|
472
|
+
if len(df) > 100000: # Só paraleliza arquivos grandes
|
|
473
|
+
def limpar_descricao(texto):
|
|
474
|
+
return texto.strip().upper() if texto else ""
|
|
475
|
+
|
|
476
|
+
processar_csv_paralelo(
|
|
477
|
+
caminho_entrada=caminho,
|
|
478
|
+
caminho_saida=f"/dados/processados/dados_limpos_{data_hoje}.csv",
|
|
479
|
+
colunas_para_tratar=["descricao", "observacao"],
|
|
480
|
+
funcao_transformacao=limpar_descricao,
|
|
481
|
+
remover_entrada=True
|
|
482
|
+
)
|
|
483
|
+
logger.success("Processamento paralelo concluído!")
|
|
484
|
+
|
|
485
|
+
logger.success(f"Pipeline concluído! Arquivo: {caminho}")
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## ☁️ Integração com Airflow
|
|
491
|
+
|
|
492
|
+
### Usando em KubernetesPodOperator
|
|
493
|
+
|
|
494
|
+
```python
|
|
495
|
+
from airflow.providers.cncf.kubernetes.operators.kubernetes_pod import KubernetesPodOperator
|
|
496
|
+
|
|
497
|
+
task = KubernetesPodOperator(
|
|
498
|
+
task_id="meu_etl",
|
|
499
|
+
name="meu-etl-pod",
|
|
500
|
+
namespace="airflow-nia-stage",
|
|
501
|
+
image="python:3.13.3",
|
|
502
|
+
cmds=[
|
|
503
|
+
"sh", "-c",
|
|
504
|
+
"pip install git+https://gitlab-dti.mprj.mp.br/nia/etl-nia/nia-etl-utils.git@v0.1.0 && "
|
|
505
|
+
"python src/extract.py"
|
|
506
|
+
],
|
|
507
|
+
env_vars={
|
|
508
|
+
"DB_POSTGRESQL_HOST": "...",
|
|
509
|
+
"EMAIL_DESTINATARIOS": "equipe@mprj.mp.br"
|
|
510
|
+
},
|
|
511
|
+
# ... outras configs
|
|
512
|
+
)
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
## ⚙️ Tecnologias Utilizadas
|
|
518
|
+
|
|
519
|
+
- Python 3.13.3
|
|
520
|
+
- Loguru (logging)
|
|
521
|
+
- python-dotenv (env vars)
|
|
522
|
+
- cx_Oracle (Oracle)
|
|
523
|
+
- psycopg2 (PostgreSQL)
|
|
524
|
+
- SQLAlchemy (engines)
|
|
525
|
+
- pandas (processamento de dados)
|
|
526
|
+
- pytest + pytest-cov (testes)
|
|
527
|
+
- ruff (linting)
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
531
|
+
## 📋 Versionamento
|
|
532
|
+
|
|
533
|
+
Este projeto usa [Semantic Versioning](https://semver.org/):
|
|
534
|
+
|
|
535
|
+
- **MAJOR**: Mudanças incompatíveis na API
|
|
536
|
+
- **MINOR**: Novas funcionalidades (retrocompatíveis)
|
|
537
|
+
- **PATCH**: Correções de bugs
|
|
538
|
+
|
|
539
|
+
**Versão atual:** `v0.1.0`
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
## 🔔 Monitoramento e Logs
|
|
544
|
+
|
|
545
|
+
- Logging estruturado via Loguru
|
|
546
|
+
- Logs organizados por pipeline e data em `/logs`
|
|
547
|
+
- Scripts retornam `sys.exit(1)` em falhas para integração com Airflow
|
|
548
|
+
- Notificações via email em pipelines de produção
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
## 🔧 CI/CD
|
|
553
|
+
|
|
554
|
+
Pipeline automatizado no GitLab com:
|
|
555
|
+
|
|
556
|
+
- ✅ Testes unitários (pytest)
|
|
557
|
+
- ✅ Cobertura de código (>= 80%)
|
|
558
|
+
- ✅ Linting (ruff)
|
|
559
|
+
- ✅ Relatórios de cobertura (HTML + XML)
|
|
560
|
+
- ✅ Execução em branches e merge requests
|
|
561
|
+
|
|
562
|
+
---
|
|
563
|
+
|
|
564
|
+
## ✏️ Contribuição
|
|
565
|
+
|
|
566
|
+
Merge requests são bem-vindos. Sempre crie uma branch a partir de `main`.
|
|
567
|
+
|
|
568
|
+
### Checklist para Contribuir:
|
|
569
|
+
|
|
570
|
+
- [ ] Testes passam: `pytest`
|
|
571
|
+
- [ ] Cobertura >= 70%: `pytest --cov=src/nia_etl_utils --cov-fail-under=80`
|
|
572
|
+
- [ ] Lint OK: `ruff check src/ tests/`
|
|
573
|
+
- [ ] Commits semânticos: `feat:`, `fix:`, `refactor:`, etc.
|
|
574
|
+
- [ ] Documentação atualizada
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
## 🔐 Licença
|
|
579
|
+
|
|
580
|
+
Projeto de uso interno do MPRJ. Sem licença pública.
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
## ✨ Responsável Técnico
|
|
585
|
+
|
|
586
|
+
**Nícolas Galdino Esmael** | Engenheiro de Dados - NIA | MPRJ
|
|
587
|
+
|
|
588
|
+
---
|
|
589
|
+
|
|
590
|
+
## 📚 Documentação Adicional
|
|
591
|
+
|
|
592
|
+
- [Documentação de Testes](tests/README.md)
|
|
593
|
+
- [Template de Variáveis de Ambiente](.env.example)
|
|
594
|
+
- [Configuração do Projeto](pyproject.toml)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
nia_etl_utils/__init__.py,sha256=PzeQS2LIdUtj99uUKJVASYFkYaCYaA-Nn82sQ6c83QA,3124
|
|
2
|
+
nia_etl_utils/database.py,sha256=XRE6n8mCoknw3r0bLQrYUIwR-TpZrrcPTc4AImTbbAM,8349
|
|
3
|
+
nia_etl_utils/email_smtp.py,sha256=dkiPlmJJLdRVjaxo-ge54GQGsqThijwvExcfK263EOk,4488
|
|
4
|
+
nia_etl_utils/env_config.py,sha256=MRXxeKXKEaPhc8mHGboQ6NGUcMKTx7rsh68RVmYOKBo,1438
|
|
5
|
+
nia_etl_utils/limpeza_pastas.py,sha256=w4Ky1cEkiB2630eNLDrimXw0NCtrn6HSvV11HEDVbPE,4540
|
|
6
|
+
nia_etl_utils/logger_config.py,sha256=2IWvmQQ76yIlfGjPX9bcQLWtQZeI1nA5XiMyjFC-zMI,5098
|
|
7
|
+
nia_etl_utils/processa_csv.py,sha256=JgYaPN71Nu4v7qQeaYsfTCzJ-yAOAb5kQBoUYjEHUfo,6959
|
|
8
|
+
nia_etl_utils/processa_csv_paralelo.py,sha256=hCjVdX_LIkq1YVPxXrXLmc0gIKoaf4i-jLFAWVCNjKY,5146
|
|
9
|
+
nia_etl_utils-0.1.0.dist-info/METADATA,sha256=4pFYjkKZ33xTJqDJiOKeW0VPZHTBaiQM1lfNbLtpVUM,17091
|
|
10
|
+
nia_etl_utils-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
+
nia_etl_utils-0.1.0.dist-info/top_level.txt,sha256=LYLtk9Gh-GaiyyQkwpVs1CoOtnn1G9t8_ijxcFHyjfY,14
|
|
12
|
+
nia_etl_utils-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nia_etl_utils
|