90 lines
3.8 KiB
Python
90 lines
3.8 KiB
Python
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,
|
|
)
|
|
# "##.##-##.##" mm, derived from ImportedPart.UACDiameterRangemm by the sync
|
|
# service. Read-only — never set outside app/services/augmented_part_sync.py.
|
|
uac_diameter_range_mm: Mapped[str | None] = mapped_column(
|
|
String, 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)
|
|
on_website: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
|
preferred_part: 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")
|