mirror of
https://github.com/ankitects/anki.git
synced 2025-11-06 12:47:11 -05:00
Feat/Prioritize previously attempted cards in intraday learning queue
This commit is contained in:
parent
321e23acb2
commit
e97a3a15a7
6 changed files with 71 additions and 5 deletions
|
|
@ -38,6 +38,7 @@ pub(crate) struct DueCard {
|
|||
pub current_deck_id: DeckId,
|
||||
pub original_deck_id: DeckId,
|
||||
pub kind: DueCardKind,
|
||||
pub reps: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -87,6 +88,7 @@ impl From<DueCard> for LearningQueueEntry {
|
|||
due: TimestampSecs(c.due as i64),
|
||||
id: c.id,
|
||||
mtime: c.mtime,
|
||||
reps: c.reps,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -274,9 +276,21 @@ fn merge_new(
|
|||
}
|
||||
}
|
||||
|
||||
fn sort_learning(mut learning: Vec<DueCard>) -> VecDeque<LearningQueueEntry> {
|
||||
learning.sort_unstable_by(|a, b| a.due.cmp(&b.due));
|
||||
learning.into_iter().map(LearningQueueEntry::from).collect()
|
||||
fn sort_learning(learning: Vec<DueCard>) -> VecDeque<LearningQueueEntry> {
|
||||
// Prioritize intraday learning cards that were previously attempted
|
||||
// (reps > 0) before never-attempted cards (reps == 0). Preserve due-time
|
||||
// ordering within each group.
|
||||
let (mut previously_attempted, mut never_attempted): (Vec<DueCard>, Vec<DueCard>) =
|
||||
learning.into_iter().partition(|c| c.reps > 0);
|
||||
|
||||
previously_attempted.sort_unstable_by(|a, b| a.due.cmp(&b.due));
|
||||
never_attempted.sort_unstable_by(|a, b| a.due.cmp(&b.due));
|
||||
|
||||
previously_attempted
|
||||
.into_iter()
|
||||
.chain(never_attempted)
|
||||
.map(LearningQueueEntry::from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl Collection {
|
||||
|
|
@ -543,4 +557,49 @@ mod test {
|
|||
col.set_current_deck(child.id).unwrap();
|
||||
assert_eq!(col.card_queue_len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn intraday_learning_prioritizes_previously_attempted() -> Result<()> {
|
||||
let mut col = Collection::new();
|
||||
|
||||
// create two notes with one card each
|
||||
let nt = col.get_notetype_by_name("Basic")?.unwrap();
|
||||
let mut n1 = nt.new_note();
|
||||
n1.set_field(0, "one")?;
|
||||
col.add_note(&mut n1, DeckId(1))?;
|
||||
let mut n2 = nt.new_note();
|
||||
n2.set_field(0, "two")?;
|
||||
col.add_note(&mut n2, DeckId(1))?;
|
||||
|
||||
// fetch their cards and make them intraday learning cards
|
||||
let mut c1 = col.storage.get_card_by_ordinal(n1.id, 0)?.unwrap();
|
||||
let mut c2 = col.storage.get_card_by_ordinal(n2.id, 0)?.unwrap();
|
||||
|
||||
c1.queue = CardQueue::Learn;
|
||||
c2.queue = CardQueue::Learn;
|
||||
|
||||
// c1: never attempted, due earlier (0)
|
||||
c1.due = 0;
|
||||
c1.reps = 0;
|
||||
// c2: previously attempted, due later (600)
|
||||
c2.due = 600;
|
||||
c2.reps = 1;
|
||||
|
||||
let id1 = c1.id;
|
||||
let id2 = c2.id;
|
||||
|
||||
col.update_cards_maybe_undoable(vec![c1, c2], false)?;
|
||||
|
||||
// build queues and inspect intraday learning order
|
||||
let queues = col.build_queues(DeckId(1))?;
|
||||
let ids: Vec<_> = queues.intraday_learning.iter().map(|e| e.id).collect();
|
||||
|
||||
// ensure the previously attempted card appears before the never-attempted one
|
||||
assert!(
|
||||
ids.iter().position(|&id| id == id2).unwrap()
|
||||
< ids.iter().position(|&id| id == id1).unwrap()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ impl From<&Card> for QueueEntry {
|
|||
due: TimestampSecs(card.due as i64),
|
||||
id: card.id,
|
||||
mtime: card.mtime,
|
||||
reps: card.reps,
|
||||
});
|
||||
}
|
||||
CardQueue::New => MainQueueEntryKind::New,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ pub(crate) struct LearningQueueEntry {
|
|||
pub due: TimestampSecs,
|
||||
pub id: CardId,
|
||||
pub mtime: TimestampSecs,
|
||||
pub reps: u32,
|
||||
}
|
||||
|
||||
impl CardQueues {
|
||||
|
|
@ -71,6 +72,7 @@ impl CardQueues {
|
|||
due: TimestampSecs(card.due as i64),
|
||||
id: card.id,
|
||||
mtime: card.mtime,
|
||||
reps: card.reps,
|
||||
};
|
||||
|
||||
Some(self.requeue_learning_entry(entry))
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ SELECT id,
|
|||
cast(ivl AS integer),
|
||||
cast(mod AS integer),
|
||||
did,
|
||||
odid
|
||||
odid,
|
||||
reps
|
||||
FROM cards
|
||||
WHERE did IN (
|
||||
SELECT id
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ SELECT id,
|
|||
due,
|
||||
cast(mod AS integer),
|
||||
did,
|
||||
odid
|
||||
odid,
|
||||
reps
|
||||
FROM cards
|
||||
WHERE did IN (
|
||||
SELECT id
|
||||
|
|
|
|||
|
|
@ -267,6 +267,7 @@ impl super::SqliteStorage {
|
|||
mtime: row.get(3)?,
|
||||
current_deck_id: row.get(4)?,
|
||||
original_deck_id: row.get(5)?,
|
||||
reps: row.get(6)?,
|
||||
kind: DueCardKind::Learning,
|
||||
})
|
||||
}
|
||||
|
|
@ -306,6 +307,7 @@ impl super::SqliteStorage {
|
|||
mtime: row.get(4)?,
|
||||
current_deck_id: row.get(5)?,
|
||||
original_deck_id: row.get(6)?,
|
||||
reps: row.get(7)?,
|
||||
kind,
|
||||
})? {
|
||||
break;
|
||||
|
|
|
|||
Loading…
Reference in a new issue