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,
|
post_ids: Optional[List[int]] = None,
|
||||||
category_names: Optional[List[str]] = None,
|
category_names: Optional[List[str]] = None,
|
||||||
category_ids: Optional[List[int]] = None,
|
category_ids: Optional[List[int]] = None,
|
||||||
|
author_names: Optional[List[str]] = None,
|
||||||
limit: Optional[int] = None,
|
limit: Optional[int] = None,
|
||||||
dry_run: bool = False,
|
dry_run: bool = False,
|
||||||
skip_existing: bool = True,
|
skip_existing: bool = True,
|
||||||
@@ -336,6 +337,7 @@ class SEOApp:
|
|||||||
post_ids: Specific post IDs to update
|
post_ids: Specific post IDs to update
|
||||||
category_names: Filter by category names
|
category_names: Filter by category names
|
||||||
category_ids: Filter by category IDs
|
category_ids: Filter by category IDs
|
||||||
|
author_names: Filter by author names
|
||||||
limit: Maximum number of posts to process
|
limit: Maximum number of posts to process
|
||||||
dry_run: If True, preview changes without updating
|
dry_run: If True, preview changes without updating
|
||||||
skip_existing: If True, skip posts with existing good quality meta descriptions
|
skip_existing: If True, skip posts with existing good quality meta descriptions
|
||||||
@@ -354,6 +356,7 @@ class SEOApp:
|
|||||||
post_ids=post_ids,
|
post_ids=post_ids,
|
||||||
category_ids=category_ids,
|
category_ids=category_ids,
|
||||||
category_names=category_names,
|
category_names=category_names,
|
||||||
|
author_names=author_names,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
dry_run=dry_run,
|
dry_run=dry_run,
|
||||||
skip_existing=skip_existing,
|
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('--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', nargs='+', help='Filter by category name(s)')
|
||||||
parser.add_argument('--category-id', type=int, nargs='+', help='Filter by category ID(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')
|
parser.add_argument('--force', action='store_true', help='Force regenerate even for good quality meta descriptions')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@@ -446,6 +447,8 @@ def cmd_update_meta(app, args):
|
|||||||
print(f" Post IDs: {args.post_ids}")
|
print(f" Post IDs: {args.post_ids}")
|
||||||
if args.category:
|
if args.category:
|
||||||
print(f" Categories: {args.category}")
|
print(f" Categories: {args.category}")
|
||||||
|
if args.author_filter:
|
||||||
|
print(f" Authors: {args.author_filter}")
|
||||||
if args.limit:
|
if args.limit:
|
||||||
print(f" Limit: {args.limit} posts")
|
print(f" Limit: {args.limit} posts")
|
||||||
return 0
|
return 0
|
||||||
@@ -460,6 +463,8 @@ def cmd_update_meta(app, args):
|
|||||||
print(f" Post IDs: {args.post_ids}")
|
print(f" Post IDs: {args.post_ids}")
|
||||||
if args.category:
|
if args.category:
|
||||||
print(f" Categories: {args.category}")
|
print(f" Categories: {args.category}")
|
||||||
|
if args.author_filter:
|
||||||
|
print(f" Authors: {args.author_filter}")
|
||||||
if args.category_id:
|
if args.category_id:
|
||||||
print(f" Category IDs: {args.category_id}")
|
print(f" Category IDs: {args.category_id}")
|
||||||
if args.limit:
|
if args.limit:
|
||||||
@@ -472,6 +477,7 @@ def cmd_update_meta(app, args):
|
|||||||
post_ids=args.post_ids,
|
post_ids=args.post_ids,
|
||||||
category_names=args.category,
|
category_names=args.category,
|
||||||
category_ids=args.category_id,
|
category_ids=args.category_id,
|
||||||
|
author_names=args.author_filter,
|
||||||
limit=args.limit,
|
limit=args.limit,
|
||||||
dry_run=args.dry_run,
|
dry_run=args.dry_run,
|
||||||
skip_existing=not args.force,
|
skip_existing=not args.force,
|
||||||
@@ -562,6 +568,7 @@ Update Meta Options:
|
|||||||
--post-ids Specific post IDs to update
|
--post-ids Specific post IDs to update
|
||||||
--category Filter by category name(s)
|
--category Filter by category name(s)
|
||||||
--category-id Filter by category ID(s)
|
--category-id Filter by category ID(s)
|
||||||
|
--author-filter Filter by author name(s)
|
||||||
--force Force regenerate even for good quality meta descriptions
|
--force Force regenerate even for good quality meta descriptions
|
||||||
|
|
||||||
Migration Options:
|
Migration Options:
|
||||||
@@ -609,6 +616,7 @@ Examples:
|
|||||||
seo update_meta --site mistergeek.net # Update all posts on site
|
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 --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 --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 update_meta --site A --dry-run # Preview changes
|
||||||
seo status
|
seo status
|
||||||
""")
|
""")
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ class MetaDescriptionUpdater:
|
|||||||
def fetch_posts(self, post_ids: Optional[List[int]] = None,
|
def fetch_posts(self, post_ids: Optional[List[int]] = None,
|
||||||
category_ids: Optional[List[int]] = None,
|
category_ids: Optional[List[int]] = None,
|
||||||
category_names: Optional[List[str]] = None,
|
category_names: Optional[List[str]] = None,
|
||||||
|
author_names: Optional[List[str]] = None,
|
||||||
limit: Optional[int] = None,
|
limit: Optional[int] = None,
|
||||||
status: Optional[List[str]] = None) -> List[Dict]:
|
status: Optional[List[str]] = None) -> List[Dict]:
|
||||||
"""
|
"""
|
||||||
@@ -66,6 +67,7 @@ class MetaDescriptionUpdater:
|
|||||||
post_ids: Specific post IDs to fetch
|
post_ids: Specific post IDs to fetch
|
||||||
category_ids: Filter by category IDs
|
category_ids: Filter by category IDs
|
||||||
category_names: Filter by category names (will be resolved to 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
|
limit: Maximum number of posts to fetch
|
||||||
status: Post statuses to fetch (default: ['publish'])
|
status: Post statuses to fetch (default: ['publish'])
|
||||||
|
|
||||||
@@ -80,6 +82,8 @@ class MetaDescriptionUpdater:
|
|||||||
logger.info(f" Category IDs: {category_ids}")
|
logger.info(f" Category IDs: {category_ids}")
|
||||||
if category_names:
|
if category_names:
|
||||||
logger.info(f" Category names: {category_names}")
|
logger.info(f" Category names: {category_names}")
|
||||||
|
if author_names:
|
||||||
|
logger.info(f" Authors: {author_names}")
|
||||||
if limit:
|
if limit:
|
||||||
logger.info(f" Limit: {limit}")
|
logger.info(f" Limit: {limit}")
|
||||||
|
|
||||||
@@ -87,6 +91,11 @@ class MetaDescriptionUpdater:
|
|||||||
if category_names and not category_ids:
|
if category_names and not category_ids:
|
||||||
category_ids = self._get_category_ids_by_names(category_names)
|
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
|
# Build API parameters
|
||||||
params = {
|
params = {
|
||||||
'per_page': 100,
|
'per_page': 100,
|
||||||
@@ -117,6 +126,9 @@ class MetaDescriptionUpdater:
|
|||||||
if category_ids:
|
if category_ids:
|
||||||
params['categories'] = ','.join(map(str, category_ids))
|
params['categories'] = ','.join(map(str, category_ids))
|
||||||
|
|
||||||
|
if author_ids:
|
||||||
|
params['author'] = ','.join(map(str, author_ids))
|
||||||
|
|
||||||
posts = []
|
posts = []
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
@@ -201,6 +213,64 @@ class MetaDescriptionUpdater:
|
|||||||
logger.error(f"Error fetching categories: {e}")
|
logger.error(f"Error fetching categories: {e}")
|
||||||
return []
|
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]:
|
def _generate_meta_description(self, post: Dict) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Generate meta description for a post using AI.
|
Generate meta description for a post using AI.
|
||||||
@@ -519,6 +589,7 @@ Return ONLY the meta description text, nothing else. No quotes, no explanations.
|
|||||||
def run(self, post_ids: Optional[List[int]] = None,
|
def run(self, post_ids: Optional[List[int]] = None,
|
||||||
category_ids: Optional[List[int]] = None,
|
category_ids: Optional[List[int]] = None,
|
||||||
category_names: Optional[List[str]] = None,
|
category_names: Optional[List[str]] = None,
|
||||||
|
author_names: Optional[List[str]] = None,
|
||||||
limit: Optional[int] = None,
|
limit: Optional[int] = None,
|
||||||
dry_run: bool = False,
|
dry_run: bool = False,
|
||||||
skip_existing: bool = False,
|
skip_existing: bool = False,
|
||||||
@@ -530,6 +601,7 @@ Return ONLY the meta description text, nothing else. No quotes, no explanations.
|
|||||||
post_ids: Specific post IDs to update
|
post_ids: Specific post IDs to update
|
||||||
category_ids: Filter by category IDs
|
category_ids: Filter by category IDs
|
||||||
category_names: Filter by category names
|
category_names: Filter by category names
|
||||||
|
author_names: Filter by author names
|
||||||
limit: Maximum number of posts to process
|
limit: Maximum number of posts to process
|
||||||
dry_run: If True, preview changes without updating
|
dry_run: If True, preview changes without updating
|
||||||
skip_existing: If True, skip posts with existing meta descriptions
|
skip_existing: If True, skip posts with existing meta descriptions
|
||||||
@@ -543,6 +615,7 @@ Return ONLY the meta description text, nothing else. No quotes, no explanations.
|
|||||||
post_ids=post_ids,
|
post_ids=post_ids,
|
||||||
category_ids=category_ids,
|
category_ids=category_ids,
|
||||||
category_names=category_names,
|
category_names=category_names,
|
||||||
|
author_names=author_names,
|
||||||
limit=limit
|
limit=limit
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user