Add relative overdueness to review order (#1757)

* Add relative overdueness to review order

* Add test for relative overdue
This commit is contained in:
Abdo 2022-04-09 06:20:09 +03:00 committed by GitHub
parent a7ad6aebba
commit 964f0a5763
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 91 additions and 10 deletions

View file

@ -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.

View file

@ -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;

View file

@ -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(())
}
}

View file

@ -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(", ")
}

View file

@ -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;