filtered decks w/ scheduling disabled in v3 now log reviews

This commit is contained in:
Damien Elmes 2021-08-19 20:13:22 +10:00
parent 8830d33826
commit 48c121e4f3
18 changed files with 58 additions and 55 deletions

View file

@ -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

View file

@ -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;

View file

@ -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 {

View file

@ -15,19 +15,19 @@ impl CardStateUpdater {
&mut self,
current: CardState,
next: NewState,
) -> Option<RevlogEntryPartial> {
) -> 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> {
) -> 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

View file

@ -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<Option<RevlogEntryPartial>> {
) -> Result<RevlogEntryPartial> {
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> {
) -> 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;

View file

@ -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> {
) -> 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
}
}

View file

@ -12,7 +12,7 @@ impl CardStateUpdater {
&mut self,
current: CardState,
next: RelearnState,
) -> Option<RevlogEntryPartial> {
) -> 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,

View file

@ -12,7 +12,7 @@ impl CardStateUpdater {
&mut self,
current: CardState,
next: ReviewState,
) -> Option<RevlogEntryPartial> {
) -> 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,

View file

@ -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<Self> {
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(

View file

@ -20,10 +20,10 @@ impl FilteredState {
}
}
pub(crate) fn revlog_kind(self) -> Option<RevlogReviewKind> {
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(),
}
}

View file

@ -40,9 +40,9 @@ impl CardState {
}
}
pub(crate) fn revlog_kind(self) -> Option<RevlogReviewKind> {
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(),
}
}

View file

@ -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(),

View file

@ -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
}

View file

@ -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();

View file

@ -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;

View file

@ -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
}

View file

@ -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<Map<number, Reviews[]>, number>;
export function gatherData(data: Stats.GraphsResponse): GraphData {
const reviewCount = new Map<number, Reviews>();
const reviewTime = new Map<number, Reviews>();
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) {

View file

@ -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;
}