1pub mod handlers;
7pub mod integration;
8#[cfg(target_arch = "wasm32")]
9pub mod wasm;
10
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::time::{SystemTime, UNIX_EPOCH};
14
15use crate::error::{ErrorKind, MetadataError};
16
17#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
19pub enum AnalyticsEventType {
20 MetadataGenerated,
22 OgImageGenerated,
24 ThemeApplied,
26 MetadataValidated,
28 PerformanceMeasured,
30 ErrorOccurred,
32 UserInteraction,
34 Custom(String),
36}
37
38impl std::fmt::Display for AnalyticsEventType {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 match self {
41 AnalyticsEventType::MetadataGenerated => write!(f, "metadata_generated"),
42 AnalyticsEventType::OgImageGenerated => write!(f, "og_image_generated"),
43 AnalyticsEventType::ThemeApplied => write!(f, "theme_applied"),
44 AnalyticsEventType::MetadataValidated => write!(f, "metadata_validated"),
45 AnalyticsEventType::PerformanceMeasured => write!(f, "performance_measured"),
46 AnalyticsEventType::ErrorOccurred => write!(f, "error_occurred"),
47 AnalyticsEventType::UserInteraction => write!(f, "user_interaction"),
48 AnalyticsEventType::Custom(name) => write!(f, "custom_{}", name),
49 }
50 }
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct AnalyticsEvent {
56 pub event_type: AnalyticsEventType,
58 pub timestamp: u64,
60 pub duration_ms: Option<u64>,
62 pub properties: HashMap<String, serde_json::Value>,
64 pub session_id: Option<String>,
66 pub page_id: Option<String>,
68 pub error: Option<ErrorDetails>,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct ErrorDetails {
75 pub message: String,
77 pub kind: String,
79 pub stack_trace: Option<String>,
81 pub context: HashMap<String, serde_json::Value>,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct PerformanceMetrics {
88 pub generation_time_ms: u64,
90 pub memory_usage_bytes: Option<u64>,
92 pub cache_hit_rate: Option<f64>,
94 pub success_rate: f64,
96 pub error_count: u32,
98 pub total_operations: u32,
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct AnalyticsSession {
105 pub session_id: String,
107 pub start_time: u64,
109 pub end_time: Option<u64>,
111 pub user_agent: Option<String>,
113 pub page_views: u32,
115 pub events: Vec<AnalyticsEvent>,
117 pub performance: PerformanceMetrics,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct AnalyticsConfig {
124 pub enabled: bool,
126 pub batch_size: usize,
128 pub flush_interval_seconds: u64,
130 pub max_local_events: usize,
132 pub track_performance: bool,
134 pub track_errors: bool,
136 pub track_interactions: bool,
138 pub custom_event_types: Vec<String>,
140 pub privacy: PrivacySettings,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct PrivacySettings {
147 pub anonymize_ip: bool,
149 pub hash_identifiers: bool,
151 pub collect_user_agent: bool,
153 pub collect_page_urls: bool,
155 pub retention_days: u32,
157}
158
159impl Default for PrivacySettings {
160 fn default() -> Self {
161 Self {
162 anonymize_ip: true,
163 hash_identifiers: true,
164 collect_user_agent: true,
165 collect_page_urls: true,
166 retention_days: 90,
167 }
168 }
169}
170
171impl Default for AnalyticsConfig {
172 fn default() -> Self {
173 Self {
174 enabled: true,
175 batch_size: 10,
176 flush_interval_seconds: 30,
177 max_local_events: 1000,
178 track_performance: true,
179 track_errors: true,
180 track_interactions: true,
181 custom_event_types: vec![],
182 privacy: PrivacySettings::default(),
183 }
184 }
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct AnalyticsInsights {
190 pub performance: PerformanceInsights,
192 pub usage: UsageInsights,
194 pub errors: ErrorInsights,
196 pub recommendations: Vec<Recommendation>,
198 pub generated_at: u64,
200}
201
202#[derive(Debug, Clone, Serialize, Deserialize)]
204pub struct PerformanceInsights {
205 pub avg_generation_time_ms: f64,
207 pub slowest_operations: Vec<SlowOperation>,
209 pub trends: PerformanceTrends,
211 pub optimization_opportunities: Vec<String>,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
217pub struct SlowOperation {
218 pub operation_type: String,
220 pub avg_duration_ms: f64,
222 pub count: u32,
224 pub last_occurrence: u64,
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct PerformanceTrends {
231 pub direction: TrendDirection,
233 pub change_percentage: f64,
235 pub time_period: String,
237}
238
239#[derive(Debug, Clone, Serialize, Deserialize)]
241pub enum TrendDirection {
242 Improving,
243 Declining,
244 Stable,
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct UsageInsights {
250 pub popular_features: Vec<FeatureUsage>,
252 pub time_patterns: TimePatterns,
254 pub engagement: EngagementMetrics,
256 pub adoption: FeatureAdoption,
258}
259
260#[derive(Debug, Clone, Serialize, Deserialize)]
262pub struct FeatureUsage {
263 pub feature_name: String,
265 pub usage_count: u32,
267 pub usage_percentage: f64,
269 pub last_used: u64,
271}
272
273#[derive(Debug, Clone, Serialize, Deserialize)]
275pub struct TimePatterns {
276 pub peak_hours: Vec<u8>,
278 pub day_of_week: HashMap<String, u32>,
280 pub monthly: HashMap<String, u32>,
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
286pub struct EngagementMetrics {
287 pub avg_session_duration_ms: f64,
289 pub bounce_rate: f64,
291 pub return_user_rate: f64,
293 pub feature_depth: f64,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
299pub struct FeatureAdoption {
300 pub new_features: Vec<String>,
302 pub adoption_rate: f64,
304 pub time_to_adoption_days: f64,
306}
307
308#[derive(Debug, Clone, Serialize, Deserialize)]
310pub struct ErrorInsights {
311 pub common_errors: Vec<CommonError>,
313 pub error_trends: ErrorTrends,
315 pub resolution: ErrorResolution,
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct CommonError {
322 pub error_type: String,
324 pub count: u32,
326 pub rate: f64,
328 pub last_occurrence: u64,
330 pub common_context: HashMap<String, serde_json::Value>,
332}
333
334#[derive(Debug, Clone, Serialize, Deserialize)]
336pub struct ErrorTrends {
337 pub overall_trend: TrendDirection,
339 pub rate_change: f64,
341 pub new_error_types: Vec<String>,
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize)]
347pub struct ErrorResolution {
348 pub auto_resolved: u32,
350 pub manual_resolution_rate: f64,
352 pub avg_resolution_time_ms: f64,
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct Recommendation {
359 pub recommendation_type: RecommendationType,
361 pub priority: Priority,
363 pub title: String,
365 pub description: String,
367 pub action_items: Vec<String>,
369 pub expected_impact: String,
371 pub implementation_effort: EffortLevel,
373}
374
375#[derive(Debug, Clone, Serialize, Deserialize)]
377pub enum RecommendationType {
378 Performance,
379 ErrorReduction,
380 FeatureUsage,
381 UserExperience,
382 Security,
383 CostOptimization,
384}
385
386#[derive(Debug, Clone, Serialize, Deserialize)]
388pub enum Priority {
389 Low,
390 Medium,
391 High,
392 Critical,
393}
394
395#[derive(Debug, Clone, Serialize, Deserialize)]
397pub enum EffortLevel {
398 Low,
399 Medium,
400 High,
401}
402
403pub struct AnalyticsManager {
405 config: AnalyticsConfig,
407 current_session: Option<AnalyticsSession>,
409 event_queue: Vec<AnalyticsEvent>,
411 performance_metrics: PerformanceMetrics,
413 event_handlers: Vec<Box<dyn AnalyticsEventHandler>>,
415}
416
417pub trait AnalyticsEventHandler: Send + Sync {
419 fn handle_event(&self, event: &AnalyticsEvent) -> Result<(), MetadataError>;
421
422 fn handle_batch(&self, events: &[AnalyticsEvent]) -> Result<(), MetadataError>;
424
425 fn name(&self) -> &str;
427}
428
429impl AnalyticsManager {
430 pub fn new(config: AnalyticsConfig) -> Self {
432 Self {
433 config,
434 current_session: None,
435 event_queue: Vec::new(),
436 performance_metrics: PerformanceMetrics {
437 generation_time_ms: 0,
438 memory_usage_bytes: None,
439 cache_hit_rate: None,
440 success_rate: 1.0,
441 error_count: 0,
442 total_operations: 0,
443 },
444 event_handlers: Vec::new(),
445 }
446 }
447
448 pub fn start_session(
450 &mut self,
451 session_id: String,
452 user_agent: Option<String>,
453 ) -> Result<(), MetadataError> {
454 if !self.config.enabled {
455 return Ok(());
456 }
457
458 let current_time = SystemTime::now()
459 .duration_since(UNIX_EPOCH)
460 .map_err(|e| MetadataError::new(ErrorKind::Unknown, e.to_string()))?
461 .as_secs();
462
463 self.current_session = Some(AnalyticsSession {
464 session_id,
465 start_time: current_time,
466 end_time: None,
467 user_agent,
468 page_views: 0,
469 events: Vec::new(),
470 performance: self.performance_metrics.clone(),
471 });
472
473 Ok(())
474 }
475
476 pub fn end_session(&mut self) -> Result<(), MetadataError> {
478 if !self.config.enabled {
479 return Ok(());
480 }
481
482 if let Some(session) = &mut self.current_session {
483 let current_time = SystemTime::now()
484 .duration_since(UNIX_EPOCH)
485 .map_err(|e| MetadataError::new(ErrorKind::Unknown, e.to_string()))?
486 .as_secs();
487
488 session.end_time = Some(current_time);
489
490 self.flush_events()?;
492 }
493
494 self.current_session = None;
495 Ok(())
496 }
497
498 pub fn track_event(
500 &mut self,
501 event_type: AnalyticsEventType,
502 properties: HashMap<String, serde_json::Value>,
503 duration_ms: Option<u64>,
504 ) -> Result<(), MetadataError> {
505 if !self.config.enabled {
506 return Ok(());
507 }
508
509 let current_time = SystemTime::now()
510 .duration_since(UNIX_EPOCH)
511 .map_err(|e| MetadataError::new(ErrorKind::Unknown, e.to_string()))?
512 .as_secs();
513
514 let event = AnalyticsEvent {
515 event_type,
516 timestamp: current_time,
517 duration_ms,
518 properties,
519 session_id: self.current_session.as_ref().map(|s| s.session_id.clone()),
520 page_id: None,
521 error: None,
522 };
523
524 self.add_event(event)?;
525 Ok(())
526 }
527
528 pub fn track_performance(
530 &mut self,
531 operation_type: &str,
532 duration_ms: u64,
533 success: bool,
534 memory_usage_bytes: Option<u64>,
535 ) -> Result<(), MetadataError> {
536 if !self.config.enabled || !self.config.track_performance {
537 return Ok(());
538 }
539
540 let mut properties = HashMap::new();
541 properties.insert(
542 "operation_type".to_string(),
543 serde_json::Value::String(operation_type.to_string()),
544 );
545 properties.insert("success".to_string(), serde_json::Value::Bool(success));
546
547 if let Some(memory) = memory_usage_bytes {
548 properties.insert(
549 "memory_usage_bytes".to_string(),
550 serde_json::Value::Number(serde_json::Number::from(memory)),
551 );
552 }
553
554 self.performance_metrics.total_operations += 1;
556 if !success {
557 self.performance_metrics.error_count += 1;
558 }
559
560 self.performance_metrics.success_rate = (self.performance_metrics.total_operations
561 - self.performance_metrics.error_count)
562 as f64
563 / self.performance_metrics.total_operations as f64;
564
565 self.track_event(
566 AnalyticsEventType::PerformanceMeasured,
567 properties,
568 Some(duration_ms),
569 )?;
570 Ok(())
571 }
572
573 pub fn track_error(
575 &mut self,
576 error: MetadataError,
577 context: HashMap<String, serde_json::Value>,
578 ) -> Result<(), MetadataError> {
579 if !self.config.enabled || !self.config.track_errors {
580 return Ok(());
581 }
582
583 let error_details = ErrorDetails {
584 message: error.message.clone(),
585 kind: format!("{:?}", error.kind),
586 stack_trace: None, context,
588 };
589
590 let mut properties = HashMap::new();
591 properties.insert(
592 "error_message".to_string(),
593 serde_json::Value::String(error.message),
594 );
595 properties.insert(
596 "error_kind".to_string(),
597 serde_json::Value::String(format!("{:?}", error.kind)),
598 );
599
600 let event = AnalyticsEvent {
601 event_type: AnalyticsEventType::ErrorOccurred,
602 timestamp: SystemTime::now()
603 .duration_since(UNIX_EPOCH)
604 .map_err(|e| MetadataError::new(ErrorKind::Unknown, e.to_string()))?
605 .as_secs(),
606 duration_ms: None,
607 properties,
608 session_id: self.current_session.as_ref().map(|s| s.session_id.clone()),
609 page_id: None,
610 error: Some(error_details),
611 };
612
613 self.add_event(event)?;
614 Ok(())
615 }
616
617 fn add_event(&mut self, event: AnalyticsEvent) -> Result<(), MetadataError> {
619 self.event_queue.push(event);
620
621 if let Some(session) = &mut self.current_session {
623 session
624 .events
625 .push(self.event_queue.last().unwrap().clone());
626 }
627
628 if self.event_queue.len() >= self.config.batch_size {
630 self.flush_events()?;
631 }
632
633 Ok(())
634 }
635
636 fn flush_events(&mut self) -> Result<(), MetadataError> {
638 if self.event_queue.is_empty() {
639 return Ok(());
640 }
641
642 let events = self.event_queue.clone();
643 self.event_queue.clear();
644
645 for handler in &self.event_handlers {
647 if let Err(e) = handler.handle_batch(&events) {
648 eprintln!("Analytics handler {} failed: {}", handler.name(), e.message);
649 }
650 }
651
652 Ok(())
653 }
654
655 pub fn add_handler(&mut self, handler: Box<dyn AnalyticsEventHandler>) {
657 self.event_handlers.push(handler);
658 }
659
660 pub fn generate_insights(&self) -> Result<AnalyticsInsights, MetadataError> {
662 let current_time = SystemTime::now()
663 .duration_since(UNIX_EPOCH)
664 .map_err(|e| MetadataError::new(ErrorKind::Unknown, e.to_string()))?
665 .as_secs();
666
667 let mut all_events = Vec::new();
669 if let Some(session) = &self.current_session {
670 all_events.extend(session.events.clone());
671 }
672 all_events.extend(self.event_queue.clone());
673
674 let performance = self.analyze_performance(&all_events);
675 let usage = self.analyze_usage(&all_events);
676 let errors = self.analyze_errors(&all_events);
677 let recommendations = self.generate_recommendations(&performance, &usage, &errors);
678
679 Ok(AnalyticsInsights {
680 performance,
681 usage,
682 errors,
683 recommendations,
684 generated_at: current_time,
685 })
686 }
687
688 fn analyze_performance(&self, events: &[AnalyticsEvent]) -> PerformanceInsights {
690 let performance_events: Vec<_> = events
691 .iter()
692 .filter(|e| matches!(e.event_type, AnalyticsEventType::PerformanceMeasured))
693 .collect();
694
695 let avg_generation_time = if !performance_events.is_empty() {
696 let total_time: u64 = performance_events
697 .iter()
698 .filter_map(|e| e.duration_ms)
699 .sum();
700 total_time as f64 / performance_events.len() as f64
701 } else {
702 0.0
703 };
704
705 let slowest_operations = self.identify_slow_operations(&performance_events);
706 let trends = self.calculate_performance_trends(&performance_events);
707 let optimization_opportunities =
708 self.identify_optimization_opportunities(&performance_events);
709
710 PerformanceInsights {
711 avg_generation_time_ms: avg_generation_time,
712 slowest_operations,
713 trends,
714 optimization_opportunities,
715 }
716 }
717
718 fn analyze_usage(&self, events: &[AnalyticsEvent]) -> UsageInsights {
720 let popular_features = self.identify_popular_features(events);
721 let time_patterns = self.analyze_time_patterns(events);
722 let engagement = self.calculate_engagement_metrics(events);
723 let adoption = self.calculate_feature_adoption(events);
724
725 UsageInsights {
726 popular_features,
727 time_patterns,
728 engagement,
729 adoption,
730 }
731 }
732
733 fn analyze_errors(&self, events: &[AnalyticsEvent]) -> ErrorInsights {
735 let error_events: Vec<_> = events
736 .iter()
737 .filter(|e| matches!(e.event_type, AnalyticsEventType::ErrorOccurred))
738 .collect();
739
740 let common_errors = self.identify_common_errors(&error_events);
741 let error_trends = self.calculate_error_trends(&error_events);
742 let resolution = self.calculate_error_resolution(&error_events);
743
744 ErrorInsights {
745 common_errors,
746 error_trends,
747 resolution,
748 }
749 }
750
751 fn identify_slow_operations(&self, events: &[&AnalyticsEvent]) -> Vec<SlowOperation> {
753 let mut operation_times: HashMap<String, Vec<u64>> = HashMap::new();
754
755 for event in events {
756 if let Some(duration) = event.duration_ms {
757 if let Some(operation_type) = event.properties.get("operation_type") {
758 if let Some(op_type) = operation_type.as_str() {
759 operation_times
760 .entry(op_type.to_string())
761 .or_default()
762 .push(duration);
763 }
764 }
765 }
766 }
767
768 let mut slow_operations = Vec::new();
769 for (operation_type, times) in operation_times {
770 let avg_duration = times.iter().sum::<u64>() as f64 / times.len() as f64;
771 let last_occurrence = events
772 .iter()
773 .filter(|e| {
774 e.properties.get("operation_type").and_then(|v| v.as_str())
775 == Some(&operation_type)
776 })
777 .map(|e| e.timestamp)
778 .max()
779 .unwrap_or(0);
780
781 slow_operations.push(SlowOperation {
782 operation_type,
783 avg_duration_ms: avg_duration,
784 count: times.len() as u32,
785 last_occurrence,
786 });
787 }
788
789 slow_operations.sort_by(|a, b| b.avg_duration_ms.partial_cmp(&a.avg_duration_ms).unwrap());
790 slow_operations.truncate(5); slow_operations
792 }
793
794 fn calculate_performance_trends(&self, events: &[&AnalyticsEvent]) -> PerformanceTrends {
796 if events.len() < 2 {
797 return PerformanceTrends {
798 direction: TrendDirection::Stable,
799 change_percentage: 0.0,
800 time_period: "insufficient_data".to_string(),
801 };
802 }
803
804 let mut sorted_events = events.to_vec();
805 sorted_events.sort_by_key(|e| e.timestamp);
806
807 let first_half = &sorted_events[..sorted_events.len() / 2];
808 let second_half = &sorted_events[sorted_events.len() / 2..];
809
810 let first_avg = first_half.iter().filter_map(|e| e.duration_ms).sum::<u64>() as f64
811 / first_half.len() as f64;
812 let second_avg = second_half
813 .iter()
814 .filter_map(|e| e.duration_ms)
815 .sum::<u64>() as f64
816 / second_half.len() as f64;
817
818 let change_percentage = if first_avg > 0.0 {
819 ((second_avg - first_avg) / first_avg) * 100.0
820 } else {
821 0.0
822 };
823
824 let direction = if change_percentage > 5.0 {
825 TrendDirection::Declining
826 } else if change_percentage < -5.0 {
827 TrendDirection::Improving
828 } else {
829 TrendDirection::Stable
830 };
831
832 PerformanceTrends {
833 direction,
834 change_percentage,
835 time_period: "recent".to_string(),
836 }
837 }
838
839 fn identify_optimization_opportunities(&self, events: &[&AnalyticsEvent]) -> Vec<String> {
841 let mut opportunities = Vec::new();
842
843 let slow_operations = self.identify_slow_operations(events);
845 for op in slow_operations {
846 if op.avg_duration_ms > 1000.0 {
847 opportunities.push(format!(
848 "Optimize {} - currently taking {:.0}ms on average",
849 op.operation_type, op.avg_duration_ms
850 ));
851 }
852 }
853
854 let total_events = events.len();
856 let error_events = events
857 .iter()
858 .filter(|e| e.properties.get("success").and_then(|v| v.as_bool()) == Some(false))
859 .count();
860
861 if total_events > 0 {
862 let error_rate = error_events as f64 / total_events as f64;
863 if error_rate > 0.1 {
864 opportunities.push(format!(
865 "High error rate detected: {:.1}% - investigate error causes",
866 error_rate * 100.0
867 ));
868 }
869 }
870
871 opportunities
872 }
873
874 fn identify_popular_features(&self, events: &[AnalyticsEvent]) -> Vec<FeatureUsage> {
876 let mut feature_counts: HashMap<String, u32> = HashMap::new();
877 let mut feature_last_used: HashMap<String, u64> = HashMap::new();
878
879 for event in events {
880 let feature_name = match &event.event_type {
881 AnalyticsEventType::MetadataGenerated => "metadata_generation",
882 AnalyticsEventType::OgImageGenerated => "og_image_generation",
883 AnalyticsEventType::ThemeApplied => "theme_application",
884 AnalyticsEventType::MetadataValidated => "metadata_validation",
885 AnalyticsEventType::PerformanceMeasured => "performance_tracking",
886 AnalyticsEventType::ErrorOccurred => "error_handling",
887 AnalyticsEventType::UserInteraction => "user_interaction",
888 AnalyticsEventType::Custom(name) => name,
889 };
890
891 *feature_counts.entry(feature_name.to_string()).or_insert(0) += 1;
892 feature_last_used.insert(feature_name.to_string(), event.timestamp);
893 }
894
895 let total_events = events.len() as f64;
896 let mut features: Vec<FeatureUsage> = feature_counts
897 .into_iter()
898 .map(|(name, count)| FeatureUsage {
899 usage_percentage: (count as f64 / total_events) * 100.0,
900 last_used: feature_last_used.get(&name).copied().unwrap_or(0),
901 feature_name: name,
902 usage_count: count,
903 })
904 .collect();
905
906 features.sort_by(|a, b| b.usage_count.cmp(&a.usage_count));
907 features.truncate(10); features
909 }
910
911 fn analyze_time_patterns(&self, events: &[AnalyticsEvent]) -> TimePatterns {
913 let mut hourly_counts = [0u32; 24];
914 let mut day_counts: HashMap<String, u32> = HashMap::new();
915 let mut monthly_counts: HashMap<String, u32> = HashMap::new();
916
917 for event in events {
918 let hour = (event.timestamp / 3600) % 24;
920 hourly_counts[hour as usize] += 1;
921
922 let day_of_week = (event.timestamp / 86400) % 7;
924 let day_name = match day_of_week {
925 0 => "Sunday",
926 1 => "Monday",
927 2 => "Tuesday",
928 3 => "Wednesday",
929 4 => "Thursday",
930 5 => "Friday",
931 6 => "Saturday",
932 _ => "Unknown",
933 };
934 *day_counts.entry(day_name.to_string()).or_insert(0) += 1;
935
936 let month = (event.timestamp / 2629746) % 12; let month_name = format!("Month_{}", month + 1);
939 *monthly_counts.entry(month_name).or_insert(0) += 1;
940 }
941
942 let mut peak_hours = Vec::new();
944 let max_hourly_count = hourly_counts.iter().max().copied().unwrap_or(0);
945 for (hour, &count) in hourly_counts.iter().enumerate() {
946 if count >= max_hourly_count * 3 / 4 {
947 peak_hours.push(hour as u8);
948 }
949 }
950
951 TimePatterns {
952 peak_hours,
953 day_of_week: day_counts,
954 monthly: monthly_counts,
955 }
956 }
957
958 fn calculate_engagement_metrics(&self, events: &[AnalyticsEvent]) -> EngagementMetrics {
960 let session_duration = if let Some(session) = &self.current_session {
961 if let Some(end_time) = session.end_time {
962 end_time - session.start_time
963 } else {
964 SystemTime::now()
965 .duration_since(UNIX_EPOCH)
966 .unwrap_or_default()
967 .as_secs()
968 - session.start_time
969 }
970 } else {
971 0
972 };
973
974 let unique_features = events
975 .iter()
976 .map(|e| format!("{:?}", e.event_type))
977 .collect::<std::collections::HashSet<_>>()
978 .len();
979
980 EngagementMetrics {
981 avg_session_duration_ms: session_duration as f64 * 1000.0,
982 bounce_rate: if events.len() < 2 { 1.0 } else { 0.0 },
983 return_user_rate: 0.0, feature_depth: unique_features as f64,
985 }
986 }
987
988 fn calculate_feature_adoption(&self, events: &[AnalyticsEvent]) -> FeatureAdoption {
990 let new_features = events
991 .iter()
992 .filter_map(|e| match &e.event_type {
993 AnalyticsEventType::Custom(name) => Some(name.clone()),
994 _ => None,
995 })
996 .collect::<std::collections::HashSet<_>>()
997 .into_iter()
998 .collect();
999
1000 FeatureAdoption {
1001 new_features,
1002 adoption_rate: 0.8, time_to_adoption_days: 7.0, }
1005 }
1006
1007 fn identify_common_errors(&self, events: &[&AnalyticsEvent]) -> Vec<CommonError> {
1009 let mut error_counts: HashMap<String, u32> = HashMap::new();
1010 let mut error_last_occurrence: HashMap<String, u64> = HashMap::new();
1011 let mut error_contexts: HashMap<String, HashMap<String, serde_json::Value>> =
1012 HashMap::new();
1013
1014 for event in events {
1015 if let Some(error) = &event.error {
1016 let error_key = format!("{}:{}", error.kind, error.message);
1017 *error_counts.entry(error_key.clone()).or_insert(0) += 1;
1018 error_last_occurrence.insert(error_key.clone(), event.timestamp);
1019 error_contexts.insert(error_key.clone(), error.context.clone());
1020 }
1021 }
1022
1023 let total_errors = events.len() as f64;
1024 let mut common_errors: Vec<CommonError> = error_counts
1025 .into_iter()
1026 .map(|(error_key, count)| {
1027 let parts: Vec<&str> = error_key.splitn(2, ':').collect();
1028 let error_type = parts.get(0).unwrap_or(&"Unknown").to_string();
1029 let _error_message = parts.get(1).unwrap_or(&"Unknown").to_string();
1030
1031 CommonError {
1032 error_type,
1033 count,
1034 rate: (count as f64 / total_errors) * 100.0,
1035 last_occurrence: error_last_occurrence.get(&error_key).copied().unwrap_or(0),
1036 common_context: error_contexts.get(&error_key).cloned().unwrap_or_default(),
1037 }
1038 })
1039 .collect();
1040
1041 common_errors.sort_by(|a, b| b.count.cmp(&a.count));
1042 common_errors.truncate(5); common_errors
1044 }
1045
1046 fn calculate_error_trends(&self, events: &[&AnalyticsEvent]) -> ErrorTrends {
1048 if events.len() < 2 {
1049 return ErrorTrends {
1050 overall_trend: TrendDirection::Stable,
1051 rate_change: 0.0,
1052 new_error_types: vec![],
1053 };
1054 }
1055
1056 let mut sorted_events = events.to_vec();
1057 sorted_events.sort_by_key(|e| e.timestamp);
1058
1059 let first_half = &sorted_events[..sorted_events.len() / 2];
1060 let second_half = &sorted_events[sorted_events.len() / 2..];
1061
1062 let first_rate = first_half.len() as f64;
1063 let second_rate = second_half.len() as f64;
1064
1065 let rate_change = if first_rate > 0.0 {
1066 ((second_rate - first_rate) / first_rate) * 100.0
1067 } else {
1068 0.0
1069 };
1070
1071 let overall_trend = if rate_change > 10.0 {
1072 TrendDirection::Declining
1073 } else if rate_change < -10.0 {
1074 TrendDirection::Improving
1075 } else {
1076 TrendDirection::Stable
1077 };
1078
1079 let mut new_error_types = Vec::new();
1081 let first_half_types: std::collections::HashSet<String> = first_half
1082 .iter()
1083 .filter_map(|e| e.error.as_ref().map(|err| err.kind.clone()))
1084 .collect();
1085
1086 for event in second_half {
1087 if let Some(error) = &event.error {
1088 if !first_half_types.contains(&error.kind) {
1089 new_error_types.push(error.kind.clone());
1090 }
1091 }
1092 }
1093
1094 ErrorTrends {
1095 overall_trend,
1096 rate_change,
1097 new_error_types,
1098 }
1099 }
1100
1101 fn calculate_error_resolution(&self, events: &[&AnalyticsEvent]) -> ErrorResolution {
1103 let total_errors = events.len() as u32;
1104 let auto_resolved = events
1105 .iter()
1106 .filter(|e| e.properties.get("auto_resolved").and_then(|v| v.as_bool()) == Some(true))
1107 .count() as u32;
1108
1109 ErrorResolution {
1110 auto_resolved,
1111 manual_resolution_rate: if total_errors > 0 {
1112 (total_errors - auto_resolved) as f64 / total_errors as f64
1113 } else {
1114 0.0
1115 },
1116 avg_resolution_time_ms: 1000.0, }
1118 }
1119
1120 fn generate_recommendations(
1122 &self,
1123 performance: &PerformanceInsights,
1124 usage: &UsageInsights,
1125 errors: &ErrorInsights,
1126 ) -> Vec<Recommendation> {
1127 let mut recommendations = Vec::new();
1128
1129 if performance.avg_generation_time_ms > 500.0 {
1131 recommendations.push(Recommendation {
1132 recommendation_type: RecommendationType::Performance,
1133 priority: Priority::High,
1134 title: "Optimize Generation Performance".to_string(),
1135 description: "Metadata generation is taking longer than expected".to_string(),
1136 action_items: vec![
1137 "Implement caching for frequently generated metadata".to_string(),
1138 "Optimize image processing algorithms".to_string(),
1139 "Consider using Web Workers for heavy operations".to_string(),
1140 ],
1141 expected_impact: "Reduce generation time by 30-50%".to_string(),
1142 implementation_effort: EffortLevel::Medium,
1143 });
1144 }
1145
1146 if errors.common_errors.iter().any(|e| e.rate > 5.0) {
1148 recommendations.push(Recommendation {
1149 recommendation_type: RecommendationType::ErrorReduction,
1150 priority: Priority::High,
1151 title: "Address Common Errors".to_string(),
1152 description: "High error rates detected in metadata operations".to_string(),
1153 action_items: vec![
1154 "Add better input validation".to_string(),
1155 "Improve error handling and recovery".to_string(),
1156 "Add retry mechanisms for transient failures".to_string(),
1157 ],
1158 expected_impact: "Reduce error rate by 50-80%".to_string(),
1159 implementation_effort: EffortLevel::Medium,
1160 });
1161 }
1162
1163 if usage.adoption.adoption_rate < 0.5 {
1165 recommendations.push(Recommendation {
1166 recommendation_type: RecommendationType::FeatureUsage,
1167 priority: Priority::Medium,
1168 title: "Improve Feature Adoption".to_string(),
1169 description: "Low feature adoption rates detected".to_string(),
1170 action_items: vec![
1171 "Add feature discovery mechanisms".to_string(),
1172 "Improve documentation and examples".to_string(),
1173 "Add progressive disclosure for advanced features".to_string(),
1174 ],
1175 expected_impact: "Increase feature adoption by 25-40%".to_string(),
1176 implementation_effort: EffortLevel::Low,
1177 });
1178 }
1179
1180 recommendations
1181 }
1182
1183 pub fn get_performance_metrics(&self) -> &PerformanceMetrics {
1185 &self.performance_metrics
1186 }
1187
1188 pub fn get_config(&self) -> &AnalyticsConfig {
1190 &self.config
1191 }
1192
1193 pub fn update_config(&mut self, config: AnalyticsConfig) {
1195 self.config = config;
1196 }
1197}
1198
1199impl Default for AnalyticsManager {
1200 fn default() -> Self {
1201 Self::new(AnalyticsConfig::default())
1202 }
1203}