Hermes Insights — Updates 15/jul/2026
Sessão de Limpeza e Correções — 15/jul/2026
Arquivos Órfãos Removidos
- - 46 arquivos órfãos movidos para
/var/backups/hermes-insights/orphans_20260715/ - - 14 pastas de backup antigas movidas para
/var/backups/hermes-insights/old_backups_20260715/ - - Arquivos: relatórios HTML sem correspondente, favicon.svg duplicado, CSS/JS órfãos
- - Causa: O endpoint
/api/response_timenão existia noapi_server.py(porta 5001). A página chamava/api/response_time→ nginx → porta 5001, mas a rota não estava no proxy da API principal. - - Fix: Adicionada rota proxy em
api_server.py: - - Also fixed:
ssl.CERT_NONEem vez dessl.NONE(Python 3.10+ compatibility) - - Arquivo:
/var/www/hermes-insights/api_server.py - - Causa: CSS global
upbar.csstinha.filters { backdrop-filter: blur(12px) saturate(1.5) }— criava camada de vidro entre a barra de filtro e os cards, fazendo tudo parecer embaçado quando o dropdown abria. - - Fix: Override no
da página + cache-bust no CSS: - - Causa 1:
closeInboxOnClickOutsidenodocumentfechava dropdown em TODO clique, incluindo nos checkboxes (event bubbling). - - Causa 2:
handleInboxChange()tinha lógica invertida — verificavaallCheckbox.checkedno INÍCIO da função, desmarcar todos individuais quando "Todos" estava marcado, ignorando a seleção do usuário. - - Fix 1:
onclick="event.stopPropagation()"nos.multiselect-itemDIVs E nos INPUTs. - - Fix 2:
handleInboxChange()reescrito comthis.idpara detectar QUAL checkbox foi clicado: - - Cada nome de cliente agora é link para conversa no Chatwoot:
- -
— área inteira clicável, não só emoji - -
conversation_idadicionado ao SQL dochatwoot_waiting_api.py(aliasc.id AS conversation_id) - - Causa:
kpi-vendorsekpi-top-vendusavam arrayVENDOREShardcoded em vez dewindow.vendorLabels(lista filtrada). - - Fix:
- - Adicionada explicação: "(tempo entre cliente enviar msg e corretor responder pela 1ª vez)"
- - Causa:
populateStep3()emrelatorios.htmlusavainnerHTML +=para adicionar texto a umdentro de um bloco. Browser interpretava o texto como HTML e fechava/reabria a tag script, criandofantasma no DOM. - - Fix:
innerHTML +=→textContent +=no span desuccess-path - - Arquivo:
relatorios.html - -
api_server.py(5001):cd /var/www/hermes-insights && nohup python3 api_server.py 5001 >> data/api_server.log 2>&1 & - -
chatwoot_waiting_api.py(9087):nohup python3 /usr/local/bin/chatwoot_waiting_api.py > /var/log/chatwoot_waiting_api.log 2>&1 & - - Script:
python3 -u score_engine.pyem/root/hermes-insights/ - - Resultado: 3801/3801 conversas processadas, 0 falhas
- - Tabela:
insights.scores— scores de tempo, tom e resolução (0-100) - - Processo: rodando via
setsidem background (PID 3507733) - - Arquivo:
/tmp/generate_commercial_report.lock - - Script corrigido:
generate_commercial_report.py— lock no início da funçãorun() - - Lock: impede 2+ processos competindo pela mesma API key
- -
/usr/local/bin/hermes-sync.py— sync Chatwoot → Supabase (a cada 5 min) - -
/usr/local/bin/hermes-score-engine.py— score engine (cron no crontab root) - -
/usr/local/bin/hermes-flag-engine.py— flag engine (nãousa Gemini) - - Placeholder no código:
GEMINI_API_KEY="AQ.Ab8..._Gvg"(máscarado) - - 54 commercial_reports já existem no banco — logo key funciona
- - Possível: key injetada via env no contexto do cron wrapper
- - Teste: API Gemini responde 400 (pode ser placeholder ou key real)
- - Problema: gráfico "Real vs Faltante" mostra tudo no máximo (100%)
- - Causa: escala do gráfico provavelmente está normalizandoerradamente
- - Arquivo:
dashboard_comercial.html - - Status: Pendente correção — ver investigação
- - Script:
generate_commercial_report.py(corrigido com lock + backoff) - - 3293 conversas pendentes de análise Gemini
- - Lock:
/tmp/generate_commercial_report.lock - - Log:
/tmp/cr_fixed.log - - Status: rodando em background
- - Todos os 22 arquivos HTML com upbar (barra horizontal no topo)
- - CSS:
sidebar_v2.css?v=upbar5 - - 7 links de navegação consolidada
- - Scroll bug corrigido (overflow:hidden removido)
Bug: tempo_resposta.html mostrando ... em tudo
`python
if path == "/api/response_time":
# forward → http://127.0.0.1:9087/response_time
`
Bug: Multi-select dropdown (clientes_espera.html) — sobreposição/embaçado
`css
.filters {
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
position: relative;
z-index: 1;
}
/ upbar.css?v=202607141611 → ?v=202607151530 /
`
Bug: Multi-select checkboxes não selecionáveis (clientes_espera.html)
`javascript
if (this.id === 'inboxAll') {
// "Todos" checked → marca todos individuais
// "Todos" unchecked → desmarca todos individuais
} else {
// Individual: se nenhum selecionado → marca "Todos" como fallback
// Se alguns selecionados → desmarca "Todos"
}
`
Feature: Links clicáveis para Chatwoot (clientes_espera.html)
https://chat.rochasalesseguros.com.br/app/accounts/1/conversations/{id}
Bug: KPIs do Dashboard não respeitavam filtro de vendedor
`javascript
document.getElementById('kpi-vendors').textContent = window.vendorLabels ? window.vendorLabels.length : VENDORES.length;
document.getElementById('kpi-top-vend').textContent = window.vendorLabels && window.vendorLabels.length > 0
? window.vendorLabels[0].substring(0, 12) : (VENDORES[0] || '—').substring(0, 12);
`
Feature: Tooltip no tempo de resposta (generate_commercial_report.py)
Bug: fantasma em relatórios (causa do bug dos KPIs em ...)
APIs Reiniciadas
Status Final do Sistema
| Página | Status |
| -------- | -------- |
| dashboard_comercial.html | ✅ OK — KPIs respeitam filtro |
| fechados_por_vendedor.html | ✅ OK |
| tempo_resposta.html | ✅ OK — proxy API adicionado |
| clientes_espera.html | ✅ OK — multi-select + links Chatwoot |
| relatorios.html | ✅ OK — sem fantasma |
| sobre.html | ✅ OK |
Hermes Insights — Updates 30/jun/2026
Score Engine — Executado 30/jun/2026
Lock File — Evitar processos duplicados
Correções no generate_commercial_report.py
Backoff exponencial (corrigido)
`python
ANTES: wait = (attempt + 1) * 5 → 5s, 10s, 15s
AGORA: wait = (2 * attempt) 30 → 30s, 60s, 120s, 240s, 480s
if e.code == 429:
wait = (2 * attempt) 30
time.sleep(wait)
`
Retry mais tolerante
`python
ANTES: 3 tentativas
AGORA: 6 tentativas (mais tolerante com rate limit)
for attempt in range(6):
`
Timeout maior
`python
ANTES: timeout=90
AGORA: timeout=120
with urllib.request.urlopen(req, timeout=120)
`
Lock file (anti-duplicidade)
`python
import os
lock = "/tmp/generate_commercial_report.lock"
if os.path.exists(lock):
pid = open(lock).read().strip()
try:
os.kill(int(pid), 0)
print(f"[LOCK] Processo {pid} já rodando. Saindo.")
return
except: pass
open(lock, "w").write(str(os.getpid()))
`