Update app.py
Browse files
app.py
CHANGED
|
@@ -71,13 +71,12 @@ from sqlalchemy import text, func, or_
|
|
| 71 |
# 🗄️ Banco ativo (Produção/Teste/Treinamento)
|
| 72 |
# Tentativa de importar utilitários do db_router, com fallback seguro.
|
| 73 |
try:
|
| 74 |
-
from db_router import current_db_choice, bank_label
|
| 75 |
_HAS_ROUTER = True
|
| 76 |
except Exception:
|
| 77 |
_HAS_ROUTER = False
|
| 78 |
|
| 79 |
def current_db_choice() -> str:
|
| 80 |
-
# Fallback simples
|
| 81 |
return st.session_state.get("__db_choice_override__", "prod")
|
| 82 |
|
| 83 |
def bank_label(choice: str) -> str:
|
|
@@ -125,7 +124,7 @@ if os.getenv("CLEAR_CACHE_ON_START", "0") == "1":
|
|
| 125 |
except Exception:
|
| 126 |
pass
|
| 127 |
|
| 128 |
-
# Rodar init_db.run() uma única vez por start de container
|
| 129 |
try:
|
| 130 |
import init_db as _init_db
|
| 131 |
_HAS_INIT_DB = hasattr(_init_db, "run")
|
|
@@ -149,17 +148,15 @@ if os.getenv("INIT_DB_ON_START", "0") == "1" and _HAS_INIT_DB:
|
|
| 149 |
pass
|
| 150 |
|
| 151 |
# ===============================
|
| 152 |
-
# RERUN por querystring (atalho ?rr=1)
|
| 153 |
# ===============================
|
| 154 |
def _get_query_params():
|
| 155 |
"""Compat: retorna query params como dict (Streamlit novo/antigo)."""
|
| 156 |
try:
|
| 157 |
-
# Streamlit >= 1.32
|
| 158 |
-
return dict(st.query_params)
|
| 159 |
except Exception:
|
| 160 |
-
# Streamlit antigo (experimental)
|
| 161 |
try:
|
| 162 |
-
return dict(st.experimental_get_query_params())
|
| 163 |
except Exception:
|
| 164 |
return {}
|
| 165 |
|
|
@@ -173,14 +170,32 @@ def _set_query_params(new_params: dict):
|
|
| 173 |
except Exception:
|
| 174 |
pass
|
| 175 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 176 |
# 🔒 ANTI-TREMOR PATCH: não consumir rr=1 quando login/quiz ainda não concluídos
|
| 177 |
def _check_rerun_qs(pagina_atual: str = ""):
|
| 178 |
"""
|
| 179 |
Se a URL contiver rr=1 (ou true), força um rerun e limpa o parâmetro para evitar loop.
|
| 180 |
-
✅
|
| 181 |
-
✅ (PATCH) Também não dispara quando estiver em 'outlook_relatorio' para não interromper leitura COM.
|
| 182 |
-
✅ (PATCH) Não dispara quando estiver em 'formulario'.
|
| 183 |
-
✅ (ANTI-TREMOR) Não dispara enquanto login/quiz não concluídos (para não mexer na logo).
|
| 184 |
"""
|
| 185 |
try:
|
| 186 |
if st.session_state.get("__qs_rr_consumed__", False):
|
|
@@ -192,7 +207,7 @@ def _check_rerun_qs(pagina_atual: str = ""):
|
|
| 192 |
|
| 193 |
# Evita rr=1 em módulos sensíveis a rerun/refresh
|
| 194 |
if pagina_atual in ("resposta", "outlook_relatorio", "formulario"):
|
| 195 |
-
return
|
| 196 |
|
| 197 |
params = _get_query_params()
|
| 198 |
rr_raw = params.get("rr", ["0"])
|
|
@@ -229,15 +244,6 @@ def _get_db_session():
|
|
| 229 |
# 3) Fallback
|
| 230 |
return SessionLocal()
|
| 231 |
|
| 232 |
-
# ===============================
|
| 233 |
-
# CONFIGURAÇÃO INICIAL
|
| 234 |
-
# ===============================
|
| 235 |
-
# Criação do schema (sem derrubar a app se o banco ainda não estiver pronto)
|
| 236 |
-
try:
|
| 237 |
-
Base.metadata.create_all(bind=engine)
|
| 238 |
-
except Exception as e:
|
| 239 |
-
st.sidebar.warning(f"Schema não foi criado automaticamente: {e}")
|
| 240 |
-
|
| 241 |
def quiz_respondido_hoje(usuario: str) -> bool:
|
| 242 |
# ✅ Usar sessão ciente do ambiente
|
| 243 |
db = _get_db_session()
|
|
@@ -758,10 +764,23 @@ def _db_choice_ui():
|
|
| 758 |
st.toast(f"Banco alterado para: {_lbl(sel_code)}", icon="🗄️")
|
| 759 |
st.rerun()
|
| 760 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 761 |
# ===============================
|
| 762 |
# MAIN
|
| 763 |
# ===============================
|
| 764 |
def main():
|
|
|
|
|
|
|
|
|
|
| 765 |
# Estados iniciais
|
| 766 |
if "logado" not in st.session_state:
|
| 767 |
st.session_state.logado = False
|
|
@@ -779,7 +798,7 @@ def main():
|
|
| 779 |
# ===== Seleção de banco (sempre visível na sidebar) =====
|
| 780 |
_db_choice_ui()
|
| 781 |
|
| 782 |
-
# 🔧
|
| 783 |
try:
|
| 784 |
from banco import init_schema
|
| 785 |
with st.sidebar.expander("⚙️ Manutenção do banco atual", expanded=False):
|
|
@@ -789,18 +808,37 @@ def main():
|
|
| 789 |
st.sidebar.success("Schema criado/atualizado no banco selecionado com sucesso.")
|
| 790 |
except Exception as e:
|
| 791 |
st.sidebar.error(f"Falha ao criar/atualizar schema: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 792 |
except Exception:
|
| 793 |
-
# Se o módulo não expor init_schema, apenas ignora silenciosamente
|
| 794 |
pass
|
| 795 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 796 |
# LOGIN
|
| 797 |
if not st.session_state.logado:
|
| 798 |
st.session_state.quiz_verificado = False
|
| 799 |
# 🔒 ANTI-TREMOR: renderiza logo top apenas 1x
|
| 800 |
exibir_logo_once(top=True, sidebar=False)
|
| 801 |
login()
|
| 802 |
-
|
| 803 |
-
# 🔕 REMOVIDO: mensagem de dica de autenticação no Spaces (conforme solicitado)
|
| 804 |
return
|
| 805 |
|
| 806 |
# 👥 Heartbeat + Badge de usuários logados (APENAS ADMIN)
|
|
@@ -824,11 +862,11 @@ def main():
|
|
| 824 |
# 🔘 Botão Admin: habilitar/desabilitar painel de diagnóstico
|
| 825 |
st.sidebar.markdown("---")
|
| 826 |
if st.session_state.get("__auth_diag__"):
|
| 827 |
-
if st.sidebar.button("🧪 Desativar diagnóstico de login (Admin)"):
|
| 828 |
st.session_state["__auth_diag__"] = False
|
| 829 |
st.rerun()
|
| 830 |
else:
|
| 831 |
-
if st.sidebar.button("🧪 Ativar diagnóstico de login (Admin)"):
|
| 832 |
st.session_state["__auth_diag__"] = True
|
| 833 |
st.rerun()
|
| 834 |
|
|
@@ -841,14 +879,14 @@ def main():
|
|
| 841 |
# ============================
|
| 842 |
from sqlalchemy import inspect # import local para evitar custo desnecessário
|
| 843 |
with st.sidebar.expander("🧪 Diagnóstico Profundo do Banco", expanded=False):
|
| 844 |
-
|
| 845 |
try:
|
| 846 |
-
eng =
|
| 847 |
st.caption(f"Engine URL: {eng.url}")
|
| 848 |
|
| 849 |
# 1) SELECT 1
|
| 850 |
try:
|
| 851 |
-
|
| 852 |
st.success("SELECT 1 OK")
|
| 853 |
except Exception as e:
|
| 854 |
st.error(f"SELECT 1 falhou: {e}")
|
|
@@ -872,7 +910,7 @@ def main():
|
|
| 872 |
|
| 873 |
# 5) Contagem
|
| 874 |
try:
|
| 875 |
-
cnt =
|
| 876 |
st.write("Quantidade de registros:", cnt)
|
| 877 |
except Exception as e:
|
| 878 |
st.error(f"COUNT(*) falhou: {e}")
|
|
@@ -881,7 +919,7 @@ def main():
|
|
| 881 |
try:
|
| 882 |
sel_cols = [c for c in ["usuario", "email", "perfil", "nome"] if c in cols]
|
| 883 |
sel_expr = ", ".join(sel_cols) if sel_cols else "*"
|
| 884 |
-
amostra =
|
| 885 |
rows = [dict(r._mapping) for r in amostra]
|
| 886 |
st.write("Amostra:", rows)
|
| 887 |
except Exception as e:
|
|
@@ -894,7 +932,7 @@ def main():
|
|
| 894 |
test_user = st.text_input("Usuário para testar hash", "", key="__bcrypt_user__")
|
| 895 |
test_pass = st.text_input("Senha para testar hash", "", type="password", key="__bcrypt_pass__")
|
| 896 |
|
| 897 |
-
if st.button("Testar bcrypt com este usuário", key="__btn_bcrypt_test__"):
|
| 898 |
try:
|
| 899 |
pass_cols = [c for c in ["senha_hash", "password_hash", "senha", "hash"] if c in cols]
|
| 900 |
user_cols = [c for c in ["usuario", "username", "login"] if c in cols]
|
|
@@ -903,7 +941,7 @@ def main():
|
|
| 903 |
else:
|
| 904 |
c_pass = pass_cols[0]
|
| 905 |
c_user = user_cols[0]
|
| 906 |
-
res =
|
| 907 |
text(f"SELECT {c_user} AS u, {c_pass} AS h FROM {target_table} WHERE {c_user} = :u LIMIT 1"),
|
| 908 |
{"u": test_user.strip()}
|
| 909 |
).fetchone()
|
|
@@ -926,7 +964,7 @@ def main():
|
|
| 926 |
st.error(f"Erro no teste: {e}")
|
| 927 |
finally:
|
| 928 |
try:
|
| 929 |
-
|
| 930 |
except Exception:
|
| 931 |
pass
|
| 932 |
|
|
@@ -935,7 +973,7 @@ def main():
|
|
| 935 |
# ============================
|
| 936 |
with st.sidebar.expander("🧰 Manutenção (Admin)", expanded=False):
|
| 937 |
col_m1, col_m2 = st.columns(2)
|
| 938 |
-
if col_m1.button("🧹 Limpar cache agora",
|
| 939 |
try:
|
| 940 |
st.cache_data.clear()
|
| 941 |
except Exception:
|
|
@@ -947,18 +985,17 @@ def main():
|
|
| 947 |
st.sidebar.success("Caches limpos.")
|
| 948 |
st.rerun()
|
| 949 |
|
| 950 |
-
# Rodar init_db.run() com dupla confirmação
|
| 951 |
if _HAS_INIT_DB:
|
| 952 |
c1, c2 = st.columns(2)
|
| 953 |
-
sure1 = c1.checkbox("Confirmo")
|
| 954 |
-
sure2 = c2.checkbox("Estou ciente")
|
| 955 |
-
if st.button("🧬 Rodar init_db.run()",
|
| 956 |
if not (sure1 and sure2):
|
| 957 |
st.warning("Marque as duas confirmações para executar.")
|
| 958 |
else:
|
| 959 |
try:
|
| 960 |
_init_db.run()
|
| 961 |
-
# Atualiza marker
|
| 962 |
with open(_INIT_MARK, "w", encoding="utf-8") as f:
|
| 963 |
f.write(f"init manual at {datetime.now().isoformat()}\n")
|
| 964 |
st.success("init_db.run() executado com sucesso.")
|
|
@@ -968,7 +1005,7 @@ def main():
|
|
| 968 |
# 🔄 Botão de Recarregar (mantém a sessão ativa) + ⏱️ Controle do intervalo
|
| 969 |
st.sidebar.markdown("---")
|
| 970 |
col_reload, col_interval = st.sidebar.columns([1, 1])
|
| 971 |
-
if col_reload.button("🔄 Recarregar (sem sair)", key="__btn_reload_now__"):
|
| 972 |
st.rerun()
|
| 973 |
|
| 974 |
if hasattr(st, "popover"):
|
|
@@ -979,7 +1016,7 @@ def main():
|
|
| 979 |
value=int(st.session_state["__auto_refresh_interval_sec__"]),
|
| 980 |
step=5, key="__auto_refresh_input__"
|
| 981 |
)
|
| 982 |
-
if st.button("Aplicar intervalo", key="__btn_apply_auto_refresh__"):
|
| 983 |
st.session_state["__auto_refresh_interval_sec__"] = int(new_val)
|
| 984 |
try:
|
| 985 |
if int(new_val) > 0:
|
|
@@ -997,7 +1034,7 @@ def main():
|
|
| 997 |
value=int(st.session_state["__auto_refresh_interval_sec__"]),
|
| 998 |
step=5, key="__auto_refresh_input__"
|
| 999 |
)
|
| 1000 |
-
if st.button("Aplicar intervalo", key="__btn_apply_auto_refresh__"):
|
| 1001 |
st.session_state["__auto_refresh_interval_sec__"] = int(new_val)
|
| 1002 |
try:
|
| 1003 |
if int(new_val) > 0:
|
|
@@ -1067,7 +1104,7 @@ def main():
|
|
| 1067 |
)
|
| 1068 |
|
| 1069 |
# 👉 Direciona para o MESMO módulo do menu (resposta.main())
|
| 1070 |
-
if st.sidebar.button("📬 Abrir Caixa de Entrada (Admin)"):
|
| 1071 |
st.session_state.nav_target = "resposta"
|
| 1072 |
st.rerun()
|
| 1073 |
|
|
@@ -1129,7 +1166,7 @@ def main():
|
|
| 1129 |
pass
|
| 1130 |
st.session_state["__user_toast_shown__"] = True
|
| 1131 |
|
| 1132 |
-
if st.sidebar.button("📥 Ver respostas"):
|
| 1133 |
st.session_state.nav_target = "sugestoes_ioirun"
|
| 1134 |
st.session_state.user_responses_viewed = True
|
| 1135 |
st.rerun()
|
|
@@ -1274,7 +1311,7 @@ def main():
|
|
| 1274 |
# Logout
|
| 1275 |
st.sidebar.markdown("---")
|
| 1276 |
if st.session_state.get("logado"):
|
| 1277 |
-
if st.sidebar.button("🚪 Sair (Logout)"):
|
| 1278 |
logout()
|
| 1279 |
|
| 1280 |
st.divider()
|
|
|
|
| 71 |
# 🗄️ Banco ativo (Produção/Teste/Treinamento)
|
| 72 |
# Tentativa de importar utilitários do db_router, com fallback seguro.
|
| 73 |
try:
|
| 74 |
+
from db_router import current_db_choice, bank_label
|
| 75 |
_HAS_ROUTER = True
|
| 76 |
except Exception:
|
| 77 |
_HAS_ROUTER = False
|
| 78 |
|
| 79 |
def current_db_choice() -> str:
|
|
|
|
| 80 |
return st.session_state.get("__db_choice_override__", "prod")
|
| 81 |
|
| 82 |
def bank_label(choice: str) -> str:
|
|
|
|
| 124 |
except Exception:
|
| 125 |
pass
|
| 126 |
|
| 127 |
+
# Rodar init_db.run() uma única vez por start de container (⚠️ roda no banco padrão do ambiente)
|
| 128 |
try:
|
| 129 |
import init_db as _init_db
|
| 130 |
_HAS_INIT_DB = hasattr(_init_db, "run")
|
|
|
|
| 148 |
pass
|
| 149 |
|
| 150 |
# ===============================
|
| 151 |
+
# RERUN por querystring (atalho ?rr=1) e aplicar ?db=teste|treinamento
|
| 152 |
# ===============================
|
| 153 |
def _get_query_params():
|
| 154 |
"""Compat: retorna query params como dict (Streamlit novo/antigo)."""
|
| 155 |
try:
|
| 156 |
+
return dict(st.query_params) # Streamlit >= 1.32
|
|
|
|
| 157 |
except Exception:
|
|
|
|
| 158 |
try:
|
| 159 |
+
return dict(st.experimental_get_query_params()) # Antigo
|
| 160 |
except Exception:
|
| 161 |
return {}
|
| 162 |
|
|
|
|
| 170 |
except Exception:
|
| 171 |
pass
|
| 172 |
|
| 173 |
+
def _apply_db_choice_from_qs():
|
| 174 |
+
"""Permite selecionar o banco via URL (?db=test|treinamento)."""
|
| 175 |
+
try:
|
| 176 |
+
params = _get_query_params()
|
| 177 |
+
db = params.get("db")
|
| 178 |
+
if not db:
|
| 179 |
+
return
|
| 180 |
+
db = db[0] if isinstance(db, (list, tuple)) else db
|
| 181 |
+
sel = str(db).strip().lower()
|
| 182 |
+
if _set_db_choice_func:
|
| 183 |
+
try:
|
| 184 |
+
_set_db_choice_func(sel)
|
| 185 |
+
except Exception:
|
| 186 |
+
os.environ["DB_CHOICE"] = sel
|
| 187 |
+
st.session_state["__db_choice_override__"] = sel
|
| 188 |
+
else:
|
| 189 |
+
os.environ["DB_CHOICE"] = sel
|
| 190 |
+
st.session_state["__db_choice_override__"] = sel
|
| 191 |
+
except Exception:
|
| 192 |
+
pass
|
| 193 |
+
|
| 194 |
# 🔒 ANTI-TREMOR PATCH: não consumir rr=1 quando login/quiz ainda não concluídos
|
| 195 |
def _check_rerun_qs(pagina_atual: str = ""):
|
| 196 |
"""
|
| 197 |
Se a URL contiver rr=1 (ou true), força um rerun e limpa o parâmetro para evitar loop.
|
| 198 |
+
✅ Bloqueios para módulos sensíveis e login/quiz.
|
|
|
|
|
|
|
|
|
|
| 199 |
"""
|
| 200 |
try:
|
| 201 |
if st.session_state.get("__qs_rr_consumed__", False):
|
|
|
|
| 207 |
|
| 208 |
# Evita rr=1 em módulos sensíveis a rerun/refresh
|
| 209 |
if pagina_atual in ("resposta", "outlook_relatorio", "formulario"):
|
| 210 |
+
return
|
| 211 |
|
| 212 |
params = _get_query_params()
|
| 213 |
rr_raw = params.get("rr", ["0"])
|
|
|
|
| 244 |
# 3) Fallback
|
| 245 |
return SessionLocal()
|
| 246 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
def quiz_respondido_hoje(usuario: str) -> bool:
|
| 248 |
# ✅ Usar sessão ciente do ambiente
|
| 249 |
db = _get_db_session()
|
|
|
|
| 764 |
st.toast(f"Banco alterado para: {_lbl(sel_code)}", icon="🗄️")
|
| 765 |
st.rerun()
|
| 766 |
|
| 767 |
+
# 🔎 DEBUG da escolha/URL (ajuda a diagnosticar se o router está aplicando)
|
| 768 |
+
try:
|
| 769 |
+
from db_router import current_db_choice as _cur, bank_label as _lbl2, get_engine as _eng
|
| 770 |
+
ch = _cur()
|
| 771 |
+
eng = _eng()
|
| 772 |
+
st.sidebar.caption(f"⚙️ DEBUG • Banco atual: {_lbl2(ch)} ({ch})")
|
| 773 |
+
st.sidebar.caption(f"⚙️ DEBUG • URL: {eng.url}")
|
| 774 |
+
except Exception as e:
|
| 775 |
+
st.sidebar.caption(f"⚙️ DEBUG router fail: {e}")
|
| 776 |
+
|
| 777 |
# ===============================
|
| 778 |
# MAIN
|
| 779 |
# ===============================
|
| 780 |
def main():
|
| 781 |
+
# Aplicar escolha via URL (ex.: ?db=test)
|
| 782 |
+
_apply_db_choice_from_qs()
|
| 783 |
+
|
| 784 |
# Estados iniciais
|
| 785 |
if "logado" not in st.session_state:
|
| 786 |
st.session_state.logado = False
|
|
|
|
| 798 |
# ===== Seleção de banco (sempre visível na sidebar) =====
|
| 799 |
_db_choice_ui()
|
| 800 |
|
| 801 |
+
# 🔧 Manutenção do banco atual: Schema + init_db.run()
|
| 802 |
try:
|
| 803 |
from banco import init_schema
|
| 804 |
with st.sidebar.expander("⚙️ Manutenção do banco atual", expanded=False):
|
|
|
|
| 808 |
st.sidebar.success("Schema criado/atualizado no banco selecionado com sucesso.")
|
| 809 |
except Exception as e:
|
| 810 |
st.sidebar.error(f"Falha ao criar/atualizar schema: {e}")
|
| 811 |
+
|
| 812 |
+
# Botão para rodar init_db.run() no banco atual
|
| 813 |
+
if _HAS_INIT_DB:
|
| 814 |
+
c1, c2 = st.columns(2)
|
| 815 |
+
sure1 = c1.checkbox("Confirmo", key="__init_confirm1__")
|
| 816 |
+
sure2 = c2.checkbox("Estou ciente", key="__init_confirm2__")
|
| 817 |
+
if st.button("Rodar init_db.run()", key="__btn_run_initdb__", help="Cria usuários padrão e garante schema", type="secondary"):
|
| 818 |
+
if not (sure1 and sure2):
|
| 819 |
+
st.warning("Marque as duas confirmações para executar.")
|
| 820 |
+
else:
|
| 821 |
+
try:
|
| 822 |
+
_init_db.run()
|
| 823 |
+
st.success("init_db.run() executado com sucesso no banco selecionado.")
|
| 824 |
+
except Exception as e:
|
| 825 |
+
st.error(f"Falha ao rodar init_db.run(): {e}")
|
| 826 |
except Exception:
|
|
|
|
| 827 |
pass
|
| 828 |
|
| 829 |
+
# ⚙️ Criação automática do schema (leve) após seleção do banco (rodará no banco correto)
|
| 830 |
+
try:
|
| 831 |
+
Base.metadata.create_all(bind=engine)
|
| 832 |
+
except Exception as e:
|
| 833 |
+
st.sidebar.warning(f"Schema não foi criado automaticamente: {e}")
|
| 834 |
+
|
| 835 |
# LOGIN
|
| 836 |
if not st.session_state.logado:
|
| 837 |
st.session_state.quiz_verificado = False
|
| 838 |
# 🔒 ANTI-TREMOR: renderiza logo top apenas 1x
|
| 839 |
exibir_logo_once(top=True, sidebar=False)
|
| 840 |
login()
|
| 841 |
+
# 🔕 REMOVIDO: mensagem de dica de autenticação no Spaces
|
|
|
|
| 842 |
return
|
| 843 |
|
| 844 |
# 👥 Heartbeat + Badge de usuários logados (APENAS ADMIN)
|
|
|
|
| 862 |
# 🔘 Botão Admin: habilitar/desabilitar painel de diagnóstico
|
| 863 |
st.sidebar.markdown("---")
|
| 864 |
if st.session_state.get("__auth_diag__"):
|
| 865 |
+
if st.sidebar.button("🧪 Desativar diagnóstico de login (Admin)", key="__btn_auth_diag_off__", type="secondary"):
|
| 866 |
st.session_state["__auth_diag__"] = False
|
| 867 |
st.rerun()
|
| 868 |
else:
|
| 869 |
+
if st.sidebar.button("🧪 Ativar diagnóstico de login (Admin)", key="__btn_auth_diag_on__", type="secondary"):
|
| 870 |
st.session_state["__auth_diag__"] = True
|
| 871 |
st.rerun()
|
| 872 |
|
|
|
|
| 879 |
# ============================
|
| 880 |
from sqlalchemy import inspect # import local para evitar custo desnecessário
|
| 881 |
with st.sidebar.expander("🧪 Diagnóstico Profundo do Banco", expanded=False):
|
| 882 |
+
dbx = _get_db_session()
|
| 883 |
try:
|
| 884 |
+
eng = dbx.bind
|
| 885 |
st.caption(f"Engine URL: {eng.url}")
|
| 886 |
|
| 887 |
# 1) SELECT 1
|
| 888 |
try:
|
| 889 |
+
dbx.execute(text("SELECT 1"))
|
| 890 |
st.success("SELECT 1 OK")
|
| 891 |
except Exception as e:
|
| 892 |
st.error(f"SELECT 1 falhou: {e}")
|
|
|
|
| 910 |
|
| 911 |
# 5) Contagem
|
| 912 |
try:
|
| 913 |
+
cnt = dbx.execute(text(f"SELECT COUNT(*) FROM {target_table}")).fetchone()[0]
|
| 914 |
st.write("Quantidade de registros:", cnt)
|
| 915 |
except Exception as e:
|
| 916 |
st.error(f"COUNT(*) falhou: {e}")
|
|
|
|
| 919 |
try:
|
| 920 |
sel_cols = [c for c in ["usuario", "email", "perfil", "nome"] if c in cols]
|
| 921 |
sel_expr = ", ".join(sel_cols) if sel_cols else "*"
|
| 922 |
+
amostra = dbx.execute(text(f"SELECT {sel_expr} FROM {target_table} LIMIT 5"))
|
| 923 |
rows = [dict(r._mapping) for r in amostra]
|
| 924 |
st.write("Amostra:", rows)
|
| 925 |
except Exception as e:
|
|
|
|
| 932 |
test_user = st.text_input("Usuário para testar hash", "", key="__bcrypt_user__")
|
| 933 |
test_pass = st.text_input("Senha para testar hash", "", type="password", key="__bcrypt_pass__")
|
| 934 |
|
| 935 |
+
if st.button("Testar bcrypt com este usuário", key="__btn_bcrypt_test__", type="secondary"):
|
| 936 |
try:
|
| 937 |
pass_cols = [c for c in ["senha_hash", "password_hash", "senha", "hash"] if c in cols]
|
| 938 |
user_cols = [c for c in ["usuario", "username", "login"] if c in cols]
|
|
|
|
| 941 |
else:
|
| 942 |
c_pass = pass_cols[0]
|
| 943 |
c_user = user_cols[0]
|
| 944 |
+
res = dbx.execute(
|
| 945 |
text(f"SELECT {c_user} AS u, {c_pass} AS h FROM {target_table} WHERE {c_user} = :u LIMIT 1"),
|
| 946 |
{"u": test_user.strip()}
|
| 947 |
).fetchone()
|
|
|
|
| 964 |
st.error(f"Erro no teste: {e}")
|
| 965 |
finally:
|
| 966 |
try:
|
| 967 |
+
dbx.close()
|
| 968 |
except Exception:
|
| 969 |
pass
|
| 970 |
|
|
|
|
| 973 |
# ============================
|
| 974 |
with st.sidebar.expander("🧰 Manutenção (Admin)", expanded=False):
|
| 975 |
col_m1, col_m2 = st.columns(2)
|
| 976 |
+
if col_m1.button("🧹 Limpar cache agora", key="__btn_clear_cache__", help="Limpa cache de dados/recursos", type="secondary"):
|
| 977 |
try:
|
| 978 |
st.cache_data.clear()
|
| 979 |
except Exception:
|
|
|
|
| 985 |
st.sidebar.success("Caches limpos.")
|
| 986 |
st.rerun()
|
| 987 |
|
| 988 |
+
# Rodar init_db.run() com dupla confirmação (Admin)
|
| 989 |
if _HAS_INIT_DB:
|
| 990 |
c1, c2 = st.columns(2)
|
| 991 |
+
sure1 = c1.checkbox("Confirmo", key="__btn_run_initdb_admin_c1__")
|
| 992 |
+
sure2 = c2.checkbox("Estou ciente", key="__btn_run_initdb_admin_c2__")
|
| 993 |
+
if st.button("🧬 Rodar init_db.run()", key="__btn_run_initdb_admin__", type="secondary"):
|
| 994 |
if not (sure1 and sure2):
|
| 995 |
st.warning("Marque as duas confirmações para executar.")
|
| 996 |
else:
|
| 997 |
try:
|
| 998 |
_init_db.run()
|
|
|
|
| 999 |
with open(_INIT_MARK, "w", encoding="utf-8") as f:
|
| 1000 |
f.write(f"init manual at {datetime.now().isoformat()}\n")
|
| 1001 |
st.success("init_db.run() executado com sucesso.")
|
|
|
|
| 1005 |
# 🔄 Botão de Recarregar (mantém a sessão ativa) + ⏱️ Controle do intervalo
|
| 1006 |
st.sidebar.markdown("---")
|
| 1007 |
col_reload, col_interval = st.sidebar.columns([1, 1])
|
| 1008 |
+
if col_reload.button("🔄 Recarregar (sem sair)", key="__btn_reload_now__", type="secondary"):
|
| 1009 |
st.rerun()
|
| 1010 |
|
| 1011 |
if hasattr(st, "popover"):
|
|
|
|
| 1016 |
value=int(st.session_state["__auto_refresh_interval_sec__"]),
|
| 1017 |
step=5, key="__auto_refresh_input__"
|
| 1018 |
)
|
| 1019 |
+
if st.button("Aplicar intervalo", key="__btn_apply_auto_refresh__", type="secondary"):
|
| 1020 |
st.session_state["__auto_refresh_interval_sec__"] = int(new_val)
|
| 1021 |
try:
|
| 1022 |
if int(new_val) > 0:
|
|
|
|
| 1034 |
value=int(st.session_state["__auto_refresh_interval_sec__"]),
|
| 1035 |
step=5, key="__auto_refresh_input__"
|
| 1036 |
)
|
| 1037 |
+
if st.button("Aplicar intervalo", key="__btn_apply_auto_refresh__", type="secondary"):
|
| 1038 |
st.session_state["__auto_refresh_interval_sec__"] = int(new_val)
|
| 1039 |
try:
|
| 1040 |
if int(new_val) > 0:
|
|
|
|
| 1104 |
)
|
| 1105 |
|
| 1106 |
# 👉 Direciona para o MESMO módulo do menu (resposta.main())
|
| 1107 |
+
if st.sidebar.button("📬 Abrir Caixa de Entrada (Admin)", key="__btn_open_inbox__", type="secondary"):
|
| 1108 |
st.session_state.nav_target = "resposta"
|
| 1109 |
st.rerun()
|
| 1110 |
|
|
|
|
| 1166 |
pass
|
| 1167 |
st.session_state["__user_toast_shown__"] = True
|
| 1168 |
|
| 1169 |
+
if st.sidebar.button("📥 Ver respostas", key="__btn_view_answers__", type="secondary"):
|
| 1170 |
st.session_state.nav_target = "sugestoes_ioirun"
|
| 1171 |
st.session_state.user_responses_viewed = True
|
| 1172 |
st.rerun()
|
|
|
|
| 1311 |
# Logout
|
| 1312 |
st.sidebar.markdown("---")
|
| 1313 |
if st.session_state.get("logado"):
|
| 1314 |
+
if st.sidebar.button("🚪 Sair (Logout)", key="__btn_logout__", type="primary"):
|
| 1315 |
logout()
|
| 1316 |
|
| 1317 |
st.divider()
|