You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
orquestrador/app/db/bootstrap.py

151 lines
4.9 KiB
Python

"""
Rotina dedicada de bootstrap de banco de dados.
Cria tabelas e executa seed inicial de forma explicita, fora do startup do app.
"""
import json
from datetime import UTC, datetime
from pathlib import Path
from sqlalchemy import inspect, text
from app.core.settings import settings
from app.db.database import Base, engine
from app.db.mock_database import MockBase, mock_engine
from app.db.models import Tool
from app.db.mock_models import (
ConversationTurn,
Customer,
IntegrationDelivery,
IntegrationRoute,
Order,
RentalContract,
RentalPayment,
RentalVehicle,
ReviewSchedule,
Vehicle,
)
from app.db.mock_seed import seed_mock_data
from app.db.tool_seed import seed_tools
from shared.contracts import (
ToolRuntimePublicationManifest,
get_generated_tool_publication_manifest_path,
get_generated_tools_runtime_dir,
)
_PROJECT_ROOT = Path(__file__).resolve().parents[2]
def _ensure_generated_tools_runtime_package() -> Path:
package_dir = get_generated_tools_runtime_dir(_PROJECT_ROOT)
package_dir.mkdir(parents=True, exist_ok=True)
init_file = package_dir / "__init__.py"
if not init_file.exists():
init_file.write_text(
"\"\"\"Isolated runtime package for admin-governed generated tools.\"\"\"\\n",
encoding="utf-8",
)
manifest_path = get_generated_tool_publication_manifest_path(_PROJECT_ROOT)
if not manifest_path.exists():
manifest = ToolRuntimePublicationManifest(
emitted_at=datetime.now(UTC),
publications=(),
)
manifest_path.write_text(
json.dumps(manifest.model_dump(mode="json"), ensure_ascii=True, indent=2, sort_keys=True),
encoding="utf-8",
)
return package_dir
def _ensure_mock_schema_evolution() -> None:
inspector = inspect(mock_engine)
table_names = set(inspector.get_table_names())
if "users" in table_names:
user_columns = {column["name"] for column in inspector.get_columns("users")}
if "email" not in user_columns:
with mock_engine.begin() as connection:
connection.execute(text("ALTER TABLE users ADD COLUMN email VARCHAR(255)"))
if "integration_deliveries" in table_names:
delivery_columns = {column["name"] for column in inspector.get_columns("integration_deliveries")}
statements: list[str] = []
if "recipient_email" not in delivery_columns:
statements.append("ALTER TABLE integration_deliveries ADD COLUMN recipient_email VARCHAR(255)")
if "recipient_name" not in delivery_columns:
statements.append("ALTER TABLE integration_deliveries ADD COLUMN recipient_name VARCHAR(120)")
if statements:
with mock_engine.begin() as connection:
for statement in statements:
connection.execute(text(statement))
def bootstrap_databases(
*,
run_tools_seed: bool | None = None,
run_mock_seed: bool | None = None,
) -> None:
"""Cria tabelas e executa seed inicial em ambos os bancos."""
print("Inicializando bancos...")
failures: list[str] = []
try:
generated_tools_dir = _ensure_generated_tools_runtime_package()
print(f"Diretorio isolado de tools geradas pronto em {generated_tools_dir}.")
except Exception as exc:
print(f"Aviso: falha ao preparar diretorio isolado de tools geradas: {exc}")
failures.append(f"generated_tools={exc}")
should_seed_tools = settings.auto_seed_tools if run_tools_seed is None else bool(run_tools_seed)
should_seed_mock = (
settings.auto_seed_mock and settings.mock_seed_enabled
if run_mock_seed is None
else bool(run_mock_seed)
)
try:
print("Criando tabelas MySQL (tools)...")
Base.metadata.create_all(bind=engine)
if should_seed_tools:
print("Populando tools iniciais...")
seed_tools()
else:
print("Seed de tools desabilitada por configuracao.")
print("MySQL tools OK.")
except Exception as exc:
print(f"Aviso: falha no MySQL (tools): {exc}")
failures.append(f"tools={exc}")
try:
print("Criando tabelas MySQL (dados ficticios)...")
MockBase.metadata.create_all(bind=mock_engine)
_ensure_mock_schema_evolution()
if should_seed_mock:
print("Populando dados ficticios iniciais...")
seed_mock_data()
else:
print("Seed mock desabilitada por configuracao.")
print("MySQL mock OK.")
except Exception as exc:
print(f"Aviso: falha no MySQL mock: {exc}")
failures.append(f"mock={exc}")
if failures:
raise RuntimeError(
"Falha ao inicializar bancos do orquestrador: " + " | ".join(failures)
)
print("Bancos inicializados com sucesso!")
def main() -> None:
"""Executa o bootstrap dedicado quando chamado via modulo."""
bootstrap_databases()
if __name__ == "__main__":
main()