Skip to content
Closed
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
462 changes: 462 additions & 0 deletions backend/app/database/memories.py

Large diffs are not rendered by default.

222 changes: 222 additions & 0 deletions backend/app/routes/memories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
from fastapi import APIRouter, HTTPException, Query, status
from app.database.memories import (
db_get_memories_on_this_day,
db_get_recent_memories,
db_get_memories_by_people,
db_get_memories_by_tags,
)
from app.schemas.memories import (
OnThisDayResponse,
RecentMemoriesResponse,
PeopleMemoriesResponse,
TagMemoriesResponse,
AllMemoriesResponse,
AllMemoriesData,
ErrorResponse,
)
from app.logging.setup_logging import get_logger

router = APIRouter()
logger = get_logger(__name__)


@router.get(
"/on-this-day",
response_model=OnThisDayResponse,
responses={500: {"model": ErrorResponse}},
)
def get_on_this_day_memories(
years_back: int = Query(5, ge=1, le=20, description="Number of years to look back")
):
"""
Get images from the same day in previous years.

Returns memories from the same month and day across different years,
creating a nostalgic "On This Day" experience.
"""
try:
memories = db_get_memories_on_this_day(years_back=years_back)

return OnThisDayResponse(
success=True,
message=f"Successfully retrieved {len(memories)} 'On This Day' memories",
data=memories,
)
except Exception as e:
logger.error(f"Error getting 'On This Day' memories: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=ErrorResponse(
success=False,
error="Internal server error",
message=f"Unable to retrieve 'On This Day' memories: {str(e)}",
).model_dump(),
)


@router.get(
"/recent",
response_model=RecentMemoriesResponse,
responses={500: {"model": ErrorResponse}},
)
def get_recent_memories(
days: int = Query(30, ge=1, le=365, description="Number of days to look back"),
min_images: int = Query(
5, ge=1, le=50, description="Minimum images per day to create a memory"
),
):
"""
Get recent collections of images grouped by date.

Returns memories from recent days where you took multiple photos,
highlighting significant photo-taking events.
"""
try:
memories = db_get_recent_memories(days=days, min_images=min_images)

return RecentMemoriesResponse(
success=True,
message=f"Successfully retrieved {len(memories)} recent memories",
data=memories,
)
except Exception as e:
logger.error(f"Error getting recent memories: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=ErrorResponse(
success=False,
error="Internal server error",
message=f"Unable to retrieve recent memories: {str(e)}",
).model_dump(),
)


@router.get(
"/people",
response_model=PeopleMemoriesResponse,
responses={500: {"model": ErrorResponse}},
)
def get_people_memories(
limit: int = Query(
10, ge=1, le=50, description="Maximum number of people to return"
)
):
"""
Get memories grouped by people (face clusters).

Returns collections of photos featuring the same person,
creating personalized memory collections.
"""
try:
memories = db_get_memories_by_people(limit=limit)

return PeopleMemoriesResponse(
success=True,
message=f"Successfully retrieved {len(memories)} people memories",
data=memories,
)
except Exception as e:
logger.error(f"Error getting people memories: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=ErrorResponse(
success=False,
error="Internal server error",
message=f"Unable to retrieve people memories: {str(e)}",
).model_dump(),
)


@router.get(
"/tags",
response_model=TagMemoriesResponse,
responses={500: {"model": ErrorResponse}},
)
def get_tag_memories(
limit: int = Query(10, ge=1, le=50, description="Maximum number of tags to return")
):
"""
Get memories grouped by common tags/objects.

Returns collections of photos featuring the same objects or themes,
creating thematic memory collections.
"""
try:
memories = db_get_memories_by_tags(limit=limit)

return TagMemoriesResponse(
success=True,
message=f"Successfully retrieved {len(memories)} tag memories",
data=memories,
)
except Exception as e:
logger.error(f"Error getting tag memories: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=ErrorResponse(
success=False,
error="Internal server error",
message=f"Unable to retrieve tag memories: {str(e)}",
).model_dump(),
)


