File size: 32,801 Bytes
e80f6e8
 
 
 
a1aa22a
1393046
6926ab7
e80f6e8
 
 
efa834e
 
 
 
 
 
 
 
 
 
 
 
 
 
e80f6e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6926ab7
 
 
 
 
 
 
c896cab
 
 
 
 
 
 
 
 
 
 
e80f6e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
043de34
e80f6e8
 
 
 
 
 
 
043de34
 
efa834e
f804c9e
 
043de34
f804c9e
efa834e
 
f804c9e
 
043de34
 
 
 
 
 
 
 
efa834e
 
 
043de34
f804c9e
e80f6e8
043de34
e80f6e8
 
 
 
f804c9e
e80f6e8
 
a1aa22a
 
7fb28a4
 
 
 
a1aa22a
e80f6e8
9200ed0
959701c
7fb28a4
9200ed0
7fb28a4
a1aa22a
970b449
9200ed0
a1aa22a
 
 
caba356
e80f6e8
 
08d46a6
 
22566db
08d46a6
e80f6e8
 
 
a1aa22a
e80f6e8
 
 
 
 
4d1212d
 
ceb5afa
4d1212d
e80f6e8
 
4d1212d
e80f6e8
 
 
a1aa22a
e80f6e8
ceb5afa
 
 
e80f6e8
ceb5afa
9b81888
 
 
 
 
 
 
 
ceb5afa
 
 
9b81888
 
 
ceb5afa
9b81888
 
 
 
 
 
 
 
 
ceb5afa
ee162f5
ceb5afa
4d1212d
caba356
4d1212d
ee162f5
e80f6e8
4d1212d
e80f6e8
a1aa22a
959701c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ceb5afa
959701c
 
 
 
 
 
 
 
 
 
 
 
 
92f84ed
 
959701c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92f84ed
959701c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
08d46a6
959701c
08d46a6
22566db
 
ceb5afa
22566db
caba356
22566db
 
caba356
22566db
caba356
22566db
 
caba356
 
 
22566db
caba356
22566db
 
 
29a804d
 
 
9255a73
29a804d
 
 
 
 
 
 
 
 
 
 
 
 
 
caba356
29a804d
1752f12
 
 
 
 
 
29a804d
1752f12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29a804d
caba356
29a804d
caba356
29a804d
959701c
 
08d46a6
29a804d
 
caba356
 
 
29a804d
 
 
9bb893b
29a804d
 
6926ab7
9bb893b
 
29a804d
 
9bb893b
 
efa834e
9bb893b
 
efa834e
9bb893b
0b0992a
9bb893b
 
 
 
 
 
 
 
 
 
 
 
 
 
efa834e
29a804d
0b0992a
 
9bb893b
29a804d
caba356
29a804d
0b0992a
29a804d
 
0b0992a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29a804d
0b0992a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29a804d
caba356
0b0992a
7b67b33
959701c
08d46a6
cbf0331
 
 
f37c10a
cbf0331
 
 
 
 
 
f37c10a
cbf0331
 
 
 
 
 
 
 
 
f37c10a
cbf0331
f37c10a
 
 
7b67b33
f37c10a
 
 
7b67b33
f37c10a
 
 
7b67b33
 
f37c10a
 
7b67b33
 
f37c10a
 
 
 
cbf0331
f37c10a
 
 
 
7b67b33
f37c10a
7b67b33
 
cbf0331
 
7b67b33
cbf0331
 
 
f37c10a
c660edf
 
2a0c675
 
f37c10a
 
c660edf
7b67b33
cbf0331
 
 
 
 
f37c10a
cbf0331
0acfe36
 
 
cbf0331
0acfe36
cbf0331
0acfe36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cbf0331
f37c10a
cbf0331
 
 
 
f37c10a
 
08d46a6
959701c
08d46a6
970b449
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
08d46a6
959701c
08d46a6
2271d67
 
 
 
 
 
cbf0331
2271d67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
de02a3c
2271d67
 
 
 
cebf045
2271d67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
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}")