Add author filter to update_meta command
- Add --author-filter option to filter posts by author name - Resolve author names to IDs via WordPress API - Support partial matching for author names - Works with other filters (category, limit, post-ids) - Fix argparse conflict with existing --author flag Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -324,6 +324,7 @@ class SEOApp:
|
||||
post_ids: Optional[List[int]] = None,
|
||||
category_names: Optional[List[str]] = None,
|
||||
category_ids: Optional[List[int]] = None,
|
||||
author_names: Optional[List[str]] = None,
|
||||
limit: Optional[int] = None,
|
||||
dry_run: bool = False,
|
||||
skip_existing: bool = True,
|
||||
@@ -336,6 +337,7 @@ class SEOApp:
|
||||
post_ids: Specific post IDs to update
|
||||
category_names: Filter by category names
|
||||
category_ids: Filter by category IDs
|
||||
author_names: Filter by author names
|
||||
limit: Maximum number of posts to process
|
||||
dry_run: If True, preview changes without updating
|
||||
skip_existing: If True, skip posts with existing good quality meta descriptions
|
||||
@@ -354,6 +356,7 @@ class SEOApp:
|
||||
post_ids=post_ids,
|
||||
category_ids=category_ids,
|
||||
category_names=category_names,
|
||||
author_names=author_names,
|
||||
limit=limit,
|
||||
dry_run=dry_run,
|
||||
skip_existing=skip_existing,
|
||||
|
||||
@@ -78,6 +78,7 @@ Examples:
|
||||
parser.add_argument('--post-ids', type=int, nargs='+', help='Specific post IDs to update')
|
||||
parser.add_argument('--category', nargs='+', help='Filter by category name(s)')
|
||||
parser.add_argument('--category-id', type=int, nargs='+', help='Filter by category ID(s)')
|
||||
parser.add_argument('--author-filter', nargs='+', dest='author_filter', help='Filter by author name(s)')
|
||||
parser.add_argument('--force', action='store_true', help='Force regenerate even for good quality meta descriptions')
|
||||
|
||||
args = parser.parse_args()
|
||||
@@ -446,6 +447,8 @@ def cmd_update_meta(app, args):
|
||||
print(f" Post IDs: {args.post_ids}")
|
||||
if args.category:
|
||||
print(f" Categories: {args.category}")
|
||||
if args.author_filter:
|
||||
print(f" Authors: {args.author_filter}")
|
||||
if args.limit:
|
||||
print(f" Limit: {args.limit} posts")
|
||||
return 0
|
||||
@@ -460,6 +463,8 @@ def cmd_update_meta(app, args):
|
||||
print(f" Post IDs: {args.post_ids}")
|
||||
if args.category:
|
||||
print(f" Categories: {args.category}")
|
||||
if args.author_filter:
|
||||
print(f" Authors: {args.author_filter}")
|
||||
if args.category_id:
|
||||
print(f" Category IDs: {args.category_id}")
|
||||
if args.limit:
|
||||
@@ -472,6 +477,7 @@ def cmd_update_meta(app, args):
|
||||
post_ids=args.post_ids,
|
||||
category_names=args.category,
|
||||
category_ids=args.category_id,
|
||||
author_names=args.author_filter,
|
||||
limit=args.limit,
|
||||
dry_run=args.dry_run,
|
||||
skip_existing=not args.force,
|
||||
@@ -562,6 +568,7 @@ Update Meta Options:
|
||||
--post-ids Specific post IDs to update
|
||||
--category Filter by category name(s)
|
||||
--category-id Filter by category ID(s)
|
||||
--author-filter Filter by author name(s)
|
||||
--force Force regenerate even for good quality meta descriptions
|
||||
|
||||
Migration Options:
|
||||
@@ -609,6 +616,7 @@ Examples:
|
||||
seo update_meta --site mistergeek.net # Update all posts on site
|
||||
seo update_meta --site A --post-ids 1 2 3 # Update specific posts
|
||||
seo update_meta --site A --category "VPN" --limit 10 # Update 10 posts in category
|
||||
seo update_meta --site A --author-filter "john" --limit 10 # Update 10 posts by author
|
||||
seo update_meta --site A --dry-run # Preview changes
|
||||
seo status
|
||||
""")
|
||||
|
||||
@@ -57,6 +57,7 @@ class MetaDescriptionUpdater:
|
||||
def fetch_posts(self, post_ids: Optional[List[int]] = None,
|
||||
category_ids: Optional[List[int]] = None,
|
||||
category_names: Optional[List[str]] = None,
|
||||
author_names: Optional[List[str]] = None,
|
||||
limit: Optional[int] = None,
|
||||
status: Optional[List[str]] = None) -> List[Dict]:
|
||||
"""
|
||||
@@ -66,6 +67,7 @@ class MetaDescriptionUpdater:
|
||||
post_ids: Specific post IDs to fetch
|
||||
category_ids: Filter by category IDs
|
||||
category_names: Filter by category names (will be resolved to IDs)
|
||||
author_names: Filter by author names
|
||||
limit: Maximum number of posts to fetch
|
||||
status: Post statuses to fetch (default: ['publish'])
|
||||
|
||||
@@ -80,6 +82,8 @@ class MetaDescriptionUpdater:
|
||||
logger.info(f" Category IDs: {category_ids}")
|
||||
if category_names:
|
||||
logger.info(f" Category names: {category_names}")
|
||||
if author_names:
|
||||
logger.info(f" Authors: {author_names}")
|
||||
if limit:
|
||||
logger.info(f" Limit: {limit}")
|
||||
|
||||
@@ -87,6 +91,11 @@ class MetaDescriptionUpdater:
|
||||
if category_names and not category_ids:
|
||||
category_ids = self._get_category_ids_by_names(category_names)
|
||||
|
||||
# Resolve author names to IDs if needed
|
||||
author_ids = None
|
||||
if author_names:
|
||||
author_ids = self._get_author_ids_by_names(author_names)
|
||||
|
||||
# Build API parameters
|
||||
params = {
|
||||
'per_page': 100,
|
||||
@@ -94,7 +103,7 @@ class MetaDescriptionUpdater:
|
||||
'status': ','.join(status) if status else 'publish',
|
||||
'_embed': True
|
||||
}
|
||||
|
||||
|
||||
if post_ids:
|
||||
# Fetch specific posts
|
||||
posts = []
|
||||
@@ -117,6 +126,9 @@ class MetaDescriptionUpdater:
|
||||
if category_ids:
|
||||
params['categories'] = ','.join(map(str, category_ids))
|
||||
|
||||
if author_ids:
|
||||
params['author'] = ','.join(map(str, author_ids))
|
||||
|
||||
posts = []
|
||||
while True:
|
||||
try:
|
||||
@@ -200,7 +212,65 @@ class MetaDescriptionUpdater:
|
||||
except Exception as e:
|
||||
logger.error(f"Error fetching categories: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def _get_author_ids_by_names(self, author_names: List[str]) -> List[int]:
|
||||
"""
|
||||
Get author/user IDs by author names.
|
||||
|
||||
Args:
|
||||
author_names: List of author names
|
||||
|
||||
Returns:
|
||||
List of author IDs
|
||||
"""
|
||||
logger.info(f"Resolving author names to IDs...")
|
||||
|
||||
try:
|
||||
response = requests.get(
|
||||
f"{self.base_url}/wp-json/wp/v2/users",
|
||||
params={'per_page': 100},
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
users = response.json()
|
||||
author_map = {}
|
||||
|
||||
# Build map of name/slug to ID
|
||||
for user in users:
|
||||
name = user.get('name', '').lower()
|
||||
slug = user.get('slug', '').lower()
|
||||
author_map[name] = user['id']
|
||||
author_map[slug] = user['id']
|
||||
|
||||
author_ids = []
|
||||
for name in author_names:
|
||||
name_lower = name.lower()
|
||||
|
||||
# Try exact match
|
||||
if name_lower in author_map:
|
||||
author_ids.append(author_map[name_lower])
|
||||
logger.info(f" ✓ '{name}' -> ID {author_map[name_lower]}")
|
||||
else:
|
||||
# Try partial match
|
||||
found = False
|
||||
for author_name, author_id in author_map.items():
|
||||
if name_lower in author_name or author_name in name_lower:
|
||||
author_ids.append(author_id)
|
||||
logger.info(f" ✓ '{name}' -> ID {author_id} (partial match: '{author_name}')")
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
logger.warning(f" ✗ Author '{name}' not found")
|
||||
|
||||
return author_ids
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error fetching authors: {e}")
|
||||
return []
|
||||
|
||||
def _generate_meta_description(self, post: Dict) -> Optional[str]:
|
||||
"""
|
||||
Generate meta description for a post using AI.
|
||||
@@ -519,22 +589,24 @@ Return ONLY the meta description text, nothing else. No quotes, no explanations.
|
||||
def run(self, post_ids: Optional[List[int]] = None,
|
||||
category_ids: Optional[List[int]] = None,
|
||||
category_names: Optional[List[str]] = None,
|
||||
author_names: Optional[List[str]] = None,
|
||||
limit: Optional[int] = None,
|
||||
dry_run: bool = False,
|
||||
skip_existing: bool = False,
|
||||
force_regenerate: bool = False) -> Dict:
|
||||
"""
|
||||
Run complete meta description update process.
|
||||
|
||||
|
||||
Args:
|
||||
post_ids: Specific post IDs to update
|
||||
category_ids: Filter by category IDs
|
||||
category_names: Filter by category names
|
||||
author_names: Filter by author names
|
||||
limit: Maximum number of posts to process
|
||||
dry_run: If True, preview changes without updating
|
||||
skip_existing: If True, skip posts with existing meta descriptions
|
||||
force_regenerate: If True, regenerate even for good quality metas
|
||||
|
||||
|
||||
Returns:
|
||||
Statistics dict
|
||||
"""
|
||||
@@ -543,13 +615,14 @@ Return ONLY the meta description text, nothing else. No quotes, no explanations.
|
||||
post_ids=post_ids,
|
||||
category_ids=category_ids,
|
||||
category_names=category_names,
|
||||
author_names=author_names,
|
||||
limit=limit
|
||||
)
|
||||
|
||||
|
||||
if not self.posts:
|
||||
logger.warning("No posts found matching criteria")
|
||||
return self.stats
|
||||
|
||||
|
||||
# Update posts
|
||||
return self.update_posts(
|
||||
dry_run=dry_run,
|
||||
|
||||
Reference in New Issue
Block a user