diff --git a/Cargo.lock b/Cargo.lock index f39c9e154..2517fa40e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2851,7 +2851,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", - "equivalent", + "equivalentclone()", "foldhash", "serde", ] diff --git a/proto/anki/scheduler.proto b/proto/anki/scheduler.proto index a88cd468c..34b350642 100644 --- a/proto/anki/scheduler.proto +++ b/proto/anki/scheduler.proto @@ -407,6 +407,8 @@ message SimulateFsrsReviewRequest { deck_config.DeckConfig.Config.ReviewCardOrder review_order = 11; optional uint32 suspend_after_lapse_count = 12; float historical_retention = 13; + uint32 learning_step_count = 14; + uint32 relearning_step_count = 15; } message SimulateFsrsReviewResponse { diff --git a/rslib/src/browser_table.rs b/rslib/src/browser_table.rs index 312a17561..c13d9c294 100644 --- a/rslib/src/browser_table.rs +++ b/rslib/src/browser_table.rs @@ -126,8 +126,9 @@ impl Card { } } - /// This uses card.due and card.ivl to infer the elapsed time. If 'set due - /// date' or an add-on has changed the due date, this won't be accurate. + /// If last_review_date isn't stored in the card, this uses card.due and + /// card.ivl to infer the elapsed time, which won't be accurate if + /// 'set due date' or an add-on has changed the due date. pub(crate) fn seconds_since_last_review(&self, timing: &SchedTimingToday) -> Option { if let Some(last_review_time) = self.last_review_time { Some(timing.now.elapsed_secs_since(last_review_time) as u32) diff --git a/rslib/src/scheduler/fsrs/simulator.rs b/rslib/src/scheduler/fsrs/simulator.rs index c782530cd..a26afda9c 100644 --- a/rslib/src/scheduler/fsrs/simulator.rs +++ b/rslib/src/scheduler/fsrs/simulator.rs @@ -238,8 +238,8 @@ impl Collection { learning_step_transitions: p.learning_step_transitions, relearning_step_transitions: p.relearning_step_transitions, state_rating_costs: p.state_rating_costs, - learning_step_count: p.learning_step_count, - relearning_step_count: p.relearning_step_count, + learning_step_count: req.learning_step_count as usize, + relearning_step_count: req.relearning_step_count as usize, }; Ok((config, converted_cards)) diff --git a/ts/routes/deck-options/FsrsOptions.svelte b/ts/routes/deck-options/FsrsOptions.svelte index 0bb6220bb..a166f2081 100644 --- a/ts/routes/deck-options/FsrsOptions.svelte +++ b/ts/routes/deck-options/FsrsOptions.svelte @@ -113,6 +113,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html easyDaysPercentages: $config.easyDaysPercentages, reviewOrder: $config.reviewOrder, historicalRetention: $config.historicalRetention, + learningStepCount: $config.learnSteps.length, + relearningStepCount: $config.relearnSteps.length, }); const DESIRED_RETENTION_LOW_THRESHOLD = 0.8; @@ -128,7 +130,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } } - let retentionWorloadInfo: undefined | Promise = + let retentionWorkloadInfo: undefined | Promise = undefined; let lastParams = [...fsrsParams($config)]; @@ -139,7 +141,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } if ( // If the cache is empty and a request has not yet been made to fill it - !retentionWorloadInfo || + !retentionWorkloadInfo || // If the parameters have been changed lastParams.toString() !== params.toString() ) { @@ -148,12 +150,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html search: defaultparamSearch, }); lastParams = [...params]; - retentionWorloadInfo = getRetentionWorkload(request); + retentionWorkloadInfo = getRetentionWorkload(request); } const previous = +startingDesiredRetention * 100; const after = retention * 100; - const resp = await retentionWorloadInfo; + const resp = await retentionWorkloadInfo; const factor = resp.costs[after] / resp.costs[previous]; desiredRetentionChangeInfo = tr.deckConfigWorkloadFactorChange({ @@ -218,29 +220,34 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html healthCheck: $healthCheck, }); - const already_optimal = + const alreadyOptimal = (params.length && params.every( (n, i) => n.toFixed(4) === resp.params[i].toFixed(4), )) || resp.params.length === 0; + let healthCheckMessage = ""; if (resp.healthCheckPassed !== undefined) { - if (resp.healthCheckPassed) { - setTimeout(() => alert(tr.deckConfigFsrsGoodFit()), 200); - } else { - setTimeout( - () => alert(tr.deckConfigFsrsBadFitWarning()), - 200, - ); - } - } else if (already_optimal) { - const msg = resp.fsrsItems + healthCheckMessage = resp.healthCheckPassed + ? tr.deckConfigFsrsGoodFit() + : tr.deckConfigFsrsBadFitWarning(); + } + let alreadyOptimalMessage = ""; + if (alreadyOptimal) { + alreadyOptimalMessage = resp.fsrsItems ? tr.deckConfigFsrsParamsOptimal() : tr.deckConfigFsrsParamsNoReviews(); - setTimeout(() => alert(msg), 200); } - if (!already_optimal) { + const message = [alreadyOptimalMessage, healthCheckMessage] + .filter((a) => a) + .join("\n\n"); + + if (message) { + setTimeout(() => alert(message), 200); + } + + if (!alreadyOptimal) { $config.fsrsParams6 = resp.params; setTimeout(() => { optimized = true;