fsrs_memory_state -> memory_state

This commit is contained in:
Damien Elmes 2023-09-16 16:18:13 +10:00
parent 5004cd332b
commit 0301ae1d8a
28 changed files with 73 additions and 79 deletions

View file

@ -49,7 +49,7 @@ message Card {
int64 original_deck_id = 16; int64 original_deck_id = 16;
uint32 flags = 17; uint32 flags = 17;
optional uint32 original_position = 18; optional uint32 original_position = 18;
optional FsrsMemoryState fsrs_memory_state = 20; optional FsrsMemoryState memory_state = 20;
string custom_data = 19; string custom_data = 19;
} }

View file

@ -63,7 +63,7 @@ message SchedulingState {
message Learning { message Learning {
uint32 remaining_steps = 1; uint32 remaining_steps = 1;
uint32 scheduled_secs = 2; uint32 scheduled_secs = 2;
optional cards.FsrsMemoryState fsrs_memory_state = 6; optional cards.FsrsMemoryState memory_state = 6;
} }
message Review { message Review {
uint32 scheduled_days = 1; uint32 scheduled_days = 1;
@ -71,7 +71,7 @@ message SchedulingState {
float ease_factor = 3; float ease_factor = 3;
uint32 lapses = 4; uint32 lapses = 4;
bool leeched = 5; bool leeched = 5;
optional cards.FsrsMemoryState fsrs_memory_state = 6; optional cards.FsrsMemoryState memory_state = 6;
} }
message Relearning { message Relearning {
Review review = 1; Review review = 1;

View file

@ -52,7 +52,7 @@ message CardStatsResponse {
float total_secs = 15; float total_secs = 15;
string card_type = 16; string card_type = 16;
string notetype = 17; string notetype = 17;
optional cards.FsrsMemoryState fsrs_memory_state = 18; optional cards.FsrsMemoryState memory_state = 18;
// not set if due date/state not available // not set if due date/state not available
optional float fsrs_retrievability = 19; optional float fsrs_retrievability = 19;
string custom_data = 20; string custom_data = 20;

View file

@ -45,7 +45,7 @@ class Card(DeprecatedNamesMixin):
odid: anki.decks.DeckId odid: anki.decks.DeckId
queue: CardQueue queue: CardQueue
type: CardType type: CardType
fsrs_memory_state: FSRSMemoryState | None memory_state: FSRSMemoryState | None
def __init__( def __init__(
self, self,
@ -95,9 +95,7 @@ class Card(DeprecatedNamesMixin):
card.original_position if card.HasField("original_position") else None card.original_position if card.HasField("original_position") else None
) )
self.custom_data = card.custom_data self.custom_data = card.custom_data
self.fsrs_memory_state = ( self.memory_state = card.memory_state if card.HasField("memory_state") else None
card.fsrs_memory_state if card.HasField("fsrs_memory_state") else None
)
def _to_backend_card(self) -> cards_pb2.Card: def _to_backend_card(self) -> cards_pb2.Card:
# mtime & usn are set by backend # mtime & usn are set by backend
@ -119,7 +117,7 @@ class Card(DeprecatedNamesMixin):
flags=self.flags, flags=self.flags,
original_position=self.original_position, original_position=self.original_position,
custom_data=self.custom_data, custom_data=self.custom_data,
fsrs_memory_state=self.fsrs_memory_state, memory_state=self.memory_state,
) )
def flush(self) -> None: def flush(self) -> None:

View file

@ -472,7 +472,7 @@ impl RowContext {
fn fsrs_stability_str(&self) -> String { fn fsrs_stability_str(&self) -> String {
self.cards[0] self.cards[0]
.fsrs_memory_state .memory_state
.as_ref() .as_ref()
.map(|s| time_span(s.stability * 86400.0, &self.tr, false)) .map(|s| time_span(s.stability * 86400.0, &self.tr, false))
.unwrap_or_default() .unwrap_or_default()
@ -480,7 +480,7 @@ impl RowContext {
fn fsrs_difficulty_str(&self) -> String { fn fsrs_difficulty_str(&self) -> String {
self.cards[0] self.cards[0]
.fsrs_memory_state .memory_state
.as_ref() .as_ref()
.map(|s| format!("{:.0}%", (s.difficulty - 1.0) / 9.0 * 100.0)) .map(|s| format!("{:.0}%", (s.difficulty - 1.0) / 9.0 * 100.0))
.unwrap_or_default() .unwrap_or_default()
@ -488,7 +488,7 @@ impl RowContext {
fn fsrs_retrievability_str(&self) -> String { fn fsrs_retrievability_str(&self) -> String {
self.cards[0] self.cards[0]
.fsrs_memory_state .memory_state
.as_ref() .as_ref()
.zip(self.cards[0].days_since_last_review(&self.timing)) .zip(self.cards[0].days_since_last_review(&self.timing))
.map(|(state, days_elapsed)| { .map(|(state, days_elapsed)| {

View file

@ -93,7 +93,7 @@ pub struct Card {
pub(crate) flags: u8, pub(crate) flags: u8,
/// The position in the new queue before leaving it. /// The position in the new queue before leaving it.
pub(crate) original_position: Option<u32>, pub(crate) original_position: Option<u32>,
pub(crate) fsrs_memory_state: Option<FsrsMemoryState>, pub(crate) memory_state: Option<FsrsMemoryState>,
/// JSON object or empty; exposed through the reviewer for persisting custom /// JSON object or empty; exposed through the reviewer for persisting custom
/// state /// state
pub(crate) custom_data: String, pub(crate) custom_data: String,
@ -126,7 +126,7 @@ impl Default for Card {
original_deck_id: DeckId(0), original_deck_id: DeckId(0),
flags: 0, flags: 0,
original_position: None, original_position: None,
fsrs_memory_state: None, memory_state: None,
custom_data: String::new(), custom_data: String::new(),
} }
} }

View file

@ -100,7 +100,7 @@ impl TryFrom<anki_proto::cards::Card> for Card {
original_deck_id: DeckId(c.original_deck_id), original_deck_id: DeckId(c.original_deck_id),
flags: c.flags as u8, flags: c.flags as u8,
original_position: c.original_position, original_position: c.original_position,
fsrs_memory_state: c.fsrs_memory_state.map(Into::into), memory_state: c.memory_state.map(Into::into),
custom_data: c.custom_data, custom_data: c.custom_data,
}) })
} }
@ -127,7 +127,7 @@ impl From<Card> for anki_proto::cards::Card {
original_deck_id: c.original_deck_id.0, original_deck_id: c.original_deck_id.0,
flags: c.flags as u32, flags: c.flags as u32,
original_position: c.original_position.map(Into::into), original_position: c.original_position.map(Into::into),
fsrs_memory_state: c.fsrs_memory_state.map(Into::into), memory_state: c.memory_state.map(Into::into),
custom_data: c.custom_data, custom_data: c.custom_data,
} }
} }

View file

@ -63,7 +63,7 @@ impl CardStateUpdater {
let lapses = self.card.lapses; let lapses = self.card.lapses;
let ease_factor = self.card.ease_factor(); let ease_factor = self.card.ease_factor();
let remaining_steps = self.card.remaining_steps(); let remaining_steps = self.card.remaining_steps();
let fsrs_memory_state = self.card.fsrs_memory_state; let memory_state = self.card.memory_state;
match self.card.ctype { match self.card.ctype {
CardType::New => NormalState::New(NewState { CardType::New => NormalState::New(NewState {
@ -73,7 +73,7 @@ impl CardStateUpdater {
LearnState { LearnState {
scheduled_secs: self.learn_steps().current_delay_secs(remaining_steps), scheduled_secs: self.learn_steps().current_delay_secs(remaining_steps),
remaining_steps, remaining_steps,
fsrs_memory_state, memory_state,
} }
} }
.into(), .into(),
@ -84,14 +84,14 @@ impl CardStateUpdater {
ease_factor, ease_factor,
lapses, lapses,
leeched: false, leeched: false,
fsrs_memory_state, memory_state,
} }
.into(), .into(),
CardType::Relearn => RelearnState { CardType::Relearn => RelearnState {
learning: LearnState { learning: LearnState {
scheduled_secs: self.relearn_steps().current_delay_secs(remaining_steps), scheduled_secs: self.relearn_steps().current_delay_secs(remaining_steps),
remaining_steps, remaining_steps,
fsrs_memory_state, memory_state,
}, },
review: ReviewState { review: ReviewState {
scheduled_days: interval, scheduled_days: interval,
@ -99,7 +99,7 @@ impl CardStateUpdater {
ease_factor, ease_factor,
lapses, lapses,
leeched: false, leeched: false,
fsrs_memory_state, memory_state,
}, },
} }
.into(), .into(),

View file

@ -24,7 +24,7 @@ impl CardStateUpdater {
self.card.queue = CardQueue::New; self.card.queue = CardQueue::New;
self.card.due = next.position as i32; self.card.due = next.position as i32;
self.card.original_position = None; self.card.original_position = None;
self.card.fsrs_memory_state = None; self.card.memory_state = None;
RevlogEntryPartial::new(current, next.into(), 0.0, self.secs_until_rollover()) RevlogEntryPartial::new(current, next.into(), 0.0, self.secs_until_rollover())
} }
@ -39,7 +39,7 @@ impl CardStateUpdater {
if let Some(position) = current.new_position() { if let Some(position) = current.new_position() {
self.card.original_position = Some(position) self.card.original_position = Some(position)
} }
self.card.fsrs_memory_state = next.fsrs_memory_state; self.card.memory_state = next.memory_state;
let interval = next let interval = next
.interval_kind() .interval_kind()

View file

@ -353,7 +353,7 @@ impl Collection {
let config = self.home_deck_config(deck.config_id(), card.original_deck_id)?; let config = self.home_deck_config(deck.config_id(), card.original_deck_id)?;
let fsrs_next_states = if config.inner.fsrs_enabled { let fsrs_next_states = if config.inner.fsrs_enabled {
let fsrs = FSRS::new(Some(&config.inner.fsrs_weights))?; let fsrs = FSRS::new(Some(&config.inner.fsrs_weights))?;
let memory_state = if let Some(state) = card.fsrs_memory_state { let memory_state = if let Some(state) = card.memory_state {
Some(MemoryState::from(state)) Some(MemoryState::from(state))
} else if card.ctype == CardType::New { } else if card.ctype == CardType::New {
None None

View file

@ -23,7 +23,7 @@ impl CardStateUpdater {
if let Some(position) = current.new_position() { if let Some(position) = current.new_position() {
self.card.original_position = Some(position) self.card.original_position = Some(position)
} }
self.card.fsrs_memory_state = next.learning.fsrs_memory_state; self.card.memory_state = next.learning.memory_state;
let interval = next let interval = next
.interval_kind() .interval_kind()

View file

@ -24,7 +24,7 @@ impl CardStateUpdater {
if let Some(position) = current.new_position() { if let Some(position) = current.new_position() {
self.card.original_position = Some(position) self.card.original_position = Some(position)
} }
self.card.fsrs_memory_state = next.fsrs_memory_state; self.card.memory_state = next.memory_state;
RevlogEntryPartial::new( RevlogEntryPartial::new(
current, current,

View file

@ -40,7 +40,7 @@ impl Collection {
let state = fsrs.memory_state(item); let state = fsrs.memory_state(item);
let mut card = self.storage.get_card(card_id)?.or_not_found(card_id)?; let mut card = self.storage.get_card(card_id)?.or_not_found(card_id)?;
let original = card.clone(); let original = card.clone();
card.fsrs_memory_state = Some(state.into()); card.memory_state = Some(state.into());
self.update_card_inner(&mut card, original, usn)?; self.update_card_inner(&mut card, original, usn)?;
} }
} }

View file

@ -57,7 +57,7 @@ impl Card {
self.reps = 0; self.reps = 0;
self.lapses = 0; self.lapses = 0;
} }
self.fsrs_memory_state = None; self.memory_state = None;
last_position.is_none() last_position.is_none()
} }

View file

@ -8,7 +8,7 @@ impl From<anki_proto::scheduler::scheduling_state::Learning> for LearnState {
LearnState { LearnState {
remaining_steps: state.remaining_steps, remaining_steps: state.remaining_steps,
scheduled_secs: state.scheduled_secs, scheduled_secs: state.scheduled_secs,
fsrs_memory_state: state.fsrs_memory_state.map(Into::into), memory_state: state.memory_state.map(Into::into),
} }
} }
} }
@ -18,7 +18,7 @@ impl From<LearnState> for anki_proto::scheduler::scheduling_state::Learning {
anki_proto::scheduler::scheduling_state::Learning { anki_proto::scheduler::scheduling_state::Learning {
remaining_steps: state.remaining_steps, remaining_steps: state.remaining_steps,
scheduled_secs: state.scheduled_secs, scheduled_secs: state.scheduled_secs,
fsrs_memory_state: state.fsrs_memory_state.map(Into::into), memory_state: state.memory_state.map(Into::into),
} }
} }
} }

View file

@ -11,7 +11,7 @@ impl From<anki_proto::scheduler::scheduling_state::Review> for ReviewState {
ease_factor: state.ease_factor, ease_factor: state.ease_factor,
lapses: state.lapses, lapses: state.lapses,
leeched: state.leeched, leeched: state.leeched,
fsrs_memory_state: state.fsrs_memory_state.map(Into::into), memory_state: state.memory_state.map(Into::into),
} }
} }
} }
@ -24,7 +24,7 @@ impl From<ReviewState> for anki_proto::scheduler::scheduling_state::Review {
ease_factor: state.ease_factor, ease_factor: state.ease_factor,
lapses: state.lapses, lapses: state.lapses,
leeched: state.leeched, leeched: state.leeched,
fsrs_memory_state: state.fsrs_memory_state.map(Into::into), memory_state: state.memory_state.map(Into::into),
} }
} }
} }

