Initial commit: Leopost Full — merge di Leopost, Post Generator e Autopilot OS
- Backend FastAPI con multi-LLM (Claude/OpenAI/Gemini) - Publishing su Facebook, Instagram, YouTube, TikTok - Calendario editoriale con awareness levels (PAS, AIDA, BAB...) - Design system Editorial Fresh (Fraunces + DM Sans) - Scheduler automatico, gestione commenti AI, affiliate links Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
320
backend/app/schemas.py
Normal file
320
backend/app/schemas.py
Normal file
@@ -0,0 +1,320 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
# === Auth ===
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
class Token(BaseModel):
|
||||
access_token: str
|
||||
token_type: str = "bearer"
|
||||
|
||||
|
||||
# === Characters ===
|
||||
|
||||
class CharacterBase(BaseModel):
|
||||
name: str
|
||||
niche: str
|
||||
topics: list[str] = []
|
||||
tone: Optional[str] = None
|
||||
visual_style: dict = {}
|
||||
social_accounts: dict = {}
|
||||
affiliate_links: list[dict] = []
|
||||
avatar_url: Optional[str] = None
|
||||
is_active: bool = True
|
||||
|
||||
|
||||
class CharacterCreate(CharacterBase):
|
||||
pass
|
||||
|
||||
|
||||
class CharacterUpdate(BaseModel):
|
||||
name: Optional[str] = None
|
||||
niche: Optional[str] = None
|
||||
topics: Optional[list[str]] = None
|
||||
tone: Optional[str] = None
|
||||
visual_style: Optional[dict] = None
|
||||
social_accounts: Optional[dict] = None
|
||||
affiliate_links: Optional[list[dict]] = None
|
||||
avatar_url: Optional[str] = None
|
||||
is_active: Optional[bool] = None
|
||||
|
||||
|
||||
class CharacterResponse(CharacterBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# === Posts / Content ===
|
||||
|
||||
class PostCreate(BaseModel):
|
||||
character_id: int
|
||||
content_type: str = "text"
|
||||
platform_hint: str = "instagram"
|
||||
text_content: Optional[str] = None
|
||||
hashtags: list[str] = []
|
||||
image_url: Optional[str] = None
|
||||
video_url: Optional[str] = None
|
||||
media_urls: list[str] = []
|
||||
affiliate_links_used: list[dict] = []
|
||||
status: str = "draft"
|
||||
|
||||
|
||||
class PostUpdate(BaseModel):
|
||||
text_content: Optional[str] = None
|
||||
hashtags: Optional[list[str]] = None
|
||||
image_url: Optional[str] = None
|
||||
video_url: Optional[str] = None
|
||||
status: Optional[str] = None
|
||||
affiliate_links_used: Optional[list[dict]] = None
|
||||
|
||||
|
||||
class PostResponse(BaseModel):
|
||||
id: int
|
||||
character_id: int
|
||||
content_type: str
|
||||
text_content: Optional[str] = None
|
||||
hashtags: list[str] = []
|
||||
image_url: Optional[str] = None
|
||||
video_url: Optional[str] = None
|
||||
media_urls: list[str] = []
|
||||
affiliate_links_used: list[dict] = []
|
||||
llm_provider: Optional[str] = None
|
||||
llm_model: Optional[str] = None
|
||||
platform_hint: Optional[str] = None
|
||||
status: str
|
||||
created_at: datetime
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class GenerateContentRequest(BaseModel):
|
||||
character_id: int
|
||||
platform: str = "instagram"
|
||||
content_type: str = "text"
|
||||
topic_hint: Optional[str] = None
|
||||
include_affiliates: bool = True
|
||||
provider: Optional[str] = None # override default LLM
|
||||
model: Optional[str] = None
|
||||
|
||||
|
||||
class GenerateImageRequest(BaseModel):
|
||||
character_id: int
|
||||
prompt: Optional[str] = None # auto-generated if not provided
|
||||
style_hint: Optional[str] = None
|
||||
size: str = "1024x1024"
|
||||
provider: Optional[str] = None # dalle, replicate
|
||||
|
||||
|
||||
# === Affiliate Links ===
|
||||
|
||||
class AffiliateLinkBase(BaseModel):
|
||||
character_id: Optional[int] = None
|
||||
network: str
|
||||
name: str
|
||||
url: str
|
||||
tag: Optional[str] = None
|
||||
topics: list[str] = []
|
||||
is_active: bool = True
|
||||
|
||||
|
||||
class AffiliateLinkCreate(AffiliateLinkBase):
|
||||
pass
|
||||
|
||||
|
||||
class AffiliateLinkUpdate(BaseModel):
|
||||
network: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
url: Optional[str] = None
|
||||
tag: Optional[str] = None
|
||||
topics: Optional[list[str]] = None
|
||||
is_active: Optional[bool] = None
|
||||
|
||||
|
||||
class AffiliateLinkResponse(AffiliateLinkBase):
|
||||
id: int
|
||||
click_count: int = 0
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# === Editorial Plans ===
|
||||
|
||||
class EditorialPlanBase(BaseModel):
|
||||
character_id: int
|
||||
name: str
|
||||
frequency: str = "daily"
|
||||
posts_per_day: int = 1
|
||||
platforms: list[str] = []
|
||||
content_types: list[str] = ["text"]
|
||||
posting_times: list[str] = ["09:00"]
|
||||
start_date: Optional[datetime] = None
|
||||
end_date: Optional[datetime] = None
|
||||
is_active: bool = False
|
||||
|
||||
|
||||
class EditorialPlanCreate(EditorialPlanBase):
|
||||
pass
|
||||
|
||||
|
||||
class EditorialPlanUpdate(BaseModel):
|
||||
name: Optional[str] = None
|
||||
frequency: Optional[str] = None
|
||||
posts_per_day: Optional[int] = None
|
||||
platforms: Optional[list[str]] = None
|
||||
content_types: Optional[list[str]] = None
|
||||
posting_times: Optional[list[str]] = None
|
||||
start_date: Optional[datetime] = None
|
||||
end_date: Optional[datetime] = None
|
||||
is_active: Optional[bool] = None
|
||||
|
||||
|
||||
class EditorialPlanResponse(EditorialPlanBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# === Scheduled Posts ===
|
||||
|
||||
class ScheduledPostCreate(BaseModel):
|
||||
plan_id: Optional[int] = None
|
||||
post_id: int
|
||||
platform: str
|
||||
scheduled_at: datetime
|
||||
|
||||
|
||||
class ScheduledPostResponse(BaseModel):
|
||||
id: int
|
||||
plan_id: Optional[int] = None
|
||||
post_id: int
|
||||
platform: str
|
||||
scheduled_at: datetime
|
||||
published_at: Optional[datetime] = None
|
||||
status: str
|
||||
error_message: Optional[str] = None
|
||||
external_post_id: Optional[str] = None
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# === Social Accounts ===
|
||||
|
||||
class SocialAccountCreate(BaseModel):
|
||||
character_id: int
|
||||
platform: str
|
||||
account_name: Optional[str] = None
|
||||
account_id: Optional[str] = None
|
||||
access_token: Optional[str] = None
|
||||
refresh_token: Optional[str] = None
|
||||
page_id: Optional[str] = None
|
||||
extra_data: dict = {}
|
||||
|
||||
|
||||
class SocialAccountUpdate(BaseModel):
|
||||
account_name: Optional[str] = None
|
||||
access_token: Optional[str] = None
|
||||
refresh_token: Optional[str] = None
|
||||
page_id: Optional[str] = None
|
||||
extra_data: Optional[dict] = None
|
||||
is_active: Optional[bool] = None
|
||||
|
||||
|
||||
class SocialAccountResponse(BaseModel):
|
||||
id: int
|
||||
character_id: int
|
||||
platform: str
|
||||
account_name: Optional[str] = None
|
||||
account_id: Optional[str] = None
|
||||
page_id: Optional[str] = None
|
||||
is_active: bool
|
||||
token_expires_at: Optional[datetime] = None
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# === Comments ===
|
||||
|
||||
class CommentResponse(BaseModel):
|
||||
id: int
|
||||
scheduled_post_id: Optional[int] = None
|
||||
platform: str
|
||||
external_comment_id: Optional[str] = None
|
||||
author_name: Optional[str] = None
|
||||
content: Optional[str] = None
|
||||
ai_suggested_reply: Optional[str] = None
|
||||
approved_reply: Optional[str] = None
|
||||
reply_status: str
|
||||
replied_at: Optional[datetime] = None
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class CommentAction(BaseModel):
|
||||
action: str # approve, edit, ignore
|
||||
reply_text: Optional[str] = None # for edit action
|
||||
|
||||
|
||||
# === System Settings ===
|
||||
|
||||
class SettingUpdate(BaseModel):
|
||||
key: str
|
||||
value: dict | str | list | int | bool | None
|
||||
|
||||
|
||||
class SettingResponse(BaseModel):
|
||||
key: str
|
||||
value: dict | str | list | int | bool | None
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# === Editorial Calendar (from PostGenerator) ===
|
||||
|
||||
class CalendarGenerateRequest(BaseModel):
|
||||
topics: list[str]
|
||||
format_narrativo: Optional[str] = None # PAS, AIDA, BAB, Storytelling, Listicle, Dato_Implicazione
|
||||
awareness_level: Optional[int] = None # 1-5 (Schwartz levels)
|
||||
num_posts: int = 7
|
||||
start_date: Optional[str] = None # YYYY-MM-DD
|
||||
character_id: Optional[int] = None
|
||||
|
||||
|
||||
class CalendarSlotResponse(BaseModel):
|
||||
indice: int
|
||||
topic: str
|
||||
formato_narrativo: str
|
||||
awareness_level: int
|
||||
awareness_label: str
|
||||
data_pubblicazione: str
|
||||
note: Optional[str] = None
|
||||
|
||||
|
||||
class CalendarResponse(BaseModel):
|
||||
slots: list[CalendarSlotResponse]
|
||||
totale_post: int
|
||||
Reference in New Issue
Block a user