""" Configuration module for WordPress SEO automation. Loads and validates environment variables and YAML configuration. """ import os import yaml from dotenv import load_dotenv from pathlib import Path # Load environment variables from .env file load_dotenv() class Config: """Configuration class for WordPress SEO automation.""" # Load configuration from YAML file CONFIG_FILE = Path(__file__).parent.parent / 'config.yaml' if CONFIG_FILE.exists(): with open(CONFIG_FILE, 'r', encoding='utf-8') as f: YAML_CONFIG = yaml.safe_load(f) else: YAML_CONFIG = {} # WordPress Settings (Primary site) WORDPRESS_URL = os.getenv('WORDPRESS_URL', YAML_CONFIG.get('primary_site', {}).get('url', '')).rstrip('/') WORDPRESS_USERNAME = os.getenv('WORDPRESS_USERNAME', YAML_CONFIG.get('primary_site', {}).get('username', '')) WORDPRESS_APP_PASSWORD = os.getenv('WORDPRESS_APP_PASSWORD', YAML_CONFIG.get('primary_site', {}).get('password', '')) # Multi-site WordPress Configuration WORDPRESS_SITES = { 'mistergeek.net': { 'url': os.getenv('WORDPRESS_MISTERGEEK_URL', YAML_CONFIG.get('wordpress_sites', {}).get('mistergeek.net', {}).get('url', 'https://www.mistergeek.net')), 'username': os.getenv('WORDPRESS_MISTERGEEK_USERNAME', os.getenv('WORDPRESS_USERNAME', YAML_CONFIG.get('wordpress_sites', {}).get('mistergeek.net', {}).get('username', ''))), 'password': os.getenv('WORDPRESS_MISTERGEEK_PASSWORD', os.getenv('WORDPRESS_APP_PASSWORD', YAML_CONFIG.get('wordpress_sites', {}).get('mistergeek.net', {}).get('password', ''))), }, 'webscroll.fr': { 'url': os.getenv('WORDPRESS_WEBSCROLL_URL', YAML_CONFIG.get('wordpress_sites', {}).get('webscroll.fr', {}).get('url', 'https://www.webscroll.fr')), 'username': os.getenv('WORDPRESS_WEBSCROLL_USERNAME', os.getenv('WORDPRESS_USERNAME', YAML_CONFIG.get('wordpress_sites', {}).get('webscroll.fr', {}).get('username', ''))), 'password': os.getenv('WORDPRESS_WEBSCROLL_PASSWORD', os.getenv('WORDPRESS_APP_PASSWORD', YAML_CONFIG.get('wordpress_sites', {}).get('webscroll.fr', {}).get('password', ''))), }, 'hellogeek.net': { 'url': os.getenv('WORDPRESS_HELLOGEEK_URL', YAML_CONFIG.get('wordpress_sites', {}).get('hellogeek.net', {}).get('url', 'https://www.hellogeek.net')), 'username': os.getenv('WORDPRESS_HELLOGEEK_USERNAME', os.getenv('WORDPRESS_USERNAME', YAML_CONFIG.get('wordpress_sites', {}).get('hellogeek.net', {}).get('username', ''))), 'password': os.getenv('WORDPRESS_HELLOGEEK_PASSWORD', os.getenv('WORDPRESS_APP_PASSWORD', YAML_CONFIG.get('wordpress_sites', {}).get('hellogeek.net', {}).get('password', ''))), } } # OpenRouter API Settings OPENROUTER_API_KEY = os.getenv('OPENROUTER_API_KEY', YAML_CONFIG.get('ai_model', {}).get('api_key', '')) AI_MODEL = os.getenv('AI_MODEL', YAML_CONFIG.get('ai_model', {}).get('name', 'anthropic/claude-3.5-sonnet')) # Script Settings BATCH_SIZE = int(os.getenv('BATCH_SIZE', str(YAML_CONFIG.get('script_settings', {}).get('batch_size', 100)))) API_DELAY_SECONDS = float(os.getenv('API_DELAY_SECONDS', str(YAML_CONFIG.get('script_settings', {}).get('api_delay_seconds', 0.5)))) # Analysis Settings ANALYSIS_MIN_POSITION = int(os.getenv('ANALYSIS_MIN_POSITION', str(YAML_CONFIG.get('analysis_settings', {}).get('min_position', 11)))) ANALYSIS_MAX_POSITION = int(os.getenv('ANALYSIS_MAX_POSITION', str(YAML_CONFIG.get('analysis_settings', {}).get('max_position', 30)))) ANALYSIS_MIN_IMPRESSIONS = int(os.getenv('ANALYSIS_MIN_IMPRESSIONS', str(YAML_CONFIG.get('analysis_settings', {}).get('min_impressions', 50)))) ANALYSIS_TOP_N_POSTS = int(os.getenv('ANALYSIS_TOP_N_POSTS', str(YAML_CONFIG.get('analysis_settings', {}).get('top_n_posts', 20)))) # Output directory OUTPUT_DIR = Path(os.getenv('OUTPUT_DIR', YAML_CONFIG.get('output_settings', {}).get('output_dir', './output'))) @classmethod def validate(cls): """Validate that all required configuration is present.""" errors = [] if not cls.WORDPRESS_URL: errors.append("WORDPRESS_URL is required") if not cls.WORDPRESS_USERNAME: errors.append("WORDPRESS_USERNAME is required") if not cls.WORDPRESS_APP_PASSWORD: errors.append("WORDPRESS_APP_PASSWORD is required") if not cls.OPENROUTER_API_KEY: errors.append("OPENROUTER_API_KEY is required (get one from https://openrouter.ai/)") if errors: raise ValueError("Configuration errors:\n" + "\n".join(f" - {e}" for e in errors)) # Create output directory if it doesn't exist cls.OUTPUT_DIR.mkdir(exist_ok=True) return True @classmethod def get_wordpress_auth(cls): """Get WordPress authentication tuple.""" return (cls.WORDPRESS_USERNAME, cls.WORDPRESS_APP_PASSWORD) @classmethod def get_api_base_url(cls): """Get WordPress REST API base URL.""" return f"{cls.WORDPRESS_URL}/wp-json/wp/v2" @classmethod def get_site_config(cls, site_name): """Get configuration for a specific site.""" return cls.WORDPRESS_SITES.get(site_name, {}) @classmethod def get_all_sites(cls): """Get all configured WordPress sites.""" return cls.WORDPRESS_SITES.keys()