217 lines
4.8 KiB
Python
217 lines
4.8 KiB
Python
from typing import Literal
|
|
from pydantic import BaseModel
|
|
|
|
|
|
class PlanInput(BaseModel):
|
|
clinic_name: str | None = None
|
|
clinic_name_en: str | None = None
|
|
address: str | None = None
|
|
phone: str | None = None
|
|
slogan: str | None = None
|
|
services: str | None = None
|
|
doctors: str | None = None
|
|
report: str | None = None
|
|
market_competitors: str | None = None
|
|
market_keywords: str | None = None
|
|
market_trend: str | None = None
|
|
market_target_audience: str | None = None
|
|
tiktok: str | None = None
|
|
instagram_en: str | None = None
|
|
facebook_en: str | None = None
|
|
naver_blog: str | None = None
|
|
channel_logos: str | None = None
|
|
brand_assets: str | None = None
|
|
|
|
|
|
# --- BrandGuide ---
|
|
|
|
class ColorSwatch(BaseModel):
|
|
name: str
|
|
hex: str
|
|
usage: str
|
|
|
|
|
|
class FontSpec(BaseModel):
|
|
family: str
|
|
weight: str
|
|
usage: str
|
|
sample_text: str
|
|
|
|
|
|
class LogoUsageRule(BaseModel):
|
|
rule: str
|
|
description: str
|
|
correct: bool
|
|
|
|
|
|
class ToneOfVoice(BaseModel):
|
|
personality: list[str]
|
|
communication_style: str
|
|
do_examples: list[str]
|
|
dont_examples: list[str]
|
|
|
|
|
|
class ChannelBrandingRule(BaseModel):
|
|
channel: str
|
|
icon: str
|
|
profile_photo: str
|
|
banner_spec: str
|
|
bio_template: str
|
|
current_status: Literal["correct", "incorrect", "N/A"]
|
|
|
|
|
|
class BrandPlanInconsistencyValue(BaseModel):
|
|
channel: str
|
|
value: str
|
|
is_correct: bool
|
|
|
|
|
|
class BrandPlanInconsistency(BaseModel):
|
|
field: str
|
|
values: list[BrandPlanInconsistencyValue]
|
|
impact: str
|
|
recommendation: str
|
|
|
|
|
|
class BrandGuide(BaseModel):
|
|
colors: list[ColorSwatch]
|
|
fonts: list[FontSpec]
|
|
logo_rules: list[LogoUsageRule]
|
|
tone_of_voice: ToneOfVoice
|
|
channel_branding: list[ChannelBrandingRule]
|
|
brand_inconsistencies: list[BrandPlanInconsistency]
|
|
|
|
|
|
# --- ChannelStrategy ---
|
|
|
|
class ChannelStrategyCard(BaseModel):
|
|
channel_id: str
|
|
channel_name: str
|
|
icon: str
|
|
current_status: str
|
|
target_goal: str
|
|
content_types: list[str]
|
|
posting_frequency: str
|
|
tone: str
|
|
format_guidelines: list[str]
|
|
priority: Literal["P0", "P1", "P2"]
|
|
customer_journey_stage: Literal["awareness", "interest", "consideration", "conversion", "loyalty"] | None = None
|
|
|
|
|
|
# --- ContentStrategy ---
|
|
|
|
class ContentPillar(BaseModel):
|
|
title: str
|
|
description: str
|
|
related_usp: str
|
|
example_topics: list[str]
|
|
color: str
|
|
|
|
|
|
class ContentTypeRow(BaseModel):
|
|
format: str
|
|
channels: list[str]
|
|
frequency: str
|
|
purpose: str
|
|
|
|
|
|
class WorkflowStep(BaseModel):
|
|
step: int
|
|
name: str
|
|
description: str
|
|
owner: str
|
|
duration: str
|
|
|
|
|
|
class RepurposingOutput(BaseModel):
|
|
format: str
|
|
channel: str
|
|
description: str
|
|
|
|
|
|
class ContentStrategyData(BaseModel):
|
|
pillars: list[ContentPillar]
|
|
type_matrix: list[ContentTypeRow]
|
|
workflow: list[WorkflowStep]
|
|
repurposing_source: str
|
|
repurposing_outputs: list[RepurposingOutput]
|
|
|
|
|
|
# --- Calendar ---
|
|
|
|
class CalendarEntry(BaseModel):
|
|
day_of_week: int
|
|
channel: str
|
|
channel_icon: str
|
|
content_type: Literal["video", "blog", "social", "ad"]
|
|
title: str
|
|
id: str | None = None
|
|
description: str | None = None
|
|
pillar: str | None = None
|
|
status: Literal["draft", "approved", "published"] | None = None
|
|
is_manual_edit: bool | None = None
|
|
ai_prompt_seed: str | None = None
|
|
|
|
|
|
class CalendarWeek(BaseModel):
|
|
week_number: int
|
|
label: str
|
|
entries: list[CalendarEntry]
|
|
|
|
|
|
class ContentCountSummary(BaseModel):
|
|
type: Literal["video", "blog", "social", "ad"]
|
|
label: str
|
|
count: int
|
|
color: str
|
|
|
|
|
|
class CalendarData(BaseModel):
|
|
weeks: list[CalendarWeek]
|
|
monthly_summary: list[ContentCountSummary]
|
|
|
|
|
|
# --- AssetCollection ---
|
|
|
|
class AssetCard(BaseModel):
|
|
id: str
|
|
source: Literal["homepage", "naver_place", "blog", "social", "youtube"]
|
|
source_label: str
|
|
type: Literal["photo", "video", "text"]
|
|
title: str
|
|
description: str
|
|
repurposing_suggestions: list[str]
|
|
status: Literal["collected", "pending", "needs_creation"]
|
|
|
|
|
|
class YouTubeRepurposeItem(BaseModel):
|
|
title: str
|
|
views: int
|
|
type: Literal["Short", "Long"]
|
|
repurpose_as: list[str]
|
|
|
|
|
|
class AssetCollectionData(BaseModel):
|
|
assets: list[AssetCard]
|
|
youtube_repurpose: list[YouTubeRepurposeItem]
|
|
|
|
|
|
# --- Repurposing ---
|
|
|
|
class RepurposingProposalItem(BaseModel):
|
|
source_video: YouTubeRepurposeItem
|
|
outputs: list[RepurposingOutput]
|
|
estimated_effort: Literal["low", "medium", "high"]
|
|
priority: Literal["high", "medium", "low"]
|
|
|
|
|
|
# --- PlanOutput ---
|
|
|
|
class PlanOutput(BaseModel):
|
|
brand_guide: BrandGuide
|
|
channel_strategies: list[ChannelStrategyCard]
|
|
content_strategy: ContentStrategyData
|
|
calendar: CalendarData
|
|
asset_collection: AssetCollectionData
|
|
repurposing_proposals: list[RepurposingProposalItem] | None = None
|