diff --git a/src/core/deck.rs b/src/core/deck.rs index b76e277..3bb2cd3 100644 --- a/src/core/deck.rs +++ b/src/core/deck.rs @@ -1,5 +1,5 @@ use crate::app::AppState; -use crate::core::srs::OrderModule::{WorstWordsSRS, FSRS, RandomSRS, SemiRandomSRS}; +use crate::core::srs::OrderModule::{Fsrs, RandomSRS, SemiRandomSRS, WorstWordsSRS}; use crate::core::srs::{ FsrsModule, OrderMode, OrderModule, RandomSRSModule, SRSModule, SemiRandomSRSModule, WordOpenMode, WorstWordsSRSModule, @@ -48,7 +48,7 @@ impl DeckData { words, current_word_index: None, state: state_for, - order_module: FSRS(FsrsModule::new()), + order_module: Fsrs(FsrsModule::new()), open_mode: settings.open_mode, settings: settings.clone(), }) @@ -75,7 +75,7 @@ impl DeckData { } module.next(self.set.as_slice()) } - FSRS(ref mut module) => { + Fsrs(ref mut module) => { if !module.initialized { module.init(self.set.as_slice()) } @@ -103,22 +103,18 @@ impl DeckData { WorstWordsSRS(ref mut module) => { module.open(status, index, word); } - FSRS(ref mut module) => { + Fsrs(ref mut module) => { module.open(status, index, word); } } - if let FSRS(ref fsrs) = self.order_module { - if fsrs.finished() { - self.order_module = match self.open_mode { - OrderMode::Default => { - SemiRandomSRS(SemiRandomSRSModule::new()) - } - OrderMode::TrainWorstFirst => { - WorstWordsSRS(WorstWordsSRSModule::new()) - } - OrderMode::FullRandom => RandomSRS(RandomSRSModule::new()), - } + if let Fsrs(ref fsrs) = self.order_module + && fsrs.finished() + { + self.order_module = match self.open_mode { + OrderMode::Default => SemiRandomSRS(SemiRandomSRSModule::new()), + OrderMode::TrainWorstFirst => WorstWordsSRS(WorstWordsSRSModule::new()), + OrderMode::FullRandom => RandomSRS(RandomSRSModule::new()), } } update_stat_score(word, &self.state.lock().connection)?; diff --git a/src/core/srs.rs b/src/core/srs.rs index dc016ce..7bfd811 100644 --- a/src/core/srs.rs +++ b/src/core/srs.rs @@ -1,10 +1,10 @@ use crate::data::entities::CardStatistics; -use chrono::Local; +use chrono::{Local, TimeDelta}; use fsrs::FSRS; use rand::distr::weighted::WeightedIndex; use rand::prelude::ThreadRng; use rand::prelude::{Distribution, SliceRandom}; -use rand::rng; +use rand::{RngExt, rng}; const MAX_HISTORY_LEN: usize = 20; const MAX_HISTORY_LEN_PART: f32 = 0.33; @@ -28,7 +28,7 @@ pub enum OrderModule { SemiRandomSRS(SemiRandomSRSModule), RandomSRS(RandomSRSModule), WorstWordsSRS(WorstWordsSRSModule), - FSRS(FsrsModule), + Fsrs(FsrsModule), } pub trait SRSModule { fn next(&mut self, set: &[CardStatistics]) -> usize; @@ -183,12 +183,12 @@ pub struct FsrsModule { queue: Vec, rng: ThreadRng, fsrs: FSRS, - pub(crate) initialized: bool + pub(crate) initialized: bool, } impl SRSModule for FsrsModule { - fn next(&mut self, set: &[CardStatistics]) -> usize { - if self.solved.len() == self.current_group.len() { + fn next(&mut self, _: &[CardStatistics]) -> usize { + if self.current_group.is_empty() { self.fill_partial_pool(); } @@ -197,16 +197,39 @@ impl SRSModule for FsrsModule { self.queue.shuffle(&mut self.rng); } - self.queue.pop().expect("empty queue") + self.queue.pop().unwrap_or(0) } fn open(&mut self, status: WordOpenMode, index: usize, updated_word: &mut CardStatistics) { let today = Local::now().date_naive(); let delta = today - updated_word.fsrs_opened; let delta_days = delta.num_days(); - - if updated_word.fsrs_open_day != today{ + + let memory = self + .fsrs + .next_states(updated_word.fsrs_memory, 0.9, delta_days as u32) + .expect("Wrong next stage calculation"); + + let next = match status { + WordOpenMode::Easy => memory.easy, + WordOpenMode::Ok => memory.good, + WordOpenMode::Hard => memory.hard, + WordOpenMode::None => memory.again, + }; + + let mut next_day = today + TimeDelta::days(next.interval.round() as i64); + if (next_day - today).num_days() >= 5 { + let offset = ((rng().random::() - 0.5) * 3.0).round() as i32; + next_day += TimeDelta::days(offset as i64); + } + updated_word.fsrs_memory = Some(next.memory); + updated_word.fsrs_open_day = next_day; + updated_word.fsrs_opened = updated_word.last_open.date_naive(); + + if updated_word.fsrs_open_day != today { self.solved.push(index); + let index = self.current_group.iter().position(|x| *x == index).unwrap(); + self.current_group.remove(index); } } @@ -226,8 +249,8 @@ impl SRSModule for FsrsModule { } impl FsrsModule { - pub fn new() -> Self{ - Self{ + pub fn new() -> Self { + Self { today_set: vec![], current_group: vec![], queue: vec![], @@ -238,21 +261,19 @@ impl FsrsModule { } } fn fill_partial_pool(&mut self) { - self.current_group.clear(); - self.current_group - .extend(self.today_set.iter().take(PARTIAL_POOL_SIZE)); - self.current_group.iter().for_each(|_| { - self.today_set.remove(0); - }); + self.current_group.extend( + self.today_set + .iter() + .filter(|x| !self.solved.contains(*x)) + .take(PARTIAL_POOL_SIZE), + ); + self.queue.clear(); - self.queue - .extend(self.current_group.clone()); - self.solved.clear(); + self.queue.extend(self.current_group.clone()); } fn fill_queue(&mut self) { - self.queue.clear(); - self.queue.extend(self.current_group.iter().filter(|c| !self.today_set.contains(*c))); + self.queue.extend(self.current_group.clone()); } pub fn finished(&self) -> bool { diff --git a/src/data/entities.rs b/src/data/entities.rs index 3448fb8..17b131c 100644 --- a/src/data/entities.rs +++ b/src/data/entities.rs @@ -1,10 +1,9 @@ use crate::app::AppState; use crate::core::srs::{FADE_PER_DAY, MAX_SCORE, OrderMode, WordOpenMode}; use crate::data::{INVALID_ID, Id}; -use chrono::{DateTime, Local, NaiveDate, Utc}; -use fsrs::{FSRS, MemoryState}; +use chrono::{DateTime, NaiveDate, Utc}; +use fsrs::MemoryState; use log::info; -use rand::{RngExt, random, rng}; use rayon::prelude::*; use rhai::{Engine, Scope}; use std::collections::HashMap; @@ -162,29 +161,6 @@ impl CardStatistics { self.last_open = Utc::now(); } - pub fn update_fsrs(&mut self, status: WordOpenMode, by_fsrs: bool, fsrs: FSRS) - { - let today = Local::now().date_naive().to_epoch_days(); - let written = self.fsrs_opened.to_epoch_days(); - let memory = fsrs - .next_states(self.fsrs_memory, 0.9, (today - written) as u32) - .expect("Wrong next stage calculation"); - let next = match status { - WordOpenMode::Easy => memory.easy, - WordOpenMode::Ok => memory.good, - WordOpenMode::Hard => memory.hard, - WordOpenMode::None => memory.again, - }; - - let mut next_day = today + next.interval.round() as i32; - if next_day - today >= 5 { - let offset = ((rng().random::() - 0.5) * 3.0).round() as i32; - next_day += offset; - } - self.fsrs_memory = Some(next.memory); - self.fsrs_open_day = NaiveDate::from_epoch_days(next_day).unwrap(); - self.fsrs_opened = self.last_open.date_naive(); - } pub fn calculated_score(&self) -> f32 { let time = Utc::now() - self.last_open; let days = time.num_days(); diff --git a/src/data_provider/card_states.rs b/src/data_provider/card_states.rs index 83643d7..248e156 100644 --- a/src/data_provider/card_states.rs +++ b/src/data_provider/card_states.rs @@ -4,7 +4,6 @@ use crate::notification::{MaybeError, NotificationData}; use chrono::NaiveDate; use fsrs::MemoryState; use log::info; -use reqwest::get; use rusqlite::{Connection, params}; use std::time::Instant; diff --git a/src/data_provider/decks.rs b/src/data_provider/decks.rs index d85fcea..b320e57 100644 --- a/src/data_provider/decks.rs +++ b/src/data_provider/decks.rs @@ -1,11 +1,7 @@ use crate::core::srs::OrderMode; -use crate::data::Id; use crate::data::entities::DeckSettings; use crate::notification::NotificationData::{DbLoadError, DbSaveError}; use crate::notification::{MaybeError, NotificationData}; -use chrono::{Datelike, Local, NaiveDate}; -use fsrs::Card; -use iced::futures::StreamExt; use rusqlite::Connection; pub fn load_sets(connection: &Connection) -> Result, NotificationData> { diff --git a/src/data_provider/history.rs b/src/data_provider/history.rs index 1c78cd6..c3cd324 100644 --- a/src/data_provider/history.rs +++ b/src/data_provider/history.rs @@ -5,13 +5,16 @@ use crate::fs::*; use crate::notification::NotificationData::PermissionDenied; use crate::notification::{MaybeError, NotificationData}; use chrono::{DateTime, NaiveDate}; +use rusqlite::Connection; use std::fs::{File, OpenOptions}; use std::io::Write; use std::io::{BufRead, BufReader}; use std::path::PathBuf; -use rusqlite::Connection; -pub fn get_open_count_of_set(id: Id, connection: &Connection) -> Result, NotificationData>{ +pub fn get_open_count_of_set( + id: Id, + connection: &Connection, +) -> Result, NotificationData> { todo!() } diff --git a/src/data_provider/mod.rs b/src/data_provider/mod.rs index f7107f4..86660ec 100644 --- a/src/data_provider/mod.rs +++ b/src/data_provider/mod.rs @@ -1,5 +1,5 @@ -pub(crate) mod decks; pub(crate) mod card_states; +pub(crate) mod decks; pub(crate) mod history; pub(crate) mod import; pub(crate) mod settings; diff --git a/src/pages/repetition_settings.rs b/src/pages/repetition_settings.rs index d09b9e2..cf801bb 100644 --- a/src/pages/repetition_settings.rs +++ b/src/pages/repetition_settings.rs @@ -1,7 +1,7 @@ use crate::app::AppState; use crate::data::entities::DeckSettings; -use crate::data_provider::decks::{delete_set, update_deck}; use crate::data_provider::card_states::{delete_stats, load_stats_of_deck}; +use crate::data_provider::decks::{delete_set, update_deck}; use crate::navigation::Page::PreviousPage; use crate::navigation::{NavigatedPage, Page, RootMessage}; use crate::notification::MaybeError; diff --git a/src/pages/repetitions.rs b/src/pages/repetitions.rs index 40223ca..84c4bc5 100644 --- a/src/pages/repetitions.rs +++ b/src/pages/repetitions.rs @@ -2,8 +2,8 @@ use crate::app::AppState; use crate::core::srs::{AppendMode, OrderMode}; use crate::data::Id; use crate::data::entities::{CardStatistics, DeckSettings}; -use crate::data_provider::decks::update_deck; use crate::data_provider::card_states::{add_stat_list, load_stats_of_deck}; +use crate::data_provider::decks::update_deck; use crate::navigation::Page::{History, PreviousPage, Repetition, RepetitionSettings}; use crate::navigation::{NavigatedPage, Page, RootMessage}; use crate::notification::{MaybeError, NotificationRequest};