""" Pydantic schemas for Overlay API responses """ from datetime import datetime from typing import Any, Dict, List, Optional from pydantic import BaseModel, Field class OverlayFeatures(BaseModel): headline_burst_z: Optional[float] = None youtube_influence_z: Optional[float] = None wiki_attention_z: Optional[float] = None theme_heat_z: Optional[float] = None crowding_stress_z: Optional[float] = None class OverlaySourcePresence(BaseModel): yahoo: bool = False youtube: bool = False wikimedia: bool = False google_trends: bool = False finra: bool = False class YahooSourceDetail(BaseModel): headline_count_6h: int = 0 headline_count_24h: int = 0 publisher_breadth_24h: int = 0 class YouTubeSourceDetail(BaseModel): mentions_24h: int = 0 weighted_views_24h: float = 0.0 class WikiSourceDetail(BaseModel): page_views_1d: Optional[int] = None page_views_7d_avg: Optional[float] = None class FinraSourceDetail(BaseModel): short_volume_ratio: Optional[float] = None short_volume_spike_zscore: Optional[float] = None class OverlaySourceDetails(BaseModel): yahoo: Optional[YahooSourceDetail] = None youtube: Optional[YouTubeSourceDetail] = None wikimedia: Optional[WikiSourceDetail] = None finra: Optional[FinraSourceDetail] = None class OverlayMetadata(BaseModel): feature_version: str = "v1" data_freshness: Optional[datetime] = None next_update_expected: Optional[datetime] = None class OverlayScoreResponse(BaseModel): symbol: str as_of_ts: Optional[datetime] = None overlay_score: float = 0.0 overlay_confidence: float = 0.0 overlay_band: Optional[str] = None hold_extension_hint: Optional[str] = None add_on_eligibility: Optional[bool] = None features: OverlayFeatures = Field(default_factory=OverlayFeatures) source_presence: OverlaySourcePresence = Field(default_factory=OverlaySourcePresence) source_details: OverlaySourceDetails = Field(default_factory=OverlaySourceDetails) metadata: OverlayMetadata = Field(default_factory=OverlayMetadata) class BulkOverlayResponse(BaseModel): results: List[OverlayScoreResponse] total_count: int metadata: Dict[str, Any] = Field(default_factory=dict) class OverlayTopMover(BaseModel): symbol: str overlay_score: float overlay_band: Optional[str] = None as_of_ts: Optional[datetime] = None class TopMoversResponse(BaseModel): top_movers: List[OverlayTopMover] total_count: int metadata: Dict[str, Any] = Field(default_factory=dict) class HeadlineItem(BaseModel): title: str publisher: Optional[str] = None published_at: datetime article_guid: str class HeadlinesResponse(BaseModel): symbol: str headlines: List[HeadlineItem] headline_count_6h: int = 0 headline_count_24h: int = 0 publisher_breadth_24h: int = 0 metadata: Dict[str, Any] = Field(default_factory=dict) class VideoItem(BaseModel): video_id: str channel_id: str title: str view_count: int = 0 comment_count: int = 0 published_at: Optional[datetime] = None channel_weight: float = 0.5 class YouTubeResponse(BaseModel): symbol: str videos: List[VideoItem] mentions_24h: int = 0 weighted_views_24h: float = 0.0 metadata: Dict[str, Any] = Field(default_factory=dict) class WikiPageviewPoint(BaseModel): date: datetime views: int page_title: str class WikiResponse(BaseModel): symbol: str pageviews: List[WikiPageviewPoint] views_1d: Optional[int] = None views_7d_avg: Optional[float] = None metadata: Dict[str, Any] = Field(default_factory=dict) class CrowdingResponse(BaseModel): symbol: str short_volume_ratio: Optional[float] = None short_volume_spike_zscore: Optional[float] = None crowding_stress_z: Optional[float] = None metadata: Dict[str, Any] = Field(default_factory=dict) class TrendPoint(BaseModel): observed_at: datetime interest_value: int topic_id: str topic_label: Optional[str] = None class TrendsResponse(BaseModel): symbol: str trends: List[TrendPoint] theme_heat_z: Optional[float] = None metadata: Dict[str, Any] = Field(default_factory=dict) class OverlayHistoryPoint(BaseModel): as_of_ts: datetime overlay_score: float overlay_confidence: float overlay_band: Optional[str] = None class OverlayHistoryResponse(BaseModel): symbol: str history: List[OverlayHistoryPoint] metadata: Dict[str, Any] = Field(default_factory=dict) class SourceHealthItem(BaseModel): source: str last_collected_at: Optional[datetime] = None status: str = "unknown" success_rate_24h: Optional[float] = None records_24h: int = 0 class AdminHealthResponse(BaseModel): overlay_enabled: bool sources: List[SourceHealthItem] last_pipeline_run: Optional[datetime] = None metadata: Dict[str, Any] = Field(default_factory=dict) class TriggerPipelineResponse(BaseModel): status: str message: str job_ids: List[str] = Field(default_factory=list) class JobLogEntry(BaseModel): id: str job_type: str status: str started_at: datetime completed_at: Optional[datetime] = None records_processed: int = 0 error_message: Optional[str] = None class JobLogResponse(BaseModel): logs: List[JobLogEntry] total_count: int metadata: Dict[str, Any] = Field(default_factory=dict)