diff --git a/src/core/deck.rs b/src/core/deck.rs index fc7976b..11687fc 100644 --- a/src/core/deck.rs +++ b/src/core/deck.rs @@ -138,7 +138,7 @@ impl DeckData { history.create_now(HistoryNote { id: Default::default(), timestamp: Utc::now(), - set_id: self.settings.id, + deck_id: self.settings.id, word_id, answer: status.clone(), before: old_score, diff --git a/src/data/entities.rs b/src/data/entities.rs index 7731d08..6c57a9a 100644 --- a/src/data/entities.rs +++ b/src/data/entities.rs @@ -1,6 +1,6 @@ use crate::core::srs::{FADE_PER_DAY, MAX_SCORE, WordOpenMode}; use crate::data::{INVALID_ID, Id}; -use chrono::{DateTime, NaiveDate, Utc}; +use chrono::{DateTime, Local, NaiveDate, Utc}; use fsrs::MemoryState; use litemap::LiteMap; use log::info; @@ -8,7 +8,9 @@ use rhai::Engine; use std::cmp::Ordering; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; +use std::sync::Arc; use std::time::Instant; +use crate::data_provider::history::{HistoryRepository, HistoryRepositoryInner}; #[derive(Clone, Debug)] pub struct WordData { @@ -63,7 +65,7 @@ impl WordData { pub struct HistoryNote { pub id: Id, pub timestamp: DateTime, - pub set_id: Id, + pub deck_id: Id, pub word_id: Id, pub answer: WordOpenMode, pub before: u8, @@ -81,6 +83,30 @@ impl EntityData for HistoryNote { } } +impl HistoryRepositoryInner { + pub fn activity_of_deck(&self, id: Id) -> Vec<(NaiveDate, u32)> { + let mut query = self.query(Arc::new(move |note| note.deck_id == id)); + let values = query.iter(&self).collect::>(); + let mut counts = vec![]; + if !values.is_empty() { + let mut date = values[0].timestamp.with_timezone(&Local).date_naive(); + let mut count = 0; + for note in values { + let current_date = note.timestamp.with_timezone(&Local).date_naive(); + if date == current_date{ + count += 1; + } + else { + counts.push((date, count)); + date = current_date; + count = 0; + } + } + } + counts + } +} + #[derive(Clone)] pub struct DeckSettings { pub id: Id, diff --git a/src/data_provider/history.rs b/src/data_provider/history.rs index f01c2e1..00aee94 100644 --- a/src/data_provider/history.rs +++ b/src/data_provider/history.rs @@ -32,7 +32,7 @@ impl TableAccessQuerySet for HistoryQuerySet { Ok(HistoryNote { id: row.get(0)?, timestamp: row.get(3)?, - set_id: row.get(1)?, + deck_id: row.get(1)?, word_id: row.get(2)?, answer: row.get(4)?, before: row.get(5)?, @@ -43,7 +43,7 @@ impl TableAccessQuerySet for HistoryQuerySet { fn insert_params(entity: &HistoryNote) -> impl Params { ( - entity.set_id, + entity.deck_id, entity.word_id, entity.timestamp, entity.answer, @@ -55,7 +55,7 @@ impl TableAccessQuerySet for HistoryQuerySet { fn update_params(entity: &HistoryNote) -> impl Params { ( - entity.set_id, + entity.deck_id, entity.word_id, entity.timestamp, entity.answer, diff --git a/src/pages/history.rs b/src/pages/history.rs index dbb2382..7665efe 100644 --- a/src/pages/history.rs +++ b/src/pages/history.rs @@ -51,7 +51,7 @@ impl HistoryState { let history_repo = &mut state.history.write(); history_repo.init().expect("TODO: panic message"); let id = id.clone(); - let mut history = history_repo.query(Arc::new(move |h| h.set_id == id)); + let mut history = history_repo.query(Arc::new(move |h| h.deck_id == id)); let words = history .iter(history_repo) .map( diff --git a/src/pages/repetitions.rs b/src/pages/repetitions.rs index 37a3c20..746e736 100644 --- a/src/pages/repetitions.rs +++ b/src/pages/repetitions.rs @@ -2,11 +2,10 @@ use crate::app::AppState; use crate::core::deck::DeckData; use crate::core::srs::{AppendMode, OrderMode}; use crate::data::entities::{CardState, DeckSettings, EntityHandle, WordData}; -use crate::data::repositories::Repository; -use crate::data_provider::card_states::add_stat_list; +use crate::data::repositories::{BatchedRepository, Repository}; use crate::navigation::Page::{History, PreviousPage, Repetition, RepetitionSettings}; use crate::navigation::{NavigatedPage, Page, RootMessage}; -use crate::notification::{MaybeError, NotificationRequest}; +use crate::notification::MaybeError; use crate::pages::history::HistoryState; use crate::pages::repetition::RepetitionState; use crate::pages::repetition_settings::RepetitionSettingsState; @@ -15,7 +14,7 @@ use crate::push_error; use crate::push_error_task; use crate::styling::*; use chrono::{Days, Local, Utc}; -use hashbrown::HashSet; +use hashbrown::{HashMap, HashSet}; pub use iced::widget::button::{Catalog, Style}; use iced::widget::button::{Status, danger}; use iced::widget::tooltip::Position::Top; @@ -33,7 +32,7 @@ use std::sync::Arc; #[derive(Clone)] pub struct RepetitionsState { selected_deck_handle: Option>, - decks: Vec, + decks: HashMap, DeckViewData>, state: AppState, } @@ -82,12 +81,7 @@ impl NavigatedPage for RepetitionsState { if let Some(index) = self.selected_deck_handle { let decks = self.state.decks.read(); if let None = decks.get_static(index) { - let position = self - .decks - .iter() - .position(|deck_view_data: &DeckViewData| deck_view_data.id == index.id) - .unwrap(); - self.decks.remove(position); + self.decks.remove(&index); drop(decks); self.clear_selection(); } @@ -105,33 +99,32 @@ impl NavigatedPage for RepetitionsState { CreateSet => { let index = self.decks.len() + 1; - self.decks.push(DeckViewData { - general_settings: DeckSettings::with_name(format!("Колода #{}", index)), - existing_words_indices: Default::default(), - available_words_indices: Default::default(), - indices_ready: false, - append_mode: Default::default(), - count: None, - open_mode: OrderMode::Default, - }); + let general = DeckSettings::with_name(format!("Колода #{}", index)); + let handle = self.state.decks.write().create(general.clone()); + self.decks.insert( + handle, + DeckViewData { + settings_handle: handle, + general_settings: general, + existing_words_indices: Default::default(), + available_words_indices: Default::default(), + indices_ready: false, + append_mode: Default::default(), + count: None, + open_mode: OrderMode::Default, + }, + ); } DeleteSet => { let index = self.selected_deck_handle.unwrap(); self.clear_selection(); - let deck = self.decks.remove( - self.decks - .iter() - .position(|dvd| dvd.id == index.id) - .unwrap(), - ); + let deck = self.decks.remove(&index); + if index.real() { let mut repo = self.state.decks.write(); if let Err(err) = repo.delete(index) { return push_error_task!(err); } - - let index = self.decks.iter().position(|dvd| dvd.id == index.id); - self.decks.remove(index.unwrap()); } } SetName(value) => { @@ -148,21 +141,14 @@ impl NavigatedPage for RepetitionsState { } } Save => { - let mut deck = self.selected_deck().unwrap().clone(); + let deck = self.selected_deck().unwrap().clone(); let handle = self.selected_deck_handle.unwrap(); let mut repo = self.state.decks.write(); - repo.set_static(handle, *deck); + repo.set_static(handle, deck.general_settings.clone()); if let Err(err) = repo.save(&mut handle.clone()) { return push_error_task!(err); } - if let Some(index) = deck.index { - state.decks[index] = deck.general_settings.clone(); - } else { - state.decks.push(deck.general_settings.clone()); - deck.index = Some(state.decks.len() - 1); - self.decks[self.selected_deck_handle.unwrap()] = deck.clone(); - } } SetForward(value) => { let deck = self.selected_deck_mut().unwrap(); @@ -213,10 +199,10 @@ impl NavigatedPage for RepetitionsState { deck.existing_words_indices.append(&mut adding.clone()); } - let deck = self.selected_deck().unwrap(); - let mut state = self.state.lock(); + let deck = self.selected_deck().unwrap().clone(); + let state = &mut self.state; if let Err(err) = - Self::append_words(&mut state, &deck.general_settings, adding.into_iter()) + Self::append_words(state, &deck.general_settings, adding.into_iter()) { return push_error_task!(err); } @@ -259,23 +245,28 @@ impl RepetitionsState { } } - fn get_decks_from_state(state: &AppState) -> Vec { + fn get_decks_from_state(state: &AppState) -> HashMap, DeckViewData> { state .decks .read() .iter() .enumerate() - .map(|(index, deck)| DeckViewData { - general_settings: deck.clone(), - existing_words_indices: Default::default(), - available_words_indices: Default::default(), - indices_ready: false, - append_mode: Default::default(), - count: None, - index: Some(index), - open_mode: OrderMode::Default, + .map(|(index, deck)| { + ( + EntityHandle::new(deck.id, index, true), + DeckViewData { + settings_handle: EntityHandle::new(deck.id, index, true), + general_settings: deck.clone(), + existing_words_indices: Default::default(), + available_words_indices: Default::default(), + indices_ready: false, + append_mode: Default::default(), + count: None, + open_mode: OrderMode::Default, + }, + ) }) - .collect() + .collect::>() } } @@ -395,22 +386,20 @@ impl RepetitionsState { fn activity_bar(&self, deck: &DeckSettings) -> Element<'_, RepetitionsMessage> { const MAX_DAY_COUNT: f32 = 128.0; - let state = self.state.lock(); - let history = state.activity.get(&deck.id); + let history = self.state.history.read(); + let history = history.activity_of_deck(deck.id); let mut counts: Vec = vec![0; 30 * 7]; let now = Local::now().date_naive(); - if let Some(history) = history { - for (date, activity) in history { - let distance = now - *date; - let index = counts.len() as i64 - distance.num_days() - 1; + for (date, activity) in history { + let distance = now - date; + let index = counts.len() as i64 - distance.num_days() - 1; - if index < 0 { - continue; - } - - counts[index as usize] = *activity; + if index < 0 { + continue; } + + counts[index as usize] = activity; } lazy((counts, now), |(counts, now)| { @@ -509,8 +498,8 @@ impl RepetitionsState { let available = selected_deck.available_words_indices.len(); text!("{} слова добавлено из {}", now, available).into() } - fn count_view(&self, deck: &DeckSettings) -> Element<'_, RepetitionsMessage> { - if let Some(count) = self.decks.iter().find() { + fn count_view(&self, deck: &DeckViewData) -> Element<'_, RepetitionsMessage> { + if let Some(count) = deck.count { return text!("Количество слов: {}", count).into(); } space().into() @@ -518,10 +507,10 @@ impl RepetitionsState { fn decks_list(&self) -> Column<'_, RepetitionsMessage> { let mut column = Column::new(); - for (i, deck) in self.decks.iter().enumerate() { + for (i, (handle, deck)) in self.decks.iter().enumerate() { column = column.push( button(text!("{}", &deck.name)) - .on_press_with(move || SelectSet(i)) + .on_press_with(move || SelectSet(*handle)) .style(move |_x: &Theme, status: Status| Style { background: if status == Status::Hovered { Some(Background::Color(Color::WHITE.scale_alpha(0.2))) @@ -566,38 +555,36 @@ impl RepetitionsState { ) -> MaybeError { let words = &state.dictionary; let stats = &mut indices - .map(|i| { - let word = &words[i]; - CardState { - id: 0.into(), - word_id: word.id, - last_open: Utc::now(), - score: 1, - fsrs_opened: Utc::now().date_naive(), - fsrs_memory: None, - deck_id: deck.id, - fsrs_open_day: Utc::now().date_naive(), - } + .map(|i| CardState { + id: 0.into(), + word_id: i.id, + last_open: Utc::now(), + score: 1, + fsrs_opened: Utc::now().date_naive(), + fsrs_memory: None, + deck_id: deck.id, + fsrs_open_day: Utc::now().date_naive(), }) .collect::>(); if !stats.is_empty() { - add_stat_list(stats, &mut state.connection)?; + let mut repo = state.card_states.write(); + repo.create_now_many(stats.as_mut_slice())?; } Ok(()) } - fn select_deck(&mut self, index: usize) { - if self.decks.get(index).is_some() { + fn select_deck(&mut self, index: EntityHandle) { + if self.decks.contains_key(&index) { self.selected_deck_handle = Some(index); } } fn selected_deck(&self) -> Option<&DeckViewData> { - self.decks.get(self.selected_deck_handle?) + self.decks.get(&self.selected_deck_handle?) } fn selected_deck_mut(&mut self) -> Option<&mut DeckViewData> { - self.decks.get_mut(self.selected_deck_handle?) + self.decks.get_mut(&self.selected_deck_handle?) } fn clear_selection(&mut self) { @@ -613,10 +600,10 @@ impl RepetitionsState { .map(|state| EntityHandle::from_id(state.word_id)) .collect::>(); let mut deck = self.selected_deck().unwrap().clone(); - let total = DeckData::list(&deck, &self.state)?; + let total = DeckData::list(&deck, &self.state); deck.existing_words_indices = added; deck.available_words_indices = total; - self.decks[self.selected_deck_handle.unwrap()] = deck; + self.decks.insert(self.selected_deck_handle.unwrap(), deck); Ok(()) } } @@ -628,7 +615,7 @@ pub enum RepetitionsMessage { CreateSet, DeleteSet, SetName(String), - SelectSet(usize), + SelectSet(EntityHandle), Save, SetForward(String), SetBackward(String), @@ -643,6 +630,7 @@ pub enum RepetitionsMessage { #[derive(Clone)] struct DeckViewData { + settings_handle: EntityHandle, general_settings: DeckSettings, existing_words_indices: Vec>, available_words_indices: Vec>,