import streamlit as st import mysql.connector import pandas as pd import bcrypt import re import pytz from datetime import datetime # --- CONFIGURAÇÕES DA PÁGINA --- st.set_page_config(page_title="Sistema de Consulta de Alunos", layout="wide") st.markdown(""" """, unsafe_allow_html=True) # --- CONEXÃO COM O BANCO --- def get_connection(): return mysql.connector.connect( user=st.secrets["DB_USER"], password=st.secrets["DB_PASS"], host=st.secrets["DB_HOST"], port=st.secrets["DB_PORT"], database=st.secrets["DB_NAME"], ssl_ca="ca.pem", use_pure=True ) # --- FUNÇÕES DE SEGURANÇA --- def verificar_senha(senha_plana, senha_hash): return bcrypt.checkpw(senha_plana.encode('utf-8'), senha_hash.encode('utf-8')) def gerar_hash(senha): return bcrypt.hashpw(senha.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') # --- FUNÇÕES DA APLICAÇÃO --- def obter_hora_brasilia(): # Define o fuso horário de Brasília explicitamente fuso_br = pytz.timezone('America/Sao_Paulo') # Pega a hora atual nesse fuso return datetime.now(fuso_br) # --- REMOVER BARRA SUPERIOR E MENU DO STREAMLIT --- hide_st_style = """ """ st.markdown(hide_st_style, unsafe_allow_html=True) # --- ESTADO DA SESSÃO --- if 'logado' not in st.session_state: st.session_state.logado = False st.session_state.perfil = None st.session_state.nome_usuario = "" # --- TELA DE LOGIN --- if not st.session_state.logado: st.title("🔐 Acesso ao Sistema") with st.form("login_form"): email = st.text_input("E-mail") senha = st.text_input("Senha", type="password") btn_login = st.form_submit_button("Entrar") if btn_login: try: conn = get_connection() cursor = conn.cursor(dictionary=True) cursor.execute("SELECT id, nome, senha_hash, perfil, ativo FROM app_usuarios WHERE email = %s", (email,)) user = cursor.fetchone() conn.close() if user and user['ativo'] and verificar_senha(senha, user['senha_hash']): st.session_state.logado = True st.session_state.perfil = user['perfil'] st.session_state.nome_usuario = user['nome'] # --- REGISTRO DE LOG DE ACESSO --- try: # 1. Calcula a hora de Brasília usando a lógica do pytz agora_br = obter_hora_brasilia() data_acesso_str = agora_br.strftime('%Y-%m-%d %H:%M:%S') conn_log = get_connection() cursor_log = conn_log.cursor() # 2. Insere explicitamente a data calculada sql_log = """ INSERT INTO app_logs_acesso (usuario_id, nome_usuario, email, data_acesso) VALUES (%s, %s, %s, %s) """ # Passamos o user['id'] que agora buscamos no SELECT acima valores_log = (user['id'], user['nome'], email, data_acesso_str) cursor_log.execute(sql_log, valores_log) conn_log.commit() conn_log.close() except Exception as e: print(f"Erro ao salvar log de acesso: {e}") # Verifique isso nos Logs do Hugging Face st.rerun() else: st.error("Credenciais inválidas ou conta desativada.") except Exception as e: st.error(f"Erro de conexão: {e}") # --- ÁREA LOGADA --- else: # Sidebar - Perfil e Navegação st.sidebar.title(f"👤 {st.session_state.nome_usuario}") # Normalizamos o perfil para evitar erros de Digitação/Maiúsculas perfil_usuario = str(st.session_state.perfil).lower().strip() st.sidebar.write(f"Perfil: **{perfil_usuario.upper()}**") st.sidebar.markdown("---") # OPÇÕES PARA TODOS OS USUÁRIOS (Consulta e Admin) opcoes_menu = ["Consultar Histórico", "Relatório de Aprovação", "Relatórios de Retenção", "Alunos sem Acesso", "Equipe de Apoio"] # OPÇÃO EXCLUSIVA PARA ADMIN if perfil_usuario == 'admin': opcoes_menu.append("Gerenciar Usuários") opcoes_menu.append("Logs do Sistema") menu = st.sidebar.radio("Navegação", opcoes_menu) st.sidebar.markdown("---") if st.sidebar.button("Sair"): st.session_state.logado = False st.rerun() ####################################################### # --- MÓDULO 1: CONSULTA DE HISTÓRICO --- ####################################################### if menu == "Consultar Histórico": st.title("🔍 Consulta de Histórico de Alunos") busca = st.text_input("Pesquisar por Nome, Sobrenome ou E-mail") if busca: try: conn = get_connection() termo = f"%{busca}%" query = """ SELECT nome, sobrenome, email, ultimo_acesso, cidade, pais, celular, nome_curso, semestre_referencia, papel, turmas, data_inicial FROM portal_alunos_flat WHERE nome LIKE %s OR sobrenome LIKE %s OR email LIKE %s ORDER BY nome ASC, semestre_referencia ASC, nome_curso ASC """ df = pd.read_sql(query, conn, params=(termo, termo, termo)) conn.close() if not df.empty: colunas_aluno = ['nome', 'sobrenome', 'email', 'ultimo_acesso', 'cidade', 'pais', 'celular'] alunos = df[colunas_aluno].drop_duplicates() for _, aluno in alunos.iterrows(): label_expander = f"👤 {aluno['nome']} {aluno['sobrenome']} ({aluno['email']})" # --- TRATAMENTO DE CAMPOS VAZIOS --- celular_valor = str(aluno['celular']).strip() if aluno['celular'] and str(aluno['celular']).lower() != 'nan' else "" celular_display = celular_valor if celular_valor else "Não cadastrado" ultimo_acesso = str(aluno['ultimo_acesso']) if aluno['ultimo_acesso'] else "Nunca" localizacao = f"{aluno['cidade']} / {aluno['pais']}" if aluno['cidade'] else "Não informada" with st.expander(label_expander): col1, col2, col3, col4 = st.columns(4) col1.metric(label="📅 Último Logon", value=ultimo_acesso) col2.metric(label="📍 Localização", value=localizacao) col3.metric(label="📱 Celular", value=celular_display) # Opcional: Link para WhatsApp apenas se houver celular if celular_valor: f_tel = "".join(filter(str.isdigit, celular_valor)) if f_tel: col4.markdown(f"[💬 WhatsApp](https://wa.me/{f_tel})") else: col4.write("🚫 Número inválido") else: col4.write("━") st.write("---") hist = df[df['email'] == aluno['email']][['semestre_referencia', 'nome_curso', 'papel', 'turmas', 'data_inicial']] hist = hist.sort_values(by=['semestre_referencia', 'nome_curso']) hist.columns = ['Semestre', 'Curso', 'Papel', 'Turmas', 'Início'] st.dataframe(hist, use_container_width=True, hide_index=True) else: st.warning("Nenhum aluno encontrado.") except Exception as e: st.error(f"Erro ao consultar: {e}") ####################################################### # --- MÓDULO 2: RELATÓRIO DE APROVAÇÃO --- ####################################################### elif menu == "Relatório de Aprovação": st.title("🎓 Relatório de Aprovação") st.write("Consulta de progresso e aprovação dos alunos por ciclo letivo.") try: conn = get_connection() # --- FILTRO 1: SEMESTRE DE REFERÊNCIA --- query_semestres = """ SELECT DISTINCT semestre_referencia FROM portal_alunos_flat WHERE semestre_referencia IS NOT NULL AND semestre_referencia != '' ORDER BY semestre_referencia DESC """ df_semestres = pd.read_sql(query_semestres, conn) lista_semestres = df_semestres['semestre_referencia'].tolist() col_sem, col_cur, col_tur = st.columns(3) with col_sem: semestre_sel = st.selectbox("Selecione o Semestre:", lista_semestres) # --- FILTRO 2: CURSO (Dependente do Semestre) --- query_cursos = f""" SELECT DISTINCT nome_curso FROM portal_alunos_flat WHERE semestre_referencia = '{semestre_sel}' ORDER BY nome_curso """ df_cursos = pd.read_sql(query_cursos, conn) lista_cursos = ["Todos"] + df_cursos['nome_curso'].tolist() with col_cur: curso_sel = st.selectbox("Filtrar por Curso:", lista_cursos) # --- FILTRO 3: TURMA (Dependente do Curso) --- lista_turmas = ["Todas"] if curso_sel != "Todos": query_turmas = f""" SELECT DISTINCT turmas FROM portal_alunos_flat WHERE semestre_referencia = '{semestre_sel}' AND nome_curso = '{curso_sel}' AND turmas IS NOT NULL AND turmas != '' ORDER BY turmas """ df_turmas = pd.read_sql(query_turmas, conn) lista_turmas = ["Todas"] + sorted(df_turmas['turmas'].unique().tolist()) with col_tur: turma_sel = st.selectbox("Filtrar por Turma:", lista_turmas, disabled=(curso_sel == "Todos")) # --- FILTRO CONTROLE: APENAS APROVADOS --- apenas_aprovados = st.checkbox("Exibir apenas alunos aprovados (Completou Aula 15)") if st.button("Gerar Relatório de Aprovação"): # Construção dinâmica da query baseada nos filtros selecionados condicoes = [f"semestre_referencia = '{semestre_sel}'", "papel = 'Estudante'"] if curso_sel != "Todos": condicoes.append(f"nome_curso = '{curso_sel}'") if turma_sel != "Todas": condicoes.append(f"turmas LIKE '%%{turma_sel}%%'") if apenas_aprovados: condicoes.append("completou_aula_15 = 'Sim'") where_clause = " AND ".join(condicoes) query_relatorio = f""" SELECT nome, sobrenome, email, celular, nome_curso, turmas, completou_aula_15 as aprovado, data_conclusao_aula_15 FROM portal_alunos_flat WHERE {where_clause} ORDER BY nome_curso ASC, turmas ASC, nome ASC """ df_aprovacao = pd.read_sql(query_relatorio, conn) if not df_aprovacao.empty: # Tratar a data "1969-12-31" para uma exibição amigável no relatório df_aprovacao['data_conclusao_aula_15'] = df_aprovacao['data_conclusao_aula_15'].apply( lambda x: x.strftime('%d/%m/%Y') if pd.notnull(x) and str(x) != '1969-12-31' else "Não concluído" ) # Renomear colunas para exibição na tela df_exibicao = df_aprovacao.copy() df_exibicao.columns = ['Nome', 'Sobrenome', 'E-mail', 'Telefone', 'Curso', 'Turma', 'Aprovado (Aula 15)', 'Data de Conclusão'] st.success(f"Foram encontrados {len(df_aprovacao)} registros.") st.dataframe(df_exibicao, use_container_width=True, hide_index=True) # --- EXPORTAÇÃO EXCEL (.XLSX) --- import io output = io.BytesIO() with pd.ExcelWriter(output, engine='openpyxl') as writer: df_exibicao.to_excel(writer, index=False, sheet_name='Relatório de Aprovação') excel_data = output.getvalue() st.download_button( label="📊 Exportar Relatório para Excel (.xlsx)", data=excel_data, file_name=f"relatorio_aprovacao_{semestre_sel}.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ) else: st.warning("Nenhum aluno encontrado para os filtros aplicados.") conn.close() except Exception as e: st.error(f"Erro ao processar relatório de aprovação: {e}") ####################################################### # --- MÓDULO 3: RELATÓRIO DE RETENÇÃO --- ####################################################### elif menu == "Relatórios de Retenção": st.title("📊 Relatório de Retenção: Ciclo IX para X") st.write("Alunos que cursaram Ciclo IX, mas não constam no Ciclo X do semestre .") qtd_semestres = st.number_input("Semestres anteriores (excluindo atual):", 1, 10, 2) hoje = datetime.now() ano, sem_atual = hoje.year, (1 if hoje.month <= 6 else 2) lista_semestres = [] t_ano, t_sem = ano, sem_atual for _ in range(qtd_semestres): if t_sem == 1: t_ano -= 1; t_sem = 2 else: t_sem = 1 lista_semestres.append(f"{t_ano}.{t_sem}") st.info(f"Analisando: {', '.join(lista_semestres)}") if st.button("Gerar Relatório"): try: conn = get_connection() semestres_sql = "', '".join(lista_semestres) query = f""" SELECT nome, sobrenome, email, celular, semestre_referencia FROM portal_alunos_flat WHERE papel = 'Estudante' AND nome_curso LIKE '%%Ciclo IX%%' AND semestre_referencia IN ('{semestres_sql}') AND email NOT IN ( SELECT DISTINCT email FROM portal_alunos_flat WHERE nome_curso LIKE '%%Ciclo X%%' AND papel = 'Estudante' ) ORDER BY semestre_referencia DESC, nome ASC """ df_retidos = pd.read_sql(query, conn) conn.close() if not df_retidos.empty: st.success(f"Encontrados {len(df_retidos)} alunos.") st.dataframe(df_retidos, use_container_width=True, hide_index=True) #csv = df_retidos.to_csv(index=False).encode('utf-8-sig') #st.download_button("📥 Baixar CSV", csv, "retidos.csv", "text/csv") # --- PREPARAÇÃO DOS ARQUIVOS PARA DOWNLOAD --- col_dl1, col_dl2 = st.columns(2) # Opção 1: CSV csv = df_retidos.to_csv(index=False).encode('utf-8-sig') with col_dl1: st.download_button( label="📥 Baixar em CSV", data=csv, file_name=f"retidos.csv", mime="text/csv", use_container_width=True ) # Opção 2: EXCEL (XLSX) import io output = io.BytesIO() with pd.ExcelWriter(output, engine='openpyxl') as writer: df_retidos.to_excel(writer, index=False, sheet_name='Retenção') excel_data = output.getvalue() with col_dl2: st.download_button( label="📊 Baixar em Excel", data=excel_data, file_name=f"retidos.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", use_container_width=True ) else: st.warning("Nenhum aluno encontrado.") except Exception as e: st.error(f"Erro no processamento: {e}") ####################################################### # --- MÓDULO 4: GESTÃO DE USUÁRIOS --- ####################################################### elif menu == "Gerenciar Usuários": st.title("👥 Gestão de Usuários") with st.expander("➕ Novo Usuário"): with st.form("cad_user"): n_nome = st.text_input("Nome") n_email = st.text_input("E-mail") n_senha = st.text_input("Senha", type="password") n_perfil = st.selectbox("Perfil", ["consulta", "admin"]) if st.form_submit_button("Salvar"): try: agora_br = obter_hora_brasilia() data_operacao = agora_br.strftime('%Y-%m-%d %H:%M:%S') conn = get_connection() cursor = conn.cursor() # Inserção do Usuário cursor.execute( "INSERT INTO app_usuarios (nome, email, senha_hash, perfil) VALUES (%s,%s,%s,%s)", (n_nome, n_email, gerar_hash(n_senha), n_perfil) ) # Registro de Log cursor.execute( """ INSERT INTO app_logs_operacoes (nome_executor, operacao, detalhes, data_operacao) VALUES (%s, %s, %s, %s) """, ( st.session_state.nome_usuario, 'CRIACAO_USUARIO', f"Criou o usuário: {n_email} com perfil {n_perfil}", data_operacao ) ) conn.commit() conn.close() st.success(f"Usuário cadastrado com sucesso às {agora_br.strftime('%H:%M:%S')}!") st.rerun() # Atualiza a lista abaixo imediatamente except Exception as e: st.error(f"Erro: {e}") # --- LISTAGEM DE USUÁRIOS COM ÚLTIMO ACESSO E STATUS FORMATADO --- try: conn = get_connection() # Query com JOIN para buscar o último registro de login de cada usuário query_users = """ SELECT u.id, u.nome, u.email, u.perfil, u.ativo, MAX(l.data_acesso) as ultimo_acesso FROM app_usuarios u LEFT JOIN app_logs_acesso l ON u.id = l.usuario_id GROUP BY u.id ORDER BY u.nome ASC """ df_users = pd.read_sql(query_users, conn) conn.close() if not df_users.empty: # 1. Ajuste do Status Ativo (1 -> Sim, 0 -> Não) df_users['ativo'] = df_users['ativo'].map({1: 'Sim', 0: 'Não'}) # 2. Formatação da Data de Último Acesso # Se for Nulo, exibe 'Nunca acessou', senão formata para padrão BR df_users['ultimo_acesso'] = df_users['ultimo_acesso'].apply( lambda x: x.strftime('%d/%m/%Y %H:%M') if pd.notnull(x) else "Nunca acessou" ) # Renomear colunas para o cabeçalho da tabela df_users.columns = ['ID', 'Nome', 'E-mail', 'Perfil', 'Ativo', 'Último Acesso'] st.write("### Usuários Cadastrados") st.dataframe(df_users, use_container_width=True, hide_index=True) else: st.info("Nenhum usuário cadastrado.") except Exception as e: st.error(f"Erro ao listar usuários: {e}") ####################################################### # --- MÓDULO 5: ALUNOS SEM ACESSO NO SEMESTRE ATUAL --- ####################################################### elif menu == "Alunos sem Acesso": st.title("🚫 Alunos sem Acesso (Semestre Atual)") # Define o semestre atual dinamicamente hoje = datetime.now() semestre_atual = f"{hoje.year}.{1 if hoje.month <= 6 else 2}" try: conn = get_connection() # --- FILTRO 1: CURSO --- query_cursos = f""" SELECT DISTINCT nome_curso FROM portal_alunos_flat WHERE semestre_referencia = '{semestre_atual}' ORDER BY nome_curso """ df_cursos = pd.read_sql(query_cursos, conn) lista_cursos = ["Todos"] + df_cursos['nome_curso'].tolist() col1, col2 = st.columns(2) with col1: curso_selecionado = st.selectbox("Filtrar por Curso:", lista_cursos) # --- FILTRO 2: TURMAS (Ajustado para o nome real da coluna) --- lista_turmas = ["Todas"] if curso_selecionado != "Todos": query_turmas = f""" SELECT DISTINCT turmas FROM portal_alunos_flat WHERE semestre_referencia = '{semestre_atual}' AND nome_curso = '{curso_selecionado}' AND turmas IS NOT NULL AND turmas != '' ORDER BY turmas """ df_turmas = pd.read_sql(query_turmas, conn) # Como a coluna é TEXT, limpamos possíveis valores duplicados ou vazios no Python lista_turmas = ["Todas"] + sorted(df_turmas['turmas'].unique().tolist()) with col2: turma_selecionada = st.selectbox("Filtrar por Turma:", lista_turmas, disabled=(curso_selecionado == "Todos")) if st.button("Gerar Lista"): # Montagem dinâmica dos filtros SQL filtro_sql = "" if curso_selecionado != "Todos": filtro_sql += f" AND nome_curso = '{curso_selecionado}'" if turma_selecionada != "Todas": # Usamos LIKE porque a coluna é TEXT e pode conter múltiplas turmas filtro_sql += f" AND turmas LIKE '%%{turma_selecionada}%%'" query_sem_acesso = f""" SELECT nome, sobrenome, email, celular, nome_curso, turmas, ultimo_acesso FROM portal_alunos_flat WHERE papel = 'Estudante' AND semestre_referencia = '{semestre_atual}' {filtro_sql} AND ( ultimo_acesso IS NULL OR ultimo_acesso = '' OR ultimo_acesso = '0000-00-00 00:00:00' OR STR_TO_DATE(ultimo_acesso, '%%d/%%m/%%Y') < '{hoje.year}-01-01' OR ultimo_acesso NOT LIKE '%%{hoje.year}%%' ) ORDER BY nome_curso ASC, turmas ASC, nome ASC """ df_sem_acesso = pd.read_sql(query_sem_acesso, conn) if not df_sem_acesso.empty: st.warning(f"Total de {len(df_sem_acesso)} alunos sem acesso em {semestre_atual}.") st.dataframe(df_sem_acesso, use_container_width=True, hide_index=True) # --- PREPARAÇÃO DOS ARQUIVOS PARA DOWNLOAD --- col_dl1, col_dl2 = st.columns(2) # Opção 1: CSV csv = df_sem_acesso.to_csv(index=False).encode('utf-8-sig') with col_dl1: st.download_button( label="📥 Baixar em CSV", data=csv, file_name=f"sem_acesso_{semestre_atual}.csv", mime="text/csv", use_container_width=True ) # Opção 2: EXCEL (XLSX) import io output = io.BytesIO() with pd.ExcelWriter(output, engine='openpyxl') as writer: df_sem_acesso.to_excel(writer, index=False, sheet_name='Alunos Sem Acesso') excel_data = output.getvalue() with col_dl2: st.download_button( label="📊 Baixar em Excel", data=excel_data, file_name=f"sem_acesso_{semestre_atual}.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", use_container_width=True ) else: st.success("Parabéns! Todos os alunos selecionados já acessaram o sistema.") conn.close() except Exception as e: st.error(f"Erro ao processar relatório: {e}") ####################################################### # --- MÓDULO 6: AUDITORIA DE LOGS (APENAS ADMIN) --- ####################################################### elif menu == "Logs do Sistema" and perfil_usuario == 'admin': st.title("📜 Auditoria e Logs do Sistema") # Seletor de Tipo de Log tipo_log = st.selectbox("Selecione o tipo de registro:", ["Acessos ao Sistema", "Operações de Usuários"]) try: conn = get_connection() if tipo_log == "Acessos ao Sistema": st.subheader("Histórico de Logins") query = "SELECT nome_usuario, email, data_acesso FROM app_logs_acesso ORDER BY data_acesso DESC" df_logs = pd.read_sql(query, conn) else: st.subheader("Histórico de Alterações Administrativas") query = "SELECT nome_executor, operacao, detalhes, data_operacao FROM app_logs_operacoes ORDER BY data_operacao DESC" df_logs = pd.read_sql(query, conn) conn.close() if not df_logs.empty: # Formatação amigável das datas no Pandas antes de exibir col_data = 'data_acesso' if tipo_log == "Acessos ao Sistema" else 'data_operacao' df_logs[col_data] = pd.to_datetime(df_logs[col_data]).dt.strftime('%d/%m/%Y %H:%M:%S') st.dataframe(df_logs, use_container_width=True, hide_index=True) # Botão para exportar os logs csv = df_logs.to_csv(index=False).encode('utf-8-sig') st.download_button(f"📥 Exportar {tipo_log}", csv, f"logs_{tipo_log.lower()}.csv", "text/csv") else: st.info("Nenhum registro encontrado nesta categoria.") except Exception as e: st.error(f"Erro ao carregar logs: {e}") ####################################################### # --- MÓDULO 7: CONSULTA DE EQUIPE DE APOIO --- ####################################################### elif menu == "Equipe de Apoio": st.title("👥 Consulta de Equipe de Apoio") st.write("Visualize Professores, Tutores e Coordenadores vinculados aos cursos.") try: conn = get_connection() # 1. Buscar Semestres disponíveis para o primeiro filtro query_semestres = "SELECT DISTINCT semestre_referencia FROM portal_alunos_flat ORDER BY semestre_referencia DESC" df_semestres = pd.read_sql(query_semestres, conn) lista_semestres = df_semestres['semestre_referencia'].tolist() semestre_sel = st.selectbox("Selecione o Semestre:", lista_semestres) if semestre_sel: # 2. Buscar Cursos do semestre selecionado para o segundo filtro query_cursos = f""" SELECT DISTINCT nome_curso FROM portal_alunos_flat WHERE semestre_referencia = '{semestre_sel}' ORDER BY nome_curso """ df_cursos = pd.read_sql(query_cursos, conn) lista_cursos = ["Todos"] + df_cursos['nome_curso'].tolist() curso_sel = st.selectbox("Selecione o Curso:", lista_cursos) if st.button("Consultar Equipe"): filtro_curso = "" if curso_sel == "Todos" else f"AND nome_curso = '{curso_sel}'" # 3. Query Principal: Todos os papéis EXCETO Estudante query_equipe = f""" SELECT nome, sobrenome, email, celular, papel, nome_curso, turmas FROM portal_alunos_flat WHERE semestre_referencia = '{semestre_sel}' AND papel != 'Estudante' {filtro_curso} ORDER BY nome_curso ASC, papel ASC, nome ASC """ df_equipe = pd.read_sql(query_equipe, conn) if not df_equipe.empty: st.success(f"Encontrados {len(df_equipe)} membros da equipe.") # Exibição organizada st.dataframe(df_equipe, use_container_width=True, hide_index=True) # Exportação csv = df_equipe.to_csv(index=False).encode('utf-8-sig') st.download_button("📥 Baixar Lista de Contatos", csv, f"equipe_{semestre_sel}.csv", "text/csv") else: st.warning("Nenhum membro de equipe encontrado para os critérios selecionados.") conn.close() except Exception as e: st.error(f"Erro ao consultar equipe: {e}")