"""Playlist model for managing sound playlists.""" from datetime import datetime from typing import TYPE_CHECKING, Optional from zoneinfo import ZoneInfo from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database import db if TYPE_CHECKING: from app.models.playlist_sound import PlaylistSound from app.models.user import User class Playlist(db.Model): """Model for playlists containing sounds.""" __tablename__ = "playlist" id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String(255), nullable=False) description: Mapped[Optional[str]] = mapped_column(Text, nullable=True) genre: Mapped[Optional[str]] = mapped_column(String(100), nullable=True) user_id: Mapped[Optional[int]] = mapped_column( Integer, ForeignKey("user.id"), nullable=True ) is_main: Mapped[bool] = mapped_column( Boolean, default=False, nullable=False ) is_deletable: Mapped[bool] = mapped_column( Boolean, default=True, nullable=True ) is_current: Mapped[bool] = mapped_column( Boolean, default=False, nullable=False ) created_at: Mapped[datetime] = mapped_column( DateTime, default=lambda: datetime.now(tz=ZoneInfo("UTC")), nullable=False, ) updated_at: Mapped[datetime] = mapped_column( DateTime, default=lambda: datetime.now(tz=ZoneInfo("UTC")), onupdate=lambda: datetime.now(tz=ZoneInfo("UTC")), nullable=False, ) # Relationships user: Mapped[Optional["User"]] = relationship( "User", back_populates="playlists" ) playlist_sounds: Mapped[list["PlaylistSound"]] = relationship( "PlaylistSound", back_populates="playlist", cascade="all, delete-orphan" ) def __repr__(self) -> str: """String representation of the playlist.""" return f"" def to_dict(self) -> dict: """Convert playlist to dictionary representation.""" return { "id": self.id, "name": self.name, "description": self.description, "genre": self.genre, "user_id": self.user_id, "is_main": self.is_main, "is_deletable": self.is_deletable, "is_current": self.is_current, "created_at": ( self.created_at.isoformat() if self.created_at else None ), "updated_at": ( self.updated_at.isoformat() if self.updated_at else None ), "sound_count": ( len(self.playlist_sounds) if self.playlist_sounds else 0 ), } @classmethod def create_playlist( cls, name: str, description: Optional[str] = None, genre: Optional[str] = None, user_id: Optional[int] = None, is_main: bool = False, is_deletable: bool = True, is_current: bool = False, commit: bool = True, ) -> "Playlist": """Create a new playlist.""" playlist = cls( name=name, description=description, genre=genre, user_id=user_id, is_main=is_main, is_deletable=is_deletable, is_current=is_current, ) db.session.add(playlist) if commit: db.session.commit() return playlist @classmethod def find_current_playlist( cls, user_id: Optional[int] = None ) -> Optional["Playlist"]: """Find the current active playlist.""" query = cls.query.filter_by(is_current=True) if user_id is not None: query = query.filter_by(user_id=user_id) return query.first() @classmethod def find_main_playlist( cls, user_id: Optional[int] = None ) -> Optional["Playlist"]: """Find the main playlist.""" query = cls.query.filter_by(is_main=True) if user_id is not None: query = query.filter_by(user_id=user_id) return query.first() def add_sound( self, sound_id: int, order: Optional[int] = None, commit: bool = True ) -> "PlaylistSound": """Add a sound to the playlist.""" from app.models.playlist_sound import PlaylistSound if order is None: # Get the next order number max_order = ( db.session.query(db.func.max(PlaylistSound.order)) .filter_by(playlist_id=self.id) .scalar() ) order = (max_order or 0) + 1 playlist_sound = PlaylistSound( playlist_id=self.id, sound_id=sound_id, order=order ) db.session.add(playlist_sound) if commit: db.session.commit() return playlist_sound