Merge branch 'main' into Fix-Cards-with-Missing-Last-Review-Time-During-Database-Check

This commit is contained in:
Jarrett Ye 2025-08-04 20:03:54 +08:00 committed by GitHub
commit 709a02198c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 58 additions and 48 deletions

46
Cargo.lock generated
View file

@ -130,7 +130,7 @@ dependencies = [
"prost", "prost",
"prost-reflect", "prost-reflect",
"pulldown-cmark 0.13.0", "pulldown-cmark 0.13.0",
"rand 0.9.1", "rand 0.9.2",
"rayon", "rayon",
"regex", "regex",
"reqwest 0.12.20", "reqwest 0.12.20",
@ -144,7 +144,7 @@ dependencies = [
"serde_tuple", "serde_tuple",
"sha1", "sha1",
"snafu", "snafu",
"strum 0.27.1", "strum 0.27.2",
"syn 2.0.103", "syn 2.0.103",
"tempfile", "tempfile",
"tokio", "tokio",
@ -220,7 +220,7 @@ dependencies = [
"prost-types", "prost-types",
"serde", "serde",
"snafu", "snafu",
"strum 0.27.1", "strum 0.27.2",
] ]
[[package]] [[package]]
@ -706,7 +706,7 @@ dependencies = [
"log", "log",
"num-traits", "num-traits",
"portable-atomic-util", "portable-atomic-util",
"rand 0.9.1", "rand 0.9.2",
"rmp-serde", "rmp-serde",
"serde", "serde",
"serde_json", "serde_json",
@ -732,7 +732,7 @@ dependencies = [
"hashbrown 0.15.4", "hashbrown 0.15.4",
"log", "log",
"num-traits", "num-traits",
"rand 0.9.1", "rand 0.9.2",
"serde", "serde",
"spin 0.10.0", "spin 0.10.0",
"text_placeholder", "text_placeholder",
@ -762,12 +762,12 @@ dependencies = [
"csv", "csv",
"derive-new 0.7.0", "derive-new 0.7.0",
"dirs 6.0.0", "dirs 6.0.0",
"rand 0.9.1", "rand 0.9.2",
"rmp-serde", "rmp-serde",
"sanitize-filename 0.6.0", "sanitize-filename 0.6.0",
"serde", "serde",
"serde_json", "serde_json",
"strum 0.27.1", "strum 0.27.2",
"tempfile", "tempfile",
"thiserror 2.0.12", "thiserror 2.0.12",
] ]
@ -817,7 +817,7 @@ dependencies = [
"num-traits", "num-traits",
"paste", "paste",
"portable-atomic-util", "portable-atomic-util",
"rand 0.9.1", "rand 0.9.2",
"seq-macro", "seq-macro",
"spin 0.10.0", "spin 0.10.0",
] ]
@ -865,7 +865,7 @@ dependencies = [
"half", "half",
"hashbrown 0.15.4", "hashbrown 0.15.4",
"num-traits", "num-traits",
"rand 0.9.1", "rand 0.9.2",
"rand_distr", "rand_distr",
"serde", "serde",
"serde_bytes", "serde_bytes",
@ -959,7 +959,7 @@ dependencies = [
"memmap2", "memmap2",
"num-traits", "num-traits",
"num_cpus", "num_cpus",
"rand 0.9.1", "rand 0.9.2",
"rand_distr", "rand_distr",
"rayon", "rayon",
"safetensors", "safetensors",
@ -1403,7 +1403,7 @@ dependencies = [
"log", "log",
"num-traits", "num-traits",
"portable-atomic", "portable-atomic",
"rand 0.9.1", "rand 0.9.2",
"sanitize-filename 0.5.0", "sanitize-filename 0.5.0",
"serde", "serde",
"serde_json", "serde_json",
@ -2214,20 +2214,20 @@ dependencies = [
[[package]] [[package]]
name = "fsrs" name = "fsrs"
version = "5.0.0" version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f590cfcbe25079bb54a39900f45e6e308935bd6067249ce00d265b280465cde2" checksum = "df5aee516ebf9d4968364363b092371f988cd9fb628f7cae94ea422b6dd52f9c"
dependencies = [ dependencies = [
"burn", "burn",
"itertools 0.14.0", "itertools 0.14.0",
"log", "log",
"ndarray", "ndarray",
"priority-queue", "priority-queue",
"rand 0.9.1", "rand 0.9.2",
"rayon", "rayon",
"serde", "serde",
"snafu", "snafu",
"strum 0.27.1", "strum 0.27.2",
] ]
[[package]] [[package]]
@ -2804,7 +2804,7 @@ dependencies = [
"cfg-if", "cfg-if",
"crunchy", "crunchy",
"num-traits", "num-traits",
"rand 0.9.1", "rand 0.9.2",
"rand_distr", "rand_distr",
"serde", "serde",
] ]
@ -3658,7 +3658,7 @@ dependencies = [
"linkcheck", "linkcheck",
"regex", "regex",
"reqwest 0.12.20", "reqwest 0.12.20",
"strum 0.27.1", "strum 0.27.2",
"tokio", "tokio",
] ]
@ -5096,7 +5096,7 @@ dependencies = [
"bytes", "bytes",
"getrandom 0.3.3", "getrandom 0.3.3",
"lru-slab", "lru-slab",
"rand 0.9.1", "rand 0.9.2",
"ring", "ring",
"rustc-hash 2.1.1", "rustc-hash 2.1.1",
"rustls", "rustls",
@ -5150,9 +5150,9 @@ dependencies = [
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.9.1" version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [ dependencies = [
"rand_chacha 0.9.0", "rand_chacha 0.9.0",
"rand_core 0.9.3", "rand_core 0.9.3",
@ -5203,7 +5203,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463"
dependencies = [ dependencies = [
"num-traits", "num-traits",
"rand 0.9.1", "rand 0.9.2",
] ]
[[package]] [[package]]
@ -6113,9 +6113,9 @@ dependencies = [
[[package]] [[package]]
name = "strum" name = "strum"
version = "0.27.1" version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
dependencies = [ dependencies = [
"strum_macros 0.27.1", "strum_macros 0.27.1",
] ]

View file

@ -33,7 +33,7 @@ git = "https://github.com/ankitects/linkcheck.git"
rev = "184b2ca50ed39ca43da13f0b830a463861adb9ca" rev = "184b2ca50ed39ca43da13f0b830a463861adb9ca"
[workspace.dependencies.fsrs] [workspace.dependencies.fsrs]
version = "5.0.0" version = "5.0.1"
# git = "https://github.com/open-spaced-repetition/fsrs-rs.git" # git = "https://github.com/open-spaced-repetition/fsrs-rs.git"
# branch = "Refactor/expected_workload_via_dp" # branch = "Refactor/expected_workload_via_dp"
# rev = "a7f7efc10f0a26b14ee348cc7402155685f2a24f" # rev = "a7f7efc10f0a26b14ee348cc7402155685f2a24f"

View file

@ -1450,7 +1450,7 @@
}, },
{ {
"name": "fsrs", "name": "fsrs",
"version": "5.0.0", "version": "5.0.1",
"authors": "Open Spaced Repetition", "authors": "Open Spaced Repetition",
"repository": "https://github.com/open-spaced-repetition/fsrs-rs", "repository": "https://github.com/open-spaced-repetition/fsrs-rs",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
@ -3322,7 +3322,7 @@
}, },
{ {
"name": "rand", "name": "rand",
"version": "0.9.1", "version": "0.9.2",
"authors": "The Rand Project Developers|The Rust Project Developers", "authors": "The Rand Project Developers|The Rust Project Developers",
"repository": "https://github.com/rust-random/rand", "repository": "https://github.com/rust-random/rand",
"license": "Apache-2.0 OR MIT", "license": "Apache-2.0 OR MIT",
@ -4132,7 +4132,7 @@
}, },
{ {
"name": "strum", "name": "strum",
"version": "0.27.1", "version": "0.27.2",
"authors": "Peter Glotfelty <peter.glotfelty@microsoft.com>", "authors": "Peter Glotfelty <peter.glotfelty@microsoft.com>",
"repository": "https://github.com/Peternator7/strum", "repository": "https://github.com/Peternator7/strum",
"license": "MIT", "license": "MIT",

View file

@ -407,6 +407,8 @@ message SimulateFsrsReviewRequest {
deck_config.DeckConfig.Config.ReviewCardOrder review_order = 11; deck_config.DeckConfig.Config.ReviewCardOrder review_order = 11;
optional uint32 suspend_after_lapse_count = 12; optional uint32 suspend_after_lapse_count = 12;
float historical_retention = 13; float historical_retention = 13;
uint32 learning_step_count = 14;
uint32 relearning_step_count = 15;
} }
message SimulateFsrsReviewResponse { message SimulateFsrsReviewResponse {

View file

@ -126,8 +126,9 @@ impl Card {
} }
} }
/// This uses card.due and card.ivl to infer the elapsed time. If 'set due /// If last_review_date isn't stored in the card, this uses card.due and
/// date' or an add-on has changed the due date, this won't be accurate. /// 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<u32> { pub(crate) fn seconds_since_last_review(&self, timing: &SchedTimingToday) -> Option<u32> {
if let Some(last_review_time) = self.last_review_time { if let Some(last_review_time) = self.last_review_time {
Some(timing.now.elapsed_secs_since(last_review_time) as u32) Some(timing.now.elapsed_secs_since(last_review_time) as u32)

View file

@ -237,8 +237,8 @@ impl Collection {
learning_step_transitions: p.learning_step_transitions, learning_step_transitions: p.learning_step_transitions,
relearning_step_transitions: p.relearning_step_transitions, relearning_step_transitions: p.relearning_step_transitions,
state_rating_costs: p.state_rating_costs, state_rating_costs: p.state_rating_costs,
learning_step_count: p.learning_step_count, learning_step_count: req.learning_step_count as usize,
relearning_step_count: p.relearning_step_count, relearning_step_count: req.relearning_step_count as usize,
}; };
Ok((config, converted_cards)) Ok((config, converted_cards))

View file

@ -113,6 +113,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
easyDaysPercentages: $config.easyDaysPercentages, easyDaysPercentages: $config.easyDaysPercentages,
reviewOrder: $config.reviewOrder, reviewOrder: $config.reviewOrder,
historicalRetention: $config.historicalRetention, historicalRetention: $config.historicalRetention,
learningStepCount: $config.learnSteps.length,
relearningStepCount: $config.relearnSteps.length,
}); });
const DESIRED_RETENTION_LOW_THRESHOLD = 0.8; 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<GetRetentionWorkloadResponse> = let retentionWorkloadInfo: undefined | Promise<GetRetentionWorkloadResponse> =
undefined; undefined;
let lastParams = [...fsrsParams($config)]; let lastParams = [...fsrsParams($config)];
@ -139,7 +141,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
if ( if (
// If the cache is empty and a request has not yet been made to fill it // If the cache is empty and a request has not yet been made to fill it
!retentionWorloadInfo || !retentionWorkloadInfo ||
// If the parameters have been changed // If the parameters have been changed
lastParams.toString() !== params.toString() lastParams.toString() !== params.toString()
) { ) {
@ -148,12 +150,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
search: defaultparamSearch, search: defaultparamSearch,
}); });
lastParams = [...params]; lastParams = [...params];
retentionWorloadInfo = getRetentionWorkload(request); retentionWorkloadInfo = getRetentionWorkload(request);
} }
const previous = +startingDesiredRetention * 100; const previous = +startingDesiredRetention * 100;
const after = retention * 100; const after = retention * 100;
const resp = await retentionWorloadInfo; const resp = await retentionWorkloadInfo;
const factor = resp.costs[after] / resp.costs[previous]; const factor = resp.costs[after] / resp.costs[previous];
desiredRetentionChangeInfo = tr.deckConfigWorkloadFactorChange({ desiredRetentionChangeInfo = tr.deckConfigWorkloadFactorChange({
@ -218,29 +220,34 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
healthCheck: $healthCheck, healthCheck: $healthCheck,
}); });
const already_optimal = const alreadyOptimal =
(params.length && (params.length &&
params.every( params.every(
(n, i) => n.toFixed(4) === resp.params[i].toFixed(4), (n, i) => n.toFixed(4) === resp.params[i].toFixed(4),
)) || )) ||
resp.params.length === 0; resp.params.length === 0;
let healthCheckMessage = "";
if (resp.healthCheckPassed !== undefined) { if (resp.healthCheckPassed !== undefined) {
if (resp.healthCheckPassed) { healthCheckMessage = resp.healthCheckPassed
setTimeout(() => alert(tr.deckConfigFsrsGoodFit()), 200); ? tr.deckConfigFsrsGoodFit()
} else { : tr.deckConfigFsrsBadFitWarning();
setTimeout( }
() => alert(tr.deckConfigFsrsBadFitWarning()), let alreadyOptimalMessage = "";
200, if (alreadyOptimal) {
); alreadyOptimalMessage = resp.fsrsItems
}
} else if (already_optimal) {
const msg = resp.fsrsItems
? tr.deckConfigFsrsParamsOptimal() ? tr.deckConfigFsrsParamsOptimal()
: tr.deckConfigFsrsParamsNoReviews(); : 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; $config.fsrsParams6 = resp.params;
setTimeout(() => { setTimeout(() => {
optimized = true; optimized = true;