vendor_report/config.py
2025-11-06 20:50:19 +04:00

220 lines
8.2 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': 'interval', # 'interval', 'cron', or 'once'
'interval_hours': 24, # For interval type
'cron_expression': '0 8 * * *', # For cron type (8 AM daily)
'timezone': 'America/New_York'
},
'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
if env_file.exists():
load_dotenv(env_file)
logging.info(f"Loaded environment variables from {env_file}")
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')
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_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')
# 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}")