""" Report Value Object - Strukturierte Berichte für Analytics """ from dataclasses import dataclass, field from datetime import datetime, timedelta from typing import List, Dict, Any, Optional from enum import Enum class ReportType(Enum): """Typen von verfügbaren Berichten""" DAILY = "daily" WEEKLY = "weekly" MONTHLY = "monthly" CUSTOM = "custom" REAL_TIME = "real_time" class MetricType(Enum): """Typen von Metriken in Berichten""" SUCCESS_RATE = "success_rate" ERROR_RATE = "error_rate" AVG_DURATION = "avg_duration" TOTAL_ACCOUNTS = "total_accounts" ACCOUNTS_PER_HOUR = "accounts_per_hour" RETRY_RATE = "retry_rate" RECOVERY_RATE = "recovery_rate" @dataclass(frozen=True) class Metric: """Einzelne Metrik im Bericht""" name: str value: float unit: str trend: float = 0.0 # Prozentuale Veränderung zum Vorperiode @property def is_improving(self) -> bool: """Prüft ob sich die Metrik verbessert""" positive_metrics = ["success_rate", "recovery_rate", "accounts_per_hour"] if self.name in positive_metrics: return self.trend > 0 return self.trend < 0 @dataclass(frozen=True) class PlatformStats: """Statistiken für eine spezifische Plattform""" platform: str total_attempts: int successful_accounts: int failed_attempts: int avg_duration_seconds: float error_distribution: Dict[str, int] @property def success_rate(self) -> float: """Berechnet die Erfolgsrate""" if self.total_attempts > 0: return self.successful_accounts / self.total_attempts return 0.0 @dataclass(frozen=True) class TimeSeriesData: """Zeitreihen-Daten für Graphen""" timestamps: List[datetime] values: List[float] label: str def get_average(self) -> float: """Berechnet den Durchschnitt der Werte""" if self.values: return sum(self.values) / len(self.values) return 0.0 def get_trend(self) -> float: """Berechnet den Trend (vereinfacht: Vergleich erste/letzte Hälfte)""" if len(self.values) < 2: return 0.0 mid = len(self.values) // 2 first_half_avg = sum(self.values[:mid]) / mid second_half_avg = sum(self.values[mid:]) / (len(self.values) - mid) if first_half_avg > 0: return ((second_half_avg - first_half_avg) / first_half_avg) * 100 return 0.0 @dataclass(frozen=True) class Report: """ Strukturierter Bericht für Analytics. Frozen dataclass macht es unveränderlich (Value Object). """ report_id: str report_type: ReportType start_date: datetime end_date: datetime generated_at: datetime # Zusammenfassende Metriken total_accounts_created: int total_attempts: int overall_success_rate: float avg_creation_time: float # in Sekunden # Detaillierte Metriken metrics: List[Metric] platform_stats: List[PlatformStats] error_summaries: List[Dict[str, Any]] # ErrorSummary als Dict # Zeitreihen-Daten success_rate_timeline: Optional[TimeSeriesData] = None creation_rate_timeline: Optional[TimeSeriesData] = None error_rate_timeline: Optional[TimeSeriesData] = None # Top-Erkenntnisse insights: List[str] = field(default_factory=list) recommendations: List[str] = field(default_factory=list) def __post_init__(self): """Validierung der Report-Daten""" if self.start_date > self.end_date: raise ValueError("Start date kann nicht nach end date liegen") if not 0.0 <= self.overall_success_rate <= 1.0: raise ValueError("Success rate muss zwischen 0.0 und 1.0 liegen") if self.avg_creation_time < 0: raise ValueError("Average creation time kann nicht negativ sein") @property def duration(self) -> timedelta: """Dauer des Berichtszeitraums""" return self.end_date - self.start_date @property def accounts_per_day(self) -> float: """Durchschnittliche Accounts pro Tag""" days = self.duration.days or 1 return self.total_accounts_created / days def get_metric(self, metric_type: MetricType) -> Optional[Metric]: """Holt eine spezifische Metrik""" for metric in self.metrics: if metric.name == metric_type.value: return metric return None def get_platform_stats(self, platform: str) -> Optional[PlatformStats]: """Holt Statistiken für eine spezifische Plattform""" for stats in self.platform_stats: if stats.platform.lower() == platform.lower(): return stats return None def to_dict(self) -> Dict[str, Any]: """Konvertiert zu Dictionary für Serialisierung""" return { 'report_id': self.report_id, 'report_type': self.report_type.value, 'start_date': self.start_date.isoformat(), 'end_date': self.end_date.isoformat(), 'generated_at': self.generated_at.isoformat(), 'duration_days': self.duration.days, 'total_accounts_created': self.total_accounts_created, 'total_attempts': self.total_attempts, 'overall_success_rate': self.overall_success_rate, 'avg_creation_time': self.avg_creation_time, 'accounts_per_day': self.accounts_per_day, 'metrics': [ { 'name': m.name, 'value': m.value, 'unit': m.unit, 'trend': m.trend, 'is_improving': m.is_improving } for m in self.metrics ], 'platform_stats': [ { 'platform': ps.platform, 'total_attempts': ps.total_attempts, 'successful_accounts': ps.successful_accounts, 'failed_attempts': ps.failed_attempts, 'success_rate': ps.success_rate, 'avg_duration_seconds': ps.avg_duration_seconds, 'error_distribution': ps.error_distribution } for ps in self.platform_stats ], 'error_summaries': self.error_summaries, 'success_rate_timeline': { 'timestamps': [t.isoformat() for t in self.success_rate_timeline.timestamps], 'values': self.success_rate_timeline.values, 'label': self.success_rate_timeline.label, 'average': self.success_rate_timeline.get_average(), 'trend': self.success_rate_timeline.get_trend() } if self.success_rate_timeline else None, 'insights': self.insights, 'recommendations': self.recommendations }