Major refactoring to create a clean, integrated CLI application: ### New Features: - Unified CLI executable (./seo) with simple command structure - All commands accept optional CSV file arguments - Auto-detection of latest files when no arguments provided - Simplified output directory structure (output/ instead of output/reports/) - Cleaner export filename format (all_posts_YYYY-MM-DD.csv) ### Commands: - export: Export all posts from WordPress sites - analyze [csv]: Analyze posts with AI (optional CSV input) - recategorize [csv]: Recategorize posts with AI - seo_check: Check SEO quality - categories: Manage categories across sites - approve [files]: Review and approve recommendations - full_pipeline: Run complete workflow - analytics, gaps, opportunities, report, status ### Changes: - Moved all scripts to scripts/ directory - Created config.yaml for configuration - Updated all scripts to use output/ directory - Deprecated old seo-cli.py in favor of new ./seo - Added AGENTS.md and CHANGELOG.md documentation - Consolidated README.md with updated usage ### Technical: - Added PyYAML dependency - Removed hardcoded configuration values - All scripts now properly integrated - Better error handling and user feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
389 lines
14 KiB
Python
Executable File
389 lines
14 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
DEPRECATED: SEO Automation CLI
|
|
|
|
This script is deprecated. Please use the new unified CLI:
|
|
- ./seo export
|
|
- ./seo analyze
|
|
- ./seo seo_check
|
|
- ./seo categories
|
|
- ./seo full_pipeline
|
|
|
|
To see all commands: ./seo help
|
|
"""
|
|
|
|
import sys
|
|
import subprocess
|
|
import argparse
|
|
from pathlib import Path
|
|
from config import Config
|
|
import os
|
|
|
|
class SEOCLI:
|
|
"""DEPRECATED: Main CLI orchestrator for SEO workflows. Use new ./seo CLI instead."""
|
|
|
|
def __init__(self):
|
|
"""Initialize CLI."""
|
|
print("⚠️ DEPRECATION WARNING: This CLI is deprecated. Use ./seo instead.")
|
|
print(" Run './seo help' to see new commands.")
|
|
self.scripts_dir = Path(__file__).parent
|
|
self.project_dir = self.scripts_dir.parent
|
|
self.output_dir = self.project_dir / 'output' / 'reports'
|
|
|
|
def run_command(self, command, description):
|
|
"""Run a command and show progress."""
|
|
print(f"\n{'='*70}")
|
|
print(f"▶ {description}")
|
|
print(f"{'='*70}\n")
|
|
|
|
try:
|
|
result = subprocess.run(command, shell=True, cwd=self.project_dir)
|
|
if result.returncode != 0:
|
|
print(f"\n❌ Error running: {description}")
|
|
return False
|
|
print(f"\n✓ {description} completed successfully")
|
|
return True
|
|
except Exception as e:
|
|
print(f"\n❌ Error: {e}")
|
|
return False
|
|
|
|
def get_latest_file(self, pattern):
|
|
"""Get most recent file matching pattern."""
|
|
import glob
|
|
# Support both old and new naming patterns
|
|
files = glob.glob(str(self.output_dir / pattern))
|
|
if not files:
|
|
# Try new pattern
|
|
files = glob.glob(str(self.output_dir / "all_posts_*.csv"))
|
|
if not files:
|
|
return None
|
|
return max(files, key=os.path.getctime)
|
|
|
|
def export_posts(self):
|
|
"""Export all posts to CSV."""
|
|
cmd = f"python {self.scripts_dir}/export_posts_for_ai_decision.py"
|
|
return self.run_command(cmd, "STEP 1: Export All Posts")
|
|
|
|
def analyze_with_ai(self, csv_file=None):
|
|
"""Analyze exported posts with AI."""
|
|
if not csv_file:
|
|
csv_file = self.get_latest_file("all_posts_for_ai_decision_*.csv")
|
|
|
|
if not csv_file:
|
|
print("\n❌ No exported CSV found. Run 'seo-cli export' first.")
|
|
return False
|
|
|
|
cmd = f"python {self.scripts_dir}/ai_analyze_posts_for_decisions.py \"{csv_file}\""
|
|
return self.run_command(cmd, "STEP 2: Analyze with AI")
|
|
|
|
def recategorize_with_ai(self, csv_file=None):
|
|
"""Recategorize posts using AI."""
|
|
if not csv_file:
|
|
csv_file = self.get_latest_file("all_posts_for_ai_decision_*.csv")
|
|
|
|
if not csv_file:
|
|
print("\n❌ No exported CSV found. Run 'seo-cli export' first.")
|
|
return False
|
|
|
|
cmd = f"python {self.scripts_dir}/ai_recategorize_posts.py \"{csv_file}\""
|
|
return self.run_command(cmd, "Recategorizing Posts with AI")
|
|
|
|
def seo_check(self, top_n=None):
|
|
"""Check SEO quality of titles and meta descriptions."""
|
|
cmd = f"python {self.scripts_dir}/multi_site_seo_analyzer.py"
|
|
if top_n:
|
|
cmd += f" --top-n {top_n}"
|
|
|
|
return self.run_command(cmd, f"SEO Quality Check (Top {top_n or 'All'} posts)")
|
|
|
|
def import_analytics(self, ga_export, gsc_export, posts_csv=None):
|
|
"""Import analytics data."""
|
|
if not posts_csv:
|
|
posts_csv = self.get_latest_file("all_posts_for_ai_decision_*.csv")
|
|
|
|
if not posts_csv:
|
|
print("\n❌ No posts CSV found. Run 'seo-cli export' first.")
|
|
return False
|
|
|
|
cmd = (
|
|
f"python {self.scripts_dir}/analytics_importer.py "
|
|
f"--ga-export \"{ga_export}\" "
|
|
f"--gsc-export \"{gsc_export}\" "
|
|
f"--posts-csv \"{posts_csv}\" "
|
|
f"--output output/posts_with_analytics.csv"
|
|
)
|
|
return self.run_command(cmd, "STEP: Import Analytics Data")
|
|
|
|
def full_pipeline(self, analyze=True, seo=True):
|
|
"""Run complete pipeline: export → analyze → seo check."""
|
|
steps = [
|
|
("Export", self.export_posts),
|
|
]
|
|
|
|
if analyze:
|
|
steps.append(("Analyze", self.analyze_with_ai))
|
|
|
|
if seo:
|
|
steps.append(("SEO Check", self.seo_check))
|
|
|
|
print("\n" + "="*70)
|
|
print("🚀 STARTING FULL PIPELINE")
|
|
print("="*70)
|
|
print(f"\nSteps to run: {', '.join([s[0] for s in steps])}\n")
|
|
|
|
completed = 0
|
|
for name, func in steps:
|
|
if func():
|
|
completed += 1
|
|
else:
|
|
print(f"\n⚠️ Pipeline stopped at: {name}")
|
|
return False
|
|
|
|
print("\n" + "="*70)
|
|
print(f"✓ PIPELINE COMPLETE - All {completed} steps succeeded!")
|
|
print("="*70)
|
|
print("\nNext steps:")
|
|
print("1. Review results in output/reports/")
|
|
print("2. Check: posts_with_ai_recommendations_*.csv")
|
|
print("3. Follow AI recommendations to optimize your content")
|
|
return True
|
|
|
|
def manage_categories(self):
|
|
"""Run category management with AI recommendations."""
|
|
cmd = f"python {self.scripts_dir}/category_manager.py"
|
|
return self.run_command(cmd, "Category Management with AI Recommendations")
|
|
|
|
def approve_recommendations(self, csv_files=None):
|
|
"""Approve recommendations from CSV files."""
|
|
if not csv_files:
|
|
print("\n❌ No CSV files provided for approval.")
|
|
return False
|
|
|
|
# Join the CSV files into a single command argument
|
|
csv_files_str = " ".join(f'"{csv_file}"' for csv_file in csv_files)
|
|
cmd = f"python {self.scripts_dir}/user_approval.py {csv_files_str}"
|
|
return self.run_command(cmd, f"Approving Recommendations from {len(csv_files)} files")
|
|
|
|
def show_status(self):
|
|
"""Show status of output files."""
|
|
print("\n" + "="*70)
|
|
print("📊 OUTPUT FILES STATUS")
|
|
print("="*70 + "\n")
|
|
|
|
import glob
|
|
files = glob.glob(str(self.output_dir / "*"))
|
|
|
|
if not files:
|
|
print("No output files yet. Run 'seo-cli export' to get started.\n")
|
|
return
|
|
|
|
# Sort by date
|
|
files.sort(key=os.path.getctime, reverse=True)
|
|
|
|
for file in files[:10]: # Show last 10 files
|
|
size = os.path.getsize(file) / 1024 # KB
|
|
mtime = os.path.getmtime(file)
|
|
from datetime import datetime
|
|
date = datetime.fromtimestamp(mtime).strftime('%Y-%m-%d %H:%M:%S')
|
|
filename = os.path.basename(file)
|
|
|
|
print(f" {filename}")
|
|
print(f" Size: {size:.1f} KB | Modified: {date}")
|
|
print()
|
|
|
|
def list_workflows(self):
|
|
"""List available workflows."""
|
|
workflows = {
|
|
'export': {
|
|
'description': 'Export all posts from your 3 WordPress sites',
|
|
'command': 'seo-cli export',
|
|
'time': '5-10 min',
|
|
'cost': 'Free'
|
|
},
|
|
'analyze': {
|
|
'description': 'Analyze exported posts with Claude AI',
|
|
'command': 'seo-cli analyze',
|
|
'time': '5-15 min',
|
|
'cost': '$1.50-2.00'
|
|
},
|
|
'recategorize': {
|
|
'description': 'Re-categorize posts for better organization',
|
|
'command': 'seo-cli recategorize',
|
|
'time': '5-15 min',
|
|
'cost': '$1.50-2.00'
|
|
},
|
|
'seo-check': {
|
|
'description': 'Check SEO quality of titles and descriptions',
|
|
'command': 'seo-cli seo-check [--top-n 50]',
|
|
'time': '3-5 min',
|
|
'cost': 'Free or $0.20-0.50'
|
|
},
|
|
'analytics': {
|
|
'description': 'Combine Google Analytics & Search Console data',
|
|
'command': 'seo-cli analytics GA4.csv GSC.csv',
|
|
'time': '5 min',
|
|
'cost': 'Free'
|
|
},
|
|
'full-pipeline': {
|
|
'description': 'Run complete pipeline: export → analyze → seo-check',
|
|
'command': 'seo-cli full-pipeline',
|
|
'time': '15-30 min',
|
|
'cost': '$1.50-2.50'
|
|
},
|
|
'categories': {
|
|
'description': 'Manage categories across all sites with AI recommendations',
|
|
'command': 'seo-cli categories',
|
|
'time': '10-20 min',
|
|
'cost': '$0.50-1.00'
|
|
},
|
|
'approve': {
|
|
'description': 'Review and approve SEO recommendations',
|
|
'command': 'seo-cli approve [csv_file1] [csv_file2]',
|
|
'time': 'Variable',
|
|
'cost': 'Free'
|
|
}
|
|
}
|
|
|
|
print("\n" + "="*70)
|
|
print("📋 AVAILABLE WORKFLOWS")
|
|
print("="*70 + "\n")
|
|
|
|
for name, info in workflows.items():
|
|
print(f"🔹 {name.upper()}")
|
|
print(f" {info['description']}")
|
|
print(f" Command: {info['command']}")
|
|
print(f" Time: {info['time']} | Cost: {info['cost']}")
|
|
print()
|
|
|
|
def show_help(self):
|
|
"""Show help message."""
|
|
print("\n" + "="*70)
|
|
print("🚀 SEO AUTOMATION CLI - Workflow Orchestrator")
|
|
print("="*70 + "\n")
|
|
|
|
print("QUICK START:")
|
|
print(" seo-cli full-pipeline Run complete workflow")
|
|
print(" seo-cli export Export all posts")
|
|
print(" seo-cli analyze Analyze with AI")
|
|
print(" seo-cli recategorize Re-categorize posts with AI")
|
|
print(" seo-cli seo-check Check SEO quality")
|
|
print()
|
|
|
|
print("CHAINING WORKFLOWS:")
|
|
print(" seo-cli export && seo-cli analyze && seo-cli seo-check")
|
|
print()
|
|
|
|
print("ADVANCED:")
|
|
print(" seo-cli seo-check --top-n 50 Check top 50 posts")
|
|
print(" seo-cli analytics GA4.csv GSC.csv Import analytics data")
|
|
print(" seo-cli status Show output files")
|
|
print(" seo-cli list List all workflows")
|
|
print()
|
|
|
|
print("Learn more:")
|
|
print(" Read: WORKFLOWS.md (complete guide)")
|
|
print(" Read: scripts/*/README.md (workflow details)")
|
|
print()
|
|
|
|
|
|
def main():
|
|
"""Main entry point."""
|
|
cli = SEOCLI()
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description='SEO Automation CLI - Chain workflows together',
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
seo-cli export # Export posts
|
|
seo-cli full-pipeline # Export + Analyze + SEO check
|
|
seo-cli export && seo-cli analyze # Chain commands
|
|
seo-cli seo-check --top-n 50 # Check top 50 posts
|
|
seo-cli analytics ga4.csv gsc.csv # Import analytics
|
|
seo-cli status # Show output files
|
|
"""
|
|
)
|
|
|
|
subparsers = parser.add_subparsers(dest='command', help='Workflow to run')
|
|
|
|
# Export workflow
|
|
subparsers.add_parser('export', help='Export all posts from WordPress sites')
|
|
|
|
# Analyze workflow
|
|
subparsers.add_parser('analyze', help='Analyze exported posts with Claude AI')
|
|
|
|
# Recategorize workflow
|
|
subparsers.add_parser('recategorize', help='Re-categorize posts with Claude AI')
|
|
|
|
# SEO check workflow
|
|
seo_parser = subparsers.add_parser('seo-check', help='Check SEO quality of titles/descriptions')
|
|
seo_parser.add_argument('--top-n', type=int, help='Analyze top N posts with AI (costs money)')
|
|
|
|
# Analytics workflow
|
|
analytics_parser = subparsers.add_parser('analytics', help='Import Google Analytics & Search Console')
|
|
analytics_parser.add_argument('ga_export', help='Path to GA4 export CSV')
|
|
analytics_parser.add_argument('gsc_export', help='Path to Search Console export CSV')
|
|
|
|
# Full pipeline
|
|
full_parser = subparsers.add_parser('full-pipeline', help='Complete pipeline: export → analyze → seo-check')
|
|
full_parser.add_argument('--no-analyze', action='store_true', help='Skip AI analysis')
|
|
full_parser.add_argument('--no-seo', action='store_true', help='Skip SEO check')
|
|
|
|
# Category management
|
|
subparsers.add_parser('categories', help='Manage categories with AI recommendations')
|
|
|
|
# Approval system
|
|
approval_parser = subparsers.add_parser('approve', help='Approve recommendations from CSV files')
|
|
approval_parser.add_argument('csv_files', nargs='*', help='CSV files containing recommendations to approve')
|
|
|
|
# Utilities
|
|
subparsers.add_parser('status', help='Show status of output files')
|
|
subparsers.add_parser('list', help='List all available workflows')
|
|
subparsers.add_parser('help', help='Show this help message')
|
|
|
|
args = parser.parse_args()
|
|
|
|
# If no command, show help
|
|
if not args.command:
|
|
cli.show_help()
|
|
return 0
|
|
|
|
# Route to appropriate command
|
|
if args.command == 'export':
|
|
success = cli.export_posts()
|
|
elif args.command == 'analyze':
|
|
success = cli.analyze_with_ai()
|
|
elif args.command == 'recategorize':
|
|
success = cli.recategorize_with_ai()
|
|
elif args.command == 'seo-check':
|
|
success = cli.seo_check(top_n=args.top_n)
|
|
elif args.command == 'analytics':
|
|
success = cli.import_analytics(args.ga_export, args.gsc_export)
|
|
elif args.command == 'full-pipeline':
|
|
success = cli.full_pipeline(
|
|
analyze=not args.no_analyze,
|
|
seo=not args.no_seo
|
|
)
|
|
elif args.command == 'categories':
|
|
success = cli.manage_categories()
|
|
elif args.command == 'approve':
|
|
success = cli.approve_recommendations(args.csv_files)
|
|
elif args.command == 'status':
|
|
cli.show_status()
|
|
success = True
|
|
elif args.command == 'list':
|
|
cli.list_workflows()
|
|
success = True
|
|
elif args.command == 'help':
|
|
cli.show_help()
|
|
success = True
|
|
else:
|
|
cli.show_help()
|
|
success = False
|
|
|
|
return 0 if success else 1
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|