Roudrigus commited on
Commit
9aabbbe
·
verified ·
1 Parent(s): af3ec70

Update db_router.py

Browse files
Files changed (1) hide show
  1. db_router.py +85 -28
db_router.py CHANGED
@@ -5,22 +5,27 @@ db_router.py — Roteia Engine/SessionLocal para:
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
- Expõe:
10
- • set_db_choice("prod"|"test"|"treinamento")
11
- current_db_choice()
12
- get_engine()
13
- get_session_factory()
14
- SessionLocal()
15
-
16
- Inclui garantia de criação do diretório pai do SQLite.
 
 
 
 
 
17
  Compatível com execução fora do Streamlit (fallback em estado global).
18
  """
19
 
20
  from __future__ import annotations
21
 
22
  import os
23
- from typing import Dict, Optional
24
 
25
  # Streamlit é preferível; mas se não houver (execução fora do app), caímos em fallback
26
  try:
@@ -37,7 +42,7 @@ from sqlalchemy.orm import sessionmaker
37
  # ============================
38
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
39
 
40
- # Nomes de arquivos de banco conforme sua especificação (com caixa)
41
  PROD_DB_NAME = "Load.db"
42
  TEST_DB_NAME = "Load_teste.db"
43
  TREINAMENTO_DB_NAME = "Load_treinamento.db"
@@ -53,10 +58,15 @@ DB_URLS: Dict[str, str] = {
53
  "treinamento": DB3_TREINAMENTO_URL,
54
  }
55
 
 
 
 
 
 
56
  DB_LABELS: Dict[str, str] = {
57
- "prod": "Banco 1 (Produção)",
58
- "test": "Banco 2 (Teste)",
59
- "treinamento": "Banco 3 (Treinamento)",
60
  }
61
 
62
  # ============================
@@ -69,15 +79,17 @@ def _ensure_parent_dir_sqlite(url: str) -> str:
69
  """
70
  if not url or not url.startswith("sqlite"):
71
  return url
72
- path = url.replace("sqlite:///", "", 1)
73
- if path.startswith("//"):
74
- path = path[1:]
 
75
  file_path = os.path.abspath(path)
76
  parent = os.path.dirname(file_path)
77
  try:
78
  os.makedirs(parent, exist_ok=True)
79
  return url
80
  except Exception:
 
81
  home_dir = os.path.join(os.path.expanduser("~"), ".ioirun")
82
  os.makedirs(home_dir, exist_ok=True)
83
  alt = os.path.join(home_dir, os.path.basename(file_path))
@@ -86,10 +98,14 @@ def _ensure_parent_dir_sqlite(url: str) -> str:
86
  # ============================
87
  # Helpers de UI
88
  # ============================
89
- def list_banks() -> list[str]:
90
  """Lista as chaves de bancos disponíveis (para UI)."""
91
  return list(DB_URLS.keys())
92
 
 
 
 
 
93
  def bank_label(choice: str) -> str:
94
  """Rótulo amigável para a UI."""
95
  return DB_LABELS.get(choice, choice)
@@ -102,8 +118,8 @@ SESSION_DB_ENGINE_KEY = "__db_engine__" # cache de engine
102
  SESSION_DB_FACTORY_KEY = "__db_session_factory__" # cache de sessionmaker
103
 
104
  # Fallback global quando não houver Streamlit
105
- _GLOBAL_STATE: Dict[str, object] = {
106
- SESSION_DB_CHOICE_KEY: "prod",
107
  SESSION_DB_ENGINE_KEY: None,
108
  SESSION_DB_FACTORY_KEY: None,
109
  }
@@ -125,28 +141,69 @@ def _session_pop(key: str):
125
  else:
126
  _GLOBAL_STATE.pop(key, None)
127
 
 
 
 
 
 
 
 
 
 
 
 
128
  # ============================
129
  # Escolha do banco
130
  # ============================
131
  def set_db_choice(choice: str):
132
  """
133
  Define o banco ativo para a sessão atual.
134
- choice ∈ {"prod", "test", "treinamento"}.
 
135
  """
