from datetime import datetime from sqlalchemy import Boolean, DateTime, ForeignKey, String, Text, func from sqlalchemy.orm import Mapped, mapped_column, relationship from app.core.database import Base from app.models.associations import augmented_part_imported_parts from app.models.imported_part import ImportedPart from app.models.manufacturer import Manufacturer from app.models.part_colour import PartColour from app.models.part_type import PartType from app.models.scope_model import ScopeModel from app.models.sub_part_type import SubPartType class AugmentedPart(Base): __tablename__ = "augmented_parts" id: Mapped[int] = mapped_column(primary_key=True) sku: Mapped[str | None] = mapped_column( String, nullable=True, unique=True, default=None ) description: Mapped[str] = mapped_column(String, nullable=False) colour_id: Mapped[int] = mapped_column( ForeignKey("part_colours.id", ondelete="RESTRICT"), nullable=False ) manufacturer_id: Mapped[int] = mapped_column( ForeignKey("manufacturers.id", ondelete="RESTRICT"), nullable=False ) scope_model_id: Mapped[int | None] = mapped_column( ForeignKey("scope_models.id", ondelete="SET NULL"), nullable=True, default=None ) part_type_id: Mapped[int] = mapped_column( ForeignKey("part_types.id", ondelete="RESTRICT"), nullable=False ) sub_part_type_id: Mapped[int | None] = mapped_column( ForeignKey("sub_part_types.id", ondelete="SET NULL"), nullable=True, default=None, ) # Set instead of deleting the row when the last backing ImportedPart is # removed, so fit/attachment-point history is preserved. Hidden from list # views by default. archived: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) # linked imported parts — not eagerly loaded; used by sync service only imported_parts: Mapped[list[ImportedPart]] = relationship( secondary=augmented_part_imported_parts, lazy="select" ) colour: Mapped[PartColour] = relationship(lazy="selectin") manufacturer: Mapped[Manufacturer] = relationship(lazy="selectin") scope_model: Mapped[ScopeModel | None] = relationship(lazy="selectin") part_type: Mapped[PartType] = relationship(lazy="selectin") sub_part_type: Mapped[SubPartType | None] = relationship(lazy="selectin") notes: Mapped[list["AugmentedPartNote"]] = relationship( back_populates="augmented_part", lazy="selectin", order_by="AugmentedPartNote.created_at", cascade="all, delete-orphan", ) class AugmentedPartNote(Base): __tablename__ = "augmented_part_notes" id: Mapped[int] = mapped_column(primary_key=True) augmented_part_id: Mapped[int] = mapped_column( ForeignKey("augmented_parts.id", ondelete="CASCADE"), nullable=False ) user_id: Mapped[int | None] = mapped_column( ForeignKey("users.id", ondelete="SET NULL"), nullable=True ) # Denormalized snapshot of who wrote the note (mirrors FitNote.username), so # the note stays attributable even if the user is later deleted. username: Mapped[str] = mapped_column(String(255), nullable=False) body: Mapped[str] = mapped_column(Text, nullable=False) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now() ) augmented_part: Mapped[AugmentedPart] = relationship(back_populates="notes")