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