""" SEO Pydantic Schemas Validation schemas for SEO plugin data (Yoast, RankMath, etc.). """ from pydantic import BaseModel, ConfigDict, Field, field_validator class SEOData(BaseModel): """Schema for SEO data (read)""" model_config = ConfigDict(extra="allow") title: str | None = Field(None, description="SEO title") description: str | None = Field(None, description="Meta description") keywords: str | None = Field(None, description="Focus keywords") canonical: str | None = Field(None, description="Canonical URL") og_title: str | None = Field(None, description="Open Graph title") og_description: str | None = Field(None, description="Open Graph description") og_image: str | None = Field(None, description="Open Graph image URL") twitter_title: str | None = Field(None, description="Twitter card title") twitter_description: str | None = Field(None, description="Twitter card description") twitter_image: str | None = Field(None, description="Twitter card image URL") robots: list[str] | None = Field(None, description="Robots meta tags") class SEOUpdate(BaseModel): """Schema for SEO data updates""" title: str | None = Field( None, max_length=60, description="SEO title (max 60 chars recommended)" ) description: str | None = Field( None, max_length=160, description="Meta description (max 160 chars recommended)" ) keywords: str | None = Field(None, description="Focus keywords (comma-separated)") canonical: str | None = Field(None, description="Canonical URL") og_title: str | None = Field(None, description="Open Graph title") og_description: str | None = Field(None, description="Open Graph description") og_image: str | None = Field(None, description="Open Graph image URL") twitter_title: str | None = Field(None, description="Twitter card title") twitter_description: str | None = Field(None, description="Twitter card description") twitter_image: str | None = Field(None, description="Twitter card image URL") robots_index: bool | None = Field(None, description="Allow search engines to index") robots_follow: bool | None = Field(None, description="Allow search engines to follow links") model_config = ConfigDict(extra="allow") @classmethod @field_validator("title") def validate_title_length(cls, v): if v and len(v) > 70: # Warning, not error - allow but discourage pass return v @classmethod @field_validator("description") def validate_description_length(cls, v): if v and len(v) > 200: # Warning, not error - allow but discourage pass return v class YoastSEO(SEOData): """Yoast SEO specific data""" model_config = ConfigDict(extra="allow") yoast_wpseo_focuskw: str | None = Field(None, description="Yoast focus keyword") yoast_wpseo_metadesc: str | None = Field(None, description="Yoast meta description") yoast_wpseo_title: str | None = Field(None, description="Yoast SEO title") class RankMathSEO(SEOData): """RankMath SEO specific data""" model_config = ConfigDict(extra="allow") rank_math_focus_keyword: str | None = Field(None, description="RankMath focus keyword") rank_math_description: str | None = Field(None, description="RankMath meta description") rank_math_title: str | None = Field(None, description="RankMath SEO title")