"""User model.""" import uuid from datetime import datetime from typing import TYPE_CHECKING from sqlalchemy import DATE, String, Boolean, JSON from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import Mapped, mapped_column, relationship from app.core.database import Base if TYPE_CHECKING: from app.models.playlist import Playlist from app.models.playlist_track import PlaylistTrack from app.models.listening_history import ListeningHistory from app.models.liked_track import LikedTrack class User(Base): """User model for authentication and user management.""" __tablename__ = "users" # Primary key id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True, ) # Authentication fields email: Mapped[str] = mapped_column( String(255), unique=True, nullable=False, index=True, ) password_hash: Mapped[str] = mapped_column( String(255), nullable=False, ) # Profile fields username: Mapped[str] = mapped_column( String(50), unique=True, nullable=False, index=True, ) display_name: Mapped[str | None] = mapped_column( String(100), ) avatar_url: Mapped[str | None] = mapped_column( String(500), ) date_of_birth: Mapped[datetime | None] = mapped_column( DATE, ) country: Mapped[str | None] = mapped_column( String(2), ) # Preferences and settings stored as JSON preferences: Mapped[dict] = mapped_column( JSON, default=dict, ) # Premium status (for future use) is_premium: Mapped[bool] = mapped_column( Boolean, default=False, ) # Timestamps created_at: Mapped[datetime] = mapped_column( default=datetime.utcnow, nullable=False, ) updated_at: Mapped[datetime] = mapped_column( default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False, ) last_login: Mapped[datetime | None] = mapped_column( default=None, ) # Relationships playlists: Mapped[list["Playlist"]] = relationship( "Playlist", back_populates="user", cascade="all, delete-orphan", lazy="selectin", ) added_playlist_tracks: Mapped[list["PlaylistTrack"]] = relationship( "PlaylistTrack", back_populates="added_by_user", foreign_keys="PlaylistTrack.added_by", lazy="selectin", ) listening_history: Mapped[list["ListeningHistory"]] = relationship( "ListeningHistory", back_populates="user", cascade="all, delete-orphan", lazy="selectin", ) liked_tracks: Mapped[list["LikedTrack"]] = relationship( "LikedTrack", back_populates="user", cascade="all, delete-orphan", lazy="selectin", ) def __repr__(self) -> str: return f"" def to_dict(self) -> dict: """Convert user model to dictionary (excluding sensitive data).""" return { "id": str(self.id), "email": self.email, "username": self.username, "display_name": self.display_name, "avatar_url": self.avatar_url, "date_of_birth": self.date_of_birth.isoformat() if self.date_of_birth else None, "country": self.country, "preferences": self.preferences, "is_premium": self.is_premium, "created_at": self.created_at.isoformat(), "updated_at": self.updated_at.isoformat(), "last_login": self.last_login.isoformat() if self.last_login else None, }