"""Image repository for database operations.""" from collections.abc import Sequence from uuid import UUID from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.database.models.board_image import BoardImage from app.database.models.image import Image class ImageRepository: """Repository for image database operations.""" def __init__(self, db: AsyncSession): """Initialize repository with database session.""" self.db = db async def create_image( self, user_id: UUID, filename: str, storage_path: str, file_size: int, mime_type: str, width: int, height: int, metadata: dict, ) -> Image: """ Create new image record. Args: user_id: Owner user ID filename: Original filename storage_path: Path in MinIO file_size: File size in bytes mime_type: MIME type width: Image width in pixels height: Image height in pixels metadata: Additional metadata (format, checksum, thumbnails, etc) Returns: Created Image instance """ image = Image( user_id=user_id, filename=filename, storage_path=storage_path, file_size=file_size, mime_type=mime_type, width=width, height=height, metadata=metadata, ) self.db.add(image) await self.db.commit() await self.db.refresh(image) return image async def get_image_by_id(self, image_id: UUID) -> Image | None: """ Get image by ID. Args: image_id: Image ID Returns: Image instance or None """ result = await self.db.execute(select(Image).where(Image.id == image_id)) return result.scalar_one_or_none() async def get_user_images(self, user_id: UUID, limit: int = 50, offset: int = 0) -> tuple[Sequence[Image], int]: """ Get all images for a user with pagination. Args: user_id: User ID limit: Maximum number of images to return offset: Number of images to skip Returns: Tuple of (images, total_count) """ # Get total count count_result = await self.db.execute(select(Image).where(Image.user_id == user_id)) total = len(count_result.scalars().all()) # Get paginated results result = await self.db.execute( select(Image).where(Image.user_id == user_id).order_by(Image.created_at.desc()).limit(limit).offset(offset) ) images = result.scalars().all() return images, total async def delete_image(self, image_id: UUID) -> bool: """ Delete image record. Args: image_id: Image ID Returns: True if deleted, False if not found """ image = await self.get_image_by_id(image_id) if not image: return False await self.db.delete(image) await self.db.commit() return True async def increment_reference_count(self, image_id: UUID) -> None: """ Increment reference count for image. Args: image_id: Image ID """ image = await self.get_image_by_id(image_id) if image: image.reference_count += 1 await self.db.commit() async def decrement_reference_count(self, image_id: UUID) -> int: """ Decrement reference count for image. Args: image_id: Image ID Returns: New reference count """ image = await self.get_image_by_id(image_id) if image and image.reference_count > 0: image.reference_count -= 1 await self.db.commit() return image.reference_count return 0 async def add_image_to_board( self, board_id: UUID, image_id: UUID, position: dict, transformations: dict, z_order: int = 0, ) -> BoardImage: """ Add image to board. Args: board_id: Board ID image_id: Image ID position: Canvas position {x, y} transformations: Image transformations z_order: Layer order Returns: Created BoardImage instance """ board_image = BoardImage( board_id=board_id, image_id=image_id, position=position, transformations=transformations, z_order=z_order, ) self.db.add(board_image) # Increment reference count await self.increment_reference_count(image_id) await self.db.commit() await self.db.refresh(board_image) return board_image async def get_board_images(self, board_id: UUID) -> Sequence[BoardImage]: """ Get all images for a board, ordered by z-order. Args: board_id: Board ID Returns: List of BoardImage instances """ result = await self.db.execute( select(BoardImage).where(BoardImage.board_id == board_id).order_by(BoardImage.z_order.asc()) ) return result.scalars().all() async def remove_image_from_board(self, board_id: UUID, image_id: UUID) -> bool: """ Remove image from board. Args: board_id: Board ID image_id: Image ID Returns: True if removed, False if not found """ result = await self.db.execute( select(BoardImage).where(BoardImage.board_id == board_id, BoardImage.image_id == image_id) ) board_image = result.scalar_one_or_none() if not board_image: return False await self.db.delete(board_image) # Decrement reference count await self.decrement_reference_count(image_id) await self.db.commit() return True