Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions app/routers/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,13 @@ async def list_collections(
session: AsyncSession = Depends(get_async_session),
):
"""List all collections for the current user."""
# Get collections with item counts
# Get collections with item counts and total value
stmt = (
select(Collection, func.count(Item.id).label("item_count"))
select(
Collection,
func.count(Item.id).label("item_count"),
func.sum(Item.estimated_value).label("total_value"),
)
.outerjoin(Item, Collection.id == Item.collection_id)
.where(Collection.user_id == str(user.id))
.group_by(Collection.id)
Expand All @@ -121,6 +125,7 @@ async def list_collections(
created_at=row.Collection.created_at,
updated_at=row.Collection.updated_at,
item_count=row.item_count,
total_value=row.total_value or None,
preview_images=previews.get(row.Collection.id, []),
)
for row in rows
Expand All @@ -135,7 +140,11 @@ async def get_collection(
):
"""Get a single collection by ID."""
stmt = (
select(Collection, func.count(Item.id).label("item_count"))
select(
Collection,
func.count(Item.id).label("item_count"),
func.sum(Item.estimated_value).label("total_value"),
)
.outerjoin(Item, Collection.id == Item.collection_id)
.where(Collection.id == str(collection_id), Collection.user_id == str(user.id))
.group_by(Collection.id)
Expand All @@ -160,6 +169,7 @@ async def get_collection(
created_at=row.Collection.created_at,
updated_at=row.Collection.updated_at,
item_count=row.item_count,
total_value=row.total_value or None,
preview_images=previews.get(row.Collection.id, []),
)

Expand Down
2 changes: 2 additions & 0 deletions app/schemas/collection.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import datetime
from decimal import Decimal
from uuid import UUID

from pydantic import BaseModel, ConfigDict, Field, field_validator
Expand Down Expand Up @@ -64,4 +65,5 @@ class CollectionReadWithCount(CollectionRead):
"""Schema for reading a Collection with item count."""

item_count: int = 0
total_value: Decimal | None = None
preview_images: list[ImagePreview] = []
102 changes: 102 additions & 0 deletions tests/test_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,3 +427,105 @@ async def test_collection_no_images_empty_preview(
assert response.status_code == 200
data = response.json()
assert data["preview_images"] == []


@pytest.mark.asyncio
async def test_list_collections_total_value(
client: AsyncClient, session: AsyncSession, test_user: User, auth_client
):
"""Test that listing collections includes total_value summing estimated_value."""
collection = Collection(user_id=str(test_user.id), name="Valued Collection")
session.add(collection)
await session.commit()
await session.refresh(collection)

item1 = Item(
user_id=str(test_user.id),
collection_id=collection.id,
name="Item 1",
estimated_value=100.50,
)
item2 = Item(
user_id=str(test_user.id),
collection_id=collection.id,
name="Item 2",
estimated_value=250.00,
)
item3 = Item(
user_id=str(test_user.id),
collection_id=collection.id,
name="Item 3 (no value)",
)
session.add_all([item1, item2, item3])
await session.commit()

response = await client.get("/collections")
assert response.status_code == 200
data = response.json()
assert len(data) == 1
assert data[0]["total_value"] == "350.50"


@pytest.mark.asyncio
async def test_get_collection_total_value(
client: AsyncClient, session: AsyncSession, test_user: User, auth_client
):
"""Test that getting a single collection includes total_value."""
collection = Collection(user_id=str(test_user.id), name="My Collection")
session.add(collection)
await session.commit()
await session.refresh(collection)

item = Item(
user_id=str(test_user.id),
collection_id=collection.id,
name="Valuable Item",
estimated_value=999.99,
)
session.add(item)
await session.commit()

response = await client.get(f"/collections/{collection.id}")
assert response.status_code == 200
data = response.json()
assert data["total_value"] == "999.99"


@pytest.mark.asyncio
async def test_collection_total_value_null_when_no_values(
client: AsyncClient, session: AsyncSession, test_user: User, auth_client
):
"""Test that total_value is null when no items have estimated_value."""
collection = Collection(user_id=str(test_user.id), name="No Values")
session.add(collection)
await session.commit()
await session.refresh(collection)

item = Item(
user_id=str(test_user.id),
collection_id=collection.id,
name="Item without value",
)
session.add(item)
await session.commit()

response = await client.get(f"/collections/{collection.id}")
assert response.status_code == 200
data = response.json()
assert data["total_value"] is None


@pytest.mark.asyncio
async def test_empty_collection_total_value_null(
client: AsyncClient, session: AsyncSession, test_user: User, auth_client
):
"""Test that total_value is null for empty collections."""
collection = Collection(user_id=str(test_user.id), name="Empty")
session.add(collection)
await session.commit()
await session.refresh(collection)

response = await client.get(f"/collections/{collection.id}")
assert response.status_code == 200
data = response.json()
assert data["total_value"] is None