📊 Explicação Detalhada - Maestro Dashboard: Gerando os gráficos e exibindo-os na página web com o Streamlit:
Após realizarmos a autenticação na API e construir os headers, podemos seguir com a estruturação dos gráficos e montagem da página web exibindo os gráficos. A seguir, veja como manipularemos o endpoint de listagem de erros e alertas, as manipulações nas respostas das chamadas aos endpoints e estruturação da página web com os gráficos formatados.
📌 Resumo do Fluxo Completo
┌─────────────────────────────────────┐
│ Usuário preenche credenciais │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Clica em "Conectar e Atualizar" │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Função login() autentica usuário │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Obtém Token + Organização │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ main() busca dados via fetch_data()│
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Renderiza gráficos e tabelas │
└─────────────────────────────────────┘
1️. Configuração Inicial da Página
Abaixo, veja o snippet de código para inicializar a página com o layout exibindo os gráficos:
st.set_page_config(page_title="Maestro Dashboard", page_icon="📊", layout="wide")
O código acima define as configurações básicas da página Streamlit:
- page_title: Título que aparece na aba do navegador
- page_icon: Ícone exibido na aba
- layout: Layout em modo "wide" para aproveitar toda a largura disponível
2️. Função de Login
def login(server, login, key):
"""Realiza o login no Maestro e retorna o token e organização."""
login_url = f"{server}/api/v2/workspace/login"
body = {"login": login, "key": key}
try:
response = requests.post(url=login_url, json=body, timeout=10)
response.raise_for_status()
content = response.json()
return content['accessToken'], content['organizationLabel']
except Exception as e:
st.error(f"Erro na autenticação: {e}")
return None, None
O que faz:
- Constrói a URL do endpoint de login do Maestro
- Envia uma requisição POST com as credenciais (login e chave de API)
- Se bem-sucedido, extrai e retorna o
accessTokeneorganizationLabel - Se houver erro, exibe uma mensagem de erro e retorna
None, None
Fluxo:
Credenciais do usuário → POST para API → Recebe Token + Organização
3️. Função de Busca de Dados
def fetch_data(server, headers, endpoint):
"""Busca dados de um endpoint específico do Maestro."""
url = f"{server}/api/v2/{endpoint}"
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
return response.json().get('content', [])
except Exception as e:
st.warning(f"Erro ao buscar {endpoint}: {e}")
return []
O que faz:
- Monta a URL do endpoint solicitado
- Faz uma requisição GET passando o token de autenticação nos headers
- Extrai o conteúdo da resposta JSON
- Em caso de erro, exibe um aviso e retorna uma lista vazia
Exemplos de uso:
fetch_data(server_input, headers, "alerts") # Busca alertas
fetch_data(server_input, headers, "error") # Busca erros
4️. Busca de Dados no Dashboard
# Buscando dados
alerts_content = fetch_data(server_input, headers, "alerts")
errors_content = fetch_data(server_input, headers, "error")
Aqui o código realiza duas requisições simultâneas:
- alerts_content: Lista com todos os alertas
- errors_content: Lista com todos os erros
5️. Exibição de Métricas
# Contagem de registros de erros e alertas
cont_erros, cont_alertas = st.columns(2)
cont_erros.metric("Total de Alertas", len(alerts_content))
cont_alertas.metric("Total de Erros", len(errors_content))
O que faz:
- Cria 2 colunas lado a lado
- Exibe o número total de alertas e erros usando cards de métrica
- Usa
len()para contar o total de registros
Resultado visual:
┌──────────────────┬──────────────────┐
│ Total Alertas │ Total Erros │
│ 42 │ 18 │
└──────────────────┴──────────────────┘
6️. Processamento de Alertas
# --- Processamento de Alertas ---
alert_counts = Counter(item['botId'] for item in alerts_content)
bots_alerts = list(alert_counts.keys())
alert_values = list(alert_counts.values())
O que faz:
- Usa
Counterpara contar quantos alertas cada bot gerou - Extrai os IDs dos bots em uma lista
- Extrai as contagens em outra lista
Exemplo:
# Se alert_counts = {'bot_001': 5, 'bot_002': 3, 'bot_003': 7}
bots_alerts = ['bot_001', 'bot_002', 'bot_003']
alert_values = [5, 3, 7]
7️. Processamento de Erros
# --- Processamento de Erros ---
error_counts = Counter(item['botId'] for item in errors_content)
bots_errors = list(error_counts.keys())
error_values = list(error_counts.values())
error_categories = Counter(item.get('type', 'N/A') for item in errors_content)
O que faz:
- Conta erros por bot (similar aos alertas)
- Também conta quantos erros de cada tipo (categoria) ocorreram
- Usa
.get('type', 'N/A')para evitar erros se a chave não existir
8️. Gráfico de Barras - Alertas
with conteiner_alertas:
st.subheader("⚠️ Alertas por Automação")
fig_alerts = go.Figure(data=[go.Bar(x=bots_alerts, y=alert_values, marker_color='mediumturquoise')])
fig_alerts.update_layout(template='plotly_dark', margin=dict(l=20, r=20, t=40, b=20))
st.plotly_chart(fig_alerts, use_container_width=True)
O que faz:
- Cria um gráfico de barras com Plotly
- Eixo X: IDs dos bots
- Eixo Y: Quantidade de alertas
- Cor: Turquesa
- Tema: Dark
use_container_width=True: Ocupa toda a largura disponível
Resultado: Gráfico visual mostrando qual bot tem mais alertas
9️. Gráfico de Pizza - Categorias de Erros
with conteiner_categorias:
st.subheader("📉 Categorias de Erros")
if error_categories:
pie_chart = go.Figure(data=[go.Pie(
labels=list(error_categories.keys()),
values=list(error_categories.values()),
hole=.3
)])
pie_chart.update_traces(hoverinfo='label+percent', textinfo='value', marker=dict(line=dict(color='#000000', width=1)))
st.plotly_chart(pie_chart, use_container_width=True)
else:
st.info("Sem dados de categorias para exibir.")
O que faz:
- Verifica se há dados de erros
- Cria um gráfico de pizza (donut) com
hole=.3 - Labels: tipos de erro
- Values: quantidade de cada tipo
- Exibe percentuais e contagens no hover
10 Tabela de Últimos Erros
with conteiners_lista:
st.subheader("📋 Últimos Erros Detectados")
if errors_content:
# Mostra os últimos 5 erros em formato de tabela
st.table([{"Bot ID": e['botId'], "Tipo": e['type'], "Data": e.get('dateCreation', 'N/A')} for e in errors_content[:5]])
else:
st.write("Nenhum erro recente.")
O que faz:
- Extrai os 5 primeiros erros (
[:5]) - Cria uma tabela com 3 colunas: Bot ID, Tipo e Data
- Usa
e.get('dateCreation', 'N/A')para retornar "N/A" se a data não existir
Resultado:
┌──────────┬─────────────┬────────────────────────┐
│ Bot ID │ Tipo │ Data │
├──────────┼─────────────┼────────────────────────┤
│ bot_001 │ Connection │ 2024-01-14 10:30:00 │
│ bot_002 │ Timeout │ 2024-01-14 09:15:00 │
│ bot_003 │ Auth Error │ 2024-01-14 08:45:00 │
└──────────┴─────────────┴────────────────────────┘
Sidebar e Interface de Entrada
st.sidebar.title("🔑 Configurações")
server_input = st.sidebar.text_input("Server URL", value="https://developers.botcity.dev")
login_input = st.sidebar.text_input("Login")
key_input = st.sidebar.text_input("API Key", type="password")
btn_conectar = st.sidebar.button("Conectar e Atualizar")
O que faz:
- Cria uma barra lateral (sidebar) com título
- Campo de entrada para URL do servidor (com valor padrão)
- Campo para login
- Campo para API Key (mascarado com asteriscos)
- Botão para conectar
Lógica Principal de Execução
Chegou o momento de implementarmos a parte que irá fazer a mágica acontecer: colocaremos, dentro de passos lógicos, a chamada para autenticação na API e renderização nos gráficos. Abaixo, temos o código e explicação do que está sendo feito na chamada do código:
if btn_conectar:
if not login_input or not key_input:
st.sidebar.error("Por favor, preencha as credenciais.")
else:
with st.spinner("Conectando ao Maestro..."):
token, org = login(server_input, login_input, key_input)
if token and org:
main(token, org)
else:
st.error("Falha ao obter Token. Verifique suas credenciais e a URL do servidor.")
else:
st.info("Insira suas credenciais na barra lateral e clique em 'Conectar' para carregar os gráficos.")
Fluxo:
- Usuário clica no botão "Conectar"
- Verifica se login e chave foram preenchidos
- Se sim, exibe spinner e tenta fazer login
- Se bem-sucedido, chama
main()para carregar todos os gráficos - Se falhar, exibe mensagem de erro
- Se usuário não clicou no botão, exibe mensagem informativa
Código-fonte completo:
Veja o código-fonte completo:
import streamlit as st
import requests
import plotly.graph_objects as go
from collections import Counter
# Configurações da página Streamlit
st.set_page_config(page_title="Maestro Dashboard", page_icon="📊", layout="wide")
def login(server, login, key):
"""Realiza o login no Maestro e retorna o token e organização."""
login_url = f"{server}/api/v2/workspace/login"
body = {"login": login, "key": key}
try:
response = requests.post(url=login_url, json=body, timeout=10)
response.raise_for_status()
content = response.json()
return content['accessToken'], content['organizationLabel']
except Exception as e:
st.error(f"Erro na autenticação: {e}")
return None, None
def fetch_data(server, headers, endpoint):
"""Busca dados de um endpoint específico do Maestro."""
url = f"{server}/api/v2/{endpoint}"
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
return response.json().get('content', [])
except Exception as e:
st.warning(f"Erro ao buscar {endpoint}: {e}")
return []
def main(token, org):
headers = {'token': token, 'organization': org}
# Buscando dados
alerts_content = fetch_data(server_input, headers, "alerts")
errors_content = fetch_data(server_input, headers, "error")
# Contagem de registros de erros e alertas
cont_erros, cont_alertas = st.columns(2)
cont_erros.metric("Total de Alertas", len(alerts_content))
cont_alertas.metric("Total de Erros", len(errors_content))
# --- Processamento de Alertas ---
alert_counts = Counter(item['botId'] for item in alerts_content)
bots_alerts = list(alert_counts.keys())
alert_values = list(alert_counts.values())
# --- Processamento de Erros ---
error_counts = Counter(item['botId'] for item in errors_content)
bots_errors = list(error_counts.keys())
error_values = list(error_counts.values())
error_categories = Counter(item.get('type', 'N/A') for item in errors_content)
# --- Layout de Gráficos ---
st.divider()
conteiner_alertas, conteiners_erros = st.columns(2)
with conteiner_alertas:
st.subheader("⚠️ Alertas por Automação")
fig_alerts = go.Figure(data=[go.Bar(x=bots_alerts, y=alert_values, marker_color='mediumturquoise')])
fig_alerts.update_layout(template='plotly_dark', margin=dict(l=20, r=20, t=40, b=20))
st.plotly_chart(fig_alerts, use_container_width=True)
with conteiners_erros:
st.subheader("❌ Erros por Automação")
fig_errors = go.Figure(data=[go.Bar(x=bots_errors, y=error_values, marker_color='#EF553B')])
fig_errors.update_layout(template='plotly_white', margin=dict(l=20, r=20, t=40, b=20))
st.plotly_chart(fig_errors, use_container_width=True)
st.divider()
conteiner_categorias, conteiners_lista = st.columns([1, 2])
with conteiner_categorias:
st.subheader("📉 Categorias de Erros")
if error_categories:
pie_chart = go.Figure(data=[go.Pie(
labels=list(error_categories.keys()),
values=list(error_categories.values()),
hole=.3
)])
pie_chart.update_traces(hoverinfo='label+percent', textinfo='value', marker=dict(line=dict(color='#000000', width=1)))
st.plotly_chart(pie_chart, use_container_width=True)
else:
st.info("Sem dados de categorias para exibir.")
with conteiners_lista:
st.subheader("📋 Últimos Erros Detectados")
if errors_content:
# Mostra os últimos 5 erros em formato de tabela
st.table([{"Bot ID": e['botId'], "Tipo": e['type'], "Data": e.get('dateCreation', 'N/A')} for e in errors_content[:5]])
else:
st.write("Nenhum erro recente.")
# --- Interface - Sidebar ---
st.sidebar.title("🔑 Configurações")
server_input = st.sidebar.text_input("Server URL", value="https://developers.botcity.dev")
login_input = st.sidebar.text_input("Login")
key_input = st.sidebar.text_input("API Key", type="password")
btn_conectar = st.sidebar.button("Conectar e Atualizar")
st.title("📊 Monitoramento de Automações")
st.markdown("Visualização de Alertas e Erros capturados via API do Maestro.")
if btn_conectar:
if not login_input or not key_input:
st.sidebar.error("Por favor, preencha as credenciais.")
else:
with st.spinner("Conectando ao Maestro..."):
token, org = login(server_input, login_input, key_input)
if token and org:
main(token,org)
else:
st.error("Falha ao obter Token. Verifique suas credenciais e a URL do servidor.")
else:
st.info("Insira suas credenciais na barra lateral e clique em 'Conectar' para carregar os gráficos.")
Executando o projeto para renderizar os gráficos:
Com o código implementado, podemos executar o projeto para gerar uma página web no servidor local, realizar a autenticação na API e exibir os gráficos. Logo, podemos executar o seguinte código no terminal:
streamlit run main.py
Ao chamar o comando acima, mensagens no seu terminal deve aparecer, indicando o endereço que essa página web está localizada, como mostrado no print abaixo:
Faça um CTRL + Click com o cursor do mouse no link de Local URL, e veja a página web renderizada no seu navegador, como indicado na imagem abaixo:
Por fim, preencha os dados de Login e Key, clique no botão Conectar e Atualizar, e veja os gráficos renderizados!
Documentações e materiais de apoio:
Saiba mais
A BotCity oferece diversos endpoints de API para controlar e monitorar suas automações.
-
Saiba mais sobre a API BotCity clicando aqui
-
Veja a documentação do Streamlit clicando aqui

