Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@L-M-Sherlock
Copy link
Contributor

@L-M-Sherlock L-M-Sherlock commented Jul 10, 2025

Source: https://forums.ankiweb.net/t/allow-configuring-dr-for-each-deck-in-a-preset/61103

TODOs:

  • support pre-deck desired retention when rescheduling

Updated the logic for retrieving deck-specific desired retention in both `memory_state.rs` and `mod.rs` to handle cases where the deck's normal state may not be available. This change ensures that the default configuration is used when necessary, improving the robustness of the retention handling.
Updated the logic for effective desired retention to use the configuration default instead of the deck-specific value. This change improves consistency in the retention value used throughout the component, ensuring that the correct value is bound to the UI elements.
Copy link
Member

@dae dae left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor feedback:

// Whether new_today applies to today or a past day.
bool new_today_active = 6;
// Deck-specific desired retention override
optional float desired_retention = 7;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you've modelled this off the old limits code, DR is not really a 'limit', and it probably makes more sense to put in CurrentDeck instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CurrentDeck is not writable in the frontend. It seems to induce a lot of changes if we make it writable? And we should also add a new field to UpdateDeckConfigsRequest and modify the related code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a huge amount of changes, but it's not a tiny amount either, so I'm ok with you leaving things as they are currently.


$: newCardsIgnoreReviewLimit = state.newCardsIgnoreReviewLimit;

// Create tabs for desired retention
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These looks a bit awkward without 'today only', but someone with more UI experience can tweak them later.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe space-evenly/space-around looks a bit better here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to be a bit better, but some people might find the standard limits are now a bit worse?

@L-M-Sherlock
Copy link
Contributor Author

After reading some texts about daily limits in the manual and FAQ, I run into a problem:

The limits of each deck will control the number of cards that are gathered from that deck and its subdecks. Limits are applied from the deck you select, so if you select a child deck, its parents' limits will not apply.

So, the per-deck daily limits would affect subdecks. But the current per-deck desired retention design of this PR doesn't.

After checking the related code, we need something like LimitTreeMap for the per-deck desired retention. But it seems not a minor change.

@dae, any thoughts?

<SpinBox bind:value {min} {max} {step} {percentage} bind:focused />
<RevertButton slot="revert" bind:value {defaultValue} />
</ConfigInput>
<Row class="flex-grow-1">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not something that needs to be done in this PR, but just a note that the SpinBoxFloatRow and SpinBoxRow components are almost identical and can probably be refactored into a single component.


$: newCardsIgnoreReviewLimit = state.newCardsIgnoreReviewLimit;

// Create tabs for desired retention
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe space-evenly/space-around looks a bit better here.

@abdnh
Copy link
Collaborator

abdnh commented Jul 17, 2025

So, the per-deck daily limits would affect subdecks. But the current per-deck desired retention design of this PR doesn't.

After checking the related code, we need something like LimitTreeMap for the per-deck desired retention. But it seems not a minor change.

Something like this maybe?