136
- choice = (choice or "").strip().lower()
137
- if choice not in DB_URLS:
138
- raise ValueError(f"db_choice inválido. Use uma destas chaves: {list(DB_URLS.keys())}")
 
 
 
 
 
 
 
139
  _session_set(SESSION_DB_CHOICE_KEY, choice)
 
 
140
  # Ao trocar, invalida caches
141
  _session_pop(SESSION_DB_ENGINE_KEY)
142
  _session_pop(SESSION_DB_FACTORY_KEY)
143
 
 
 
 
 
144
  def current_db_choice() -> str:
145
- """Retorna 'prod' | 'test' | 'treinamento' (default: 'prod')."""
146
- val = _session_get(SESSION_DB_CHOICE_KEY, "prod")
147
- if val not in DB_URLS:
 
 
 
 
 
 
 
 
 
 
 
148
  val = "prod"
149
  _session_set(SESSION_DB_CHOICE_KEY, val)
 
 
 
 
 
150
  return val
151
 
152
  # ============================
@@ -175,7 +232,7 @@ def get_engine():
175
  return cached
176
 
177
  url = _url_for_choice(choice)
178
- url = _ensure_parent_dir_sqlite(url) # ⬅️ garante diretório pai
179
 
180
  eng = create_engine(url, **_engine_args_for_url(url))
181
  setattr(eng, "__db_choice__", choice)
 
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) e em variável
9
+ de ambiente (DB_CHOICE) para persistir entre reruns/contexts.
10
+
11
+ APIs expostas (compatíveis com o app):
12
+ get_available_choices() -> list[str]
13
+ set_current_db_choice(choice: str) -> None
14
+ set_db_choice(choice: str) -> None
15
+ • current_db_choice() -> str
16
+ bank_label(choice: str) -> str
17
+ • get_engine() -> sqlalchemy.Engine
18
+ • get_session_factory() -> sqlalchemy.orm.sessionmaker
19
+ • SessionLocal() -> sqlalchemy.orm.Session
20
+
21
+ Inclui garantia de criação do diretório pai do SQLite com fallback para ~/.ioirun.
22
  Compatível com execução fora do Streamlit (fallback em estado global).
23
  """
24
 
25
  from __future__ import annotations
26
 
27
  import os
28
+ from typing import Dict, Optional, Any
29
 
30
  # Streamlit é preferível; mas se não houver (execução fora do app), caímos em fallback
31
  try:
 
42
  # ============================
43
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
44
 
45
+ # Nomes de arquivos de banco conforme sua especificação
46
  PROD_DB_NAME = "Load.db"
47
  TEST_DB_NAME = "Load_teste.db"
48
  TREINAMENTO_DB_NAME = "Load_treinamento.db"
 
58
  "treinamento": DB3_TREINAMENTO_URL,
59
  }
60
 
61
+ # Aliases aceitos (ex.: "train" → "treinamento")
62
+ CHOICE_ALIASES: Dict[str, str] = {
63
+ "train": "treinamento",
64
+ }
65
+
66
  DB_LABELS: Dict[str, str] = {
67
+ "prod": "🟢 Produção",
68
+ "test": "🔴 Teste",
69
+ "treinamento": "🟡 Treinamento",
70
  }
71
 
72
  # ============================
 
79
  """
80
  if not url or not url.startswith("sqlite"):
81
  return url
82
+ # Extrai caminho local do SQLite (sqlite:////abs/path ou sqlite:///rel/path)
83
+ prefix = "sqlite:///"
84
+ path = url[len(prefix):] if url.startswith(prefix) else url
85
+ # Normaliza e cria parent
86
  file_path = os.path.abspath(path)
87
  parent = os.path.dirname(file_path)
88
  try:
89
  os.makedirs(parent, exist_ok=True)
90
  return url
91
  except Exception:
92
+ # Fallback para HOME gravável
93
  home_dir = os.path.join(os.path.expanduser("~"), ".ioirun")
94
  os.makedirs(home_dir, exist_ok=True)
95
  alt = os.path.join(home_dir, os.path.basename(file_path))
 
98
  # ============================
99
  # Helpers de UI
100
  # ============================
