mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 15:02:21 -04:00
fix new ease not being applied to card on lapse
+ zero remaining steps when graduating (they shouldn't have been doing any harm, but this is neater) + add some more tests that cover these cases
This commit is contained in:
parent
195c41cba3
commit
29f9717c84
3 changed files with 167 additions and 28 deletions
|
@ -370,40 +370,52 @@ impl Collection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// test helpers
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
impl Collection {
|
pub mod test_helpers {
|
||||||
pub(crate) fn answer_again(&mut self) {
|
use super::*;
|
||||||
self.answer(|states| states.again, Rating::Again).unwrap()
|
|
||||||
|
pub struct PostAnswerState {
|
||||||
|
pub card_id: CardId,
|
||||||
|
pub new_state: CardState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
impl Collection {
|
||||||
pub(crate) fn answer_hard(&mut self) {
|
pub(crate) fn answer_again(&mut self) -> PostAnswerState {
|
||||||
self.answer(|states| states.hard, Rating::Hard).unwrap()
|
self.answer(|states| states.again, Rating::Again).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn answer_good(&mut self) {
|
#[allow(dead_code)]
|
||||||
self.answer(|states| states.good, Rating::Good).unwrap()
|
pub(crate) fn answer_hard(&mut self) -> PostAnswerState {
|
||||||
}
|
self.answer(|states| states.hard, Rating::Hard).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn answer_easy(&mut self) {
|
pub(crate) fn answer_good(&mut self) -> PostAnswerState {
|
||||||
self.answer(|states| states.easy, Rating::Easy).unwrap()
|
self.answer(|states| states.good, Rating::Good).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn answer<F>(&mut self, get_state: F, rating: Rating) -> Result<()>
|
pub(crate) fn answer_easy(&mut self) -> PostAnswerState {
|
||||||
where
|
self.answer(|states| states.easy, Rating::Easy).unwrap()
|
||||||
F: FnOnce(&NextCardStates) -> CardState,
|
}
|
||||||
{
|
|
||||||
let queued = self.next_card()?.unwrap();
|
fn answer<F>(&mut self, get_state: F, rating: Rating) -> Result<PostAnswerState>
|
||||||
self.answer_card(&CardAnswer {
|
where
|
||||||
card_id: queued.card.id,
|
F: FnOnce(&NextCardStates) -> CardState,
|
||||||
current_state: queued.next_states.current,
|
{
|
||||||
new_state: get_state(&queued.next_states),
|
let queued = self.next_card()?.unwrap();
|
||||||
rating,
|
let new_state = get_state(&queued.next_states);
|
||||||
answered_at: TimestampMillis::now(),
|
self.answer_card(&CardAnswer {
|
||||||
milliseconds_taken: 0,
|
card_id: queued.card.id,
|
||||||
})?;
|
current_state: queued.next_states.current,
|
||||||
Ok(())
|
new_state,
|
||||||
|
rating,
|
||||||
|
answered_at: TimestampMillis::now(),
|
||||||
|
milliseconds_taken: 0,
|
||||||
|
})?;
|
||||||
|
Ok(PostAnswerState {
|
||||||
|
card_id: queued.card.id,
|
||||||
|
new_state,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,3 +428,128 @@ fn get_fuzz_seed(card: &Card) -> Option<u64> {
|
||||||
Some((card.id.0 as u64).wrapping_add(card.reps as u64))
|
Some((card.id.0 as u64).wrapping_add(card.reps as u64))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::{card::CardType, collection::open_test_collection};
|
||||||
|
|
||||||
|
fn current_state(col: &mut Collection, card_id: CardId) -> CardState {
|
||||||
|
col.get_next_card_states(card_id).unwrap().current
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the 'current' state for a card matches the
|
||||||
|
// state we applied to it
|
||||||
|
#[test]
|
||||||
|
fn state_application() -> Result<()> {
|
||||||
|
let mut col = open_test_collection();
|
||||||
|
let nt = col.get_notetype_by_name("Basic")?.unwrap();
|
||||||
|
let mut note = nt.new_note();
|
||||||
|
col.add_note(&mut note, DeckId(1))?;
|
||||||
|
|
||||||
|
// new->learning
|
||||||
|
let post_answer = col.answer_again();
|
||||||
|
assert_eq!(
|
||||||
|
post_answer.new_state,
|
||||||
|
current_state(&mut col, post_answer.card_id)
|
||||||
|
);
|
||||||
|
let card = col.storage.get_card(post_answer.card_id)?.unwrap();
|
||||||
|
assert_eq!(card.queue, CardQueue::Learn);
|
||||||
|
assert_eq!(card.remaining_steps, 2);
|
||||||
|
|
||||||
|
// learning step
|
||||||
|
col.storage.db.execute_batch("update cards set due=0")?;
|
||||||
|
col.clear_study_queues();
|
||||||
|
let post_answer = col.answer_good();
|
||||||
|
assert_eq!(
|
||||||
|
post_answer.new_state,
|
||||||
|
current_state(&mut col, post_answer.card_id)
|
||||||
|
);
|
||||||
|
let card = col.storage.get_card(post_answer.card_id)?.unwrap();
|
||||||
|
assert_eq!(card.queue, CardQueue::Learn);
|
||||||
|
assert_eq!(card.remaining_steps, 1);
|
||||||
|
|
||||||
|
// graduation
|
||||||
|
col.storage.db.execute_batch("update cards set due=0")?;
|
||||||
|
col.clear_study_queues();
|
||||||
|
let mut post_answer = col.answer_good();
|
||||||
|
// compensate for shifting the due date
|
||||||
|
if let CardState::Normal(NormalState::Review(state)) = &mut post_answer.new_state {
|
||||||
|
state.elapsed_days = 1;
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
post_answer.new_state,
|
||||||
|
current_state(&mut col, post_answer.card_id)
|
||||||
|
);
|
||||||
|
let card = col.storage.get_card(post_answer.card_id)?.unwrap();
|
||||||
|
assert_eq!(card.queue, CardQueue::Review);
|
||||||
|
assert_eq!(card.interval, 1);
|
||||||
|
assert_eq!(card.remaining_steps, 0);
|
||||||
|
|
||||||
|
// answering a review card again; easy boost
|
||||||
|
col.storage.db.execute_batch("update cards set due=0")?;
|
||||||
|
col.clear_study_queues();
|
||||||
|
let mut post_answer = col.answer_easy();
|
||||||
|
if let CardState::Normal(NormalState::Review(state)) = &mut post_answer.new_state {
|
||||||
|
state.elapsed_days = 4;
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
post_answer.new_state,
|
||||||
|
current_state(&mut col, post_answer.card_id)
|
||||||
|
);
|
||||||
|
let card = col.storage.get_card(post_answer.card_id)?.unwrap();
|
||||||
|
assert_eq!(card.queue, CardQueue::Review);
|
||||||
|
assert_eq!(card.interval, 4);
|
||||||
|
assert_eq!(card.ease_factor, 2650);
|
||||||
|
|
||||||
|
// lapsing it
|
||||||
|
col.storage.db.execute_batch("update cards set due=0")?;
|
||||||
|
col.clear_study_queues();
|
||||||
|
let mut post_answer = col.answer_again();
|
||||||
|
if let CardState::Normal(NormalState::Relearning(state)) = &mut post_answer.new_state {
|
||||||
|
state.review.elapsed_days = 1;
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
post_answer.new_state,
|
||||||
|
current_state(&mut col, post_answer.card_id)
|
||||||
|
);
|
||||||
|
let card = col.storage.get_card(post_answer.card_id)?.unwrap();
|
||||||
|
assert_eq!(card.queue, CardQueue::Learn);
|
||||||
|
assert_eq!(card.ctype, CardType::Relearn);
|
||||||
|
assert_eq!(card.interval, 1);
|
||||||
|
assert_eq!(card.ease_factor, 2450);
|
||||||
|
assert_eq!(card.lapses, 1);
|
||||||
|
|
||||||
|
// failed in relearning
|
||||||
|
col.storage.db.execute_batch("update cards set due=0")?;
|
||||||
|
col.clear_study_queues();
|
||||||
|
let mut post_answer = col.answer_again();
|
||||||
|
if let CardState::Normal(NormalState::Relearning(state)) = &mut post_answer.new_state {
|
||||||
|
state.review.elapsed_days = 1;
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
post_answer.new_state,
|
||||||
|
current_state(&mut col, post_answer.card_id)
|
||||||
|
);
|
||||||
|
let card = col.storage.get_card(post_answer.card_id)?.unwrap();
|
||||||
|
assert_eq!(card.queue, CardQueue::Learn);
|
||||||
|
assert_eq!(card.lapses, 1);
|
||||||
|
|
||||||
|
// re-graduating
|
||||||
|
col.storage.db.execute_batch("update cards set due=0")?;
|
||||||
|
col.clear_study_queues();
|
||||||
|
let mut post_answer = col.answer_good();
|
||||||
|
if let CardState::Normal(NormalState::Review(state)) = &mut post_answer.new_state {
|
||||||
|
state.elapsed_days = 1;
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
post_answer.new_state,
|
||||||
|
current_state(&mut col, post_answer.card_id)
|
||||||
|
);
|
||||||
|
let card = col.storage.get_card(post_answer.card_id)?.unwrap();
|
||||||
|
assert_eq!(card.queue, CardQueue::Review);
|
||||||
|
assert_eq!(card.interval, 1);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ impl CardStateUpdater {
|
||||||
self.card.remaining_steps = next.learning.remaining_steps;
|
self.card.remaining_steps = next.learning.remaining_steps;
|
||||||
self.card.ctype = CardType::Relearn;
|
self.card.ctype = CardType::Relearn;
|
||||||
self.card.lapses = next.review.lapses;
|
self.card.lapses = next.review.lapses;
|
||||||
|
self.card.ease_factor = (next.review.ease_factor * 1000.0).round() as u16;
|
||||||
|
|
||||||
let interval = next
|
let interval = next
|
||||||
.interval_kind()
|
.interval_kind()
|
||||||
|
|
|
@ -19,6 +19,7 @@ impl CardStateUpdater {
|
||||||
self.card.due = (self.timing.days_elapsed + next.scheduled_days) as i32;
|
self.card.due = (self.timing.days_elapsed + next.scheduled_days) as i32;
|
||||||
self.card.ease_factor = (next.ease_factor * 1000.0).round() as u16;
|
self.card.ease_factor = (next.ease_factor * 1000.0).round() as u16;
|
||||||
self.card.lapses = next.lapses;
|
self.card.lapses = next.lapses;
|
||||||
|
self.card.remaining_steps = 0;
|
||||||
|
|
||||||
RevlogEntryPartial::maybe_new(
|
RevlogEntryPartial::maybe_new(
|
||||||
current,
|
current,
|
||||||
|
|
Loading…
Reference in a new issue