From 25c8058bae502f9215c2fafeba9717b2c77a6367 Mon Sep 17 00:00:00 2001 From: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com> Date: Tue, 1 Apr 2025 15:34:01 -0400 Subject: [PATCH] fix: remove db creation at start and sync migrations and models --- scripts/__init__.py | 0 scripts/provision_db.py | 18 ++++++++++++++++++ src/db.py | 18 ++++++------------ src/main.py | 29 +++++++++++++++++++++++++++-- src/models.py | 16 ++++++++++++++++ 5 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 scripts/__init__.py create mode 100644 scripts/provision_db.py diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/scripts/provision_db.py b/scripts/provision_db.py new file mode 100644 index 00000000..3530e5a5 --- /dev/null +++ b/scripts/provision_db.py @@ -0,0 +1,18 @@ +# honcho/scripts/provision_db.py +import os +import sys + +# Add the project root to the path +# This assumes the script is run from the scripts directory +project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +sys.path.insert(0, project_root) + +## First import the models to register them with Base +from src import ( + models, +) # This registers all models with Base Now you can import from src +from src.db import scaffold_db + +if __name__ == "__main__": + scaffold_db() + print("Database created") diff --git a/src/db.py b/src/db.py index 43d6de62..c3498ba1 100644 --- a/src/db.py +++ b/src/db.py @@ -1,7 +1,5 @@ import os -from alembic import command -from alembic.config import Config from dotenv import load_dotenv from sqlalchemy import MetaData, create_engine, inspect from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine @@ -13,11 +11,6 @@ connect_args = { "prepare_threshold": None, } -# if ( -# os.environ["DATABASE_TYPE"] == "sqlite" -# ): # https://fastapi.tiangolo.com/tutorial/sql-databases/#note -# connect_args = {"check_same_thread": False} - engine = create_async_engine( os.environ["CONNECTION_URI"], connect_args=connect_args, @@ -48,6 +41,12 @@ def scaffold_db(): """use a sync engine for scaffolding the database. ddl operations are unavailable with async engines """ + + # Debug: Print all tables that should be created + print("Tables defined in Base.metadata:") + for table in Base.metadata.sorted_tables: + print(f" - {table.name}") + # Create engine engine = create_engine( os.environ["CONNECTION_URI"], @@ -67,8 +66,3 @@ def scaffold_db(): # Clean up engine.dispose() - - # Run Alembic migrations regardless - print("Running database migrations...") - alembic_cfg = Config("alembic.ini") - command.upgrade(alembic_cfg, "head") diff --git a/src/main.py b/src/main.py index 3233c046..60f39695 100644 --- a/src/main.py +++ b/src/main.py @@ -23,9 +23,35 @@ from src.routers import ( from .db import engine, scaffold_db + +def get_log_level(env_var="LOG_LEVEL", default="INFO"): + """ + Convert log level string from environment variable to logging module constant. + + Args: + env_var: Name of the environment variable to check + default: Default log level if environment variable is not set + + Returns: + int: The logging level constant (e.g., logging.INFO) + """ + log_level_str = os.getenv(env_var, default).upper() + + log_levels = { + "CRITICAL": logging.CRITICAL, # 50 + "ERROR": logging.ERROR, # 40 + "WARNING": logging.WARNING, # 30 + "INFO": logging.INFO, # 20 + "DEBUG": logging.DEBUG, # 10 + "NOTSET": logging.NOTSET, # 0 + } + + return log_levels.get(log_level_str, logging.INFO) + + # Configure logging logging.basicConfig( - level=logging.INFO, + level=get_log_level(), format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", ) logger = logging.getLogger(__name__) @@ -52,7 +78,6 @@ if SENTRY_ENABLED: @asynccontextmanager async def lifespan(app: FastAPI): - scaffold_db() # Scaffold Database on Startup yield await engine.dispose() diff --git a/src/models.py b/src/models.py index e78dfd0d..8bd53f97 100644 --- a/src/models.py +++ b/src/models.py @@ -10,7 +10,9 @@ from sqlalchemy import ( DateTime, ForeignKey, Identity, + Index, UniqueConstraint, + text, ) from sqlalchemy.dialects.postgresql import JSONB, TEXT from sqlalchemy.orm import Mapped, mapped_column, relationship @@ -66,6 +68,7 @@ class User(Base): CheckConstraint("length(public_id) = 21", name="public_id_length"), CheckConstraint("length(name) <= 512", name="name_length"), CheckConstraint("public_id ~ '^[A-Za-z0-9_-]+$'", name="public_id_format"), + Index("idx_users_app_lookup", "app_id", "public_id"), ) def __repr__(self) -> str: @@ -92,6 +95,7 @@ class Session(Base): __table_args__ = ( CheckConstraint("length(public_id) = 21", name="public_id_length"), CheckConstraint("public_id ~ '^[A-Za-z0-9_-]+$'", name="public_id_format"), + Index("idx_sessions_user_lookup", "user_id", "public_id"), ) def __repr__(self) -> str: @@ -123,6 +127,12 @@ class Message(Base): CheckConstraint("length(public_id) = 21", name="public_id_length"), CheckConstraint("public_id ~ '^[A-Za-z0-9_-]+$'", name="public_id_format"), CheckConstraint("length(content) <= 65535", name="content_length"), + Index( + "idx_messages_session_lookup", + "session_id", + "id", + postgresql_include=["public_id", "is_user", "created_at"], + ), ) def __repr__(self) -> str: @@ -156,6 +166,12 @@ class Metamessage(Base): CheckConstraint( "length(metamessage_type) <= 512", name="metamessage_type_length" ), + Index( + "idx_metamessages_lookup", + "metamessage_type", + text("id DESC"), + postgresql_include=["public_id", "message_id", "created_at"], + ), ) def __repr__(self) -> str: