diff --git a/proto/anki/scheduler.proto b/proto/anki/scheduler.proto index 1823184a3..41dad12f1 100644 --- a/proto/anki/scheduler.proto +++ b/proto/anki/scheduler.proto @@ -47,8 +47,6 @@ service SchedulerService { rpc RepositionDefaults(generic.Empty) returns (RepositionDefaultsResponse); rpc ComputeFsrsWeights(ComputeFsrsWeightsRequest) returns (ComputeFsrsWeightsResponse); - rpc ComputeFsrsWeightsFromItems(ComputeFsrsWeightsFromItemsRequest) - returns (ComputeFsrsWeightsResponse); rpc GetOptimalRetentionParameters(GetOptimalRetentionParametersRequest) returns (GetOptimalRetentionParametersResponse); rpc ComputeOptimalRetention(ComputeOptimalRetentionRequest) @@ -59,7 +57,10 @@ service SchedulerService { // Implicitly includes any of the above methods that are not listed in the // backend service. -service BackendSchedulerService {} +service BackendSchedulerService { + rpc ComputeFsrsWeightsFromItems(ComputeFsrsWeightsFromItemsRequest) + returns (ComputeFsrsWeightsResponse); +} message SchedulingState { message New { diff --git a/pylib/anki/_backend.py b/pylib/anki/_backend.py index 7e5d68221..617754578 100644 --- a/pylib/anki/_backend.py +++ b/pylib/anki/_backend.py @@ -5,7 +5,7 @@ from __future__ import annotations import sys import traceback -from typing import Any, Sequence +from typing import TYPE_CHECKING, Any, Iterable, Sequence from weakref import ref from markdown import markdown @@ -18,6 +18,9 @@ from anki.dbproxy import Row as DBRow from anki.dbproxy import ValueForDB from anki.utils import from_json_bytes, to_json_bytes +if TYPE_CHECKING: + from anki.collection import FsrsItem + from .errors import ( BackendError, BackendIOError, @@ -140,6 +143,9 @@ class RustBackend(RustBackendGenerated): ) return self.format_timespan(seconds=seconds, context=context) + def compute_weights_from_items(self, items: Iterable[FsrsItem]) -> Sequence[float]: + return self.compute_fsrs_weights_from_items(items).weights + def _run_command(self, service: int, method: int, input: bytes) -> bytes: try: return self._backend.command(service, method, input) diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 57281e32a..32e73436c 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -1341,9 +1341,6 @@ class Collection(DeprecatedNamesMixin): else: return ComputedMemoryState(desired_retention=resp.desired_retention) - def compute_weights_from_items(self, items: Iterable[FsrsItem]) -> Sequence[float]: - return self._backend.compute_fsrs_weights_from_items(items).weights - # Timeboxing ########################################################################## # fixme: there doesn't seem to be a good reason why this code is in main.py diff --git a/rslib/src/scheduler/fsrs/weights.rs b/rslib/src/scheduler/fsrs/weights.rs index e0bc77014..7464e40b3 100644 --- a/rslib/src/scheduler/fsrs/weights.rs +++ b/rslib/src/scheduler/fsrs/weights.rs @@ -4,7 +4,6 @@ use std::iter; use std::thread; use std::time::Duration; -use anki_proto::scheduler::ComputeFsrsWeightsFromItemsRequest; use anki_proto::scheduler::ComputeFsrsWeightsResponse; use fsrs::FSRSItem; use fsrs::FSRSReview; @@ -54,22 +53,6 @@ impl Collection { }) } - pub fn compute_weights_from_items( - &mut self, - req: ComputeFsrsWeightsFromItemsRequest, - ) -> Result { - let fsrs = FSRS::new(None)?; - let fsrs_items = req.items.len() as u32; - let weights = fsrs.compute_weights( - req.items.into_iter().map(fsrs_item_proto_to_fsrs).collect(), - None, - )?; - Ok(ComputeFsrsWeightsResponse { - weights, - fsrs_items, - }) - } - pub(crate) fn revlog_for_srs( &mut self, search: impl TryIntoSearch, @@ -226,23 +209,6 @@ impl RevlogEntry { } } -fn fsrs_item_proto_to_fsrs(item: anki_proto::scheduler::FsrsItem) -> FSRSItem { - FSRSItem { - reviews: item - .reviews - .into_iter() - .map(fsrs_review_proto_to_fsrs) - .collect(), - } -} - -fn fsrs_review_proto_to_fsrs(review: anki_proto::scheduler::FsrsReview) -> FSRSReview { - FSRSReview { - delta_t: review.delta_t, - rating: review.rating, - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/rslib/src/scheduler/service/mod.rs b/rslib/src/scheduler/service/mod.rs index e94a966b4..f63dfa390 100644 --- a/rslib/src/scheduler/service/mod.rs +++ b/rslib/src/scheduler/service/mod.rs @@ -7,11 +7,16 @@ mod states; use anki_proto::cards; use anki_proto::generic; use anki_proto::scheduler; +use anki_proto::scheduler::ComputeFsrsWeightsResponse; use anki_proto::scheduler::ComputeMemoryStateResponse; use anki_proto::scheduler::ComputeOptimalRetentionRequest; use anki_proto::scheduler::ComputeOptimalRetentionResponse; use anki_proto::scheduler::GetOptimalRetentionParametersResponse; +use fsrs::FSRSItem; +use fsrs::FSRSReview; +use fsrs::FSRS; +use crate::backend::Backend; use crate::prelude::*; use crate::scheduler::new::NewCardDueOrder; use crate::scheduler::states::CardState; @@ -250,13 +255,6 @@ impl crate::services::SchedulerService for Collection { self.compute_weights(&input.search) } - fn compute_fsrs_weights_from_items( - &mut self, - input: scheduler::ComputeFsrsWeightsFromItemsRequest, - ) -> Result { - self.compute_weights_from_items(input) - } - fn compute_optimal_retention( &mut self, input: ComputeOptimalRetentionRequest, @@ -291,3 +289,38 @@ impl crate::services::SchedulerService for Collection { self.compute_memory_state(input.into()) } } + +impl crate::services::BackendSchedulerService for Backend { + fn compute_fsrs_weights_from_items( + &self, + req: scheduler::ComputeFsrsWeightsFromItemsRequest, + ) -> Result { + let fsrs = FSRS::new(None)?; + let fsrs_items = req.items.len() as u32; + let weights = fsrs.compute_weights( + req.items.into_iter().map(fsrs_item_proto_to_fsrs).collect(), + None, + )?; + Ok(ComputeFsrsWeightsResponse { + weights, + fsrs_items, + }) + } +} + +fn fsrs_item_proto_to_fsrs(item: anki_proto::scheduler::FsrsItem) -> FSRSItem { + FSRSItem { + reviews: item + .reviews + .into_iter() + .map(fsrs_review_proto_to_fsrs) + .collect(), + } +} + +fn fsrs_review_proto_to_fsrs(review: anki_proto::scheduler::FsrsReview) -> FSRSReview { + FSRSReview { + delta_t: review.delta_t, + rating: review.rating, + } +}