""" Database Models and Connection ============================== SQLite database schema for feature storage using SQLAlchemy. """ from pathlib import Path from typing import Optional from sqlalchemy import Boolean, Column, Integer, String, Text, create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, Session from sqlalchemy.types import JSON Base = declarative_base() class Feature(Base): """Feature model representing a test case/feature to implement.""" __tablename__ = "features" id = Column(Integer, primary_key=True, index=True) priority = Column(Integer, nullable=False, default=999, index=True) category = Column(String(100), nullable=False) name = Column(String(255), nullable=False) description = Column(Text, nullable=False) steps = Column(JSON, nullable=False) # Stored as JSON array passes = Column(Boolean, default=False, index=True) def to_dict(self) -> dict: """Convert feature to dictionary for JSON serialization.""" return { "id": self.id, "priority": self.priority, "category": self.category, "name": self.name, "description": self.description, "steps": self.steps, "passes": self.passes, } def get_database_path(project_dir: Path) -> Path: """Return the path to the SQLite database for a project.""" return project_dir / "features.db" def get_database_url(project_dir: Path) -> str: """Return the SQLAlchemy database URL for a project. Uses POSIX-style paths (forward slashes) for cross-platform compatibility. """ db_path = get_database_path(project_dir) return f"sqlite:///{db_path.as_posix()}" def create_database(project_dir: Path) -> tuple: """ Create database and return engine + session maker. Args: project_dir: Directory containing the project Returns: Tuple of (engine, SessionLocal) """ db_url = get_database_url(project_dir) engine = create_engine(db_url, connect_args={"check_same_thread": False}) Base.metadata.create_all(bind=engine) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) return engine, SessionLocal # Global session maker - will be set when server starts _session_maker: Optional[sessionmaker] = None def set_session_maker(session_maker: sessionmaker) -> None: """Set the global session maker.""" global _session_maker _session_maker = session_maker def get_db() -> Session: """ Dependency for FastAPI to get database session. Yields a database session and ensures it's closed after use. """ if _session_maker is None: raise RuntimeError("Database not initialized. Call set_session_maker first.") db = _session_maker() try: yield db finally: db.close()