mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
Add relative overdueness to review order (#1757)
* Add relative overdueness to review order * Add test for relative overdue
This commit is contained in:
parent
a7ad6aebba
commit
964f0a5763
5 changed files with 91 additions and 10 deletions
|
@ -170,6 +170,7 @@ deck-config-sort-order-ascending-intervals = Ascending intervals
|
|||
deck-config-sort-order-descending-intervals = Descending intervals
|
||||
deck-config-sort-order-ascending-ease = Ascending ease
|
||||
deck-config-sort-order-descending-ease = Descending ease
|
||||
deck-config-sort-order-relative-overdueness = Relative overdueness
|
||||
deck-config-display-order-will-use-current-deck =
|
||||
Anki will use the display order from the deck you
|
||||
select to study, and not any subdecks it may have.
|
||||
|
|
|
@ -68,6 +68,7 @@ message DeckConfig {
|
|||
REVIEW_CARD_ORDER_INTERVALS_DESCENDING = 4;
|
||||
REVIEW_CARD_ORDER_EASE_ASCENDING = 5;
|
||||
REVIEW_CARD_ORDER_EASE_DESCENDING = 6;
|
||||
REVIEW_CARD_ORDER_RELATIVE_OVERDUENESS = 7;
|
||||
}
|
||||
enum ReviewMix {
|
||||
REVIEW_MIX_MIX_WITH_REVIEWS = 0;
|
||||
|
|
|
@ -265,6 +265,7 @@ mod test {
|
|||
use super::*;
|
||||
use crate::{
|
||||
backend_proto::deck_config::config::{NewCardGatherPriority, NewCardSortOrder},
|
||||
card::{CardQueue, CardType},
|
||||
collection::open_test_collection,
|
||||
};
|
||||
|
||||
|
@ -296,10 +297,29 @@ mod test {
|
|||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn set_deck_review_order(&mut self, deck: &mut Deck, order: ReviewCardOrder) {
|
||||
let mut conf = DeckConfig::default();
|
||||
conf.inner.review_order = order as i32;
|
||||
self.add_or_update_deck_config(&mut conf).unwrap();
|
||||
deck.normal_mut().unwrap().config_id = conf.id.0;
|
||||
self.add_or_update_deck(deck).unwrap();
|
||||
}
|
||||
|
||||
fn queue_as_due_and_ivl(&mut self, deck_id: DeckId) -> Vec<(i32, u32)> {
|
||||
self.build_queues(deck_id)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|entry| {
|
||||
let card = self.storage.get_card(entry.card_id()).unwrap().unwrap();
|
||||
(card.due, card.interval)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn queue_building() -> Result<()> {
|
||||
fn new_queue_building() -> Result<()> {
|
||||
let mut col = open_test_collection();
|
||||
col.set_config_bool(BoolKey::Sched2021, true, false)?;
|
||||
|
||||
|
@ -366,4 +386,52 @@ mod test {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn review_queue_building() -> Result<()> {
|
||||
let mut col = open_test_collection();
|
||||
col.set_config_bool(BoolKey::Sched2021, true, false)?;
|
||||
|
||||
let mut deck = col.get_or_create_normal_deck("Default").unwrap();
|
||||
let nt = col.get_notetype_by_name("Basic")?.unwrap();
|
||||
let mut cards = vec![];
|
||||
|
||||
// relative overdueness
|
||||
let expected_queue = vec![
|
||||
(-150, 1),
|
||||
(-100, 1),
|
||||
(-50, 1),
|
||||
(-150, 5),
|
||||
(-100, 5),
|
||||
(-50, 5),
|
||||
(-150, 20),
|
||||
(-150, 20),
|
||||
(-100, 20),
|
||||
(-50, 20),
|
||||
(-150, 100),
|
||||
(-100, 100),
|
||||
(-50, 100),
|
||||
(0, 1),
|
||||
(0, 5),
|
||||
(0, 20),
|
||||
(0, 100),
|
||||
];
|
||||
for t in expected_queue.iter() {
|
||||
let mut note = nt.new_note();
|
||||
note.set_field(0, "foo")?;
|
||||
note.id.0 = 0;
|
||||
col.add_note(&mut note, deck.id)?;
|
||||
let mut card = col.storage.get_card_by_ordinal(note.id, 0)?.unwrap();
|
||||
card.interval = t.1;
|
||||
card.due = t.0;
|
||||
card.ctype = CardType::Review;
|
||||
card.queue = CardQueue::Review;
|
||||
cards.push(card);
|
||||
}
|
||||
col.update_cards_maybe_undoable(cards, false)?;
|
||||
col.set_deck_review_order(&mut deck, ReviewCardOrder::RelativeOverdueness);
|
||||
assert_eq!(col.queue_as_due_and_ivl(deck.id), expected_queue);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
pub(crate) mod data;
|
||||
pub(crate) mod filtered;
|
||||
|
||||
use std::{collections::HashSet, convert::TryFrom, result};
|
||||
use std::{collections::HashSet, convert::TryFrom, fmt, result};
|
||||
|
||||
use rusqlite::{
|
||||
named_params, params,
|
||||
|
@ -216,7 +216,7 @@ impl super::SqliteStorage {
|
|||
where
|
||||
F: FnMut(DueCard) -> bool,
|
||||
{
|
||||
let order_clause = review_order_sql(order);
|
||||
let order_clause = review_order_sql(order, day_cutoff);
|
||||
let mut stmt = self.db.prepare_cached(&format!(
|
||||
"{} order by {}",
|
||||
include_str!("due_cards.sql"),
|
||||
|
@ -612,11 +612,13 @@ enum ReviewOrderSubclause {
|
|||
IntervalsDescending,
|
||||
EaseAscending,
|
||||
EaseDescending,
|
||||
RelativeOverdueness { today: u32 },
|
||||
}
|
||||
|
||||
impl ReviewOrderSubclause {
|
||||
fn to_str(self) -> &'static str {
|
||||
match self {
|
||||
impl fmt::Display for ReviewOrderSubclause {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let temp_string;
|
||||
let clause = match self {
|
||||
ReviewOrderSubclause::Day => "due",
|
||||
ReviewOrderSubclause::Deck => "(select rowid from active_decks ad where ad.id = did)",
|
||||
ReviewOrderSubclause::Random => "fnvhash(id, mod)",
|
||||
|
@ -624,11 +626,16 @@ impl ReviewOrderSubclause {
|
|||
ReviewOrderSubclause::IntervalsDescending => "ivl desc",
|
||||
ReviewOrderSubclause::EaseAscending => "factor asc",
|
||||
ReviewOrderSubclause::EaseDescending => "factor desc",
|
||||
}
|
||||
ReviewOrderSubclause::RelativeOverdueness { today } => {
|
||||
temp_string = format!("ivl / cast({today}-due+0.001 as real)", today = today);
|
||||
&temp_string
|
||||
}
|
||||
};
|
||||
write!(f, "{}", clause)
|
||||
}
|
||||
}
|
||||
|
||||
fn review_order_sql(order: ReviewCardOrder) -> String {
|
||||
fn review_order_sql(order: ReviewCardOrder, today: u32) -> String {
|
||||
let mut subclauses = match order {
|
||||
ReviewCardOrder::Day => vec![ReviewOrderSubclause::Day],
|
||||
ReviewCardOrder::DayThenDeck => vec![ReviewOrderSubclause::Day, ReviewOrderSubclause::Deck],
|
||||
|
@ -637,12 +644,15 @@ fn review_order_sql(order: ReviewCardOrder) -> String {
|
|||
ReviewCardOrder::IntervalsDescending => vec![ReviewOrderSubclause::IntervalsDescending],
|
||||
ReviewCardOrder::EaseAscending => vec![ReviewOrderSubclause::EaseAscending],
|
||||
ReviewCardOrder::EaseDescending => vec![ReviewOrderSubclause::EaseDescending],
|
||||
ReviewCardOrder::RelativeOverdueness => {
|
||||
vec![ReviewOrderSubclause::RelativeOverdueness { today }]
|
||||
}
|
||||
};
|
||||
subclauses.push(ReviewOrderSubclause::Random);
|
||||
|
||||
let v: Vec<_> = subclauses
|
||||
.into_iter()
|
||||
.map(ReviewOrderSubclause::to_str)
|
||||
.iter()
|
||||
.map(ReviewOrderSubclause::to_string)
|
||||
.collect();
|
||||
v.join(", ")
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
tr.deckConfigSortOrderDescendingIntervals(),
|
||||
tr.deckConfigSortOrderAscendingEase(),
|
||||
tr.deckConfigSortOrderDescendingEase(),
|
||||
tr.deckConfigSortOrderRelativeOverdueness(),
|
||||
];
|
||||
|
||||
const GatherOrder = DeckConfig.DeckConfig.Config.NewCardGatherPriority;
|
||||
|
|
Loading…
Reference in a new issue