diff --git a/proto/anki/decks.proto b/proto/anki/decks.proto index 250e15e4c..21973cce3 100644 --- a/proto/anki/decks.proto +++ b/proto/anki/decks.proto @@ -134,14 +134,20 @@ message DeckTreeNode { uint32 level = 4; bool collapsed = 5; - // counts after limits applied + // counts after adding children+applying limits uint32 review_count = 6; uint32 learn_count = 7; uint32 new_count = 8; - // due counts without limits applied - uint32 intraday_learning_total = 9; - uint32 interday_learning_total = 10; + // card counts without children or limits applied + uint32 intraday_learning = 9; + uint32 interday_learning_uncapped = 10; + uint32 new_uncapped = 11; + uint32 review_uncapped = 12; + uint32 total_in_deck = 13; + + uint32 total_including_children = 14; + bool filtered = 16; // low index so key can be packed into a byte, but at bottom diff --git a/rslib/src/decks/counts.rs b/rslib/src/decks/counts.rs index db6ad7002..c2b84ca10 100644 --- a/rslib/src/decks/counts.rs +++ b/rslib/src/decks/counts.rs @@ -13,6 +13,7 @@ pub(crate) struct DueCounts { pub intraday_learning: u32, pub interday_learning: u32, + pub total_cards: u32, } impl Deck { diff --git a/rslib/src/decks/tree.rs b/rslib/src/decks/tree.rs index c41043cf9..3f32304b2 100644 --- a/rslib/src/decks/tree.rs +++ b/rslib/src/decks/tree.rs @@ -86,8 +86,11 @@ fn add_counts(node: &mut DeckTreeNode, counts: &HashMap) { node.new_count = counts.new; node.review_count = counts.review; node.learn_count = counts.learning; - node.intraday_learning_total = counts.intraday_learning; - node.interday_learning_total = counts.interday_learning; + node.intraday_learning = counts.intraday_learning; + node.interday_learning_uncapped = counts.interday_learning; + node.new_uncapped = counts.new; + node.review_uncapped = counts.review; + node.total_in_deck = counts.total_cards; } for child in &mut node.children { add_counts(child, counts); @@ -95,7 +98,7 @@ fn add_counts(node: &mut DeckTreeNode, counts: &HashMap) { } /// Apply parent limits to children, and add child counts to parents. -fn apply_limits_v1( +fn sum_counts_and_apply_limits_v1( node: &mut DeckTreeNode, limits: &HashMap, parent_limits: RemainingLimits, @@ -110,7 +113,7 @@ fn apply_limits_v1( let mut child_new_total = 0; let mut child_rev_total = 0; for child in &mut node.children { - apply_limits_v1(child, limits, remaining); + sum_counts_and_apply_limits_v1(child, limits, remaining); child_new_total += child.new_count; child_rev_total += child.review_count; // no limit on learning cards @@ -125,7 +128,7 @@ fn apply_limits_v1( /// Apply parent new limits to children, and add child counts to parents. Unlike /// v1, reviews are not capped by their parents, and we /// return the uncapped review amount to add to the parent. -fn apply_limits_v2( +fn sum_counts_and_apply_limits_v2( node: &mut DeckTreeNode, limits: &HashMap, parent_limits: RemainingLimits, @@ -141,7 +144,7 @@ fn apply_limits_v2( let mut child_new_total = 0; let mut child_rev_total = 0; for child in &mut node.children { - child_rev_total += apply_limits_v2(child, limits, remaining); + child_rev_total += sum_counts_and_apply_limits_v2(child, limits, remaining); child_new_total += child.new_count; // no limit on learning cards node.learn_count += child.learn_count; @@ -161,6 +164,7 @@ struct NodeCountsV3 { review: u32, intraday_learning: u32, interday_learning: u32, + total: u32, } impl NodeCountsV3 { @@ -183,13 +187,14 @@ impl AddAssign for NodeCountsV3 { self.review += rhs.review; self.intraday_learning += rhs.intraday_learning; self.interday_learning += rhs.interday_learning; + self.total += rhs.total; } } /// Adjust new, review and learning counts based on the daily limits. /// As part of this process, the separate interday and intraday learning /// counts are combined after the limits have been applied. -fn apply_limits_v3( +fn sum_counts_and_apply_limits_v3( node: &mut DeckTreeNode, limits: &HashMap, ) -> NodeCountsV3 { @@ -202,19 +207,21 @@ fn apply_limits_v3( let this_node_uncapped = NodeCountsV3 { new: node.new_count, review: node.review_count, - intraday_learning: node.intraday_learning_total, - interday_learning: node.interday_learning_total, + intraday_learning: node.intraday_learning, + interday_learning: node.interday_learning_uncapped, + total: node.total_in_deck, }; let mut individually_capped_total = this_node_uncapped.capped(&remaining); // and add the capped values from child decks for child in &mut node.children { - individually_capped_total += apply_limits_v3(child, limits); + individually_capped_total += sum_counts_and_apply_limits_v3(child, limits); } + node.total_including_children = individually_capped_total.total; - // We already have a tally of the current deck's capped cards+its child decks' + // We already have a sum of the current deck's capped cards+its child decks' // capped cards, which we'll return to the parent. But because clicking on a // given deck imposes that deck's limits on the total number of cards shown, - // the tally we'll display needs to be capped again by the limits of the current + // the sum we'll display needs to be capped again by the limits of the current // deck. let total_constrained_by_current_deck = individually_capped_total.capped(&remaining); node.new_count = total_constrained_by_current_deck.new; @@ -310,12 +317,12 @@ impl Collection { let limits = remaining_limits_map(decks_map.values(), &dconf, days_elapsed); if sched_ver == SchedulerVersion::V2 { if v3 { - apply_limits_v3(&mut tree, &limits); + sum_counts_and_apply_limits_v3(&mut tree, &limits); } else { - apply_limits_v2(&mut tree, &limits, RemainingLimits::default()); + sum_counts_and_apply_limits_v2(&mut tree, &limits, RemainingLimits::default()); } } else { - apply_limits_v1(&mut tree, &limits, RemainingLimits::default()); + sum_counts_and_apply_limits_v1(&mut tree, &limits, RemainingLimits::default()); } } diff --git a/rslib/src/storage/deck/due_counts.sql b/rslib/src/storage/deck/due_counts.sql index 94e8aa33c..b1fac9781 100644 --- a/rslib/src/storage/deck/due_counts.sql +++ b/rslib/src/storage/deck/due_counts.sql @@ -1,5 +1,7 @@ SELECT did, + -- new sum(queue = :new_queue), + -- reviews sum( queue = :review_queue AND due <= :day_cutoff @@ -35,6 +37,7 @@ SELECT did, ) END ) - ) -FROM cards -WHERE queue >= 0 \ No newline at end of file + ), + -- total + COUNT(1) +FROM cards \ No newline at end of file diff --git a/rslib/src/storage/deck/mod.rs b/rslib/src/storage/deck/mod.rs index 0818f4f8e..bfcafafe9 100644 --- a/rslib/src/storage/deck/mod.rs +++ b/rslib/src/storage/deck/mod.rs @@ -44,6 +44,7 @@ fn row_to_due_counts(row: &Row) -> Result<(DeckId, DueCounts)> { let review = row.get(2)?; let interday_learning: u32 = row.get(3)?; let intraday_learning: u32 = row.get(4)?; + let total_cards: u32 = row.get(5)?; // used as-is in v1/v2; recalculated in v3 after limits are applied let learning = intraday_learning + interday_learning; Ok(( @@ -54,6 +55,7 @@ fn row_to_due_counts(row: &Row) -> Result<(DeckId, DueCounts)> { learning, intraday_learning, interday_learning, + total_cards, }, )) } @@ -293,7 +295,7 @@ impl SqliteStorage { }); sql = concat!( include_str!("due_counts.sql"), - " and did in (select id from decks where name = :top_deck ", + " where did in (select id from decks where name = :top_deck ", "or (name >= :prefix_start and name < :prefix_end)) group by did " ); } else {