nia-etl-utils 0.2.0__tar.gz → 0.2.1__tar.gz

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.
Files changed (33) hide show
  1. nia_etl_utils-0.2.1/PKG-INFO +723 -0
  2. nia_etl_utils-0.2.1/README.md +680 -0
  3. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/pyproject.toml +3 -1
  4. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/src/nia_etl_utils/__init__.py +21 -1
  5. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/src/nia_etl_utils/exceptions.py +67 -0
  6. nia_etl_utils-0.2.1/src/nia_etl_utils/ocr.py +401 -0
  7. nia_etl_utils-0.2.1/src/nia_etl_utils.egg-info/PKG-INFO +723 -0
  8. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/src/nia_etl_utils.egg-info/SOURCES.txt +2 -0
  9. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/src/nia_etl_utils.egg-info/requires.txt +2 -0
  10. nia_etl_utils-0.2.1/tests/test_ocr.py +426 -0
  11. nia_etl_utils-0.2.0/PKG-INFO +0 -615
  12. nia_etl_utils-0.2.0/README.md +0 -574
  13. nia_etl_utils-0.2.0/src/nia_etl_utils.egg-info/PKG-INFO +0 -615
  14. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/setup.cfg +0 -0
  15. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/setup.py +0 -0
  16. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/src/nia_etl_utils/config.py +0 -0
  17. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/src/nia_etl_utils/database.py +0 -0
  18. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/src/nia_etl_utils/email_smtp.py +0 -0
  19. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/src/nia_etl_utils/env_config.py +0 -0
  20. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/src/nia_etl_utils/limpeza_pastas.py +0 -0
  21. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/src/nia_etl_utils/logger_config.py +0 -0
  22. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/src/nia_etl_utils/processa_csv.py +0 -0
  23. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/src/nia_etl_utils/processa_csv_paralelo.py +0 -0
  24. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/src/nia_etl_utils/results.py +0 -0
  25. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/src/nia_etl_utils.egg-info/dependency_links.txt +0 -0
  26. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/src/nia_etl_utils.egg-info/top_level.txt +0 -0
  27. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/tests/test_database.py +0 -0
  28. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/tests/test_email_smtp.py +0 -0
  29. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/tests/test_env_config.py +0 -0
  30. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/tests/test_limpeza_pastas.py +0 -0
  31. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/tests/test_logger_config.py +0 -0
  32. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/tests/test_processa_csv.py +0 -0
  33. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.1}/tests/test_processa_csv_paralelo.py +0 -0
