Initial commit
Dieser Commit ist enthalten in:
254
infrastructure/repositories/rotation_session_repository.py
Normale Datei
254
infrastructure/repositories/rotation_session_repository.py
Normale Datei
@ -0,0 +1,254 @@
|
||||
"""
|
||||
SQLite implementation of rotation session repository.
|
||||
Handles persistence and retrieval of rotation sessions.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sqlite3
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Optional, Dict, Any
|
||||
|
||||
from domain.entities.method_rotation import RotationSession
|
||||
from domain.repositories.method_rotation_repository import IRotationSessionRepository
|
||||
from database.db_manager import DatabaseManager
|
||||
|
||||
|
||||
class RotationSessionRepository(IRotationSessionRepository):
|
||||
"""SQLite implementation of rotation session repository"""
|
||||
|
||||
def __init__(self, db_manager):
|
||||
self.db_manager = db_manager
|
||||
|
||||
def save(self, session: RotationSession) -> None:
|
||||
"""Save or update a rotation session"""
|
||||
query = """
|
||||
INSERT OR REPLACE INTO rotation_sessions (
|
||||
id, platform, account_id, current_method, attempted_methods,
|
||||
session_start, last_rotation, rotation_count, success_count,
|
||||
failure_count, is_active, rotation_reason, fingerprint_id, session_metadata
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
|
||||
params = (
|
||||
session.session_id,
|
||||
session.platform,
|
||||
session.account_id,
|
||||
session.current_method,
|
||||
json.dumps(session.attempted_methods),
|
||||
session.session_start.isoformat(),
|
||||
session.last_rotation.isoformat() if session.last_rotation else None,
|
||||
session.rotation_count,
|
||||
session.success_count,
|
||||
session.failure_count,
|
||||
session.is_active,
|
||||
session.rotation_reason,
|
||||
session.fingerprint_id,
|
||||
json.dumps(session.session_metadata)
|
||||
)
|
||||
|
||||
self.db_manager.execute_query(query, params)
|
||||
|
||||
def find_by_id(self, session_id: str) -> Optional[RotationSession]:
|
||||
"""Find a session by its ID"""
|
||||
query = "SELECT * FROM rotation_sessions WHERE id = ?"
|
||||
result = self.db_manager.fetch_one(query, (session_id,))
|
||||
return self._row_to_session(result) if result else None
|
||||
|
||||
def find_active_session(self, platform: str, account_id: Optional[str] = None) -> Optional[RotationSession]:
|
||||
"""Find an active session for a platform/account"""
|
||||
if account_id:
|
||||
query = """
|
||||
SELECT * FROM rotation_sessions
|
||||
WHERE platform = ? AND account_id = ? AND is_active = 1
|
||||
ORDER BY session_start DESC
|
||||
LIMIT 1
|
||||
"""
|
||||
params = (platform, account_id)
|
||||
else:
|
||||
query = """
|
||||
SELECT * FROM rotation_sessions
|
||||
WHERE platform = ? AND is_active = 1
|
||||
ORDER BY session_start DESC
|
||||
LIMIT 1
|
||||
"""
|
||||
params = (platform,)
|
||||
|
||||
result = self.db_manager.fetch_one(query, params)
|
||||
return self._row_to_session(result) if result else None
|
||||
|
||||
def find_active_sessions_by_platform(self, platform: str) -> List[RotationSession]:
|
||||
"""Find all active sessions for a platform"""
|
||||
query = """
|
||||
SELECT * FROM rotation_sessions
|
||||
WHERE platform = ? AND is_active = 1
|
||||
ORDER BY session_start DESC
|
||||
"""
|
||||
results = self.db_manager.fetch_all(query, (platform,))
|
||||
return [self._row_to_session(row) for row in results]
|
||||
|
||||
def update_session_metrics(self, session_id: str, success: bool,
|
||||
method_name: str, error_message: Optional[str] = None) -> None:
|
||||
"""Update session metrics after a method attempt"""
|
||||
session = self.find_by_id(session_id)
|
||||
if not session:
|
||||
return
|
||||
|
||||
session.add_attempt(method_name, success, error_message)
|
||||
self.save(session)
|
||||
|
||||
def archive_session(self, session_id: str, final_success: bool = False) -> None:
|
||||
"""Mark a session as completed/archived"""
|
||||
session = self.find_by_id(session_id)
|
||||
if not session:
|
||||
return
|
||||
|
||||
session.complete_session(final_success)
|
||||
self.save(session)
|
||||
|
||||
def get_session_history(self, platform: str, limit: int = 100) -> List[RotationSession]:
|
||||
"""Get recent session history for a platform"""
|
||||
query = """
|
||||
SELECT * FROM rotation_sessions
|
||||
WHERE platform = ?
|
||||
ORDER BY session_start DESC
|
||||
LIMIT ?
|
||||
"""
|
||||
results = self.db_manager.fetch_all(query, (platform, limit))
|
||||
return [self._row_to_session(row) for row in results]
|
||||
|
||||
def get_session_statistics(self, platform: str, days: int = 30) -> Dict[str, Any]:
|
||||
"""Get session statistics for a platform over specified days"""
|
||||
cutoff_date = datetime.now() - timedelta(days=days)
|
||||
|
||||
query = """
|
||||
SELECT
|
||||
COUNT(*) as total_sessions,
|
||||
COUNT(CASE WHEN is_active = 1 THEN 1 END) as active_sessions,
|
||||
COUNT(CASE WHEN is_active = 0 AND JSON_EXTRACT(session_metadata, '$.final_success') = 1 THEN 1 END) as successful_sessions,
|
||||
COUNT(CASE WHEN is_active = 0 AND JSON_EXTRACT(session_metadata, '$.final_success') = 0 THEN 1 END) as failed_sessions,
|
||||
AVG(rotation_count) as avg_rotations,
|
||||
MAX(rotation_count) as max_rotations,
|
||||
AVG(success_count + failure_count) as avg_attempts,
|
||||
AVG(CASE WHEN success_count + failure_count > 0 THEN success_count * 1.0 / (success_count + failure_count) ELSE 0 END) as avg_success_rate
|
||||
FROM rotation_sessions
|
||||
WHERE platform = ? AND session_start >= ?
|
||||
"""
|
||||
|
||||
result = self.db_manager.fetch_one(query, (platform, cutoff_date.isoformat()))
|
||||
|
||||
if not result:
|
||||
return {}
|
||||
|
||||
return {
|
||||
'total_sessions': result[0] or 0,
|
||||
'active_sessions': result[1] or 0,
|
||||
'successful_sessions': result[2] or 0,
|
||||
'failed_sessions': result[3] or 0,
|
||||
'avg_rotations_per_session': round(result[4] or 0.0, 2),
|
||||
'max_rotations_in_session': result[5] or 0,
|
||||
'avg_attempts_per_session': round(result[6] or 0.0, 2),
|
||||
'avg_session_success_rate': round(result[7] or 0.0, 3)
|
||||
}
|
||||
|
||||
def cleanup_old_sessions(self, days_to_keep: int = 30) -> int:
|
||||
"""Clean up old session data and return number of records removed"""
|
||||
cutoff_date = datetime.now() - timedelta(days=days_to_keep)
|
||||
|
||||
query = """
|
||||
DELETE FROM rotation_sessions
|
||||
WHERE is_active = 0 AND session_start < ?
|
||||
"""
|
||||
|
||||
cursor = self.db_manager.execute_query(query, (cutoff_date.isoformat(),))
|
||||
return cursor.rowcount if cursor else 0
|
||||
|
||||
def get_method_usage_statistics(self, platform: str, days: int = 30) -> Dict[str, Any]:
|
||||
"""Get method usage statistics from sessions"""
|
||||
cutoff_date = datetime.now() - timedelta(days=days)
|
||||
|
||||
query = """
|
||||
SELECT
|
||||
current_method,
|
||||
COUNT(*) as usage_count,
|
||||
AVG(success_count) as avg_success_count,
|
||||
AVG(failure_count) as avg_failure_count,
|
||||
AVG(rotation_count) as avg_rotation_count
|
||||
FROM rotation_sessions
|
||||
WHERE platform = ? AND session_start >= ?
|
||||
GROUP BY current_method
|
||||
ORDER BY usage_count DESC
|
||||
"""
|
||||
|
||||
results = self.db_manager.fetch_all(query, (platform, cutoff_date.isoformat()))
|
||||
|
||||
method_stats = {}
|
||||
for row in results:
|
||||
method_stats[row[0]] = {
|
||||
'usage_count': row[1],
|
||||
'avg_success_count': round(row[2] or 0.0, 2),
|
||||
'avg_failure_count': round(row[3] or 0.0, 2),
|
||||
'avg_rotation_count': round(row[4] or 0.0, 2)
|
||||
}
|
||||
|
||||
return method_stats
|
||||
|
||||
def find_sessions_by_fingerprint(self, fingerprint_id: str) -> List[RotationSession]:
|
||||
"""Find sessions associated with a specific fingerprint"""
|
||||
query = """
|
||||
SELECT * FROM rotation_sessions
|
||||
WHERE fingerprint_id = ?
|
||||
ORDER BY session_start DESC
|
||||
"""
|
||||
results = self.db_manager.fetch_all(query, (fingerprint_id,))
|
||||
return [self._row_to_session(row) for row in results]
|
||||
|
||||
def get_long_running_sessions(self, hours: int = 24) -> List[RotationSession]:
|
||||
"""Find sessions that have been running for too long"""
|
||||
cutoff_time = datetime.now() - timedelta(hours=hours)
|
||||
|
||||
query = """
|
||||
SELECT * FROM rotation_sessions
|
||||
WHERE is_active = 1 AND session_start < ?
|
||||
ORDER BY session_start ASC
|
||||
"""
|
||||
|
||||
results = self.db_manager.fetch_all(query, (cutoff_time.isoformat(),))
|
||||
return [self._row_to_session(row) for row in results]
|
||||
|
||||
def force_archive_stale_sessions(self, hours: int = 24) -> int:
|
||||
"""Force archive sessions that have been running too long"""
|
||||
cutoff_time = datetime.now() - timedelta(hours=hours)
|
||||
|
||||
query = """
|
||||
UPDATE rotation_sessions
|
||||
SET is_active = 0,
|
||||
session_metadata = JSON_SET(
|
||||
session_metadata,
|
||||
'$.completed_at', ?,
|
||||
'$.final_success', 0,
|
||||
'$.force_archived', 1
|
||||
)
|
||||
WHERE is_active = 1 AND session_start < ?
|
||||
"""
|
||||
|
||||
cursor = self.db_manager.execute_query(query, (datetime.now().isoformat(), cutoff_time.isoformat()))
|
||||
return cursor.rowcount if cursor else 0
|
||||
|
||||
def _row_to_session(self, row) -> RotationSession:
|
||||
"""Convert database row to RotationSession entity"""
|
||||
return RotationSession(
|
||||
session_id=row[0],
|
||||
platform=row[1],
|
||||
account_id=row[2],
|
||||
current_method=row[3],
|
||||
attempted_methods=json.loads(row[4]) if row[4] else [],
|
||||
session_start=datetime.fromisoformat(row[5]),
|
||||
last_rotation=datetime.fromisoformat(row[6]) if row[6] else None,
|
||||
rotation_count=row[7],
|
||||
success_count=row[8],
|
||||
failure_count=row[9],
|
||||
is_active=bool(row[10]),
|
||||
rotation_reason=row[11],
|
||||
fingerprint_id=row[12],
|
||||
session_metadata=json.loads(row[13]) if row[13] else {}
|
||||
)
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren