from datetime import datetime from typing import TYPE_CHECKING, List from sqlalchemy import ( DateTime, ForeignKey, Integer, String, Text, func, ) from sqlalchemy.dialects.mysql import LONGTEXT from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database.session import Base if TYPE_CHECKING: from app.home.models import Project from app.song.models import Song from app.video.models import Video class Lyric(Base): """ 가사 테이블 AI를 통해 생성된 가사 정보를 저장합니다. 프롬프트와 생성 결과, 처리 상태를 관리합니다. Attributes: id: 고유 식별자 (자동 증가) project_id: 연결된 Project의 id (외래키) task_id: 가사 생성 작업의 고유 식별자 (UUID 형식) status: 처리 상태 (pending, processing, completed, failed 등) lyric_prompt: 가사 생성에 사용된 프롬프트 lyric_result: 생성된 가사 결과 (LONGTEXT로 긴 가사 지원) created_at: 생성 일시 (자동 설정) Relationships: project: 연결된 Project songs: 이 가사를 사용한 노래 목록 videos: 이 가사를 사용한 영상 목록 """ __tablename__ = "lyric" __table_args__ = ( {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}, ) id: Mapped[int] = mapped_column( Integer, primary_key=True, nullable=False, autoincrement=True, comment="고유 식별자", ) project_id: Mapped[int] = mapped_column( Integer, ForeignKey("project.id", ondelete="CASCADE"), nullable=False, index=True, comment="연결된 Project의 id", ) task_id: Mapped[str] = mapped_column( String(36), nullable=False, unique=True, comment="가사 생성 작업 고유 식별자 (UUID)", ) status: Mapped[str] = mapped_column( String(50), nullable=False, comment="처리 상태 (processing, completed, failed)", ) lyric_prompt: Mapped[str] = mapped_column( Text, nullable=False, comment="가사 생성에 사용된 프롬프트", ) lyric_result: Mapped[str] = mapped_column( LONGTEXT, nullable=True, comment="생성된 가사 결과", ) created_at: Mapped[datetime] = mapped_column( DateTime, nullable=True, server_default=func.now(), comment="생성 일시", ) # Relationships project: Mapped["Project"] = relationship( "Project", back_populates="lyrics", ) songs: Mapped[List["Song"]] = relationship( "Song", back_populates="lyric", cascade="all, delete-orphan", lazy="selectin", ) videos: Mapped[List["Video"]] = relationship( "Video", back_populates="lyric", cascade="all, delete-orphan", lazy="selectin", ) def __repr__(self) -> str: def truncate(value: str | None, max_len: int = 10) -> str: if value is None: return "None" return (value[:max_len] + "...") if len(value) > max_len else value return ( f"" )