nia-etl-utils 0.2.0__tar.gz → 0.2.2__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 (34) hide show
  1. nia_etl_utils-0.2.2/PKG-INFO +722 -0
  2. nia_etl_utils-0.2.2/README.md +679 -0
  3. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/pyproject.toml +3 -1
  4. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/src/nia_etl_utils/__init__.py +50 -29
  5. nia_etl_utils-0.2.2/src/nia_etl_utils/exceptions.py +237 -0
  6. nia_etl_utils-0.2.2/src/nia_etl_utils/ocr.py +327 -0
  7. nia_etl_utils-0.2.2/src/nia_etl_utils.egg-info/PKG-INFO +722 -0
  8. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/src/nia_etl_utils.egg-info/SOURCES.txt +2 -0
  9. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/src/nia_etl_utils.egg-info/requires.txt +2 -0
  10. nia_etl_utils-0.2.2/tests/test_ocr.py +427 -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/exceptions.py +0 -327
  14. nia_etl_utils-0.2.0/src/nia_etl_utils.egg-info/PKG-INFO +0 -615
  15. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/setup.cfg +0 -0
  16. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/setup.py +0 -0
  17. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/src/nia_etl_utils/config.py +0 -0
  18. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/src/nia_etl_utils/database.py +0 -0
  19. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/src/nia_etl_utils/email_smtp.py +0 -0
  20. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/src/nia_etl_utils/env_config.py +0 -0
  21. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/src/nia_etl_utils/limpeza_pastas.py +0 -0
  22. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/src/nia_etl_utils/logger_config.py +0 -0
  23. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/src/nia_etl_utils/processa_csv.py +0 -0
  24. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/src/nia_etl_utils/processa_csv_paralelo.py +0 -0
  25. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/src/nia_etl_utils/results.py +0 -0
  26. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/src/nia_etl_utils.egg-info/dependency_links.txt +0 -0
  27. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/src/nia_etl_utils.egg-info/top_level.txt +0 -0
  28. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/tests/test_database.py +0 -0
  29. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/tests/test_email_smtp.py +0 -0
  30. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/tests/test_env_config.py +0 -0
  31. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/tests/test_limpeza_pastas.py +0 -0
  32. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/tests/test_logger_config.py +0 -0
  33. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/tests/test_processa_csv.py +0 -0
  34. {nia_etl_utils-0.2.0 → nia_etl_utils-0.2.2}/tests/test_processa_csv_paralelo.py +0 -0
