nia-etl-utils 0.1.0__py3-none-any.whl → 0.2.1__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 +173 -43
- nia_etl_utils/config.py +391 -0
- nia_etl_utils/database.py +249 -153
- nia_etl_utils/email_smtp.py +201 -67
- nia_etl_utils/env_config.py +137 -15
- nia_etl_utils/exceptions.py +394 -0
- nia_etl_utils/limpeza_pastas.py +192 -59
- nia_etl_utils/logger_config.py +98 -40
- nia_etl_utils/ocr.py +401 -0
- nia_etl_utils/processa_csv.py +257 -114
- nia_etl_utils/processa_csv_paralelo.py +150 -37
- nia_etl_utils/results.py +304 -0
- nia_etl_utils-0.2.1.dist-info/METADATA +723 -0
- nia_etl_utils-0.2.1.dist-info/RECORD +16 -0
- {nia_etl_utils-0.1.0.dist-info → nia_etl_utils-0.2.1.dist-info}/WHEEL +1 -1
- nia_etl_utils-0.1.0.dist-info/METADATA +0 -594
- nia_etl_utils-0.1.0.dist-info/RECORD +0 -12
- {nia_etl_utils-0.1.0.dist-info → nia_etl_utils-0.2.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,394 @@
|
|
|
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
|