Anki/rslib/src/scheduler/queue/builder/sorting.rs
RumovZ cbe37d7095 V3 parent limits (#1638)
* avoid repinning Rust deps by default

* add id_tree dependency

* Respect intermediate child limits in v3

* Test new behaviour of v3 counts

* Rework v3 queue building to respect parent limits

* Add missing did field to SQL query

* Fix `LimitTreeMap::is_exhausted()`

* Rework tree building logic

https://github.com/ankitects/anki/pull/1638#discussion_r798328734

* Add timer for build_queues()

* `is_exhausted()` -> `limit_reached()`

* Move context and limits into `QueueBuilder`

This allows for moving more logic into QueueBuilder, so less passing
around of arguments. Unfortunately, some tests will require additional
work to set up.

* Fix stop condition in new_cards_by_position

* Fix order gather order of new cards by deck

* Add scheduler/queue/builder/burying.rs

* Fix bad tree due to unsorted child decks

* Fix comment

* Fix `cap_new_to_review_rec()`

* Add test for new card gathering

* Always sort `child_decks()`

* Fix deck removal in `cap_new_to_review_rec()`

* Fix sibling ordering in new card gathering

* Remove limits for deck total count with children

* Add random gather order

* Remove bad sibling order handling

All routines ensure ascending order now.
Also do some other minor refactoring.

* Remove queue truncating

All routines stop now as soon as the root limit is reached.

* Move deck fetching into `QueueBuilder::new()`

* Rework new card gather and sort options

https://github.com/ankitects/anki/pull/1638#issuecomment-1032173013

* Disable new sort order choices ...

depending on set gather order.

* Use enum instead of numbers

* Ensure valid sort order setting

* Update new gather and sort order tooltips

* Warn about random insertion order with v3

* Revert "Add timer for build_queues()"

This reverts commit c9f5fc6ebe.

* Update rslib/src/storage/card/mod.rs (dae)

* minor wording tweaks to the tooltips (dae)

+ move legacy strings to bottom
+ consistent capitalization (our leech action still needs fixing,
but that will require introducing a new 'suspend card' string as the
existing one is used elsewhere as well)
2022-02-10 09:55:43 +10:00

78 lines
2.5 KiB
Rust

// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
use std::{cmp::Ordering, hash::Hasher};
use fnv::FnvHasher;
use super::{NewCard, NewCardSortOrder, QueueBuilder};
impl QueueBuilder {
pub(super) fn sort_new(&mut self) {
match self.context.sort_options.new_order {
// preserve gather order
NewCardSortOrder::NoSort => (),
NewCardSortOrder::Template => {
// stable sort to preserve gather order
self.new
.sort_by(|a, b| a.template_index.cmp(&b.template_index))
}
NewCardSortOrder::TemplateThenRandom => {
self.hash_new_cards_by_id();
self.new.sort_unstable_by(cmp_template_then_hash);
}
NewCardSortOrder::RandomNoteThenTemplate => {
self.hash_new_cards_by_note_id();
self.new.sort_unstable_by(cmp_hash_then_template);
}
NewCardSortOrder::RandomCard => {
self.hash_new_cards_by_id();
self.new.sort_unstable_by(cmp_hash)
}
}
}
fn hash_new_cards_by_id(&mut self) {
self.new
.iter_mut()
.for_each(|card| card.hash_id_with_salt(self.context.timing.days_elapsed as i64));
}
fn hash_new_cards_by_note_id(&mut self) {
self.new
.iter_mut()
.for_each(|card| card.hash_note_id_with_salt(self.context.timing.days_elapsed as i64));
}
}
fn cmp_hash(a: &NewCard, b: &NewCard) -> Ordering {
a.hash.cmp(&b.hash)
}
fn cmp_template_then_hash(a: &NewCard, b: &NewCard) -> Ordering {
(a.template_index, a.hash).cmp(&(b.template_index, b.hash))
}
fn cmp_hash_then_template(a: &NewCard, b: &NewCard) -> Ordering {
(a.hash, a.template_index).cmp(&(b.hash, b.template_index))
}
// We sort based on a hash so that if the queue is rebuilt, remaining
// cards come back in the same approximate order (mixing + due learning cards
// may still result in a different card)
impl NewCard {
fn hash_id_with_salt(&mut self, salt: i64) {
let mut hasher = FnvHasher::default();
hasher.write_i64(self.id.0);
hasher.write_i64(salt);
self.hash = hasher.finish();
}
fn hash_note_id_with_salt(&mut self, salt: i64) {
let mut hasher = FnvHasher::default();
hasher.write_i64(self.note_id.0);
hasher.write_i64(salt);
self.hash = hasher.finish();
}
}