""" Lyric API Schemas 이 모듈은 가사 관련 API 엔드포인트에서 사용되는 Pydantic 스키마를 정의합니다. 사용 예시: from app.lyric.schemas.lyric import ( LyricStatusResponse, LyricDetailResponse, LyricListItem, PaginatedResponse, ) # 라우터에서 response_model로 사용 @router.get("/lyric/{task_id}", response_model=LyricDetailResponse) async def get_lyric(task_id: str): ... # 페이지네이션 응답 (다른 모델에서도 재사용 가능) @router.get("/songs", response_model=PaginatedResponse[SongListItem]) async def list_songs(...): ... """ import math from datetime import datetime from typing import Generic, List, Optional, TypeVar from pydantic import BaseModel, Field class GenerateLyricRequest(BaseModel): """가사 생성 요청 스키마 Usage: POST /lyric/generate Request body for generating lyrics. Example Request: { "customer_name": "스테이 머뭄", "region": "군산", "detail_region_info": "군산 신흥동 말랭이 마을", "language": "Korean" } """ model_config = { "json_schema_extra": { "example": { "customer_name": "스테이 머뭄", "region": "군산", "detail_region_info": "군산 신흥동 말랭이 마을", "language": "Korean", } } } customer_name: str = Field(..., description="고객명/가게명") region: str = Field(..., description="지역명") detail_region_info: Optional[str] = Field(None, description="상세 지역 정보") language: str = Field( default="Korean", description="가사 출력 언어 (Korean, English, Chinese, Japanese, Thai, Vietnamese)", ) class GenerateLyricResponse(BaseModel): """가사 생성 응답 스키마 Usage: POST /lyric/generate Returns the generated lyrics. Example Response: { "success": true, "lyric": "생성된 가사...", "language": "Korean", "prompt_used": "..." } """ success: bool = Field(..., description="생성 성공 여부") lyric: Optional[str] = Field(None, description="생성된 가사") language: str = Field(..., description="가사 언어") prompt_used: Optional[str] = Field(None, description="사용된 프롬프트") error_message: Optional[str] = Field(None, description="에러 메시지 (실패 시)") class LyricStatusResponse(BaseModel): """가사 상태 조회 응답 스키마 Usage: GET /lyric/status/{task_id} Returns the current processing status of a lyric generation task. Example Response: { "task_id": "019123ab-cdef-7890-abcd-ef1234567890", "status": "completed", "message": "가사 생성이 완료되었습니다." } """ task_id: str = Field(..., description="작업 고유 식별자") status: str = Field(..., description="처리 상태 (processing, completed, failed)") message: str = Field(..., description="상태 메시지") class LyricDetailResponse(BaseModel): """가사 상세 조회 응답 스키마 Usage: GET /lyric/{task_id} Returns the generated lyric content for a specific task. Example Response: { "id": 1, "task_id": "019123ab-cdef-7890-abcd-ef1234567890", "project_id": 1, "status": "completed", "lyric_prompt": "...", "lyric_result": "생성된 가사...", "created_at": "2024-01-01T12:00:00" } """ id: int = Field(..., description="가사 ID") task_id: str = Field(..., description="작업 고유 식별자") project_id: int = Field(..., description="프로젝트 ID") status: str = Field(..., description="처리 상태") lyric_prompt: str = Field(..., description="가사 생성 프롬프트") lyric_result: Optional[str] = Field(None, description="생성된 가사") created_at: Optional[datetime] = Field(None, description="생성 일시") class LyricListItem(BaseModel): """가사 목록 아이템 스키마 Usage: Used as individual items in paginated lyric list responses. """ id: int = Field(..., description="가사 ID") task_id: str = Field(..., description="작업 고유 식별자") status: str = Field(..., description="처리 상태") lyric_result: Optional[str] = Field(None, description="생성된 가사 (미리보기)") created_at: Optional[datetime] = Field(None, description="생성 일시") T = TypeVar("T") class PaginatedResponse(BaseModel, Generic[T]): """페이지네이션 응답 스키마 (재사용 가능) Usage: 다른 모델에서도 페이지네이션이 필요할 때 재사용 가능: - PaginatedResponse[LyricListItem] - PaginatedResponse[SongListItem] - PaginatedResponse[VideoListItem] Example: from app.lyric.schemas.lyric import PaginatedResponse @router.get("/items", response_model=PaginatedResponse[ItemModel]) async def get_items(page: int = 1, page_size: int = 20): ... Example Response: { "items": [...], "total": 100, "page": 1, "page_size": 20, "total_pages": 5, "has_next": true, "has_prev": false } """ items: List[T] = Field(..., description="데이터 목록") total: int = Field(..., description="전체 데이터 수") page: int = Field(..., description="현재 페이지 (1부터 시작)") page_size: int = Field(..., description="페이지당 데이터 수") total_pages: int = Field(..., description="전체 페이지 수") has_next: bool = Field(..., description="다음 페이지 존재 여부") has_prev: bool = Field(..., description="이전 페이지 존재 여부") @classmethod def create( cls, items: List[T], total: int, page: int, page_size: int, ) -> "PaginatedResponse[T]": """페이지네이션 응답을 생성하는 헬퍼 메서드 Args: items: 현재 페이지의 데이터 목록 total: 전체 데이터 수 page: 현재 페이지 번호 page_size: 페이지당 데이터 수 Returns: PaginatedResponse: 완성된 페이지네이션 응답 Usage: items = [LyricListItem(...) for lyric in lyrics] return PaginatedResponse.create(items, total=100, page=1, page_size=20) """ total_pages = math.ceil(total / page_size) if total > 0 else 1 return cls( items=items, total=total, page=page, page_size=page_size, total_pages=total_pages, has_next=page < total_pages, has_prev=page > 1, )