import uuid from typing import TYPE_CHECKING from sqlalchemy import ForeignKey, JSON, Integer, String, Text, Boolean from sqlalchemy import Uuid as UUID from sqlalchemy.orm import Mapped, mapped_column, relationship from src.models.base import Base, TimestampMixin, UUIDPrimaryKeyMixin if TYPE_CHECKING: from src.models.project import Project from src.models.tool_type import ToolType from src.models.user import User class ConfigProfile(UUIDPrimaryKeyMixin, TimestampMixin, Base): __tablename__ = "config_profiles" user_id: Mapped[uuid.UUID] = mapped_column( UUID(), ForeignKey("users.id", ondelete="CASCADE"), nullable=False ) name: Mapped[str] = mapped_column(String(255), nullable=False) description: Mapped[str | None] = mapped_column(Text, nullable=True) project_id: Mapped[uuid.UUID | None] = mapped_column( UUID(), ForeignKey("projects.id", ondelete="CASCADE"), nullable=True ) tool_type_id: Mapped[uuid.UUID | None] = mapped_column( UUID(), ForeignKey("tool_types.id", ondelete="CASCADE"), nullable=True ) env_vars: Mapped[dict] = mapped_column( JSON, default=dict, nullable=False ) # {"VAR_NAME": "value", ...} runtime_hints: Mapped[dict] = mapped_column( JSON, default=dict, nullable=False ) # {"start_command": "...", "working_dir": "...", ...} mounts: Mapped[list] = mapped_column( JSON, default=list, nullable=False ) # [{"target": "/path", "mode": "rw", "files": {"rel/path": "content"}}, ...] files: Mapped[dict] = mapped_column( JSON, default=dict, nullable=False ) # {"rel/path": "content", ...} git_mounts: Mapped[list] = mapped_column( JSON, default=list, nullable=False ) # [{"remote_url": "https://github.com/user/repo.git", "source_path": ".", "target_path": "/path", "branch": "main"}, ...] is_default: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) user: Mapped["User"] = relationship() project: Mapped["Project | None"] = relationship() tool_type: Mapped["ToolType | None"] = relationship() includes: Mapped[list["ConfigProfileInclude"]] = relationship( "ConfigProfileInclude", foreign_keys="ConfigProfileInclude.profile_id", order_by="ConfigProfileInclude.order_index", cascade="all, delete-orphan", ) class ConfigProfileInclude(UUIDPrimaryKeyMixin, TimestampMixin, Base): __tablename__ = "config_profile_includes" profile_id: Mapped[uuid.UUID] = mapped_column( UUID(), ForeignKey("config_profiles.id", ondelete="CASCADE"), nullable=False ) included_profile_id: Mapped[uuid.UUID] = mapped_column( UUID(), ForeignKey("config_profiles.id", ondelete="CASCADE"), nullable=False ) order_index: Mapped[int] = mapped_column(Integer, nullable=False, default=0) profile: Mapped["ConfigProfile"] = relationship( "ConfigProfile", foreign_keys=[profile_id], back_populates="includes", ) included_profile: Mapped["ConfigProfile"] = relationship( "ConfigProfile", foreign_keys=[included_profile_id], )