Moodle / app.py
lucianorabelo's picture
Update app.py
92f84ed verified
Raw
History Blame Contribute Delete
32.8 kB
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("""
<style>
/* Esconde o menu sanduíche e a barra superior do Streamlit */
#MainMenu {visibility: hidden;}
header {visibility: hidden;}
footer {visibility: hidden;}
/* Ajuste para diminuir o espaço em branco no topo após esconder o header */
.block-container {
padding-top: 1rem;
padding-bottom: 0rem;
}
</style>
""", 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 = """
<style>
#MainMenu {visibility: hidden;}
header {visibility: hidden;}
footer {visibility: hidden;}
.stDeployButton {display:none;}
</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}")