@router.get(
"/all",
response_model=AllMemoriesResponse,
responses={500: {"model": ErrorResponse}},
)
def get_all_memories(
years_back: int = Query(
5, ge=1, le=20, description="Years to look back for 'On This Day'"
),
recent_days: int = Query(
30, ge=1, le=365, description="Days to look back for recent memories"
),
min_images: int = Query(
5, ge=1, le=50, description="Minimum images per recent memory"
),
people_limit: int = Query(
10, ge=1, le=50, description="Maximum number of people memories"
),
tags_limit: int = Query(
10, ge=1, le=50, description="Maximum number of tag memories"
),
):
"""
Get all types of memories in a single request.

Returns a comprehensive collection including:
- On This Day memories
- Recent memories
- People-based memories
- Tag-based memories
"""
try:
on_this_day = db_get_memories_on_this_day(years_back=years_back)
recent = db_get_recent_memories(days=recent_days, min_images=min_images)
people = db_get_memories_by_people(limit=people_limit)
tags = db_get_memories_by_tags(limit=tags_limit)

total_memories = len(on_this_day) + len(recent) + len(people) + len(tags)

return AllMemoriesResponse(
success=True,
message=f"Successfully retrieved {total_memories} total memories",
data=AllMemoriesData(
on_this_day=on_this_day,
recent=recent,
people=people,
tags=tags,
),
)
except Exception as e:
logger.error(f"Error getting all memories: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=ErrorResponse(
success=False,
error="Internal server error",
message=f"Unable to retrieve memories: {str(e)}",
).model_dump(),
)
93 changes: 93 additions & 0 deletions backend/app/schemas/memories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from pydantic import BaseModel
from typing import List, Optional


class MemoryImageMetadata(BaseModel):
name: str
date_created: Optional[str] = None
width: int
height: int
file_location: str
file_size: int
item_type: str
latitude: Optional[float] = None
longitude: Optional[float] = None
location: Optional[str] = None


class MemoryImage(BaseModel):
id: str
path: str
thumbnailPath: str
metadata: MemoryImageMetadata
isTagged: bool
tags: Optional[List[str]] = None


class OnThisDayMemory(BaseModel):
year: int
years_ago: int
date: str
images: List[MemoryImage]


class RecentMemory(BaseModel):
date: str
iso_date: str
images: List[MemoryImage]


class PersonMemory(BaseModel):
cluster_id: str
person_name: str
image_count: int
images: List[MemoryImage]


class TagMemory(BaseModel):
tag_name: str
image_count: int
images: List[MemoryImage]


class OnThisDayResponse(BaseModel):
success: bool
message: str
data: List[OnThisDayMemory]


class RecentMemoriesResponse(BaseModel):
success: bool
message: str
data: List[RecentMemory]


class PeopleMemoriesResponse(BaseModel):
success: bool
message: str
data: List[PersonMemory]


class TagMemoriesResponse(BaseModel):
success: bool
message: str
data: List[TagMemory]


class AllMemoriesData(BaseModel):
on_this_day: List[OnThisDayMemory]
recent: List[RecentMemory]
people: List[PersonMemory]
tags: List[TagMemory]


class AllMemoriesResponse(BaseModel):
success: bool
message: str
data: AllMemoriesData


class ErrorResponse(BaseModel):
success: bool
error: str
message: str
2 changes: 2 additions & 0 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from app.routes.images import router as images_router
from app.routes.face_clusters import router as face_clusters_router
from app.routes.user_preferences import router as user_preferences_router
from app.routes.memories import router as memories_router
from fastapi.openapi.utils import get_openapi
from app.logging.setup_logging import (
configure_uvicorn_logging,
Expand Down Expand Up @@ -132,6 +133,7 @@ async def root():
app.include_router(
user_preferences_router, prefix="/user-preferences", tags=["User Preferences"]
)
app.include_router(memories_router, prefix="/memories", tags=["Memories"])


# Entry point for running with: python3 main.py
Expand Down
Loading
Loading