Files
mcphub/plugins/wordpress/schemas/seo.py
airano c73e39f6e4 fix(ci): fix black/ruff failures, add CoC and PR template
- Fix Python formatting (sync no longer strips blank lines from .py files)
- Remove test_community_build.py (tests private sync module)
- Fix ruff warnings in test files
- Add CODE_OF_CONDUCT.md
- Add .github/PULL_REQUEST_TEMPLATE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 18:19:39 +03:30

85 lines
3.4 KiB
Python

"""
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")