diff --git a/rslib/src/scheduler/answering/mod.rs b/rslib/src/scheduler/answering/mod.rs
index 53f16c753..e875f245a 100644
--- a/rslib/src/scheduler/answering/mod.rs
+++ b/rslib/src/scheduler/answering/mod.rs
@@ -447,12 +447,20 @@ impl Collection {

         // Get deck-specific desired retention if available, otherwise use config
         // default
+        let config_desired_retention = if let Some(config_id) = self.get_current_deck()?.config_id()
+        {
+            self.get_deck_config(config_id, true)?
+                .unwrap_or_default()
+                .inner
+                .desired_retention
+        } else {
+            config.inner.desired_retention
+        };
         let desired_retention = deck
             .normal()
             .ok()
             .and_then(|d| d.desired_retention)
-            .unwrap_or(config.inner.desired_retention);
-
+            .unwrap_or(config_desired_retention);
         let fsrs_enabled = self.get_config_bool(BoolKey::Fsrs);
         let fsrs_next_states = if fsrs_enabled {
             let params = config.fsrs_params();

@L-M-Sherlock
Copy link
Contributor Author

+        let config_desired_retention = if let Some(config_id) = self.get_current_deck()?.config_id()

Sorry, I don't know what the current_deck means here. What is it different from storage.get_deck(card.deck_id)?

@abdnh
Copy link
Collaborator

abdnh commented Jul 18, 2025

It's different from card.deck_id when the card is in a subdeck of the deck you selected, so the parent deck's desired retention will be used, similar to the case with daily limits.

This code is more correct (the current deck's specific value is checked first):

diff --git a/rslib/src/scheduler/answering/mod.rs b/rslib/src/scheduler/answering/mod.rs
index 53f16c753..9f04436d7 100644
--- a/rslib/src/scheduler/answering/mod.rs
+++ b/rslib/src/scheduler/answering/mod.rs
@@ -447,11 +447,15 @@ impl Collection {

         // Get deck-specific desired retention if available, otherwise use config
         // default
-        let desired_retention = deck
+        let current_deck = self.get_current_deck()?;
+        let current_deck_config = self
+            .get_deck_config(current_deck.config_id().unwrap_or_default(), true)?
+            .unwrap_or_default();
+        let desired_retention = current_deck
             .normal()
             .ok()
             .and_then(|d| d.desired_retention)
-            .unwrap_or(config.inner.desired_retention);
+            .unwrap_or(current_deck_config.inner.desired_retention);

         let fsrs_enabled = self.get_config_bool(BoolKey::Fsrs);
         let fsrs_next_states = if fsrs_enabled {

@dae
Copy link
Member

dae commented Jul 18, 2025

Apologies if it's me here who's misreading things, but I think you guys might be talking about two different things?

  1. Jarrett seems to be asking if we should fall back on a parent deck's setting if the current deck hasn't been customized. I think the answer is ... probably not? This new setting is not really a 'limit', but we put it there for convenience. We only propagate the new/review limits down the tree because each of the limits affects its children, which doesn't apply here.
  2. I think Abdo's comments were more about the filtered deck case? Or was there something else you're referring to Abdo - maybe the deck that the user has selected? Is your thinking that the currently-selected deck should apply the setting to child decks? That seems inconsistent with other scheduling settings, which are drawn from the current card's deck/config.

@abdnh
Copy link
Collaborator

abdnh commented Jul 18, 2025

Is your thinking that the currently-selected deck should apply the setting to child decks? That seems inconsistent with other scheduling settings, which are drawn from the current card's deck/config

I was thinking maybe it should work similar to Display Order settings, which only use the selected deck. But yes, that's inconsistent with most other settings.

@L-M-Sherlock
Copy link
Contributor Author

Let me elaborate it via a hypothetical collection with decks and presets:

Preset A's desired retention = 90%
Preset B's desired retention = 80%

Preset B: Parent: {Card 1}
Preset A: Parent::Child1: {Card 2} (with deck-specific desired retention 70%)
Preset A: Parent::Child1::Grandchild: {Card 3}

When I review Card 2, the possible values of desired retention to use are:

  1. 70% (just respect the deck-specific desired retention of the deck which the card belong to)
  2. 70% or 80% or 90% (depends on which deck I selected)

When I review Card 3, the possible values of desired retention to use are:

  1. 90% (when the deck-specific DR is missing, respect to the preset's DR)
  2. 70% (the deck-specific DR will override all its subdecks' DR)
  3. 70% or 80% or 90% (depends on which deck I selected)

In my opinion, 70% or 80% or 90% is too confusing. And the current implementation is fallback to preset's DR. But I prefer the override version.

@Gardengul
Copy link

Gardengul commented Jul 19, 2025

2. 70% (the deck-specific DR will override all its subdecks' DR)

If someone wants to redefine all the subdecks in the deck, they will use the "Save to All Subdecks".

1. 70% (just respect the deck-specific desired retention of the deck which the card belong to)
1. 90% (when the deck-specific DR is missing, respect to the preset's DR)

It seems this is exactly the option users expect.
You can ask the users on the forum.
https://forums.ankiweb.net/t/allow-configuring-dr-for-each-deck-in-a-preset/61103

@user1823
Copy link
Contributor

user1823 commented Jul 19, 2025

IMO, the expected behaviour would be:

  1. 70% (just respect the deck-specific desired retention of the deck which the card belong to)

  2. 70% (the deck-specific DR will override all its subdecks' DR) [unless the subdecks have their own deck-specific DR]

Note: I have added a caveat to the second case in square brackets.

@Expertium
Copy link
Contributor

@Expertium
Copy link
Contributor

Expertium commented Jul 19, 2025

It seems like there is no consensus Actually, maybe there is
image

After reading replies, I think a good plan would be:

  1. Rename "Limits start from top" to something that doesn't use the word "limits". We could create another setting like that specifically for DR, but idk if that's really necessary
  2. If that setting is enabled, then per-deck DR of deck A also applies to its subdeck B and to its subsubdeck C, etc.
  3. If disabled, per-deck DR only applies to deck A, but not to its subdecks

Thoughts, everyone?

Or just go with the "no subdecks" approach since that's what the majority have voted for ¯\(ツ)

EDIT: ok, let's just go with "no subdecks" approach, seems like that's what most people want

@L-M-Sherlock
Copy link
Contributor Author

Sounds good to me, lol. Let's keep the current implementation.

@L-M-Sherlock L-M-Sherlock marked this pull request as ready for review July 20, 2025 05:58
@user1823
Copy link
Contributor

There's duplicated logic for obtaining a deck's effective desired retention value in compute_memory_state() and answering/mod.rs. This logic can be extracted to a helper function to avoid code duplication.

fn effective_desired_retention(deck: &Deck, config: &DeckConfig) -> f32 {
    deck
        .normal()
        .ok()
        .and_then(|d| d.desired_retention)
        .unwrap_or(config.inner.desired_retention)
}
let desired_retention = effective_desired_retention(&deck, &config);

@L-M-Sherlock L-M-Sherlock requested a review from dae July 21, 2025 10:29
@dae
Copy link
Member

dae commented Jul 28, 2025

Thanks for your work on this Jarrett, and for your patience.

@dae dae merged commit 46bcf4e into ankitects:main Jul 28, 2025
1 check passed
@L-M-Sherlock L-M-Sherlock deleted the Feat/per-deck-desired-retention branch July 28, 2025 08:29
@tmujir
Copy link

tmujir commented Oct 28, 2025

Hi there! I am just a user so apologies if this is not the right place to post. But what is the point of this feature if it doesn't allow for subdecks to have their own individual DRs separate from the parent deck DR? I was dismayed to find out that it doesn't work that way. It would be really great if we had a toggle for "Subdeck DR overrides parent deck DR" because otherwise this feature only works for parent level decks, no? Please correct me if I am mistaken.

@L-M-Sherlock
Copy link
Contributor Author

Please check the previous discussion.

@tmujir
Copy link

tmujir commented Oct 28, 2025

Thanks for your reply! I saw that the majority voted for the "no subdecks" approach, is it possible that we can get a toggle option to have the subdeck DR override the parent DR so everyone can be satisfied?

@L-M-Sherlock
Copy link
Contributor Author

I think it's feasible but very complicated. I recommend creating an issue to request that feature.

@tmujir
Copy link

tmujir commented Oct 28, 2025

Thanks, I made a thread!

https://forums.ankiweb.net/t/request-option-for-deck-specific-dr-to-apply-subdecks/67215

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants