Skip to content

Commit 50ddc23

Browse files
Glauber Varjãoclaude
andcommitted
fix: auditoria completa — 15 correções API ISAPI + 3 crashes + threading + otimização
API ISAPI (isapi_client.py): - faceLibType: blackFD→staticFD em TODOS os endpoints (era blocklist!) - FDSearch: FaceLibType→faceLibType (casing), startNumber (era searchResultPosition) - get_face: busca por faceCustomCondition/byEmployeeNo conforme spec - delete_faces batch: POST→PUT + body FDSearchCond conforme spec - delete_all_users inner: POST→PUT + body FDSearchCond - modify_user: /Modify→/SetUp (endpoint correto) - modify_card: /Modify→/SetUp (endpoint correto) - add_face: campos multipart faceURL+img conforme doc API - Download de fotos: cooldown adaptativo (0.3s-3s), fallback FDSearch multipart Backup (backup.py): - restore_backup: checa device_info.get("ok") corretamente - list_backups: busca serial completo E truncado App crashes (app.py): - NameError src_label/dst_label no clone facial — agora derivados de src_td/dst_td - ScreenCadastros: adicionado log_text via create_log_area - ScreenCadastros: _get_selected_terminal→find_terminal_by_display - log_activity/set_status: thread-safe via self.after(0, ...) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9521d76 commit 50ddc23

5 files changed

Lines changed: 117 additions & 134 deletions

File tree

app.py

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -551,38 +551,44 @@ def clear_log(self):
551551
self.log_count_var.set("")
552552

553553
def log_activity(self, msg: str, level: str = "info"):
554-
"""Adiciona entrada ao log de atividades.
555-
level: info, success, warning, error
556-
"""
557-
ts = datetime.now().strftime('%H:%M:%S')
558-
prefix = {"info": "ℹ", "success": "✔", "warning": "⚠", "error": "✖"}.get(level, "·")
559-
line = f"[{ts}] {prefix} {msg}\n"
560-
self.log_textbox.configure(state="normal")
561-
self.log_textbox.insert("end", line)
562-
self.log_textbox.configure(state="disabled")
563-
self.log_textbox.see("end")
564-
self._log_count += 1
565-
self.log_count_var.set(f"{self._log_count} entradas")
566-
# Se tiver erro e log estiver fechado, pisca o botão
567-
if level == "error" and not self.log_visible:
568-
self.log_toggle_btn.configure(text_color=RED)
554+
"""Adiciona entrada ao log de atividades. Thread-safe via self.after()."""
555+
def _do():
556+
ts = datetime.now().strftime('%H:%M:%S')
557+
prefix = {"info": "ℹ", "success": "✔", "warning": "⚠", "error": "✖"}.get(level, "·")
558+
line = f"[{ts}] {prefix} {msg}\n"
559+
self.log_textbox.configure(state="normal")
560+
self.log_textbox.insert("end", line)
561+
self.log_textbox.configure(state="disabled")
562+
self.log_textbox.see("end")
563+
self._log_count += 1
564+
self.log_count_var.set(f"{self._log_count} entradas")
565+
if level == "error" and not self.log_visible:
566+
self.log_toggle_btn.configure(text_color=RED)
567+
try:
568+
self.after(0, _do)
569+
except Exception:
570+
pass # Widget destruído, ignorar
569571

570572
def set_status(self, msg: str, color=None):
571-
ts = datetime.now().strftime('%H:%M:%S')
572-
self.status_var.set(f" {ts} | {msg}")
573-
if hasattr(self, 'status_label_widget') and color:
574-
self.status_label_widget.configure(text_color=color)
575-
# Também envia pro log de atividades
576-
level = "info"
577-
if color == GREEN:
578-
level = "success"
579-
elif color == ORANGE:
580-
level = "warning"
581-
elif color == RED:
582-
level = "error"
583-
if hasattr(self, 'log_textbox'):
584-
self.log_activity(msg, level)
585-
self.update_idletasks()
573+
"""Atualiza barra de status. Thread-safe via self.after()."""
574+
def _do():
575+
ts = datetime.now().strftime('%H:%M:%S')
576+
self.status_var.set(f" {ts} | {msg}")
577+
if hasattr(self, 'status_label_widget') and color:
578+
self.status_label_widget.configure(text_color=color)
579+
level = "info"
580+
if color == GREEN:
581+
level = "success"
582+
elif color == ORANGE:
583+
level = "warning"
584+
elif color == RED:
585+
level = "error"
586+
if hasattr(self, 'log_textbox'):
587+
self.log_activity(msg, level)
588+
try:
589+
self.after(0, _do)
590+
except Exception:
591+
pass
586592

