lbxtoolkit 2.2.8__tar.gz → 2.2.10__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.
- {lbxtoolkit-2.2.8 → lbxtoolkit-2.2.10}/PKG-INFO +8 -6
- {lbxtoolkit-2.2.8 → lbxtoolkit-2.2.10}/README.md +7 -5
- {lbxtoolkit-2.2.8 → lbxtoolkit-2.2.10}/lbxtoolkit/api_rest.py +8 -2
- {lbxtoolkit-2.2.8 → lbxtoolkit-2.2.10}/lbxtoolkit/lbx_logger.py +21 -2
- {lbxtoolkit-2.2.8 → lbxtoolkit-2.2.10}/lbxtoolkit/misc.py +13 -4
- {lbxtoolkit-2.2.8 → lbxtoolkit-2.2.10}/lbxtoolkit/postgresql.py +49 -7
- {lbxtoolkit-2.2.8 → lbxtoolkit-2.2.10}/pyproject.toml +1 -1
- {lbxtoolkit-2.2.8 → lbxtoolkit-2.2.10}/lbxtoolkit/__init__.py +0 -0
- {lbxtoolkit-2.2.8 → lbxtoolkit-2.2.10}/lbxtoolkit/auth_entra_id.py +0 -0
- {lbxtoolkit-2.2.8 → lbxtoolkit-2.2.10}/lbxtoolkit/config_manager.py +0 -0
- {lbxtoolkit-2.2.8 → lbxtoolkit-2.2.10}/lbxtoolkit/service.py +0 -0
- {lbxtoolkit-2.2.8 → lbxtoolkit-2.2.10}/lbxtoolkit/service_windows.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lbxtoolkit
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.10
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Cristiano P. Ferrari
|
|
6
6
|
Author-email: cristiano.ferrari@lbx.com.br
|
|
@@ -60,7 +60,7 @@ Interage com o banco de dados PostgreSQL
|
|
|
60
60
|
Interage com APIs RESTfull, especialmente providas para a plataforma Sienge
|
|
61
61
|
- `auth_base` Autentica (HTTPBasicAuth) sessão na API
|
|
62
62
|
- `auth_bearer` Autentica sessão na API pelos métodos: OAuth, JWT, Bearer
|
|
63
|
-
- `endpoint_json` Realizad chama ao endpoint. Payload em formato `json`
|
|
63
|
+
- `endpoint_json` Realizad chama ao endpoint. Aceita opcionalmente Payload em formato `json` ou arquivo binário.
|
|
64
64
|
- `trata_erro_sienge` Retorna a mensagem de erro do Sienge caso código de retorno seja diferente de 200.
|
|
65
65
|
- `close` Encerra a sessão autenticada
|
|
66
66
|
|
|
@@ -145,7 +145,7 @@ São nessários 2 parâmetros posicionais obrigatórios, e 5 parametros nominais
|
|
|
145
145
|
- `cadencia` Número máximo de chamadas *por segudo* à API
|
|
146
146
|
- `timeout` Tempo máximo (segundos) para aguardar retorno à chamada. Padrão 6s, se omisso.
|
|
147
147
|
- `logger` O objeto _log handler_ para lidar com as informações de saída. Se não informado, todas as saídas serão direcionadas para a stdout.
|
|
148
|
-
- `headers` Cabeçalhos _http_ para a requisição à API.
|
|
148
|
+
- `headers` Cabeçalhos _http_ para a requisição à API. Se não fornecido, será incluida implicitamente a chave `Content-Type` com valor `application/json`.
|
|
149
149
|
- `verify` Verifica a validade do certificado SSL do servidor de destino da requisição.
|
|
150
150
|
|
|
151
151
|
Quanto às credenciais de autenticação, assim como a classe de interação com o PostgreSQL, elas precisam ser fornecidas na forma de um *dicionário*.
|
|
@@ -162,12 +162,14 @@ Caso a autenticação seja pelo método `api_rest.aut_bearer()`, o dicionário d
|
|
|
162
162
|
A classe possui 3 métodos:
|
|
163
163
|
- `api_rest.auth_basic()`: instanciamento da sessão autenticando pelo método HTTPBasicAuth
|
|
164
164
|
- `api_rest.auth_bearer()`: instanciamento da sessão autenticando pelos métodos OAuth, JWT, Bearer
|
|
165
|
-
- `api_rest.endpoint_json([endereço], [método], payload=None)`: para a chamada ao endpoint
|
|
165
|
+
- `api_rest.endpoint_json([endereço], [método], payload=None, files=None)`: para a chamada ao endpoint
|
|
166
166
|
- `close()` para encerra a instância/sessão
|
|
167
167
|
|
|
168
|
-
O consumo é feito pelo método `api_rest.endpoint_json` que suporta
|
|
168
|
+
O consumo é feito pelo método `api_rest.endpoint_json` que suporta APIs cujo payload (opcional) seja aceito no formato JSON ou binario (files).
|
|
169
169
|
|
|
170
|
-
Esse método espera 2 parametros posicionais obrigatórios: o endereço do endpoint e o verbo (get, post, patch ou put), tendo parametro opcional o objeto de 'payload' (json).
|
|
170
|
+
Esse método espera 2 parametros posicionais obrigatórios: o endereço do endpoint e o verbo (get, post, patch ou put), tendo parametro opcional o objeto de 'payload' (json) ou 'files' (binário).
|
|
171
|
+
O método define no `headers` a chavee `Content-Type` implicita e automaticamente com o valor `application/json`, independente de ser ou não fornecido payload.
|
|
172
|
+
Se a carga for do tipo binário, é necessário informar o Content-Type correto (`multipart/form-data`) ou repassar um dicionario vazio (`{}`) para o parametro `headers` no instanciamento da classe `api_rest`
|
|
171
173
|
Note que o endereço do endpoint deve ser informado completo. A URL informada no instanciamento da classe corresponde apenas ao endereço de autenticação.
|
|
172
174
|
|
|
173
175
|
O tempo, em segundos, transcorrido entre a chamada a atual e a chamada anterior ao endpoint pode ser consultado pelo argumento `.Intervalo` no objeto recebido do retorno à chamada ao método `.endpoint_json`.
|
|
@@ -38,7 +38,7 @@ Interage com o banco de dados PostgreSQL
|
|
|
38
38
|
Interage com APIs RESTfull, especialmente providas para a plataforma Sienge
|
|
39
39
|
- `auth_base` Autentica (HTTPBasicAuth) sessão na API
|
|
40
40
|
- `auth_bearer` Autentica sessão na API pelos métodos: OAuth, JWT, Bearer
|
|
41
|
-
- `endpoint_json` Realizad chama ao endpoint. Payload em formato `json`
|
|
41
|
+
- `endpoint_json` Realizad chama ao endpoint. Aceita opcionalmente Payload em formato `json` ou arquivo binário.
|
|
42
42
|
- `trata_erro_sienge` Retorna a mensagem de erro do Sienge caso código de retorno seja diferente de 200.
|
|
43
43
|
- `close` Encerra a sessão autenticada
|
|
44
44
|
|
|
@@ -123,7 +123,7 @@ São nessários 2 parâmetros posicionais obrigatórios, e 5 parametros nominais
|
|
|
123
123
|
- `cadencia` Número máximo de chamadas *por segudo* à API
|
|
124
124
|
- `timeout` Tempo máximo (segundos) para aguardar retorno à chamada. Padrão 6s, se omisso.
|
|
125
125
|
- `logger` O objeto _log handler_ para lidar com as informações de saída. Se não informado, todas as saídas serão direcionadas para a stdout.
|
|
126
|
-
- `headers` Cabeçalhos _http_ para a requisição à API.
|
|
126
|
+
- `headers` Cabeçalhos _http_ para a requisição à API. Se não fornecido, será incluida implicitamente a chave `Content-Type` com valor `application/json`.
|
|
127
127
|
- `verify` Verifica a validade do certificado SSL do servidor de destino da requisição.
|
|
128
128
|
|
|
129
129
|
Quanto às credenciais de autenticação, assim como a classe de interação com o PostgreSQL, elas precisam ser fornecidas na forma de um *dicionário*.
|
|
@@ -140,12 +140,14 @@ Caso a autenticação seja pelo método `api_rest.aut_bearer()`, o dicionário d
|
|
|
140
140
|
A classe possui 3 métodos:
|
|
141
141
|
- `api_rest.auth_basic()`: instanciamento da sessão autenticando pelo método HTTPBasicAuth
|
|
142
142
|
- `api_rest.auth_bearer()`: instanciamento da sessão autenticando pelos métodos OAuth, JWT, Bearer
|
|
143
|
-
- `api_rest.endpoint_json([endereço], [método], payload=None)`: para a chamada ao endpoint
|
|
143
|
+
- `api_rest.endpoint_json([endereço], [método], payload=None, files=None)`: para a chamada ao endpoint
|
|
144
144
|
- `close()` para encerra a instância/sessão
|
|
145
145
|
|
|
146
|
-
O consumo é feito pelo método `api_rest.endpoint_json` que suporta
|
|
146
|
+
O consumo é feito pelo método `api_rest.endpoint_json` que suporta APIs cujo payload (opcional) seja aceito no formato JSON ou binario (files).
|
|
147
147
|
|
|
148
|
-
Esse método espera 2 parametros posicionais obrigatórios: o endereço do endpoint e o verbo (get, post, patch ou put), tendo parametro opcional o objeto de 'payload' (json).
|
|
148
|
+
Esse método espera 2 parametros posicionais obrigatórios: o endereço do endpoint e o verbo (get, post, patch ou put), tendo parametro opcional o objeto de 'payload' (json) ou 'files' (binário).
|
|
149
|
+
O método define no `headers` a chavee `Content-Type` implicita e automaticamente com o valor `application/json`, independente de ser ou não fornecido payload.
|
|
150
|
+
Se a carga for do tipo binário, é necessário informar o Content-Type correto (`multipart/form-data`) ou repassar um dicionario vazio (`{}`) para o parametro `headers` no instanciamento da classe `api_rest`
|
|
149
151
|
Note que o endereço do endpoint deve ser informado completo. A URL informada no instanciamento da classe corresponde apenas ao endereço de autenticação.
|
|
150
152
|
|
|
151
153
|
O tempo, em segundos, transcorrido entre a chamada a atual e a chamada anterior ao endpoint pode ser consultado pelo argumento `.Intervalo` no objeto recebido do retorno à chamada ao método `.endpoint_json`.
|
|
@@ -216,17 +216,23 @@ Para uso em APIs com autenticação JWT (JSON Web Token), OAuth, Bearer Token Au
|
|
|
216
216
|
return self.RetEndPoint
|
|
217
217
|
#
|
|
218
218
|
#
|
|
219
|
-
def endpoint_json(self, endpoint, metodo, payload=None): # Interage com End Point
|
|
219
|
+
def endpoint_json(self, endpoint, metodo, payload=None, file=None): # Interage com End Point
|
|
220
220
|
self.ult_tempo_req = time()
|
|
221
221
|
self.Metodo = metodo.lower()
|
|
222
222
|
#self.EndPoint = self.Url + endpoint
|
|
223
223
|
self.EndPoint = endpoint
|
|
224
224
|
self.Payload = payload
|
|
225
|
+
self.File = file
|
|
226
|
+
|
|
225
227
|
MetodosAceitos = ['post', 'get', 'patch', 'put']
|
|
226
228
|
if not any(element in self.Metodo for element in MetodosAceitos):
|
|
227
229
|
self.logger.critico(f'Método {self.Metodo} não previsto. Abortando chamada!', exit=1)
|
|
228
230
|
else:
|
|
229
|
-
ChamadaApi = f'self.Sessao.{self.Metodo}(self.EndPoint, timeout=self.Timeout, headers=self.Headers, verify=self.Verify)' if self.Payload is None
|
|
231
|
+
ChamadaApi = f'self.Sessao.{self.Metodo}(self.EndPoint, timeout=self.Timeout, headers=self.Headers, verify=self.Verify)' if self.Payload is None and self.File is None else \
|
|
232
|
+
f'self.Sessao.{self.Metodo}(self.EndPoint, timeout=self.Timeout, headers=self.Headers, verify=self.Verify, json=self.Payload)' if self.Payload and self.File is None else \
|
|
233
|
+
f'self.Sessao.{self.Metodo}(self.EndPoint, timeout=self.Timeout, headers=self.Headers, verify=self.Verify, files=self.File)' if self.Payload is None and self.File else \
|
|
234
|
+
f'self.Sessao.{self.Metodo}(self.EndPoint, timeout=self.Timeout, headers=self.Headers, verify=self.Verify, json=self.Payload, files=self.File)'
|
|
235
|
+
|
|
230
236
|
self.controla_cadencia()
|
|
231
237
|
self.TempoUltReq = time()
|
|
232
238
|
try:
|
|
@@ -3,6 +3,7 @@ import sys
|
|
|
3
3
|
import re
|
|
4
4
|
import datetime
|
|
5
5
|
import logging
|
|
6
|
+
import traceback
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
8
9
|
class lbx_logger: # Classe para gerenciar a saída para log
|
|
@@ -75,6 +76,9 @@ Considerando que os parametros são posicionais, caso queira omitir apenas um do
|
|
|
75
76
|
|
|
76
77
|
A saída dessa função retorna um objeto, que pode ser salvo em disco ou impresso na tela.
|
|
77
78
|
|
|
79
|
+
5. O método `.traceback_log`, quando acionado, grava, de maneira incremental, o tracback do último erro no arquivo `traceback.log` no mesmo diretório de log onde o arquivo principal de log está salvo. Se o log não está sendo salvo, a mensagem será exibida na tela.
|
|
80
|
+
|
|
81
|
+
Os métodos `.erro()` e `.critico()` fazem isso automaticamente por padrão.
|
|
78
82
|
|
|
79
83
|
5. Exemplos de uso:
|
|
80
84
|
|
|
@@ -168,7 +172,7 @@ for line in filtered_lines:
|
|
|
168
172
|
|
|
169
173
|
if log_file_path:
|
|
170
174
|
# Criando um handler para escrever em um arquivo de log
|
|
171
|
-
file_handler = logging.FileHandler(self.log_file_path)
|
|
175
|
+
file_handler = logging.FileHandler(self.log_file_path, encoding="utf-8")
|
|
172
176
|
file_handler.setLevel(log_level) # Sempre registrar tudo no arquivo
|
|
173
177
|
|
|
174
178
|
# Criando um handler para exibir no console
|
|
@@ -187,6 +191,9 @@ for line in filtered_lines:
|
|
|
187
191
|
# Adicionando os handlers ao logger
|
|
188
192
|
self.logger.addHandler(file_handler)
|
|
189
193
|
self.logger.addHandler(console_handler)
|
|
194
|
+
|
|
195
|
+
# Saida para o traceback
|
|
196
|
+
self.traceback_file = Path(self.log_file_path.parent, 'traceback.log')
|
|
190
197
|
else:
|
|
191
198
|
# Tudo direcionado para o console
|
|
192
199
|
console_handler = logging.StreamHandler()
|
|
@@ -245,6 +252,7 @@ for line in filtered_lines:
|
|
|
245
252
|
#
|
|
246
253
|
#
|
|
247
254
|
def write(self, message):
|
|
255
|
+
message=message
|
|
248
256
|
if message.strip(): # Ignorar mensagens vazias
|
|
249
257
|
self.logger.info(message.strip())
|
|
250
258
|
#
|
|
@@ -285,6 +293,7 @@ for line in filtered_lines:
|
|
|
285
293
|
msg = self.msg[:corte] if corte else self.msg
|
|
286
294
|
self.logger.error(msg)
|
|
287
295
|
self.msg = ''
|
|
296
|
+
self.log_traceback()
|
|
288
297
|
if exit:
|
|
289
298
|
os._exit(exit)
|
|
290
299
|
#
|
|
@@ -294,6 +303,7 @@ for line in filtered_lines:
|
|
|
294
303
|
msg = self.msg[:corte] if corte else self.msg
|
|
295
304
|
self.logger.critical(msg)
|
|
296
305
|
self.msg = ''
|
|
306
|
+
self.log_traceback()
|
|
297
307
|
if exit:
|
|
298
308
|
os._exit(exit)
|
|
299
309
|
#
|
|
@@ -387,7 +397,16 @@ for line in filtered_lines:
|
|
|
387
397
|
# Retornar o objeto contendo as linhas filtradas
|
|
388
398
|
return filtered_lines
|
|
389
399
|
#
|
|
390
|
-
#
|
|
400
|
+
#
|
|
401
|
+
def log_traceback(self):
|
|
402
|
+
if self.traceback_file:
|
|
403
|
+
with open(self.traceback_file, "a", encoding="utf-8") as f:
|
|
404
|
+
f.write("\n" + "-"*50 + "\n") # Separador entre erros
|
|
405
|
+
f.write(f"\n{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S,%f")[:-3]}")
|
|
406
|
+
f.write(traceback.format_exc())
|
|
407
|
+
else:
|
|
408
|
+
traceback.format_exc()
|
|
409
|
+
|
|
391
410
|
class SafeLogger: ## intercepta as entradas de log para parar o daemon/watcher quando parametro EXIT existir
|
|
392
411
|
"""
|
|
393
412
|
Essa classe tem o propoósito de interceptar as ordens de saída (exit) disparadas pelos métodos do lbx_logger e interromper um daemon/serviço que esteja em execução antes.
|
|
@@ -44,14 +44,23 @@ Sintaxe e exemplos de uso. Parametros omissos assume-se os valores padrão indic
|
|
|
44
44
|
return Diretorio
|
|
45
45
|
#
|
|
46
46
|
#
|
|
47
|
-
def normaliza(Original): # Limpa e padroniza nomes
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
def normaliza(Original, Case=None, KeepSpaces=False, KeepDelim=False, KeepDirTree=False): # Limpa e padroniza nomes
|
|
48
|
+
|
|
49
|
+
Lixo = r'?%§ªº°`´^~*|"<>!@#$%¨&*_+=-"\''
|
|
50
|
+
Lixo = Lixo + r' ' if not KeepSpaces else Lixo
|
|
51
|
+
Lixo = Lixo + r'(){}[]' if not KeepDelim else Lixo
|
|
52
|
+
Lixo = Lixo + r':/\\' if not KeepDirTree else Lixo
|
|
53
|
+
|
|
54
|
+
if Case and not Case.lower() in ['upper', 'lower', 'keep']:
|
|
55
|
+
raise ValueError('Parametro opcioanl "Case" deve ser ["upper", "lower", "keep"]. Se omisso (None), string será convertida para caixa baixa.')
|
|
56
|
+
|
|
57
|
+
Normalizar = (''.join(c for c in normalize('NFKD', Original) if c.encode('ascii', 'ignore').decode('ascii'))) if Case.upper() == 'keep' else normalize('NFKD', Original).encode('ASCII', 'ignore').decode('ASCII')
|
|
50
58
|
RemoverLixo = [c if c not in Lixo else '_' for c in Normalizar]
|
|
51
59
|
Limpo = "".join(RemoverLixo)
|
|
52
60
|
Limpo = re.sub(r'\.(?=.*\.)', '_', Limpo) # troca todos os pontos por underline
|
|
53
61
|
Limpo = re.sub(r'_+', '_', Limpo) # limpa as reptições do underline
|
|
54
|
-
|
|
62
|
+
Limpo = Limpo.lower() if not Case or Case.lower() == 'lower' else Limpo.upper() if Case.lower() == 'upper' else Limpo
|
|
63
|
+
return Limpo
|
|
55
64
|
#
|
|
56
65
|
#
|
|
57
66
|
def get_cmd_window(): # Captura a referencia da janela atual para retornar o foco à ela depois de chamar os pickers
|
|
@@ -4,6 +4,9 @@ import psycopg2
|
|
|
4
4
|
from psycopg2.extras import execute_values
|
|
5
5
|
import pandas as pd
|
|
6
6
|
import numpy as np
|
|
7
|
+
from io import StringIO
|
|
8
|
+
import json
|
|
9
|
+
from pathlib import Path
|
|
7
10
|
|
|
8
11
|
class postgreSQL: # Classe de acesso e interação com banco PostgreSQL
|
|
9
12
|
"""
|
|
@@ -153,11 +156,13 @@ conexao.close()
|
|
|
153
156
|
raise
|
|
154
157
|
#
|
|
155
158
|
#
|
|
156
|
-
def csv_df(self,
|
|
159
|
+
def csv_df(self, Csv, CsvDelim=';', **kwargs): # Le arquivo CSV e gera Dataframe do Pandas
|
|
157
160
|
"""
|
|
161
|
+
Parametro 'Csv' pode ser um caminho do pathlib [Path()] ou uma string com o conteúdo do CSV.
|
|
158
162
|
Os parametros de read_csv() do Pandas podem ser passados opcionalmente para a função para ajustar a importação
|
|
159
163
|
"""
|
|
160
164
|
try:
|
|
165
|
+
CsvPath = Csv if isinstance(Csv,Path) else StringIO(Csv)
|
|
161
166
|
DataFrame = pd.read_csv(CsvPath, delimiter=CsvDelim, **kwargs) # Verifique se o delimitador é ';'
|
|
162
167
|
DataFrame.replace({np.nan: None}, inplace=True) ## troca 'NaN' por None (null no postgresql)
|
|
163
168
|
return DataFrame
|
|
@@ -165,22 +170,59 @@ conexao.close()
|
|
|
165
170
|
raise
|
|
166
171
|
#
|
|
167
172
|
#
|
|
168
|
-
def db_insert_df(self, DataFrame, Tabela, Schema=None, Colunas=None, OnConflict=None): # Insere os dados de um dataframe em uma tabela equivalente no banco (exige mesma estrutura de colunas)
|
|
173
|
+
def db_insert_df(self, DataFrame, Tabela, Schema=None, Colunas=None, OnConflict=None, TypeCast=None): # Insere os dados de um dataframe em uma tabela equivalente no banco (exige mesma estrutura de colunas)
|
|
174
|
+
"""
|
|
175
|
+
Insere um DataFrame no PostgreSQL usando psycopg2 e execute_values().
|
|
176
|
+
|
|
177
|
+
Parâmetros:
|
|
178
|
+
- DataFrame: O DataFrame do pandas contendo os dados.
|
|
179
|
+
- Tabela: Nome da tabela no PostgreSQL.
|
|
180
|
+
- Schema: (Opcional) Schema da tabela (padrão: 'public').
|
|
181
|
+
- Colunas: (Opcional) Lista de colunas para inserir (padrão: todas as colunas do DataFrame).
|
|
182
|
+
- OnConflict: (Opcional) Cláusula ON CONFLICT para tratamento de duplicatas.
|
|
183
|
+
- TypeCast: (Opcional) Dicionário mapeando colunas para seus respectivos tipos de dado no PostgreSQL.
|
|
184
|
+
"""
|
|
169
185
|
# Essa função exige que os nomes dos cabeçalhos das colunas do CSV sejam os mesmos das colunas da tabela de destino
|
|
170
186
|
Colunas = Colunas or DataFrame.columns.tolist() # Caso não seja fornecida a lista de colunas, assume as colunas do DataFrame
|
|
171
|
-
|
|
172
|
-
|
|
187
|
+
Schema = Schema or 'public'
|
|
188
|
+
#Valores = [tuple(Linha) for Linha in DataFrame[Colunas].values] ## código anterior para definir os valores. alterado em 05/02/25 para tratar o typecast das colunas
|
|
189
|
+
#Definição dos valores com typecast das colunas
|
|
190
|
+
TypeCast = TypeCast or {}
|
|
191
|
+
|
|
192
|
+
# Converter JSON de string para objeto Python, se necessário
|
|
193
|
+
# for coluna, tipo in TypeCast.items():
|
|
194
|
+
# if tipo == "jsonb" and coluna in DataFrame.columns:
|
|
195
|
+
# DataFrame[coluna] = DataFrame[coluna].apply(json.loads) # Transforma string JSON em objeto Python
|
|
196
|
+
|
|
197
|
+
# Gerar valores tratados, aplicando casts se necessário
|
|
198
|
+
Valores = []
|
|
199
|
+
for _, Linha in DataFrame.iterrows():
|
|
200
|
+
Linha_convertida = []
|
|
201
|
+
for coluna in Colunas:
|
|
202
|
+
valor = Linha[coluna]
|
|
203
|
+
|
|
204
|
+
# Tratamento especial para JSONB
|
|
205
|
+
if coluna in TypeCast and TypeCast[coluna] == "jsonb":
|
|
206
|
+
valor = json.dumps(valor) # Converte de volta para string JSON válida
|
|
207
|
+
#valor += "::jsonb" # Adiciona typecast explícito
|
|
208
|
+
|
|
209
|
+
# Aplicar typecast para outros tipos de dados
|
|
210
|
+
elif coluna in TypeCast:
|
|
211
|
+
valor = f"{valor}::{TypeCast[coluna]}"
|
|
212
|
+
|
|
213
|
+
Linha_convertida.append(valor)
|
|
214
|
+
Valores.append(tuple(Linha_convertida))
|
|
215
|
+
|
|
173
216
|
Query = f'insert into {Schema}.{Tabela} ({', '.join(Colunas)}) values %s '
|
|
174
|
-
|
|
175
|
-
Query = Query + OnConflict
|
|
217
|
+
Query = Query + OnConflict if OnConflict else Query
|
|
176
218
|
|
|
177
219
|
try:
|
|
178
220
|
self.Cursor = self.Conexao.cursor()
|
|
179
221
|
execute_values(self.Cursor, Query, Valores)
|
|
180
222
|
self.Conexao.commit()
|
|
181
223
|
except Exception as Err:
|
|
224
|
+
self.logger.erro(f'{__name__}: Erro [{Err}] ao inserir dados na tabela {Schema}.{Tabela}!')
|
|
182
225
|
self.Conexao.rollback()
|
|
183
|
-
raise
|
|
184
226
|
finally:
|
|
185
227
|
self.Cursor.close()
|
|
186
228
|
#Conexao.close() ## conexão precisa ser fechada explicitamente fora da classe
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|