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-descending-intervals = Descending intervals
|
||||||
deck-config-sort-order-ascending-ease = Ascending ease
|
deck-config-sort-order-ascending-ease = Ascending ease
|
||||||
deck-config-sort-order-descending-ease = Descending 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 =
|
deck-config-display-order-will-use-current-deck =
|
||||||
Anki will use the display order from the deck you
|
Anki will use the display order from the deck you
|
||||||
select to study, and not any subdecks it may have.
|
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_INTERVALS_DESCENDING = 4;
|
||||||
REVIEW_CARD_ORDER_EASE_ASCENDING = 5;
|
REVIEW_CARD_ORDER_EASE_ASCENDING = 5;
|
||||||
REVIEW_CARD_ORDER_EASE_DESCENDING = 6;
|
REVIEW_CARD_ORDER_EASE_DESCENDING = 6;
|
||||||
|
REVIEW_CARD_ORDER_RELATIVE_OVERDUENESS = 7;
|
||||||
}
|
}
|
||||||
enum ReviewMix {
|
enum ReviewMix {
|
||||||
REVIEW_MIX_MIX_WITH_REVIEWS = 0;
|
REVIEW_MIX_MIX_WITH_REVIEWS = 0;
|
||||||
|
|
|
@ -265,6 +265,7 @@ mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
backend_proto::deck_config::config::{NewCardGatherPriority, NewCardSortOrder},
|
backend_proto::deck_config::config::{NewCardGatherPriority, NewCardSortOrder},
|
||||||
|
card::{CardQueue, CardType},
|
||||||
collection::open_test_collection,
|
collection::open_test_collection,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -296,10 +297,29 @@ mod test {
|
||||||
})
|
})
|
||||||
.collect()
|
.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]
|
#[test]
|
||||||
fn queue_building() -> Result<()> {
|
fn new_queue_building() -> Result<()> {
|
||||||
let mut col = open_test_collection();
|
let mut col = open_test_collection();
|
||||||
col.set_config_bool(BoolKey::Sched2021, true, false)?;
|
col.set_config_bool(BoolKey::Sched2021, true, false)?;
|
||||||
|
|
||||||
|
@ -366,4 +386,52 @@ mod test {
|
||||||
|
|
||||||
Ok(())
|
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 data;
|
||||||
pub(crate) mod filtered;
|
pub(crate) mod filtered;
|
||||||
|
|
||||||
use std::{collections::HashSet, convert::TryFrom, result};
|
use std::{collections::HashSet, convert::TryFrom, fmt, result};
|
||||||
|
|
||||||
use rusqlite::{
|
use rusqlite::{
|
||||||
named_params, params,
|
named_params, params,
|
||||||
|
@ -216,7 +216,7 @@ impl super::SqliteStorage {
|
||||||
where
|
where
|
||||||
F: FnMut(DueCard) -> bool,
|
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!(
|
let mut stmt = self.db.prepare_cached(&format!(
|
||||||
"{} order by {}",
|
"{} order by {}",
|
||||||
include_str!("due_cards.sql"),
|
include_str!("due_cards.sql"),
|
||||||
|
@ -612,11 +612,13 @@ enum ReviewOrderSubclause {
|
||||||
IntervalsDescending,
|
IntervalsDescending,
|
||||||
EaseAscending,
|
EaseAscending,
|
||||||
EaseDescending,
|
EaseDescending,
|
||||||
|
RelativeOverdueness { today: u32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReviewOrderSubclause {
|
impl fmt::Display for ReviewOrderSubclause {
|
||||||
fn to_str(self) -> &'static str {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
let temp_string;
|
||||||
|
let clause = match self {
|
||||||
ReviewOrderSubclause::Day => "due",
|
ReviewOrderSubclause::Day => "due",
|
||||||
ReviewOrderSubclause::Deck => "(select rowid from active_decks ad where ad.id = did)",
|
ReviewOrderSubclause::Deck => "(select rowid from active_decks ad where ad.id = did)",
|
||||||
ReviewOrderSubclause::Random => "fnvhash(id, mod)",
|
ReviewOrderSubclause::Random => "fnvhash(id, mod)",
|
||||||
|
@ -624,11 +626,16 @@ impl ReviewOrderSubclause {
|
||||||
ReviewOrderSubclause::IntervalsDescending => "ivl desc",
|
ReviewOrderSubclause::IntervalsDescending => "ivl desc",
|
||||||
ReviewOrderSubclause::EaseAscending => "factor asc",
|
ReviewOrderSubclause::EaseAscending => "factor asc",
|
||||||
ReviewOrderSubclause::EaseDescending => "factor desc",
|
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 {
|
let mut subclauses = match order {
|
||||||
ReviewCardOrder::Day => vec![ReviewOrderSubclause::Day],
|
ReviewCardOrder::Day => vec![ReviewOrderSubclause::Day],
|
||||||
ReviewCardOrder::DayThenDeck => vec![ReviewOrderSubclause::Day, ReviewOrderSubclause::Deck],
|
ReviewCardOrder::DayThenDeck => vec![ReviewOrderSubclause::Day, ReviewOrderSubclause::Deck],
|
||||||
|
@ -637,12 +644,15 @@ fn review_order_sql(order: ReviewCardOrder) -> String {
|
||||||
ReviewCardOrder::IntervalsDescending => vec![ReviewOrderSubclause::IntervalsDescending],
|
ReviewCardOrder::IntervalsDescending => vec![ReviewOrderSubclause::IntervalsDescending],
|
||||||
ReviewCardOrder::EaseAscending => vec![ReviewOrderSubclause::EaseAscending],
|
ReviewCardOrder::EaseAscending => vec![ReviewOrderSubclause::EaseAscending],
|
||||||
ReviewCardOrder::EaseDescending => vec![ReviewOrderSubclause::EaseDescending],
|
ReviewCardOrder::EaseDescending => vec![ReviewOrderSubclause::EaseDescending],
|
||||||
|
ReviewCardOrder::RelativeOverdueness => {
|
||||||
|
vec![ReviewOrderSubclause::RelativeOverdueness { today }]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
subclauses.push(ReviewOrderSubclause::Random);
|
subclauses.push(ReviewOrderSubclause::Random);
|
||||||
|
|
||||||
let v: Vec<_> = subclauses
|
let v: Vec<_> = subclauses
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(ReviewOrderSubclause::to_str)
|
.map(ReviewOrderSubclause::to_string)
|
||||||
.collect();
|
.collect();
|
||||||
v.join(", ")
|
v.join(", ")
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
tr.deckConfigSortOrderDescendingIntervals(),
|
tr.deckConfigSortOrderDescendingIntervals(),
|
||||||
tr.deckConfigSortOrderAscendingEase(),
|
tr.deckConfigSortOrderAscendingEase(),
|
||||||
tr.deckConfigSortOrderDescendingEase(),
|
tr.deckConfigSortOrderDescendingEase(),
|
||||||
|
tr.deckConfigSortOrderRelativeOverdueness(),
|
||||||
];
|
];
|
||||||
|
|
||||||
const GatherOrder = DeckConfig.DeckConfig.Config.NewCardGatherPriority;
|
const GatherOrder = DeckConfig.DeckConfig.Config.NewCardGatherPriority;
|
||||||
|
|
Loading…
Reference in a new issue