From f788915ea15cd6ed8d793e6ab68e7c7a47e5a1eb Mon Sep 17 00:00:00 2001 From: Jarrett Ye Date: Sun, 20 Jul 2025 13:56:56 +0800 Subject: [PATCH] support deck-specific desired retention when rescheduling --- rslib/src/deckconfig/update.rs | 8 ++++++-- rslib/src/scheduler/fsrs/memory_state.rs | 15 +++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/rslib/src/deckconfig/update.rs b/rslib/src/deckconfig/update.rs index bcbbd59bf..0bd549a20 100644 --- a/rslib/src/deckconfig/update.rs +++ b/rslib/src/deckconfig/update.rs @@ -212,10 +212,13 @@ impl Collection { if fsrs_toggled { self.set_config_bool_inner(BoolKey::Fsrs, req.fsrs)?; } + let mut deck_desired_retention: HashMap = Default::default(); for deck in self.storage.get_all_decks()? { if let Ok(normal) = deck.normal() { let deck_id = deck.id; - + if let Some(desired_retention) = normal.desired_retention { + deck_desired_retention.insert(deck_id, desired_retention); + } // previous order & params let previous_config_id = DeckConfigId(normal.config_id); let previous_config = configs_before_update.get(&previous_config_id); @@ -277,10 +280,11 @@ impl Collection { if req.fsrs { Some(UpdateMemoryStateRequest { params: c.fsrs_params().clone(), - desired_retention: c.inner.desired_retention, + preset_desired_retention: c.inner.desired_retention, max_interval: c.inner.maximum_review_interval, reschedule: req.fsrs_reschedule, historical_retention: c.inner.historical_retention, + deck_desired_retention: deck_desired_retention.clone(), }) } else { None diff --git a/rslib/src/scheduler/fsrs/memory_state.rs b/rslib/src/scheduler/fsrs/memory_state.rs index 3429ccf67..a61e737b5 100644 --- a/rslib/src/scheduler/fsrs/memory_state.rs +++ b/rslib/src/scheduler/fsrs/memory_state.rs @@ -45,10 +45,11 @@ pub(crate) fn get_decay_from_params(params: &[f32]) -> f32 { #[derive(Debug)] pub(crate) struct UpdateMemoryStateRequest { pub params: Params, - pub desired_retention: f32, + pub preset_desired_retention: f32, pub historical_retention: f32, pub max_interval: u32, pub reschedule: bool, + pub deck_desired_retention: HashMap, } pub(crate) struct UpdateMemoryStateEntry { @@ -98,7 +99,8 @@ impl Collection { historical_retention.unwrap_or(0.9), ignore_before, )?; - let desired_retention = req.as_ref().map(|w| w.desired_retention); + let preset_desired_retention = + req.as_ref().map(|w| w.preset_desired_retention).unwrap(); let mut progress = self.new_progress_handler::(); progress.update(false, |s| s.total_cards = items.len() as u32)?; for (idx, (card_id, item)) in items.into_iter().enumerate() { @@ -109,7 +111,12 @@ impl Collection { // Store decay and desired retention in the card so that add-ons, card info, // stats and browser search/sorts don't need to access the deck config. // Unlike memory states, scheduler doesn't use decay and dr stored in the card. - card.desired_retention = desired_retention; + let deck_id = card.original_or_current_deck_id(); + let desired_retention = *req + .deck_desired_retention + .get(&deck_id) + .unwrap_or(&preset_desired_retention); + card.desired_retention = Some(desired_retention); card.decay = decay; if let Some(item) = item { card.set_memory_state(&fsrs, Some(item), historical_retention.unwrap())?; @@ -132,7 +139,7 @@ impl Collection { let original_interval = card.interval; let interval = fsrs.next_interval( Some(state.stability), - desired_retention.unwrap(), + desired_retention, 0, ); card.interval = rescheduler