1use super::WorkRecord;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use time::Date;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct DayData {
8 pub date: Date,
9 pub last_id: u32,
10 pub work_records: HashMap<u32, WorkRecord>,
11}
12
13impl DayData {
14 pub fn new(date: Date) -> Self {
15 DayData {
16 date,
17 last_id: 0,
18 work_records: HashMap::new(),
19 }
20 }
21
22 pub fn add_record(&mut self, record: WorkRecord) {
23 if record.id > self.last_id {
24 self.last_id = record.id;
25 }
26 self.work_records.insert(record.id, record);
27 }
28
29 pub fn remove_record(&mut self, id: u32) -> Option<WorkRecord> {
30 self.work_records.remove(&id)
31 }
32
33 pub fn next_id(&mut self) -> u32 {
34 self.last_id += 1;
35 self.last_id
36 }
37
38 pub fn get_sorted_records(&self) -> Vec<&WorkRecord> {
39 let mut records: Vec<&WorkRecord> = self.work_records.values().collect();
40 records.sort_by_key(|r| r.start);
41 records
42 }
43
44 pub fn get_grouped_totals(&self) -> Vec<(String, u32)> {
45 let mut totals: HashMap<String, u32> = HashMap::new();
46
47 for record in self.work_records.values() {
48 *totals.entry(record.name.clone()).or_insert(0) += record.total_minutes;
49 }
50
51 let mut result: Vec<(String, u32)> = totals.into_iter().collect();
52 result.sort_by(|a, b| b.1.cmp(&a.1));
53 result
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60 use crate::models::TimePoint;
61
62 fn create_test_date() -> Date {
63 Date::from_calendar_date(2025, time::Month::November, 6).unwrap()
64 }
65
66 fn create_test_record(id: u32, name: &str, start_hour: u8, end_hour: u8) -> WorkRecord {
67 let start = TimePoint::new(start_hour, 0).unwrap();
68 let end = TimePoint::new(end_hour, 0).unwrap();
69 WorkRecord::new(id, name.to_string(), start, end)
70 }
71
72 #[test]
73 fn test_new_day_data() {
74 let date = create_test_date();
75 let day = DayData::new(date);
76
77 assert_eq!(day.date, date);
78 assert_eq!(day.last_id, 0);
79 assert_eq!(day.work_records.len(), 0);
80 }
81
82 #[test]
83 fn test_add_record() {
84 let mut day = DayData::new(create_test_date());
85 let record = create_test_record(1, "Coding", 9, 17);
86
87 day.add_record(record.clone());
88
89 assert_eq!(day.work_records.len(), 1);
90 assert_eq!(day.last_id, 1);
91 assert!(day.work_records.contains_key(&1));
92 }
93
94 #[test]
95 fn test_add_multiple_records() {
96 let mut day = DayData::new(create_test_date());
97
98 day.add_record(create_test_record(1, "Coding", 9, 12));
99 day.add_record(create_test_record(2, "Meeting", 13, 14));
100 day.add_record(create_test_record(3, "Code Review", 14, 16));
101
102 assert_eq!(day.work_records.len(), 3);
103 assert_eq!(day.last_id, 3);
104 }
105
106 #[test]
107 fn test_add_record_updates_last_id() {
108 let mut day = DayData::new(create_test_date());
109
110 day.add_record(create_test_record(5, "Task", 9, 10));
111 assert_eq!(day.last_id, 5);
112
113 day.add_record(create_test_record(2, "Task2", 10, 11));
114 assert_eq!(day.last_id, 5); day.add_record(create_test_record(10, "Task3", 11, 12));
117 assert_eq!(day.last_id, 10);
118 }
119
120 #[test]
121 fn test_remove_record() {
122 let mut day = DayData::new(create_test_date());
123 day.add_record(create_test_record(1, "Coding", 9, 17));
124
125 let removed = day.remove_record(1);
126 assert!(removed.is_some());
127 assert_eq!(removed.unwrap().name, "Coding");
128 assert_eq!(day.work_records.len(), 0);
129 }
130
131 #[test]
132 fn test_remove_nonexistent_record() {
133 let mut day = DayData::new(create_test_date());
134 day.add_record(create_test_record(1, "Coding", 9, 17));
135
136 let removed = day.remove_record(999);
137 assert!(removed.is_none());
138 assert_eq!(day.work_records.len(), 1);
139 }
140
141 #[test]
142 fn test_next_id() {
143 let mut day = DayData::new(create_test_date());
144
145 assert_eq!(day.next_id(), 1);
146 assert_eq!(day.next_id(), 2);
147 assert_eq!(day.next_id(), 3);
148 assert_eq!(day.last_id, 3);
149 }
150
151 #[test]
152 fn test_next_id_after_add_record() {
153 let mut day = DayData::new(create_test_date());
154 day.add_record(create_test_record(5, "Task", 9, 10));
155
156 assert_eq!(day.last_id, 5);
157 assert_eq!(day.next_id(), 6);
158 assert_eq!(day.next_id(), 7);
159 }
160
161 #[test]
162 fn test_get_sorted_records_empty() {
163 let day = DayData::new(create_test_date());
164 let sorted = day.get_sorted_records();
165 assert_eq!(sorted.len(), 0);
166 }
167
168 #[test]
169 fn test_get_sorted_records_single() {
170 let mut day = DayData::new(create_test_date());
171 day.add_record(create_test_record(1, "Coding", 9, 17));
172
173 let sorted = day.get_sorted_records();
174 assert_eq!(sorted.len(), 1);
175 assert_eq!(sorted[0].name, "Coding");
176 }
177
178 #[test]
179 fn test_get_sorted_records_already_sorted() {
180 let mut day = DayData::new(create_test_date());
181 day.add_record(create_test_record(1, "Morning", 9, 12));
182 day.add_record(create_test_record(2, "Afternoon", 13, 17));
183
184 let sorted = day.get_sorted_records();
185 assert_eq!(sorted.len(), 2);
186 assert_eq!(sorted[0].name, "Morning");
187 assert_eq!(sorted[1].name, "Afternoon");
188 }
189
190 #[test]
191 fn test_get_sorted_records_unsorted() {
192 let mut day = DayData::new(create_test_date());
193 day.add_record(create_test_record(1, "Afternoon", 13, 17));
194 day.add_record(create_test_record(2, "Morning", 9, 12));
195 day.add_record(create_test_record(3, "Evening", 18, 20));
196
197 let sorted = day.get_sorted_records();
198 assert_eq!(sorted.len(), 3);
199 assert_eq!(sorted[0].name, "Morning");
200 assert_eq!(sorted[1].name, "Afternoon");
201 assert_eq!(sorted[2].name, "Evening");
202 }
203
204 #[test]
205 fn test_get_sorted_records_same_start_time() {
206 let mut day = DayData::new(create_test_date());
207 let start = TimePoint::new(9, 0).unwrap();
208 let end1 = TimePoint::new(10, 0).unwrap();
209 let end2 = TimePoint::new(11, 0).unwrap();
210
211 day.add_record(WorkRecord::new(1, "Task1".to_string(), start, end1));
212 day.add_record(WorkRecord::new(2, "Task2".to_string(), start, end2));
213
214 let sorted = day.get_sorted_records();
215 assert_eq!(sorted.len(), 2);
216 assert!(sorted.iter().any(|r| r.name == "Task1"));
218 assert!(sorted.iter().any(|r| r.name == "Task2"));
219 }
220
221 #[test]
222 fn test_get_grouped_totals_empty() {
223 let day = DayData::new(create_test_date());
224 let totals = day.get_grouped_totals();
225 assert_eq!(totals.len(), 0);
226 }
227
228 #[test]
229 fn test_get_grouped_totals_single_task() {
230 let mut day = DayData::new(create_test_date());
231 day.add_record(create_test_record(1, "Coding", 9, 17)); let totals = day.get_grouped_totals();
234 assert_eq!(totals.len(), 1);
235 assert_eq!(totals[0].0, "Coding");
236 assert_eq!(totals[0].1, 480); }
238
239 #[test]
240 fn test_get_grouped_totals_multiple_different_tasks() {
241 let mut day = DayData::new(create_test_date());
242 day.add_record(create_test_record(1, "Coding", 9, 12)); day.add_record(create_test_record(2, "Meeting", 13, 14)); day.add_record(create_test_record(3, "Code Review", 14, 16)); let totals = day.get_grouped_totals();
247 assert_eq!(totals.len(), 3);
248
249 assert_eq!(totals[0].0, "Coding");
251 assert_eq!(totals[0].1, 180);
252 assert_eq!(totals[1].0, "Code Review");
253 assert_eq!(totals[1].1, 120);
254 assert_eq!(totals[2].0, "Meeting");
255 assert_eq!(totals[2].1, 60);
256 }
257
258 #[test]
259 fn test_get_grouped_totals_same_task_multiple_times() {
260 let mut day = DayData::new(create_test_date());
261 day.add_record(create_test_record(1, "Coding", 9, 11)); day.add_record(create_test_record(2, "Meeting", 11, 12)); day.add_record(create_test_record(3, "Coding", 13, 16)); day.add_record(create_test_record(4, "Coding", 16, 17)); let totals = day.get_grouped_totals();
267 assert_eq!(totals.len(), 2);
268
269 assert_eq!(totals[0].0, "Coding");
271 assert_eq!(totals[0].1, 360);
272 assert_eq!(totals[1].0, "Meeting");
273 assert_eq!(totals[1].1, 60);
274 }
275
276 #[test]
277 fn test_get_grouped_totals_sorted_by_duration() {
278 let mut day = DayData::new(create_test_date());
279 day.add_record(create_test_record(1, "Short", 9, 10)); day.add_record(create_test_record(2, "Long", 10, 15)); day.add_record(create_test_record(3, "Medium", 15, 17)); let totals = day.get_grouped_totals();
284
285 assert_eq!(totals[0].0, "Long");
287 assert_eq!(totals[1].0, "Medium");
288 assert_eq!(totals[2].0, "Short");
289 }
290
291 #[test]
292 fn test_clone() {
293 let mut day1 = DayData::new(create_test_date());
294 day1.add_record(create_test_record(1, "Coding", 9, 17));
295
296 let day2 = day1.clone();
297
298 assert_eq!(day1.date, day2.date);
299 assert_eq!(day1.last_id, day2.last_id);
300 assert_eq!(day1.work_records.len(), day2.work_records.len());
301 }
302}