Allow cards with no learning history when not training

This commit is contained in:
Damien Elmes 2023-09-30 13:39:22 +10:00
parent 93499d4182
commit d16faacd0f
2 changed files with 64 additions and 6 deletions

View file

@ -125,3 +125,58 @@ pub(crate) fn single_card_revlog_to_item(
let items = single_card_revlog_to_items(entries, next_day_at, false); let items = single_card_revlog_to_items(entries, next_day_at, false);
items.and_then(|mut i| i.pop()) items.and_then(|mut i| i.pop())
} }
#[cfg(test)]
mod tests {
use fsrs::MemoryState;
use super::super::weights::tests::fsrs_items;
use super::*;
use crate::revlog::RevlogReviewKind;
use crate::scheduler::fsrs::weights::tests::convert;
use crate::scheduler::fsrs::weights::tests::review;
use crate::scheduler::fsrs::weights::tests::revlog;
#[test]
fn bypassed_learning_is_handled() {
// cards without any learning steps due to truncated history still have memory
// state calculated
assert_eq!(
convert(
&[
RevlogEntry {
ease_factor: 2500,
..revlog(RevlogReviewKind::Manual, 7)
},
revlog(RevlogReviewKind::Review, 6),
],
false,
),
fsrs_items!([review(0)])
);
}
#[test]
fn zero_history_is_handled() {
// when the history is empty, no items are produced
assert_eq!(convert(&[], false), None);
// but memory state should still be inferred, by using the card's current state
let mut card = Card {
ctype: CardType::Review,
interval: 100,
ease_factor: 1300,
..Default::default()
};
card.set_memory_state(&FSRS::new(Some(&[])).unwrap(), None);
assert_eq!(
card.memory_state,
Some(
MemoryState {
stability: 100.0,
difficulty: 9.692858
}
.into()
)
);
}
}

View file

@ -135,8 +135,8 @@ pub(crate) fn single_card_revlog_to_items(
if idx > 0 { if idx > 0 {
entries.drain(..idx); entries.drain(..idx);
} }
} else { } else if training {
// we ignore cards that don't have any learning steps // when training, we ignore cards that don't have any learning steps
return None; return None;
} }
@ -210,12 +210,12 @@ impl RevlogEntry {
} }
#[cfg(test)] #[cfg(test)]
mod tests { pub(crate) mod tests {
use super::*; use super::*;
const NEXT_DAY_AT: TimestampSecs = TimestampSecs(86400 * 100); const NEXT_DAY_AT: TimestampSecs = TimestampSecs(86400 * 100);
fn revlog(review_kind: RevlogReviewKind, days_ago: i64) -> RevlogEntry { pub(crate) fn revlog(review_kind: RevlogReviewKind, days_ago: i64) -> RevlogEntry {
RevlogEntry { RevlogEntry {
review_kind, review_kind,
id: ((NEXT_DAY_AT.0 - days_ago * 86400) * 1000).into(), id: ((NEXT_DAY_AT.0 - days_ago * 86400) * 1000).into(),
@ -224,14 +224,15 @@ mod tests {
} }
} }
fn review(delta_t: u32) -> FSRSReview { pub(crate) fn review(delta_t: u32) -> FSRSReview {
FSRSReview { rating: 3, delta_t } FSRSReview { rating: 3, delta_t }
} }
fn convert(revlog: &[RevlogEntry], training: bool) -> Option<Vec<FSRSItem>> { pub(crate) fn convert(revlog: &[RevlogEntry], training: bool) -> Option<Vec<FSRSItem>> {
single_card_revlog_to_items(revlog.to_vec(), NEXT_DAY_AT, training) single_card_revlog_to_items(revlog.to_vec(), NEXT_DAY_AT, training)
} }
#[macro_export]
macro_rules! fsrs_items { macro_rules! fsrs_items {
($($reviews:expr),*) => { ($($reviews:expr),*) => {
Some(vec![ Some(vec![
@ -244,6 +245,8 @@ mod tests {
}; };
} }
pub(crate) use fsrs_items;
#[test] #[test]
fn delta_t_is_correct() -> Result<()> { fn delta_t_is_correct() -> Result<()> {
assert_eq!( assert_eq!(