"""Pydantic schemas for the Chrysopedia API. Read-only schemas for list/detail endpoints and input schemas for creation. Each schema mirrors the corresponding SQLAlchemy model in models.py. """ from __future__ import annotations import uuid from datetime import datetime from pydantic import BaseModel, ConfigDict, Field # ── Health ─────────────────────────────────────────────────────────────────── class HealthResponse(BaseModel): status: str = "ok" service: str = "chrysopedia-api" version: str = "0.1.0" database: str = "unknown" # ── Creator ────────────────────────────────────────────────────────────────── class CreatorBase(BaseModel): name: str slug: str genres: list[str] | None = None folder_name: str class CreatorCreate(CreatorBase): pass class CreatorRead(CreatorBase): model_config = ConfigDict(from_attributes=True) id: uuid.UUID view_count: int = 0 created_at: datetime updated_at: datetime class CreatorDetail(CreatorRead): """Creator with nested video count.""" video_count: int = 0 # ── SourceVideo ────────────────────────────────────────────────────────────── class SourceVideoBase(BaseModel): filename: str file_path: str duration_seconds: int | None = None content_type: str transcript_path: str | None = None class SourceVideoCreate(SourceVideoBase): creator_id: uuid.UUID class SourceVideoRead(SourceVideoBase): model_config = ConfigDict(from_attributes=True) id: uuid.UUID creator_id: uuid.UUID processing_status: str = "pending" created_at: datetime updated_at: datetime # ── TranscriptSegment ──────────────────────────────────────────────────────── class TranscriptSegmentBase(BaseModel): start_time: float end_time: float text: str segment_index: int topic_label: str | None = None class TranscriptSegmentCreate(TranscriptSegmentBase): source_video_id: uuid.UUID class TranscriptSegmentRead(TranscriptSegmentBase): model_config = ConfigDict(from_attributes=True) id: uuid.UUID source_video_id: uuid.UUID # ── KeyMoment ──────────────────────────────────────────────────────────────── class KeyMomentBase(BaseModel): title: str summary: str start_time: float end_time: float content_type: str plugins: list[str] | None = None raw_transcript: str | None = None class KeyMomentCreate(KeyMomentBase): source_video_id: uuid.UUID technique_page_id: uuid.UUID | None = None class KeyMomentRead(KeyMomentBase): model_config = ConfigDict(from_attributes=True) id: uuid.UUID source_video_id: uuid.UUID technique_page_id: uuid.UUID | None = None review_status: str = "pending" created_at: datetime updated_at: datetime # ── TechniquePage ──────────────────────────────────────────────────────────── class TechniquePageBase(BaseModel): title: str slug: str topic_category: str topic_tags: list[str] | None = None summary: str | None = None body_sections: dict | None = None signal_chains: list | None = None plugins: list[str] | None = None class TechniquePageCreate(TechniquePageBase): creator_id: uuid.UUID source_quality: str | None = None class TechniquePageRead(TechniquePageBase): model_config = ConfigDict(from_attributes=True) id: uuid.UUID creator_id: uuid.UUID source_quality: str | None = None view_count: int = 0 review_status: str = "draft" created_at: datetime updated_at: datetime # ── RelatedTechniqueLink ───────────────────────────────────────────────────── class RelatedTechniqueLinkBase(BaseModel): source_page_id: uuid.UUID target_page_id: uuid.UUID relationship: str class RelatedTechniqueLinkCreate(RelatedTechniqueLinkBase): pass class RelatedTechniqueLinkRead(RelatedTechniqueLinkBase): model_config = ConfigDict(from_attributes=True) id: uuid.UUID # ── Tag ────────────────────────────────────────────────────────────────────── class TagBase(BaseModel): name: str category: str aliases: list[str] | None = None class TagCreate(TagBase): pass class TagRead(TagBase): model_config = ConfigDict(from_attributes=True) id: uuid.UUID # ── Transcript Ingestion ───────────────────────────────────────────────────── class TranscriptIngestResponse(BaseModel): """Response returned after successfully ingesting a transcript.""" video_id: uuid.UUID creator_id: uuid.UUID creator_name: str filename: str segments_stored: int processing_status: str is_reupload: bool # ── Pagination wrapper ─────────────────────────────────────────────────────── class PaginatedResponse(BaseModel): """Generic paginated list response.""" items: list = Field(default_factory=list) total: int = 0 offset: int = 0 limit: int = 50