o2o-infinith-backend/app/models/report.py

307 lines
7.4 KiB
Python

from __future__ import annotations
from models.common import CamelModel
from models.status import Severity, ChannelStatus, DataSource, Language, VideoType, AnnotationType
class ScreenshotAnnotation(CamelModel):
type: AnnotationType
x: float
y: float
width: float | None = None
height: float | None = None
label: str | None = None
color: str | None = None
class ScreenshotEvidence(CamelModel):
id: str
url: str
channel: str
captured_at: str
caption: str
source_url: str | None = None
annotations: list[ScreenshotAnnotation] | None = None
class DiagnosisItem(CamelModel):
category: str
detail: str
severity: Severity
evidence_ids: list[str] | None = None
class LeadDoctor(CamelModel):
name: str
credentials: str
rating: float
review_count: int
class PriceRange(CamelModel):
min: str
max: str
currency: str
class LogoImages(CamelModel):
circle: str | None = None
horizontal: str | None = None
korean: str | None = None
class BrandColors(CamelModel):
primary: str
accent: str
text: str
class RegistryData(CamelModel):
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(CamelModel):
# _build_clinic_snapshot은 source 데이터 있을 때만 필드 추가 (`if x:` 가드).
# 강남언니/홈페이지 수집 누락된 병원에서 required면 ValidationError로 리포트 전체 실패.
name: str | None = None
name_en: str | None = None
staff_count: int | None = None
lead_doctor: LeadDoctor | None = None
overall_rating: float | None = None
total_reviews: int | None = None
certifications: list[str] = []
location: str | None = None
phone: str | None = None
domain: str | None = None
logo_images: LogoImages | None = None
brand_colors: BrandColors | None = None
source: DataSource | None = None
registry_data: RegistryData | None = None
class ChannelScore(CamelModel):
channel: str
icon: str
score: int
max_score: int
status: Severity
headline: str
class WeeklyViewGrowth(CamelModel):
absolute: int
percentage: float
class EstimatedRevenue(CamelModel):
min: int
max: int
class LinkedUrl(CamelModel):
label: str
url: str
class TopVideo(CamelModel):
title: str
views: int
uploaded_ago: str
type: VideoType
duration: str | None = None
class YouTubeAudit(CamelModel):
# YouTube 채널 없는 병원이면 _build_youtube_audit가 채울 수 없는 필드들 (channel_name 등)이 빔.
# required면 ValidationError로 리포트 실패 → Optional로 받아 부분 응답 허용.
channel_name: str | None = None
handle: str | None = None
subscribers: int | None = None
total_videos: int | None = None
total_views: int | None = None
weekly_view_growth: WeeklyViewGrowth | None = None
estimated_monthly_revenue: EstimatedRevenue | None = None
avg_video_length: str | None = None
upload_frequency: str | None = None
channel_created_date: str | None = None
channel_description: str | None = None
linked_urls: list[LinkedUrl] = []
playlists: list[str] = []
top_videos: list[TopVideo] = []
diagnosis: list[DiagnosisItem] = []
class InstagramAccount(CamelModel):
# 인스타 계정(KR/EN) 미수집 시 빈 필드 가능 — Optional.
handle: str | None = None
language: Language | None = None
label: str | None = None
posts: int | None = None
followers: int | None = None
following: int | None = None
category: str | None = None
profile_link: str | None = None
highlights: list[str] = []
reels_count: int | None = None
content_format: str | None = None
profile_photo: str | None = None
bio: str | None = None
class InstagramAudit(CamelModel):
accounts: list[InstagramAccount] = []
diagnosis: list[DiagnosisItem] = []
class BrandInconsistencyValue(CamelModel):
channel: str
value: str
is_correct: bool
class BrandInconsistency(CamelModel):
field: str
values: list[BrandInconsistencyValue]
impact: str
recommendation: str
class FacebookPage(CamelModel):
# 페북 페이지(KR/EN) 미수집 시 빈 필드 가능 — Optional.
url: str | None = None
page_name: str | None = None
language: Language | None = None
label: str | None = None
followers: int | None = None
following: int | None = None
category: str | None = None
bio: str | None = None
logo: str | None = None
logo_description: str | None = None
link: str | None = None
linked_domain: str | None = None
reviews: int | None = None
recent_post_age: str | None = None
has_whatsapp: bool | None = None
post_frequency: str | None = None
top_content_type: str | None = None
engagement: str | None = None
class FacebookAudit(CamelModel):
pages: list[FacebookPage] = []
diagnosis: list[DiagnosisItem] = []
brand_inconsistencies: list[BrandInconsistency] = []
consolidation_recommendation: str | None = None
class OtherChannel(CamelModel):
name: str
status: ChannelStatus
details: str
url: str | None = None
class TrackingPixel(CamelModel):
name: str
installed: bool
details: str | None = None
class SnsLink(CamelModel):
platform: str
url: str
location: str
class AdditionalDomain(CamelModel):
domain: str
purpose: str
class WebsiteAudit(CamelModel):
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
class AsIsToBeItem(CamelModel):
area: str
as_is: str
to_be: str
class StrategyDetail(CamelModel):
strategy: str
detail: str
class PlatformStrategy(CamelModel):
platform: str
icon: str
current_metric: str
target_metric: str
strategies: list[StrategyDetail]
class NewChannelProposal(CamelModel):
channel: str
priority: str
rationale: str
class TransformationProposal(CamelModel):
brand_identity: list[AsIsToBeItem]
content_strategy: list[AsIsToBeItem]
platform_strategies: list[PlatformStrategy]
website_improvements: list[AsIsToBeItem]
new_channel_proposals: list[NewChannelProposal]
class RoadmapTask(CamelModel):
task: str
completed: bool
class RoadmapMonth(CamelModel):
month: int
title: str
subtitle: str
tasks: list[RoadmapTask]
class KPIMetric(CamelModel):
metric: str
current: str
target_3_month: str
target_12_month: str
class MarketingReportResponse(CamelModel):
id: str
clinic_name: str | None = None
clinic_name_en: str | None = None
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