View file

@ -13,7 +13,7 @@ use crate::revlog::RevlogReviewKind;
pub struct LearnState { pub struct LearnState {
pub remaining_steps: u32, pub remaining_steps: u32,
pub scheduled_secs: u32, pub scheduled_secs: u32,
pub fsrs_memory_state: Option<FsrsMemoryState>, pub memory_state: Option<FsrsMemoryState>,
} }
impl LearnState { impl LearnState {
@ -39,7 +39,7 @@ impl LearnState {
LearnState { LearnState {
remaining_steps: ctx.steps.remaining_for_failed(), remaining_steps: ctx.steps.remaining_for_failed(),
scheduled_secs: ctx.steps.again_delay_secs_learn(), scheduled_secs: ctx.steps.again_delay_secs_learn(),
fsrs_memory_state: ctx.fsrs_next_states.as_ref().map(|s| s.again.memory.into()), memory_state: ctx.fsrs_next_states.as_ref().map(|s| s.again.memory.into()),
} }
} }
@ -50,18 +50,18 @@ impl LearnState {
.hard_delay_secs(self.remaining_steps) .hard_delay_secs(self.remaining_steps)
// user has 0 learning steps, which the UI doesn't allow // user has 0 learning steps, which the UI doesn't allow
.unwrap_or(60), .unwrap_or(60),
fsrs_memory_state: ctx.fsrs_next_states.as_ref().map(|s| s.hard.memory.into()), memory_state: ctx.fsrs_next_states.as_ref().map(|s| s.hard.memory.into()),
..self ..self
} }
} }
fn answer_good(self, ctx: &StateContext) -> CardState { fn answer_good(self, ctx: &StateContext) -> CardState {
let fsrs_memory_state = ctx.fsrs_next_states.as_ref().map(|s| s.good.memory.into()); let memory_state = ctx.fsrs_next_states.as_ref().map(|s| s.good.memory.into());
if let Some(good_delay) = ctx.steps.good_delay_secs(self.remaining_steps) { if let Some(good_delay) = ctx.steps.good_delay_secs(self.remaining_steps) {
LearnState { LearnState {
remaining_steps: ctx.steps.remaining_for_good(self.remaining_steps), remaining_steps: ctx.steps.remaining_for_good(self.remaining_steps),
scheduled_secs: good_delay, scheduled_secs: good_delay,
fsrs_memory_state, memory_state,
} }
.into() .into()
} else { } else {
@ -74,7 +74,7 @@ impl LearnState {
ReviewState { ReviewState {
scheduled_days: ctx.with_review_fuzz(interval as f32, minimum, maximum), scheduled_days: ctx.with_review_fuzz(interval as f32, minimum, maximum),
ease_factor: ctx.initial_ease_factor, ease_factor: ctx.initial_ease_factor,
fsrs_memory_state, memory_state,
..Default::default() ..Default::default()
} }
.into() .into()
@ -92,7 +92,7 @@ impl LearnState {
ReviewState { ReviewState {
scheduled_days: ctx.with_review_fuzz(interval as f32, minimum, maximum), scheduled_days: ctx.with_review_fuzz(interval as f32, minimum, maximum),
ease_factor: ctx.initial_ease_factor, ease_factor: ctx.initial_ease_factor,
fsrs_memory_state: ctx.fsrs_next_states.as_ref().map(|s| s.easy.memory.into()), memory_state: ctx.fsrs_next_states.as_ref().map(|s| s.easy.memory.into()),
..Default::default() ..Default::default()
} }
} }

View file

@ -44,7 +44,7 @@ impl NormalState {
let next_states = LearnState { let next_states = LearnState {
remaining_steps: ctx.steps.remaining_for_failed(), remaining_steps: ctx.steps.remaining_for_failed(),
scheduled_secs: 0, scheduled_secs: 0,
fsrs_memory_state: None, memory_state: None,
} }
.next_states(ctx); .next_states(ctx);
// .. but with current as New, not Learning // .. but with current as New, not Learning

View file

@ -35,18 +35,18 @@ impl RelearnState {
} }
fn answer_again(self, ctx: &StateContext) -> CardState { fn answer_again(self, ctx: &StateContext) -> CardState {
let (scheduled_days, fsrs_memory_state) = self.review.failing_review_interval(ctx); let (scheduled_days, memory_state) = self.review.failing_review_interval(ctx);
if let Some(again_delay) = ctx.relearn_steps.again_delay_secs_relearn() { if let Some(again_delay) = ctx.relearn_steps.again_delay_secs_relearn() {
RelearnState { RelearnState {
learning: LearnState { learning: LearnState {
remaining_steps: ctx.relearn_steps.remaining_for_failed(), remaining_steps: ctx.relearn_steps.remaining_for_failed(),
scheduled_secs: again_delay, scheduled_secs: again_delay,
fsrs_memory_state, memory_state,
}, },
review: ReviewState { review: ReviewState {
scheduled_days, scheduled_days,
elapsed_days: 0, elapsed_days: 0,
fsrs_memory_state, memory_state,
..self.review ..self.review
}, },
} }
@ -57,7 +57,7 @@ impl RelearnState {
} }
fn answer_hard(self, ctx: &StateContext) -> CardState { fn answer_hard(self, ctx: &StateContext) -> CardState {
let fsrs_memory_state = ctx.fsrs_next_states.as_ref().map(|s| s.hard.memory.into()); let memory_state = ctx.fsrs_next_states.as_ref().map(|s| s.hard.memory.into());
if let Some(hard_delay) = ctx if let Some(hard_delay) = ctx
.relearn_steps .relearn_steps
.hard_delay_secs(self.learning.remaining_steps) .hard_delay_secs(self.learning.remaining_steps)
@ -65,12 +65,12 @@ impl RelearnState {
RelearnState { RelearnState {
learning: LearnState { learning: LearnState {
scheduled_secs: hard_delay, scheduled_secs: hard_delay,
fsrs_memory_state, memory_state,
..self.learning ..self.learning
}, },
review: ReviewState { review: ReviewState {
elapsed_days: 0, elapsed_days: 0,
fsrs_memory_state, memory_state,
..self.review ..self.review
}, },
} }
@ -81,7 +81,7 @@ impl RelearnState {
} }
fn answer_good(self, ctx: &StateContext) -> CardState { fn answer_good(self, ctx: &StateContext) -> CardState {
let fsrs_memory_state = ctx.fsrs_next_states.as_ref().map(|s| s.good.memory.into()); let memory_state = ctx.fsrs_next_states.as_ref().map(|s| s.good.memory.into());
if let Some(good_delay) = ctx if let Some(good_delay) = ctx
.relearn_steps .relearn_steps
.good_delay_secs(self.learning.remaining_steps) .good_delay_secs(self.learning.remaining_steps)
@ -92,11 +92,11 @@ impl RelearnState {
remaining_steps: ctx remaining_steps: ctx
.relearn_steps .relearn_steps
.remaining_for_good(self.learning.remaining_steps), .remaining_for_good(self.learning.remaining_steps),
fsrs_memory_state, memory_state,
}, },
review: ReviewState { review: ReviewState {
elapsed_days: 0, elapsed_days: 0,
fsrs_memory_state, memory_state,
..self.review ..self.review
}, },
} }
@ -110,7 +110,7 @@ impl RelearnState {
ReviewState { ReviewState {
scheduled_days: self.review.scheduled_days + 1, scheduled_days: self.review.scheduled_days + 1,
elapsed_days: 0, elapsed_days: 0,
fsrs_memory_state: ctx.fsrs_next_states.as_ref().map(|s| s.easy.memory.into()), memory_state: ctx.fsrs_next_states.as_ref().map(|s| s.easy.memory.into()),
..self.review ..self.review
} }
} }

View file

@ -25,7 +25,7 @@ pub struct ReviewState {
pub ease_factor: f32, pub ease_factor: f32,
pub lapses: u32, pub lapses: u32,
pub leeched: bool, pub leeched: bool,
pub fsrs_memory_state: Option<FsrsMemoryState>, pub memory_state: Option<FsrsMemoryState>,
} }
impl Default for ReviewState { impl Default for ReviewState {
@ -36,7 +36,7 @@ impl Default for ReviewState {
ease_factor: INITIAL_EASE_FACTOR, ease_factor: INITIAL_EASE_FACTOR,
lapses: 0, lapses: 0,
leeched: false, leeched: false,
fsrs_memory_state: None, memory_state: None,
} }
} }
} }
@ -89,14 +89,14 @@ impl ReviewState {
fn answer_again(self, ctx: &StateContext) -> CardState { fn answer_again(self, ctx: &StateContext) -> CardState {
let lapses = self.lapses + 1; let lapses = self.lapses + 1;
let leeched = leech_threshold_met(lapses, ctx.leech_threshold); let leeched = leech_threshold_met(lapses, ctx.leech_threshold);
let (scheduled_days, fsrs_memory_state) = self.failing_review_interval(ctx); let (scheduled_days, memory_state) = self.failing_review_interval(ctx);
let again_review = ReviewState { let again_review = ReviewState {
scheduled_days, scheduled_days,
elapsed_days: 0, elapsed_days: 0,
ease_factor: (self.ease_factor + EASE_FACTOR_AGAIN_DELTA).max(MINIMUM_EASE_FACTOR), ease_factor: (self.ease_factor + EASE_FACTOR_AGAIN_DELTA).max(MINIMUM_EASE_FACTOR),
lapses, lapses,
leeched, leeched,
fsrs_memory_state, memory_state,
}; };
if let Some(again_delay) = ctx.relearn_steps.again_delay_secs_relearn() { if let Some(again_delay) = ctx.relearn_steps.again_delay_secs_relearn() {
@ -104,7 +104,7 @@ impl ReviewState {
learning: LearnState { learning: LearnState {
remaining_steps: ctx.relearn_steps.remaining_for_failed(), remaining_steps: ctx.relearn_steps.remaining_for_failed(),
scheduled_secs: again_delay, scheduled_secs: again_delay,
fsrs_memory_state, memory_state,
}, },
review: again_review, review: again_review,
} }
@ -119,7 +119,7 @@ impl ReviewState {
scheduled_days, scheduled_days,
elapsed_days: 0, elapsed_days: 0,
ease_factor: (self.ease_factor + EASE_FACTOR_HARD_DELTA).max(MINIMUM_EASE_FACTOR), ease_factor: (self.ease_factor + EASE_FACTOR_HARD_DELTA).max(MINIMUM_EASE_FACTOR),
fsrs_memory_state: ctx.fsrs_next_states.as_ref().map(|s| s.hard.memory.into()), memory_state: ctx.fsrs_next_states.as_ref().map(|s| s.hard.memory.into()),
..self ..self
} }
} }
@ -128,7 +128,7 @@ impl ReviewState {
ReviewState { ReviewState {
scheduled_days, scheduled_days,
elapsed_days: 0, elapsed_days: 0,
fsrs_memory_state: ctx.fsrs_next_states.as_ref().map(|s| s.good.memory.into()), memory_state: ctx.fsrs_next_states.as_ref().map(|s| s.good.memory.into()),
..self ..self
} }
} }
@ -138,7 +138,7 @@ impl ReviewState {
scheduled_days, scheduled_days,
elapsed_days: 0, elapsed_days: 0,
ease_factor: self.ease_factor + EASE_FACTOR_EASY_DELTA, ease_factor: self.ease_factor + EASE_FACTOR_EASY_DELTA,
fsrs_memory_state: ctx.fsrs_next_states.as_ref().map(|s| s.easy.memory.into()), memory_state: ctx.fsrs_next_states.as_ref().map(|s| s.easy.memory.into()),
..self ..self
} }
} }
@ -313,7 +313,7 @@ mod test {
ease_factor: 1.3, ease_factor: 1.3,
lapses: 0, lapses: 0,
leeched: false, leeched: false,
fsrs_memory_state: None, memory_state: None,
}; };
ctx.fuzz_factor = Some(0.0); ctx.fuzz_factor = Some(0.0);
assert_eq!(state.passing_review_intervals(&ctx), (2, 3, 4)); assert_eq!(state.passing_review_intervals(&ctx), (2, 3, 4));
@ -342,7 +342,7 @@ mod test {
ease_factor: 1.3, ease_factor: 1.3,
lapses: 0, lapses: 0,
leeched: false, leeched: false,
fsrs_memory_state: None, memory_state: None,
}; };
ctx.fuzz_factor = Some(0.0); ctx.fuzz_factor = Some(0.0);
assert_eq!(state.passing_review_intervals(&ctx), (1, 3, 4)); assert_eq!(state.passing_review_intervals(&ctx), (1, 3, 4));

