Add the core labeling data model (label sets, ad-hoc Main->Sub class hierarchy, frames, bounding-box labels, per-frame/per-set label status) behind a FastAPI app, with SQLite as the default swappable DATABASE_URL. Multi-user support is attribution-only for now (get-or-create by username, no login flow yet). Dataset versioning/promotion is intentionally deferred - it needs its own design pass around snapshot semantics. Each test gets a fully isolated app+DB via create_app(database_url=...) rather than relying on process-global state. 13/13 tests pass; also verified live end-to-end against a running uvicorn instance. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
41 lines
1.4 KiB
Python
41 lines
1.4 KiB
Python
"""Database engine/session setup.
|
|
|
|
Each `Database` instance owns its own engine and session factory, so a
|
|
production run and a test can each get a fully isolated database instead of
|
|
sharing process-global state (see `main.create_app`).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from typing import Iterator, Optional
|
|
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import Session, sessionmaker
|
|
|
|
from .models import Base
|
|
|
|
DEFAULT_DATABASE_URL = "sqlite:///./labeling.db"
|
|
|
|
|
|
class Database:
|
|
def __init__(self, database_url: Optional[str] = None):
|
|
self.url = database_url or os.environ.get("DATABASE_URL", DEFAULT_DATABASE_URL)
|
|
connect_args = {"check_same_thread": False} if self.url.startswith("sqlite") else {}
|
|
self.engine = create_engine(self.url, connect_args=connect_args)
|
|
self.session_factory = sessionmaker(autocommit=False, autoflush=False, bind=self.engine)
|
|
|
|
def init_models(self) -> None:
|
|
Base.metadata.create_all(bind=self.engine)
|
|
|
|
def get_session(self) -> Iterator[Session]:
|
|
session = self.session_factory()
|
|
try:
|
|
yield session
|
|
finally:
|
|
session.close()
|
|
|
|
|
|
def get_db() -> Iterator[Session]:
|
|
"""Marker dependency; `create_app` overrides this with a real `Database.get_session`."""
|
|
raise NotImplementedError("get_db must be overridden via app.dependency_overrides")
|