1use crate::calendar::Calendar;
11use crate::config;
12use crate::db_sqlite;
13use crate::language::Language;
14use crate::models::*;
15use crate::ordering::Ordering;
16use crate::ordering::Result;
17use crate::prelude::Result as AppResult;
18use crate::today;
19use crate::week_info::WeekInfo;
20use crate::weekdays::WeekDaysUnixOffset;
21use crate::weekdays::SEVEN_DAY_WEEK_SIZE;
22use serde::Serialize;
23
24#[derive(Debug, Clone, Default)]
25pub struct Week {
26 pub reference_day: i32,
27 pub start_day: i32,
28 pub middle_day: i32,
29 pub end_day: i32,
30 pub items: Vec<Item>,
31 pub week_view: WeekView,
33}
34
35#[derive(Debug, Serialize, Clone, Default)]
36pub struct WeekView {
37 pub week_info_main: WeekInfo,
38 pub week_info_aux: Option<WeekInfo>,
39 pub items: Vec<ItemView>,
40}
41
42impl Week {
43 pub fn new() -> Self {
44 let mut week = Week::default();
45 let _ = week.current();
46 week
47 }
48
49 fn calculate_week_start_middle_end_unix_day(
56 unix_day: i32,
57 day_offset: i32,
58 week_size: i32,
59 ) -> (i32, i32, i32) {
60 let start = ((unix_day - day_offset) / week_size) * week_size + day_offset;
61 let middle =
62 ((unix_day - day_offset) / week_size) * week_size + day_offset + (week_size / 2);
63 let end = ((unix_day - day_offset) / week_size) * week_size + day_offset + week_size - 1;
64 (start, middle, end)
65 }
66
67 pub fn update(&mut self) -> AppResult<()> {
68 let start_week_day: WeekDaysUnixOffset =
70 config::get_config().main_calendar_start_weekday.into();
71 let start_week_day_offset: i32 = start_week_day as i32;
72 let (start_day, middle_day, end_day) = Self::calculate_week_start_middle_end_unix_day(
73 self.reference_day,
74 start_week_day_offset,
75 SEVEN_DAY_WEEK_SIZE,
76 );
77 self.start_day = start_day;
78 self.middle_day = middle_day;
79 self.end_day = end_day;
80
81 let items = db_sqlite::read_items_between_days(self.start_day, self.end_day, true)?;
83 self.items = items;
85 self.check_and_fix_ordering();
86
87 let today = today::get_unix_day();
89 let main_cal: Calendar = config::get_config().main_calendar_type.into();
90 let main_cal_lang = config::get_config().main_calendar_language.into();
91 self.week_view.week_info_main = WeekInfo::from_unix_start_end_days(
92 self.start_day,
93 self.end_day,
94 today,
95 main_cal,
96 main_cal_lang,
97 )?;
98 let aux_cal: Option<Calendar> = config::get_config()
99 .secondary_calendar_type
100 .map(|s| s.into());
101 self.week_view.week_info_aux = aux_cal.map(|cal| {
102 let aux_language: Language = config::get_config()
103 .secondary_calendar_language
104 .unwrap_or_default()
105 .into();
106 WeekInfo::from_unix_start_end_days(
107 self.start_day,
108 self.end_day,
109 today,
110 cal,
111 aux_language,
112 )
113 .unwrap_or_default()
114 });
115 self.week_view.items = self.items.iter().map(ItemView::from).collect();
116 Ok(())
117 }
118
119 pub fn get_view(&self) -> WeekView {
120 self.week_view.clone()
121 }
122
123 #[allow(clippy::should_implement_trait)]
124 pub fn next(&mut self) -> AppResult<()> {
125 self.reference_day += SEVEN_DAY_WEEK_SIZE;
126 self.update()
127 }
128
129 pub fn previous(&mut self) -> AppResult<()> {
130 self.reference_day -= SEVEN_DAY_WEEK_SIZE;
131 self.update()
132 }
133
134 pub fn current(&mut self) -> AppResult<()> {
135 self.reference_day = today::get_unix_day();
136 self.update()
141 }
142
143 pub fn add_new_item(
144 &mut self,
145 kind: i32,
146 text: String,
147 after_id: Option<i32>,
148 ) -> AppResult<i32> {
149 let main_cal: Calendar = config::get_config().main_calendar_type.into();
150 let calendar: i32 = main_cal.into();
151 let ordering_key: String = self.get_new_ordering_key(after_id);
152 let new_item = NewItem::new(
153 calendar,
154 None, None, None, self.middle_day,
158 kind,
159 text,
160 ordering_key,
161 );
162 db_sqlite::create_item(&new_item)
163 }
164
165 pub fn move_item_to_other_time_period_offset(&mut self, id: i32, offset: i32) -> Result<usize> {
166 if let Some(pos) = self.items.iter().position(|item| item.id == id) {
167 let mut item = self.items[pos].clone();
168 item.day += SEVEN_DAY_WEEK_SIZE * offset;
169 item.order_in_week = None;
170 let result = db_sqlite::update_item(&item);
171 let _ = self.update();
172 result
173 } else {
174 let _ = self.update();
175 Err("id not in list!".into())
176 }
177 }
178}
179
180impl Ordering for Week {
181 fn get_keys(&self) -> Vec<Option<String>> {
182 self.items.iter().map(|i| i.order_in_week.clone()).collect()
183 }
184
185 fn set_ordering_key_of_posision(&mut self, i: usize, key: Option<String>) -> Result<()> {
190 self.items
191 .get_mut(i)
192 .ok_or("invalid pos".to_string())?
193 .order_in_week = key;
194 Ok(())
195 }
196
197 fn get_ordering_key_of_id(&self, id: i32) -> Option<Option<String>> {
202 let pos = self.items.iter().position(|item| item.id == id)?;
203 Some(self.items.get(pos).unwrap().order_in_week.clone())
204 }
205
206 fn new_ordering_finished(&self) {
207 let _ = db_sqlite::update_items(&self.items);
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use crate::time;
214 use crate::week::Week;
215 use crate::weekdays::{WeekDaysUnixOffset, SEVEN_DAY_WEEK_SIZE};
216 use chrono::{DateTime, Local};
217
218 #[test]
219 fn test_week_middle_day_ref() {
220 let gregorian_dates_and_reference = vec![
221 ("2024-07-12 23:22:11 +03:30", 19913),
223 ("2024-07-12 23:59:36 +03:30", 19913),
224 ("2024-07-12 23:59:59 +03:30", 19913),
225 ("2024-07-13 00:00:00 +03:30", 19920),
227 ("2024-07-13 00:00:01 +03:30", 19920),
228 ("2024-07-13 00:00:11 +03:30", 19920),
229 ("2024-07-13 01:01:01 +03:30", 19920),
230 ("2024-07-13 12:00:00 +03:30", 19920),
231 ("2024-07-14 12:00:00 +03:30", 19920),
232 ("2024-07-15 00:00:00 +03:30", 19920),
233 ("2024-07-16 23:00:00 +03:30", 19920),
234 ("2024-07-17 23:23:00 +03:30", 19920),
235 ("2024-07-18 23:23:23 +03:30", 19920),
236 ("2024-07-18 23:59:23 +03:30", 19920),
237 ("2024-07-19 00:00:00 +03:30", 19920),
238 ("2024-07-19 02:00:00 +03:30", 19920),
239 ("2024-07-19 05:00:00 +03:30", 19920),
240 ("2024-07-19 18:00:00 +03:30", 19920),
241 ("2024-07-19 23:00:00 +03:30", 19920),
242 ("2024-07-19 23:59:59 +03:30", 19920),
243 ("2024-07-20 00:00:00 +03:30", 19927),
245 ("2024-07-20 00:00:01 +03:30", 19927),
246 ("2024-07-20 00:01:01 +03:30", 19927),
247 ("2024-07-20 01:00:01 +03:30", 19927),
248 ("2024-07-20 03:00:00 +03:30", 19927),
249 ("2024-07-20 04:00:00 +03:30", 19927),
250 ];
251 assert!(check_gregorian_dates_and_reference(
252 gregorian_dates_and_reference
253 ));
254 }
255
256 fn check_gregorian_dates_and_reference(dates_and_ref: Vec<(&str, i32)>) -> bool {
257 println!("----");
258 for (date_string, expected_middle_day) in dates_and_ref {
259 let dt = date_string.parse::<DateTime<Local>>().unwrap();
260 let unix_day = time::get_unix_day_from_local_datetime(dt);
261 let (s, m, e) = Week::calculate_week_start_middle_end_unix_day(
262 unix_day,
263 WeekDaysUnixOffset::Sat as i32,
264 SEVEN_DAY_WEEK_SIZE,
265 );
266 println!(
267 "date: {}, start_day: {}, middle_day: {}, end_day: {}",
268 dt, s, m, e
269 );
270 if expected_middle_day != m {
271 return false;
272 }
273 }
274 true
275 }
276}