101
+ def get_available_choices() -> list[str]:
102
  """Lista as chaves de bancos disponíveis (para UI)."""
103
  return list(DB_URLS.keys())
104
 
105
+ def list_banks() -> list[str]:
106
+ """Compat anterior — alias de get_available_choices()."""
107
+ return get_available_choices()
108
+
109
  def bank_label(choice: str) -> str:
110
  """Rótulo amigável para a UI."""
111
  return DB_LABELS.get(choice, choice)
 
118
  SESSION_DB_FACTORY_KEY = "__db_session_factory__" # cache de sessionmaker
119
 
120
  # Fallback global quando não houver Streamlit
121
+ _GLOBAL_STATE: Dict[str, Any] = {
122
+ SESSION_DB_CHOICE_KEY: os.getenv("DB_CHOICE", "prod"),
123
  SESSION_DB_ENGINE_KEY: None,
124
  SESSION_DB_FACTORY_KEY: None,
125
  }
 
141
  else:
142
  _GLOBAL_STATE.pop(key, None)
143
 
144
+ # ============================
145
+ # Normalização da escolha
146
+ # ============================
147
+ def _normalize_choice(raw: Optional[str]) -> str:
148
+ val = (raw or "").strip().lower()
149
+ if val in CHOICE_ALIASES:
150
+ val = CHOICE_ALIASES[val]
151
+ if val not in DB_URLS:
152
+ val = "prod"
153
+ return val
154
+
155
  # ============================
156
  # Escolha do banco
157
  # ============================
158
  def set_db_choice(choice: str):
159
  """
160
  Define o banco ativo para a sessão atual.
161
+ choice ∈ {"prod", "test", "treinamento"} (aceita alias "train" → "treinamento").
162
+ Invalida caches de engine/session e atualiza ENV (DB_CHOICE).
163
  """
164
+ choice = _normalize_choice(choice)
165
+
166
+ # Se houver engine cacheado, faça dispose antes de trocar
167
+ cached_engine = _session_get(SESSION_DB_ENGINE_KEY)
168
+ if cached_engine is not None:
169
+ try:
170
+ cached_engine.dispose()
171
+ except Exception:
172
+ pass
173
+
174
  _session_set(SESSION_DB_CHOICE_KEY, choice)
175
+ os.environ["DB_CHOICE"] = choice # permite que outros módulos leiam
176
+
177
  # Ao trocar, invalida caches
178
  _session_pop(SESSION_DB_ENGINE_KEY)
179
  _session_pop(SESSION_DB_FACTORY_KEY)
180
 
181
+ def set_current_db_choice(choice: str) -> None:
182
+ """Alias compatível com app: set_current_db_choice(choice)."""
183
+ set_db_choice(choice)
184
+
185
  def current_db_choice() -> str:
186
+ """
187
+ Retorna 'prod' | 'test' | 'treinamento' (default: 'prod').
188
+ Prioriza session_state; se ausente, lê DB_CHOICE do ambiente.
189
+ """
190
+ # 1) Session
191
+ val = _session_get(SESSION_DB_CHOICE_KEY)
192
+ # 2) ENV (permite seleção por URL/externo)
193
+ if val is None:
194
+ env_val = os.getenv("DB_CHOICE")
195
+ if env_val:
196
+ val = _normalize_choice(env_val)
197
+ _session_set(SESSION_DB_CHOICE_KEY, val)
198
+ # 3) Default
199
+ if val is None:
200
  val = "prod"
201
  _session_set(SESSION_DB_CHOICE_KEY, val)
202
+
203
+ # Sanitize
204
+ val = _normalize_choice(val)
205
+ if val != _session_get(SESSION_DB_CHOICE_KEY):
206
+ _session_set(SESSION_DB_CHOICE_KEY, val)
207
  return val
208
 
209
  # ============================
 
232
  return cached
233
 
234
  url = _url_for_choice(choice)
235
+ url = _ensure_parent_dir_sqlite(url) # ⬅️ garante diretório pai se for SQLite
236
 
237
  eng = create_engine(url, **_engine_args_for_url(url))
238
  setattr(eng, "__db_choice__", choice)