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
502 changes: 502 additions & 0 deletions backend/app/database/memories.py

Large diffs are not rendered by default.

195 changes: 195 additions & 0 deletions backend/app/routes/memories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
from fastapi import APIRouter, HTTPException, status
from typing import List
from app.database.memories import (
db_generate_memories,
db_get_memory_images,
db_get_memories_for_current_date
)
from app.schemas.memories import (
Memory,
MemoryImage,
GetMemoriesResponse,
GetMemoryImagesResponse,
ErrorResponse
)
from app.logging.setup_logging import get_logger

# Initialize logger
logger = get_logger(__name__)
router = APIRouter()


@router.get(
"/",
response_model=GetMemoriesResponse,
responses={500: {"model": ErrorResponse}},
)
def get_all_memories():
"""
Get all generated memories.

Returns memories grouped by time and location, showing representative images.
"""
try:
memories_data = db_generate_memories()

# Convert to response format
memories = []
for memory in memories_data:
memory_images = [
MemoryImage(
id=img["id"],
path=img["path"],
thumbnail=img["thumbnail"],
date=img["date"].isoformat() if hasattr(img["date"], 'isoformat') else str(img["date"]),
location=img.get("location")
)
for img in memory.get("images", [])
]

memories.append(
Memory(
id=memory["id"],
title=memory["title"],
description=memory["description"],
start_date=memory["start_date"],
end_date=memory["end_date"],
location=memory.get("location"),
latitude=memory.get("latitude"),
longitude=memory.get("longitude"),
image_count=memory["image_count"],
images=memory_images
)
)

return GetMemoriesResponse(
success=True,
message=f"Successfully retrieved {len(memories)} memories",
data=memories
)

except Exception as e:
logger.error(f"Error in get_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()
)


@router.get(
"/today",
response_model=GetMemoriesResponse,
responses={500: {"model": ErrorResponse}},
)
def get_memories_for_today():
"""
Get memories relevant to today's date.

Returns "On this day" style memories from previous years.
"""
try:
memories_data = db_get_memories_for_current_date()

# Convert to response format
memories = []
for memory in memories_data:
memory_images = [
MemoryImage(
id=img["id"],
path=img["path"],
thumbnail=img["thumbnail"],
date=img["date"].isoformat() if hasattr(img["date"], 'isoformat') else str(img["date"]),
location=img.get("location")
)
for img in memory.get("images", [])
]

memories.append(
Memory(
id=memory["id"],
title=memory["title"],
description=memory["description"],
start_date=memory["start_date"],
end_date=memory["end_date"],
location=memory.get("location"),
latitude=memory.get("latitude"),
longitude=memory.get("longitude"),
image_count=memory["image_count"],
images=memory_images
)
)

return GetMemoriesResponse(
success=True,
message=f"Successfully retrieved {len(memories)} memories for today",
data=memories
)

except Exception as e:
logger.error(f"Error in get_memories_for_today: {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()
)


@router.get(
"/{memory_id}/images",
response_model=GetMemoryImagesResponse,
responses={500: {"model": ErrorResponse}},
)
def get_memory_detail(memory_id: str):
"""
Get all images for a specific memory.

Args:
memory_id: The unique identifier of the memory

Returns:
All images associated with the memory
"""
try:
images_data = db_get_memory_images(memory_id)

# Convert to response format with defensive metadata handling
images = []
for img in images_data:
# Safely extract metadata
metadata = img.get("metadata") or {}
if not isinstance(metadata, dict):
metadata = {}

images.append(
MemoryImage(
id=img.get("id", ""),
path=img.get("path", ""),
thumbnail=img.get("thumbnail", ""),
date=metadata.get("date_created", ""),
location=metadata.get("location")
)
)

return GetMemoryImagesResponse(
success=True,
message=f"Successfully retrieved {len(images)} images",
data=images
)

except Exception as e:
logger.error(f"Error in get_memory_detail: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=ErrorResponse(
success=False,
error="Internal server error",
message=f"Unable to retrieve memory images: {str(e)}"
).model_dump()
)
41 changes: 41 additions & 0 deletions backend/app/schemas/memories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from pydantic import BaseModel
from typing import List, Optional


class MemoryImage(BaseModel):
id: str
path: str
thumbnail: str
date: str
location: Optional[str] = None


class Memory(BaseModel):
id: str
title: str
description: str
start_date: str
end_date: str
location: Optional[str] = None
latitude: Optional[float] = None
longitude: Optional[float] = None
image_count: int
images: List[MemoryImage]


class GetMemoriesResponse(BaseModel):
success: bool
message: str
data: List[Memory]


class GetMemoryImagesResponse(BaseModel):
success: bool
message: str
data: List[MemoryImage]


class ErrorResponse(BaseModel):
success: bool
error: str
message: str
5 changes: 5 additions & 0 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@
from app.database.albums import db_create_album_images_table
from app.database.folders import db_create_folders_table
from app.database.metadata import db_create_metadata_table
from app.database.memories import db_create_memories_table, db_prepopulate_memories_cache
from app.utils.microservice import microservice_util_start_sync_service

from app.routes.folders import router as folders_router
from app.routes.albums import router as albums_router
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 All @@ -52,6 +54,8 @@ async def lifespan(app: FastAPI):
db_create_albums_table()
db_create_album_images_table()
db_create_metadata_table()
db_create_memories_table()
db_prepopulate_memories_cache() # Pre-populate memories cache at startup
microservice_util_start_sync_service()
# Create ProcessPoolExecutor and attach it to app.state
app.state.executor = ProcessPoolExecutor(max_workers=1)
Expand Down Expand Up @@ -132,6 +136,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