nia-etl-utils 0.2.1__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.
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/PKG-INFO +4 -5
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/README.md +3 -4
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/pyproject.toml +1 -1
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils/__init__.py +32 -31
- nia_etl_utils-0.2.2/src/nia_etl_utils/exceptions.py +237 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils/ocr.py +12 -86
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils.egg-info/PKG-INFO +4 -5
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/tests/test_ocr.py +5 -4
- nia_etl_utils-0.2.1/src/nia_etl_utils/exceptions.py +0 -394
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/setup.cfg +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/setup.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils/config.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils/database.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils/email_smtp.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils/env_config.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils/limpeza_pastas.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils/logger_config.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils/processa_csv.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils/processa_csv_paralelo.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils/results.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils.egg-info/SOURCES.txt +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils.egg-info/dependency_links.txt +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils.egg-info/requires.txt +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/src/nia_etl_utils.egg-info/top_level.txt +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/tests/test_database.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/tests/test_email_smtp.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/tests/test_env_config.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/tests/test_limpeza_pastas.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/tests/test_logger_config.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/tests/test_processa_csv.py +0 -0
- {nia_etl_utils-0.2.1 → nia_etl_utils-0.2.2}/tests/test_processa_csv_paralelo.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nia-etl-utils
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Utilitários compartilhados para pipelines ETL do NIA/MPRJ
|
|
5
5
|
Author-email: Nícolas Esmael <nicolas.esmael@mprj.mp.br>
|
|
6
6
|
License: MIT
|
|
@@ -51,7 +51,7 @@ Desenvolvida para eliminar duplicação de código, padronizar boas práticas e
|
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
54
|
-
## Novidades da v0.2.
|
|
54
|
+
## Novidades da v0.2.2
|
|
55
55
|
|
|
56
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
57
|
- **Exceções de OCR** (`OcrError`, `OcrSubmissaoError`, `OcrProcessamentoError`, `OcrTimeoutError`) — tratamento granular de erros de OCR
|
|
@@ -444,8 +444,7 @@ A API processa documentos de forma assíncrona:
|
|
|
444
444
|
**Formatos suportados:** PDF, JPG, PNG, GIF, BMP, TIFF (detecção automática via magic bytes)
|
|
445
445
|
|
|
446
446
|
```python
|
|
447
|
-
from nia_etl_utils import executar_ocr
|
|
448
|
-
from nia_etl_utils.ocr import OcrError, OcrTimeoutError
|
|
447
|
+
from nia_etl_utils import executar_ocr, OcrError, OcrTimeoutError
|
|
449
448
|
|
|
450
449
|
# Uso básico com variável de ambiente
|
|
451
450
|
with open("documento.pdf", "rb") as f:
|
|
@@ -683,7 +682,7 @@ Este projeto usa [Semantic Versioning](https://semver.org/):
|
|
|
683
682
|
- **MINOR**: Novas funcionalidades (retrocompatíveis)
|
|
684
683
|
- **PATCH**: Correções de bugs
|
|
685
684
|
|
|
686
|
-
**Versão atual:** `v0.2.
|
|
685
|
+
**Versão atual:** `v0.2.2`
|
|
687
686
|
|
|
688
687
|
---
|
|
689
688
|
|
|
@@ -8,7 +8,7 @@ Desenvolvida para eliminar duplicação de código, padronizar boas práticas e
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
## Novidades da v0.2.
|
|
11
|
+
## Novidades da v0.2.2
|
|
12
12
|
|
|
13
13
|
- **Módulo OCR** (`executar_ocr`) — processamento de OCR via API IntelliDoc com suporte a PDF, imagens e detecção automática de formato
|
|
14
14
|
- **Exceções de OCR** (`OcrError`, `OcrSubmissaoError`, `OcrProcessamentoError`, `OcrTimeoutError`) — tratamento granular de erros de OCR
|
|
@@ -401,8 +401,7 @@ A API processa documentos de forma assíncrona:
|
|
|
401
401
|
**Formatos suportados:** PDF, JPG, PNG, GIF, BMP, TIFF (detecção automática via magic bytes)
|
|
402
402
|
|
|
403
403
|
```python
|
|
404
|
-
from nia_etl_utils import executar_ocr
|
|
405
|
-
from nia_etl_utils.ocr import OcrError, OcrTimeoutError
|
|
404
|
+
from nia_etl_utils import executar_ocr, OcrError, OcrTimeoutError
|
|
406
405
|
|
|
407
406
|
# Uso básico com variável de ambiente
|
|
408
407
|
with open("documento.pdf", "rb") as f:
|
|
@@ -640,7 +639,7 @@ Este projeto usa [Semantic Versioning](https://semver.org/):
|
|
|
640
639
|
- **MINOR**: Novas funcionalidades (retrocompatíveis)
|
|
641
640
|
- **PATCH**: Correções de bugs
|
|
642
641
|
|
|
643
|
-
**Versão atual:** `v0.2.
|
|
642
|
+
**Versão atual:** `v0.2.2`
|
|
644
643
|
|
|
645
644
|
---
|
|
646
645
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nia-etl-utils"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Utilitários compartilhados para pipelines ETL do NIA/MPRJ"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Nícolas Esmael", email = "nicolas.esmael@mprj.mp.br"}
|
|
@@ -20,8 +20,9 @@ Exemplo de uso:
|
|
|
20
20
|
conectar_postgresql_nia,
|
|
21
21
|
exportar_para_csv,
|
|
22
22
|
PostgresConfig,
|
|
23
|
+
ConexaoError,
|
|
24
|
+
OcrError,
|
|
23
25
|
)
|
|
24
|
-
from nia_etl_utils.exceptions import ConexaoError, ExtracaoVaziaError
|
|
25
26
|
|
|
26
27
|
# Configuração
|
|
27
28
|
configurar_logger_padrao_nia("meu_pipeline")
|
|
@@ -33,39 +34,25 @@ Exemplo de uso:
|
|
|
33
34
|
dados = conn.cursor.fetchall()
|
|
34
35
|
except ConexaoError as e:
|
|
35
36
|
logger.error(f"Falha na conexão: {e}")
|
|
36
|
-
sys.exit(1)
|
|
37
|
-
|
|
38
|
-
# Ou com configuração explícita (para testes)
|
|
39
|
-
config = PostgresConfig(
|
|
40
|
-
host="localhost",
|
|
41
|
-
port="5432",
|
|
42
|
-
database="teste",
|
|
43
|
-
user="user",
|
|
44
|
-
password="pass"
|
|
45
|
-
)
|
|
46
|
-
with conectar_postgresql(config) as conn:
|
|
47
|
-
# ...
|
|
37
|
+
sys.exit(1)
|
|
48
38
|
|
|
49
39
|
Exemplo de OCR:
|
|
50
40
|
|
|
51
|
-
from nia_etl_utils import executar_ocr
|
|
52
|
-
from nia_etl_utils.ocr import OcrError
|
|
41
|
+
from nia_etl_utils import executar_ocr, OcrError, OcrTimeoutError
|
|
53
42
|
|
|
54
43
|
try:
|
|
55
44
|
resultado = executar_ocr(blob_bytes, url_base="INTELLIDOC_URL")
|
|
56
45
|
texto = resultado["full_text"]
|
|
57
46
|
qualidade = resultado["overall_quality"]
|
|
47
|
+
except OcrTimeoutError as e:
|
|
48
|
+
logger.error(f"Timeout no OCR: {e}")
|
|
58
49
|
except OcrError as e:
|
|
59
50
|
logger.error(f"Falha no OCR: {e}")
|
|
60
51
|
"""
|
|
61
52
|
|
|
62
|
-
__version__ = "0.2.
|
|
53
|
+
__version__ = "0.2.2"
|
|
63
54
|
__author__ = "Nícolas Galdino Esmael"
|
|
64
55
|
|
|
65
|
-
# =============================================================================
|
|
66
|
-
# EXCEÇÕES - Importar primeiro para uso em type hints
|
|
67
|
-
# =============================================================================
|
|
68
|
-
|
|
69
56
|
# =============================================================================
|
|
70
57
|
# CONFIGURAÇÕES (Dataclasses)
|
|
71
58
|
# =============================================================================
|
|
@@ -76,8 +63,9 @@ from .config import (
|
|
|
76
63
|
SmtpConfig,
|
|
77
64
|
)
|
|
78
65
|
|
|
79
|
-
#
|
|
80
|
-
#
|
|
66
|
+
# =============================================================================
|
|
67
|
+
# DATABASE
|
|
68
|
+
# =============================================================================
|
|
81
69
|
from .database import (
|
|
82
70
|
conectar_oracle,
|
|
83
71
|
conectar_oracle_ouvidorias,
|
|
@@ -89,7 +77,9 @@ from .database import (
|
|
|
89
77
|
obter_engine_postgresql_opengeo,
|
|
90
78
|
)
|
|
91
79
|
|
|
92
|
-
#
|
|
80
|
+
# =============================================================================
|
|
81
|
+
# EMAIL
|
|
82
|
+
# =============================================================================
|
|
93
83
|
from .email_smtp import (
|
|
94
84
|
enviar_email,
|
|
95
85
|
enviar_email_smtp,
|
|
@@ -99,13 +89,16 @@ from .email_smtp import (
|
|
|
99
89
|
# =============================================================================
|
|
100
90
|
# FUNÇÕES UTILITÁRIAS
|
|
101
91
|
# =============================================================================
|
|
102
|
-
# Configuração de ambiente
|
|
103
92
|
from .env_config import (
|
|
104
93
|
obter_variavel_env,
|
|
105
94
|
obter_variavel_env_bool,
|
|
106
95
|
obter_variavel_env_int,
|
|
107
96
|
obter_variavel_env_lista,
|
|
108
97
|
)
|
|
98
|
+
|
|
99
|
+
# =============================================================================
|
|
100
|
+
# EXCEÇÕES
|
|
101
|
+
# =============================================================================
|
|
109
102
|
from .exceptions import (
|
|
110
103
|
# Arquivos
|
|
111
104
|
ArquivoError,
|
|
@@ -125,41 +118,44 @@ from .exceptions import (
|
|
|
125
118
|
LeituraArquivoError,
|
|
126
119
|
# Base
|
|
127
120
|
NiaEtlError,
|
|
121
|
+
# OCR
|
|
122
|
+
OcrError,
|
|
123
|
+
OcrProcessamentoError,
|
|
124
|
+
OcrSubmissaoError,
|
|
125
|
+
OcrTimeoutError,
|
|
128
126
|
ProcessamentoError,
|
|
129
127
|
SmtpError,
|
|
130
128
|
# Validação
|
|
131
129
|
ValidacaoError,
|
|
132
130
|
VariavelAmbienteError,
|
|
133
131
|
)
|
|
134
|
-
|
|
135
|
-
# Manipulação de arquivos
|
|
136
132
|
from .limpeza_pastas import (
|
|
137
133
|
criar_pasta_se_nao_existir,
|
|
138
134
|
limpar_pasta,
|
|
139
135
|
listar_arquivos,
|
|
140
136
|
remover_pasta_recursivamente,
|
|
141
137
|
)
|
|
142
|
-
|
|
143
|
-
# Logging
|
|
144
138
|
from .logger_config import (
|
|
145
139
|
configurar_logger,
|
|
146
140
|
configurar_logger_padrao_nia,
|
|
147
141
|
remover_handlers_existentes,
|
|
148
142
|
)
|
|
149
143
|
|
|
144
|
+
# =============================================================================
|
|
150
145
|
# OCR
|
|
146
|
+
# =============================================================================
|
|
151
147
|
from .ocr import (
|
|
152
148
|
executar_ocr,
|
|
153
149
|
)
|
|
154
150
|
|
|
155
|
-
#
|
|
151
|
+
# =============================================================================
|
|
152
|
+
# CSV
|
|
153
|
+
# =============================================================================
|
|
156
154
|
from .processa_csv import (
|
|
157
155
|
exportar_multiplos_csv,
|
|
158
156
|
exportar_para_csv,
|
|
159
157
|
extrair_e_exportar_csv,
|
|
160
158
|
)
|
|
161
|
-
|
|
162
|
-
# Processamento CSV Paralelo
|
|
163
159
|
from .processa_csv_paralelo import (
|
|
164
160
|
calcular_chunksize,
|
|
165
161
|
processar_csv_paralelo,
|
|
@@ -204,6 +200,11 @@ __all__ = [
|
|
|
204
200
|
"EmailError",
|
|
205
201
|
"DestinatarioError",
|
|
206
202
|
"SmtpError",
|
|
203
|
+
# Exceções - OCR
|
|
204
|
+
"OcrError",
|
|
205
|
+
"OcrSubmissaoError",
|
|
206
|
+
"OcrProcessamentoError",
|
|
207
|
+
"OcrTimeoutError",
|
|
207
208
|
# Exceções - Validação
|
|
208
209
|
"ValidacaoError",
|
|
209
210
|
# Configurações
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"""Exceções customizadas para o pacote nia_etl_utils.
|
|
2
|
+
|
|
3
|
+
Este módulo define a hierarquia de exceções usada em todo o pacote,
|
|
4
|
+
permitindo tratamento granular de erros pelos consumidores da biblioteca.
|
|
5
|
+
|
|
6
|
+
Hierarquia:
|
|
7
|
+
NiaEtlError (base)
|
|
8
|
+
├── ConfiguracaoError
|
|
9
|
+
│ └── VariavelAmbienteError
|
|
10
|
+
├── DatabaseError
|
|
11
|
+
│ └── ConexaoError
|
|
12
|
+
├── ArquivoError
|
|
13
|
+
│ ├── EscritaArquivoError
|
|
14
|
+
│ ├── LeituraArquivoError
|
|
15
|
+
│ └── DiretorioError
|
|
16
|
+
├── ExtracaoError
|
|
17
|
+
│ └── ExtracaoVaziaError
|
|
18
|
+
├── EmailError
|
|
19
|
+
│ ├── DestinatarioError
|
|
20
|
+
│ └── SmtpError
|
|
21
|
+
├── OcrError
|
|
22
|
+
│ ├── OcrSubmissaoError
|
|
23
|
+
│ ├── OcrProcessamentoError
|
|
24
|
+
│ └── OcrTimeoutError
|
|
25
|
+
└── ValidacaoError
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
Capturando erros específicos:
|
|
29
|
+
|
|
30
|
+
>>> from nia_etl_utils import ConexaoError, OcrTimeoutError
|
|
31
|
+
>>>
|
|
32
|
+
>>> try:
|
|
33
|
+
... resultado = executar_ocr(documento)
|
|
34
|
+
... except OcrTimeoutError as e:
|
|
35
|
+
... logger.error(f"Timeout no OCR: {e}")
|
|
36
|
+
|
|
37
|
+
Capturando qualquer erro do pacote:
|
|
38
|
+
|
|
39
|
+
>>> from nia_etl_utils import NiaEtlError
|
|
40
|
+
>>>
|
|
41
|
+
>>> try:
|
|
42
|
+
... executar_pipeline()
|
|
43
|
+
... except NiaEtlError as e:
|
|
44
|
+
... logger.error(f"Erro no pipeline: {e}")
|
|
45
|
+
... sys.exit(1)
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class NiaEtlError(Exception):
|
|
50
|
+
"""Exceção base para todos os erros do pacote nia_etl_utils.
|
|
51
|
+
|
|
52
|
+
Todas as exceções customizadas do pacote herdam desta classe,
|
|
53
|
+
permitindo captura genérica quando necessário.
|
|
54
|
+
|
|
55
|
+
Attributes:
|
|
56
|
+
message: Descrição do erro.
|
|
57
|
+
details: Informações adicionais opcionais sobre o erro.
|
|
58
|
+
|
|
59
|
+
Examples:
|
|
60
|
+
>>> try:
|
|
61
|
+
... raise NiaEtlError("Algo deu errado", details={"codigo": 123})
|
|
62
|
+
... except NiaEtlError as e:
|
|
63
|
+
... print(e.message)
|
|
64
|
+
... print(e.details)
|
|
65
|
+
Algo deu errado
|
|
66
|
+
{'codigo': 123}
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def __init__(self, message: str, details: dict | None = None):
|
|
70
|
+
self.message = message
|
|
71
|
+
self.details = details or {}
|
|
72
|
+
super().__init__(self.message)
|
|
73
|
+
|
|
74
|
+
def __str__(self) -> str:
|
|
75
|
+
if self.details:
|
|
76
|
+
return f"{self.message} | Detalhes: {self.details}"
|
|
77
|
+
return self.message
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# =============================================================================
|
|
81
|
+
# CONFIGURAÇÃO
|
|
82
|
+
# =============================================================================
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ConfiguracaoError(NiaEtlError):
|
|
86
|
+
"""Erro de configuração do sistema."""
|
|
87
|
+
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class VariavelAmbienteError(ConfiguracaoError):
|
|
92
|
+
"""Variável de ambiente ausente ou inválida."""
|
|
93
|
+
|
|
94
|
+
def __init__(self, nome_variavel: str):
|
|
95
|
+
self.nome_variavel = nome_variavel
|
|
96
|
+
super().__init__(
|
|
97
|
+
f"Variável de ambiente '{nome_variavel}' não encontrada "
|
|
98
|
+
f"e nenhum valor padrão foi fornecido",
|
|
99
|
+
details={"variavel": nome_variavel}
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# =============================================================================
|
|
104
|
+
# DATABASE
|
|
105
|
+
# =============================================================================
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class DatabaseError(NiaEtlError):
|
|
109
|
+
"""Erro base para operações de banco de dados."""
|
|
110
|
+
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class ConexaoError(DatabaseError):
|
|
115
|
+
"""Falha ao estabelecer conexão com banco de dados."""
|
|
116
|
+
|
|
117
|
+
pass
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# =============================================================================
|
|
121
|
+
# ARQUIVOS E DIRETÓRIOS
|
|
122
|
+
# =============================================================================
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class ArquivoError(NiaEtlError):
|
|
126
|
+
"""Erro base para operações de arquivo e diretório."""
|
|
127
|
+
|
|
128
|
+
pass
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class EscritaArquivoError(ArquivoError):
|
|
132
|
+
"""Falha ao escrever arquivo."""
|
|
133
|
+
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class LeituraArquivoError(ArquivoError):
|
|
138
|
+
"""Falha ao ler arquivo."""
|
|
139
|
+
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class DiretorioError(ArquivoError):
|
|
144
|
+
"""Falha em operação de diretório."""
|
|
145
|
+
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# =============================================================================
|
|
150
|
+
# EXTRAÇÃO E PROCESSAMENTO
|
|
151
|
+
# =============================================================================
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class ExtracaoError(NiaEtlError):
|
|
155
|
+
"""Erro base para operações de extração de dados."""
|
|
156
|
+
|
|
157
|
+
pass
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class ExtracaoVaziaError(ExtracaoError):
|
|
161
|
+
"""Extração retornou DataFrame vazio ou None."""
|
|
162
|
+
|
|
163
|
+
def __init__(self, nome_extracao: str):
|
|
164
|
+
self.nome_extracao = nome_extracao
|
|
165
|
+
super().__init__(
|
|
166
|
+
f"Nenhum dado retornado para extração '{nome_extracao}'",
|
|
167
|
+
details={"extracao": nome_extracao}
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class ProcessamentoError(ExtracaoError):
|
|
172
|
+
"""Erro durante processamento de dados."""
|
|
173
|
+
|
|
174
|
+
pass
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# =============================================================================
|
|
178
|
+
# EMAIL
|
|
179
|
+
# =============================================================================
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class EmailError(NiaEtlError):
|
|
183
|
+
"""Erro base para operações de email."""
|
|
184
|
+
|
|
185
|
+
pass
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class DestinatarioError(EmailError):
|
|
189
|
+
"""Erro relacionado a destinatários de email."""
|
|
190
|
+
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class SmtpError(EmailError):
|
|
195
|
+
"""Erro de comunicação com servidor SMTP."""
|
|
196
|
+
|
|
197
|
+
pass
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# =============================================================================
|
|
201
|
+
# OCR
|
|
202
|
+
# =============================================================================
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class OcrError(NiaEtlError):
|
|
206
|
+
"""Erro base para operações de OCR."""
|
|
207
|
+
|
|
208
|
+
pass
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class OcrSubmissaoError(OcrError):
|
|
212
|
+
"""Falha ao submeter documento para OCR."""
|
|
213
|
+
|
|
214
|
+
pass
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class OcrProcessamentoError(OcrError):
|
|
218
|
+
"""Falha no processamento do documento pela API."""
|
|
219
|
+
|
|
220
|
+
pass
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class OcrTimeoutError(OcrError):
|
|
224
|
+
"""Timeout aguardando resultado do OCR."""
|
|
225
|
+
|
|
226
|
+
pass
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
# =============================================================================
|
|
230
|
+
# VALIDAÇÃO
|
|
231
|
+
# =============================================================================
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class ValidacaoError(NiaEtlError):
|
|
235
|
+
"""Erro de validação de parâmetros ou dados."""
|
|
236
|
+
|
|
237
|
+
pass
|
|
@@ -10,23 +10,13 @@ A API IntelliDoc processa documentos de forma assíncrona:
|
|
|
10
10
|
Example:
|
|
11
11
|
Uso básico com variável de ambiente:
|
|
12
12
|
|
|
13
|
-
>>> from nia_etl_utils
|
|
13
|
+
>>> from nia_etl_utils import executar_ocr, OcrError
|
|
14
14
|
>>>
|
|
15
|
-
>>>
|
|
16
|
-
... resultado = executar_ocr(
|
|
17
|
-
...
|
|
18
|
-
...
|
|
19
|
-
... )
|
|
20
|
-
>>> print(resultado["full_text"])
|
|
21
|
-
|
|
22
|
-
Uso com URL direta e configurações customizadas:
|
|
23
|
-
|
|
24
|
-
>>> resultado = executar_ocr(
|
|
25
|
-
... conteudo=blob_bytes,
|
|
26
|
-
... url_base="http://google.com",
|
|
27
|
-
... timeout_polling=600,
|
|
28
|
-
... max_tentativas=5,
|
|
29
|
-
... )
|
|
15
|
+
>>> try:
|
|
16
|
+
... resultado = executar_ocr(blob_bytes, url_base="INTELLIDOC_URL")
|
|
17
|
+
... print(resultado["full_text"])
|
|
18
|
+
... except OcrError as e:
|
|
19
|
+
... logger.error(f"Falha no OCR: {e}")
|
|
30
20
|
"""
|
|
31
21
|
|
|
32
22
|
import time
|
|
@@ -35,70 +25,11 @@ import requests
|
|
|
35
25
|
from loguru import logger
|
|
36
26
|
|
|
37
27
|
from .env_config import obter_variavel_env
|
|
38
|
-
from .exceptions import
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class OcrError(NiaEtlError):
|
|
46
|
-
"""Erro base para operações de OCR.
|
|
47
|
-
|
|
48
|
-
Examples:
|
|
49
|
-
>>> raise OcrError("Falha no processamento OCR")
|
|
50
|
-
"""
|
|
51
|
-
|
|
52
|
-
pass
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class OcrSubmissaoError(OcrError):
|
|
56
|
-
"""Falha ao submeter documento para OCR.
|
|
57
|
-
|
|
58
|
-
Levantado quando não é possível enviar o documento para a API,
|
|
59
|
-
seja por problemas de rede, timeout ou resposta inválida.
|
|
60
|
-
|
|
61
|
-
Examples:
|
|
62
|
-
>>> raise OcrSubmissaoError(
|
|
63
|
-
... "Timeout ao submeter documento",
|
|
64
|
-
... details={"tentativas": 3, "status": 504}
|
|
65
|
-
... )
|
|
66
|
-
"""
|
|
67
|
-
|
|
68
|
-
pass
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class OcrProcessamentoError(OcrError):
|
|
72
|
-
"""Falha no processamento do documento pela API.
|
|
73
|
-
|
|
74
|
-
Levantado quando a API retorna status FAILURE ou REVOKED,
|
|
75
|
-
indicando que o documento não pôde ser processado.
|
|
76
|
-
|
|
77
|
-
Examples:
|
|
78
|
-
>>> raise OcrProcessamentoError(
|
|
79
|
-
... "Documento corrompido",
|
|
80
|
-
... details={"document_id": "abc-123", "erro_api": "invalid format"}
|
|
81
|
-
... )
|
|
82
|
-
"""
|
|
83
|
-
|
|
84
|
-
pass
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
class OcrTimeoutError(OcrError):
|
|
88
|
-
"""Timeout aguardando resultado do OCR.
|
|
89
|
-
|
|
90
|
-
Levantado quando o tempo máximo de polling é atingido
|
|
91
|
-
sem que a API retorne um resultado final.
|
|
92
|
-
|
|
93
|
-
Examples:
|
|
94
|
-
>>> raise OcrTimeoutError(
|
|
95
|
-
... "Timeout após 300s",
|
|
96
|
-
... details={"document_id": "abc-123", "ultimo_status": "PENDING"}
|
|
97
|
-
... )
|
|
98
|
-
"""
|
|
99
|
-
|
|
100
|
-
pass
|
|
101
|
-
|
|
28
|
+
from .exceptions import (
|
|
29
|
+
OcrProcessamentoError,
|
|
30
|
+
OcrSubmissaoError,
|
|
31
|
+
OcrTimeoutError,
|
|
32
|
+
)
|
|
102
33
|
|
|
103
34
|
# =============================================================================
|
|
104
35
|
# CONSTANTES
|
|
@@ -369,15 +300,10 @@ def executar_ocr(
|
|
|
369
300
|
|
|
370
301
|
>>> resultado = executar_ocr(
|
|
371
302
|
... conteudo=pdf_bytes,
|
|
372
|
-
... url_base="http://
|
|
303
|
+
... url_base="http://intellidoc.mprj.mp.br",
|
|
373
304
|
... timeout_polling=600,
|
|
374
305
|
... )
|
|
375
306
|
>>> print(f"Qualidade: {resultado['overall_quality']}")
|
|
376
|
-
|
|
377
|
-
Acessando detalhes das páginas:
|
|
378
|
-
|
|
379
|
-
>>> for page in resultado["pages"]:
|
|
380
|
-
... print(f"Página {page['page_number']}: {page['extraction_method']}")
|
|
381
307
|
"""
|
|
382
308
|
conteudo_bytes = _normalizar_para_bytes(conteudo)
|
|
383
309
|
extensao = _detectar_extensao(conteudo_bytes)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nia-etl-utils
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Utilitários compartilhados para pipelines ETL do NIA/MPRJ
|
|
5
5
|
Author-email: Nícolas Esmael <nicolas.esmael@mprj.mp.br>
|
|
6
6
|
License: MIT
|
|
@@ -51,7 +51,7 @@ Desenvolvida para eliminar duplicação de código, padronizar boas práticas e
|
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
54
|
-
## Novidades da v0.2.
|
|
54
|
+
## Novidades da v0.2.2
|
|
55
55
|
|
|
56
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
57
|
- **Exceções de OCR** (`OcrError`, `OcrSubmissaoError`, `OcrProcessamentoError`, `OcrTimeoutError`) — tratamento granular de erros de OCR
|
|
@@ -444,8 +444,7 @@ A API processa documentos de forma assíncrona:
|
|
|
444
444
|
**Formatos suportados:** PDF, JPG, PNG, GIF, BMP, TIFF (detecção automática via magic bytes)
|
|
445
445
|
|
|
446
446
|
```python
|
|
447
|
-
from nia_etl_utils import executar_ocr
|
|
448
|
-
from nia_etl_utils.ocr import OcrError, OcrTimeoutError
|
|
447
|
+
from nia_etl_utils import executar_ocr, OcrError, OcrTimeoutError
|
|
449
448
|
|
|
450
449
|
# Uso básico com variável de ambiente
|
|
451
450
|
with open("documento.pdf", "rb") as f:
|
|
@@ -683,7 +682,7 @@ Este projeto usa [Semantic Versioning](https://semver.org/):
|
|
|
683
682
|
- **MINOR**: Novas funcionalidades (retrocompatíveis)
|
|
684
683
|
- **PATCH**: Correções de bugs
|
|
685
684
|
|
|
686
|
-
**Versão atual:** `v0.2.
|
|
685
|
+
**Versão atual:** `v0.2.2`
|
|
687
686
|
|
|
688
687
|
---
|
|
689
688
|
|
|
@@ -4,15 +4,18 @@ from unittest.mock import MagicMock, patch
|
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
-
from nia_etl_utils
|
|
7
|
+
from nia_etl_utils import (
|
|
8
|
+
NiaEtlError,
|
|
8
9
|
OcrError,
|
|
9
10
|
OcrProcessamentoError,
|
|
10
11
|
OcrSubmissaoError,
|
|
11
12
|
OcrTimeoutError,
|
|
13
|
+
executar_ocr,
|
|
14
|
+
)
|
|
15
|
+
from nia_etl_utils.ocr import (
|
|
12
16
|
_detectar_extensao,
|
|
13
17
|
_normalizar_para_bytes,
|
|
14
18
|
_resolver_url_base,
|
|
15
|
-
executar_ocr,
|
|
16
19
|
)
|
|
17
20
|
|
|
18
21
|
|
|
@@ -400,8 +403,6 @@ class TestExcecoesOcr:
|
|
|
400
403
|
"""Testes para exceções de OCR."""
|
|
401
404
|
|
|
402
405
|
def test_ocr_error_herda_nia_etl_error(self) -> None:
|
|
403
|
-
from nia_etl_utils.exceptions import NiaEtlError
|
|
404
|
-
|
|
405
406
|
erro = OcrError("teste")
|
|
406
407
|
assert isinstance(erro, NiaEtlError)
|
|
407
408
|
|
|
@@ -1,394 +0,0 @@
|
|
|
1
|
-
"""Exceções customizadas para o pacote nia_etl_utils.
|
|
2
|
-
|
|
3
|
-
Este módulo define a hierarquia de exceções usada em todo o pacote,
|
|
4
|
-
permitindo tratamento granular de erros pelos consumidores da biblioteca.
|
|
5
|
-
|
|
6
|
-
Hierarquia:
|
|
7
|
-
NiaEtlError (base)
|
|
8
|
-
├── ConfiguracaoError
|
|
9
|
-
│ └── VariavelAmbienteError
|
|
10
|
-
├── DatabaseError
|
|
11
|
-
│ └── ConexaoError
|
|
12
|
-
├── ArquivoError
|
|
13
|
-
│ ├── EscritaArquivoError
|
|
14
|
-
│ ├── LeituraArquivoError
|
|
15
|
-
│ └── DiretorioError
|
|
16
|
-
├── ExtracaoError
|
|
17
|
-
│ └── ExtracaoVaziaError
|
|
18
|
-
├── EmailError
|
|
19
|
-
│ ├── DestinatarioError
|
|
20
|
-
│ └── SmtpError
|
|
21
|
-
├── OcrError
|
|
22
|
-
│ ├── OcrSubmissaoError
|
|
23
|
-
│ ├── OcrProcessamentoError
|
|
24
|
-
│ └── OcrTimeoutError
|
|
25
|
-
└── ValidacaoError
|
|
26
|
-
|
|
27
|
-
Examples:
|
|
28
|
-
Capturando erros específicos:
|
|
29
|
-
|
|
30
|
-
>>> from nia_etl_utils.exceptions import ConexaoError, ExtracaoVaziaError
|
|
31
|
-
>>>
|
|
32
|
-
>>> try:
|
|
33
|
-
... conn = conectar_postgresql(config)
|
|
34
|
-
... except ConexaoError as e:
|
|
35
|
-
... logger.error(f"Falha na conexão: {e}")
|
|
36
|
-
... # tratamento específico
|
|
37
|
-
|
|
38
|
-
Capturando qualquer erro do pacote:
|
|
39
|
-
|
|
40
|
-
>>> from nia_etl_utils.exceptions import NiaEtlError
|
|
41
|
-
>>>
|
|
42
|
-
>>> try:
|
|
43
|
-
... executar_pipeline()
|
|
44
|
-
... except NiaEtlError as e:
|
|
45
|
-
... logger.error(f"Erro no pipeline: {e}")
|
|
46
|
-
... sys.exit(1) # decisão do CHAMADOR
|
|
47
|
-
"""
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class NiaEtlError(Exception):
|
|
51
|
-
"""Exceção base para todos os erros do pacote nia_etl_utils.
|
|
52
|
-
|
|
53
|
-
Todas as exceções customizadas do pacote herdam desta classe,
|
|
54
|
-
permitindo captura genérica quando necessário.
|
|
55
|
-
|
|
56
|
-
Attributes:
|
|
57
|
-
message: Descrição do erro.
|
|
58
|
-
details: Informações adicionais opcionais sobre o erro.
|
|
59
|
-
|
|
60
|
-
Examples:
|
|
61
|
-
>>> try:
|
|
62
|
-
... raise NiaEtlError("Algo deu errado", details={"codigo": 123})
|
|
63
|
-
... except NiaEtlError as e:
|
|
64
|
-
... print(e.message)
|
|
65
|
-
... print(e.details)
|
|
66
|
-
Algo deu errado
|
|
67
|
-
{'codigo': 123}
|
|
68
|
-
"""
|
|
69
|
-
|
|
70
|
-
def __init__(self, message: str, details: dict | None = None):
|
|
71
|
-
self.message = message
|
|
72
|
-
self.details = details or {}
|
|
73
|
-
super().__init__(self.message)
|
|
74
|
-
|
|
75
|
-
def __str__(self) -> str:
|
|
76
|
-
if self.details:
|
|
77
|
-
return f"{self.message} | Detalhes: {self.details}"
|
|
78
|
-
return self.message
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
# =============================================================================
|
|
82
|
-
# CONFIGURAÇÃO
|
|
83
|
-
# =============================================================================
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
class ConfiguracaoError(NiaEtlError):
|
|
87
|
-
"""Erro de configuração do sistema.
|
|
88
|
-
|
|
89
|
-
Levantado quando há problemas com configurações necessárias
|
|
90
|
-
para o funcionamento do pacote.
|
|
91
|
-
|
|
92
|
-
Examples:
|
|
93
|
-
>>> raise ConfiguracaoError("Configuração inválida para conexão")
|
|
94
|
-
"""
|
|
95
|
-
|
|
96
|
-
pass
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
class VariavelAmbienteError(ConfiguracaoError):
|
|
100
|
-
"""Variável de ambiente ausente ou inválida.
|
|
101
|
-
|
|
102
|
-
Levantado quando uma variável de ambiente obrigatória não está
|
|
103
|
-
definida e nenhum valor padrão foi fornecido.
|
|
104
|
-
|
|
105
|
-
Attributes:
|
|
106
|
-
nome_variavel: Nome da variável de ambiente que causou o erro.
|
|
107
|
-
|
|
108
|
-
Examples:
|
|
109
|
-
>>> raise VariavelAmbienteError("DB_HOST")
|
|
110
|
-
"""
|
|
111
|
-
|
|
112
|
-
def __init__(self, nome_variavel: str):
|
|
113
|
-
self.nome_variavel = nome_variavel
|
|
114
|
-
super().__init__(
|
|
115
|
-
f"Variável de ambiente '{nome_variavel}' não encontrada "
|
|
116
|
-
f"e nenhum valor padrão foi fornecido",
|
|
117
|
-
details={"variavel": nome_variavel}
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
# =============================================================================
|
|
122
|
-
# DATABASE
|
|
123
|
-
# =============================================================================
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
class DatabaseError(NiaEtlError):
|
|
127
|
-
"""Erro base para operações de banco de dados.
|
|
128
|
-
|
|
129
|
-
Examples:
|
|
130
|
-
>>> raise DatabaseError("Falha na operação de banco de dados")
|
|
131
|
-
"""
|
|
132
|
-
|
|
133
|
-
pass
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
class ConexaoError(DatabaseError):
|
|
137
|
-
"""Falha ao estabelecer conexão com banco de dados.
|
|
138
|
-
|
|
139
|
-
Levantado quando não é possível conectar ao banco de dados,
|
|
140
|
-
seja por credenciais inválidas, host inacessível ou outros
|
|
141
|
-
problemas de conectividade.
|
|
142
|
-
|
|
143
|
-
Examples:
|
|
144
|
-
>>> raise ConexaoError(
|
|
145
|
-
... "Timeout ao conectar",
|
|
146
|
-
... details={"host": "localhost", "port": 5432}
|
|
147
|
-
... )
|
|
148
|
-
"""
|
|
149
|
-
|
|
150
|
-
pass
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
# =============================================================================
|
|
154
|
-
# ARQUIVOS E DIRETÓRIOS
|
|
155
|
-
# =============================================================================
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
class ArquivoError(NiaEtlError):
|
|
159
|
-
"""Erro base para operações de arquivo e diretório.
|
|
160
|
-
|
|
161
|
-
Examples:
|
|
162
|
-
>>> raise ArquivoError("Operação de arquivo falhou")
|
|
163
|
-
"""
|
|
164
|
-
|
|
165
|
-
pass
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
class EscritaArquivoError(ArquivoError):
|
|
169
|
-
"""Falha ao escrever arquivo.
|
|
170
|
-
|
|
171
|
-
Levantado quando não é possível criar ou escrever em um arquivo,
|
|
172
|
-
seja por falta de permissão, disco cheio ou caminho inválido.
|
|
173
|
-
|
|
174
|
-
Examples:
|
|
175
|
-
>>> raise EscritaArquivoError(
|
|
176
|
-
... "Sem permissão para escrita",
|
|
177
|
-
... details={"caminho": "/etc/arquivo.csv"}
|
|
178
|
-
... )
|
|
179
|
-
"""
|
|
180
|
-
|
|
181
|
-
pass
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
class LeituraArquivoError(ArquivoError):
|
|
185
|
-
"""Falha ao ler arquivo.
|
|
186
|
-
|
|
187
|
-
Levantado quando não é possível ler um arquivo, seja porque
|
|
188
|
-
ele não existe, não há permissão ou está corrompido.
|
|
189
|
-
|
|
190
|
-
Examples:
|
|
191
|
-
>>> raise LeituraArquivoError(
|
|
192
|
-
... "Arquivo não encontrado",
|
|
193
|
-
... details={"caminho": "/tmp/dados.csv"}
|
|
194
|
-
... )
|
|
195
|
-
"""
|
|
196
|
-
|
|
197
|
-
pass
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
class DiretorioError(ArquivoError):
|
|
201
|
-
"""Falha em operação de diretório.
|
|
202
|
-
|
|
203
|
-
Levantado quando não é possível criar, limpar ou remover
|
|
204
|
-
um diretório.
|
|
205
|
-
|
|
206
|
-
Examples:
|
|
207
|
-
>>> raise DiretorioError(
|
|
208
|
-
... "Sem permissão para criar diretório",
|
|
209
|
-
... details={"caminho": "/root/dados"}
|
|
210
|
-
... )
|
|
211
|
-
"""
|
|
212
|
-
|
|
213
|
-
pass
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
# =============================================================================
|
|
217
|
-
# EXTRAÇÃO E PROCESSAMENTO
|
|
218
|
-
# =============================================================================
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
class ExtracaoError(NiaEtlError):
|
|
222
|
-
"""Erro base para operações de extração de dados.
|
|
223
|
-
|
|
224
|
-
Examples:
|
|
225
|
-
>>> raise ExtracaoError("Falha na extração de dados")
|
|
226
|
-
"""
|
|
227
|
-
|
|
228
|
-
pass
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
class ExtracaoVaziaError(ExtracaoError):
|
|
232
|
-
"""Extração retornou DataFrame vazio ou None.
|
|
233
|
-
|
|
234
|
-
Levantado quando uma função de extração não retorna dados.
|
|
235
|
-
Pode ser esperado em alguns contextos (extração incremental
|
|
236
|
-
sem novos dados) ou indicar um problema.
|
|
237
|
-
|
|
238
|
-
Attributes:
|
|
239
|
-
nome_extracao: Identificador da extração que falhou.
|
|
240
|
-
|
|
241
|
-
Examples:
|
|
242
|
-
>>> raise ExtracaoVaziaError("clientes_novos")
|
|
243
|
-
"""
|
|
244
|
-
|
|
245
|
-
def __init__(self, nome_extracao: str):
|
|
246
|
-
self.nome_extracao = nome_extracao
|
|
247
|
-
super().__init__(
|
|
248
|
-
f"Nenhum dado retornado para extração '{nome_extracao}'",
|
|
249
|
-
details={"extracao": nome_extracao}
|
|
250
|
-
)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
class ProcessamentoError(ExtracaoError):
|
|
254
|
-
"""Erro durante processamento de dados.
|
|
255
|
-
|
|
256
|
-
Levantado quando há falha durante transformação ou
|
|
257
|
-
processamento de dados.
|
|
258
|
-
|
|
259
|
-
Examples:
|
|
260
|
-
>>> raise ProcessamentoError(
|
|
261
|
-
... "Falha ao processar chunk",
|
|
262
|
-
... details={"chunk": 5, "erro": "memória insuficiente"}
|
|
263
|
-
... )
|
|
264
|
-
"""
|
|
265
|
-
|
|
266
|
-
pass
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
# =============================================================================
|
|
270
|
-
# EMAIL
|
|
271
|
-
# =============================================================================
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
class EmailError(NiaEtlError):
|
|
275
|
-
"""Erro base para operações de email.
|
|
276
|
-
|
|
277
|
-
Examples:
|
|
278
|
-
>>> raise EmailError("Falha no envio de email")
|
|
279
|
-
"""
|
|
280
|
-
|
|
281
|
-
pass
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
class DestinatarioError(EmailError):
|
|
285
|
-
"""Erro relacionado a destinatários de email.
|
|
286
|
-
|
|
287
|
-
Levantado quando não há destinatários configurados ou
|
|
288
|
-
quando os destinatários são inválidos.
|
|
289
|
-
|
|
290
|
-
Examples:
|
|
291
|
-
>>> raise DestinatarioError("Nenhum destinatário configurado")
|
|
292
|
-
"""
|
|
293
|
-
|
|
294
|
-
pass
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
class SmtpError(EmailError):
|
|
298
|
-
"""Erro de comunicação com servidor SMTP.
|
|
299
|
-
|
|
300
|
-
Levantado quando há falha na conexão ou comunicação
|
|
301
|
-
com o servidor de email.
|
|
302
|
-
|
|
303
|
-
Examples:
|
|
304
|
-
>>> raise SmtpError(
|
|
305
|
-
... "Conexão recusada",
|
|
306
|
-
... details={"servidor": "smtp.empresa.com", "porta": 587}
|
|
307
|
-
... )
|
|
308
|
-
"""
|
|
309
|
-
|
|
310
|
-
pass
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
# =============================================================================
|
|
314
|
-
# OCR
|
|
315
|
-
# =============================================================================
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
class OcrError(NiaEtlError):
|
|
319
|
-
"""Erro base para operações de OCR.
|
|
320
|
-
|
|
321
|
-
Examples:
|
|
322
|
-
>>> raise OcrError("Falha no processamento OCR")
|
|
323
|
-
"""
|
|
324
|
-
|
|
325
|
-
pass
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
class OcrSubmissaoError(OcrError):
|
|
329
|
-
"""Falha ao submeter documento para OCR.
|
|
330
|
-
|
|
331
|
-
Levantado quando não é possível enviar o documento para a API,
|
|
332
|
-
seja por problemas de rede, timeout ou resposta inválida.
|
|
333
|
-
|
|
334
|
-
Examples:
|
|
335
|
-
>>> raise OcrSubmissaoError(
|
|
336
|
-
... "Timeout ao submeter documento",
|
|
337
|
-
... details={"tentativas": 3, "status": 504}
|
|
338
|
-
... )
|
|
339
|
-
"""
|
|
340
|
-
|
|
341
|
-
pass
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
class OcrProcessamentoError(OcrError):
|
|
345
|
-
"""Falha no processamento do documento pela API.
|
|
346
|
-
|
|
347
|
-
Levantado quando a API retorna status FAILURE ou REVOKED,
|
|
348
|
-
indicando que o documento não pôde ser processado.
|
|
349
|
-
|
|
350
|
-
Examples:
|
|
351
|
-
>>> raise OcrProcessamentoError(
|
|
352
|
-
... "Documento corrompido",
|
|
353
|
-
... details={"document_id": "abc-123", "erro_api": "invalid format"}
|
|
354
|
-
... )
|
|
355
|
-
"""
|
|
356
|
-
|
|
357
|
-
pass
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
class OcrTimeoutError(OcrError):
|
|
361
|
-
"""Timeout aguardando resultado do OCR.
|
|
362
|
-
|
|
363
|
-
Levantado quando o tempo máximo de polling é atingido
|
|
364
|
-
sem que a API retorne um resultado final.
|
|
365
|
-
|
|
366
|
-
Examples:
|
|
367
|
-
>>> raise OcrTimeoutError(
|
|
368
|
-
... "Timeout após 300s",
|
|
369
|
-
... details={"document_id": "abc-123", "ultimo_status": "PENDING"}
|
|
370
|
-
... )
|
|
371
|
-
"""
|
|
372
|
-
|
|
373
|
-
pass
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
# =============================================================================
|
|
377
|
-
# VALIDAÇÃO
|
|
378
|
-
# =============================================================================
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
class ValidacaoError(NiaEtlError):
|
|
382
|
-
"""Erro de validação de parâmetros ou dados.
|
|
383
|
-
|
|
384
|
-
Levantado quando parâmetros fornecidos são inválidos
|
|
385
|
-
ou não atendem aos requisitos esperados.
|
|
386
|
-
|
|
387
|
-
Examples:
|
|
388
|
-
>>> raise ValidacaoError(
|
|
389
|
-
... "Nome do arquivo não pode ser vazio",
|
|
390
|
-
... details={"parametro": "nome_arquivo", "valor": ""}
|
|
391
|
-
... )
|
|
392
|
-
"""
|
|
393
|
-
|
|
394
|
-
pass
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|