282 Zeilen
11 KiB
Python
282 Zeilen
11 KiB
Python
"""
|
|
SQLite implementation of method strategy repository.
|
|
Handles persistence and retrieval of method strategies with performance optimization.
|
|
"""
|
|
|
|
import json
|
|
import sqlite3
|
|
from datetime import datetime, timedelta
|
|
from typing import List, Optional, Dict, Any
|
|
|
|
from domain.entities.method_rotation import MethodStrategy, RiskLevel
|
|
from domain.repositories.method_rotation_repository import IMethodStrategyRepository
|
|
from database.db_manager import DatabaseManager
|
|
|
|
|
|
class MethodStrategyRepository(IMethodStrategyRepository):
|
|
"""SQLite implementation of method strategy repository"""
|
|
|
|
def __init__(self, db_manager):
|
|
self.db_manager = db_manager
|
|
|
|
def save(self, strategy: MethodStrategy) -> None:
|
|
"""Save or update a method strategy"""
|
|
strategy.updated_at = datetime.now()
|
|
|
|
query = """
|
|
INSERT OR REPLACE INTO method_strategies (
|
|
id, platform, method_name, priority, success_rate, failure_rate,
|
|
last_success, last_failure, cooldown_period, max_daily_attempts,
|
|
risk_level, is_active, configuration, tags, created_at, updated_at
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
"""
|
|
|
|
params = (
|
|
strategy.strategy_id,
|
|
strategy.platform,
|
|
strategy.method_name,
|
|
strategy.priority,
|
|
strategy.success_rate,
|
|
strategy.failure_rate,
|
|
strategy.last_success.isoformat() if strategy.last_success else None,
|
|
strategy.last_failure.isoformat() if strategy.last_failure else None,
|
|
strategy.cooldown_period,
|
|
strategy.max_daily_attempts,
|
|
strategy.risk_level.value,
|
|
strategy.is_active,
|
|
json.dumps(strategy.configuration),
|
|
json.dumps(strategy.tags),
|
|
strategy.created_at.isoformat(),
|
|
strategy.updated_at.isoformat()
|
|
)
|
|
|
|
self.db_manager.execute_query(query, params)
|
|
|
|
def find_by_id(self, strategy_id: str) -> Optional[MethodStrategy]:
|
|
"""Find a strategy by its ID"""
|
|
query = "SELECT * FROM method_strategies WHERE id = ?"
|
|
result = self.db_manager.fetch_one(query, (strategy_id,))
|
|
return self._row_to_strategy(result) if result else None
|
|
|
|
def find_by_platform(self, platform: str) -> List[MethodStrategy]:
|
|
"""Find all strategies for a platform"""
|
|
query = """
|
|
SELECT * FROM method_strategies
|
|
WHERE platform = ?
|
|
ORDER BY priority DESC, success_rate DESC
|
|
"""
|
|
results = self.db_manager.fetch_all(query, (platform,))
|
|
return [self._row_to_strategy(row) for row in results]
|
|
|
|
def find_active_by_platform(self, platform: str) -> List[MethodStrategy]:
|
|
"""Find all active strategies for a platform, ordered by effectiveness"""
|
|
query = """
|
|
SELECT * FROM method_strategies
|
|
WHERE platform = ? AND is_active = 1
|
|
ORDER BY priority DESC, success_rate DESC, last_success DESC
|
|
"""
|
|
results = self.db_manager.fetch_all(query, (platform,))
|
|
strategies = [self._row_to_strategy(row) for row in results]
|
|
|
|
# Sort by effectiveness score
|
|
strategies.sort(key=lambda s: s.effectiveness_score, reverse=True)
|
|
return strategies
|
|
|
|
def find_by_platform_and_method(self, platform: str, method_name: str) -> Optional[MethodStrategy]:
|
|
"""Find a specific method strategy"""
|
|
query = "SELECT * FROM method_strategies WHERE platform = ? AND method_name = ?"
|
|
result = self.db_manager.fetch_one(query, (platform, method_name))
|
|
return self._row_to_strategy(result) if result else None
|
|
|
|
def update_performance_metrics(self, strategy_id: str, success: bool,
|
|
execution_time: float = 0.0) -> None:
|
|
"""Update performance metrics for a strategy"""
|
|
strategy = self.find_by_id(strategy_id)
|
|
if not strategy:
|
|
return
|
|
|
|
strategy.update_performance(success, execution_time)
|
|
self.save(strategy)
|
|
|
|
def get_next_available_method(self, platform: str,
|
|
excluded_methods: List[str] = None,
|
|
max_risk_level: str = "HIGH") -> Optional[MethodStrategy]:
|
|
"""Get the next best available method for a platform"""
|
|
if excluded_methods is None:
|
|
excluded_methods = []
|
|
|
|
# Build query with exclusions
|
|
placeholders = ','.join(['?' for _ in excluded_methods])
|
|
exclusion_clause = f"AND method_name NOT IN ({placeholders})" if excluded_methods else ""
|
|
|
|
# Build risk level clause
|
|
risk_clause = "'LOW', 'MEDIUM'"
|
|
if max_risk_level == 'HIGH':
|
|
risk_clause += ", 'HIGH'"
|
|
|
|
query = f"""
|
|
SELECT * FROM method_strategies
|
|
WHERE platform = ?
|
|
AND is_active = 1
|
|
AND risk_level IN ({risk_clause})
|
|
{exclusion_clause}
|
|
ORDER BY priority DESC, success_rate DESC
|
|
LIMIT 1
|
|
"""
|
|
|
|
params = [platform] + excluded_methods
|
|
result = self.db_manager.fetch_one(query, params)
|
|
|
|
if not result:
|
|
return None
|
|
|
|
strategy = self._row_to_strategy(result)
|
|
|
|
# Check if method is on cooldown
|
|
if strategy.is_on_cooldown:
|
|
# Try to find another method
|
|
excluded_methods.append(strategy.method_name)
|
|
return self.get_next_available_method(platform, excluded_methods, max_risk_level)
|
|
|
|
return strategy
|
|
|
|
def disable_method(self, platform: str, method_name: str, reason: str) -> None:
|
|
"""Disable a method temporarily or permanently"""
|
|
query = """
|
|
UPDATE method_strategies
|
|
SET is_active = 0, updated_at = ?
|
|
WHERE platform = ? AND method_name = ?
|
|
"""
|
|
self.db_manager.execute_query(query, (datetime.now().isoformat(), platform, method_name))
|
|
|
|
# Log the reason in configuration
|
|
strategy = self.find_by_platform_and_method(platform, method_name)
|
|
if strategy:
|
|
strategy.configuration['disabled_reason'] = reason
|
|
strategy.configuration['disabled_at'] = datetime.now().isoformat()
|
|
self.save(strategy)
|
|
|
|
def enable_method(self, platform: str, method_name: str) -> None:
|
|
"""Re-enable a disabled method"""
|
|
query = """
|
|
UPDATE method_strategies
|
|
SET is_active = 1, updated_at = ?
|
|
WHERE platform = ? AND method_name = ?
|
|
"""
|
|
self.db_manager.execute_query(query, (datetime.now().isoformat(), platform, method_name))
|
|
|
|
# Clear disabled reason from configuration
|
|
strategy = self.find_by_platform_and_method(platform, method_name)
|
|
if strategy:
|
|
strategy.configuration.pop('disabled_reason', None)
|
|
strategy.configuration.pop('disabled_at', None)
|
|
self.save(strategy)
|
|
|
|
def get_platform_statistics(self, platform: str) -> Dict[str, Any]:
|
|
"""Get aggregated statistics for all methods on a platform"""
|
|
query = """
|
|
SELECT
|
|
COUNT(*) as total_methods,
|
|
COUNT(CASE WHEN is_active = 1 THEN 1 END) as active_methods,
|
|
AVG(success_rate) as avg_success_rate,
|
|
MAX(success_rate) as best_success_rate,
|
|
MIN(success_rate) as worst_success_rate,
|
|
AVG(priority) as avg_priority,
|
|
COUNT(CASE WHEN last_success > datetime('now', '-24 hours') THEN 1 END) as recent_successes,
|
|
COUNT(CASE WHEN last_failure > datetime('now', '-24 hours') THEN 1 END) as recent_failures
|
|
FROM method_strategies
|
|
WHERE platform = ?
|
|
"""
|
|
|
|
result = self.db_manager.fetch_one(query, (platform,))
|
|
|
|
if not result:
|
|
return {}
|
|
|
|
return {
|
|
'total_methods': result[0] or 0,
|
|
'active_methods': result[1] or 0,
|
|
'avg_success_rate': round(result[2] or 0.0, 3),
|
|
'best_success_rate': result[3] or 0.0,
|
|
'worst_success_rate': result[4] or 0.0,
|
|
'avg_priority': round(result[5] or 0.0, 1),
|
|
'recent_successes_24h': result[6] or 0,
|
|
'recent_failures_24h': result[7] or 0
|
|
}
|
|
|
|
def cleanup_old_data(self, days_to_keep: int = 90) -> int:
|
|
"""Clean up old performance data and return number of records removed"""
|
|
# This implementation doesn't remove strategies but resets old performance data
|
|
cutoff_date = datetime.now() - timedelta(days=days_to_keep)
|
|
|
|
query = """
|
|
UPDATE method_strategies
|
|
SET last_success = NULL, last_failure = NULL, success_rate = 0.0, failure_rate = 0.0
|
|
WHERE (last_success < ? OR last_failure < ?)
|
|
AND (last_success IS NOT NULL OR last_failure IS NOT NULL)
|
|
"""
|
|
|
|
cursor = self.db_manager.execute_query(query, (cutoff_date.isoformat(), cutoff_date.isoformat()))
|
|
return cursor.rowcount if cursor else 0
|
|
|
|
def get_methods_by_risk_level(self, platform: str, risk_level: RiskLevel) -> List[MethodStrategy]:
|
|
"""Get methods filtered by risk level"""
|
|
query = """
|
|
SELECT * FROM method_strategies
|
|
WHERE platform = ? AND risk_level = ? AND is_active = 1
|
|
ORDER BY priority DESC, success_rate DESC
|
|
"""
|
|
results = self.db_manager.fetch_all(query, (platform, risk_level.value))
|
|
return [self._row_to_strategy(row) for row in results]
|
|
|
|
def get_emergency_methods(self, platform: str) -> List[MethodStrategy]:
|
|
"""Get only the most reliable methods for emergency mode"""
|
|
query = """
|
|
SELECT * FROM method_strategies
|
|
WHERE platform = ?
|
|
AND is_active = 1
|
|
AND risk_level = 'LOW'
|
|
AND success_rate > 0.5
|
|
ORDER BY success_rate DESC, priority DESC
|
|
LIMIT 2
|
|
"""
|
|
results = self.db_manager.fetch_all(query, (platform,))
|
|
return [self._row_to_strategy(row) for row in results]
|
|
|
|
def bulk_update_priorities(self, platform: str, priority_updates: Dict[str, int]) -> None:
|
|
"""Bulk update method priorities for a platform"""
|
|
query = """
|
|
UPDATE method_strategies
|
|
SET priority = ?, updated_at = ?
|
|
WHERE platform = ? AND method_name = ?
|
|
"""
|
|
|
|
params_list = [
|
|
(priority, datetime.now().isoformat(), platform, method_name)
|
|
for method_name, priority in priority_updates.items()
|
|
]
|
|
|
|
with self.db_manager.get_connection() as conn:
|
|
conn.executemany(query, params_list)
|
|
conn.commit()
|
|
|
|
def _row_to_strategy(self, row) -> MethodStrategy:
|
|
"""Convert database row to MethodStrategy entity"""
|
|
return MethodStrategy(
|
|
strategy_id=row[0],
|
|
platform=row[1],
|
|
method_name=row[2],
|
|
priority=row[3],
|
|
success_rate=row[4],
|
|
failure_rate=row[5],
|
|
last_success=datetime.fromisoformat(row[6]) if row[6] else None,
|
|
last_failure=datetime.fromisoformat(row[7]) if row[7] else None,
|
|
cooldown_period=row[8],
|
|
max_daily_attempts=row[9],
|
|
risk_level=RiskLevel(row[10]),
|
|
is_active=bool(row[11]),
|
|
configuration=json.loads(row[12]) if row[12] else {},
|
|
tags=json.loads(row[13]) if row[13] else [],
|
|
created_at=datetime.fromisoformat(row[14]),
|
|
updated_at=datetime.fromisoformat(row[15])
|
|
) |