|
|
""" |
|
|
Feature Flags System |
|
|
Allows dynamic toggling of application modules and features |
|
|
""" |
|
|
from typing import Dict, Any |
|
|
import json |
|
|
from pathlib import Path |
|
|
from datetime import datetime |
|
|
import logging |
|
|
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
class FeatureFlagManager: |
|
|
"""Manage application feature flags""" |
|
|
|
|
|
DEFAULT_FLAGS = { |
|
|
"enableWhaleTracking": True, |
|
|
"enableMarketOverview": True, |
|
|
"enableFearGreedIndex": True, |
|
|
"enableNewsFeed": True, |
|
|
"enableSentimentAnalysis": True, |
|
|
"enableMlPredictions": False, |
|
|
"enableProxyAutoMode": True, |
|
|
"enableDefiProtocols": True, |
|
|
"enableTrendingCoins": True, |
|
|
"enableGlobalStats": True, |
|
|
"enableProviderRotation": True, |
|
|
"enableWebSocketStreaming": True, |
|
|
"enableDatabaseLogging": True, |
|
|
"enableRealTimeAlerts": False, |
|
|
"enableAdvancedCharts": True, |
|
|
"enableExportFeatures": True, |
|
|
"enableCustomProviders": True, |
|
|
"enablePoolManagement": True, |
|
|
"enableHFIntegration": True, |
|
|
} |
|
|
|
|
|
def __init__(self, storage_path: str = "data/feature_flags.json"): |
|
|
""" |
|
|
Initialize feature flag manager |
|
|
|
|
|
Args: |
|
|
storage_path: Path to persist feature flags |
|
|
""" |
|
|
self.storage_path = Path(storage_path) |
|
|
self.flags = self.DEFAULT_FLAGS.copy() |
|
|
self.load_flags() |
|
|
|
|
|
def load_flags(self): |
|
|
"""Load feature flags from storage""" |
|
|
try: |
|
|
if self.storage_path.exists(): |
|
|
with open(self.storage_path, 'r', encoding='utf-8') as f: |
|
|
saved_flags = json.load(f) |
|
|
|
|
|
self.flags.update(saved_flags.get('flags', {})) |
|
|
logger.info(f"Loaded feature flags from {self.storage_path}") |
|
|
else: |
|
|
|
|
|
self.storage_path.parent.mkdir(parents=True, exist_ok=True) |
|
|
self.save_flags() |
|
|
logger.info("Initialized default feature flags") |
|
|
except Exception as e: |
|
|
logger.error(f"Error loading feature flags: {e}") |
|
|
self.flags = self.DEFAULT_FLAGS.copy() |
|
|
|
|
|
def save_flags(self): |
|
|
"""Save feature flags to storage""" |
|
|
try: |
|
|
self.storage_path.parent.mkdir(parents=True, exist_ok=True) |
|
|
data = { |
|
|
'flags': self.flags, |
|
|
'last_updated': datetime.now().isoformat() |
|
|
} |
|
|
with open(self.storage_path, 'w', encoding='utf-8') as f: |
|
|
json.dump(data, f, indent=2) |
|
|
logger.info("Feature flags saved successfully") |
|
|
except Exception as e: |
|
|
logger.error(f"Error saving feature flags: {e}") |
|
|
|
|
|
def get_all_flags(self) -> Dict[str, bool]: |
|
|
"""Get all feature flags""" |
|
|
return self.flags.copy() |
|
|
|
|
|
def get_flag(self, flag_name: str) -> bool: |
|
|
""" |
|
|
Get a specific feature flag value |
|
|
|
|
|
Args: |
|
|
flag_name: Name of the flag |
|
|
|
|
|
Returns: |
|
|
bool: Flag value (defaults to False if not found) |
|
|
""" |
|
|
return self.flags.get(flag_name, False) |
|
|
|
|
|
def set_flag(self, flag_name: str, value: bool) -> bool: |
|
|
""" |
|
|
Set a feature flag value |
|
|
|
|
|
Args: |
|
|
flag_name: Name of the flag |
|
|
value: New value (True/False) |
|
|
|
|
|
Returns: |
|
|
bool: Success status |
|
|
""" |
|
|
try: |
|
|
self.flags[flag_name] = bool(value) |
|
|
self.save_flags() |
|
|
logger.info(f"Feature flag '{flag_name}' set to {value}") |
|
|
return True |
|
|
except Exception as e: |
|
|
logger.error(f"Error setting feature flag: {e}") |
|
|
return False |
|
|
|
|
|
def update_flags(self, updates: Dict[str, bool]) -> bool: |
|
|
""" |
|
|
Update multiple flags at once |
|
|
|
|
|
Args: |
|
|
updates: Dictionary of flag name -> value pairs |
|
|
|
|
|
Returns: |
|
|
bool: Success status |
|
|
""" |
|
|
try: |
|
|
for flag_name, value in updates.items(): |
|
|
self.flags[flag_name] = bool(value) |
|
|
self.save_flags() |
|
|
logger.info(f"Updated {len(updates)} feature flags") |
|
|
return True |
|
|
except Exception as e: |
|
|
logger.error(f"Error updating feature flags: {e}") |
|
|
return False |
|
|
|
|
|
def reset_to_defaults(self) -> bool: |
|
|
"""Reset all flags to default values""" |
|
|
try: |
|
|
self.flags = self.DEFAULT_FLAGS.copy() |
|
|
self.save_flags() |
|
|
logger.info("Feature flags reset to defaults") |
|
|
return True |
|
|
except Exception as e: |
|
|
logger.error(f"Error resetting feature flags: {e}") |
|
|
return False |
|
|
|
|
|
def is_enabled(self, flag_name: str) -> bool: |
|
|
""" |
|
|
Check if a feature is enabled (alias for get_flag) |
|
|
|
|
|
Args: |
|
|
flag_name: Name of the flag |
|
|
|
|
|
Returns: |
|
|
bool: True if enabled, False otherwise |
|
|
""" |
|
|
return self.get_flag(flag_name) |
|
|
|
|
|
def get_enabled_features(self) -> Dict[str, bool]: |
|
|
"""Get only enabled features""" |
|
|
return {k: v for k, v in self.flags.items() if v is True} |
|
|
|
|
|
def get_disabled_features(self) -> Dict[str, bool]: |
|
|
"""Get only disabled features""" |
|
|
return {k: v for k, v in self.flags.items() if v is False} |
|
|
|
|
|
def get_flag_count(self) -> Dict[str, int]: |
|
|
"""Get count of enabled/disabled flags""" |
|
|
enabled = sum(1 for v in self.flags.values() if v) |
|
|
disabled = len(self.flags) - enabled |
|
|
return { |
|
|
'total': len(self.flags), |
|
|
'enabled': enabled, |
|
|
'disabled': disabled |
|
|
} |
|
|
|
|
|
def get_feature_info(self) -> Dict[str, Any]: |
|
|
"""Get comprehensive feature flag information""" |
|
|
counts = self.get_flag_count() |
|
|
return { |
|
|
'flags': self.flags, |
|
|
'counts': counts, |
|
|
'enabled_features': list(self.get_enabled_features().keys()), |
|
|
'disabled_features': list(self.get_disabled_features().keys()), |
|
|
'storage_path': str(self.storage_path), |
|
|
'last_loaded': datetime.now().isoformat() |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
feature_flags = FeatureFlagManager() |
|
|
|
|
|
|
|
|
|
|
|
def is_feature_enabled(flag_name: str) -> bool: |
|
|
"""Check if a feature is enabled""" |
|
|
return feature_flags.is_enabled(flag_name) |
|
|
|
|
|
|
|
|
def get_all_feature_flags() -> Dict[str, bool]: |
|
|
"""Get all feature flags""" |
|
|
return feature_flags.get_all_flags() |
|
|
|
|
|
|
|
|
def set_feature_flag(flag_name: str, value: bool) -> bool: |
|
|
"""Set a feature flag""" |
|
|
return feature_flags.set_flag(flag_name, value) |
|
|
|
|
|
|
|
|
def update_feature_flags(updates: Dict[str, bool]) -> bool: |
|
|
"""Update multiple feature flags""" |
|
|
return feature_flags.update_flags(updates) |
|
|
|