mirror of
https://github.com/ankitects/anki.git
synced 2025-11-07 05:07:10 -05:00
* Feat/grade now * pass ci * fix from_queue * Refactor card answering to support from_queue flag - Add `from_queue` field to `CardAnswer` struct and proto message - Modify `answer_card_inner` to handle queue updates based on `from_queue` - Remove `grade_card` method and consolidate card answering logic - Update related test cases to set `from_queue` flag * fix current_changes() called when no op set * Optimize queue updates for batch card processing - Refactor `grade_now` to collect processed card IDs first - Add new `update_queues_for_processed_cards` method for efficient batch queue updates - Improve queue management by removing entries and updating counts in a single pass - Remove individual queue update method in favor of batch processing * pass ci * keep the same style * remove ineffective code * remove unused imports
139 lines
4.3 KiB
Rust
139 lines
4.3 KiB
Rust
// Copyright: Ankitects Pty Ltd and contributors
|
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
use super::CardStateUpdater;
|
|
use super::RevlogEntryPartial;
|
|
use crate::card::CardQueue;
|
|
use crate::scheduler::states::CardState;
|
|
use crate::scheduler::states::IntervalKind;
|
|
use crate::scheduler::states::PreviewState;
|
|
|
|
impl CardStateUpdater {
|
|
pub(super) fn apply_preview_state(
|
|
&mut self,
|
|
current: CardState,
|
|
next: PreviewState,
|
|
) -> RevlogEntryPartial {
|
|
let revlog = RevlogEntryPartial::new(current, next.into(), 0.0, self.secs_until_rollover());
|
|
if next.finished {
|
|
self.card.remove_from_filtered_deck_restoring_queue();
|
|
return revlog;
|
|
}
|
|
|
|
self.card.queue = CardQueue::PreviewRepeat;
|
|
|
|
let interval = next.interval_kind();
|
|
match interval {
|
|
IntervalKind::InSecs(secs) => {
|
|
self.card.due = self.fuzzed_next_learning_timestamp(secs);
|
|
}
|
|
IntervalKind::InDays(_days) => {
|
|
// unsupported
|
|
}
|
|
}
|
|
|
|
revlog
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
use crate::card::CardType;
|
|
use crate::prelude::*;
|
|
use crate::scheduler::answering::CardAnswer;
|
|
use crate::scheduler::answering::Rating;
|
|
use crate::scheduler::states::CardState;
|
|
use crate::scheduler::states::FilteredState;
|
|
use crate::timestamp::TimestampMillis;
|
|
|
|
#[test]
|
|
fn preview() -> Result<()> {
|
|
let mut col = Collection::new();
|
|
let mut c = Card {
|
|
deck_id: DeckId(1),
|
|
ctype: CardType::Learn,
|
|
queue: CardQueue::DayLearn,
|
|
remaining_steps: 2,
|
|
due: 123,
|
|
..Default::default()
|
|
};
|
|
col.add_card(&mut c)?;
|
|
|
|
// pull the card into a preview deck
|
|
let mut filtered_deck = Deck::new_filtered();
|
|
filtered_deck.filtered_mut()?.reschedule = false;
|
|
col.add_or_update_deck(&mut filtered_deck)?;
|
|
assert_eq!(col.rebuild_filtered_deck(filtered_deck.id)?.output, 1);
|
|
|
|
let next = col.get_scheduling_states(c.id)?;
|
|
assert!(matches!(
|
|
next.current,
|
|
CardState::Filtered(FilteredState::Preview(_))
|
|
));
|
|
// the exit state should have a 0 second interval, which will show up as (end)
|
|
assert!(matches!(
|
|
next.easy,
|
|
CardState::Filtered(FilteredState::Preview(PreviewState {
|
|
scheduled_secs: 0,
|
|
finished: true
|
|
}))
|
|
));
|
|
assert!(matches!(
|
|
next.good,
|
|
CardState::Filtered(FilteredState::Preview(PreviewState {
|
|
scheduled_secs: 0,
|
|
finished: true
|
|
}))
|
|
));
|
|
|
|
// use Again on the preview
|
|
col.answer_card(&mut CardAnswer {
|
|
card_id: c.id,
|
|
current_state: next.current,
|
|
new_state: next.again,
|
|
rating: Rating::Again,
|
|
answered_at: TimestampMillis::now(),
|
|
milliseconds_taken: 0,
|
|
custom_data: None,
|
|
from_queue: true,
|
|
})?;
|
|
|
|
c = col.storage.get_card(c.id)?.unwrap();
|
|
assert_eq!(c.queue, CardQueue::PreviewRepeat);
|
|
|
|
// hard
|
|
let next = col.get_scheduling_states(c.id)?;
|
|
col.answer_card(&mut CardAnswer {
|
|
card_id: c.id,
|
|
current_state: next.current,
|
|
new_state: next.hard,
|
|
rating: Rating::Hard,
|
|
answered_at: TimestampMillis::now(),
|
|
milliseconds_taken: 0,
|
|
custom_data: None,
|
|
from_queue: true,
|
|
})?;
|
|
c = col.storage.get_card(c.id)?.unwrap();
|
|
assert_eq!(c.queue, CardQueue::PreviewRepeat);
|
|
|
|
// and then it should return to its old state once good or easy selected,
|
|
// with the default filtered config
|
|
let next = col.get_scheduling_states(c.id)?;
|
|
col.answer_card(&mut CardAnswer {
|
|
card_id: c.id,
|
|
current_state: next.current,
|
|
new_state: next.good,
|
|
rating: Rating::Good,
|
|
answered_at: TimestampMillis::now(),
|
|
milliseconds_taken: 0,
|
|
custom_data: None,
|
|
from_queue: true,
|
|
})?;
|
|
c = col.storage.get_card(c.id)?.unwrap();
|
|
assert_eq!(c.queue, CardQueue::DayLearn);
|
|
assert_eq!(c.due, 123);
|
|
|
|
Ok(())
|
|
}
|
|
}
|