587593
def show_error(self, msg): messagebox.showerror("Erro", msg)
588594
def show_info(self, msg): messagebox.showinfo("Informacao", msg)
@@ -3734,6 +3740,9 @@ def setup_ui(self):
37343740
self.lbl_issues = ctk.CTkLabel(self.count_frame, text="", font=FONT_SMALL, text_color=RED)
37353741
self.lbl_issues.pack(side="left", padx=8)
37363742

3743+
# Log de atividades (necessário para self.log(self.log_text, msg))
3744+
self.log_text = self.create_log_area(main)
3745+
37373746
def clear_screen(self):
37383747
"""Limpa toda a tela de cadastros — tree, foto, bio, detalhes, contadores."""
37393748
# Limpar dados internos
@@ -5466,6 +5475,8 @@ def _execute_clone_facial(self, src_td, dst_td, src_ip, dst_ip, session=None):
54665475
ctk.CTkLabel(monitor, text=f"Clonagem Facial",
54675476
font=("Segoe UI", 18, "bold"), text_color=TEXT_PRIMARY
54685477
).pack(pady=(10, 1))
5478+
src_label = src_td.get("name", "") or src_ip
5479+
dst_label = dst_td.get("name", "") or dst_ip
54695480
ctk.CTkLabel(monitor, text=f"{src_label} → {dst_label}",
54705481
font=FONT_BODY, text_color=TEXT_SECONDARY).pack(pady=(0, 6))
54715482

@@ -6213,8 +6224,9 @@ def _suspend_clone(self, monitor_win, btn):
62136224
# === APAGAR FACES DO TERMINAL ===
62146225
def delete_faces_dialog(self):
62156226
"""Abre diálogo para selecionar e apagar faces do terminal."""
6216-
td = self._get_selected_terminal()
6227+
td = self.app.find_terminal_by_display(self.term_om.get())
62176228
if not td:
6229+
self.app.show_warning("Selecione um terminal.")
62186230
return
62196231
if not self.users_human:
62206232
self.app.show_warning("Carregue as pessoas primeiro (botão 'Carregar Pessoas').")

core/backup.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -377,18 +377,13 @@ def log(msg, level="info"):
377377
# === 2. Verificar serial (spec item 9: só se serial idêntico) ===
378378
log("Restore: Verificando serial do dispositivo...")
379379
device_info = target_client.get_device_info()
380-
if not device_info:
381-
report["error"] = "Falha ao obter info do dispositivo destino"
380+
if not device_info or not device_info.get("ok"):
381+
err = device_info.get("error", "sem resposta") if isinstance(device_info, dict) else "sem resposta"
382+
report["error"] = f"Falha ao obter info do dispositivo destino: {err}"
382383
log(report["error"], "error")
383384
return report
384385

385-
# Extrair serial (compatível com diferentes formatos de retorno)
386-
if isinstance(device_info, dict):
387-
target_serial = (device_info.get("serialNumber") or
388-
device_info.get("serial") or
389-
device_info.get("data", {}).get("serial", ""))
390-
else:
391-
target_serial = ""
386+
target_serial = device_info.get("data", {}).get("serial", "")
392387
backup_serial = manifest.get("device_serial", "")
393388

394389
if target_serial != backup_serial:
@@ -506,10 +501,12 @@ def list_backups(self, serial: str = None) -> List[Dict[str, Any]]:
506501
backups = []
507502
try:
508503
if serial:
509-
# Buscar em diretório específico
510-
serial_dir = os.path.join(self.backup_dir, serial)
511-
if os.path.isdir(serial_dir):
512-
self._scan_dir(serial_dir, backups)
504+
# Buscar em diretório específico (tenta serial completo e truncado)
505+
serial_short = serial[-12:] if len(serial) > 12 else serial
506+
for s in dict.fromkeys([serial, serial_short]): # sem duplicatas
507+
serial_dir = os.path.join(self.backup_dir, s)
508+
if os.path.isdir(serial_dir):
509+
self._scan_dir(serial_dir, backups)
513510
else:
514511
# Buscar em todos os diretórios
515512
if os.path.isdir(self.backup_dir):

0 commit comments

Comments
 (0)