from pydantic import BaseModel, ConfigDict, Field from datetime import datetime, date from typing import Optional, List, Dict, Any class TopicFieldBase(BaseModel): name: str icon: Optional[str] = None color: Optional[str] = None description: Optional[str] = None parent_id: Optional[int] = None sort_order: int = 0 is_active: bool = True class TopicFieldResponse(TopicFieldBase): id: int created_at: Optional[datetime] = None updated_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class TopicConfigFieldBase(BaseModel): name: str key: str field_type: str = "number" weight: float = 1.0 options: List[Any] = [] min_value: Optional[float] = None max_value: Optional[float] = None is_required: bool = False sort_order: int = 0 class TopicConfigFieldResponse(TopicConfigFieldBase): id: int field_id: int created_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class TopicStatusConfigBase(BaseModel): status: str label: str color: Optional[str] = None icon: Optional[str] = None sort_order: int = 0 is_default: bool = False class TopicStatusConfigResponse(TopicStatusConfigBase): id: int created_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class TopicBase(BaseModel): id: str field_id: Optional[int] = None field_name: Optional[str] = None org_id: Optional[str] = None title: str format: Optional[str] = None core_concept: Optional[str] = None audience_pain: Optional[str] = None unique_angle: Optional[str] = None priority: Optional[str] = None priority_score: int | float = 0 total_score: Optional[float] = None status: str = "pending" tags: List[str] = [] custom_data: Dict[str, Any] = {} scoring_data: Dict[str, Any] = {} lock_by: Optional[str] = None lock_at: Optional[datetime] = None class TopicCreate(BaseModel): id: Optional[str] = None field_id: Optional[int] = None title: str = Field(..., min_length=1) format: Optional[str] = None core_concept: Optional[str] = None audience_pain: Optional[str] = None unique_angle: Optional[str] = None priority: Optional[str] = None tags: List[str] = [] custom_data: Dict[str, Any] = {} scoring_data: Dict[str, Any] = {} class TopicUpdate(BaseModel): field_id: Optional[int] = None title: Optional[str] = None format: Optional[str] = None core_concept: Optional[str] = None audience_pain: Optional[str] = None unique_angle: Optional[str] = None priority: Optional[str] = None status: Optional[str] = None tags: Optional[List[str]] = None custom_data: Optional[Dict[str, Any]] = None scoring_data: Optional[Dict[str, Any]] = None priority_score: Optional[int] = None total_score: Optional[float] = None class TopicResponse(TopicBase): created_at: Optional[datetime] = None updated_at: Optional[datetime] = None generated_at: Optional[datetime] = None ready_at: Optional[date] = None published_at: Optional[date] = None compliance_score: Optional[int] = None platform_urls: Dict[str, str] = {} cases: List[Any] = [] model_config = ConfigDict(from_attributes=True) class TopicScoreRequest(BaseModel): scoring_data: Dict[str, Any] class ArticleBase(BaseModel): id: str topic_id: str platform: str file_path: str status: str = "draft" compliance_score: Optional[int] = None word_count: Optional[int] = None outline: Optional[str] = None created_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class ArticleResponse(ArticleBase): html_content: Optional[str] = None model_config = ConfigDict(from_attributes=True) class ContentCalendarBase(BaseModel): topic_id: Optional[str] = None field_id: Optional[int] = None title: str planned_date: date platform: Optional[str] = None status: str = "planned" reminder_time: Optional[datetime] = None notes: Optional[str] = None class ContentCalendarCreate(ContentCalendarBase): created_by: Optional[str] = None class ContentCalendarUpdate(BaseModel): topic_id: Optional[str] = None title: Optional[str] = None planned_date: Optional[date] = None published_date: Optional[date] = None platform: Optional[str] = None status: Optional[str] = None reminder_time: Optional[datetime] = None notes: Optional[str] = None class ContentCalendarResponse(ContentCalendarBase): id: int published_date: Optional[date] = None created_by: Optional[str] = None created_at: Optional[datetime] = None updated_at: Optional[datetime] = None topic_status: Optional[str] = None platform_icon: Optional[str] = None model_config = ConfigDict(from_attributes=True) class ContentMetricsBase(BaseModel): topic_id: str platform: str publish_url: Optional[str] = None views: int = 0 likes: int = 0 favorites: int = 0 comments: int = 0 shares: int = 0 class ContentMetricsCreate(ContentMetricsBase): data_snapshot: Optional[Dict[str, Any]] = None class ContentMetricsUpdate(BaseModel): publish_url: Optional[str] = None views: Optional[int] = None likes: Optional[int] = None favorites: Optional[int] = None comments: Optional[int] = None shares: Optional[int] = None data_snapshot: Optional[Dict[str, Any]] = None class ContentMetricsResponse(ContentMetricsBase): id: int engagement_rate: float = 0 last_fetched: Optional[datetime] = None created_at: Optional[datetime] = None updated_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class MediaAssetBase(BaseModel): filename: str file_path: str file_url: Optional[str] = None file_type: str mime_type: Optional[str] = None size: Optional[int] = None width: Optional[int] = None height: Optional[int] = None thumbnail_path: Optional[str] = None alt_text: Optional[str] = None tags: List[str] = [] topic_ids: List[str] = [] class MediaAssetCreate(MediaAssetBase): uploaded_by: Optional[str] = None class MediaAssetUpdate(BaseModel): alt_text: Optional[str] = None tags: Optional[List[str]] = None topic_ids: Optional[List[str]] = None class MediaAssetResponse(MediaAssetBase): id: int usage_count: int = 0 uploaded_by: Optional[str] = None created_at: Optional[datetime] = None updated_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class PlatformConfigBase(BaseModel): platform: str name: str icon: Optional[str] = None api_endpoint: Optional[str] = None auth_config: Dict[str, Any] = {} format_template: Dict[str, Any] = {} compliance_rules: Dict[str, Any] = {} default_format: Optional[str] = None is_active: bool = True class PlatformConfigCreate(PlatformConfigBase): pass class PlatformConfigUpdate(BaseModel): name: Optional[str] = None icon: Optional[str] = None api_endpoint: Optional[str] = None auth_config: Optional[Dict[str, Any]] = None format_template: Optional[Dict[str, Any]] = None compliance_rules: Optional[Dict[str, Any]] = None default_format: Optional[str] = None is_active: Optional[bool] = None class PlatformConfigResponse(PlatformConfigBase): id: int created_at: Optional[datetime] = None updated_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class ContentTaskBase(BaseModel): topic_id: Optional[str] = None stage: str status: str = "pending" progress: int = 0 message: Optional[str] = None class ContentTaskCreate(BaseModel): topic_id: Optional[str] = None stage: str created_by: Optional[str] = None class ContentTaskResponse(ContentTaskBase): id: int task_id: str result_data: Dict[str, Any] = {} error_msg: Optional[str] = None started_at: Optional[datetime] = None finished_at: Optional[datetime] = None duration: Optional[int] = None created_by: Optional[str] = None created_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class MetricsDashboard(BaseModel): total_topics: int topics_by_status: Dict[str, int] total_published: int total_views: int total_likes: int avg_engagement_rate: float top_topics: List[Dict[str, Any]] recent_metrics: List[ContentMetricsResponse] class CalendarMonthResponse(BaseModel): year: int month: int entries: List[ContentCalendarResponse] stats: Dict[str, int] class AuditLogBase(BaseModel): user_id: Optional[int] = None username: str action: str resource_type: Optional[str] = None resource_id: Optional[str] = None details: Optional[Dict[str, Any]] = None ip_address: Optional[str] = None user_agent: Optional[str] = None created_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class AuditLogResponse(AuditLogBase): id: int class UserBase(BaseModel): username: str role: str = "user" class UserCreate(UserBase): password: str org_id: Optional[str] = None class UserUpdate(BaseModel): username: Optional[str] = None password: Optional[str] = None role: Optional[str] = None class UserResponse(UserBase): id: int org_id: Optional[str] = None created_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class LoginRequest(BaseModel): username: str password: str class TokenResponse(BaseModel): token: str role: str user: UserResponse class PublishRequest(BaseModel): topic_id: str platform_urls: Dict[str, str] class PublishActionRequest(BaseModel): action: str platform: Optional[str] = None status: str operator: Optional[str] = None description: Optional[str] = None suggestion: Optional[str] = None url: Optional[str] = None error_msg: Optional[str] = None class PublishRecordResponse(BaseModel): id: int topic_id: str platform: str action: str status: str operator: Optional[str] = None description: Optional[str] = None suggestion: Optional[str] = None url: Optional[str] = None error_msg: Optional[str] = None created_at: datetime model_config = ConfigDict(from_attributes=True) class CaseBase(BaseModel): title: str field: str summary: str key_metrics: Optional[str] = None date: Optional[str] = None source: str source_url: Optional[str] = None credibility_rating: Optional[str] = None china_applicability: Optional[str] = None class CaseResponse(CaseBase): id: int created_at: Optional[datetime] = None updated_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class TaskLogBase(BaseModel): task_name: str topic_id: Optional[str] = None status: str message: Optional[str] = None started_at: Optional[datetime] = None finished_at: Optional[datetime] = None duration: Optional[int] = None class TaskLogResponse(TaskLogBase): id: int model_config = ConfigDict(from_attributes=True) class LLMConfigBase(BaseModel): name: str system_prompt: Optional[str] = None user_prompt_template: str temperature: float = 0.7 max_tokens: int = 2000 model: Optional[str] = None provider: str = "opencode-go" base_url: Optional[str] = None api_key: Optional[str] = None is_active: bool = True class LLMConfigResponse(LLMConfigBase): id: int created_at: Optional[datetime] = None updated_at: Optional[datetime] = None created_at: Optional[datetime] = None updated_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class SystemConfigBase(BaseModel): key: str value: Optional[str] = None description: Optional[str] = None class SystemConfigResponse(SystemConfigBase): value: Optional[Any] = None updated_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class SystemStatus(BaseModel): total_topics: int topics_by_status: Dict[str, int] ready_topics: List[TopicResponse] today_articles: int compliance_rate: float last_optimization: Optional[datetime] = None execution_time: Optional[float] = None class OptimizationRequest(BaseModel): topic_ids: Optional[List[str]] = None class BatchPublishRequest(BaseModel): date: str