lbxtoolkit 2.2.10__tar.gz → 2.2.11__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.10 → lbxtoolkit-2.2.11}/PKG-INFO +1 -1
- {lbxtoolkit-2.2.10 → lbxtoolkit-2.2.11}/lbxtoolkit/lbx_logger.py +2 -2
- {lbxtoolkit-2.2.10 → lbxtoolkit-2.2.11}/lbxtoolkit/misc.py +2 -2
- {lbxtoolkit-2.2.10 → lbxtoolkit-2.2.11}/lbxtoolkit/postgresql.py +41 -28
- {lbxtoolkit-2.2.10 → lbxtoolkit-2.2.11}/lbxtoolkit/service_windows.py +40 -0
- {lbxtoolkit-2.2.10 → lbxtoolkit-2.2.11}/pyproject.toml +1 -1
- {lbxtoolkit-2.2.10 → lbxtoolkit-2.2.11}/README.md +0 -0
- {lbxtoolkit-2.2.10 → lbxtoolkit-2.2.11}/lbxtoolkit/__init__.py +0 -0
- {lbxtoolkit-2.2.10 → lbxtoolkit-2.2.11}/lbxtoolkit/api_rest.py +0 -0
- {lbxtoolkit-2.2.10 → lbxtoolkit-2.2.11}/lbxtoolkit/auth_entra_id.py +0 -0
- {lbxtoolkit-2.2.10 → lbxtoolkit-2.2.11}/lbxtoolkit/config_manager.py +0 -0
- {lbxtoolkit-2.2.10 → lbxtoolkit-2.2.11}/lbxtoolkit/service.py +0 -0
|
@@ -318,7 +318,7 @@ for line in filtered_lines:
|
|
|
318
318
|
self.logger.removeHandler(handler)
|
|
319
319
|
#
|
|
320
320
|
#
|
|
321
|
-
def filtra(self, log_file, dh_ini, dh_fim):
|
|
321
|
+
def filtra(self, log_file, dh_ini, dh_fim, logEncode='ISO-8859-1'):
|
|
322
322
|
# Validar parametros de entrada
|
|
323
323
|
if dh_ini:
|
|
324
324
|
if not isinstance(dh_ini, datetime.datetime):
|
|
@@ -374,7 +374,7 @@ for line in filtered_lines:
|
|
|
374
374
|
return dh_inicio <= timestamp <= dh_fim
|
|
375
375
|
|
|
376
376
|
# Ler e filtrar o arquivo de log com a codificação ISO-8859-1
|
|
377
|
-
with open(log_file_path, 'r', encoding=
|
|
377
|
+
with open(log_file_path, 'r', encoding=logEncode) as log_file:
|
|
378
378
|
log_lines = log_file.readlines()
|
|
379
379
|
|
|
380
380
|
# Variável para armazenar o último timestamp válido
|
|
@@ -44,7 +44,7 @@ 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, Case=
|
|
47
|
+
def normaliza(Original, Case='keep', KeepSpaces=False, KeepDelim=False, KeepDirTree=False): # Limpa e padroniza nomes
|
|
48
48
|
|
|
49
49
|
Lixo = r'?%§ªº°`´^~*|"<>!@#$%¨&*_+=-"\''
|
|
50
50
|
Lixo = Lixo + r' ' if not KeepSpaces else Lixo
|
|
@@ -54,7 +54,7 @@ Sintaxe e exemplos de uso. Parametros omissos assume-se os valores padrão indic
|
|
|
54
54
|
if Case and not Case.lower() in ['upper', 'lower', 'keep']:
|
|
55
55
|
raise ValueError('Parametro opcioanl "Case" deve ser ["upper", "lower", "keep"]. Se omisso (None), string será convertida para caixa baixa.')
|
|
56
56
|
|
|
57
|
-
Normalizar = (''.join(c for c in normalize('NFKD', Original) if c.encode('ascii', 'ignore').decode('ascii'))) if Case.
|
|
57
|
+
Normalizar = (''.join(c for c in normalize('NFKD', Original) if c.encode('ascii', 'ignore').decode('ascii'))) if Case.lower() == 'keep' else normalize('NFKD', Original).encode('ASCII', 'ignore').decode('ASCII')
|
|
58
58
|
RemoverLixo = [c if c not in Lixo else '_' for c in Normalizar]
|
|
59
59
|
Limpo = "".join(RemoverLixo)
|
|
60
60
|
Limpo = re.sub(r'\.(?=.*\.)', '_', Limpo) # troca todos os pontos por underline
|
|
@@ -170,7 +170,7 @@ conexao.close()
|
|
|
170
170
|
raise
|
|
171
171
|
#
|
|
172
172
|
#
|
|
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)
|
|
173
|
+
def db_insert_df(self, DataFrame, Tabela, Schema=None, Colunas=None, OnConflict=None, TypeCast=None, Returning=None): # Insere os dados de um dataframe em uma tabela equivalente no banco (exige mesma estrutura de colunas)
|
|
174
174
|
"""
|
|
175
175
|
Insere um DataFrame no PostgreSQL usando psycopg2 e execute_values().
|
|
176
176
|
|
|
@@ -183,49 +183,62 @@ conexao.close()
|
|
|
183
183
|
- TypeCast: (Opcional) Dicionário mapeando colunas para seus respectivos tipos de dado no PostgreSQL.
|
|
184
184
|
"""
|
|
185
185
|
# Essa função exige que os nomes dos cabeçalhos das colunas do CSV sejam os mesmos das colunas da tabela de destino
|
|
186
|
-
Colunas = Colunas or DataFrame.columns.tolist()
|
|
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
|
|
186
|
+
Colunas = Colunas or DataFrame.columns.tolist()
|
|
187
|
+
Schema = Schema or 'public'
|
|
190
188
|
TypeCast = TypeCast or {}
|
|
191
189
|
|
|
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
190
|
Valores = []
|
|
191
|
+
|
|
199
192
|
for _, Linha in DataFrame.iterrows():
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
193
|
+
Linha_convertida = []
|
|
194
|
+
for coluna in Colunas:
|
|
195
|
+
valor = Linha[coluna]
|
|
196
|
+
|
|
197
|
+
# 🔹 Converte Timestamp para string ISO 8601
|
|
198
|
+
if isinstance(valor, pd.Timestamp):
|
|
199
|
+
valor = valor.strftime('%Y-%m-%d %H:%M:%S')
|
|
200
|
+
|
|
201
|
+
# 🔹 Converte JSON para string válida
|
|
202
|
+
if coluna in TypeCast and TypeCast[coluna] == "jsonb":
|
|
203
|
+
if isinstance(valor, str):
|
|
204
|
+
try:
|
|
205
|
+
json.loads(valor) # Valida se já é um JSON válido
|
|
206
|
+
except json.JSONDecodeError:
|
|
207
|
+
valor = json.dumps(valor) # Serializa para JSON
|
|
208
|
+
else:
|
|
209
|
+
valor = json.dumps(valor) # Converte para JSON corretamente
|
|
203
210
|
|
|
204
|
-
|
|
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
|
|
211
|
+
Linha_convertida.append(valor)
|
|
208
212
|
|
|
209
|
-
#
|
|
210
|
-
elif coluna in TypeCast:
|
|
211
|
-
valor = f"{valor}::{TypeCast[coluna]}"
|
|
213
|
+
Valores.append(tuple(Linha_convertida)) # Garante que cada linha seja uma tupla
|
|
212
214
|
|
|
213
|
-
|
|
214
|
-
Valores.append(tuple(Linha_convertida))
|
|
215
|
+
Retorno = None
|
|
215
216
|
|
|
216
|
-
|
|
217
|
-
|
|
217
|
+
Retornar = f"RETURNING {', '.join(Returning)}" if Returning else ''
|
|
218
|
+
Conflito = OnConflict if OnConflict else ''
|
|
219
|
+
|
|
220
|
+
Parametros = ", ".join(["%s"] * len(Colunas))
|
|
221
|
+
Query = f'INSERT INTO {Schema}.{Tabela} ({", ".join(Colunas)}) VALUES ({Parametros}) {Conflito} {Retornar}'
|
|
218
222
|
|
|
219
223
|
try:
|
|
220
|
-
self.Cursor = self.Conexao.cursor()
|
|
221
|
-
|
|
222
|
-
|
|
224
|
+
self.Cursor = self.Conexao.cursor()
|
|
225
|
+
|
|
226
|
+
if Returning:
|
|
227
|
+
Retorno = []
|
|
228
|
+
for valor in Valores:
|
|
229
|
+
self.Cursor.execute(Query, valor)
|
|
230
|
+
Retorno.append(self.Cursor.fetchone())
|
|
231
|
+
else:
|
|
232
|
+
self.Cursor.executemany(Query, Valores)
|
|
233
|
+
|
|
234
|
+
self.Conexao.commit()
|
|
223
235
|
except Exception as Err:
|
|
224
236
|
self.logger.erro(f'{__name__}: Erro [{Err}] ao inserir dados na tabela {Schema}.{Tabela}!')
|
|
225
237
|
self.Conexao.rollback()
|
|
226
238
|
finally:
|
|
227
239
|
self.Cursor.close()
|
|
228
240
|
#Conexao.close() ## conexão precisa ser fechada explicitamente fora da classe
|
|
241
|
+
return Retorno if Retorno else None
|
|
229
242
|
#
|
|
230
243
|
#
|
|
231
244
|
def db_select(self, Query): # Retorna um cursor à partir de um select
|
|
@@ -195,6 +195,46 @@ Gerencia a instalação e execução de um daemon como um serviço do windows. T
|
|
|
195
195
|
self.RunAsDaemon()
|
|
196
196
|
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
|
|
197
197
|
servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, ''))
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@staticmethod
|
|
201
|
+
def SvcStatus(ServiceName=None): # Verifica o status de um serviço específico ou do próprio serviço
|
|
202
|
+
"""
|
|
203
|
+
Retorna o status do serviço do Windows.
|
|
204
|
+
Se um nome for passado como parâmetro, verifica o status do serviço informado.
|
|
205
|
+
Caso contrário, retorna o status do próprio serviço.
|
|
206
|
+
"""
|
|
207
|
+
try:
|
|
208
|
+
# Recuperar nome do serviço, se não for passado
|
|
209
|
+
if not ServiceName:
|
|
210
|
+
config = ConfigManager.get('config')
|
|
211
|
+
if not config or 'name' not in config:
|
|
212
|
+
raise ValueError("Configuração do serviço inválida. O nome do serviço não pode ser determinado.")
|
|
213
|
+
ServiceName = config['name']
|
|
214
|
+
|
|
215
|
+
# Executa o comando SC QUERY para verificar o status
|
|
216
|
+
resultado = subprocess.run(["sc", "query", ServiceName], capture_output=True, text=True)
|
|
217
|
+
saida = resultado.stdout
|
|
218
|
+
|
|
219
|
+
# Buscar o status na saída do comando
|
|
220
|
+
if "RUNNING" in saida:
|
|
221
|
+
return "RUNNING"
|
|
222
|
+
elif "STOPPED" in saida:
|
|
223
|
+
return "STOPPED"
|
|
224
|
+
elif "PAUSED" in saida:
|
|
225
|
+
return "PAUSED"
|
|
226
|
+
elif "START_PENDING" in saida:
|
|
227
|
+
return "START_PENDING"
|
|
228
|
+
elif "STOP_PENDING" in saida:
|
|
229
|
+
return "STOP_PENDING"
|
|
230
|
+
else:
|
|
231
|
+
return f"UNKNOWN STATUS: {saida.strip()}"
|
|
232
|
+
|
|
233
|
+
except Exception as Err:
|
|
234
|
+
log = ConfigManager.get('log')
|
|
235
|
+
if log:
|
|
236
|
+
log.erro(f"Erro ao obter status do serviço [{ServiceName}]: {Err}")
|
|
237
|
+
return f"ERROR: {Err}"
|
|
198
238
|
#
|
|
199
239
|
#
|
|
200
240
|
@staticmethod
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|