rust_filesearch/px/
frecency.rs1use chrono::{DateTime, Duration, Utc};
8
9pub fn calculate_frecency(access_count: u32, last_accessed: Option<DateTime<Utc>>) -> f64 {
26 let frequency_score = ((access_count + 1) as f64).ln() * 10.0;
29
30 let recency_score = if let Some(last_access) = last_accessed {
32 let now = Utc::now();
33 let age = now.signed_duration_since(last_access);
34 recency_weight(age)
35 } else {
36 0.0 };
38
39 frequency_score + recency_score
40}
41
42fn recency_weight(age: Duration) -> f64 {
54 let days = age.num_days();
55
56 match days {
57 0..=4 => 100.0, 5..=14 => 70.0, 15..=31 => 50.0, 32..=90 => 30.0, _ => 10.0, }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use chrono::Duration;
69
70 #[test]
71 fn test_calculate_frecency_never_accessed() {
72 let score = calculate_frecency(0, None);
73 assert_eq!(score, 0.0);
75 }
76
77 #[test]
78 fn test_calculate_frecency_accessed_today() {
79 let now = Utc::now();
80 let score = calculate_frecency(5, Some(now));
81
82 let expected = (6.0_f64).ln() * 10.0 + 100.0;
84 assert!((score - expected).abs() < 0.001);
85 }
86
87 #[test]
88 fn test_calculate_frecency_accessed_week_ago() {
89 let week_ago = Utc::now() - Duration::days(7);
90 let score = calculate_frecency(3, Some(week_ago));
91
92 let expected = (4.0_f64).ln() * 10.0 + 70.0;
94 assert!((score - expected).abs() < 0.001);
95 }
96
97 #[test]
98 fn test_calculate_frecency_accessed_month_ago() {
99 let month_ago = Utc::now() - Duration::days(20);
100 let score = calculate_frecency(10, Some(month_ago));
101
102 let expected = (11.0_f64).ln() * 10.0 + 50.0;
104 assert!((score - expected).abs() < 0.001);
105 }
106
107 #[test]
108 fn test_calculate_frecency_accessed_long_ago() {
109 let long_ago = Utc::now() - Duration::days(100);
110 let score = calculate_frecency(2, Some(long_ago));
111
112 let expected = (3.0_f64).ln() * 10.0 + 10.0;
114 assert!((score - expected).abs() < 0.001);
115 }
116
117 #[test]
118 fn test_recency_weight() {
119 assert_eq!(recency_weight(Duration::days(0)), 100.0);
120 assert_eq!(recency_weight(Duration::days(2)), 100.0);
121 assert_eq!(recency_weight(Duration::days(4)), 100.0);
122 assert_eq!(recency_weight(Duration::days(5)), 70.0);
123 assert_eq!(recency_weight(Duration::days(10)), 70.0);
124 assert_eq!(recency_weight(Duration::days(20)), 50.0);
125 assert_eq!(recency_weight(Duration::days(60)), 30.0);
126 assert_eq!(recency_weight(Duration::days(100)), 10.0);
127 }
128
129 #[test]
130 fn test_frecency_favors_recent_over_frequent() {
131 let recent_low_count = calculate_frecency(2, Some(Utc::now()));
132 let old_high_count = calculate_frecency(20, Some(Utc::now() - Duration::days(100)));
133
134 assert!(recent_low_count > old_high_count);
137 }
138
139 #[test]
140 fn test_frecency_frequency_still_matters() {
141 let recent_high = calculate_frecency(20, Some(Utc::now()));
142 let recent_low = calculate_frecency(2, Some(Utc::now()));
143
144 assert!(recent_high > recent_low);
146 }
147}