mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
parent
4dc9890845
commit
e6aaeb85e9
5 changed files with 63 additions and 1 deletions
|
@ -52,6 +52,7 @@ service SchedulerService {
|
|||
rpc ComputeOptimalRetention(ComputeOptimalRetentionRequest)
|
||||
returns (ComputeOptimalRetentionResponse);
|
||||
rpc EvaluateWeights(EvaluateWeightsRequest) returns (EvaluateWeightsResponse);
|
||||
rpc ComputeMemoryState(cards.CardId) returns (ComputeMemoryStateResponse);
|
||||
}
|
||||
|
||||
// Implicitly includes any of the above methods that are not listed in the
|
||||
|
@ -383,3 +384,8 @@ message EvaluateWeightsResponse {
|
|||
float log_loss = 1;
|
||||
float rmse_bins = 2;
|
||||
}
|
||||
|
||||
message ComputeMemoryStateResponse {
|
||||
optional cards.FsrsMemoryState state = 1;
|
||||
float desired_retention = 2;
|
||||
}
|
||||
|
|
|
@ -127,6 +127,13 @@ class CardIdsLimit:
|
|||
ExportLimit = Union[DeckIdLimit, NoteIdsLimit, CardIdsLimit, None]
|
||||
|
||||
|
||||
@dataclass
|
||||
class ComputedMemoryState:
|
||||
desired_retention: float
|
||||
stability: float | None = None
|
||||
difficulty: float | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class AddNoteRequest:
|
||||
note: Note
|
||||
|
@ -1320,6 +1327,17 @@ class Collection(DeprecatedNamesMixin):
|
|||
def extract_cloze_for_typing(self, text: str, ordinal: int) -> str:
|
||||
return self._backend.extract_cloze_for_typing(text=text, ordinal=ordinal)
|
||||
|
||||
def compute_memory_state(self, card_id: CardId) -> ComputedMemoryState:
|
||||
resp = self._backend.compute_memory_state(card_id)
|
||||
if resp.HasField("state"):
|
||||
return ComputedMemoryState(
|
||||
desired_retention=resp.desired_retention,
|
||||
stability=resp.state.stability,
|
||||
difficulty=resp.state.difficulty,
|
||||
)
|
||||
else:
|
||||
return ComputedMemoryState(desired_retention=resp.desired_retention)
|
||||
|
||||
# Timeboxing
|
||||
##########################################################################
|
||||
# fixme: there doesn't seem to be a good reason why this code is in main.py
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
// Copyright: Ankitects Pty Ltd and contributors
|
||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
use anki_proto::scheduler::ComputeMemoryStateResponse;
|
||||
use fsrs::FSRS;
|
||||
|
||||
use crate::card::FsrsMemoryState;
|
||||
use crate::prelude::*;
|
||||
use crate::scheduler::fsrs::weights::fsrs_items_for_memory_state;
|
||||
use crate::scheduler::fsrs::weights::single_card_revlog_to_items;
|
||||
use crate::scheduler::fsrs::weights::Weights;
|
||||
use crate::search::JoinSearches;
|
||||
use crate::search::Negated;
|
||||
|
@ -57,4 +60,33 @@ impl Collection {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_memory_state(&mut self, card_id: CardId) -> Result<ComputeMemoryStateResponse> {
|
||||
let card = self.storage.get_card(card_id)?.or_not_found(card_id)?;
|
||||
let deck_id = card.original_deck_id.or(card.deck_id);
|
||||
let deck = self.get_deck(deck_id)?.or_not_found(card.deck_id)?;
|
||||
let conf_id = DeckConfigId(deck.normal()?.config_id);
|
||||
let config = self
|
||||
.storage
|
||||
.get_deck_config(conf_id)?
|
||||
.or_not_found(conf_id)?;
|
||||
let desired_retention = config.inner.desired_retention;
|
||||
let fsrs = FSRS::new(Some(&config.inner.fsrs_weights))?;
|
||||
let revlog = self.revlog_for_srs(SearchNode::CardIds(card.id.to_string()))?;
|
||||
let items = single_card_revlog_to_items(revlog, self.timing_today()?.next_day_at, false);
|
||||
if let Some(mut items) = items {
|
||||
if let Some(last) = items.pop() {
|
||||
let state = fsrs.memory_state(last);
|
||||
let state = FsrsMemoryState::from(state);
|
||||
return Ok(ComputeMemoryStateResponse {
|
||||
state: Some(state.into()),
|
||||
desired_retention,
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(ComputeMemoryStateResponse {
|
||||
state: None,
|
||||
desired_retention,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ pub(crate) fn fsrs_items_for_memory_state(
|
|||
/// `[1,2,3]`, we create FSRSItems corresponding to `[1,2]` and `[1,2,3]`
|
||||
/// in training, and `[1]`, [1,2]` and `[1,2,3]` when calculating memory
|
||||
/// state.
|
||||
fn single_card_revlog_to_items(
|
||||
pub(crate) fn single_card_revlog_to_items(
|
||||
mut entries: Vec<RevlogEntry>,
|
||||
next_day_at: TimestampSecs,
|
||||
training: bool,
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
mod answering;
|
||||
mod states;
|
||||
|
||||
use anki_proto::cards;
|
||||
use anki_proto::generic;
|
||||
use anki_proto::scheduler;
|
||||
use anki_proto::scheduler::ComputeMemoryStateResponse;
|
||||
use anki_proto::scheduler::ComputeOptimalRetentionRequest;
|
||||
use anki_proto::scheduler::ComputeOptimalRetentionResponse;
|
||||
use anki_proto::scheduler::GetOptimalRetentionParametersResponse;
|
||||
|
@ -277,4 +279,8 @@ impl crate::services::SchedulerService for Collection {
|
|||
params: Some(params),
|
||||
})
|
||||
}
|
||||
|
||||
fn compute_memory_state(&mut self, input: cards::CardId) -> Result<ComputeMemoryStateResponse> {
|
||||
self.compute_memory_state(input.into())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue