expose uncapped counts and total in tree node

In the future this information could be revealed on hover, to make it
clearer how limits are being applied.
This commit is contained in:
Damien Elmes 2021-08-22 15:29:13 +10:00
parent 8417cde5c9
commit dffa38c479
5 changed files with 42 additions and 23 deletions

View file

@ -134,14 +134,20 @@ message DeckTreeNode {
uint32 level = 4; uint32 level = 4;
bool collapsed = 5; bool collapsed = 5;
// counts after limits applied // counts after adding children+applying limits
uint32 review_count = 6; uint32 review_count = 6;
uint32 learn_count = 7; uint32 learn_count = 7;
uint32 new_count = 8; uint32 new_count = 8;
// due counts without limits applied // card counts without children or limits applied
uint32 intraday_learning_total = 9; uint32 intraday_learning = 9;
uint32 interday_learning_total = 10; 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; bool filtered = 16;
// low index so key can be packed into a byte, but at bottom // low index so key can be packed into a byte, but at bottom

View file

@ -13,6 +13,7 @@ pub(crate) struct DueCounts {
pub intraday_learning: u32, pub intraday_learning: u32,
pub interday_learning: u32, pub interday_learning: u32,
pub total_cards: u32,
} }
impl Deck { impl Deck {

View file

@ -86,8 +86,11 @@ fn add_counts(node: &mut DeckTreeNode, counts: &HashMap<DeckId, DueCounts>) {
node.new_count = counts.new; node.new_count = counts.new;
node.review_count = counts.review; node.review_count = counts.review;
node.learn_count = counts.learning; node.learn_count = counts.learning;
node.intraday_learning_total = counts.intraday_learning; node.intraday_learning = counts.intraday_learning;
node.interday_learning_total = counts.interday_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 { for child in &mut node.children {
add_counts(child, counts); add_counts(child, counts);
@ -95,7 +98,7 @@ fn add_counts(node: &mut DeckTreeNode, counts: &HashMap<DeckId, DueCounts>) {
} }
/// Apply parent limits to children, and add child counts to parents. /// 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, node: &mut DeckTreeNode,
limits: &HashMap<DeckId, RemainingLimits>, limits: &HashMap<DeckId, RemainingLimits>,
parent_limits: RemainingLimits, parent_limits: RemainingLimits,
@ -110,7 +113,7 @@ fn apply_limits_v1(
let mut child_new_total = 0; let mut child_new_total = 0;
let mut child_rev_total = 0; let mut child_rev_total = 0;
for child in &mut node.children { 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_new_total += child.new_count;
child_rev_total += child.review_count; child_rev_total += child.review_count;
// no limit on learning cards // 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 /// Apply parent new limits to children, and add child counts to parents. Unlike
/// v1, reviews are not capped by their parents, and we /// v1, reviews are not capped by their parents, and we
/// return the uncapped review amount to add to the parent. /// return the uncapped review amount to add to the parent.
fn apply_limits_v2( fn sum_counts_and_apply_limits_v2(
node: &mut DeckTreeNode, node: &mut DeckTreeNode,
limits: &HashMap<DeckId, RemainingLimits>, limits: &HashMap<DeckId, RemainingLimits>,
parent_limits: RemainingLimits, parent_limits: RemainingLimits,
@ -141,7 +144,7 @@ fn apply_limits_v2(
let mut child_new_total = 0; let mut child_new_total = 0;
let mut child_rev_total = 0; let mut child_rev_total = 0;
for child in &mut node.children { 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; child_new_total += child.new_count;
// no limit on learning cards // no limit on learning cards
node.learn_count += child.learn_count; node.learn_count += child.learn_count;
@ -161,6 +164,7 @@ struct NodeCountsV3 {
review: u32, review: u32,
intraday_learning: u32, intraday_learning: u32,
interday_learning: u32, interday_learning: u32,
total: u32,
} }
impl NodeCountsV3 { impl NodeCountsV3 {
@ -183,13 +187,14 @@ impl AddAssign for NodeCountsV3 {
self.review += rhs.review; self.review += rhs.review;
self.intraday_learning += rhs.intraday_learning; self.intraday_learning += rhs.intraday_learning;
self.interday_learning += rhs.interday_learning; self.interday_learning += rhs.interday_learning;
self.total += rhs.total;
} }
} }
/// Adjust new, review and learning counts based on the daily limits. /// Adjust new, review and learning counts based on the daily limits.
/// As part of this process, the separate interday and intraday learning /// As part of this process, the separate interday and intraday learning
/// counts are combined after the limits have been applied. /// counts are combined after the limits have been applied.
fn apply_limits_v3( fn sum_counts_and_apply_limits_v3(
node: &mut DeckTreeNode, node: &mut DeckTreeNode,
limits: &HashMap<DeckId, RemainingLimits>, limits: &HashMap<DeckId, RemainingLimits>,
) -> NodeCountsV3 { ) -> NodeCountsV3 {
@ -202,19 +207,21 @@ fn apply_limits_v3(
let this_node_uncapped = NodeCountsV3 { let this_node_uncapped = NodeCountsV3 {
new: node.new_count, new: node.new_count,
review: node.review_count, review: node.review_count,
intraday_learning: node.intraday_learning_total, intraday_learning: node.intraday_learning,
interday_learning: node.interday_learning_total, interday_learning: node.interday_learning_uncapped,
total: node.total_in_deck,
}; };
let mut individually_capped_total = this_node_uncapped.capped(&remaining); let mut individually_capped_total = this_node_uncapped.capped(&remaining);
// and add the capped values from child decks // and add the capped values from child decks
for child in &mut node.children { 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 // 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, // 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. // deck.
let total_constrained_by_current_deck = individually_capped_total.capped(&remaining); let total_constrained_by_current_deck = individually_capped_total.capped(&remaining);
node.new_count = total_constrained_by_current_deck.new; 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); let limits = remaining_limits_map(decks_map.values(), &dconf, days_elapsed);
if sched_ver == SchedulerVersion::V2 { if sched_ver == SchedulerVersion::V2 {
if v3 { if v3 {
apply_limits_v3(&mut tree, &limits); sum_counts_and_apply_limits_v3(&mut tree, &limits);
} else { } else {
apply_limits_v2(&mut tree, &limits, RemainingLimits::default()); sum_counts_and_apply_limits_v2(&mut tree, &limits, RemainingLimits::default());
} }
} else { } else {
apply_limits_v1(&mut tree, &limits, RemainingLimits::default()); sum_counts_and_apply_limits_v1(&mut tree, &limits, RemainingLimits::default());
} }
} }

View file

@ -1,5 +1,7 @@
SELECT did, SELECT did,
-- new
sum(queue = :new_queue), sum(queue = :new_queue),
-- reviews
sum( sum(
queue = :review_queue queue = :review_queue
AND due <= :day_cutoff AND due <= :day_cutoff
@ -35,6 +37,7 @@ SELECT did,
) )
END END
) )
) ),
-- total
COUNT(1)
FROM cards FROM cards
WHERE queue >= 0

View file

@ -44,6 +44,7 @@ fn row_to_due_counts(row: &Row) -> Result<(DeckId, DueCounts)> {
let review = row.get(2)?; let review = row.get(2)?;
let interday_learning: u32 = row.get(3)?; let interday_learning: u32 = row.get(3)?;
let intraday_learning: u32 = row.get(4)?; 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 // used as-is in v1/v2; recalculated in v3 after limits are applied
let learning = intraday_learning + interday_learning; let learning = intraday_learning + interday_learning;
Ok(( Ok((
@ -54,6 +55,7 @@ fn row_to_due_counts(row: &Row) -> Result<(DeckId, DueCounts)> {
learning, learning,
intraday_learning, intraday_learning,
interday_learning, interday_learning,
total_cards,
}, },
)) ))
} }
@ -293,7 +295,7 @@ impl SqliteStorage {
}); });
sql = concat!( sql = concat!(
include_str!("due_counts.sql"), 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 " "or (name >= :prefix_start and name < :prefix_end)) group by did "
); );
} else { } else {