Adding fits

This commit is contained in:
Ian Roddis committed 2026-07-14 15:24:05 -03:00
1 parent 8185f1f8b3
commit ab39191af5
19 files changed
+1253 -1

No files matched your search

+98
View File
@@ -0,0 +1,98 @@
from sqlalchemy import or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.attachment_point import AttachmentPoint
from app.models.fit import Fit, FitLabel
# "updated_at" is included because it's a server-side onupdate default: after an
# UPDATE, SQLAlchemy doesn't know its new value unless it's named explicitly here
# too, and access to an unrefreshed attribute would otherwise trigger a lazy load
# outside of an awaited context (MissingGreenlet) during response serialization.
_RELATIONSHIPS = ["attachment_point_a", "attachment_point_b", "notes", "updated_at"]
def _ordered(
ap_a: AttachmentPoint, ap_b: AttachmentPoint
) -> tuple[AttachmentPoint, AttachmentPoint]:
"""Canonicalize so attachment_point_a.id < attachment_point_b.id.
Keeps a single row per unordered pair of attachment points.
"""
return (ap_a, ap_b) if ap_a.id < ap_b.id else (ap_b, ap_a)
async def get(db: AsyncSession, fit_id: int) -> Fit | None:
return await db.get(Fit, fit_id)
async def get_by_attachment_points(
db: AsyncSession, ap_a: AttachmentPoint, ap_b: AttachmentPoint
) -> Fit | None:
first, second = _ordered(ap_a, ap_b)
result = await db.execute(
select(Fit).where(
Fit.attachment_point_a_id == first.id,
Fit.attachment_point_b_id == second.id,
)
)
return result.scalar_one_or_none()
async def list_for_part(db: AsyncSession, augmented_part_id: int) -> list[Fit]:
result = await db.execute(
select(Fit)
.where(
or_(
Fit.augmented_part_a_id == augmented_part_id,
Fit.augmented_part_b_id == augmented_part_id,
)
)
.order_by(Fit.id)
)
return list(result.scalars().all())
async def list_all(
db: AsyncSession,
skip: int = 0,
limit: int = 100,
label: FitLabel | None = None,
) -> list[Fit]:
q = select(Fit)
if label is not None:
q = q.where(Fit.label == label)
result = await db.execute(q.order_by(Fit.id).offset(skip).limit(limit))
return list(result.scalars().all())
async def create(
db: AsyncSession,
*,
ap_a: AttachmentPoint,
ap_b: AttachmentPoint,
label: FitLabel,
) -> Fit:
first, second = _ordered(ap_a, ap_b)
fit = Fit(
attachment_point_a_id=first.id,
attachment_point_b_id=second.id,
augmented_part_a_id=first.augmented_part_id,
augmented_part_b_id=second.augmented_part_id,
label=label,
)
db.add(fit)
await db.commit()
await db.refresh(fit, attribute_names=_RELATIONSHIPS)
return fit
async def set_label(db: AsyncSession, fit: Fit, label: FitLabel) -> Fit:
fit.label = label
await db.commit()
await db.refresh(fit, attribute_names=_RELATIONSHIPS)
return fit
async def delete(db: AsyncSession, fit: Fit) -> None:
await db.delete(fit)
await db.commit()
+17
View File
@@ -0,0 +1,17 @@
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.fit import Fit, FitNote
from app.models.user import User
async def create(db: AsyncSession, *, fit: Fit, author: User, body: str) -> FitNote:
note = FitNote(
fit_id=fit.id,
user_id=author.id,
username=author.full_name or author.email,
body=body,
)
db.add(note)
await db.commit()
await db.refresh(note)
return note