From 66d0046b8a847df3b460207ed4ed52964da44063 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Mon, 8 Jan 2024 14:05:09 +1000 Subject: [PATCH] Check review count up front https://forums.ankiweb.net/t/inconsistent-number-of-reviews-when-clicking-optimize/39275/7 --- ftl/core/deck-config.ftl | 2 +- rslib/src/backend/error.rs | 1 + rslib/src/error/mod.rs | 8 ++++++++ rslib/src/scheduler/fsrs/retention.rs | 5 +++++ rslib/src/scheduler/fsrs/weights.rs | 10 ++++++++++ 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/ftl/core/deck-config.ftl b/ftl/core/deck-config.ftl index d5530d401..10120d905 100644 --- a/ftl/core/deck-config.ftl +++ b/ftl/core/deck-config.ftl @@ -339,7 +339,7 @@ deck-config-must-have-1000-reviews = { $count -> [one] Only { $count } review was found. *[other] Only { $count } reviews were found. - } You must have at least 1000 reviews to generate custom parameters. + } You must have at least 1000 reviews for this operation. # Numbers that control how aggressively the FSRS algorithm schedules cards deck-config-weights = FSRS parameters deck-config-compute-optimal-weights = Optimize FSRS parameters diff --git a/rslib/src/backend/error.rs b/rslib/src/backend/error.rs index da7addf51..d1671f074 100644 --- a/rslib/src/backend/error.rs +++ b/rslib/src/backend/error.rs @@ -48,6 +48,7 @@ impl AnkiError { #[cfg(windows)] AnkiError::WindowsError { .. } => Kind::OsError, AnkiError::SchedulerUpgradeRequired => Kind::SchedulerUpgradeRequired, + AnkiError::FsrsInsufficientReviews { .. } => Kind::InvalidInput, }; anki_proto::backend::BackendError { diff --git a/rslib/src/error/mod.rs b/rslib/src/error/mod.rs index 47ad88fbe..77c6b164e 100644 --- a/rslib/src/error/mod.rs +++ b/rslib/src/error/mod.rs @@ -114,7 +114,12 @@ pub enum AnkiError { InvalidMethodIndex, InvalidServiceIndex, FsrsWeightsInvalid, + // Returned by fsrs-rs; may happen even if 1000+ reviews FsrsInsufficientData, + // Generated by our backend if count < 1000 + FsrsInsufficientReviews { + count: usize, + }, FsrsUnableToDetermineDesiredRetention, SchedulerUpgradeRequired, } @@ -169,6 +174,9 @@ impl AnkiError { AnkiError::InvalidInput { source } => source.message(), AnkiError::NotFound { source } => source.message(tr), AnkiError::FsrsInsufficientData => tr.deck_config_not_enough_history().into(), + AnkiError::FsrsInsufficientReviews { count } => { + tr.deck_config_must_have_1000_reviews(*count).into() + } AnkiError::FsrsWeightsInvalid => tr.deck_config_invalid_weights().into(), AnkiError::SchedulerUpgradeRequired => { tr.scheduling_update_required().replace("V2", "v3") diff --git a/rslib/src/scheduler/fsrs/retention.rs b/rslib/src/scheduler/fsrs/retention.rs index 509008605..375e5870b 100644 --- a/rslib/src/scheduler/fsrs/retention.rs +++ b/rslib/src/scheduler/fsrs/retention.rs @@ -75,6 +75,11 @@ impl Collection { .col .storage .get_revlog_entries_for_searched_cards_in_card_order()?; + if revlogs.len() < 1000 { + return Err(AnkiError::FsrsInsufficientReviews { + count: revlogs.len(), + }); + } let first_rating_count = revlogs .iter() diff --git a/rslib/src/scheduler/fsrs/weights.rs b/rslib/src/scheduler/fsrs/weights.rs index dfaa007b4..e8049225b 100644 --- a/rslib/src/scheduler/fsrs/weights.rs +++ b/rslib/src/scheduler/fsrs/weights.rs @@ -39,6 +39,11 @@ impl Collection { let mut anki_progress = self.new_progress_handler::(); let timing = self.timing_today()?; let revlogs = self.revlog_for_srs(search)?; + if revlogs.len() < 1000 { + return Err(AnkiError::FsrsInsufficientReviews { + count: revlogs.len(), + }); + } let items = fsrs_items_for_training(revlogs, timing.next_day_at); let fsrs_items = items.len() as u32; anki_progress.update(false, |p| { @@ -118,6 +123,11 @@ impl Collection { .col .storage .get_revlog_entries_for_searched_cards_in_card_order()?; + if revlogs.len() < 1000 { + return Err(AnkiError::FsrsInsufficientReviews { + count: revlogs.len(), + }); + } anki_progress.state.fsrs_items = revlogs.len() as u32; let items = fsrs_items_for_training(revlogs, timing.next_day_at); let fsrs = FSRS::new(Some(weights))?;