@@ -0,0 +1,723 @@
1
+ Metadata-Version: 2.4
2
+ Name: nia-etl-utils
3
+ Version: 0.2.1
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
+ Project-URL: Documentation, https://gitlab-dti.mprj.mp.br/nia/etl-nia/nia-etl-utils/-/blob/main/README.md
9
+ Project-URL: Changelog, https://gitlab-dti.mprj.mp.br/nia/etl-nia/nia-etl-utils/-/blob/main/CHANGELOG.md
10
+ Keywords: etl,data-engineering,pipeline,mprj,nia
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Database
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ Requires-Dist: loguru>=0.7.0
26
+ Requires-Dist: python-dotenv>=1.0.0
27
+ Requires-Dist: cx-Oracle>=8.3.0
28
+ Requires-Dist: psycopg2-binary>=2.9.0
29
+ Requires-Dist: sqlalchemy>=2.0.0
30
+ Requires-Dist: pandas>=2.0.0
31
+ Requires-Dist: requests>=2.28.0
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
34
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
35
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
36
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
37
+ Requires-Dist: pandas-stubs>=2.0.0; extra == "dev"
38
+ Requires-Dist: types-requests>=2.28.0; extra == "dev"
39
+ Provides-Extra: docs
40
+ Requires-Dist: mkdocs>=1.5.0; extra == "docs"
41
+ Requires-Dist: mkdocs-material>=9.0.0; extra == "docs"
42
+ Requires-Dist: mkdocstrings[python]>=0.24.0; extra == "docs"
43
+
44
+ # Módulos Utilitários do NIA
45
+
46
+ ## Visão Geral
47
+
48
+ 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.
49
+
50
+ 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.
51
+
52
+ ---
53
+
54
+ ## Novidades da v0.2.1
55
+
56
+ - **Módulo OCR** (`executar_ocr`) — processamento de OCR via API IntelliDoc com suporte a PDF, imagens e detecção automática de formato
57
+ - **Exceções de OCR** (`OcrError`, `OcrSubmissaoError`, `OcrProcessamentoError`, `OcrTimeoutError`) — tratamento granular de erros de OCR
58
+
59
+ ## Novidades da v0.2.0
60
+
61
+ - **Dataclasses de configuração** (`PostgresConfig`, `OracleConfig`, `SmtpConfig`, `LogConfig`) — configurações imutáveis e type-safe
62
+ - **Exceções customizadas hierárquicas** — tratamento de erros mais preciso e informativo
63
+ - **Dataclasses de resultado** (`Conexao`, `ResultadoExtracao`, `ResultadoLote`, `ResultadoEmail`)
64
+ - **Funções adicionais de env** — `obter_variavel_env_int`, `obter_variavel_env_bool`, `obter_variavel_env_lista`
65
+ - **Context managers** para conexões de banco — fechamento automático e seguro
66
+
67
+ ---
68
+
69
+ ## Estrutura do Projeto
70
+
71
+ ```plaintext
72
+ .
73
+ ├── src/
74
+ │ └── nia_etl_utils/
75
+ │ ├── __init__.py # Exporta funções principais
76
+ │ ├── config.py # Dataclasses de configuração
77
+ │ ├── exceptions.py # Exceções customizadas
78
+ │ ├── results.py # Dataclasses de resultado
79
+ │ ├── env_config.py # Gerenciamento de variáveis de ambiente
80
+ │ ├── email_smtp.py # Envio de emails via SMTP
81
+ │ ├── database.py # Conexões PostgreSQL e Oracle
82
+ │ ├── logger_config.py # Configuração de logging com Loguru
83
+ │ ├── processa_csv.py # Processamento e exportação de CSV
84
+ │ ├── processa_csv_paralelo.py # Processamento paralelo de CSV grandes
85
+ │ ├── limpeza_pastas.py # Manipulação de arquivos e diretórios
86
+ │ └── ocr.py # OCR via API IntelliDoc
87
+
88
+ ├── tests/ # Testes unitários
89
+ ├── .gitlab-ci.yml # Pipeline CI/CD
90
+ ├── pyproject.toml # Configuração do pacote
91
+ └── README.md
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Módulos Disponíveis
97
+
98
+ ### 1. Configuração de Ambiente (`env_config.py`)
99
+
100
+ Gerenciamento robusto de variáveis de ambiente com validação e tipagem.
101
+
102
+ ```python
103
+ from nia_etl_utils import (
104
+ obter_variavel_env,
105
+ obter_variavel_env_int,
106
+ obter_variavel_env_bool,
107
+ obter_variavel_env_lista
108
+ )
109
+
110
+ # String obrigatória (falha com sys.exit(1) se não existir)
111
+ db_host = obter_variavel_env('DB_POSTGRESQL_HOST')
112
+
113
+ # String opcional com fallback
114
+ porta = obter_variavel_env('DB_PORT', default='5432')
115
+
116
+ # Inteiro
117
+ max_conexoes = obter_variavel_env_int('MAX_CONEXOES', default=10)
118
+
119
+ # Booleano (aceita: true/false, 1/0, yes/no, on/off)
120
+ debug = obter_variavel_env_bool('DEBUG_MODE', default=False)
121
+
122
+ # Lista (separada por vírgula)
123
+ destinatarios = obter_variavel_env_lista('EMAIL_DESTINATARIOS')
124
+ # ['email1@mprj.mp.br', 'email2@mprj.mp.br']
125
+ ```
126
+
127
+ ---
128
+
129
+ ### 2. Dataclasses de Configuração (`config.py`)
130
+
131
+ Configurações imutáveis e type-safe com factory methods.
132
+
133
+ ```python
134
+ from nia_etl_utils import PostgresConfig, OracleConfig, SmtpConfig, LogConfig
135
+
136
+ # Configuração explícita (recomendado para testes)
137
+ config = PostgresConfig(
138
+ host="localhost",
139
+ port="5432",
140
+ database="teste",
141
+ user="user",
142
+ password="pass"
143
+ )
144
+
145
+ # Configuração via variáveis de ambiente (recomendado para produção)
146
+ config = PostgresConfig.from_env() # usa DB_POSTGRESQL_*
147
+ config = PostgresConfig.from_env("_OPENGEO") # usa DB_POSTGRESQL_*_OPENGEO
148
+
149
+ # Connection string para SQLAlchemy
150
+ print(config.connection_string)
151
+ # postgresql+psycopg2://user:pass@localhost:5432/teste
152
+
153
+ # Oracle
154
+ oracle_config = OracleConfig.from_env()
155
+
156
+ # SMTP
157
+ smtp_config = SmtpConfig.from_env()
158
+
159
+ # Logging com padrões NIA
160
+ log_config = LogConfig.padrao_nia("meu_pipeline")
161
+ ```
162
+
163
+ ---
164
+
165
+ ### 3. Exceções Customizadas (`exceptions.py`)
166
+
167
+ Hierarquia de exceções para tratamento preciso de erros.
168
+
169
+ ```python
170
+ from nia_etl_utils import (
171
+ # Base
172
+ NiaEtlError,
173
+ # Configuração
174
+ ConfiguracaoError,
175
+ VariavelAmbienteError,
176
+ # Database
177
+ DatabaseError,
178
+ ConexaoError,
179
+ # Arquivos
180
+ ArquivoError,
181
+ DiretorioError,
182
+ EscritaArquivoError,
183
+ LeituraArquivoError,
184
+ # Extração
185
+ ExtracaoError,
186
+ ExtracaoVaziaError,
187
+ ProcessamentoError,
188
+ # Email
189
+ EmailError,
190
+ DestinatarioError,
191
+ SmtpError,
192
+ # Validação
193
+ ValidacaoError,
194
+ )
195
+ from nia_etl_utils.ocr import (
196
+ # OCR
197
+ OcrError,
198
+ OcrSubmissaoError,
199
+ OcrProcessamentoError,
200
+ OcrTimeoutError,
201
+ )
202
+
203
+ # Uso em try/except
204
+ try:
205
+ config = PostgresConfig.from_env("_INEXISTENTE")
206
+ except ConfiguracaoError as e:
207
+ logger.error(f"Configuração inválida: {e}")
208
+ logger.debug(f"Detalhes: {e.details}")
209
+
210
+ # Exceções incluem contexto
211
+ try:
212
+ with conectar_postgresql(config) as conn:
213
+ conn.cursor.execute("SELECT * FROM tabela")
214
+ except ConexaoError as e:
215
+ # e.details contém informações adicionais
216
+ print(e.details) # {'host': 'localhost', 'database': 'teste', ...}
217
+ ```
218
+
219
+ ---
220
+
221
+ ### 4. Dataclasses de Resultado (`results.py`)
222
+
223
+ Estruturas para retorno de operações.
224
+
225
+ ```python
226
+ from nia_etl_utils import Conexao, ResultadoExtracao, ResultadoLote, ResultadoEmail
227
+
228
+ # Conexao - retornada por conectar_postgresql/oracle
229
+ with conectar_postgresql(config) as conn:
230
+ conn.cursor.execute("SELECT 1")
231
+ # conn.cursor e conn.connection disponíveis
232
+ # Fechamento automático ao sair do context manager
233
+
234
+ # ResultadoExtracao - retornado por extrair_e_exportar_csv
235
+ resultado = extrair_e_exportar_csv(...)
236
+ print(resultado.sucesso) # True/False
237
+ print(resultado.caminho) # '/dados/arquivo.csv'
238
+ print(resultado.linhas) # 1500
239
+ print(resultado.tempo_execucao) # 2.34 (segundos)
240
+
241
+ # ResultadoLote - retornado por exportar_multiplos_csv
242
+ lote = exportar_multiplos_csv(...)
243
+ print(lote.total) # 5
244
+ print(lote.sucessos) # 4
245
+ print(lote.falhas) # 1
246
+ print(lote.resultados) # Lista de ResultadoExtracao
247
+ ```
248
+
249
+ ---
250
+
251
+ ### 5. Conexões de Banco (`database.py`)
252
+
253
+ Conexões com context manager para fechamento automático.
254
+
255
+ #### PostgreSQL
256
+
257
+ ```python
258
+ from nia_etl_utils import conectar_postgresql, PostgresConfig
259
+
260
+ # Com configuração explícita
261
+ config = PostgresConfig(
262
+ host="localhost",
263
+ port="5432",
264
+ database="meu_banco",
265
+ user="usuario",
266
+ password="senha"
267
+ )
268
+
269
+ with conectar_postgresql(config) as conn:
270
+ conn.cursor.execute("SELECT * FROM tabela")
271
+ resultados = conn.cursor.fetchall()
272
+ # Conexão fechada automaticamente
273
+
274
+ # Com variáveis de ambiente
275
+ config = PostgresConfig.from_env()
276
+ with conectar_postgresql(config) as conn:
277
+ conn.cursor.execute("SELECT 1")
278
+
279
+ # Wrappers de conveniência (mantidos para retrocompatibilidade)
280
+ from nia_etl_utils import conectar_postgresql_nia, conectar_postgresql_opengeo
281
+
282
+ with conectar_postgresql_nia() as conn:
283
+ conn.cursor.execute("SELECT * FROM ouvidorias")
284
+
285
+ # Engine SQLAlchemy
286
+ from nia_etl_utils import obter_engine_postgresql
287
+ import pandas as pd
288
+
289
+ engine = obter_engine_postgresql(config)
290
+ df = pd.read_sql("SELECT * FROM tabela", engine)
291
+ ```
292
+
293
+ #### Oracle
294
+
295
+ ```python
296
+ from nia_etl_utils import conectar_oracle, OracleConfig
297
+
298
+ config = OracleConfig.from_env()
299
+ with conectar_oracle(config) as conn:
300
+ conn.cursor.execute("SELECT * FROM tabela WHERE ROWNUM <= 10")
301
+ resultados = conn.cursor.fetchall()
302
+ ```
303
+
304
+ ---
305
+
306
+ ### 6. Email SMTP (`email_smtp.py`)
307
+
308
+ Envio de emails com suporte a anexos.
309
+
310
+ ```python
311
+ from nia_etl_utils import enviar_email_smtp
312
+
313
+ # Uso padrão (destinatários da env var EMAIL_DESTINATARIOS)
314
+ enviar_email_smtp(
315
+ corpo_do_email="Pipeline concluído com sucesso",
316
+ assunto="[PROD] ETL Finalizado"
317
+ )
318
+
319
+ # Com destinatários específicos e anexo
320
+ enviar_email_smtp(
321
+ destinatarios=["diretor@mprj.mp.br"],
322
+ corpo_do_email="Relatório executivo anexo",
323
+ assunto="Relatório Mensal",
324
+ anexo="/tmp/relatorio.pdf"
325
+ )
326
+ ```
327
+
328
+ ---
329
+
330
+ ### 7. Logging (`logger_config.py`)
331
+
332
+ Configuração padronizada do Loguru.
333
+
334
+ ```python
335
+ from nia_etl_utils import configurar_logger_padrao_nia, configurar_logger
336
+ from loguru import logger
337
+
338
+ # Configuração rápida com padrões do NIA
339
+ caminho_log = configurar_logger_padrao_nia("ouvidorias_etl")
340
+ logger.info("Pipeline iniciado")
341
+
342
+ # Configuração customizada
343
+ caminho_log = configurar_logger(
344
+ prefixo="meu_pipeline",
345
+ data_extracao="2025_01_20",
346
+ pasta_logs="/var/logs/nia",
347
+ rotation="50 MB",
348
+ retention="30 days",
349
+ level="INFO"
350
+ )
351
+ ```
352
+
353
+ ---
354
+
355
+ ### 8. Processamento CSV (`processa_csv.py`)
356
+
357
+ Exportação de DataFrames para CSV com nomenclatura padronizada.
358
+
359
+ ```python
360
+ from nia_etl_utils import exportar_para_csv, extrair_e_exportar_csv
361
+ import pandas as pd
362
+
363
+ # Exportação simples
364
+ df = pd.DataFrame({"col1": [1, 2], "col2": [3, 4]})
365
+ caminho = exportar_para_csv(
366
+ df=df,
367
+ nome_arquivo="dados_clientes",
368
+ data_extracao="2025_01_20",
369
+ diretorio_base="/tmp/dados"
370
+ )
371
+
372
+ # Extração + Exportação
373
+ def extrair_dados():
374
+ return pd.DataFrame({"dados": [1, 2, 3]})
375
+
376
+ resultado = extrair_e_exportar_csv(
377
+ nome_extracao="dados_vendas",
378
+ funcao_extracao=extrair_dados,
379
+ data_extracao="2025_01_20",
380
+ diretorio_base="/tmp/dados",
381
+ falhar_se_vazio=True
382
+ )
383
+
384
+ # Múltiplas extrações em lote
385
+ from nia_etl_utils import exportar_multiplos_csv
386
+
387
+ extractions = [
388
+ {"nome": "clientes", "funcao": extrair_clientes},
389
+ {"nome": "vendas", "funcao": extrair_vendas}
390
+ ]
391
+
392
+ lote = exportar_multiplos_csv(
393
+ extractions=extractions,
394
+ data_extracao="2025_01_20",
395
+ diretorio_base="/tmp/dados"
396
+ )
397
+ ```
398
+
399
+ ---
400
+
401
+ ### 9. Processamento Paralelo de CSV (`processa_csv_paralelo.py`)
402
+
403
+ Processa arquivos CSV grandes em paralelo.
404
+
405
+ ```python
406
+ from nia_etl_utils import processar_csv_paralelo
407
+
408
+ def limpar_texto(texto):
409
+ return texto.strip().upper()
410
+
411
+ processar_csv_paralelo(
412
+ caminho_entrada="dados_brutos.csv",
413
+ caminho_saida="dados_limpos.csv",
414
+ colunas_para_tratar=["nome", "descricao"],
415
+ funcao_transformacao=limpar_texto,
416
+ remover_entrada=True
417
+ )
418
+ ```
419
+
420
+ ---
421
+
422
+ ### 10. Manipulação de Arquivos (`limpeza_pastas.py`)
423
+
424
+ Utilitários para limpeza e criação de diretórios.
425
+
426
+ ```python
427
+ from nia_etl_utils import limpar_pasta, remover_pasta_recursivamente, criar_pasta_se_nao_existir
428
+
429
+ limpar_pasta("/tmp/dados")
430
+ remover_pasta_recursivamente("/tmp/temporario")
431
+ criar_pasta_se_nao_existir("/dados/processados/2025/01")
432
+ ```
433
+
434
+ ---
435
+
436
+ ### 11. OCR via API IntelliDoc (`ocr.py`)
437
+
438
+ Processamento de OCR (Reconhecimento Óptico de Caracteres) via API IntelliDoc do MPRJ.
439
+
440
+ A API processa documentos de forma assíncrona:
441
+ 1. Submete documento → retorna `document_id`
442
+ 2. Consulta status via polling → retorna resultado quando pronto
443
+
444
+ **Formatos suportados:** PDF, JPG, PNG, GIF, BMP, TIFF (detecção automática via magic bytes)
445
+
446
+ ```python
447
+ from nia_etl_utils import executar_ocr
448
+ from nia_etl_utils.ocr import OcrError, OcrTimeoutError
449
+
450
+ # Uso básico com variável de ambiente
451
+ with open("documento.pdf", "rb") as f:
452
+ resultado = executar_ocr(
453
+ conteudo=f.read(),
454
+ url_base="INTELLIDOC_URL", # nome da env var
455
+ )
456
+
457
+ print(resultado["full_text"]) # Texto extraído completo
458
+ print(resultado["overall_quality"]) # Qualidade do OCR (0-1)
459
+ print(resultado["total_pages"]) # Número de páginas
460
+
461
+ # Uso com URL direta e configurações customizadas
462
+ resultado = executar_ocr(
463
+ conteudo=blob_bytes,
464
+ url_base="http://google.com",
465
+ timeout_polling=600, # Timeout máximo em segundos (default: 300)
466
+ max_tentativas=5, # Tentativas de submissão (default: 3)
467
+ intervalo_retry=10, # Segundos entre retries (default: 5)
468
+ intervalo_polling=2, # Segundos entre consultas (default: 1)
469
+ )
470
+
471
+ # Suporta LOBs do Oracle diretamente
472
+ with conectar_oracle(config) as conn:
473
+ conn.cursor.execute("SELECT blob_documento FROM documentos WHERE id = :id", {"id": 123})
474
+ blob = conn.cursor.fetchone()[0]
475
+ resultado = executar_ocr(blob) # LOB é convertido automaticamente
476
+
477
+ # Acessando detalhes das páginas
478
+ for page in resultado["pages"]:
479
+ print(f"Página {page['page_number']}: {page['extraction_method']}")
480
+
481
+ # Tratamento de erros
482
+ try:
483
+ resultado = executar_ocr(conteudo, url_base="INTELLIDOC_URL")
484
+ except OcrTimeoutError as e:
485
+ logger.error(f"Timeout aguardando OCR: {e}")
486
+ logger.debug(f"Detalhes: {e.details}") # {'document_id': '...', 'ultimo_status': 'PENDING'}
487
+ except OcrError as e:
488
+ logger.error(f"Erro no OCR: {e}")
489
+ ```
490
+
491
+ **Retorno da função `executar_ocr`:**
492
+
493
+ | Campo | Tipo | Descrição |
494
+ | -------------------- | ----- | ----------------------- |
495
+ | `document_id` | str | ID único do documento |
496
+ | `full_text` | str | Texto extraído completo |
497
+ | `mime_type` | str | Tipo MIME detectado |
498
+ | `overall_quality` | float | Qualidade geral (0-1) |
499
+ | `total_pages` | int | Número de páginas |
500
+ | `processing_time_ms` | int | Tempo de processamento |
501
+ | `pages` | list | Detalhes por página |
502
+ | `metadata` | dict | Metadados adicionais |
503
+
504
+ **Exceções:**
505
+
506
+ | Exceção | Quando ocorre |
507
+ | ----------------------- | --------------------------------------------------------------------- |
508
+ | `OcrSubmissaoError` | Falha ao enviar documento (rede, timeout, resposta inválida) |
509
+ | `OcrProcessamentoError` | API retornou FAILURE/REVOKED (documento corrompido, formato inválido) |
510
+ | `OcrTimeoutError` | Tempo máximo de polling atingido |
511
+ | `TypeError` | Tipo de conteúdo não suportado |
512
+
513
+ ---
514
+
515
+ ## Instalação
516
+
517
+ ### Via PyPI
518
+
519
+ ```bash
520
+ pip install nia-etl-utils
521
+ ```
522
+
523
+ ### Via GitLab
524
+
525
+ ```bash
526
+ pip install git+https://gitlab-dti.mprj.mp.br/nia/etl-nia/nia-etl-utils.git@v0.2.0
527
+ ```
528
+
529
+ ### Modo Desenvolvimento
530
+
531
+ ```bash
532
+ git clone https://gitlab-dti.mprj.mp.br/nia/etl-nia/nia-etl-utils.git
533
+ cd nia-etl-utils
534
+ pip install -e ".[dev]"
535
+ ```
536
+
537
+ ---
538
+
539
+ ## Configuração
540
+
541
+ ### Variáveis de Ambiente
542
+
543
+ ```env
544
+ # Email SMTP
545
+ MAIL_SMTP_SERVER=smtp.mprj.mp.br
546
+ MAIL_SMTP_PORT=587
547
+ MAIL_SENDER=etl@mprj.mp.br
548
+ EMAIL_DESTINATARIOS=equipe@mprj.mp.br,gestor@mprj.mp.br
549
+
550
+ # PostgreSQL - NIA
551
+ DB_POSTGRESQL_HOST=postgres-nia.mprj.mp.br
552
+ DB_POSTGRESQL_PORT=5432
553
+ DB_POSTGRESQL_DATABASE=nia_database
554
+ DB_POSTGRESQL_USER=usuario
555
+ DB_POSTGRESQL_PASSWORD=senha
556
+
557
+ # PostgreSQL - OpenGeo
558
+ DB_POSTGRESQL_HOST_OPENGEO=postgres-opengeo.mprj.mp.br
559
+ DB_POSTGRESQL_PORT_OPENGEO=5432
560
+ DB_POSTGRESQL_DATABASE_OPENGEO=opengeo_database
561
+ DB_POSTGRESQL_USER_OPENGEO=usuario
562
+ DB_POSTGRESQL_PASSWORD_OPENGEO=senha
563
+
564
+ # Oracle
565
+ DB_ORACLE_HOST=oracle.mprj.mp.br
566
+ DB_ORACLE_PORT=1521
567
+ DB_ORACLE_SERVICE_NAME=ORCL
568
+ DB_ORACLE_USER=usuario
569
+ DB_ORACLE_PASSWORD=senha
570
+
571
+ # OCR (IntelliDoc)
572
+ INTELLIDOC_URL=http://intellidoc.mprj.mp.br
573
+ ```
574
+
575
+ ---
576
+
577
+ ## Testes
578
+
579
+ ```bash
580
+ # Todos os testes
581
+ pytest
582
+
583
+ # Com cobertura
584
+ pytest --cov=src/nia_etl_utils --cov-report=term-missing
585
+
586
+ # Ou usar o script helper
587
+ ./run_tests.sh --coverage --verbose
588
+ ```
589
+
590
+ ---
591
+
592
+ ## Exemplo de Uso Completo
593
+
594
+ ```python
595
+ from nia_etl_utils import (
596
+ configurar_logger_padrao_nia,
597
+ PostgresConfig,
598
+ conectar_postgresql,
599
+ exportar_para_csv,
600
+ ConexaoError,
601
+ )
602
+ from loguru import logger
603
+ import pandas as pd
604
+ from datetime import datetime
605
+
606
+ # 1. Configura logging
607
+ configurar_logger_padrao_nia("meu_pipeline")
608
+
609
+ # 2. Carrega configuração
610
+ config = PostgresConfig.from_env()
611
+
612
+ # 3. Conecta e extrai dados
613
+ try:
614
+ with conectar_postgresql(config) as conn:
615
+ logger.info("Extraindo dados...")
616
+ conn.cursor.execute("SELECT * FROM tabela WHERE data >= CURRENT_DATE - 7")
617
+ resultados = conn.cursor.fetchall()
618
+ colunas = [desc[0] for desc in conn.cursor.description]
619
+ df = pd.DataFrame(resultados, columns=colunas)
620
+ except ConexaoError as e:
621
+ logger.error(f"Falha na conexão: {e}")
622
+ raise
623
+
624
+ logger.info(f"Extração concluída: {len(df)} registros")
625
+
626
+ # 4. Exporta CSV
627
+ data_hoje = datetime.now().strftime("%Y_%m_%d")
628
+ caminho = exportar_para_csv(
629
+ df=df,
630
+ nome_arquivo="dados_extraidos",
631
+ data_extracao=data_hoje,
632
+ diretorio_base="/dados/processados"
633
+ )
634
+
635
+ logger.success(f"Pipeline concluído! Arquivo: {caminho}")
636
+ ```
637
+
638
+ ---
639
+
640
+ ## Integração com Airflow
641
+
642
+ ```python
643
+ from airflow.providers.cncf.kubernetes.operators.kubernetes_pod import KubernetesPodOperator
644
+
645
+ task = KubernetesPodOperator(
646
+ task_id="meu_etl",
647
+ name="meu-etl-pod",
648
+ namespace="airflow-nia-stage",
649
+ image="python:3.13.3",
650
+ cmds=[
651
+ "sh", "-c",
652
+ "pip install nia-etl-utils && python src/extract.py"
653
+ ],
654
+ env_vars={
655
+ "DB_POSTGRESQL_HOST": "...",
656
+ "EMAIL_DESTINATARIOS": "equipe@mprj.mp.br"
657
+ },
658
+ )
659
+ ```
660
+
661
+ ---
662
+
663
+ ## Tecnologias Utilizadas
664
+
665
+ - Python 3.13.3
666
+ - Loguru (logging)
667
+ - python-dotenv (env vars)
668
+ - requests (HTTP/OCR)
669
+ - cx_Oracle (Oracle)
670
+ - psycopg2 (PostgreSQL)
671
+ - SQLAlchemy (engines)
672
+ - pandas (processamento de dados)
673
+ - pytest + pytest-cov (testes)
674
+ - ruff (linting)
675
+
676
+ ---
677
+
678
+ ## Versionamento
679
+
680
+ Este projeto usa [Semantic Versioning](https://semver.org/):
681
+
682
+ - **MAJOR**: Mudanças incompatíveis na API
683
+ - **MINOR**: Novas funcionalidades (retrocompatíveis)
684
+ - **PATCH**: Correções de bugs
685
+
686
+ **Versão atual:** `v0.2.1`
687
+
688
+ ---
689
+
690
+ ## CI/CD
691
+
692
+ Pipeline automatizado no GitLab:
693
+
694
+ - Testes unitários (pytest)
695
+ - Cobertura de código (>= 70%)
696
+ - Linting (ruff)
697
+ - Deploy automático no PyPI (em tags)
698
+
699
+ ---
700
+
701
+ ## Contribuição
702
+
703
+ Merge requests são bem-vindos. Sempre crie uma branch a partir de `main`.
704
+
705
+ ### Checklist:
706
+
707
+ - [ ] Testes passam: `pytest`
708
+ - [ ] Cobertura >= 70%
709
+ - [ ] Lint OK: `ruff check src/ tests/`
710
+ - [ ] Commits semânticos
711
+ - [ ] Documentação atualizada
712
+
713
+ ---
714
+
715
+ ## Licença
716
+
717
+ Projeto de uso interno do MPRJ. Sem licença pública.
718
+
719
+ ---
720
+
721
+ ## Responsável Técnico
722
+
723
+ **Nícolas Galdino Esmael** | Engenheiro de Dados - NIA | MPRJ