Roudrigus commited on
Commit
a050b5d
·
verified ·
1 Parent(s): cfd9653

Update db_router.py

Browse files
Files changed (1) hide show
  1. db_router.py +220 -152
db_router.py CHANGED
@@ -1,152 +1,220 @@
1
-
2
- # -*- coding: utf-8 -*-
3
- """
4
- db_router.py Roteia Engine/SessionLocal para 'prod' (Load.db), 'test' (Load_teste.db)
5
- e 'treinamento' (Load_treinamento.db), conforme escolha do usuário (mantida em st.session_state).
6
-
7
- • set_db_choice("prod"|"test"|"treinamento") → define o banco ativo para a sessão do usuário
8
- current_db_choice() → retorna 'prod' | 'test' | 'treinamento'
9
- get_engine() → engine do banco ativo (SQLite)
10
- • get_session_factory() → sessionmaker do banco ativo
11
- SessionLocal() sessão pronta no banco ativo
12
-
13
- Observação:
14
- - Este arquivo monta os caminhos dos bancos SQLite com base na pasta do projeto
15
- (onde está este arquivo), sem depender de .env.
16
- - Se quiser usar .env futuramente, há uma seção comentada para isso.
17
- """
18
-
19
- import os
20
- import streamlit as st
21
- from sqlalchemy import create_engine
22
- from sqlalchemy.orm import sessionmaker
23
-
24
- # ============================
25
- # Caminhos dos bancos (SQLite)
26
- # ============================
27
- # Pasta base do projeto (onde está este db_router.py)
28
- BASE_DIR = os.path.dirname(os.path.abspath(__file__))
29
-
30
- # Nomes dos arquivos de banco conforme sua especificação
31
- PROD_DB_NAME = "Load.db"
32
- TEST_DB_NAME = "Load_teste.db"
33
- TREINAMENTO_DB_NAME = "Load_treinamento.db"
34
-
35
- # Monta URLs SQLite (formato: sqlite:///C:/.../arquivo.db)
36
- DB1_PROD_URL = f"sqlite:///{os.path.join(BASE_DIR, PROD_DB_NAME)}"
37
- DB2_TEST_URL = f"sqlite:///{os.path.join(BASE_DIR, TEST_DB_NAME)}"
38
- DB3_TREINAMENTO_URL = f"sqlite:///{os.path.join(BASE_DIR, TREINAMENTO_DB_NAME)}"
39
-
40
- # ============================
41
- # (Opcional) Uso de .env
42
- # ============================
43
- # Se preferir usar .env futuramente, descomente abaixo e defina:
44
- # DB1_PROD_URL=sqlite:///C:/.../Load.db
45
- # DB2_TEST_URL=sqlite:///C:/.../Load_teste.db
46
- # DB3_TREINAMENTO_URL=sqlite:///C:/.../Load_treinamento.db
47
- #
48
- # from dotenv import load_dotenv
49
- # load_dotenv()
50
- # DB1_PROD_URL = os.getenv("DB1_PROD_URL", DB1_PROD_URL)
51
- # DB2_TEST_URL = os.getenv("DB2_TEST_URL", DB2_TEST_URL)
52
- # DB3_TREINAMENTO_URL = os.getenv("DB3_TREINAMENTO_URL", DB3_TREINAMENTO_URL)
53
-
54
- # ============================
55
- # Catálogo (helpers de UI — opcional)
56
- # ============================
57
- DB_URLS = {
58
- "prod": DB1_PROD_URL,
59
- "test": DB2_TEST_URL,
60
- "treinamento": DB3_TREINAMENTO_URL,
61
- }
62
-
63
- DB_LABELS = {
64
- "prod": "Banco 1 (Produção)",
65
- "test": "Banco 2 (Teste)",
66
- "treinamento": "Banco 3 (Treinamento)",
67
- }
68
-
69
- def list_banks() -> list[str]:
70
- """Lista as chaves de bancos disponíveis (opcional para UI)."""
71
- return list(DB_URLS.keys())
72
-
73
- def bank_label(choice: str) -> str:
74
- """Rótulo amigável para a UI (opcional)."""
75
- return DB_LABELS.get(choice, choice)
76
-
77
- # ============================
78
- # Chaves de sessão
79
- # ============================
80
- SESSION_DB_CHOICE_KEY = "__db_choice__" # "prod" | "test" | "treinamento"
81
- SESSION_DB_ENGINE_KEY = "__db_engine__" # cache de engine por escolha
82
- SESSION_DB_FACTORY_KEY = "__db_session_factory__" # cache de sessionmaker por escolha
83
-
84
- def set_db_choice(choice: str):
85
- """
86
- Define o banco ativo para a sessão do usuário.
87
- choice {"prod", "test", "treinamento"}.
88
- """
89
- choice = (choice or "").strip().lower()
90
- if choice not in DB_URLS:
91
- raise ValueError(f"db_choice inválido. Use uma destas chaves: {list(DB_URLS.keys())}")
92
- st.session_state[SESSION_DB_CHOICE_KEY] = choice
93
- # Ao trocar banco, invalida caches locais
94
- st.session_state.pop(SESSION_DB_ENGINE_KEY, None)
95
- st.session_state.pop(SESSION_DB_FACTORY_KEY, None)
96
-
97
- def current_db_choice() -> str:
98
- """Retorna 'prod' | 'test' | 'treinamento'. Default: 'prod'."""
99
- return st.session_state.get(SESSION_DB_CHOICE_KEY, "prod")
100
-
101
- def _url_for_choice(choice: str) -> str:
102
- return DB_URLS[choice]
103
-
104
- def get_engine():
105
- """
106
- Entrega o engine do banco ATIVO (por sessão). Cria se não existir.
107
- """
108
- choice = current_db_choice()
109
- cached = st.session_state.get(SESSION_DB_ENGINE_KEY)
110
- if cached and getattr(cached, "__db_choice__", None) == choice:
111
- return cached
112
-
113
- url = _url_for_choice(choice)
114
- engine_args = {
115
- "echo": False,
116
- "pool_pre_ping": True,
117
- # check_same_thread=False para permitir uso em múltiplas threads do Streamlit
118
- "connect_args": {"check_same_thread": False} if url.startswith("sqlite") else {},
119
- }
120
-
121
- eng = create_engine(url, **engine_args)
122
- setattr(eng, "__db_choice__", choice)
123
- st.session_state[SESSION_DB_ENGINE_KEY] = eng
124
- return eng
125
-
126
- def get_session_factory():
127
- """
128
- Entrega um sessionmaker vinculado ao engine do banco ATIVO.
129
- """
130
- choice = current_db_choice()
131
- fac = st.session_state.get(SESSION_DB_FACTORY_KEY)
132
- if fac and getattr(fac, "__db_choice__", None) == choice:
133
- return fac
134
-
135
- fac = sessionmaker(bind=get_engine(), autocommit=False, autoflush=False)
136
- setattr(fac, "__db_choice__", choice)
137
- st.session_state[SESSION_DB_FACTORY_KEY] = fac
138
- return fac
139
-
140
- def SessionLocal():
141
- """
142
- Cria uma sessão (sempre no banco ativo).
143
- Uso:
144
- db = SessionLocal()
145
- try:
146
- ...
147
- finally:
148
- db.close()
149
- """
150
- return get_session_factory()()
151
-
152
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ db_router.py — Roteia Engine/SessionLocal para:
4
+ - 'prod' Load.db
5
+ - 'test' Load_teste.db
6
+ - 'treinamento' → Load_treinamento.db
7
+
8
+ Mantém a escolha do usuário em st.session_state (quando disponível).
9
+ Exibe uma API simples esperada pelo banco.py:
10
+
11
+ set_db_choice("prod"|"test"|"treinamento") define o banco ativo
12
+ • current_db_choice() → retorna 'prod' | 'test' | 'treinamento'
13
+ • get_engine() → engine do banco ativo (SQLite)
14
+ get_session_factory() → sessionmaker do banco ativo
15
+ • SessionLocal() sessão no banco ativo
16
+
17
+ Compatibilidade:
18
+ - Se Streamlit não estiver disponível (execução fora do app), usa cache global interno (fallback).
19
+ - Pode ler URLs do .env/Secrets (opcional) para apontar bancos em outro local/driver.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import os
25
+ from typing import Dict, Optional
26
+
27
+ # Streamlit é preferível; mas se não houver (execução fora do app), caímos em fallback
28
+ try:
29
+ import streamlit as st
30
+ _HAS_ST = True
31
+ except Exception:
32
+ _HAS_ST = False
33
+
34
+ from sqlalchemy import create_engine
35
+ from sqlalchemy.orm import sessionmaker
36
+
37
+ # ============================
38
+ # Configuração e caminhos base
39
+ # ============================
40
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
41
+
42
+ # Nomes de arquivos de banco conforme sua especificação (com caixa)
43
+ PROD_DB_NAME = "Load.db"
44
+ TEST_DB_NAME = "Load_teste.db"
45
+ TREINAMENTO_DB_NAME = "Load_treinamento.db"
46
+
47
+ # ============================
48
+ # (Opcional) uso de .env/Secrets
49
+ # ============================
50
+ # Se quiser apontar para outros caminhos/URIs via Secrets do Spaces:
51
+ # DB1_PROD_URL, DB2_TEST_URL, DB3_TREINAMENTO_URL
52
+ # Se não definir, usamos o SQLite no diretório do projeto.
53
+ DB1_PROD_URL = os.getenv("DB1_PROD_URL", f"sqlite:///{os.path.join(BASE_DIR, PROD_DB_NAME)}")
54
+ DB2_TEST_URL = os.getenv("DB2_TEST_URL", f"sqlite:///{os.path.join(BASE_DIR, TEST_DB_NAME)}")
55
+ DB3_TREINAMENTO_URL = os.getenv("DB3_TREINAMENTO_URL", f"sqlite:///{os.path.join(BASE_DIR, TREINAMENTO_DB_NAME)}")
56
+
57
+ DB_URLS: Dict[str, str] = {
58
+ "prod": DB1_PROD_URL,
59
+ "test": DB2_TEST_URL,
60
+ "treinamento": DB3_TREINAMENTO_URL,
61
+ }
62
+
63
+ DB_LABELS: Dict[str, str] = {
64
+ "prod": "Banco 1 (Produção)",
65
+ "test": "Banco 2 (Teste)",
66
+ "treinamento": "Banco 3 (Treinamento)",
67
+ }
68
+
69
+ # ============================
70
+ # (Opcional) normalização de case para SQLite
71
+ # ============================
72
+ # Se você quiser garantir que exista também uma cópia "load.db" (minúsculo)
73
+ # a partir de "Load.db" (maiúsculo) útil se outros módulos esperarem minúsculas:
74
+ _ENABLE_SQLITE_LOWERCASE_ALIAS = False # mude para True se quiser habilitar
75
+
76
+ def _ensure_sqlite_lowercase_alias():
77
+ import shutil
78
+ # Para cada caminho SQLite "Load*.db", garantir um "load*.db"
79
+ for key, url in DB_URLS.items():
80
+ if not url.startswith("sqlite:///"):
81
+ continue
82
+ path = url.replace("sqlite:///", "", 1)
83
+ # Se usar caminho relativo da BASE_DIR, resolva:
84
+ if not os.path.isabs(path):
85
+ path = os.path.join(BASE_DIR, path)
86
+ dirn, fname = os.path.split(path)
87
+ lower_name = fname.lower()
88
+ lower_path = os.path.join(dirn, lower_name)
89
+ if fname != lower_name and os.path.exists(path) and not os.path.exists(lower_path):
90
+ try:
91
+ shutil.copy(path, lower_path)
92
+ except Exception:
93
+ # Se falhar, ignora (não é crítico)
94
+ pass
95
+
96
+ if _ENABLE_SQLITE_LOWERCASE_ALIAS:
97
+ _ensure_sqlite_lowercase_alias()
98
+
99
+ # ============================
100
+ # Helpers de UI
101
+ # ============================
102
+ def list_banks() -> list[str]:
103
+ """Lista as chaves de bancos disponíveis (para UI)."""
104
+ return list(DB_URLS.keys())
105
+
106
+ def bank_label(choice: str) -> str:
107
+ """Rótulo amigável para a UI."""
108
+ return DB_LABELS.get(choice, choice)
109
+
110
+ # ============================
111
+ # Chaves de sessão (ou fallback global)
112
+ # ============================
113
+ SESSION_DB_CHOICE_KEY = "__db_choice__" # "prod" | "test" | "treinamento"
114
+ SESSION_DB_ENGINE_KEY = "__db_engine__" # cache de engine
115
+ SESSION_DB_FACTORY_KEY = "__db_session_factory__" # cache de sessionmaker
116
+
117
+ # Fallback global quando não houver Streamlit
118
+ _GLOBAL_STATE: Dict[str, object] = {
119
+ SESSION_DB_CHOICE_KEY: "prod",
120
+ SESSION_DB_ENGINE_KEY: None,
121
+ SESSION_DB_FACTORY_KEY: None,
122
+ }
123
+
124
+ def _session_get(key: str, default=None):
125
+ if _HAS_ST:
126
+ return st.session_state.get(key, default)
127
+ return _GLOBAL_STATE.get(key, default)
128
+
129
+ def _session_set(key: str, value):
130
+ if _HAS_ST:
131
+ st.session_state[key] = value
132
+ else:
133
+ _GLOBAL_STATE[key] = value
134
+
135
+ def _session_pop(key: str):
136
+ if _HAS_ST:
137
+ st.session_state.pop(key, None)
138
+ else:
139
+ _GLOBAL_STATE.pop(key, None)
140
+
141
+ # ============================
142
+ # Escolha do banco
143
+ # ============================
144
+ def set_db_choice(choice: str):
145
+ """
146
+ Define o banco ativo para a sessão atual.
147
+ choice ∈ {"prod", "test", "treinamento"}.
148
+ """
149
+ choice = (choice or "").strip().lower()
150
+ if choice not in DB_URLS:
151
+ raise ValueError(f"db_choice inválido. Use uma destas chaves: {list(DB_URLS.keys())}")
152
+ _session_set(SESSION_DB_CHOICE_KEY, choice)
153
+ # Ao trocar, invalida caches
154
+ _session_pop(SESSION_DB_ENGINE_KEY)
155
+ _session_pop(SESSION_DB_FACTORY_KEY)
156
+
157
+ def current_db_choice() -> str:
158
+ """Retorna 'prod' | 'test' | 'treinamento' (default: 'prod')."""
159
+ val = _session_get(SESSION_DB_CHOICE_KEY, "prod")
160
+ if val not in DB_URLS:
161
+ val = "prod"
162
+ _session_set(SESSION_DB_CHOICE_KEY, val)
163
+ return val
164
+
165
+ # ============================
166
+ # Engine / Session por ambiente
167
+ # ============================
168
+ def _url_for_choice(choice: str) -> str:
169
+ return DB_URLS[choice]
170
+
171
+ def _engine_args_for_url(url: str) -> dict:
172
+ args = {
173
+ "echo": False,
174
+ "pool_pre_ping": True,
175
+ }
176
+ if url.startswith("sqlite:///"):
177
+ # evita erro em threads do Streamlit
178
+ args["connect_args"] = {"check_same_thread": False}
179
+ return args
180
+
181
+ def get_engine():
182
+ """
183
+ Entrega o engine do banco ATIVO (por sessão). Cria e cacheia se necessário.
184
+ """
185
+ choice = current_db_choice()
186
+ cached = _session_get(SESSION_DB_ENGINE_KEY)
187
+ if cached is not None and getattr(cached, "__db_choice__", None) == choice:
188
+ return cached
189
+
190
+ url = _url_for_choice(choice)
191
+ eng = create_engine(url, **_engine_args_for_url(url))
192
+ setattr(eng, "__db_choice__", choice)
193
+ _session_set(SESSION_DB_ENGINE_KEY, eng)
194
+ return eng
195
+
196
+ def get_session_factory():
197
+ """
198
+ Entrega um sessionmaker vinculado ao engine do banco ATIVO (em cache).
199
+ """
200
+ choice = current_db_choice()
201
+ fac = _session_get(SESSION_DB_FACTORY_KEY)
202
+ if fac is not None and getattr(fac, "__db_choice__", None) == choice:
203
+ return fac
204
+
205
+ fac = sessionmaker(bind=get_engine(), autocommit=False, autoflush=False)
206
+ setattr(fac, "__db_choice__", choice)
207
+ _session_set(SESSION_DB_FACTORY_KEY, fac)
208
+ return fac
209
+
210
+ def SessionLocal():
211
+ """
212
+ Cria uma sessão no banco ATIVO.
213
+ Uso:
214
+ db = SessionLocal()
215
+ try:
216
+ ...
217
+ finally:
218
+ db.close()
219
+ """
220
+ return get_session_factory()()