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)
|
rpc ComputeOptimalRetention(ComputeOptimalRetentionRequest)
|
||||||
returns (ComputeOptimalRetentionResponse);
|
returns (ComputeOptimalRetentionResponse);
|
||||||
rpc EvaluateWeights(EvaluateWeightsRequest) returns (EvaluateWeightsResponse);
|
rpc EvaluateWeights(EvaluateWeightsRequest) returns (EvaluateWeightsResponse);
|
||||||
|
rpc ComputeMemoryState(cards.CardId) returns (ComputeMemoryStateResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implicitly includes any of the above methods that are not listed in the
|
// Implicitly includes any of the above methods that are not listed in the
|
||||||
|
@ -383,3 +384,8 @@ message EvaluateWeightsResponse {
|
||||||
float log_loss = 1;
|
float log_loss = 1;
|
||||||
float rmse_bins = 2;
|
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]
|
ExportLimit = Union[DeckIdLimit, NoteIdsLimit, CardIdsLimit, None]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ComputedMemoryState:
|
||||||
|
desired_retention: float
|
||||||
|
stability: float | None = None
|
||||||
|
difficulty: float | None = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class AddNoteRequest:
|
class AddNoteRequest:
|
||||||
note: Note
|
note: Note
|
||||||
|
@ -1320,6 +1327,17 @@ class Collection(DeprecatedNamesMixin):
|
||||||
def extract_cloze_for_typing(self, text: str, ordinal: int) -> str:
|
def extract_cloze_for_typing(self, text: str, ordinal: int) -> str:
|
||||||
return self._backend.extract_cloze_for_typing(text=text, ordinal=ordinal)
|
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
|
# Timeboxing
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# fixme: there doesn't seem to be a good reason why this code is in main.py
|
# 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
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
use anki_proto::scheduler::ComputeMemoryStateResponse;
|
||||||
use fsrs::FSRS;
|
use fsrs::FSRS;
|
||||||
|
|
||||||
|
use crate::card::FsrsMemoryState;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::scheduler::fsrs::weights::fsrs_items_for_memory_state;
|
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::scheduler::fsrs::weights::Weights;
|
||||||
use crate::search::JoinSearches;
|
use crate::search::JoinSearches;
|
||||||
use crate::search::Negated;
|
use crate::search::Negated;
|
||||||
|
@ -57,4 +60,33 @@ impl Collection {
|
||||||
}
|
}
|
||||||
Ok(())
|
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]`
|
/// `[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
|
/// in training, and `[1]`, [1,2]` and `[1,2,3]` when calculating memory
|
||||||
/// state.
|
/// state.
|
||||||
fn single_card_revlog_to_items(
|
pub(crate) fn single_card_revlog_to_items(
|
||||||
mut entries: Vec<RevlogEntry>,
|
mut entries: Vec<RevlogEntry>,
|
||||||
next_day_at: TimestampSecs,
|
next_day_at: TimestampSecs,
|
||||||
training: bool,
|
training: bool,
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
mod answering;
|
mod answering;
|
||||||
mod states;
|
mod states;
|
||||||
|
|
||||||
|
use anki_proto::cards;
|
||||||
use anki_proto::generic;
|
use anki_proto::generic;
|
||||||
use anki_proto::scheduler;
|
use anki_proto::scheduler;
|
||||||
|
use anki_proto::scheduler::ComputeMemoryStateResponse;
|
||||||
use anki_proto::scheduler::ComputeOptimalRetentionRequest;
|
use anki_proto::scheduler::ComputeOptimalRetentionRequest;
|
||||||
use anki_proto::scheduler::ComputeOptimalRetentionResponse;
|
use anki_proto::scheduler::ComputeOptimalRetentionResponse;
|
||||||
use anki_proto::scheduler::GetOptimalRetentionParametersResponse;
|
use anki_proto::scheduler::GetOptimalRetentionParametersResponse;
|
||||||
|
@ -277,4 +279,8 @@ impl crate::services::SchedulerService for Collection {
|
||||||
params: Some(params),
|
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