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:
Kevin Bataille
2026-02-17 00:10:20 +01:00
parent 93ea5794f0
commit 66ea25002a
3 changed files with 90 additions and 6 deletions

View File

@@ -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,

View File

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

View File

@@ -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,