diff --git a/proto/backend.proto b/proto/backend.proto index 21516d2bd..ee954826e 100644 --- a/proto/backend.proto +++ b/proto/backend.proto @@ -528,6 +528,7 @@ message SchedTimingTodayOut { message DeckTreeIn { bool include_counts = 1; int64 top_deck_id = 2; + int32 today_delta = 3; } message DeckTreeNode { diff --git a/pylib/anki/decks.py b/pylib/anki/decks.py index 60aa1fa44..05cf0d8a5 100644 --- a/pylib/anki/decks.py +++ b/pylib/anki/decks.py @@ -140,7 +140,9 @@ class DeckManager: return from_json_bytes(self.col.backend.new_deck_legacy(filtered)) def deck_tree(self) -> pb.DeckTreeNode: - return self.col.backend.deck_tree(include_counts=False, top_deck_id=0) + return self.col.backend.deck_tree( + include_counts=False, top_deck_id=0, today_delta=0 + ) @classmethod def find_deck_in_tree( diff --git a/pylib/anki/schedv2.py b/pylib/anki/schedv2.py index 6b6685fa8..d37848eb4 100644 --- a/pylib/anki/schedv2.py +++ b/pylib/anki/schedv2.py @@ -224,7 +224,9 @@ order by due""" def deck_due_tree(self, top_deck_id: int = 0) -> DeckTreeNode: """Returns a tree of decks with counts. If top_deck_id provided, counts are limited to that node.""" - return self.col.backend.deck_tree(include_counts=True, top_deck_id=top_deck_id) + return self.col.backend.deck_tree( + include_counts=True, top_deck_id=top_deck_id, today_delta=0 + ) # Getting the next card ########################################################################## diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs index 11fc2942b..bd83e2f73 100644 --- a/rslib/src/backend/mod.rs +++ b/rslib/src/backend/mod.rs @@ -476,7 +476,14 @@ impl BackendService for Backend { } else { None }; - self.with_col(|col| col.deck_tree(input.include_counts, lim)) + self.with_col(|col| { + let today = if input.include_counts { + Some(col.current_due_day(input.today_delta)?) + } else { + None + }; + col.deck_tree(today, lim) + }) } fn deck_tree_legacy(&mut self, _input: pb::Empty) -> BackendResult { diff --git a/rslib/src/decks/tree.rs b/rslib/src/decks/tree.rs index 677df6c4b..79932cb75 100644 --- a/rslib/src/decks/tree.rs +++ b/rslib/src/decks/tree.rs @@ -209,11 +209,16 @@ impl From for LegacyDueCounts { } impl Collection { - /// Get the deck tree, optionally populating it with due counts. + /// Get the deck tree. + /// If today is provided, due counts for the provided day will be populated. /// If top_deck_id is provided, only the node starting at the provided deck ID will /// have the counts populated. Currently the entire tree is returned in this case, but /// this may change in the future. - pub fn deck_tree(&mut self, counts: bool, top_deck_id: Option) -> Result { + pub fn deck_tree( + &mut self, + today: Option, + top_deck_id: Option, + ) -> Result { let names = self.storage.get_all_deck_names()?; let mut tree = deck_names_to_tree(names); @@ -224,12 +229,12 @@ impl Collection { .map(|d| (d.id, d)) .collect(); - add_collapsed_and_filtered(&mut tree, &decks_map, !counts); + add_collapsed_and_filtered(&mut tree, &decks_map, today.is_none()); if self.default_deck_is_empty()? { hide_default_deck(&mut tree); } - if counts { + if let Some(today) = today { let limit = top_deck_id.and_then(|did| { if let Some(deck) = decks_map.get(&did) { Some(deck.name.as_str()) @@ -238,7 +243,6 @@ impl Collection { } }); let counts = self.due_counts(limit)?; - let today = self.timing_today()?.days_elapsed; let dconf: HashMap<_, _> = self .storage .all_deck_config()? @@ -260,12 +264,14 @@ impl Collection { pub fn current_deck_tree(&mut self) -> Result> { let target = self.get_current_deck_id(); - let tree = self.deck_tree(true, Some(target))?; + let today = self.current_due_day(0)?; + let tree = self.deck_tree(Some(today), Some(target))?; Ok(get_subnode(tree, target)) } pub(crate) fn legacy_deck_tree(&mut self) -> Result { - let tree = self.deck_tree(true, None)?; + let today = self.current_due_day(0)?; + let tree = self.deck_tree(Some(today), None)?; Ok(LegacyDueCounts::from(tree)) } @@ -303,7 +309,7 @@ mod test { col.get_or_create_normal_deck("2::c::A")?; col.get_or_create_normal_deck("3")?; - let tree = col.deck_tree(false, None)?; + let tree = col.deck_tree(None, None)?; assert_eq!(tree.children.len(), 3); @@ -326,7 +332,7 @@ mod test { col.storage.remove_deck(col.get_deck_id("2")?.unwrap())?; col.storage.remove_deck(col.get_deck_id("2::3")?.unwrap())?; - let tree = col.deck_tree(false, None)?; + let tree = col.deck_tree(None, None)?; assert_eq!(tree.children.len(), 1); Ok(()) @@ -345,7 +351,7 @@ mod test { note.fields[0] = "{{c1::}} {{c2::}} {{c3::}} {{c4::}}".into(); col.add_note(&mut note, child_deck.id)?; - let tree = col.deck_tree(true, None)?; + let tree = col.deck_tree(Some(0), None)?; assert_eq!(tree.children[0].new_count, 4); assert_eq!(tree.children[0].children[0].new_count, 4); @@ -356,7 +362,7 @@ mod test { col.add_or_update_deck(&mut parent_deck)?; // with the default limit of 20, there should still be 4 due - let tree = col.deck_tree(true, None)?; + let tree = col.deck_tree(Some(0), None)?; assert_eq!(tree.children[0].new_count, 4); assert_eq!(tree.children[0].children[0].new_count, 4); @@ -365,7 +371,7 @@ mod test { conf.inner.new_per_day = 4; col.add_or_update_deck_config(&mut conf, false)?; - let tree = col.deck_tree(true, None)?; + let tree = col.deck_tree(Some(0), None)?; assert_eq!(tree.children[0].new_count, 3); assert_eq!(tree.children[0].children[0].new_count, 3); diff --git a/rslib/src/sched/mod.rs b/rslib/src/sched/mod.rs index dff0a9508..034734a46 100644 --- a/rslib/src/sched/mod.rs +++ b/rslib/src/sched/mod.rs @@ -18,6 +18,10 @@ impl Collection { self.timing_for_timestamp(TimestampSecs::now()) } + pub fn current_due_day(&mut self, delta: i32) -> Result { + Ok(((self.timing_today()?.days_elapsed as i32) + delta).max(0) as u32) + } + pub(crate) fn timing_for_timestamp(&mut self, now: TimestampSecs) -> Result { let local_offset = if self.server { self.get_local_mins_west()