From 48c121e4f3202d0d22ee868908a830d613f407cf Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 19 Aug 2021 20:13:22 +1000 Subject: [PATCH] filtered decks w/ scheduling disabled in v3 now log reviews --- ftl/core/statistics.ftl | 2 +- proto/anki/stats.proto | 3 ++- rslib/src/revlog/mod.rs | 6 +++-- rslib/src/scheduler/answering/learning.rs | 8 +++---- rslib/src/scheduler/answering/mod.rs | 12 +++++----- rslib/src/scheduler/answering/preview.rs | 9 ++++---- rslib/src/scheduler/answering/relearning.rs | 4 ++-- rslib/src/scheduler/answering/review.rs | 4 ++-- rslib/src/scheduler/answering/revlog.rs | 23 +++++++++----------- rslib/src/scheduler/states/filtered.rs | 6 ++--- rslib/src/scheduler/states/mod.rs | 4 ++-- rslib/src/scheduler/states/preview_filter.rs | 5 +++++ rslib/src/scheduler/states/review.rs | 2 +- rslib/src/stats/card.rs | 4 ++-- ts/graphs/buttons.ts | 3 ++- ts/graphs/hours.ts | 2 +- ts/graphs/reviews.ts | 14 ++++++------ ts/graphs/today.ts | 2 +- 18 files changed, 58 insertions(+), 55 deletions(-) diff --git a/ftl/core/statistics.ftl b/ftl/core/statistics.ftl index 8b122a644..5799a54fb 100644 --- a/ftl/core/statistics.ftl +++ b/ftl/core/statistics.ftl @@ -83,7 +83,7 @@ statistics-counts-young-cards = Young statistics-counts-mature-cards = Mature statistics-counts-suspended-cards = Suspended statistics-counts-buried-cards = Buried -statistics-counts-early-cards = Early +statistics-counts-filtered-cards = Filtered statistics-counts-learning-cards = Learning statistics-counts-relearning-cards = Relearning statistics-counts-title = Card Counts diff --git a/proto/anki/stats.proto b/proto/anki/stats.proto index 21f69170e..712ec29e5 100644 --- a/proto/anki/stats.proto +++ b/proto/anki/stats.proto @@ -49,7 +49,8 @@ message RevlogEntry { LEARNING = 0; REVIEW = 1; RELEARNING = 2; - EARLY_REVIEW = 3; + // Recent Anki versions only use this when rescheduling disabled + FILTERED = 3; MANUAL = 4; } int64 id = 1; diff --git a/rslib/src/revlog/mod.rs b/rslib/src/revlog/mod.rs index 13b1d00d3..509cc8475 100644 --- a/rslib/src/revlog/mod.rs +++ b/rslib/src/revlog/mod.rs @@ -63,9 +63,11 @@ pub enum RevlogReviewKind { Learning = 0, Review = 1, Relearning = 2, - EarlyReview = 3, + /// Old Anki versions called this "Cram" or "Early", and assigned it when + /// reviewing cards ahead. It is now only used for filtered decks with + /// rescheduling disabled. + Filtered = 3, Manual = 4, - // Preview = 5, } impl Default for RevlogReviewKind { diff --git a/rslib/src/scheduler/answering/learning.rs b/rslib/src/scheduler/answering/learning.rs index 7896dc2f7..86a1b083e 100644 --- a/rslib/src/scheduler/answering/learning.rs +++ b/rslib/src/scheduler/answering/learning.rs @@ -15,19 +15,19 @@ impl CardStateUpdater { &mut self, current: CardState, next: NewState, - ) -> Option { + ) -> RevlogEntryPartial { self.card.ctype = CardType::New; self.card.queue = CardQueue::New; self.card.due = next.position as i32; - RevlogEntryPartial::maybe_new(current, next.into(), 0.0, self.secs_until_rollover()) + RevlogEntryPartial::new(current, next.into(), 0.0, self.secs_until_rollover()) } pub(super) fn apply_learning_state( &mut self, current: CardState, next: LearnState, - ) -> Option { + ) -> RevlogEntryPartial { self.card.remaining_steps = next.remaining_steps; self.card.ctype = CardType::Learn; @@ -45,7 +45,7 @@ impl CardStateUpdater { } } - RevlogEntryPartial::maybe_new(current, next.into(), 0.0, self.secs_until_rollover()) + RevlogEntryPartial::new(current, next.into(), 0.0, self.secs_until_rollover()) } /// Adds secs + fuzz to current time diff --git a/rslib/src/scheduler/answering/mod.rs b/rslib/src/scheduler/answering/mod.rs index 8222169bc..a1d5016cb 100644 --- a/rslib/src/scheduler/answering/mod.rs +++ b/rslib/src/scheduler/answering/mod.rs @@ -42,8 +42,6 @@ pub struct CardAnswer { pub milliseconds_taken: u32, } -// fixme: log preview review - /// Holds the information required to determine a given card's /// current state, and to apply a state change to it. struct CardStateUpdater { @@ -103,7 +101,7 @@ impl CardStateUpdater { &mut self, current: CardState, next: CardState, - ) -> Result> { + ) -> Result { let revlog = match next { CardState::Normal(normal) => { // transitioning from filtered state? @@ -141,7 +139,7 @@ impl CardStateUpdater { &mut self, current: CardState, next: NormalState, - ) -> Option { + ) -> RevlogEntryPartial { self.card.reps += 1; self.card.original_due = 0; @@ -259,9 +257,9 @@ impl Collection { current_state, answer.current_state, ))); } - if let Some(revlog_partial) = updater.apply_study_state(current_state, answer.new_state)? { - self.add_partial_revlog(revlog_partial, usn, answer)?; - } + let revlog_partial = updater.apply_study_state(current_state, answer.new_state)?; + self.add_partial_revlog(revlog_partial, usn, answer)?; + self.update_deck_stats_from_answer(usn, answer, &updater, original.queue)?; self.maybe_bury_siblings(&original, &updater.config)?; let timing = updater.timing; diff --git a/rslib/src/scheduler/answering/preview.rs b/rslib/src/scheduler/answering/preview.rs index 74b29a780..eff09c5ea 100644 --- a/rslib/src/scheduler/answering/preview.rs +++ b/rslib/src/scheduler/answering/preview.rs @@ -9,17 +9,16 @@ use crate::{ }; impl CardStateUpdater { - // fixme: check learning card moved into preview - // restores correctly in both learn and day-learn case pub(super) fn apply_preview_state( &mut self, current: CardState, next: PreviewState, - ) -> Option { + ) -> RevlogEntryPartial { + let revlog = RevlogEntryPartial::new(current, next.into(), 0.0, self.secs_until_rollover()); if next.finished { self.card .remove_from_filtered_deck_restoring_queue(SchedulerVersion::V2); - return None; + return revlog; } self.card.queue = CardQueue::PreviewRepeat; @@ -34,7 +33,7 @@ impl CardStateUpdater { } } - RevlogEntryPartial::maybe_new(current, next.into(), 0.0, self.secs_until_rollover()) + revlog } } diff --git a/rslib/src/scheduler/answering/relearning.rs b/rslib/src/scheduler/answering/relearning.rs index adb9096f9..7645d0ca0 100644 --- a/rslib/src/scheduler/answering/relearning.rs +++ b/rslib/src/scheduler/answering/relearning.rs @@ -12,7 +12,7 @@ impl CardStateUpdater { &mut self, current: CardState, next: RelearnState, - ) -> Option { + ) -> RevlogEntryPartial { self.card.interval = next.review.scheduled_days; self.card.remaining_steps = next.learning.remaining_steps; self.card.ctype = CardType::Relearn; @@ -33,7 +33,7 @@ impl CardStateUpdater { } } - RevlogEntryPartial::maybe_new( + RevlogEntryPartial::new( current, next.into(), next.review.ease_factor, diff --git a/rslib/src/scheduler/answering/review.rs b/rslib/src/scheduler/answering/review.rs index 61a1208dc..6d36812ef 100644 --- a/rslib/src/scheduler/answering/review.rs +++ b/rslib/src/scheduler/answering/review.rs @@ -12,7 +12,7 @@ impl CardStateUpdater { &mut self, current: CardState, next: ReviewState, - ) -> Option { + ) -> RevlogEntryPartial { self.card.queue = CardQueue::Review; self.card.ctype = CardType::Review; self.card.interval = next.scheduled_days; @@ -21,7 +21,7 @@ impl CardStateUpdater { self.card.lapses = next.lapses; self.card.remaining_steps = 0; - RevlogEntryPartial::maybe_new( + RevlogEntryPartial::new( current, next.into(), next.ease_factor, diff --git a/rslib/src/scheduler/answering/revlog.rs b/rslib/src/scheduler/answering/revlog.rs index 159cba23e..be64a98bb 100644 --- a/rslib/src/scheduler/answering/revlog.rs +++ b/rslib/src/scheduler/answering/revlog.rs @@ -15,24 +15,21 @@ pub struct RevlogEntryPartial { } impl RevlogEntryPartial { - /// Returns None in the Preview case, since preview cards do not currently log. - pub(super) fn maybe_new( + pub(super) fn new( current: CardState, next: CardState, ease_factor: f32, secs_until_rollover: u32, - ) -> Option { - current.revlog_kind().map(|review_kind| { - let next_interval = next.interval_kind().maybe_as_days(secs_until_rollover); - let current_interval = current.interval_kind().maybe_as_days(secs_until_rollover); + ) -> Self { + let next_interval = next.interval_kind().maybe_as_days(secs_until_rollover); + let current_interval = current.interval_kind().maybe_as_days(secs_until_rollover); - RevlogEntryPartial { - interval: next_interval, - last_interval: current_interval, - ease_factor, - review_kind, - } - }) + RevlogEntryPartial { + interval: next_interval, + last_interval: current_interval, + ease_factor, + review_kind: current.revlog_kind(), + } } pub(super) fn into_revlog_entry( diff --git a/rslib/src/scheduler/states/filtered.rs b/rslib/src/scheduler/states/filtered.rs index 0905d5738..0845360d6 100644 --- a/rslib/src/scheduler/states/filtered.rs +++ b/rslib/src/scheduler/states/filtered.rs @@ -20,10 +20,10 @@ impl FilteredState { } } - pub(crate) fn revlog_kind(self) -> Option { + pub(crate) fn revlog_kind(self) -> RevlogReviewKind { match self { - FilteredState::Preview(_state) => None, - FilteredState::Rescheduling(state) => Some(state.revlog_kind()), + FilteredState::Preview(state) => state.revlog_kind(), + FilteredState::Rescheduling(state) => state.revlog_kind(), } } diff --git a/rslib/src/scheduler/states/mod.rs b/rslib/src/scheduler/states/mod.rs index 15b97bcd3..6ef659f62 100644 --- a/rslib/src/scheduler/states/mod.rs +++ b/rslib/src/scheduler/states/mod.rs @@ -40,9 +40,9 @@ impl CardState { } } - pub(crate) fn revlog_kind(self) -> Option { + pub(crate) fn revlog_kind(self) -> RevlogReviewKind { match self { - CardState::Normal(normal) => Some(normal.revlog_kind()), + CardState::Normal(normal) => normal.revlog_kind(), CardState::Filtered(filtered) => filtered.revlog_kind(), } } diff --git a/rslib/src/scheduler/states/preview_filter.rs b/rslib/src/scheduler/states/preview_filter.rs index 5835eaa2a..055df9d65 100644 --- a/rslib/src/scheduler/states/preview_filter.rs +++ b/rslib/src/scheduler/states/preview_filter.rs @@ -2,6 +2,7 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use super::{IntervalKind, NextCardStates, StateContext}; +use crate::revlog::RevlogReviewKind; #[derive(Debug, Clone, Copy, PartialEq)] pub struct PreviewState { @@ -14,6 +15,10 @@ impl PreviewState { IntervalKind::InSecs(self.scheduled_secs) } + pub(crate) fn revlog_kind(self) -> RevlogReviewKind { + RevlogReviewKind::Filtered + } + pub(crate) fn next_states(self, ctx: &StateContext) -> NextCardStates { NextCardStates { current: self.into(), diff --git a/rslib/src/scheduler/states/review.rs b/rslib/src/scheduler/states/review.rs index 76806b8ce..1f8c35980 100644 --- a/rslib/src/scheduler/states/review.rs +++ b/rslib/src/scheduler/states/review.rs @@ -46,7 +46,7 @@ impl ReviewState { pub(crate) fn revlog_kind(self) -> RevlogReviewKind { if self.days_late() < 0 { - RevlogReviewKind::EarlyReview + RevlogReviewKind::Filtered } else { RevlogReviewKind::Review } diff --git a/rslib/src/stats/card.rs b/rslib/src/stats/card.rs index 527d1b69d..4b5ce5ab1 100644 --- a/rslib/src/stats/card.rs +++ b/rslib/src/stats/card.rs @@ -213,14 +213,14 @@ fn revlog_to_text(e: RevlogEntry, tr: &I18n) -> RevlogText { RevlogReviewKind::Learning => tr.card_stats_review_log_type_learn().into(), RevlogReviewKind::Review => tr.card_stats_review_log_type_review().into(), RevlogReviewKind::Relearning => tr.card_stats_review_log_type_relearn().into(), - RevlogReviewKind::EarlyReview => tr.card_stats_review_log_type_filtered().into(), + RevlogReviewKind::Filtered => tr.card_stats_review_log_type_filtered().into(), RevlogReviewKind::Manual => tr.card_stats_review_log_type_manual().into(), }; let kind_class = match e.review_kind { RevlogReviewKind::Learning => String::from("revlog-learn"), RevlogReviewKind::Review => String::from("revlog-review"), RevlogReviewKind::Relearning => String::from("revlog-relearn"), - RevlogReviewKind::EarlyReview => String::from("revlog-filtered"), + RevlogReviewKind::Filtered => String::from("revlog-filtered"), RevlogReviewKind::Manual => String::from("revlog-manual"), }; let rating = e.button_chosen.to_string(); diff --git a/ts/graphs/buttons.ts b/ts/graphs/buttons.ts index 1108da1c5..b4c36246e 100644 --- a/ts/graphs/buttons.ts +++ b/ts/graphs/buttons.ts @@ -65,13 +65,14 @@ export function gatherData(data: Stats.GraphsResponse, range: GraphRange): Graph break; case ReviewKind.REVIEW: - case ReviewKind.EARLY_REVIEW: if (review.lastInterval < 21) { buttons = young; } else { buttons = mature; } break; + case ReviewKind.FILTERED: + break; } buttons[buttonNum - 1] += 1; diff --git a/ts/graphs/hours.ts b/ts/graphs/hours.ts index 743f4d0e5..8ccbc6063 100644 --- a/ts/graphs/hours.ts +++ b/ts/graphs/hours.ts @@ -51,7 +51,7 @@ function gatherData(data: Stats.GraphsResponse, range: GraphRange): Hour[] { case ReviewKind.REVIEW: case ReviewKind.RELEARNING: break; // from switch - case ReviewKind.EARLY_REVIEW: + case ReviewKind.FILTERED: case ReviewKind.MANUAL: continue; // next loop iteration } diff --git a/ts/graphs/reviews.ts b/ts/graphs/reviews.ts index 9ab89e4a5..e8250e0c0 100644 --- a/ts/graphs/reviews.ts +++ b/ts/graphs/reviews.ts @@ -41,7 +41,7 @@ interface Reviews { relearn: number; young: number; mature: number; - early: number; + filtered: number; } export interface GraphData { @@ -56,7 +56,7 @@ type BinType = Bin, number>; export function gatherData(data: Stats.GraphsResponse): GraphData { const reviewCount = new Map(); const reviewTime = new Map(); - const empty = { mature: 0, young: 0, learn: 0, relearn: 0, early: 0 }; + const empty = { mature: 0, young: 0, learn: 0, relearn: 0, filtered: 0 }; for (const review of data.revlog as Stats.RevlogEntry[]) { if (review.reviewKind == ReviewKind.MANUAL) { @@ -89,9 +89,9 @@ export function gatherData(data: Stats.GraphsResponse): GraphData { timeEntry.mature += review.takenMillis; } break; - case ReviewKind.EARLY_REVIEW: - countEntry.early += 1; - timeEntry.early += review.takenMillis; + case ReviewKind.FILTERED: + countEntry.filtered += 1; + timeEntry.filtered += review.takenMillis; break; } } @@ -106,7 +106,7 @@ function totalsForBin(bin: BinType): number[] { total[1] += entry[1].relearn; total[2] += entry[1].young; total[3] += entry[1].mature; - total[4] += entry[1].early; + total[4] += entry[1].filtered; } return total; @@ -246,7 +246,7 @@ export function renderReviews( [reds(1), tr.statisticsCountsRelearningCards(), valueLabel(totals[1])], [lighterGreens(1), tr.statisticsCountsYoungCards(), valueLabel(totals[2])], [darkerGreens(1), tr.statisticsCountsMatureCards(), valueLabel(totals[3])], - [purples(1), tr.statisticsCountsEarlyCards(), valueLabel(totals[4])], + [purples(1), tr.statisticsCountsFilteredCards(), valueLabel(totals[4])], ["transparent", tr.statisticsRunningTotal(), valueLabel(cumulative)], ]; for (const [colour, label, detail] of lines) { diff --git a/ts/graphs/today.ts b/ts/graphs/today.ts index f12b23e13..ed13563a9 100644 --- a/ts/graphs/today.ts +++ b/ts/graphs/today.ts @@ -63,7 +63,7 @@ export function gatherData(data: Stats.GraphsResponse): TodayData { case ReviewKind.RELEARNING: relearnCount += 1; break; - case ReviewKind.EARLY_REVIEW: + case ReviewKind.FILTERED: earlyReviewCount += 1; break; }