Files
2026-08-17 22:19:28 -03:00

90 lines
3.1 KiB
Python

import enum
from datetime import datetime
from sqlalchemy import DateTime, Enum, ForeignKey, String, Text, UniqueConstraint, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.core.database import Base
class FitLabel(enum.StrEnum):
POSSIBLE = "possible"
EXACT = "exact"
NONE = "none"
class Fit(Base):
"""A candidate or confirmed fit between two attachment points on different parts."""
__tablename__ = "fits"
__table_args__ = (
UniqueConstraint("attachment_point_a_id", "attachment_point_b_id"),
)
id: Mapped[int] = mapped_column(primary_key=True)
# Canonical ordering (attachment_point_a_id < attachment_point_b_id) is enforced
# by app.crud.fit so an unordered pair only ever has one row.
attachment_point_a_id: Mapped[int] = mapped_column(
ForeignKey("attachment_points.id", ondelete="CASCADE"), nullable=False
)
attachment_point_b_id: Mapped[int] = mapped_column(
ForeignKey("attachment_points.id", ondelete="CASCADE"), nullable=False
)
# Denormalized from the attachment points' augmented_part_id, for querying
# "fits for this part" without joining through attachment_points.
augmented_part_a_id: Mapped[int] = mapped_column(
ForeignKey("augmented_parts.id", ondelete="CASCADE"), nullable=False
)
augmented_part_b_id: Mapped[int] = mapped_column(
ForeignKey("augmented_parts.id", ondelete="CASCADE"), nullable=False
)
label: Mapped[FitLabel] = mapped_column(
Enum(FitLabel, values_callable=lambda enum_cls: [e.value for e in enum_cls]),
nullable=False,
default=FitLabel.POSSIBLE,
)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now()
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)
attachment_point_a = relationship(
"AttachmentPoint", foreign_keys=[attachment_point_a_id], lazy="selectin"
)
attachment_point_b = relationship(
"AttachmentPoint", foreign_keys=[attachment_point_b_id], lazy="selectin"
)
notes: Mapped[list["FitNote"]] = relationship(
back_populates="fit",
lazy="selectin",
order_by="FitNote.created_at",
cascade="all, delete-orphan",
)
class FitNote(Base):
__tablename__ = "fit_notes"
id: Mapped[int] = mapped_column(primary_key=True)
fit_id: Mapped[int] = mapped_column(
ForeignKey("fits.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 AuditLog.user_email),
# 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()
)
fit: Mapped[Fit] = relationship(back_populates="notes")