mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
Convert FSRS to a global option
Allowing some decks to be FSRS and some SM-2 will lead to confusing behavior when sorting on SM-2 or FSRS-specific fields, or when moving cards between decks.
This commit is contained in:
parent
0071094e6c
commit
c78de23cf9
11 changed files with 27 additions and 24 deletions
|
@ -135,7 +135,6 @@ message DeckConfig {
|
||||||
bool bury_reviews = 28;
|
bool bury_reviews = 28;
|
||||||
bool bury_interday_learning = 29;
|
bool bury_interday_learning = 29;
|
||||||
|
|
||||||
bool fsrs_enabled = 36;
|
|
||||||
float desired_retention = 37; // for fsrs
|
float desired_retention = 37; // for fsrs
|
||||||
|
|
||||||
bytes other = 255;
|
bytes other = 255;
|
||||||
|
@ -179,6 +178,7 @@ message DeckConfigsForUpdate {
|
||||||
string card_state_customizer = 6;
|
string card_state_customizer = 6;
|
||||||
// only applies to v3 scheduler
|
// only applies to v3 scheduler
|
||||||
bool new_cards_ignore_review_limit = 7;
|
bool new_cards_ignore_review_limit = 7;
|
||||||
|
bool fsrs = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UpdateDeckConfigsRequest {
|
message UpdateDeckConfigsRequest {
|
||||||
|
@ -191,4 +191,5 @@ message UpdateDeckConfigsRequest {
|
||||||
string card_state_customizer = 5;
|
string card_state_customizer = 5;
|
||||||
DeckConfigsForUpdate.CurrentDeck.Limits limits = 6;
|
DeckConfigsForUpdate.CurrentDeck.Limits limits = 6;
|
||||||
bool new_cards_ignore_review_limit = 7;
|
bool new_cards_ignore_review_limit = 7;
|
||||||
|
bool fsrs = 8;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ pub enum BoolKey {
|
||||||
MergeNotetypes,
|
MergeNotetypes,
|
||||||
WithScheduling,
|
WithScheduling,
|
||||||
StopTimerOnAnswer,
|
StopTimerOnAnswer,
|
||||||
|
Fsrs,
|
||||||
|
|
||||||
#[strum(to_string = "normalize_note_text")]
|
#[strum(to_string = "normalize_note_text")]
|
||||||
NormalizeNoteText,
|
NormalizeNoteText,
|
||||||
|
|
|
@ -66,7 +66,6 @@ const DEFAULT_DECK_CONFIG_INNER: DeckConfigInner = DeckConfigInner {
|
||||||
bury_new: false,
|
bury_new: false,
|
||||||
bury_reviews: false,
|
bury_reviews: false,
|
||||||
bury_interday_learning: false,
|
bury_interday_learning: false,
|
||||||
fsrs_enabled: false,
|
|
||||||
fsrs_weights: vec![],
|
fsrs_weights: vec![],
|
||||||
desired_retention: 0.9,
|
desired_retention: 0.9,
|
||||||
other: Vec::new(),
|
other: Vec::new(),
|
||||||
|
|
|
@ -68,8 +68,6 @@ pub struct DeckConfSchema11 {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
fsrs_weights: Vec<f32>,
|
fsrs_weights: Vec<f32>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
fsrs_enabled: bool,
|
|
||||||
#[serde(default)]
|
|
||||||
desired_retention: f32,
|
desired_retention: f32,
|
||||||
|
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -258,7 +256,6 @@ impl Default for DeckConfSchema11 {
|
||||||
new_gather_priority: 0,
|
new_gather_priority: 0,
|
||||||
bury_interday_learning: false,
|
bury_interday_learning: false,
|
||||||
fsrs_weights: vec![],
|
fsrs_weights: vec![],
|
||||||
fsrs_enabled: false,
|
|
||||||
desired_retention: 0.9,
|
desired_retention: 0.9,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -329,7 +326,6 @@ impl From<DeckConfSchema11> for DeckConfig {
|
||||||
bury_reviews: c.rev.bury,
|
bury_reviews: c.rev.bury,
|
||||||
bury_interday_learning: c.bury_interday_learning,
|
bury_interday_learning: c.bury_interday_learning,
|
||||||
fsrs_weights: c.fsrs_weights,
|
fsrs_weights: c.fsrs_weights,
|
||||||
fsrs_enabled: c.fsrs_enabled,
|
|
||||||
desired_retention: c.desired_retention,
|
desired_retention: c.desired_retention,
|
||||||
other: other_bytes,
|
other: other_bytes,
|
||||||
},
|
},
|
||||||
|
@ -423,7 +419,6 @@ impl From<DeckConfig> for DeckConfSchema11 {
|
||||||
new_gather_priority: i.new_card_gather_priority,
|
new_gather_priority: i.new_card_gather_priority,
|
||||||
bury_interday_learning: i.bury_interday_learning,
|
bury_interday_learning: i.bury_interday_learning,
|
||||||
fsrs_weights: i.fsrs_weights,
|
fsrs_weights: i.fsrs_weights,
|
||||||
fsrs_enabled: i.fsrs_enabled,
|
|
||||||
desired_retention: i.desired_retention,
|
desired_retention: i.desired_retention,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -448,7 +443,6 @@ static RESERVED_DECKCONF_KEYS: Set<&'static str> = phf_set! {
|
||||||
"newGatherPriority",
|
"newGatherPriority",
|
||||||
"fsrsWeights",
|
"fsrsWeights",
|
||||||
"desiredRetention",
|
"desiredRetention",
|
||||||
"fsrsEnabled",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static RESERVED_DECKCONF_NEW_KEYS: Set<&'static str> = phf_set! {
|
static RESERVED_DECKCONF_NEW_KEYS: Set<&'static str> = phf_set! {
|
||||||
|
|
|
@ -102,6 +102,7 @@ impl From<anki_proto::deck_config::UpdateDeckConfigsRequest> for UpdateDeckConfi
|
||||||
card_state_customizer: c.card_state_customizer,
|
card_state_customizer: c.card_state_customizer,
|
||||||
limits: c.limits.unwrap_or_default(),
|
limits: c.limits.unwrap_or_default(),
|
||||||
new_cards_ignore_review_limit: c.new_cards_ignore_review_limit,
|
new_cards_ignore_review_limit: c.new_cards_ignore_review_limit,
|
||||||
|
fsrs: c.fsrs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ pub struct UpdateDeckConfigsRequest {
|
||||||
pub card_state_customizer: String,
|
pub card_state_customizer: String,
|
||||||
pub limits: Limits,
|
pub limits: Limits,
|
||||||
pub new_cards_ignore_review_limit: bool,
|
pub new_cards_ignore_review_limit: bool,
|
||||||
|
pub fsrs: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Collection {
|
impl Collection {
|
||||||
|
@ -48,6 +49,7 @@ impl Collection {
|
||||||
v3_scheduler: self.get_config_bool(BoolKey::Sched2021),
|
v3_scheduler: self.get_config_bool(BoolKey::Sched2021),
|
||||||
card_state_customizer: self.get_config_string(StringKey::CardStateCustomizer),
|
card_state_customizer: self.get_config_string(StringKey::CardStateCustomizer),
|
||||||
new_cards_ignore_review_limit: self.get_config_bool(BoolKey::NewCardsIgnoreReviewLimit),
|
new_cards_ignore_review_limit: self.get_config_bool(BoolKey::NewCardsIgnoreReviewLimit),
|
||||||
|
fsrs: self.get_config_bool(BoolKey::Fsrs),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,6 +163,10 @@ impl Collection {
|
||||||
let selected_config = input.configs.last().unwrap();
|
let selected_config = input.configs.last().unwrap();
|
||||||
let mut decks_needing_memory_recompute: HashMap<DeckConfigId, Vec<SearchNode>> =
|
let mut decks_needing_memory_recompute: HashMap<DeckConfigId, Vec<SearchNode>> =
|
||||||
Default::default();
|
Default::default();
|
||||||
|
let fsrs_toggled = self.get_config_bool(BoolKey::Fsrs) != input.fsrs;
|
||||||
|
if fsrs_toggled {
|
||||||
|
self.set_config_bool_inner(BoolKey::Fsrs, input.fsrs)?;
|
||||||
|
}
|
||||||
for deck in self.storage.get_all_decks()? {
|
for deck in self.storage.get_all_decks()? {
|
||||||
if let Ok(normal) = deck.normal() {
|
if let Ok(normal) = deck.normal() {
|
||||||
let deck_id = deck.id;
|
let deck_id = deck.id;
|
||||||
|
@ -171,9 +177,6 @@ impl Collection {
|
||||||
let previous_order = previous_config
|
let previous_order = previous_config
|
||||||
.map(|c| c.inner.new_card_insert_order())
|
.map(|c| c.inner.new_card_insert_order())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let previous_fsrs_on = previous_config
|
|
||||||
.map(|c| c.inner.fsrs_enabled)
|
|
||||||
.unwrap_or_default();
|
|
||||||
let previous_weights = previous_config.map(|c| &c.inner.fsrs_weights);
|
let previous_weights = previous_config.map(|c| &c.inner.fsrs_weights);
|
||||||
|
|
||||||
// if a selected (sub)deck, or its old config was removed, update deck to point
|
// if a selected (sub)deck, or its old config was removed, update deck to point
|
||||||
|
@ -200,11 +203,8 @@ impl Collection {
|
||||||
}
|
}
|
||||||
|
|
||||||
// if weights differ, memory state needs to be recomputed
|
// if weights differ, memory state needs to be recomputed
|
||||||
let current_fsrs_on = current_config
|
|
||||||
.map(|c| c.inner.fsrs_enabled)
|
|
||||||
.unwrap_or_default();
|
|
||||||
let current_weights = current_config.map(|c| &c.inner.fsrs_weights);
|
let current_weights = current_config.map(|c| &c.inner.fsrs_weights);
|
||||||
if current_fsrs_on != previous_fsrs_on || previous_weights != current_weights {
|
if fsrs_toggled || previous_weights != current_weights {
|
||||||
decks_needing_memory_recompute
|
decks_needing_memory_recompute
|
||||||
.entry(current_config_id)
|
.entry(current_config_id)
|
||||||
.or_default()
|
.or_default()
|
||||||
|
@ -220,7 +220,7 @@ impl Collection {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(conf_id, search)| {
|
.map(|(conf_id, search)| {
|
||||||
let weights = configs_after_update.get(&conf_id).and_then(|c| {
|
let weights = configs_after_update.get(&conf_id).and_then(|c| {
|
||||||
if c.inner.fsrs_enabled {
|
if input.fsrs {
|
||||||
Some(c.inner.fsrs_weights.clone())
|
Some(c.inner.fsrs_weights.clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -365,6 +365,7 @@ mod test {
|
||||||
card_state_customizer: "".to_string(),
|
card_state_customizer: "".to_string(),
|
||||||
limits: Limits::default(),
|
limits: Limits::default(),
|
||||||
new_cards_ignore_review_limit: false,
|
new_cards_ignore_review_limit: false,
|
||||||
|
fsrs: false,
|
||||||
};
|
};
|
||||||
assert!(!col.update_deck_configs(input.clone())?.changes.had_change());
|
assert!(!col.update_deck_configs(input.clone())?.changes.had_change());
|
||||||
|
|
||||||
|
|
|
@ -351,7 +351,7 @@ impl Collection {
|
||||||
.get_deck(card.deck_id)?
|
.get_deck(card.deck_id)?
|
||||||
.or_not_found(card.deck_id)?;
|
.or_not_found(card.deck_id)?;
|
||||||
let config = self.home_deck_config(deck.config_id(), card.original_deck_id)?;
|
let config = self.home_deck_config(deck.config_id(), card.original_deck_id)?;
|
||||||
let fsrs_next_states = if config.inner.fsrs_enabled {
|
let fsrs_next_states = if self.get_config_bool(BoolKey::Fsrs) {
|
||||||
let fsrs = FSRS::new(Some(&config.inner.fsrs_weights))?;
|
let fsrs = FSRS::new(Some(&config.inner.fsrs_weights))?;
|
||||||
let memory_state = if let Some(state) = card.memory_state {
|
let memory_state = if let Some(state) = card.memory_state {
|
||||||
Some(MemoryState::from(state))
|
Some(MemoryState::from(state))
|
||||||
|
|
|
@ -28,6 +28,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
const config = state.currentConfig;
|
const config = state.currentConfig;
|
||||||
const defaults = state.defaults;
|
const defaults = state.defaults;
|
||||||
const cardStateCustomizer = state.cardStateCustomizer;
|
const cardStateCustomizer = state.cardStateCustomizer;
|
||||||
|
const fsrs = state.fsrs;
|
||||||
|
|
||||||
const settings = {
|
const settings = {
|
||||||
maximumInterval: {
|
maximumInterval: {
|
||||||
|
@ -76,7 +77,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
carousel.to(index);
|
carousel.to(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
$: fsrsClientWarning = $config.fsrsEnabled ? tr.deckConfigFsrsOnAllClients() : "";
|
$: fsrsClientWarning = $fsrs ? tr.deckConfigFsrsOnAllClients() : "";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<TitledContainer title={tr.deckConfigAdvancedTitle()}>
|
<TitledContainer title={tr.deckConfigAdvancedTitle()}>
|
||||||
|
@ -93,7 +94,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
<DynamicallySlottable slotHost={Item} {api}>
|
<DynamicallySlottable slotHost={Item} {api}>
|
||||||
{#if state.v3Scheduler}
|
{#if state.v3Scheduler}
|
||||||
<Item>
|
<Item>
|
||||||
<SwitchRow bind:value={$config.fsrsEnabled} defaultValue={false}>
|
<SwitchRow bind:value={$fsrs} defaultValue={false}>
|
||||||
<SettingTitle>FSRS</SettingTitle>
|
<SettingTitle>FSRS</SettingTitle>
|
||||||
</SwitchRow>
|
</SwitchRow>
|
||||||
</Item>
|
</Item>
|
||||||
|
@ -117,7 +118,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
</SpinBoxRow>
|
</SpinBoxRow>
|
||||||
</Item>
|
</Item>
|
||||||
|
|
||||||
{#if !$config.fsrsEnabled || !state.v3Scheduler}
|
{#if !$fsrs || !state.v3Scheduler}
|
||||||
<Item>
|
<Item>
|
||||||
<SpinBoxFloatRow
|
<SpinBoxFloatRow
|
||||||
bind:value={$config.initialEase}
|
bind:value={$config.initialEase}
|
||||||
|
|
|
@ -26,6 +26,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
const config = state.currentConfig;
|
const config = state.currentConfig;
|
||||||
const defaults = state.defaults;
|
const defaults = state.defaults;
|
||||||
|
const fsrs = state.fsrs;
|
||||||
|
|
||||||
let stepsExceedMinimumInterval: string;
|
let stepsExceedMinimumInterval: string;
|
||||||
let stepsTooLargeForFsrs: string;
|
let stepsTooLargeForFsrs: string;
|
||||||
|
@ -38,7 +39,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
? tr.deckConfigRelearningStepsAboveMinimumInterval()
|
? tr.deckConfigRelearningStepsAboveMinimumInterval()
|
||||||
: "";
|
: "";
|
||||||
stepsTooLargeForFsrs =
|
stepsTooLargeForFsrs =
|
||||||
$config.fsrsEnabled && lastRelearnStepInDays >= 1
|
$fsrs && lastRelearnStepInDays >= 1
|
||||||
? tr.deckConfigStepsTooLargeForFsrs()
|
? tr.deckConfigStepsTooLargeForFsrs()
|
||||||
: "";
|
: "";
|
||||||
}
|
}
|
||||||
|
@ -106,7 +107,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
<Warning warning={stepsTooLargeForFsrs} />
|
<Warning warning={stepsTooLargeForFsrs} />
|
||||||
</Item>
|
</Item>
|
||||||
|
|
||||||
{#if !$config.fsrsEnabled}
|
{#if !$fsrs}
|
||||||
<Item>
|
<Item>
|
||||||
<SpinBoxRow
|
<SpinBoxRow
|
||||||
bind:value={$config.minimumLapseInterval}
|
bind:value={$config.minimumLapseInterval}
|
||||||
|
|
|
@ -27,6 +27,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
const config = state.currentConfig;
|
const config = state.currentConfig;
|
||||||
const defaults = state.defaults;
|
const defaults = state.defaults;
|
||||||
|
const fsrs = state.fsrs;
|
||||||
|
|
||||||
let stepsExceedGraduatingInterval: string;
|
let stepsExceedGraduatingInterval: string;
|
||||||
let stepsTooLargeForFsrs: string;
|
let stepsTooLargeForFsrs: string;
|
||||||
|
@ -39,7 +40,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
? tr.deckConfigLearningStepAboveGraduatingInterval()
|
? tr.deckConfigLearningStepAboveGraduatingInterval()
|
||||||
: "";
|
: "";
|
||||||
stepsTooLargeForFsrs =
|
stepsTooLargeForFsrs =
|
||||||
$config.fsrsEnabled && lastLearnStepInDays >= 1
|
$fsrs && lastLearnStepInDays >= 1
|
||||||
? tr.deckConfigStepsTooLargeForFsrs()
|
? tr.deckConfigStepsTooLargeForFsrs()
|
||||||
: "";
|
: "";
|
||||||
}
|
}
|
||||||
|
@ -118,7 +119,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
<Warning warning={stepsTooLargeForFsrs} />
|
<Warning warning={stepsTooLargeForFsrs} />
|
||||||
</Item>
|
</Item>
|
||||||
|
|
||||||
{#if !$config.fsrsEnabled}
|
{#if !$fsrs}
|
||||||
<Item>
|
<Item>
|
||||||
<SpinBoxRow
|
<SpinBoxRow
|
||||||
bind:value={$config.graduatingIntervalGood}
|
bind:value={$config.graduatingIntervalGood}
|
||||||
|
|
|
@ -48,6 +48,7 @@ export class DeckOptionsState {
|
||||||
readonly addonComponents: Writable<DynamicSvelteComponent[]>;
|
readonly addonComponents: Writable<DynamicSvelteComponent[]>;
|
||||||
readonly v3Scheduler: boolean;
|
readonly v3Scheduler: boolean;
|
||||||
readonly newCardsIgnoreReviewLimit: Writable<boolean>;
|
readonly newCardsIgnoreReviewLimit: Writable<boolean>;
|
||||||
|
readonly fsrs: Writable<boolean>;
|
||||||
|
|
||||||
private targetDeckId: DeckOptionsId;
|
private targetDeckId: DeckOptionsId;
|
||||||
private configs: ConfigWithCount[];
|
private configs: ConfigWithCount[];
|
||||||
|
@ -79,6 +80,7 @@ export class DeckOptionsState {
|
||||||
this.cardStateCustomizer = writable(data.cardStateCustomizer);
|
this.cardStateCustomizer = writable(data.cardStateCustomizer);
|
||||||
this.deckLimits = writable(data.currentDeck?.limits ?? createLimits());
|
this.deckLimits = writable(data.currentDeck?.limits ?? createLimits());
|
||||||
this.newCardsIgnoreReviewLimit = writable(data.newCardsIgnoreReviewLimit);
|
this.newCardsIgnoreReviewLimit = writable(data.newCardsIgnoreReviewLimit);
|
||||||
|
this.fsrs = writable(data.fsrs);
|
||||||
|
|
||||||
// decrement the use count of the starting item, as we'll apply +1 to currently
|
// decrement the use count of the starting item, as we'll apply +1 to currently
|
||||||
// selected one at display time
|
// selected one at display time
|
||||||
|
@ -205,6 +207,7 @@ export class DeckOptionsState {
|
||||||
cardStateCustomizer: get(this.cardStateCustomizer),
|
cardStateCustomizer: get(this.cardStateCustomizer),
|
||||||
limits: get(this.deckLimits),
|
limits: get(this.deckLimits),
|
||||||
newCardsIgnoreReviewLimit: get(this.newCardsIgnoreReviewLimit),
|
newCardsIgnoreReviewLimit: get(this.newCardsIgnoreReviewLimit),
|
||||||
|
fsrs: get(this.fsrs),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue