"""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 []