Feat/expected_workload_with_existing_cards implementation (#4243)

* https://github.com/open-spaced-repetition/fsrs-rs/pull/355

* add is_included card

* bump version

* ./check

* update package.lock

* parallellify

* bump fsrs
This commit is contained in:
Luc Mcgrady 2025-08-06 09:01:06 +01:00 committed by GitHub
parent f7e6e9cb0d
commit 402008950c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 34 additions and 13 deletions

4
Cargo.lock generated
View file

@ -2214,9 +2214,9 @@ dependencies = [
[[package]]
name = "fsrs"
version = "5.0.1"
version = "5.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df5aee516ebf9d4968364363b092371f988cd9fb628f7cae94ea422b6dd52f9c"
checksum = "04954cc67c3c11ee342a2ee1f5222bf76d73f7772df08d37dc9a6cdd73c467eb"
dependencies = [
"burn",
"itertools 0.14.0",

View file

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

View file

@ -1450,7 +1450,7 @@
},
{
"name": "fsrs",
"version": "5.0.1",
"version": "5.1.0",
"authors": "Open Spaced Repetition",
"repository": "https://github.com/open-spaced-repetition/fsrs-rs",
"license": "BSD-3-Clause",

View file

@ -3,6 +3,8 @@
use std::collections::HashMap;
use anki_proto::generic;
use rayon::iter::IntoParallelIterator;
use rayon::iter::ParallelIterator;
use crate::collection::Collection;
use crate::deckconfig::DeckConfSchema11;
@ -11,6 +13,7 @@ use crate::deckconfig::DeckConfigId;
use crate::deckconfig::UpdateDeckConfigsRequest;
use crate::error::Result;
use crate::scheduler::fsrs::params::ignore_revlogs_before_date_to_ms;
use crate::scheduler::fsrs::simulator::is_included_card;
impl crate::services::DeckConfigService for Collection {
fn add_or_update_deck_config_legacy(
@ -103,6 +106,7 @@ impl crate::services::DeckConfigService for Collection {
&mut self,
input: anki_proto::deck_config::GetRetentionWorkloadRequest,
) -> Result<anki_proto::deck_config::GetRetentionWorkloadResponse> {
let days_elapsed = self.timing_today().unwrap().days_elapsed as i32;
let guard =
self.search_cards_into_table(&input.search, crate::search::SortMode::NoOrder)?;
@ -112,12 +116,26 @@ impl crate::services::DeckConfigService for Collection {
.get_revlog_entries_for_searched_cards_in_card_order()?;
let config = guard.col.get_optimal_retention_parameters(revlogs)?;
let cards = guard
.col
.storage
.all_searched_cards()?
.into_iter()
.filter(is_included_card)
.filter_map(|c| crate::card::Card::convert(c.clone(), days_elapsed, c.memory_state?))
.collect::<Vec<fsrs::Card>>();
let costs = (70u32..=99u32)
.into_par_iter()
.map(|dr| {
Ok((
dr,
fsrs::expected_workload(&input.w, dr as f32 / 100., &config)?,
fsrs::expected_workload_with_existing_cards(
&input.w,
dr as f32 / 100.,
&config,
&cards,
)?,
))
})
.collect::<Result<HashMap<_, _>>>()?;

View file

@ -121,6 +121,12 @@ fn create_review_priority_fn(
}
}
pub(crate) fn is_included_card(c: &Card) -> bool {
c.queue != CardQueue::Suspended
&& c.queue != CardQueue::PreviewRepeat
&& c.ctype != CardType::New
}
impl Collection {
pub fn simulate_request_to_config(
&mut self,
@ -133,11 +139,6 @@ impl Collection {
.get_revlog_entries_for_searched_cards_in_card_order()?;
let mut cards = guard.col.storage.all_searched_cards()?;
drop(guard);
fn is_included_card(c: &Card) -> bool {
c.queue != CardQueue::Suspended
&& c.queue != CardQueue::PreviewRepeat
&& c.ctype != CardType::New
}
// calculate any missing memory state
for c in &mut cards {
if is_included_card(c) && c.memory_state.is_none() {
@ -306,7 +307,11 @@ impl Collection {
}
impl Card {
fn convert(card: Card, days_elapsed: i32, memory_state: FsrsMemoryState) -> Option<fsrs::Card> {
pub(crate) fn convert(
card: Card,
days_elapsed: i32,
memory_state: FsrsMemoryState,
) -> Option<fsrs::Card> {
match card.queue {
CardQueue::DayLearn | CardQueue::Review => {
let due = card.original_or_current_due();