from __future__ import annotations from pydantic import BaseModel from models.status import Severity, ChannelStatus, DataSource, Language, VideoType, AnnotationType class ScreenshotAnnotation(BaseModel): type: AnnotationType x: float y: float width: float | None = None height: float | None = None label: str | None = None color: str | None = None class ScreenshotEvidence(BaseModel): id: str url: str channel: str captured_at: str caption: str source_url: str | None = None annotations: list[ScreenshotAnnotation] | None = None class DiagnosisItem(BaseModel): category: str detail: str severity: Severity evidence_ids: list[str] | None = None # --- ClinicSnapshot --- class LeadDoctor(BaseModel): name: str credentials: str rating: float review_count: int class PriceRange(BaseModel): min: str max: str currency: str class LogoImages(BaseModel): circle: str | None = None horizontal: str | None = None korean: str | None = None class BrandColors(BaseModel): primary: str accent: str text: str class RegistryData(BaseModel): district: str | None = None branches: str | None = None brand_group: str | None = None website_en: str | None = None naver_place_url: str | None = None gangnam_unni_url: str | None = None google_maps_url: str | None = None class ClinicSnapshot(BaseModel): name: str name_en: str staff_count: int lead_doctor: LeadDoctor overall_rating: float total_reviews: int certifications: list[str] location: str phone: str domain: str logo_images: LogoImages | None = None brand_colors: BrandColors | None = None source: DataSource | None = None registry_data: RegistryData | None = None # --- ChannelScore --- class ChannelScore(BaseModel): channel: str icon: str score: int max_score: int status: Severity headline: str # --- YouTube --- class WeeklyViewGrowth(BaseModel): absolute: int percentage: float class EstimatedRevenue(BaseModel): min: int max: int class LinkedUrl(BaseModel): label: str url: str class TopVideo(BaseModel): title: str views: int uploaded_ago: str type: VideoType duration: str | None = None class YouTubeAudit(BaseModel): channel_name: str handle: str subscribers: int total_videos: int total_views: int weekly_view_growth: WeeklyViewGrowth estimated_monthly_revenue: EstimatedRevenue avg_video_length: str upload_frequency: str channel_created_date: str channel_description: str linked_urls: list[LinkedUrl] playlists: list[str] top_videos: list[TopVideo] diagnosis: list[DiagnosisItem] # --- Instagram --- class InstagramAccount(BaseModel): handle: str language: Language label: str posts: int followers: int following: int category: str profile_link: str highlights: list[str] reels_count: int content_format: str profile_photo: str bio: str class InstagramAudit(BaseModel): accounts: list[InstagramAccount] diagnosis: list[DiagnosisItem] # --- Facebook --- class BrandInconsistencyValue(BaseModel): channel: str value: str is_correct: bool class BrandInconsistency(BaseModel): field: str values: list[BrandInconsistencyValue] impact: str recommendation: str class FacebookPage(BaseModel): url: str page_name: str language: Language label: str followers: int following: int category: str bio: str logo: str logo_description: str link: str linked_domain: str reviews: int recent_post_age: str has_whatsapp: bool post_frequency: str | None = None top_content_type: str | None = None engagement: str | None = None class FacebookAudit(BaseModel): pages: list[FacebookPage] diagnosis: list[DiagnosisItem] brand_inconsistencies: list[BrandInconsistency] consolidation_recommendation: str # --- 기타 채널 / 웹사이트 --- class OtherChannel(BaseModel): name: str status: ChannelStatus details: str url: str | None = None class TrackingPixel(BaseModel): name: str installed: bool details: str | None = None class SnsLink(BaseModel): platform: str url: str location: str class AdditionalDomain(BaseModel): domain: str purpose: str class WebsiteAudit(BaseModel): primary_domain: str additional_domains: list[AdditionalDomain] sns_links_on_site: bool sns_links_detail: list[SnsLink] | None = None tracking_pixels: list[TrackingPixel] main_cta: str # --- Transformation --- class AsIsToBeItem(BaseModel): area: str as_is: str to_be: str class StrategyDetail(BaseModel): strategy: str detail: str class PlatformStrategy(BaseModel): platform: str icon: str current_metric: str target_metric: str strategies: list[StrategyDetail] class NewChannelProposal(BaseModel): channel: str priority: str rationale: str class TransformationProposal(BaseModel): brand_identity: list[AsIsToBeItem] content_strategy: list[AsIsToBeItem] platform_strategies: list[PlatformStrategy] website_improvements: list[AsIsToBeItem] new_channel_proposals: list[NewChannelProposal] # --- Roadmap / KPI --- class RoadmapTask(BaseModel): task: str completed: bool class RoadmapMonth(BaseModel): month: int title: str subtitle: str tasks: list[RoadmapTask] class KPIMetric(BaseModel): metric: str current: str target_3_month: str target_12_month: str # --- ReportInput (prompt 템플릿 변수) --- class ReportInput(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 instagram: str | None = None facebook: str | None = None naver_blog: str | None = None youtube: str | None = None gangnam_unni: str | None = None market_competitors: str | None = None market_keywords: str | None = None market_trend: str | None = None market_target_audience: str | None = None # --- MarketingReport --- class MarketingReport(BaseModel): id: str created_at: str target_url: str overall_score: int clinic_snapshot: ClinicSnapshot channel_scores: list[ChannelScore] youtube_audit: YouTubeAudit instagram_audit: InstagramAudit facebook_audit: FacebookAudit other_channels: list[OtherChannel] website_audit: WebsiteAudit problem_diagnosis: list[DiagnosisItem] transformation: TransformationProposal roadmap: list[RoadmapMonth] kpi_dashboard: list[KPIMetric] screenshots: list[ScreenshotEvidence] | None = None ReportOutput = MarketingReport # --- YouTubeDiagnosis --- class YouTubeDiagnosisInput(BaseModel): channel_name: str | None = None subscribers: int | None = None total_videos: int | None = None total_views: int | None = None avg_video_length: str | None = None upload_frequency: str | None = None top_videos: str | None = None playlists: str | None = None class YouTubeDiagnosisOutput(BaseModel): diagnosis: list[DiagnosisItem] # --- BrandConsistency --- class BrandConsistencyInput(BaseModel): clinic_name: str | None = None mainpage: str | None = None instagram: str | None = None facebook: str | None = None youtube: str | None = None gangnam_unni: str | None = None class BrandConsistencyOutput(BaseModel): brand_inconsistencies: list[BrandInconsistency]