"""Content Brief v2 Model — Struggle-First Pipeline. SQLAlchemy model for content_briefs_v2 table. """ from datetime import datetime from typing import Optional, List, Dict, Any from sqlalchemy import Column, String, Text, Integer, Float, DateTime, JSON, CheckConstraint from sqlalchemy.sql import func from ...database import Base class ContentBrief(Base): """Content brief with struggle-first structure.""" __tablename__ = "content_briefs_v2" id = Column(String(36), primary_key=True) # UUID title = Column(Text, nullable=False) struggle_angle = Column(Text, nullable=False) origin_story = Column(Text, nullable=False) attempts = Column(JSON, nullable=False) # [{"attempt": str, "why_failed": str}] the_moment = Column(Text, nullable=False) the_fix = Column(Text, nullable=False) reflection = Column(Text, nullable=False) voice_checklist = Column(JSON, nullable=False) # {str: bool} # Workflow state status = Column( String(20), nullable=False, default="draft", server_default="draft" ) # Optional fields style_reference = Column(String(100), nullable=True) # slug like "dns-night" target_length = Column(Integer, nullable=True) # word count target # Metadata created_by = Column(String(100), nullable=False) # user/agent who created created_at = Column(DateTime(timezone=True), server_default=func.now()) approved_at = Column(DateTime(timezone=True), nullable=True) approved_by = Column(String(100), nullable=True) # who approved (Matt/Aundrea) # Generation generation_job_id = Column(String(100), nullable=True) struggle_score = Column(Float, nullable=True) # 0-100 content_output = Column(Text, nullable=True) # generated markdown __table_args__ = ( CheckConstraint( "status IN ('draft', 'pending', 'approved', 'rejected', 'generating', 'completed')", name="valid_status" ), ) def to_dict(self) -> Dict[str, Any]: """Convert to dictionary for API responses.""" return { "id": self.id, "title": self.title, "struggle_angle": self.struggle_angle, "origin_story": self.origin_story, "attempts": self.attempts, "the_moment": self.the_moment, "the_fix": self.the_fix, "reflection": self.reflection, "voice_checklist": self.voice_checklist, "status": self.status, "style_reference": self.style_reference, "target_length": self.target_length, "created_by": self.created_by, "created_at": self.created_at.isoformat() if self.created_at else None, "approved_at": self.approved_at.isoformat() if self.approved_at else None, "approved_by": self.approved_by, "generation_job_id": self.generation_job_id, "struggle_score": self.struggle_score, "content_output": self.content_output, } class BriefCreateRequest: """Request model for creating a brief.""" title: str struggle_angle: str origin_story: str attempts: List[Dict[str, str]] the_moment: str the_fix: str reflection: str voice_checklist: Dict[str, bool] style_reference: Optional[str] = None target_length: Optional[int] = None def __init__(self, **data): for key, value in data.items(): setattr(self, key, value) class BriefValidationResult: """Result of brief validation.""" def __init__(self, valid: bool, errors: List[str], warnings: List[str] = None): self.valid = valid self.errors = errors or [] self.warnings = warnings or []