@@ -0,0 +1,722 @@
1
+ Metadata-Version: 2.4
2
+ Name: nia-etl-utils
3
+ Version: 0.2.2
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.2
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, OcrError, OcrTimeoutError
448
+
449
+ # Uso básico com variável de ambiente
450
+ with open("documento.pdf", "rb") as f:
451
+ resultado = executar_ocr(
452
+ conteudo=f.read(),
453
+ url_base="INTELLIDOC_URL", # nome da env var
454
+ )
455
+
456
+ print(resultado["full_text"]) # Texto extraído completo
457
+ print(resultado["overall_quality"]) # Qualidade do OCR (0-1)
458
+ print(resultado["total_pages"]) # Número de páginas
459
+
460
+ # Uso com URL direta e configurações customizadas
461
+ resultado = executar_ocr(
462
+ conteudo=blob_bytes,
463
+ url_base="http://google.com",
464
+ timeout_polling=600, # Timeout máximo em segundos (default: 300)
465
+ max_tentativas=5, # Tentativas de submissão (default: 3)
466
+ intervalo_retry=10, # Segundos entre retries (default: 5)
467
+ intervalo_polling=2, # Segundos entre consultas (default: 1)
468
+ )
469
+
470
+ # Suporta LOBs do Oracle diretamente
471
+ with conectar_oracle(config) as conn:
472
+ conn.cursor.execute("SELECT blob_documento FROM documentos WHERE id = :id", {"id": 123})
473
+ blob = conn.cursor.fetchone()[0]
474
+ resultado = executar_ocr(blob) # LOB é convertido automaticamente
475
+
476
+ # Acessando detalhes das páginas
477
+ for page in resultado["pages"]:
478
+ print(f"Página {page['page_number']}: {page['extraction_method']}")
479
+
480
+ # Tratamento de erros
481
+ try:
482
+ resultado = executar_ocr(conteudo, url_base="INTELLIDOC_URL")
483
+ except OcrTimeoutError as e:
484
+ logger.error(f"Timeout aguardando OCR: {e}")
485
+ logger.debug(f"Detalhes: {e.details}") # {'document_id': '...', 'ultimo_status': 'PENDING'}
486
+ except OcrError as e:
487
+ logger.error(f"Erro no OCR: {e}")
488
+ ```
489
+
490
+ **Retorno da função `executar_ocr`:**
491
+
492
+ | Campo | Tipo | Descrição |
493
+ | -------------------- | ----- | ----------------------- |
494
+ | `document_id` | str | ID único do documento |
495
+ | `full_text` | str | Texto extraído completo |
496
+ | `mime_type` | str | Tipo MIME detectado |
497
+ | `overall_quality` | float | Qualidade geral (0-1) |
498
+ | `total_pages` | int | Número de páginas |
499
+ | `processing_time_ms` | int | Tempo de processamento |
500
+ | `pages` | list | Detalhes por página |
501
+ | `metadata` | dict | Metadados adicionais |
502
+
503
+ **Exceções:**
504
+
505
+ | Exceção | Quando ocorre |
506
+ | ----------------------- | --------------------------------------------------------------------- |
507
+ | `OcrSubmissaoError` | Falha ao enviar documento (rede, timeout, resposta inválida) |
508
+ | `OcrProcessamentoError` | API retornou FAILURE/REVOKED (documento corrompido, formato inválido) |
509
+ | `OcrTimeoutError` | Tempo máximo de polling atingido |
510
+ | `TypeError` | Tipo de conteúdo não suportado |
511
+
512
+ ---
513
+
514
+ ## Instalação
515
+
516
+ ### Via PyPI
517
+
518
+ ```bash
519
+ pip install nia-etl-utils
520
+ ```
521
+
522
+ ### Via GitLab
523
+
524
+ ```bash
525
+ pip install git+https://gitlab-dti.mprj.mp.br/nia/etl-nia/nia-etl-utils.git@v0.2.0
526
+ ```
527
+
528
+ ### Modo Desenvolvimento
529
+
530
+ ```bash
531
+ git clone https://gitlab-dti.mprj.mp.br/nia/etl-nia/nia-etl-utils.git
532
+ cd nia-etl-utils
533
+ pip install -e ".[dev]"
534
+ ```
535
+
536
+ ---
537
+
538
+ ## Configuração
539
+
540
+ ### Variáveis de Ambiente
541
+
542
+ ```env
543
+ # Email SMTP
544
+ MAIL_SMTP_SERVER=smtp.mprj.mp.br
545
+ MAIL_SMTP_PORT=587
546
+ MAIL_SENDER=etl@mprj.mp.br
547
+ EMAIL_DESTINATARIOS=equipe@mprj.mp.br,gestor@mprj.mp.br
548
+
549
+ # PostgreSQL - NIA
550
+ DB_POSTGRESQL_HOST=postgres-nia.mprj.mp.br
551
+ DB_POSTGRESQL_PORT=5432
552
+ DB_POSTGRESQL_DATABASE=nia_database
553
+ DB_POSTGRESQL_USER=usuario
554
+ DB_POSTGRESQL_PASSWORD=senha
555
+
556
+ # PostgreSQL - OpenGeo
557
+ DB_POSTGRESQL_HOST_OPENGEO=postgres-opengeo.mprj.mp.br
558
+ DB_POSTGRESQL_PORT_OPENGEO=5432
559
+ DB_POSTGRESQL_DATABASE_OPENGEO=opengeo_database
560
+ DB_POSTGRESQL_USER_OPENGEO=usuario
561
+ DB_POSTGRESQL_PASSWORD_OPENGEO=senha
562
+
563
+ # Oracle
564
+ DB_ORACLE_HOST=oracle.mprj.mp.br
565
+ DB_ORACLE_PORT=1521
566
+ DB_ORACLE_SERVICE_NAME=ORCL
567
+ DB_ORACLE_USER=usuario
568
+ DB_ORACLE_PASSWORD=senha
569
+
570
+ # OCR (IntelliDoc)
571
+ INTELLIDOC_URL=http://intellidoc.mprj.mp.br
572
+ ```
573
+
574
+ ---
575
+
576
+ ## Testes
577
+
578
+ ```bash
579
+ # Todos os testes
580
+ pytest
581
+
582
+ # Com cobertura
583
+ pytest --cov=src/nia_etl_utils --cov-report=term-missing
584
+
585
+ # Ou usar o script helper
586
+ ./run_tests.sh --coverage --verbose
587
+ ```
588
+
589
+ ---
590
+
591
+ ## Exemplo de Uso Completo
592
+
593
+ ```python
594
+ from nia_etl_utils import (
595
+ configurar_logger_padrao_nia,
596
+ PostgresConfig,
597
+ conectar_postgresql,
598
+ exportar_para_csv,
599
+ ConexaoError,
600
+ )
601
+ from loguru import logger
602
+ import pandas as pd
603
+ from datetime import datetime
604
+
605
+ # 1. Configura logging
606
+ configurar_logger_padrao_nia("meu_pipeline")
607
+
608
+ # 2. Carrega configuração
609
+ config = PostgresConfig.from_env()
610
+
611
+ # 3. Conecta e extrai dados
612
+ try:
613
+ with conectar_postgresql(config) as conn:
614
+ logger.info("Extraindo dados...")
615
+ conn.cursor.execute("SELECT * FROM tabela WHERE data >= CURRENT_DATE - 7")
616
+ resultados = conn.cursor.fetchall()
617
+ colunas = [desc[0] for desc in conn.cursor.description]
618
+ df = pd.DataFrame(resultados, columns=colunas)
619
+ except ConexaoError as e:
620
+ logger.error(f"Falha na conexão: {e}")
621
+ raise
622
+
623
+ logger.info(f"Extração concluída: {len(df)} registros")
624
+
625
+ # 4. Exporta CSV
626
+ data_hoje = datetime.now().strftime("%Y_%m_%d")
627
+ caminho = exportar_para_csv(
628
+ df=df,
629
+ nome_arquivo="dados_extraidos",
630
+ data_extracao=data_hoje,
631
+ diretorio_base="/dados/processados"
632
+ )
633
+
634
+ logger.success(f"Pipeline concluído! Arquivo: {caminho}")
635
+ ```
636
+
637
+ ---
638
+
639
+ ## Integração com Airflow
640
+
641
+ ```python
642
+ from airflow.providers.cncf.kubernetes.operators.kubernetes_pod import KubernetesPodOperator
643
+
644
+ task = KubernetesPodOperator(
645
+ task_id="meu_etl",
646
+ name="meu-etl-pod",
647
+ namespace="airflow-nia-stage",
648
+ image="python:3.13.3",
649
+ cmds=[
650
+ "sh", "-c",
651
+ "pip install nia-etl-utils && python src/extract.py"
652
+ ],
653
+ env_vars={
654
+ "DB_POSTGRESQL_HOST": "...",
655
+ "EMAIL_DESTINATARIOS": "equipe@mprj.mp.br"
656
+ },
657
+ )
658
+ ```
659
+
660
+ ---
661
+
662
+ ## Tecnologias Utilizadas
663
+
664
+ - Python 3.13.3
665
+ - Loguru (logging)
666
+ - python-dotenv (env vars)
667
+ - requests (HTTP/OCR)
668
+ - cx_Oracle (Oracle)
669
+ - psycopg2 (PostgreSQL)
670
+ - SQLAlchemy (engines)
671
+ - pandas (processamento de dados)
672
+ - pytest + pytest-cov (testes)
673
+ - ruff (linting)
674
+
675
+ ---
676
+
677
+ ## Versionamento
678
+
679
+ Este projeto usa [Semantic Versioning](https://semver.org/):
680
+
681
+ - **MAJOR**: Mudanças incompatíveis na API
682
+ - **MINOR**: Novas funcionalidades (retrocompatíveis)
683
+ - **PATCH**: Correções de bugs
684
+
685
+ **Versão atual:** `v0.2.2`
686
+
687
+ ---
688
+
689
+ ## CI/CD
690
+
691
+ Pipeline automatizado no GitLab:
692
+
693
+ - Testes unitários (pytest)
694
+ - Cobertura de código (>= 70%)
695
+ - Linting (ruff)
696
+ - Deploy automático no PyPI (em tags)
697
+
698
+ ---
699
+
700
+ ## Contribuição
701
+
702
+ Merge requests são bem-vindos. Sempre crie uma branch a partir de `main`.
703
+
704
+ ### Checklist:
705
+
706
+ - [ ] Testes passam: `pytest`
707
+ - [ ] Cobertura >= 70%
708
+ - [ ] Lint OK: `ruff check src/ tests/`
709
+ - [ ] Commits semânticos
710
+ - [ ] Documentação atualizada
711
+
712
+ ---
713
+
714
+ ## Licença
715
+
716
+ Projeto de uso interno do MPRJ. Sem licença pública.
717
+
718
+ ---
719
+
720
+ ## Responsável Técnico
721
+
722
+ **Nícolas Galdino Esmael** | Engenheiro de Dados - NIA | MPRJ