libretificacaotjcore 0.1.59__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.
- libretificacaotjcore/__init__.py +0 -0
- libretificacaotjcore/database/__init__.py +1 -0
- libretificacaotjcore/database/arquivo_repository.py +61 -0
- libretificacaotjcore/database/certificado_repository.py +39 -0
- libretificacaotjcore/database/config_db.py +62 -0
- libretificacaotjcore/database/protocolo_repository.py +97 -0
- libretificacaotjcore/database/rubrica_repository.py +35 -0
- libretificacaotjcore/database/solicitacao_xml_repository.py +40 -0
- libretificacaotjcore/database/tempo_processo_repository.py +119 -0
- libretificacaotjcore/dtos/__init__.py +0 -0
- libretificacaotjcore/dtos/arquivo_dto.py +20 -0
- libretificacaotjcore/dtos/processo_dto.py +79 -0
- libretificacaotjcore/dtos/solicitacao_dto.py +69 -0
- libretificacaotjcore/enums/__init__.py +0 -0
- libretificacaotjcore/enums/e_eventos.py +11 -0
- libretificacaotjcore/enums/e_fase_retificacao.py +38 -0
- libretificacaotjcore/services/__init__.py +0 -0
- libretificacaotjcore/services/crypto_pass_service.py +18 -0
- libretificacaotjcore/services/file_service.py +53 -0
- libretificacaotjcore/services/rabbitmq_consumer.py +59 -0
- libretificacaotjcore/services/rabbitmq_publisher.py +56 -0
- libretificacaotjcore/services/request_servico_api.py +20 -0
- libretificacaotjcore/services/s3_service.py +83 -0
- libretificacaotjcore-0.1.59.dist-info/METADATA +68 -0
- libretificacaotjcore-0.1.59.dist-info/RECORD +27 -0
- libretificacaotjcore-0.1.59.dist-info/WHEEL +5 -0
- libretificacaotjcore-0.1.59.dist-info/top_level.txt +1 -0
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
|
|
2
|
+
import uuid
|
|
3
|
+
from pymongo.errors import BulkWriteError
|
|
4
|
+
|
|
5
|
+
class ArquivoRepository:
|
|
6
|
+
def __init__(self, db):
|
|
7
|
+
self.__db = db
|
|
8
|
+
|
|
9
|
+
async def inserir_arquivo(self, arquivo: dict) -> bool:
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
arquivo_no_db = await self.__db.arquivos.find_one(
|
|
13
|
+
{"solicitacaoId": arquivo["solicitacaoId"], "cpf": arquivo["cpf"]}
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
arquivo['id'] = str(uuid.uuid4())
|
|
17
|
+
|
|
18
|
+
if arquivo_no_db is None:
|
|
19
|
+
await self.__db.arquivos.insert_one(arquivo)
|
|
20
|
+
return True
|
|
21
|
+
|
|
22
|
+
await self.__db.arquivos.delete_one(
|
|
23
|
+
{"solicitacaoId": arquivo["solicitacaoId"], "cpf": arquivo["cpf"]}
|
|
24
|
+
)
|
|
25
|
+
await self.__db.arquivos.insert_one(arquivo)
|
|
26
|
+
return True
|
|
27
|
+
except Exception as e:
|
|
28
|
+
print(f"❌ Erro ao inserir o arquivo: {e}")
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
async def inserir_arquivos_em_lote(self, arquivos: list[dict]) -> bool:
|
|
32
|
+
try:
|
|
33
|
+
if not arquivos:
|
|
34
|
+
return False
|
|
35
|
+
|
|
36
|
+
filtros = [{"solicitacaoId": a["solicitacaoId"], "cpf": a["cpf"], "id": str(uuid.uuid4())} for a in arquivos]
|
|
37
|
+
await self.__db.arquivos.delete_many({"$or": filtros})
|
|
38
|
+
|
|
39
|
+
await self.__db.arquivos.insert_many(arquivos)
|
|
40
|
+
return True
|
|
41
|
+
except BulkWriteError as bwe:
|
|
42
|
+
print(f"❌ Erro de escrita em lote: {bwe.details}")
|
|
43
|
+
return False
|
|
44
|
+
except Exception as e:
|
|
45
|
+
print(f"❌ Erro ao inserir arquivos em lote: {e}")
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
async def remover_arquivo(self, solicitacaoId: int) -> bool:
|
|
49
|
+
try:
|
|
50
|
+
await self.__db.arquivos.delete_many({"solicitacaoId": solicitacaoId})
|
|
51
|
+
return True
|
|
52
|
+
except Exception as e:
|
|
53
|
+
print(f"❌ Erro ao remover o arquivo: {e}")
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
async def buscar_por_solicitacao_id(self, solicitacaoId: int) -> list[dict]:
|
|
57
|
+
try:
|
|
58
|
+
return await self.__db.arquivos.find({"solicitacaoId": solicitacaoId}).to_list(length=None)
|
|
59
|
+
except Exception as e:
|
|
60
|
+
print(f"❌ Erro ao buscar por solicitacaoId: {e}")
|
|
61
|
+
return []
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from pymongo.errors import BulkWriteError
|
|
2
|
+
|
|
3
|
+
class CertificadoRepository:
|
|
4
|
+
def __init__(self, db):
|
|
5
|
+
self.__db = db
|
|
6
|
+
|
|
7
|
+
async def inserir_certificado(self, certificado: dict) -> bool:
|
|
8
|
+
try:
|
|
9
|
+
certificado_no_db = await self.__db.certificado.find_one(
|
|
10
|
+
{"cnpj": certificado["cnpj"]}
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
if certificado_no_db is None:
|
|
14
|
+
await self.__db.certificado.insert_one(certificado)
|
|
15
|
+
return True
|
|
16
|
+
|
|
17
|
+
await self.__db.certificado.delete_one(
|
|
18
|
+
{"cnpj": certificado["cnpj"]}
|
|
19
|
+
)
|
|
20
|
+
await self.__db.certificado.insert_one(certificado)
|
|
21
|
+
return True
|
|
22
|
+
except Exception as e:
|
|
23
|
+
print(f"❌ Erro ao inserir o arquivo: {e}")
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
async def remover_certificado(self, cnpj: str) -> bool:
|
|
27
|
+
try:
|
|
28
|
+
await self.__db.certificado.delete_many({"cnpj": cnpj})
|
|
29
|
+
return True
|
|
30
|
+
except Exception as e:
|
|
31
|
+
print(f"❌ Erro ao remover o certificado: {e}")
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
async def buscar_certificado_por_cnpj(self, cnpj: str) -> list[dict]:
|
|
35
|
+
try:
|
|
36
|
+
return await self.__db.certificado.find({"cnpj": cnpj}).to_list(length=None)
|
|
37
|
+
except Exception as e:
|
|
38
|
+
print(f"❌ Erro ao buscar certificado: {e}")
|
|
39
|
+
return []
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import motor.motor_asyncio
|
|
2
|
+
|
|
3
|
+
class ConfigDb:
|
|
4
|
+
def __init__(self, host: str, user: str, password: str, port: int, db_name: str):
|
|
5
|
+
self.host = host
|
|
6
|
+
self.user = user
|
|
7
|
+
self.password = password
|
|
8
|
+
self.port = port
|
|
9
|
+
self.db_name = db_name
|
|
10
|
+
|
|
11
|
+
self.client = motor.motor_asyncio.AsyncIOMotorClient(
|
|
12
|
+
f"mongodb://{self.host}:{self.port}"
|
|
13
|
+
)
|
|
14
|
+
self.__db = self.client[self.db_name]
|
|
15
|
+
self.db_initialized = False
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def criar_schema(self):
|
|
19
|
+
global _db_initialized
|
|
20
|
+
if not self.db_initialized:
|
|
21
|
+
if "arquivos" not in (await self.__db.list_collection_names()):
|
|
22
|
+
await self.__db.create_collection("arquivos")
|
|
23
|
+
|
|
24
|
+
await self.__db.arquivos.create_index([("cnpj", 1)])
|
|
25
|
+
await self.__db.arquivos.create_index([("solicitacaoId", 1)])
|
|
26
|
+
await self.__db.arquivos.create_index([("id", 1)], unique=True)
|
|
27
|
+
await self.__db.arquivos.create_index([("solicitacaoId", 1), ("cpf", 1)], unique=True)
|
|
28
|
+
self.db_initialized = True
|
|
29
|
+
|
|
30
|
+
if "protocolos" not in (await self.__db.list_collection_names()):
|
|
31
|
+
await self.__db.create_collection("protocolos")
|
|
32
|
+
|
|
33
|
+
await self.__db.protocolos.create_index([("cnpj", 1)])
|
|
34
|
+
await self.__db.protocolos.create_index([("solicitacaoId", 1)])
|
|
35
|
+
await self.__db.protocolos.create_index([("id", 1)], unique=True)
|
|
36
|
+
await self.__db.protocolos.create_index([("solicitacaoId", 1), ("evento", 1)], unique=True)
|
|
37
|
+
self.db_initialized = True
|
|
38
|
+
|
|
39
|
+
if "rubricas" not in (await self.__db.list_collection_names()):
|
|
40
|
+
await self.__db.create_collection("rubricas")
|
|
41
|
+
|
|
42
|
+
await self.__db.rubricas.create_index([("cnpj", 1)])
|
|
43
|
+
await self.__db.rubricas.create_index([("solicitacaoId", 1)], unique=True)
|
|
44
|
+
await self.__db.rubricas.create_index([("id", 1)], unique=True)
|
|
45
|
+
self.db_initialized = True
|
|
46
|
+
|
|
47
|
+
if "tempo_processos" not in (await self.__db.list_collection_names()):
|
|
48
|
+
await self.__db.create_collection("tempo_processos")
|
|
49
|
+
|
|
50
|
+
await self.__db.tempo_processos.create_index([("SolicitacaoId", 1)])
|
|
51
|
+
await self.__db.tempo_processos.create_index([("id", 1)], unique=True)
|
|
52
|
+
self.db_initialized = True
|
|
53
|
+
|
|
54
|
+
if "solicitacao_xmls" not in (await self.__db.list_collection_names()):
|
|
55
|
+
await self.__db.create_collection("solicitacao_xmls")
|
|
56
|
+
|
|
57
|
+
await self.__db.solicitacao_xmls.create_index([("SolicitacaoId", 1)], unique=True)
|
|
58
|
+
self.db_initialized = True
|
|
59
|
+
|
|
60
|
+
async def get_db(self):
|
|
61
|
+
await self.criar_schema()
|
|
62
|
+
return self.__db
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from pymongo.errors import BulkWriteError
|
|
3
|
+
|
|
4
|
+
class ProtocoloRepository:
|
|
5
|
+
def __init__(self, db):
|
|
6
|
+
self.__db = db
|
|
7
|
+
|
|
8
|
+
async def inserir_protocolo(self, protocolo: dict) -> bool:
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
protocolo_no_db = await self.__db.protocolos.find_one(
|
|
12
|
+
{"solicitacaoId": protocolo["solicitacaoId"], "evento": protocolo["evento"]}
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
protocolo['id'] = str(uuid.uuid4())
|
|
16
|
+
if protocolo_no_db is None:
|
|
17
|
+
await self.__db.protocolos.insert_one(protocolo)
|
|
18
|
+
return True
|
|
19
|
+
|
|
20
|
+
await self.__db.protocolos.delete_one(
|
|
21
|
+
{"solicitacaoId": protocolo["solicitacaoId"], "evento": protocolo["evento"]}
|
|
22
|
+
)
|
|
23
|
+
await self.__db.protocolos.insert_one(protocolo)
|
|
24
|
+
return True
|
|
25
|
+
except Exception as e:
|
|
26
|
+
print(f"❌ Erro ao inserir o protocolo: {e}")
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
async def inserir_protocolos_em_lote(self, protocolos: list[dict]) -> bool:
|
|
30
|
+
try:
|
|
31
|
+
if not protocolos:
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
filtros = [{"solicitacaoId": a["solicitacaoId"], "evento": a["evento"], a['id']: str(uuid.uuid4())} for a in protocolos]
|
|
35
|
+
await self.__db.protocolos.delete_many({"$or": filtros})
|
|
36
|
+
|
|
37
|
+
await self.__db.protocolos.insert_many(protocolos)
|
|
38
|
+
return True
|
|
39
|
+
except BulkWriteError as bwe:
|
|
40
|
+
print(f"❌ Erro de escrita em lote: {bwe.details}")
|
|
41
|
+
return False
|
|
42
|
+
except Exception as e:
|
|
43
|
+
print(f"❌ Erro ao inserir protocolos em lote: {e}")
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
async def remover_protocolo(self, solicitacaoId: int) -> bool:
|
|
47
|
+
try:
|
|
48
|
+
await self.__db.protocolos.delete_many({"solicitacaoId": solicitacaoId})
|
|
49
|
+
return True
|
|
50
|
+
except Exception as e:
|
|
51
|
+
print(f"❌ Erro ao remover o protocolo: {e}")
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
async def atualizar_protocolo(
|
|
55
|
+
self, solicitacaoId: int, per_apur: str, protocolo: str, codigo_retorno: str, descricao_retorno: str
|
|
56
|
+
) -> bool:
|
|
57
|
+
"""
|
|
58
|
+
Atualiza os campos codigo_retorno e descricao_retorno
|
|
59
|
+
dentro do array 'protocolo' filtrando pelo solicitacaoId, per_apur e protocolo.
|
|
60
|
+
"""
|
|
61
|
+
try:
|
|
62
|
+
resultado = await self.__db.protocolos.update_one(
|
|
63
|
+
{
|
|
64
|
+
"solicitacaoId": solicitacaoId,
|
|
65
|
+
"protocolo.per_apur": per_apur,
|
|
66
|
+
"protocolo.protocolo": protocolo
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"$set": {
|
|
70
|
+
"protocolo.$.codigo_retorno": codigo_retorno,
|
|
71
|
+
"protocolo.$.descricao_retorno": descricao_retorno
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if resultado.matched_count == 0:
|
|
77
|
+
print("⚠️ Nenhum protocolo encontrado para atualizar.")
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
return True
|
|
81
|
+
except Exception as e:
|
|
82
|
+
print(f"❌ Erro ao atualizar protocolo: {e}")
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
async def buscar_por_solicitacao_id(self, solicitacaoId: int) -> list[dict]:
|
|
86
|
+
try:
|
|
87
|
+
return await self.__db.protocolos.find({"solicitacaoId": solicitacaoId}).to_list(length=None)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
print(f"❌ Erro ao buscar protocolo por solicitacaoId: {e}")
|
|
90
|
+
return []
|
|
91
|
+
|
|
92
|
+
async def buscar_por_solicitacao_id_e_evento(self, solicitacaoId: int, evento: str) -> dict | None:
|
|
93
|
+
try:
|
|
94
|
+
return await self.__db.protocolos.find_one({"solicitacaoId": solicitacaoId, "evento": evento})
|
|
95
|
+
except Exception as e:
|
|
96
|
+
print(f"❌ Erro ao buscar protocolo por solicitacaoId e evento: {e}")
|
|
97
|
+
return None
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from pymongo.errors import BulkWriteError
|
|
3
|
+
|
|
4
|
+
class RubricaRepository:
|
|
5
|
+
def __init__(self, db):
|
|
6
|
+
self.__db = db
|
|
7
|
+
|
|
8
|
+
async def inserir_rubrica(self, rubrica: dict) -> bool:
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
rubricas_no_db = await self.__db.rubricas.find_one(
|
|
12
|
+
{"solicitacaoId": rubrica["solicitacaoId"]}
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
rubrica['id'] = str(uuid.uuid4())
|
|
16
|
+
|
|
17
|
+
if rubricas_no_db is None:
|
|
18
|
+
await self.__db.rubricas.insert_one(rubrica)
|
|
19
|
+
return True
|
|
20
|
+
|
|
21
|
+
await self.__db.rubricas.delete_one(
|
|
22
|
+
{"solicitacaoId": rubrica["solicitacaoId"]}
|
|
23
|
+
)
|
|
24
|
+
await self.__db.rubricas.insert_one(rubrica)
|
|
25
|
+
return True
|
|
26
|
+
except Exception as e:
|
|
27
|
+
print(f"❌ Erro ao inserir o rubrica: {e}")
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
async def buscar_por_solicitacao_id(self, solicitacaoId: int) -> dict:
|
|
31
|
+
try:
|
|
32
|
+
return await self.__db.rubricas.find_one({"solicitacaoId": solicitacaoId})
|
|
33
|
+
except Exception as e:
|
|
34
|
+
print(f"❌ Erro ao buscar rubricas por solicitacaoId: {e}")
|
|
35
|
+
return {}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
|
|
3
|
+
class SolicitacaoXmlsRepository:
|
|
4
|
+
def __init__(self, db):
|
|
5
|
+
self.__db = db
|
|
6
|
+
|
|
7
|
+
async def inserir_solicitacao_xml(self, solicitacao_xml: dict) -> bool:
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
solicitacao_xml_no_db = await self.__db.solicitacao_xmls.find_one(
|
|
11
|
+
{"solicitacaoId": solicitacao_xml["solicitacaoId"]}
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
if solicitacao_xml_no_db is None:
|
|
15
|
+
await self.__db.solicitacao_xmls.insert_one(solicitacao_xml)
|
|
16
|
+
return True
|
|
17
|
+
|
|
18
|
+
await self.__db.solicitacao_xmls.delete_one(
|
|
19
|
+
{"solicitacaoId": solicitacao_xml["solicitacaoId"]}
|
|
20
|
+
)
|
|
21
|
+
await self.__db.solicitacao_xmls.insert_one(solicitacao_xml)
|
|
22
|
+
return True
|
|
23
|
+
except Exception as e:
|
|
24
|
+
print(f"❌ Erro ao inserir o solicitacao xml: {e}")
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
async def buscar_por_solicitacao_id(self, solicitacaoId: int) -> dict:
|
|
28
|
+
try:
|
|
29
|
+
return await self.__db.solicitacao_xmls.find_one({"solicitacaoId": solicitacaoId})
|
|
30
|
+
except Exception as e:
|
|
31
|
+
print(f"❌ Erro ao buscar solicitacao xml por solicitacaoId: {e}")
|
|
32
|
+
return {}
|
|
33
|
+
|
|
34
|
+
async def remover_por_solicitacao_id(self, solicitacaoId: int) -> bool:
|
|
35
|
+
try:
|
|
36
|
+
await self.__db.solicitacao_xmls.delete_one({"solicitacaoId": solicitacaoId})
|
|
37
|
+
return True
|
|
38
|
+
except Exception as e:
|
|
39
|
+
print(f"❌ Erro ao remover solicitacao xml por solicitacaoId: {e}")
|
|
40
|
+
return False
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
import uuid
|
|
3
|
+
from pymongo.errors import BulkWriteError
|
|
4
|
+
|
|
5
|
+
from libretificacaotjcore.dtos.processo_dto import ProcessoDto
|
|
6
|
+
from libretificacaotjcore.enums.e_fase_retificacao import EFaseRetificacao
|
|
7
|
+
|
|
8
|
+
class TempoProcessoRepository:
|
|
9
|
+
def __init__(self, db):
|
|
10
|
+
self.__db = db
|
|
11
|
+
|
|
12
|
+
async def inserir_processo(self, *, processo: ProcessoDto) -> bool:
|
|
13
|
+
try:
|
|
14
|
+
processo_dict = processo.model_dump(exclude_none=True)
|
|
15
|
+
processo_dict["DataInicio"] = processo.DataInicio.strftime("%Y-%m-%d")
|
|
16
|
+
processo_dict["DataFim"] = processo.DataFim.strftime("%Y-%m-%d")
|
|
17
|
+
processo_dict["FaseDescricao"] = EFaseRetificacao(processo.Fase).name
|
|
18
|
+
|
|
19
|
+
processo_no_db = await self.__db.tempo_processos.find_one(
|
|
20
|
+
{
|
|
21
|
+
"SolicitacaoId": processo.SolicitacaoId,
|
|
22
|
+
"Fase": processo.Fase,
|
|
23
|
+
"DataInicio": processo_dict["DataInicio"],
|
|
24
|
+
"DataFim": processo_dict["DataFim"],
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
processo_dict["id"] = str(uuid.uuid4())
|
|
29
|
+
|
|
30
|
+
if processo_no_db is None:
|
|
31
|
+
processo_dict["InicioProcesso"] = datetime.now()
|
|
32
|
+
await self.__db.tempo_processos.insert_one(processo_dict)
|
|
33
|
+
return True
|
|
34
|
+
|
|
35
|
+
await self.__db.tempo_processos.delete_one(
|
|
36
|
+
{
|
|
37
|
+
"SolicitacaoId": processo.SolicitacaoId,
|
|
38
|
+
"Fase": processo.Fase,
|
|
39
|
+
"DataInicio": processo_dict["DataInicio"],
|
|
40
|
+
"DataFim": processo_dict["DataFim"],
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
processo_dict["InicioProcesso"] = datetime.now()
|
|
45
|
+
await self.__db.tempo_processos.insert_one(processo_dict)
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
except Exception as e:
|
|
49
|
+
print(f"❌ Erro ao inserir o processo: {e}")
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
async def atualizar_processo(self, *, processo: ProcessoDto) -> bool:
|
|
53
|
+
try:
|
|
54
|
+
processo_dict = processo.model_dump()
|
|
55
|
+
processo_dict["DataInicio"] = processo.DataInicio.strftime("%Y-%m-%d")
|
|
56
|
+
processo_dict["DataFim"] = processo.DataFim.strftime("%Y-%m-%d")
|
|
57
|
+
|
|
58
|
+
processo_no_db = await self.__db.tempo_processos.find_one(
|
|
59
|
+
{
|
|
60
|
+
"SolicitacaoId": processo.SolicitacaoId,
|
|
61
|
+
"Fase": processo.Fase,
|
|
62
|
+
"DataInicio": processo_dict["DataInicio"],
|
|
63
|
+
"DataFim": processo_dict["DataFim"],
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if processo_no_db is None:
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
processo_no_db['FimProcesso'] = datetime.now()
|
|
71
|
+
tempo_de_processo = self._tempo_de_processo(processo_no_db['InicioProcesso'], processo_no_db['FimProcesso'])
|
|
72
|
+
processo_no_db['TempoDeProcesso'] = tempo_de_processo
|
|
73
|
+
|
|
74
|
+
await self.__db.tempo_processos.update_one(
|
|
75
|
+
{
|
|
76
|
+
"SolicitacaoId": processo.SolicitacaoId,
|
|
77
|
+
"Fase": processo.Fase,
|
|
78
|
+
"DataInicio": processo_dict["DataInicio"],
|
|
79
|
+
"DataFim": processo_dict["DataFim"],
|
|
80
|
+
},
|
|
81
|
+
{"$set": processo_no_db
|
|
82
|
+
})
|
|
83
|
+
return True
|
|
84
|
+
except Exception as e:
|
|
85
|
+
print(f"❌ Erro ao atualizar o processo: {e}")
|
|
86
|
+
return False
|
|
87
|
+
|
|
88
|
+
async def buscar_por_solicitacao_id(self, solicitacao_id: int) -> list[ProcessoDto]:
|
|
89
|
+
try:
|
|
90
|
+
processos = await self.__db.tempo_processos.find(
|
|
91
|
+
{
|
|
92
|
+
"SolicitacaoId": solicitacao_id
|
|
93
|
+
}
|
|
94
|
+
).to_list(length=None)
|
|
95
|
+
processos_dto = []
|
|
96
|
+
|
|
97
|
+
for processo in processos:
|
|
98
|
+
processo_dto = ProcessoDto(**processo)
|
|
99
|
+
processos_dto.append(processo_dto)
|
|
100
|
+
|
|
101
|
+
return processos_dto
|
|
102
|
+
except Exception as e:
|
|
103
|
+
print(f"❌ Erro ao buscar os processos por solicitação: {e}")
|
|
104
|
+
return []
|
|
105
|
+
|
|
106
|
+
def _tempo_de_processo(self, tempo_inicio: datetime, tempo_fim: datetime) -> str | None:
|
|
107
|
+
if tempo_inicio and tempo_fim:
|
|
108
|
+
delta = tempo_fim - tempo_inicio
|
|
109
|
+
total_segundos = int(delta.total_seconds())
|
|
110
|
+
|
|
111
|
+
horas = total_segundos // 3600
|
|
112
|
+
minutos = (total_segundos % 3600) // 60
|
|
113
|
+
segundos = total_segundos % 60
|
|
114
|
+
|
|
115
|
+
tempo_formatado = f"{horas:02d}:{minutos:02d}:{segundos:02d}"
|
|
116
|
+
tempo_formatado = "00:00:01" if tempo_formatado == "00:00:00" else tempo_formatado
|
|
117
|
+
return tempo_formatado
|
|
118
|
+
|
|
119
|
+
return None
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from ast import Dict
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ArquivoDTO(BaseModel):
|
|
7
|
+
SolicitacaoId: int = Field(..., description="ID da solicitação")
|
|
8
|
+
Cpf: str = Field(..., description="CPF do contribuinte")
|
|
9
|
+
S1200: list[Dict] | None = Field(None, description="Lista de arquivos S-1200")
|
|
10
|
+
S1210: list[Dict] | None = Field(None, description="Lista de arquivos S-1210")
|
|
11
|
+
S2200: list[Dict] | None = Field(None, description="Lista de arquivos S-2200")
|
|
12
|
+
S2299: list[Dict] | None = Field(None, description="Lista de arquivos S-2299")
|
|
13
|
+
IdeEmpregador: Dict = Field(..., description="Identificação do empregador")
|
|
14
|
+
Cnpj: str = Field(..., description="CNPJ do empregador")
|
|
15
|
+
DataInicio: str = Field(..., description="Data de início no formato YYYY-MM-DD")
|
|
16
|
+
DataFim: str = Field(..., description="Data de fim no formato YYYY-MM-DD")
|
|
17
|
+
CertificadoId: int = Field(..., description="ID do certificado")
|
|
18
|
+
DataProcessamento: str = Field(
|
|
19
|
+
..., description="Data de processamento no formato YYYY-MM-DD"
|
|
20
|
+
)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from datetime import date, datetime, timedelta
|
|
2
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
3
|
+
from libretificacaotjcore.enums.e_fase_retificacao import EFaseRetificacao
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ProcessoDto(BaseModel):
|
|
7
|
+
model_config = ConfigDict(frozen=False)
|
|
8
|
+
SolicitacaoId: int = Field(..., description="ID da solicitação")
|
|
9
|
+
DataInicio: date = Field(..., description="Data de início no formato YYYY-MM-DD")
|
|
10
|
+
DataFim: date = Field(..., description="Data de fim no formato YYYY-MM-DD")
|
|
11
|
+
Fase: int | None = Field(None, description="Fase de retificação")
|
|
12
|
+
InicioProcesso: datetime | None = Field(None, description="Data e hora de início do processo")
|
|
13
|
+
FimProcesso: datetime | None = Field(None, description="Data e hora de fim do processo")
|
|
14
|
+
TempoDeProcesso: str | None = Field(None, description="Tempo de processamento")
|
|
15
|
+
Observacoes: str | None = Field(None, description="Observações adicionais")
|
|
16
|
+
|
|
17
|
+
# --- Validadores ---
|
|
18
|
+
|
|
19
|
+
@field_validator("SolicitacaoId")
|
|
20
|
+
@classmethod
|
|
21
|
+
def validar_solicitacao_id(cls, v: int) -> int:
|
|
22
|
+
if v <= 0:
|
|
23
|
+
raise ValueError("O solicitacaoId deve ser um inteiro positivo.")
|
|
24
|
+
return v
|
|
25
|
+
|
|
26
|
+
@field_validator("DataInicio", mode="before")
|
|
27
|
+
@classmethod
|
|
28
|
+
def formatar_data_inicio(cls, value) -> date:
|
|
29
|
+
"""
|
|
30
|
+
Aceita date, datetime ou string.
|
|
31
|
+
"""
|
|
32
|
+
if isinstance(value, date):
|
|
33
|
+
return value
|
|
34
|
+
if isinstance(value, datetime):
|
|
35
|
+
return value.date()
|
|
36
|
+
if isinstance(value, str):
|
|
37
|
+
try:
|
|
38
|
+
return datetime.strptime(value, "%Y-%m-%d").date()
|
|
39
|
+
except ValueError as ve:
|
|
40
|
+
raise ValueError("A DataInicio deve estar no formato YYYY-MM-DD.") from ve
|
|
41
|
+
raise TypeError("Tipo inválido para DataInicio.")
|
|
42
|
+
|
|
43
|
+
@field_validator("DataFim", mode="before")
|
|
44
|
+
@classmethod
|
|
45
|
+
def ajustar_data_fim(cls, value) -> date:
|
|
46
|
+
"""
|
|
47
|
+
Aceita date, datetime ou string.
|
|
48
|
+
Retorna o último dia do mês da data fornecida.
|
|
49
|
+
"""
|
|
50
|
+
if isinstance(value, datetime):
|
|
51
|
+
value = value.date()
|
|
52
|
+
if isinstance(value, date):
|
|
53
|
+
ano, mes = value.year, value.month
|
|
54
|
+
elif isinstance(value, str):
|
|
55
|
+
try:
|
|
56
|
+
ano, mes = map(int, value.split("-")[:2])
|
|
57
|
+
except Exception:
|
|
58
|
+
raise ValueError("A DataFim deve estar no formato YYYY-MM-DD.")
|
|
59
|
+
else:
|
|
60
|
+
raise TypeError("Tipo inválido para DataFim.")
|
|
61
|
+
|
|
62
|
+
if mes < 1 or mes > 12:
|
|
63
|
+
raise ValueError("A DataFim deve conter um mês válido (1–12).")
|
|
64
|
+
|
|
65
|
+
# calcula o último dia do mês
|
|
66
|
+
if mes == 12:
|
|
67
|
+
proximo_mes = datetime(ano + 1, 1, 1)
|
|
68
|
+
else:
|
|
69
|
+
proximo_mes = datetime(ano, mes + 1, 1)
|
|
70
|
+
return (proximo_mes - timedelta(days=1)).date()
|
|
71
|
+
|
|
72
|
+
@field_validator("Fase")
|
|
73
|
+
@classmethod
|
|
74
|
+
def validar_fase(cls, v):
|
|
75
|
+
if v is None:
|
|
76
|
+
return v
|
|
77
|
+
if v not in EFaseRetificacao:
|
|
78
|
+
raise ValueError(f"Fase '{v}' não é válida.")
|
|
79
|
+
return v
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from datetime import date, datetime, timedelta
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, Field, field_validator
|
|
5
|
+
from libretificacaotjcore.enums.e_fase_retificacao import EFaseRetificacao
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SolicitacaoDTO(BaseModel):
|
|
9
|
+
SolicitacaoId: int = Field(..., description="ID da solicitação")
|
|
10
|
+
Cnpj: str = Field(..., description="CNPJ da empresa")
|
|
11
|
+
DataInicio: date = Field(..., description="Data de início no formato YYYY-MM-DD")
|
|
12
|
+
DataFim: date = Field(..., description="Data de fim no formato YYYY-MM-DD")
|
|
13
|
+
CertificadoId: int = Field(..., description="ID do certificado")
|
|
14
|
+
Fase: int | None = Field(None, description="Fase de retificação")
|
|
15
|
+
FaseUnica: bool = Field(False, description="Fase única")
|
|
16
|
+
|
|
17
|
+
@field_validator("SolicitacaoId")
|
|
18
|
+
@classmethod
|
|
19
|
+
def validar_solicitacao_id(cls, v: int) -> int:
|
|
20
|
+
if v <= 0:
|
|
21
|
+
raise ValueError("O solicitacaoId deve ser um inteiro positivo.")
|
|
22
|
+
return v
|
|
23
|
+
|
|
24
|
+
@field_validator("CertificadoId")
|
|
25
|
+
@classmethod
|
|
26
|
+
def validar_certificado_id(cls, v: int) -> int:
|
|
27
|
+
if v <= 0:
|
|
28
|
+
raise ValueError("O certificadoId deve ser um inteiro positivo.")
|
|
29
|
+
return v
|
|
30
|
+
|
|
31
|
+
@field_validator("Cnpj")
|
|
32
|
+
@classmethod
|
|
33
|
+
def validar_cnpj(cls, v: str) -> str:
|
|
34
|
+
cnpj_limpo = re.sub(r"\D", "", v)
|
|
35
|
+
if len(cnpj_limpo) != 14 or not cnpj_limpo.isdigit():
|
|
36
|
+
raise ValueError("O CNPJ deve conter 14 dígitos numéricos.")
|
|
37
|
+
return cnpj_limpo
|
|
38
|
+
|
|
39
|
+
@field_validator("DataInicio", mode="before")
|
|
40
|
+
@classmethod
|
|
41
|
+
def formatar_data_inicio(cls, value: str) -> date:
|
|
42
|
+
try:
|
|
43
|
+
return datetime.strptime(value, "%Y-%m-%d").date()
|
|
44
|
+
except ValueError as ve:
|
|
45
|
+
raise ValueError("A dataInicio deve estar no formato YYYY-MM-DD.") from ve
|
|
46
|
+
|
|
47
|
+
@field_validator("DataFim", mode="before")
|
|
48
|
+
@classmethod
|
|
49
|
+
def ajustar_data_fim(cls, value: str) -> date:
|
|
50
|
+
ano, mes = map(int, value.split("-")[:2])
|
|
51
|
+
if mes < 1 or mes > 12:
|
|
52
|
+
raise ValueError(
|
|
53
|
+
"A dataFim deve estar no formato YYYY-MM-DD e conter um mês válido."
|
|
54
|
+
)
|
|
55
|
+
if mes == 12:
|
|
56
|
+
proximo_mes = datetime(ano + 1, 1, 1)
|
|
57
|
+
else:
|
|
58
|
+
proximo_mes = datetime(ano, mes + 1, 1)
|
|
59
|
+
return (proximo_mes - timedelta(days=1)).date()
|
|
60
|
+
|
|
61
|
+
@field_validator("Fase")
|
|
62
|
+
@classmethod
|
|
63
|
+
def validar_fase(cls, v: str) -> str | None:
|
|
64
|
+
if v is None:
|
|
65
|
+
return v
|
|
66
|
+
|
|
67
|
+
if v not in EFaseRetificacao:
|
|
68
|
+
raise ValueError(f"Fase '{v}' não é válido.")
|
|
69
|
+
return v
|
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class EFaseRetificacao(Enum):
|
|
5
|
+
NaoIniciado = 0
|
|
6
|
+
SolicitacaoXml = 1
|
|
7
|
+
AguardandoXml = 2
|
|
8
|
+
DownloadXml = 3
|
|
9
|
+
ExtraindoDadosDoXml = 4
|
|
10
|
+
#? Abertura de Competencia
|
|
11
|
+
EstruturandoXmlAberturaCompetencia = 5
|
|
12
|
+
AberturaDeCompetencia = 6
|
|
13
|
+
ConsultandoESocialAberturaCompetencia = 7
|
|
14
|
+
#? Rubricas
|
|
15
|
+
EstruturandoXmlInclusaoRubricas = 8
|
|
16
|
+
InclusaoDasRubricas = 9
|
|
17
|
+
ConsultandoESocialInclusaoRubricas = 10
|
|
18
|
+
#? Exclusao de Pagamentos
|
|
19
|
+
EstruturandoXmlExclusaoPagamentos = 11
|
|
20
|
+
ExclusaoDePagamentos = 12
|
|
21
|
+
ConsultandoESocialExclusaoPagamentos = 13
|
|
22
|
+
#? Retificacao
|
|
23
|
+
EstruturandoXmlRetificacaoRemuneracao = 14
|
|
24
|
+
RetificacaoDaRemuneracao = 15
|
|
25
|
+
ConsultandoESocialRetificacaoRemuneracao = 16
|
|
26
|
+
#? Desligamento
|
|
27
|
+
EstruturandoXmlDesligamento = 17
|
|
28
|
+
Desligamento = 18
|
|
29
|
+
ConsultandoESocialDesligamento = 19
|
|
30
|
+
#? Inclusao de Pagamentos
|
|
31
|
+
EstruturandoXmlInclusaoPagamentos = 20
|
|
32
|
+
InclusaoDosPagamentos = 21
|
|
33
|
+
ConsultandoESocialInclusaoPagamentos = 22
|
|
34
|
+
#? Fechamento de Competencia
|
|
35
|
+
EstruturandoXmlFechamentoCompetencia = 23
|
|
36
|
+
FechamentoDeCompetencia = 24
|
|
37
|
+
ConsultandoESocialFechamentoCompetencia = 25
|
|
38
|
+
Finalizado = 26
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from cryptography.fernet import Fernet
|
|
2
|
+
|
|
3
|
+
class CryptoPassService:
|
|
4
|
+
def __init__(self, key: bytes | None = None):
|
|
5
|
+
# Se a chave não for passada, gera uma nova
|
|
6
|
+
self.key = key or Fernet.generate_key()
|
|
7
|
+
self.cipher = Fernet(self.key)
|
|
8
|
+
|
|
9
|
+
def encrypt_password(self, password: str) -> bytes:
|
|
10
|
+
# Recebe a senha em texto e retorna a senha criptografada em bytes
|
|
11
|
+
password_bytes = password.encode()
|
|
12
|
+
encrypted = self.cipher.encrypt(password_bytes)
|
|
13
|
+
return encrypted
|
|
14
|
+
|
|
15
|
+
def decrypt_password(self, token: bytes) -> str:
|
|
16
|
+
# Recebe o token criptografado e retorna a senha em texto
|
|
17
|
+
decrypted_bytes = self.cipher.decrypt(token)
|
|
18
|
+
return decrypted_bytes.decode()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from io import BytesIO
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import zipfile
|
|
7
|
+
import aiofiles
|
|
8
|
+
import aiofiles.os
|
|
9
|
+
|
|
10
|
+
class FileService:
|
|
11
|
+
async def zip_folder(self, folder_path: str, output_path: str):
|
|
12
|
+
buffer = BytesIO()
|
|
13
|
+
|
|
14
|
+
with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
|
|
15
|
+
for root, _, files in os.walk(folder_path):
|
|
16
|
+
for file in files:
|
|
17
|
+
full_path = os.path.join(root, file)
|
|
18
|
+
arcname = os.path.relpath(full_path, folder_path)
|
|
19
|
+
|
|
20
|
+
async with aiofiles.open(full_path, "rb") as f:
|
|
21
|
+
content = await f.read()
|
|
22
|
+
zip_file.writestr(arcname, content)
|
|
23
|
+
|
|
24
|
+
async with aiofiles.open(output_path, "wb") as f:
|
|
25
|
+
await f.write(buffer.getvalue())
|
|
26
|
+
|
|
27
|
+
async def extract_zip(self, folder_path_zip: str, output_path: str):
|
|
28
|
+
if not await aiofiles.os.path.exists(folder_path_zip):
|
|
29
|
+
raise FileNotFoundError(
|
|
30
|
+
f"Arquivo ZIP não encontrado: {folder_path_zip}"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if not await aiofiles.os.path.exists(output_path):
|
|
34
|
+
await aiofiles.os.makedirs(output_path, exist_ok=True)
|
|
35
|
+
|
|
36
|
+
loop = asyncio.get_event_loop()
|
|
37
|
+
await loop.run_in_executor(
|
|
38
|
+
None, self._extrair_zip, folder_path_zip, output_path
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
async def remove_file(self, file_path: str):
|
|
42
|
+
if await aiofiles.os.path.exists(file_path):
|
|
43
|
+
await aiofiles.os.remove(file_path)
|
|
44
|
+
|
|
45
|
+
async def remove_folder(self, folder_path: str):
|
|
46
|
+
if await aiofiles.os.path.exists(folder_path):
|
|
47
|
+
loop = asyncio.get_running_loop()
|
|
48
|
+
await loop.run_in_executor(None, shutil.rmtree, folder_path)
|
|
49
|
+
|
|
50
|
+
def _extrair_zip(self, folder_path_zip: str, output_path: str):
|
|
51
|
+
with zipfile.ZipFile(folder_path_zip, "r") as zip_ref:
|
|
52
|
+
zip_ref.extractall(output_path)
|
|
53
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import aio_pika
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
class RabbitMQConsumer:
|
|
6
|
+
def __init__(
|
|
7
|
+
self,
|
|
8
|
+
*,
|
|
9
|
+
host: str,
|
|
10
|
+
queue: str,
|
|
11
|
+
username: str,
|
|
12
|
+
password: str,
|
|
13
|
+
vhost: str = "/",
|
|
14
|
+
):
|
|
15
|
+
self.host = host
|
|
16
|
+
self.queue = queue
|
|
17
|
+
self.username = username
|
|
18
|
+
self.password = password
|
|
19
|
+
self.vhost = vhost
|
|
20
|
+
self.connection = None
|
|
21
|
+
self.channel = None
|
|
22
|
+
|
|
23
|
+
async def connect(self):
|
|
24
|
+
self.connection = await aio_pika.connect_robust(
|
|
25
|
+
host=self.host,
|
|
26
|
+
login=self.username,
|
|
27
|
+
password=self.password,
|
|
28
|
+
virtualhost=self.vhost,
|
|
29
|
+
heartbeat=600,
|
|
30
|
+
)
|
|
31
|
+
self.channel = await self.connection.channel()
|
|
32
|
+
await self.channel.set_qos(prefetch_count=1)
|
|
33
|
+
await self.channel.declare_queue(self.queue, durable=True)
|
|
34
|
+
|
|
35
|
+
async def start_consuming(self, callback):
|
|
36
|
+
if not self.channel:
|
|
37
|
+
raise RuntimeError("❌ Canal RabbitMQ não conectado. Chame connect() antes.")
|
|
38
|
+
|
|
39
|
+
queue = await self.channel.get_queue(self.queue)
|
|
40
|
+
|
|
41
|
+
async def on_message(message):
|
|
42
|
+
async with message.process():
|
|
43
|
+
try:
|
|
44
|
+
mensagem = json.loads(message.body.decode())
|
|
45
|
+
await callback(mensagem) # aqui sim passa o DTO
|
|
46
|
+
except Exception as e:
|
|
47
|
+
print(f"❌ Erro ao processar mensagem: {e}")
|
|
48
|
+
|
|
49
|
+
await queue.consume(on_message) # registra callback
|
|
50
|
+
|
|
51
|
+
print(f'[*] Aguardando mensagens na fila "{self.queue}". Para sair pressione CTRL+C')
|
|
52
|
+
|
|
53
|
+
# Mantém o consumer rodando
|
|
54
|
+
await asyncio.Future()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def close(self):
|
|
58
|
+
if self.connection:
|
|
59
|
+
await self.connection.close()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import aio_pika
|
|
2
|
+
import json
|
|
3
|
+
from aio_pika.abc import AbstractRobustConnection, AbstractChannel, AbstractQueue
|
|
4
|
+
|
|
5
|
+
class RabbitMQPublisher:
|
|
6
|
+
def __init__(
|
|
7
|
+
self,
|
|
8
|
+
host: str,
|
|
9
|
+
queue: str,
|
|
10
|
+
username: str,
|
|
11
|
+
password: str,
|
|
12
|
+
vhost: str = "/",
|
|
13
|
+
):
|
|
14
|
+
self.host = host
|
|
15
|
+
self.queue = queue
|
|
16
|
+
self.username = username
|
|
17
|
+
self.password = password
|
|
18
|
+
self.vhost = vhost
|
|
19
|
+
|
|
20
|
+
self.connection: AbstractRobustConnection | None = None
|
|
21
|
+
self.channel: AbstractChannel | None = None
|
|
22
|
+
self.queue_obj: AbstractQueue | None = None
|
|
23
|
+
|
|
24
|
+
async def connect(self):
|
|
25
|
+
self.connection = await aio_pika.connect_robust(
|
|
26
|
+
host=self.host,
|
|
27
|
+
login=self.username,
|
|
28
|
+
password=self.password,
|
|
29
|
+
virtualhost=self.vhost,
|
|
30
|
+
heartbeat=60,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
self.channel = await self.connection.channel()
|
|
35
|
+
# garante que mensagens não são perdidas
|
|
36
|
+
self.queue_obj = await self.channel.declare_queue(
|
|
37
|
+
self.queue, durable=True
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
async def publish(self, message: dict):
|
|
41
|
+
if not self.channel:
|
|
42
|
+
raise RuntimeError("❌ Canal RabbitMQ não conectado. Chame connect() antes.")
|
|
43
|
+
|
|
44
|
+
body = json.dumps(message).encode("utf-8")
|
|
45
|
+
await self.channel.default_exchange.publish(
|
|
46
|
+
aio_pika.Message(
|
|
47
|
+
body=body,
|
|
48
|
+
delivery_mode=aio_pika.DeliveryMode.PERSISTENT, # torna a msg persistente
|
|
49
|
+
),
|
|
50
|
+
routing_key=self.queue,
|
|
51
|
+
)
|
|
52
|
+
print(f"✅ Mensagem publicada na fila '{self.queue}': {message}")
|
|
53
|
+
|
|
54
|
+
async def close(self):
|
|
55
|
+
if self.connection:
|
|
56
|
+
await self.connection.close()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RequestServicoApi:
|
|
5
|
+
def __init__(self, url, token):
|
|
6
|
+
self.url = url
|
|
7
|
+
self.token = token
|
|
8
|
+
self.client = httpx.AsyncClient(timeout=120, verify=False)
|
|
9
|
+
|
|
10
|
+
async def handler(self, *, mensagem_atualizacao: dict):
|
|
11
|
+
print(self.token)
|
|
12
|
+
response = await self.client.post(self.url, json=mensagem_atualizacao)
|
|
13
|
+
|
|
14
|
+
if response.status_code != 200:
|
|
15
|
+
raise Exception(f"Erro ao fazer request ao servico de API: {response.status_code}")
|
|
16
|
+
|
|
17
|
+
await self.close()
|
|
18
|
+
|
|
19
|
+
async def close(self):
|
|
20
|
+
await self.client.aclose()
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from functools import partial
|
|
3
|
+
import os
|
|
4
|
+
import boto3
|
|
5
|
+
from botocore.exceptions import ClientError
|
|
6
|
+
|
|
7
|
+
class S3Service:
|
|
8
|
+
def __init__(self, aws_access_key_id, aws_secret_access_key, region_name, bucket_name, bucket_path):
|
|
9
|
+
self.s3 = boto3.client(
|
|
10
|
+
"s3",
|
|
11
|
+
aws_access_key_id=aws_access_key_id,
|
|
12
|
+
aws_secret_access_key=aws_secret_access_key,
|
|
13
|
+
region_name=region_name,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
self.bucket_name = bucket_name
|
|
17
|
+
self.bucket_path = bucket_path
|
|
18
|
+
|
|
19
|
+
self.loop = asyncio.get_running_loop()
|
|
20
|
+
|
|
21
|
+
async def save_on_s3(self, file_path, file_name):
|
|
22
|
+
try:
|
|
23
|
+
await self.loop.run_in_executor(
|
|
24
|
+
None,
|
|
25
|
+
self.s3.upload_file,
|
|
26
|
+
file_path,
|
|
27
|
+
self.bucket_name,
|
|
28
|
+
self.bucket_path + file_name,
|
|
29
|
+
)
|
|
30
|
+
except Exception as e:
|
|
31
|
+
print(f"❌ Erro ao salvar o arquivo no S3: {e}")
|
|
32
|
+
|
|
33
|
+
async def save_many_paths_on_s3(self, file_paths):
|
|
34
|
+
"""
|
|
35
|
+
Salva vários arquivos no S3 informando apenas os caminhos locais.
|
|
36
|
+
O nome do arquivo no S3 será o mesmo do arquivo local.
|
|
37
|
+
"""
|
|
38
|
+
tasks = [
|
|
39
|
+
self.save_on_s3(
|
|
40
|
+
file_path["caminho_arquivo_local"], file_path["nome_arquivo"]
|
|
41
|
+
)
|
|
42
|
+
for file_path in file_paths
|
|
43
|
+
]
|
|
44
|
+
await asyncio.gather(*tasks)
|
|
45
|
+
|
|
46
|
+
async def file_on_s3(self, file_name):
|
|
47
|
+
try:
|
|
48
|
+
head_object_func = partial(
|
|
49
|
+
self.s3.head_object,
|
|
50
|
+
Bucket=self.bucket_name,
|
|
51
|
+
Key=self.bucket_path + file_name,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
await self.loop.run_in_executor(None, head_object_func)
|
|
55
|
+
return True
|
|
56
|
+
except ClientError as e:
|
|
57
|
+
if e.response["Error"]["Code"] == "404":
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
raise
|
|
61
|
+
except Exception as e:
|
|
62
|
+
print(f"❌ Erro ao obter o arquivo do S3: {e}")
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
async def get_file_from_s3(self, file_name, destination_path):
|
|
66
|
+
try:
|
|
67
|
+
os.makedirs(os.path.dirname(destination_path), exist_ok=True)
|
|
68
|
+
|
|
69
|
+
key = self.bucket_path + file_name
|
|
70
|
+
print(f"🔍 Verificando chave: {key}")
|
|
71
|
+
download_func = partial(
|
|
72
|
+
self.s3.download_file, self.bucket_name, key, destination_path
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
await self.loop.run_in_executor(None, download_func)
|
|
76
|
+
print(f"📖✅ Arquivo '{file_name}' baixado com sucesso.")
|
|
77
|
+
except ClientError as e:
|
|
78
|
+
if e.response["Error"]["Code"] == "404":
|
|
79
|
+
print(f"⚠️ Arquivo '{file_name}' não encontrado no S3.")
|
|
80
|
+
else:
|
|
81
|
+
print(f"❌ Erro ao baixar o arquivo do S3: {e}")
|
|
82
|
+
except Exception as e:
|
|
83
|
+
print(f"❌ Erro inesperado ao baixar o arquivo do S3: {e}")
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: libretificacaotjcore
|
|
3
|
+
Version: 0.1.59
|
|
4
|
+
Summary: Biblioteca para centralizar conexao com filas no rabbit e banco de dados no mongodb para os servicos de retificacao da TJ
|
|
5
|
+
Author-email: Jhonatan Azevedo <dev.azevedo@outlook.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/seu-usuario/libretificacaotjcore
|
|
7
|
+
Project-URL: Issues, https://github.com/seu-usuario/libretificacaotjcore/issues
|
|
8
|
+
Project-URL: Repository, https://github.com/seu-usuario/libretificacaotjcore
|
|
9
|
+
Keywords: tj,tributo justo,retificação,automação,pydantic,rabbitmq,boto3,motor
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Operating System :: OS Independent
|
|
22
|
+
Requires-Python: >=3.12
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Requires-Dist: aio-pika>=9.5.7
|
|
25
|
+
Requires-Dist: aiofiles>=24.1.0
|
|
26
|
+
Requires-Dist: boto3>=1.39.16
|
|
27
|
+
Requires-Dist: cryptography>=45.0.6
|
|
28
|
+
Requires-Dist: httpx>=0.28.1
|
|
29
|
+
Requires-Dist: motor>=3.7.1
|
|
30
|
+
Requires-Dist: pika>=1.3.2
|
|
31
|
+
Requires-Dist: py7zr>=1.0.0
|
|
32
|
+
Requires-Dist: pydantic>=2.11.7
|
|
33
|
+
|
|
34
|
+
# 🛠️ LIBRETIFICACAOTJCORE
|
|
35
|
+
|
|
36
|
+
## 📝 Descrição
|
|
37
|
+
|
|
38
|
+
O Objetivo desse serviço é:
|
|
39
|
+
- Centralizar conexão com filas no rabbit e consumo de mensagens
|
|
40
|
+
- Centralizar conexão banco de dados no mongodb para os serviços de retificação da TJ
|
|
41
|
+
- Centralizar todas as operações de criação, leitura e atualização de arquivos
|
|
42
|
+
- Centralizar todas as operações de criação, leitura e atualização de protocolos
|
|
43
|
+
- Disponibilizar metodos para tratativas de arquivos
|
|
44
|
+
- Disponibilizar Dtos e Enums comuns em todos os serviços de retificações
|
|
45
|
+
|
|
46
|
+
## ⚙️ Configuração
|
|
47
|
+
nessesário ter o [uv astral](https://docs.astral.sh/uv/getting-started/installation/) instalado
|
|
48
|
+
|
|
49
|
+
Com o UV instalado, execute o comando abaixo para criar o arquivo de configuração:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
uv sync
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 📺 Como publicar?
|
|
56
|
+
|
|
57
|
+
Para publicar o serviço, execute o comando abaixo:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
uv build
|
|
61
|
+
```
|
|
62
|
+
e depois
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
twine upload dist/*
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Obs: É necessário informa o token do pypi para que o comando funcione
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
libretificacaotjcore/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
libretificacaotjcore/database/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
3
|
+
libretificacaotjcore/database/arquivo_repository.py,sha256=uVJVzuTg3t5XDM6CfHKMZo_G4dIm1YbqYIYUAlF9NOs,2243
|
|
4
|
+
libretificacaotjcore/database/certificado_repository.py,sha256=LF3rV1rQmRGZVB4wPh_vmDj81Gf_env_5hqtTbxXNFM,1396
|
|
5
|
+
libretificacaotjcore/database/config_db.py,sha256=Hgy_b9z-LI0Xs7WWENniT9QAQEIXtL5ym6cXs17NpSM,2894
|
|
6
|
+
libretificacaotjcore/database/protocolo_repository.py,sha256=B2d-b_zx0PMdSrEoQyC9F3EsW52OsbzwFgx-biZO05s,3878
|
|
7
|
+
libretificacaotjcore/database/rubrica_repository.py,sha256=GtUgoRfuYHYNyBfllMWZPg69noVsfwCJoaXgDTmvmuw,1192
|
|
8
|
+
libretificacaotjcore/database/solicitacao_xml_repository.py,sha256=FWSPSV_tDjKQAnqaffejfX5lpndI3ZLiXa20fYn1BjM,1582
|
|
9
|
+
libretificacaotjcore/database/tempo_processo_repository.py,sha256=WisJ1ZGZp7wUV77PybCajNxM08lUnjzJ0LNJpcwkW_Q,4790
|
|
10
|
+
libretificacaotjcore/dtos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
libretificacaotjcore/dtos/arquivo_dto.py,sha256=D4LLJZf-Z0XhJmOvu7nAcBq0j70m9TFtbgxI9ikHXQ0,1045
|
|
12
|
+
libretificacaotjcore/dtos/processo_dto.py,sha256=vk2wyEsKLebqNVbxaEw5t-huPDoPR63fsy4092uORBw,3153
|
|
13
|
+
libretificacaotjcore/dtos/solicitacao_dto.py,sha256=UJ-JpB_85yNHLyea5UGdDv1D6XW9-Ig4P3NyNe2ddAs,2639
|
|
14
|
+
libretificacaotjcore/enums/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
libretificacaotjcore/enums/e_eventos.py,sha256=4NICIKFpxEvxMbHu_kcKqNXqylDETcr6qY1tK2bKmi4,207
|
|
16
|
+
libretificacaotjcore/enums/e_fase_retificacao.py,sha256=nbe0viCplUYBBUpoBLVJ8qUz-z1rfviW8_YoNuSNSL0,1232
|
|
17
|
+
libretificacaotjcore/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
libretificacaotjcore/services/crypto_pass_service.py,sha256=9D0vyjan6f_8AfNxGkLpGdvyMpojsJq_AAySpv_zKMc,740
|
|
19
|
+
libretificacaotjcore/services/file_service.py,sha256=14CJokBbrsryQGmL0_unH2QKZpnteEAfxf5CPFdv6cE,2075
|
|
20
|
+
libretificacaotjcore/services/rabbitmq_consumer.py,sha256=yRS3sAkaMIIn8Y06icwkO1bpddlLC7BmNSHJVhUalzY,1818
|
|
21
|
+
libretificacaotjcore/services/rabbitmq_publisher.py,sha256=xsb9LmIuZizzq5lxnEEZKMK3U952KSQ2oqlTBZi2Lt8,1787
|
|
22
|
+
libretificacaotjcore/services/request_servico_api.py,sha256=G7vnmvOfwrCGL-Jy_5tCKG-l5E00OCkJfkuoa5Y6sHo,613
|
|
23
|
+
libretificacaotjcore/services/s3_service.py,sha256=HKR_jt2H3XdV1PCzo5R5bnhmoQ3I46Yn5IqAvVPhsjs,2946
|
|
24
|
+
libretificacaotjcore-0.1.59.dist-info/METADATA,sha256=fACouGH6Jc1LPvEjuwUYmN5q8PqKxSMApdVKTlgQgWg,2564
|
|
25
|
+
libretificacaotjcore-0.1.59.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
26
|
+
libretificacaotjcore-0.1.59.dist-info/top_level.txt,sha256=J9vnz_X9OUnxC-eXHiAzlc9xIrWBwZ5bgnIDQIIFY4c,21
|
|
27
|
+
libretificacaotjcore-0.1.59.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
libretificacaotjcore
|