""" Pydantic schemas for Yield/Intent Routing feature. """ from datetime import datetime from decimal import Decimal from typing import Optional from pydantic import BaseModel, Field # ============================================================================ # Intent Detection # ============================================================================ class IntentAnalysis(BaseModel): """Intent detection result for a domain.""" category: str subcategory: Optional[str] = None confidence: float = Field(ge=0.0, le=1.0) keywords_matched: list[str] = [] suggested_partners: list[str] = [] monetization_potential: str # "high", "medium", "low" class YieldValueEstimate(BaseModel): """Estimated yield value for a domain.""" estimated_monthly_min: int estimated_monthly_max: int currency: str = "CHF" potential: str confidence: float geo: Optional[str] = None class DomainYieldAnalysis(BaseModel): """Complete yield analysis for a domain.""" domain: str intent: IntentAnalysis value: YieldValueEstimate partners: list[str] = [] monetization_potential: str # ============================================================================ # Yield Domain CRUD # ============================================================================ class YieldDomainCreate(BaseModel): """Create a new yield domain.""" domain: str = Field(..., min_length=3, max_length=255) class YieldDomainUpdate(BaseModel): """Update yield domain settings.""" active_route: Optional[str] = None landing_page_url: Optional[str] = None status: Optional[str] = None class YieldDomainResponse(BaseModel): """Yield domain response.""" id: int domain: str status: str # Intent detected_intent: Optional[str] = None intent_confidence: float = 0.0 # Routing active_route: Optional[str] = None partner_name: Optional[str] = None # DNS dns_verified: bool = False dns_verified_at: Optional[datetime] = None connected_at: Optional[datetime] = None # Stats total_clicks: int = 0 total_conversions: int = 0 total_revenue: Decimal = Decimal("0") currency: str = "CHF" # Timestamps activated_at: Optional[datetime] = None created_at: datetime class Config: from_attributes = True class YieldDomainListResponse(BaseModel): """List of yield domains with summary stats.""" domains: list[YieldDomainResponse] total: int # Aggregates total_active: int = 0 total_revenue: Decimal = Decimal("0") total_clicks: int = 0 # ============================================================================ # Transactions # ============================================================================ class YieldTransactionResponse(BaseModel): """Single transaction record.""" id: int event_type: str partner_slug: str click_id: Optional[str] = None gross_amount: Decimal net_amount: Decimal currency: str status: str geo_country: Optional[str] = None created_at: datetime confirmed_at: Optional[datetime] = None class Config: from_attributes = True class YieldTransactionListResponse(BaseModel): """List of transactions.""" transactions: list[YieldTransactionResponse] total: int # Aggregates total_gross: Decimal = Decimal("0") total_net: Decimal = Decimal("0") # ============================================================================ # Payouts # ============================================================================ class YieldPayoutResponse(BaseModel): """Payout record.""" id: int amount: Decimal currency: str period_start: datetime period_end: datetime transaction_count: int status: str payment_method: Optional[str] = None payment_reference: Optional[str] = None created_at: datetime completed_at: Optional[datetime] = None class Config: from_attributes = True class YieldPayoutListResponse(BaseModel): """List of payouts.""" payouts: list[YieldPayoutResponse] total: int total_paid: Decimal = Decimal("0") total_pending: Decimal = Decimal("0") # ============================================================================ # Dashboard # ============================================================================ class YieldDashboardStats(BaseModel): """Yield dashboard statistics.""" # Domain counts total_domains: int = 0 active_domains: int = 0 pending_domains: int = 0 # Revenue (current month) monthly_revenue: Decimal = Decimal("0") monthly_clicks: int = 0 monthly_conversions: int = 0 # Lifetime lifetime_revenue: Decimal = Decimal("0") lifetime_clicks: int = 0 lifetime_conversions: int = 0 # Pending payout pending_payout: Decimal = Decimal("0") next_payout_date: Optional[datetime] = None currency: str = "CHF" class YieldDashboardResponse(BaseModel): """Complete yield dashboard data.""" stats: YieldDashboardStats domains: list[YieldDomainResponse] recent_transactions: list[YieldTransactionResponse] top_domains: list[YieldDomainResponse] # ============================================================================ # Partners # ============================================================================ class AffiliatePartnerResponse(BaseModel): """Affiliate partner info (public view).""" slug: str name: str network: str intent_categories: list[str] geo_countries: list[str] payout_type: str description: Optional[str] = None logo_url: Optional[str] = None class Config: from_attributes = True # ============================================================================ # DNS Verification # ============================================================================ class DNSVerificationResult(BaseModel): """Result of DNS verification check.""" domain: str verified: bool expected_ns: list[str] actual_ns: list[str] cname_ok: bool = False error: Optional[str] = None checked_at: datetime class DNSSetupInstructions(BaseModel): """DNS setup instructions for a domain.""" domain: str # Option 1: Nameserver delegation nameservers: list[str] # Option 2: CNAME cname_host: str cname_target: str # Verification verification_url: str # ============================================================================ # Activation Flow # ============================================================================ class ActivateYieldRequest(BaseModel): """Request to activate a domain for yield.""" domain: str = Field(..., min_length=3, max_length=255) accept_terms: bool = False class ActivateYieldResponse(BaseModel): """Response after initiating yield activation.""" domain_id: int domain: str status: str # Analysis intent: IntentAnalysis value_estimate: YieldValueEstimate # Setup dns_instructions: DNSSetupInstructions message: str