View file

@ -28,7 +28,7 @@ impl Collection {
let (due_date, due_position) = self.due_date_and_position(&card)?; let (due_date, due_position) = self.due_date_and_position(&card)?;
let timing = self.timing_today()?; let timing = self.timing_today()?;
let fsrs_retrievability = card let fsrs_retrievability = card
.fsrs_memory_state .memory_state
.zip(card.days_since_last_review(&timing)) .zip(card.days_since_last_review(&timing))
.map(|(state, days)| { .map(|(state, days)| {
FSRS::new(None) FSRS::new(None)
@ -53,7 +53,7 @@ impl Collection {
card_type: nt.get_template(card.template_idx)?.name.clone(), card_type: nt.get_template(card.template_idx)?.name.clone(),
notetype: nt.name.clone(), notetype: nt.name.clone(),
revlog: revlog.iter().rev().map(stats_revlog_entry).collect(), revlog: revlog.iter().rev().map(stats_revlog_entry).collect(),
fsrs_memory_state: card.fsrs_memory_state.map(Into::into), memory_state: card.memory_state.map(Into::into),
fsrs_retrievability, fsrs_retrievability,
custom_data: card.custom_data, custom_data: card.custom_data,
}) })

View file

@ -12,7 +12,7 @@ impl GraphsContext {
let mut eases = Eases::default(); let mut eases = Eases::default();
let mut difficulty = Eases::default(); let mut difficulty = Eases::default();
for card in &self.cards { for card in &self.cards {
if let Some(state) = card.fsrs_memory_state { if let Some(state) = card.memory_state {
*difficulty *difficulty
.eases .eases
.entry(round_to_nearest_five( .entry(round_to_nearest_five(

View file

@ -19,7 +19,7 @@ impl GraphsContext {
}; };
let fsrs = FSRS::new(None).unwrap(); let fsrs = FSRS::new(None).unwrap();
for card in &self.cards { for card in &self.cards {
if let Some(state) = card.fsrs_memory_state { if let Some(state) = card.memory_state {
let r = fsrs.current_retrievability( let r = fsrs.current_retrievability(
state.into(), state.into(),
card.days_since_last_review(&timing).unwrap_or_default(), card.days_since_last_review(&timing).unwrap_or_default(),

View file

@ -50,8 +50,8 @@ impl CardData {
pub(crate) fn from_card(card: &Card) -> Self { pub(crate) fn from_card(card: &Card) -> Self {
Self { Self {
original_position: card.original_position, original_position: card.original_position,
fsrs_stability: card.fsrs_memory_state.as_ref().map(|m| m.stability), fsrs_stability: card.memory_state.as_ref().map(|m| m.stability),
fsrs_difficulty: card.fsrs_memory_state.as_ref().map(|m| m.difficulty), fsrs_difficulty: card.memory_state.as_ref().map(|m| m.difficulty),
custom_data: card.custom_data.clone(), custom_data: card.custom_data.clone(),
} }
} }
@ -60,7 +60,7 @@ impl CardData {
serde_json::from_str(s).unwrap_or_default() serde_json::from_str(s).unwrap_or_default()
} }
pub(crate) fn fsrs_memory_state(&self) -> Option<FsrsMemoryState> { pub(crate) fn memory_state(&self) -> Option<FsrsMemoryState> {
if let Some(stability) = self.fsrs_stability { if let Some(stability) = self.fsrs_stability {
if let Some(difficulty) = self.fsrs_difficulty { if let Some(difficulty) = self.fsrs_difficulty {
return Some(FsrsMemoryState { return Some(FsrsMemoryState {

View file

@ -80,7 +80,7 @@ fn row_to_card(row: &Row) -> result::Result<Card, rusqlite::Error> {
original_deck_id: row.get(15)?, original_deck_id: row.get(15)?,
flags: row.get(16)?, flags: row.get(16)?,
original_position: data.original_position, original_position: data.original_position,
fsrs_memory_state: data.fsrs_memory_state(), memory_state: data.memory_state(),
custom_data: data.custom_data, custom_data: data.custom_data,
}) })
} }

View file

@ -296,7 +296,7 @@ fn add_extract_fsrs_retrievability(db: &Connection) -> rusqlite::Result<()> {
let review_day = due - ivl; let review_day = due - ivl;
let days_elapsed = days_elapsed.saturating_sub(review_day) as u32; let days_elapsed = days_elapsed.saturating_sub(review_day) as u32;
let card_data = &CardData::from_str(card_data); let card_data = &CardData::from_str(card_data);
Ok(card_data.fsrs_memory_state().map(|state| { Ok(card_data.memory_state().map(|state| {
FSRS::new(None) FSRS::new(None)
.unwrap() .unwrap()
.current_retrievability(state.into(), days_elapsed) .current_retrievability(state.into(), days_elapsed)

View file

@ -330,7 +330,7 @@ impl From<CardEntry> for Card {
original_deck_id: e.odid, original_deck_id: e.odid,
flags: e.flags, flags: e.flags,
original_position: data.original_position, original_position: data.original_position,
fsrs_memory_state: data.fsrs_memory_state(), memory_state: data.memory_state(),
custom_data: data.custom_data, custom_data: data.custom_data,
} }
} }

View file

@ -55,14 +55,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
value: timeSpan(stats.interval * DAY), value: timeSpan(stats.interval * DAY),
}); });
} }
if (stats.fsrsMemoryState) { if (stats.memoryState) {
let stability = timeSpan( let stability = timeSpan(stats.memoryState.stability * 86400, false, false);
stats.fsrsMemoryState.stability * 86400, if (stats.memoryState.stability > 31) {
false, const nativeStability = stats.memoryState.stability.toFixed(0);
false,
);
if (stats.fsrsMemoryState.stability > 31) {
const nativeStability = stats.fsrsMemoryState.stability.toFixed(0);
stability += ` (${nativeStability})`; stability += ` (${nativeStability})`;
} }
statsRows.push({ statsRows.push({
@ -70,7 +66,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
value: stability, value: stability,
}); });
const difficulty = ( const difficulty = (
((stats.fsrsMemoryState.difficulty - 1.0) / 9.0) * ((stats.memoryState.difficulty - 1.0) / 9.0) *
100.0 100.0
).toFixed(0); ).toFixed(0);
statsRows.push({ statsRows.push({