1use crate::adaptive::RecommendationType;
7use crate::traits::{
8 Achievement, AchievementTier, FocusArea, Goal, GoalMetric, ProgressSnapshot, TimeRange,
9};
10use crate::FeedbackError;
11use chrono::{DateTime, Utc};
12use serde::{Deserialize, Serialize};
13use std::collections::{HashMap, VecDeque};
14use std::time::Duration;
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct AnalyticsConfig {
23 pub enable_detailed_analytics: bool,
25 pub data_retention_days: u32,
27 pub max_metrics_capacity: usize,
29 pub memory_limit_bytes: usize,
31 pub cleanup_interval_minutes: u32,
33 pub memory_cleanup_threshold: f64,
35 pub enable_auto_aggregation: bool,
37 pub max_aggregated_metrics: usize,
39}
40
41impl Default for AnalyticsConfig {
42 fn default() -> Self {
43 Self {
44 enable_detailed_analytics: true,
45 data_retention_days: 90,
46 max_metrics_capacity: 10_000,
47 memory_limit_bytes: 50 * 1024 * 1024, cleanup_interval_minutes: 60, memory_cleanup_threshold: 0.8, enable_auto_aggregation: true,
51 max_aggregated_metrics: 1_000,
52 }
53 }
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct AnalyticsMetric {
59 pub name: String,
61 pub value: f64,
63 pub timestamp: DateTime<Utc>,
65 pub metric_type: MetricType,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
71pub enum MetricType {
72 Counter,
74 Gauge,
76 Histogram,
78 Timer,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct ComprehensiveAnalyticsReport {
85 pub timestamp: DateTime<Utc>,
87 pub metrics: Vec<AnalyticsMetric>,
89 pub summary: AnalyticsSummary,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct AnalyticsSummary {
96 pub total_metrics: usize,
98 pub average_value: f64,
100 pub time_range: TimeRange,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct StatisticalSignificanceResult {
107 pub p_value: f64,
109 pub is_significant: bool,
111 pub confidence_level: f64,
113 pub effect_size: f64,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct ComparativeAnalyticsResult {
120 pub baseline_value: f64,
122 pub comparison_value: f64,
124 pub percentage_change: f64,
126 pub statistical_significance: StatisticalSignificanceResult,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct LongitudinalStudyData {
133 pub study_period: TimeRange,
135 pub data_points: Vec<LongitudinalDataPoint>,
137 pub trend_analysis: TrendAnalysis,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct LongitudinalDataPoint {
144 pub timestamp: DateTime<Utc>,
146 pub value: f64,
148 pub metadata: HashMap<String, String>,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct TrendAnalysis {
155 pub trend_direction: crate::progress::analytics::TrendDirection,
157 pub slope: f64,
159 pub r_squared: f64,
161}
162
163#[derive(Debug, Clone)]
165pub struct TrendAnalytics {
166 pub improvement_velocity: f32,
168 pub performance_stability: f32,
170 pub trend_direction: crate::progress::analytics::TrendDirection,
172 pub slope: f32,
174 pub r_squared: f32,
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct AchievementDefinition {
185 pub id: String,
187 pub name: String,
189 pub description: String,
191 pub condition: AchievementCondition,
193 pub tier: AchievementTier,
195 pub points: u32,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
201pub enum AchievementCondition {
202 SessionCount(usize),
204 SkillLevel(f32),
206 Streak(usize),
208 AreaMastery(FocusArea, f32),
210 TrainingTime(Duration),
212}
213
214#[derive(Debug, Clone, Serialize, Deserialize)]
216pub struct DetailedProgressReport {
217 pub user_id: String,
219 pub period: TimeRange,
221 pub overall_improvement: f32,
223 pub area_improvements: HashMap<FocusArea, f32>,
225 pub skill_trends: HashMap<FocusArea, crate::progress::analytics::TrendDirection>,
227 pub session_analytics: crate::progress::metrics::SessionAnalytics,
229 pub consistency_metrics: crate::progress::metrics::ConsistencyMetrics,
231 pub achievement_progress: Vec<AchievementAnalysis>,
233 pub goal_analysis: Vec<GoalAnalysis>,
235 pub recommendations: Vec<ProgressRecommendation>,
237 pub comparative_analysis: ComparativeAnalysis,
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct AchievementAnalysis {
244 pub achievement_id: String,
246 pub name: String,
248 pub current_progress: f32,
250 pub is_unlocked: bool,
252 pub estimated_time_to_unlock: Option<Duration>,
254}
255
256#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct GoalAnalysis {
259 pub goal: Goal,
261 pub current_value: f32,
263 pub progress_percentage: f32,
265 pub on_track: bool,
267 pub estimated_completion: Option<DateTime<Utc>>,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
273pub struct ProgressRecommendation {
274 pub recommendation_type: RecommendationType,
276 pub title: String,
278 pub description: String,
280 pub priority: f32,
282 pub estimated_impact: f32,
284 pub suggested_actions: Vec<String>,
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct ComparativeAnalysis {
291 pub user_percentile: f32,
293 pub average_user_score: f32,
295 pub user_score: f32,
297 pub improvement_rate_vs_average: f32,
299 pub strengths_vs_peers: Vec<String>,
301 pub areas_for_improvement: Vec<String>,
303}
304
305#[derive(Debug, Clone, Serialize, Deserialize)]
307pub struct LearningPatternAnalysis {
308 pub learning_velocity: f32,
310 pub optimal_session_length: Duration,
312 pub peak_performance_times: Vec<PeakPerformanceTime>,
314 pub difficulty_preference: DifficultyPreference,
316 pub focus_area_patterns: HashMap<FocusArea, FocusPattern>,
318 pub consistency_patterns: ConsistencyPattern,
320 pub improvement_plateaus: Vec<LearningPlateau>,
322 pub learning_style_indicators: LearningStyleProfile,
324}
325
326#[derive(Debug, Clone, Serialize, Deserialize)]
328pub struct PeakPerformanceTime {
329 pub time_range: String,
331 pub performance_boost: f32,
333 pub confidence: f32,
335}
336
337#[derive(Debug, Clone, Serialize, Deserialize)]
339pub struct DifficultyPreference {
340 pub preferred_level: f32,
342 pub adaptability: f32,
344 pub challenge_seeking: f32,
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize)]
350pub struct FocusPattern {
351 pub attention_frequency: f32,
353 pub improvement_rate: f32,
355 pub plateau_tendency: f32,
357}
358
359#[derive(Debug, Clone, Serialize, Deserialize)]
361pub struct ConsistencyPattern {
362 pub overall_consistency: f32,
364 pub performance_variance: f32,
366 pub streak_tendency: f32,
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
372pub struct LearningPlateau {
373 pub start_date: DateTime<Utc>,
375 pub end_date: DateTime<Utc>,
377 pub skill_area: FocusArea,
379 pub plateau_level: f32,
381 pub breakthrough_suggestions: Vec<String>,
383}
384
385#[derive(Debug, Clone, Serialize, Deserialize)]
387pub struct LearningStyleProfile {
388 pub visual_preference: f32,
390 pub auditory_preference: f32,
392 pub kinesthetic_preference: f32,
394 pub structured_preference: f32,
396 pub experimental_preference: f32,
398}
399
400#[derive(Debug, Clone, Serialize, Deserialize)]
402pub struct AchievementProgress {
403 pub achievement_definition: AchievementDefinition,
405 pub current_progress: f32,
407 pub is_unlocked: bool,
409 pub unlock_date: Option<DateTime<Utc>>,
411}
412
413#[derive(Debug, Clone, Serialize, Deserialize)]
419pub struct AdaptiveMilestone {
420 pub milestone_id: String,
422 pub title: String,
424 pub description: String,
426 pub milestone_type: MilestoneType,
428 pub criteria: MilestoneCriteria,
430 pub estimated_duration: Duration,
432 pub difficulty: f32,
434 pub motivational_impact: f32,
436 pub personalized_message: String,
438 pub prerequisites: Vec<String>,
440 pub rewards: Vec<MilestoneReward>,
442 pub created_at: DateTime<Utc>,
444 pub achieved_at: Option<DateTime<Utc>>,
446}
447
448#[derive(Debug, Clone, Serialize, Deserialize)]
450pub enum MilestoneType {
451 SkillImprovement,
453 ConsistencyGoal,
455 AchievementGoal,
457 ProgressGoal,
459 Custom,
461}
462
463#[derive(Debug, Clone, Serialize, Deserialize)]
465pub enum MilestoneCriteria {
466 SkillLevel {
468 focus_area: FocusArea,
470 target_level: f32,
472 },
473 SessionCount {
475 target_sessions: usize,
477 },
478 Streak {
480 target_streak: usize,
482 },
483 OverallSkill {
485 target_level: f32,
487 },
488 TrainingTime {
490 target_duration: Duration,
492 },
493}
494
495#[derive(Debug, Clone, Serialize, Deserialize)]
497pub enum MilestoneReward {
498 Points(u32),
500 Badge(String),
502 UnlockFeature(String),
504 Certificate(String),
506 Custom(String),
508}
509
510#[derive(Debug, Clone, Default)]
512pub struct ComprehensiveStreakAnalysis {
513 pub current_streaks: HashMap<StreakType, CurrentStreak>,
515 pub historical_streaks: Vec<HistoricalStreak>,
517 pub streak_patterns: StreakPatterns,
519 pub motivation_maintenance: MotivationMaintenance,
521 pub recovery_mechanisms: Vec<RecoveryMechanism>,
523 pub achievement_potential: f32,
525}
526
527#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
529pub enum StreakType {
530 Practice,
532 Quality,
534 Improvement,
536 Consistency,
538}
539
540#[derive(Debug, Clone)]
542pub struct CurrentStreak {
543 pub streak_type: StreakType,
545 pub current_count: usize,
547 pub start_date: DateTime<Utc>,
549 pub last_activity: DateTime<Utc>,
551 pub strength: f32,
553}
554
555#[derive(Debug, Clone, Serialize, Deserialize)]
557pub struct HistoricalStreak {
558 pub streak_type: StreakType,
560 pub length: usize,
562 pub start_date: DateTime<Utc>,
564 pub end_date: DateTime<Utc>,
566 pub peak_performance: f32,
568 pub break_reason: StreakBreakReason,
570}
571
572#[derive(Debug, Clone, Serialize, Deserialize)]
574pub enum StreakBreakReason {
575 TimeConstraints,
577 LackOfMotivation,
579 Technical,
581 External,
583 Planned,
585 Unknown,
587 Ongoing,
589}
590
591#[derive(Debug, Clone, Default)]
593pub struct StreakPatterns {
594 pub average_streak_length: f32,
596 pub longest_streak_ever: usize,
598 pub common_break_reasons: Vec<StreakBreakReason>,
600 pub optimal_session_times: Vec<String>,
602 pub seasonal_variations: HashMap<String, f32>,
604}
605
606#[derive(Debug, Clone, Default)]
608pub struct MotivationMaintenance {
609 pub current_motivation_level: f32,
611 pub motivation_trend: f32,
613 pub burnout_risk: f32,
615 pub engagement_strategies: Vec<String>,
617}
618
619#[derive(Debug, Clone)]
621pub struct RecoveryMechanism {
622 pub strategy_type: RecoveryStrategyType,
624 pub description: String,
626 pub estimated_effectiveness: f32,
628 pub time_commitment: Duration,
630}
631
632#[derive(Debug, Clone)]
634pub enum RecoveryStrategyType {
635 GradualReturn,
637 MotivationBoost,
639 SocialSupport,
641 RoutineAdjustment,
643 GoalModification,
645}
646
647#[derive(Debug, Clone)]
649pub struct StreakRecoveryPlan {
650 pub recovery_strategy: RecoveryStrategyType,
652 pub suggested_actions: Vec<String>,
654 pub motivation_boosters: Vec<String>,
656 pub milestone_adjustments: Vec<String>,
658 pub estimated_recovery_time: Duration,
660 pub success_probability: f32,
662}
663
664#[derive(Debug, Clone, Serialize, Deserialize)]
670pub struct MemoryBoundedMetrics {
671 storage: VecDeque<(String, AnalyticsMetric)>,
673 capacity: usize,
675 index: HashMap<String, usize>,
677}
678
679impl MemoryBoundedMetrics {
680 #[must_use]
682 pub fn new(capacity: usize) -> Self {
683 Self {
684 storage: VecDeque::with_capacity(capacity),
685 capacity,
686 index: HashMap::new(),
687 }
688 }
689
690 pub fn insert(&mut self, key: String, metric: AnalyticsMetric) {
692 if self.storage.len() >= self.capacity {
694 if let Some((old_key, _)) = self.storage.pop_front() {
695 self.index.remove(&old_key);
696 }
697 }
698
699 let new_index = self.storage.len();
701 self.storage.push_back((key.clone(), metric));
702 self.index.insert(key, new_index);
703
704 if self.storage.len() != self.index.len() {
706 self.rebuild_index();
707 }
708 }
709
710 #[must_use]
712 pub fn get(&self, key: &str) -> Option<&AnalyticsMetric> {
713 if let Some(&index) = self.index.get(key) {
714 if index < self.storage.len() {
715 return Some(&self.storage[index].1);
716 }
717 }
718 None
719 }
720
721 pub fn cleanup_before(&mut self, cutoff_time: DateTime<Utc>) {
723 let mut removed_count = 0;
724
725 while let Some((key, metric)) = self.storage.front() {
727 if metric.timestamp < cutoff_time {
728 let (removed_key, _) = self.storage.pop_front().unwrap();
729 self.index.remove(&removed_key);
730 removed_count += 1;
731 } else {
732 break;
733 }
734 }
735
736 if removed_count > 0 {
738 self.rebuild_index();
739 }
740 }
741
742 fn rebuild_index(&mut self) {
744 self.index.clear();
745 for (i, (key, _)) in self.storage.iter().enumerate() {
746 self.index.insert(key.clone(), i);
747 }
748 }
749
750 #[must_use]
752 pub fn len(&self) -> usize {
753 self.storage.len()
754 }
755
756 #[must_use]
758 pub fn is_empty(&self) -> bool {
759 self.storage.is_empty()
760 }
761
762 #[must_use]
764 pub fn capacity(&self) -> usize {
765 self.capacity
766 }
767}
768
769#[derive(Debug, Clone, Serialize, Deserialize)]
771pub struct AggregatedMetric {
772 pub name: String,
774 pub count: u64,
776 pub sum: f64,
778 pub sum_of_squares: f64,
780 pub min: f64,
782 pub max: f64,
784 pub last_updated: DateTime<Utc>,
786 pub metric_type: MetricType,
788}
789
790impl AggregatedMetric {
791 #[must_use]
793 pub fn mean(&self) -> f64 {
794 if self.count > 0 {
795 self.sum / self.count as f64
796 } else {
797 0.0
798 }
799 }
800
801 #[must_use]
803 pub fn variance(&self) -> f64 {
804 if self.count > 1 {
805 let mean = self.mean();
806 (self.sum_of_squares - self.count as f64 * mean * mean) / (self.count - 1) as f64
807 } else {
808 0.0
809 }
810 }
811
812 #[must_use]
814 pub fn std_dev(&self) -> f64 {
815 self.variance().sqrt()
816 }
817}
818
819#[derive(Debug, Clone, Serialize, Deserialize)]
821pub struct MemoryStats {
822 pub total_metrics: usize,
824 pub aggregated_metrics: usize,
826 pub estimated_memory_bytes: usize,
828 pub memory_limit_bytes: usize,
830 pub memory_utilization: f64,
832}
833
834#[derive(Debug, Clone, Serialize, Deserialize)]
836pub struct CircularProgressHistory {
837 buffer: Vec<ProgressSnapshot>,
839 write_pos: usize,
841 items_written: usize,
843 capacity: usize,
845}
846
847impl CircularProgressHistory {
848 #[must_use]
850 pub fn new(capacity: usize) -> Self {
851 let mut buffer = Vec::with_capacity(capacity);
852 buffer.resize_with(capacity, || ProgressSnapshot {
853 timestamp: Utc::now(),
854 overall_score: 0.0,
855 area_scores: HashMap::new(),
856 session_count: 0,
857 events: Vec::new(),
858 });
859
860 Self {
861 buffer,
862 write_pos: 0,
863 items_written: 0,
864 capacity,
865 }
866 }
867
868 pub fn push(&mut self, snapshot: ProgressSnapshot) {
870 self.buffer[self.write_pos] = snapshot;
871 self.write_pos = (self.write_pos + 1) % self.capacity;
872 self.items_written += 1;
873 }
874
875 #[must_use]
877 pub fn get_recent(&self, count: usize) -> Vec<ProgressSnapshot> {
878 let actual_count = count.min(self.len());
879 let mut result = Vec::with_capacity(actual_count);
880
881 for i in 0..actual_count {
882 let pos = if self.write_pos > i {
883 self.write_pos - i - 1
884 } else {
885 self.capacity - (i + 1 - self.write_pos)
886 };
887 result.push(self.buffer[pos].clone());
888 }
889
890 result
891 }
892
893 #[must_use]
895 pub fn len(&self) -> usize {
896 self.items_written.min(self.capacity)
897 }
898
899 #[must_use]
901 pub fn is_empty(&self) -> bool {
902 self.items_written == 0
903 }
904
905 #[must_use]
907 pub fn memory_usage(&self) -> usize {
908 self.capacity * std::mem::size_of::<ProgressSnapshot>()
909 }
910}
911
912#[derive(Debug, Clone)]
914pub struct MemoryOptimizedProgress {
915 pub user_id: String,
917 pub overall_skill_level: f32,
919 pub skill_stats: HashMap<FocusArea, crate::progress::skills::CompressedSkillStats>,
921 pub progress_history: CircularProgressHistory,
923 pub achievement_summary: AchievementSummary,
925 pub training_stats: EssentialTrainingStats,
927 pub last_updated: DateTime<Utc>,
929}
930
931#[derive(Debug, Clone, Serialize, Deserialize)]
933pub struct EssentialTrainingStats {
934 pub total_sessions: u32,
936 pub total_practice_time_secs: u64,
938 pub current_streak: u16,
940 pub best_streak: u16,
942 pub avg_session_duration_secs: u32,
944}
945
946#[derive(Debug, Clone, Serialize, Deserialize)]
948pub struct AchievementSummary {
949 pub total_earned: u16,
951 pub by_tier: HashMap<AchievementTier, u16>,
953 pub recent: Vec<String>,
955}
956
957pub struct AnalyticsMemoryManager {
959 memory_limit: usize,
961 current_usage: usize,
963 cleanup_threshold: f64, }
966
967impl AnalyticsMemoryManager {
968 #[must_use]
970 pub fn new(memory_limit_mb: usize) -> Self {
971 Self {
972 memory_limit: memory_limit_mb * 1024 * 1024,
973 current_usage: 0,
974 cleanup_threshold: 0.8, }
976 }
977
978 pub fn update_usage(&mut self, new_usage: usize) {
980 self.current_usage = new_usage;
981 }
982
983 #[must_use]
985 pub fn needs_cleanup(&self) -> bool {
986 self.current_usage as f64 / self.memory_limit as f64 > self.cleanup_threshold
987 }
988
989 #[must_use]
991 pub fn utilization(&self) -> f64 {
992 self.current_usage as f64 / self.memory_limit as f64
993 }
994
995 #[must_use]
997 pub fn suggest_cleanup_actions(&self) -> Vec<CleanupAction> {
998 let mut actions = Vec::new();
999
1000 if self.utilization() > 0.9 {
1001 actions.push(CleanupAction::CompressOldData);
1002 actions.push(CleanupAction::RemoveOldestMetrics);
1003 } else if self.utilization() > 0.8 {
1004 actions.push(CleanupAction::CompressOldData);
1005 } else if self.utilization() > 0.7 {
1006 actions.push(CleanupAction::AggregateOldMetrics);
1007 }
1008
1009 actions
1010 }
1011}
1012
1013#[derive(Debug, Clone, PartialEq)]
1015pub enum CleanupAction {
1016 CompressOldData,
1018 RemoveOldestMetrics,
1020 AggregateOldMetrics,
1022 OptimizeDataStructures,
1024}
1025
1026pub trait MemoryOptimized {
1028 fn memory_usage(&self) -> usize;
1030 fn compress(&self) -> Self
1032 where
1033 Self: Sized;
1034 fn should_compress(&self) -> bool;
1036}