chore: initial project scaffold and gitignore

This commit is contained in:
2026-04-03 07:33:32 +00:00
commit 3e3acbd13d
41 changed files with 3658 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
"""Alembic helpers for PortfolioHub database migrations."""
from __future__ import annotations
from pathlib import Path
from alembic import command
from alembic.config import Config
import psycopg
from app.core.config import Settings
def get_alembic_config(settings: Settings) -> Config:
project_root = Path(__file__).resolve().parents[2]
config = Config(str(project_root / "alembic.ini"))
config.set_main_option("script_location", str(project_root / "migrations"))
config.set_main_option("sqlalchemy.url", settings.active_database_url.replace("%", "%%"))
return config
def upgrade_to_head(settings: Settings) -> None:
command.upgrade(get_alembic_config(settings), "head")
def bootstrap_or_upgrade_to_head(settings: Settings) -> str:
if _has_alembic_version_table(settings):
upgrade_to_head(settings)
return "head"
if _has_legacy_portfolio_schema(settings):
command.stamp(get_alembic_config(settings), "head")
return "head"
upgrade_to_head(settings)
return "head"
def current_revision(settings: Settings) -> str | None:
output: list[str] = []
def capture(message: str, *_args, **_kwargs) -> None:
if message.strip():
output.append(message.strip())
config = get_alembic_config(settings)
command.current(config, verbose=False)
return output[-1] if output else None
def _connect(settings: Settings) -> psycopg.Connection:
return psycopg.connect(
host=settings.active_db_host,
port=settings.active_db_port,
dbname=settings.active_db_name,
user=settings.active_db_user,
password=settings.active_db_password,
sslmode=settings.active_db_sslmode,
connect_timeout=settings.active_db_connect_timeout_sec,
application_name=settings.active_db_application_name,
autocommit=True,
)
def _has_alembic_version_table(settings: Settings) -> bool:
with _connect(settings) as conn:
with conn.cursor() as cur:
cur.execute("SELECT to_regclass('public.alembic_version')")
return cur.fetchone()[0] is not None
def _has_legacy_portfolio_schema(settings: Settings) -> bool:
required_tables = {"strategy_sets", "portfolios", "portfolio_ledger"}
with _connect(settings) as conn:
with conn.cursor() as cur:
cur.execute(
"SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND tablename = ANY(%s)",
(list(required_tables),),
)
found = {row[0] for row in cur.fetchall()}
return found == required_tables