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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lbxtoolkit
3
- Version: 2.2.10
3
+ Version: 2.2.11
4
4
  Summary:
5
5
  Author: Cristiano P. Ferrari
6
6
  Author-email: cristiano.ferrari@lbx.com.br
@@ -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='ISO-8859-1') as log_file:
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=None, KeepSpaces=False, KeepDelim=False, KeepDirTree=False): # Limpa e padroniza nomes
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.upper() == 'keep' else normalize('NFKD', Original).encode('ASCII', 'ignore').decode('ASCII')
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() # Caso não seja fornecida a lista de colunas, assume as colunas do DataFrame
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
- Linha_convertida = []
201
- for coluna in Colunas:
202
- valor = Linha[coluna]
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
- # 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
211
+ Linha_convertida.append(valor)
208
212
 
209
- # Aplicar typecast para outros tipos de dados
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
- Linha_convertida.append(valor)
214
- Valores.append(tuple(Linha_convertida))
215
+ Retorno = None
215
216
 
216
- Query = f'insert into {Schema}.{Tabela} ({', '.join(Colunas)}) values %s '
217
- Query = Query + OnConflict if OnConflict else Query
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
- execute_values(self.Cursor, Query, Valores)
222
- self.Conexao.commit()
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
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "lbxtoolkit"
3
- version = "2.2.10"
3
+ version = "2.2.11"
4
4
  description = ""
5
5
  authors = ["Cristiano P. Ferrari <cristiano.ferrari@lbx.com.br>"]
6
6
  readme = "README.md"
File without changes