From 9268dce70790cce41c0f792e7305499a828ed3e9 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Mon, 6 Nov 2023 12:27:53 +1000 Subject: [PATCH] Expose fuzz delta for FSRS add-on --- proto/anki/scheduler.proto | 12 ++++++++++++ pylib/anki/collection.py | 4 ++++ rslib/src/scheduler/answering/mod.rs | 2 +- rslib/src/scheduler/service/mod.rs | 8 ++++++++ rslib/src/scheduler/states/fuzz.rs | 21 +++++++++++++++++++++ 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/proto/anki/scheduler.proto b/proto/anki/scheduler.proto index 525b35caf..3370138bd 100644 --- a/proto/anki/scheduler.proto +++ b/proto/anki/scheduler.proto @@ -53,6 +53,9 @@ service SchedulerService { returns (ComputeOptimalRetentionResponse); rpc EvaluateWeights(EvaluateWeightsRequest) returns (EvaluateWeightsResponse); rpc ComputeMemoryState(cards.CardId) returns (ComputeMemoryStateResponse); + // The number of days the calculated interval will be fuzzed by. Utilized by + // the FSRS add-on. + rpc FuzzDelta(FuzzDeltaRequest) returns (FuzzDeltaResponse); } // Implicitly includes any of the above methods that are not listed in the @@ -406,3 +409,12 @@ message ComputeMemoryStateResponse { optional cards.FsrsMemoryState state = 1; float desired_retention = 2; } + +message FuzzDeltaRequest { + int64 card_id = 1; + uint32 interval = 2; +} + +message FuzzDeltaResponse { + sint32 delta_days = 1; +} diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 76d2c406a..c1993130f 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -1153,6 +1153,10 @@ class Collection(DeprecatedNamesMixin): else: return ComputedMemoryState(desired_retention=resp.desired_retention) + def fuzz_delta(self, card_id: CardId, interval: int) -> int: + "The delta days of fuzz applied if reviewing the card in v3." + return self._backend.fuzz_delta(card_id=card_id, interval=interval) + # Timeboxing ########################################################################## # fixme: there doesn't seem to be a good reason why this code is in main.py diff --git a/rslib/src/scheduler/answering/mod.rs b/rslib/src/scheduler/answering/mod.rs index 84caf150a..fe00d61ff 100644 --- a/rslib/src/scheduler/answering/mod.rs +++ b/rslib/src/scheduler/answering/mod.rs @@ -395,7 +395,7 @@ impl Collection { }) } - fn home_deck_config( + pub(crate) fn home_deck_config( &self, config_id: Option, home_deck_id: DeckId, diff --git a/rslib/src/scheduler/service/mod.rs b/rslib/src/scheduler/service/mod.rs index 6a3895c2b..d98c53e11 100644 --- a/rslib/src/scheduler/service/mod.rs +++ b/rslib/src/scheduler/service/mod.rs @@ -11,6 +11,8 @@ use anki_proto::scheduler::ComputeFsrsWeightsResponse; use anki_proto::scheduler::ComputeMemoryStateResponse; use anki_proto::scheduler::ComputeOptimalRetentionRequest; use anki_proto::scheduler::ComputeOptimalRetentionResponse; +use anki_proto::scheduler::FuzzDeltaRequest; +use anki_proto::scheduler::FuzzDeltaResponse; use anki_proto::scheduler::GetOptimalRetentionParametersResponse; use fsrs::FSRSItem; use fsrs::FSRSReview; @@ -288,6 +290,12 @@ impl crate::services::SchedulerService for Collection { fn compute_memory_state(&mut self, input: cards::CardId) -> Result { self.compute_memory_state(input.into()) } + + fn fuzz_delta(&mut self, input: FuzzDeltaRequest) -> Result { + Ok(FuzzDeltaResponse { + delta_days: self.get_fuzz_delta(input.card_id.into(), input.interval)?, + }) + } } impl crate::services::BackendSchedulerService for Backend { diff --git a/rslib/src/scheduler/states/fuzz.rs b/rslib/src/scheduler/states/fuzz.rs index d541f432d..1a893aacd 100644 --- a/rslib/src/scheduler/states/fuzz.rs +++ b/rslib/src/scheduler/states/fuzz.rs @@ -2,6 +2,8 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use super::StateContext; +use crate::collection::Collection; +use crate::prelude::*; /// Describes a range of days for which a certain amount of fuzz is applied to /// the new interval. @@ -37,6 +39,25 @@ impl<'a> StateContext<'a> { } } +impl Collection { + /// Used for FSRS add-on. + pub(crate) fn get_fuzz_delta(&self, card_id: CardId, interval: u32) -> Result { + let card = self.storage.get_card(card_id)?.or_not_found(card_id)?; + let deck = self + .storage + .get_deck(card.deck_id)? + .or_not_found(card.deck_id)?; + let config = self.home_deck_config(deck.config_id(), card.original_deck_id)?; + let fuzzed = with_review_fuzz( + card.get_fuzz_factor(), + interval as f32, + 1, + config.inner.maximum_review_interval, + ); + Ok((fuzzed as i32) - (interval as i32)) + } +} + pub(crate) fn with_review_fuzz( fuzz_factor: Option, interval: f32,