267 lines
11 KiB
Python
267 lines
11 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Configuration Management
|
|
|
|
Loads configuration from YAML file or environment variables.
|
|
"""
|
|
|
|
import os
|
|
import yaml
|
|
import logging
|
|
from pathlib import Path
|
|
from typing import Dict, Optional, Any
|
|
|
|
try:
|
|
from dotenv import load_dotenv
|
|
DOTENV_AVAILABLE = True
|
|
except ImportError:
|
|
DOTENV_AVAILABLE = False
|
|
|
|
DEFAULT_CONFIG = {
|
|
'sharepoint': {
|
|
'enabled': False,
|
|
'site_url': '',
|
|
'folder_path': '/Shared Documents/Reports',
|
|
'file_path': None, # Use folder_path for multiple files, file_path for single file
|
|
'local_dir': 'reports',
|
|
'username': None,
|
|
'password': None,
|
|
'client_id': None,
|
|
'client_secret': None,
|
|
'use_app_authentication': False,
|
|
'file_pattern': '*.xlsx',
|
|
'overwrite': True
|
|
},
|
|
'scheduler': {
|
|
'enabled': False,
|
|
'schedule_type': 'cron', # 'interval', 'cron', or 'once'
|
|
'interval_hours': 24, # For interval type
|
|
'cron_expression': '0 10 * * *', # For cron type (10 AM EST/EDT daily)
|
|
'timezone': 'America/New_York' # EST/EDT timezone
|
|
},
|
|
'api': {
|
|
'enabled': False,
|
|
'host': '0.0.0.0',
|
|
'port': 8080,
|
|
'api_key': None # Optional API key for authentication
|
|
},
|
|
'report': {
|
|
'output_dir': 'output',
|
|
'reports_dir': 'reports'
|
|
}
|
|
}
|
|
|
|
|
|
def load_config(config_path: Optional[str] = None) -> Dict[str, Any]:
|
|
"""
|
|
Load configuration from YAML file or environment variables.
|
|
|
|
Args:
|
|
config_path: Path to config.yaml file (default: config.yaml in current directory)
|
|
|
|
Returns:
|
|
Configuration dictionary
|
|
"""
|
|
# Load .env file if available (from current directory or parent taskboard directory)
|
|
if DOTENV_AVAILABLE:
|
|
# Try loading from vendor_report/.env first
|
|
env_file = Path(__file__).parent / ".env"
|
|
if not env_file.exists():
|
|
# Try loading from parent taskboard/.env
|
|
parent_env = Path(__file__).parent.parent / "taskboard" / ".env"
|
|
if parent_env.exists():
|
|
env_file = parent_env
|
|
logging.info(f"Found .env file in taskboard directory: {env_file}")
|
|
else:
|
|
logging.warning(f".env file not found in vendor_report or taskboard directory")
|
|
logging.warning(f"Checked: {Path(__file__).parent / '.env'}")
|
|
logging.warning(f"Checked: {parent_env}")
|
|
else:
|
|
logging.info(f"Found .env file in vendor_report directory: {env_file}")
|
|
|
|
if env_file.exists():
|
|
load_dotenv(env_file, override=True) # override=True ensures env vars take precedence
|
|
logging.info(f"Loaded environment variables from {env_file.absolute()}")
|
|
|
|
# Log which SharePoint env vars were found (checking both SHAREPOINT_* and AZURE_AD_* fallbacks)
|
|
sp_vars = ['SHAREPOINT_ENABLED', 'SHAREPOINT_SITE_URL', 'SHAREPOINT_FOLDER_PATH']
|
|
found_vars = [var for var in sp_vars if os.getenv(var)]
|
|
|
|
# Check credentials (with fallback)
|
|
client_id = os.getenv('SHAREPOINT_CLIENT_ID') or os.getenv('AZURE_AD_CLIENT_ID')
|
|
tenant_id = os.getenv('SHAREPOINT_TENANT_ID') or os.getenv('AZURE_AD_TENANT_ID')
|
|
client_secret = os.getenv('SHAREPOINT_CLIENT_SECRET') or os.getenv('AZURE_AD_CLIENT_SECRET')
|
|
|
|
if client_id:
|
|
found_vars.append('CLIENT_ID (from SHAREPOINT_CLIENT_ID or AZURE_AD_CLIENT_ID)')
|
|
if tenant_id:
|
|
found_vars.append('TENANT_ID (from SHAREPOINT_TENANT_ID or AZURE_AD_TENANT_ID)')
|
|
if client_secret:
|
|
found_vars.append('CLIENT_SECRET (from SHAREPOINT_CLIENT_SECRET or AZURE_AD_CLIENT_SECRET)')
|
|
|
|
logging.info(f"Found SharePoint environment variables: {', '.join(found_vars)}")
|
|
|
|
missing_vars = []
|
|
if not client_id:
|
|
missing_vars.append('CLIENT_ID (SHAREPOINT_CLIENT_ID or AZURE_AD_CLIENT_ID)')
|
|
if not tenant_id:
|
|
missing_vars.append('TENANT_ID (SHAREPOINT_TENANT_ID or AZURE_AD_TENANT_ID)')
|
|
if not client_secret:
|
|
missing_vars.append('CLIENT_SECRET (SHAREPOINT_CLIENT_SECRET or AZURE_AD_CLIENT_SECRET)')
|
|
|
|
if missing_vars:
|
|
logging.warning(f"Missing SharePoint credentials: {', '.join(missing_vars)}")
|
|
|
|
if config_path is None:
|
|
config_path = Path(__file__).parent / "config.yaml"
|
|
else:
|
|
config_path = Path(config_path)
|
|
|
|
config = DEFAULT_CONFIG.copy()
|
|
|
|
# Load from YAML file if exists
|
|
if config_path.exists():
|
|
try:
|
|
with open(config_path, 'r') as f:
|
|
file_config = yaml.safe_load(f) or {}
|
|
# Deep merge with defaults
|
|
config = _deep_merge(config, file_config)
|
|
except Exception as e:
|
|
print(f"Warning: Failed to load config from {config_path}: {e}")
|
|
|
|
# Override with environment variables
|
|
config = _load_from_env(config)
|
|
|
|
return config
|
|
|
|
|
|
def _deep_merge(base: Dict, override: Dict) -> Dict:
|
|
"""Deep merge two dictionaries."""
|
|
result = base.copy()
|
|
for key, value in override.items():
|
|
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
|
result[key] = _deep_merge(result[key], value)
|
|
else:
|
|
result[key] = value
|
|
return result
|
|
|
|
|
|
def _load_from_env(config: Dict) -> Dict:
|
|
"""Load configuration from environment variables."""
|
|
# SharePoint settings
|
|
if os.getenv('SHAREPOINT_ENABLED'):
|
|
config['sharepoint']['enabled'] = os.getenv('SHAREPOINT_ENABLED').lower() == 'true'
|
|
if os.getenv('SHAREPOINT_SITE_URL'):
|
|
config['sharepoint']['site_url'] = os.getenv('SHAREPOINT_SITE_URL')
|
|
if os.getenv('SHAREPOINT_FOLDER_PATH'):
|
|
config['sharepoint']['folder_path'] = os.getenv('SHAREPOINT_FOLDER_PATH')
|
|
if os.getenv('SHAREPOINT_USERNAME'):
|
|
config['sharepoint']['username'] = os.getenv('SHAREPOINT_USERNAME')
|
|
if os.getenv('SHAREPOINT_PASSWORD'):
|
|
config['sharepoint']['password'] = os.getenv('SHAREPOINT_PASSWORD')
|
|
# Check for SHAREPOINT_CLIENT_ID first, fallback to AZURE_AD_CLIENT_ID
|
|
if os.getenv('SHAREPOINT_CLIENT_ID'):
|
|
config['sharepoint']['client_id'] = os.getenv('SHAREPOINT_CLIENT_ID')
|
|
elif os.getenv('AZURE_AD_CLIENT_ID'):
|
|
config['sharepoint']['client_id'] = os.getenv('AZURE_AD_CLIENT_ID')
|
|
|
|
# Check for SHAREPOINT_CLIENT_SECRET first, fallback to AZURE_AD_CLIENT_SECRET
|
|
if os.getenv('SHAREPOINT_CLIENT_SECRET'):
|
|
config['sharepoint']['client_secret'] = os.getenv('SHAREPOINT_CLIENT_SECRET')
|
|
elif os.getenv('AZURE_AD_CLIENT_SECRET'):
|
|
config['sharepoint']['client_secret'] = os.getenv('AZURE_AD_CLIENT_SECRET')
|
|
|
|
# Tenant ID (required for Microsoft Graph API)
|
|
if os.getenv('SHAREPOINT_TENANT_ID'):
|
|
config['sharepoint']['tenant_id'] = os.getenv('SHAREPOINT_TENANT_ID')
|
|
elif os.getenv('AZURE_AD_TENANT_ID'):
|
|
config['sharepoint']['tenant_id'] = os.getenv('AZURE_AD_TENANT_ID')
|
|
|
|
if os.getenv('SHAREPOINT_USE_APP_AUTH'):
|
|
config['sharepoint']['use_app_authentication'] = os.getenv('SHAREPOINT_USE_APP_AUTH').lower() == 'true'
|
|
elif os.getenv('SHAREPOINT_USE_APP_AUTH') is None and os.getenv('AZURE_AD_CLIENT_ID'):
|
|
# If Azure AD credentials are present, default to app auth
|
|
config['sharepoint']['use_app_authentication'] = True
|
|
|
|
# Scheduler settings
|
|
if os.getenv('SCHEDULER_ENABLED'):
|
|
config['scheduler']['enabled'] = os.getenv('SCHEDULER_ENABLED').lower() == 'true'
|
|
if os.getenv('SCHEDULER_SCHEDULE_TYPE'):
|
|
config['scheduler']['schedule_type'] = os.getenv('SCHEDULER_SCHEDULE_TYPE')
|
|
if os.getenv('SCHEDULER_INTERVAL_HOURS'):
|
|
config['scheduler']['interval_hours'] = int(os.getenv('SCHEDULER_INTERVAL_HOURS'))
|
|
if os.getenv('SCHEDULER_CRON'):
|
|
config['scheduler']['cron_expression'] = os.getenv('SCHEDULER_CRON')
|
|
if os.getenv('SCHEDULER_TIMEZONE'):
|
|
config['scheduler']['timezone'] = os.getenv('SCHEDULER_TIMEZONE')
|
|
|
|
# API settings
|
|
if os.getenv('API_ENABLED'):
|
|
config['api']['enabled'] = os.getenv('API_ENABLED').lower() == 'true'
|
|
if os.getenv('API_PORT'):
|
|
config['api']['port'] = int(os.getenv('API_PORT'))
|
|
if os.getenv('API_HOST'):
|
|
config['api']['host'] = os.getenv('API_HOST')
|
|
if os.getenv('API_KEY'):
|
|
config['api']['api_key'] = os.getenv('API_KEY')
|
|
|
|
# Report settings
|
|
if os.getenv('REPORT_OUTPUT_DIR'):
|
|
config['report']['output_dir'] = os.getenv('REPORT_OUTPUT_DIR')
|
|
if os.getenv('REPORT_REPORTS_DIR'):
|
|
config['report']['reports_dir'] = os.getenv('REPORT_REPORTS_DIR')
|
|
|
|
return config
|
|
|
|
|
|
def save_config_template(config_path: Optional[str] = None) -> None:
|
|
"""Save a template configuration file."""
|
|
if config_path is None:
|
|
config_path = Path(__file__).parent / "config.yaml.template"
|
|
else:
|
|
config_path = Path(config_path)
|
|
|
|
template = """# Vendor Report Generator Configuration
|
|
|
|
# SharePoint Integration
|
|
sharepoint:
|
|
enabled: false # Set to true to enable SharePoint downloads
|
|
site_url: "https://yourcompany.sharepoint.com/sites/YourSite"
|
|
folder_path: "/Shared Documents/Reports" # Path to folder containing Excel files
|
|
# file_path: "/Shared Documents/Reports/file.xlsx" # Alternative: single file path
|
|
local_dir: "reports" # Local directory to save downloaded files
|
|
username: null # Username for user authentication (leave null if using app auth)
|
|
password: null # Password for user authentication (leave null if using app auth)
|
|
client_id: null # Azure AD app client ID (for app authentication)
|
|
client_secret: null # Azure AD app client secret (for app authentication)
|
|
use_app_authentication: false # Set to true to use app authentication (recommended)
|
|
file_pattern: "*.xlsx" # Pattern to filter files
|
|
overwrite: true # Whether to overwrite existing files
|
|
|
|
# Scheduler Configuration
|
|
scheduler:
|
|
enabled: false # Set to true to enable scheduled report generation
|
|
schedule_type: "interval" # Options: "interval", "cron", or "once"
|
|
interval_hours: 24 # For interval type: generate report every N hours
|
|
cron_expression: "0 8 * * *" # For cron type: generate at 8 AM daily (cron format)
|
|
timezone: "America/New_York" # Timezone for scheduling
|
|
|
|
# API Configuration (for on-demand report generation)
|
|
api:
|
|
enabled: false # Set to true to enable web API
|
|
host: "0.0.0.0" # Host to bind API server
|
|
port: 8080 # Port for API server
|
|
api_key: null # Optional API key for authentication (set to enable auth)
|
|
|
|
# Report Settings
|
|
report:
|
|
output_dir: "output" # Directory for generated reports
|
|
reports_dir: "reports" # Directory containing Excel files
|
|
"""
|
|
|
|
with open(config_path, 'w') as f:
|
|
f.write(template)
|
|
|
|
print(f"Configuration template saved to: {config_path}")
|
|
|