Initial commit
Dieser Commit ist enthalten in:
281
infrastructure/services/structured_analytics_service.py
Normale Datei
281
infrastructure/services/structured_analytics_service.py
Normale Datei
@ -0,0 +1,281 @@
|
||||
"""
|
||||
Structured Analytics Service - Konkrete Implementation für Analytics
|
||||
"""
|
||||
|
||||
import logging
|
||||
import json
|
||||
from typing import List, Optional, Dict, Any, Union
|
||||
from datetime import datetime, timedelta
|
||||
import uuid
|
||||
|
||||
from domain.services.analytics_service import IAnalyticsService
|
||||
from domain.entities.account_creation_event import AccountCreationEvent
|
||||
from domain.entities.error_event import ErrorEvent
|
||||
from domain.value_objects.error_summary import ErrorSummary
|
||||
from domain.value_objects.report import Report, ReportType, Metric, PlatformStats, TimeSeriesData
|
||||
from infrastructure.repositories.analytics_repository import AnalyticsRepository
|
||||
|
||||
logger = logging.getLogger("structured_analytics_service")
|
||||
|
||||
|
||||
class StructuredAnalyticsService(IAnalyticsService):
|
||||
"""Konkrete Implementation des Analytics Service"""
|
||||
|
||||
def __init__(self, repository: AnalyticsRepository = None):
|
||||
self.repository = repository or AnalyticsRepository()
|
||||
|
||||
def log_event(self, event: Union[AccountCreationEvent, ErrorEvent, Any]) -> None:
|
||||
"""Loggt ein Event für spätere Analyse"""
|
||||
if isinstance(event, AccountCreationEvent):
|
||||
self.repository.save_account_creation_event(event)
|
||||
logger.debug(f"Logged account creation event {event.event_id}")
|
||||
elif isinstance(event, ErrorEvent):
|
||||
self.repository.save_error_event(event)
|
||||
logger.debug(f"Logged error event {event.error_id}")
|
||||
else:
|
||||
logger.warning(f"Unknown event type: {type(event)}")
|
||||
|
||||
def get_success_rate(self,
|
||||
timeframe: Optional[timedelta] = None,
|
||||
platform: Optional[str] = None) -> float:
|
||||
"""Berechnet die Erfolgsrate"""
|
||||
return self.repository.get_success_rate(timeframe, platform)
|
||||
|
||||
def get_common_errors(self,
|
||||
limit: int = 10,
|
||||
timeframe: Optional[timedelta] = None) -> List[ErrorSummary]:
|
||||
"""Holt die häufigsten Fehler"""
|
||||
return self.repository.get_common_errors(limit, timeframe)
|
||||
|
||||
def generate_report(self,
|
||||
report_type: ReportType,
|
||||
start: datetime,
|
||||
end: datetime,
|
||||
platforms: Optional[List[str]] = None) -> Report:
|
||||
"""Generiert einen Report"""
|
||||
# Hole Basis-Metriken
|
||||
timeframe = end - start
|
||||
success_rate = self.get_success_rate(timeframe, platforms[0] if platforms else None)
|
||||
|
||||
# Hole Platform-Statistiken
|
||||
platform_stats_data = self.repository.get_platform_stats(timeframe)
|
||||
platform_stats = []
|
||||
|
||||
for platform, stats in platform_stats_data.items():
|
||||
if not platforms or platform in platforms:
|
||||
platform_stats.append(PlatformStats(
|
||||
platform=platform,
|
||||
total_attempts=stats['total_attempts'],
|
||||
successful_accounts=stats['successful_accounts'],
|
||||
failed_attempts=stats['failed_attempts'],
|
||||
avg_duration_seconds=stats['avg_duration_seconds'],
|
||||
error_distribution={} # TODO: Implementiere Error Distribution
|
||||
))
|
||||
|
||||
# Berechne Gesamt-Statistiken
|
||||
total_attempts = sum(ps.total_attempts for ps in platform_stats)
|
||||
total_accounts = sum(ps.successful_accounts for ps in platform_stats)
|
||||
avg_duration = sum(ps.avg_duration_seconds * ps.total_attempts for ps in platform_stats) / total_attempts if total_attempts > 0 else 0
|
||||
|
||||
# Erstelle Metriken
|
||||
metrics = [
|
||||
Metric("success_rate", success_rate, "percentage", 0.0),
|
||||
Metric("total_accounts", float(total_accounts), "count", 0.0),
|
||||
Metric("avg_duration", avg_duration, "seconds", 0.0)
|
||||
]
|
||||
|
||||
# Hole Timeline-Daten
|
||||
timeline_data = self.repository.get_timeline_data('success_rate', 24, platforms[0] if platforms else None)
|
||||
|
||||
success_timeline = None
|
||||
if timeline_data:
|
||||
timestamps = [datetime.fromisoformat(d['timestamp']) for d in timeline_data]
|
||||
values = [d['success_rate'] for d in timeline_data]
|
||||
success_timeline = TimeSeriesData(timestamps, values, "Success Rate")
|
||||
|
||||
# Hole Error Summaries
|
||||
error_summaries = []
|
||||
common_errors = self.get_common_errors(10, timeframe)
|
||||
for error in common_errors:
|
||||
error_summaries.append(error.to_dict())
|
||||
|
||||
# Erstelle Report
|
||||
return Report(
|
||||
report_id=str(uuid.uuid4()),
|
||||
report_type=report_type,
|
||||
start_date=start,
|
||||
end_date=end,
|
||||
generated_at=datetime.now(),
|
||||
total_accounts_created=total_accounts,
|
||||
total_attempts=total_attempts,
|
||||
overall_success_rate=success_rate,
|
||||
avg_creation_time=avg_duration,
|
||||
metrics=metrics,
|
||||
platform_stats=platform_stats,
|
||||
error_summaries=error_summaries,
|
||||
success_rate_timeline=success_timeline
|
||||
)
|
||||
|
||||
def get_real_time_metrics(self) -> Dict[str, Any]:
|
||||
"""Holt Echtzeit-Metriken"""
|
||||
# Letzte Stunde
|
||||
one_hour_ago = datetime.now() - timedelta(hours=1)
|
||||
|
||||
# Timeline für letzte Stunde
|
||||
timeline = self.repository.get_timeline_data('success_rate', 1)
|
||||
|
||||
# Berechne Metriken
|
||||
total_attempts = sum(d['total'] for d in timeline)
|
||||
successful = sum(d['successful'] for d in timeline)
|
||||
success_rate = successful / total_attempts if total_attempts > 0 else 0
|
||||
|
||||
# Platform Stats
|
||||
platform_stats = self.repository.get_platform_stats(timedelta(hours=1))
|
||||
|
||||
return {
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'active_sessions': len(self.repository._execute_query(
|
||||
"SELECT DISTINCT session_id FROM account_creation_analytics WHERE timestamp > ?",
|
||||
(one_hour_ago,)
|
||||
)),
|
||||
'accounts_last_hour': successful,
|
||||
'attempts_last_hour': total_attempts,
|
||||
'success_rate_last_hour': success_rate,
|
||||
'avg_creation_time': sum(
|
||||
stats.get('avg_duration_seconds', 0)
|
||||
for stats in platform_stats.values()
|
||||
) / len(platform_stats) if platform_stats else 0,
|
||||
'platform_breakdown': platform_stats,
|
||||
'hourly_trend': self._calculate_trend(timeline)
|
||||
}
|
||||
|
||||
def _calculate_trend(self, timeline: List[Dict[str, Any]]) -> float:
|
||||
"""Berechnet Trend aus Timeline"""
|
||||
if len(timeline) < 2:
|
||||
return 0.0
|
||||
|
||||
# Vergleiche erste und letzte Hälfte
|
||||
mid = len(timeline) // 2
|
||||
first_half = timeline[:mid]
|
||||
second_half = timeline[mid:]
|
||||
|
||||
first_rate = sum(d.get('success_rate', 0) for d in first_half) / len(first_half) if first_half else 0
|
||||
second_rate = sum(d.get('success_rate', 0) for d in second_half) / len(second_half) if second_half else 0
|
||||
|
||||
if first_rate > 0:
|
||||
return ((second_rate - first_rate) / first_rate) * 100
|
||||
return 0.0
|
||||
|
||||
def track_performance(self,
|
||||
metric_name: str,
|
||||
value: float,
|
||||
tags: Optional[Dict[str, str]] = None) -> None:
|
||||
"""Trackt Performance-Metrik"""
|
||||
# Würde in echter Implementation in separater Tabelle gespeichert
|
||||
logger.info(f"Performance metric: {metric_name}={value} tags={tags}")
|
||||
|
||||
def get_account_creation_timeline(self,
|
||||
hours: int = 24,
|
||||
platform: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""Holt Account Creation Timeline"""
|
||||
timeline = self.repository.get_timeline_data('accounts', hours, platform)
|
||||
|
||||
return {
|
||||
'hours': hours,
|
||||
'platform': platform,
|
||||
'data_points': timeline,
|
||||
'total': sum(d['successful'] for d in timeline),
|
||||
'peak_hour': max(timeline, key=lambda d: d['successful'])['timestamp'] if timeline else None
|
||||
}
|
||||
|
||||
def analyze_failure_patterns(self,
|
||||
timeframe: Optional[timedelta] = None) -> Dict[str, Any]:
|
||||
"""Analysiert Fehler-Muster"""
|
||||
errors = self.get_common_errors(50, timeframe)
|
||||
|
||||
patterns = {
|
||||
'timeframe': str(timeframe) if timeframe else 'all',
|
||||
'total_error_types': len(errors),
|
||||
'critical_errors': [],
|
||||
'recurring_errors': [],
|
||||
'error_clusters': []
|
||||
}
|
||||
|
||||
# Identifiziere kritische Fehler
|
||||
for error in errors:
|
||||
if error.severity_score > 0.7:
|
||||
patterns['critical_errors'].append({
|
||||
'type': error.error_type,
|
||||
'frequency': error.frequency,
|
||||
'impact': error.total_user_impact + error.total_system_impact
|
||||
})
|
||||
|
||||
# Identifiziere wiederkehrende Fehler
|
||||
for error in errors:
|
||||
if error.error_count > 10:
|
||||
patterns['recurring_errors'].append({
|
||||
'type': error.error_type,
|
||||
'count': error.error_count,
|
||||
'recovery_rate': error.recovery_success_rate
|
||||
})
|
||||
|
||||
return patterns
|
||||
|
||||
def get_platform_comparison(self,
|
||||
timeframe: Optional[timedelta] = None) -> Dict[str, Any]:
|
||||
"""Vergleicht Plattformen"""
|
||||
platform_stats = self.repository.get_platform_stats(timeframe)
|
||||
|
||||
comparison = {}
|
||||
for platform, stats in platform_stats.items():
|
||||
comparison[platform] = {
|
||||
'success_rate': stats['success_rate'],
|
||||
'total_accounts': stats['successful_accounts'],
|
||||
'avg_duration': stats['avg_duration_seconds'],
|
||||
'performance_score': self._calculate_platform_score(stats)
|
||||
}
|
||||
|
||||
return comparison
|
||||
|
||||
def _calculate_platform_score(self, stats: Dict[str, Any]) -> float:
|
||||
"""Berechnet Performance Score für Platform"""
|
||||
# Gewichtete Bewertung
|
||||
success_weight = 0.5
|
||||
speed_weight = 0.3
|
||||
volume_weight = 0.2
|
||||
|
||||
# Normalisiere Werte
|
||||
success_score = stats['success_rate']
|
||||
speed_score = 1.0 - min(stats['avg_duration_seconds'] / 300, 1.0) # 5 min max
|
||||
volume_score = min(stats['total_attempts'] / 100, 1.0) # 100 als Referenz
|
||||
|
||||
return (success_score * success_weight +
|
||||
speed_score * speed_weight +
|
||||
volume_score * volume_weight)
|
||||
|
||||
def export_data(self,
|
||||
format: str = "json",
|
||||
start: Optional[datetime] = None,
|
||||
end: Optional[datetime] = None) -> bytes:
|
||||
"""Exportiert Daten"""
|
||||
# Generiere Report für Zeitraum
|
||||
report = self.generate_report(
|
||||
ReportType.CUSTOM,
|
||||
start or datetime.now() - timedelta(days=7),
|
||||
end or datetime.now()
|
||||
)
|
||||
|
||||
if format == "json":
|
||||
return json.dumps(report.to_dict(), indent=2).encode()
|
||||
elif format == "csv":
|
||||
# Vereinfachte CSV-Implementation
|
||||
csv_data = "platform,attempts,success,rate\n"
|
||||
for stat in report.platform_stats:
|
||||
csv_data += f"{stat.platform},{stat.total_attempts},{stat.successful_accounts},{stat.success_rate}\n"
|
||||
return csv_data.encode()
|
||||
else:
|
||||
raise ValueError(f"Unsupported format: {format}")
|
||||
|
||||
def cleanup_old_events(self, older_than: datetime) -> int:
|
||||
"""Bereinigt alte Events"""
|
||||
return self.repository.cleanup_old_events(older_than)
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren