mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
move reschedCards() to backend
This commit is contained in:
parent
8f9037cf0f
commit
39212a38aa
8 changed files with 86 additions and 25 deletions
|
@ -105,6 +105,7 @@ service BackendService {
|
||||||
rpc BuryOrSuspendCards (BuryOrSuspendCardsIn) returns (Empty);
|
rpc BuryOrSuspendCards (BuryOrSuspendCardsIn) returns (Empty);
|
||||||
rpc EmptyFilteredDeck (DeckID) returns (Empty);
|
rpc EmptyFilteredDeck (DeckID) returns (Empty);
|
||||||
rpc RebuildFilteredDeck (DeckID) returns (UInt32);
|
rpc RebuildFilteredDeck (DeckID) returns (UInt32);
|
||||||
|
rpc ScheduleCardsAsReviews (ScheduleCardsAsReviewsIn) returns (Empty);
|
||||||
|
|
||||||
// stats
|
// stats
|
||||||
|
|
||||||
|
@ -1056,3 +1057,9 @@ message BuryOrSuspendCardsIn {
|
||||||
repeated int64 card_ids = 1;
|
repeated int64 card_ids = 1;
|
||||||
Mode mode = 2;
|
Mode mode = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ScheduleCardsAsReviewsIn {
|
||||||
|
repeated int64 card_ids = 1;
|
||||||
|
uint32 min_interval = 2;
|
||||||
|
uint32 max_interval = 3;
|
||||||
|
}
|
||||||
|
|
|
@ -1419,29 +1419,9 @@ and (queue={QUEUE_TYPE_NEW} or (queue={QUEUE_TYPE_REV} and due<=?))""",
|
||||||
|
|
||||||
def reschedCards(self, ids: List[int], imin: int, imax: int) -> None:
|
def reschedCards(self, ids: List[int], imin: int, imax: int) -> None:
|
||||||
"Put cards in review queue with a new interval in days (min, max)."
|
"Put cards in review queue with a new interval in days (min, max)."
|
||||||
d = []
|
self.col.backend.schedule_cards_as_reviews(
|
||||||
t = self.today
|
card_ids=ids, min_interval=imin, max_interval=imax
|
||||||
mod = intTime()
|
|
||||||
for id in ids:
|
|
||||||
r = random.randint(imin, imax)
|
|
||||||
d.append(
|
|
||||||
(
|
|
||||||
max(1, r),
|
|
||||||
r + t,
|
|
||||||
self.col.usn(),
|
|
||||||
mod,
|
|
||||||
STARTING_FACTOR,
|
|
||||||
id,
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
self.remFromDyn(ids)
|
|
||||||
self.col.db.executemany(
|
|
||||||
f"""
|
|
||||||
update cards set type={CARD_TYPE_REV},queue={QUEUE_TYPE_REV},ivl=?,due=?,odue=0,
|
|
||||||
usn=?,mod=?,factor=? where id=?""",
|
|
||||||
d,
|
|
||||||
)
|
|
||||||
self.col.log(ids)
|
|
||||||
|
|
||||||
def resetCards(self, ids: List[int]) -> None:
|
def resetCards(self, ids: List[int]) -> None:
|
||||||
"Completely reset cards for export."
|
"Completely reset cards for export."
|
||||||
|
|
|
@ -547,6 +547,18 @@ impl BackendService for Backend {
|
||||||
self.with_col(|col| col.rebuild_filtered_deck(input.did.into()).map(Into::into))
|
self.with_col(|col| col.rebuild_filtered_deck(input.did.into()).map(Into::into))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn schedule_cards_as_reviews(
|
||||||
|
&mut self,
|
||||||
|
input: pb::ScheduleCardsAsReviewsIn,
|
||||||
|
) -> BackendResult<Empty> {
|
||||||
|
let cids: Vec<_> = input.card_ids.into_iter().map(CardID).collect();
|
||||||
|
let (min, max) = (input.min_interval, input.max_interval);
|
||||||
|
self.with_col(|col| {
|
||||||
|
col.reschedule_cards_as_reviews(&cids, min, max)
|
||||||
|
.map(Into::into)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// statistics
|
// statistics
|
||||||
//-----------------------------------------------
|
//-----------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,18 @@ impl Card {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn remove_from_filtered_deck(&mut self, sched: SchedulerVersion) {
|
/// Restores to the original deck and clears original_due.
|
||||||
|
/// This does not update the queue or type, so should only be used as
|
||||||
|
/// part of an operation that adjusts those separately.
|
||||||
|
pub(crate) fn remove_from_filtered_deck_before_reschedule(&mut self) {
|
||||||
|
if self.original_deck_id.0 != 0 {
|
||||||
|
self.deck_id = self.original_deck_id;
|
||||||
|
self.original_deck_id.0 = 0;
|
||||||
|
self.original_due = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn remove_from_filtered_deck_restoring_queue(&mut self, sched: SchedulerVersion) {
|
||||||
if self.original_deck_id.0 == 0 {
|
if self.original_deck_id.0 == 0 {
|
||||||
// not in a filtered deck
|
// not in a filtered deck
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -66,7 +66,7 @@ impl Collection {
|
||||||
for cid in cids {
|
for cid in cids {
|
||||||
if let Some(mut card) = self.storage.get_card(*cid)? {
|
if let Some(mut card) = self.storage.get_card(*cid)? {
|
||||||
let original = card.clone();
|
let original = card.clone();
|
||||||
card.remove_from_filtered_deck(sched);
|
card.remove_from_filtered_deck_restoring_queue(sched);
|
||||||
self.update_card(&mut card, &original, usn)?;
|
self.update_card(&mut card, &original, usn)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,7 @@ impl Collection {
|
||||||
};
|
};
|
||||||
if card.queue != desired_queue {
|
if card.queue != desired_queue {
|
||||||
if sched == SchedulerVersion::V1 {
|
if sched == SchedulerVersion::V1 {
|
||||||
card.remove_from_filtered_deck(sched);
|
card.remove_from_filtered_deck_restoring_queue(sched);
|
||||||
card.remove_from_learning();
|
card.remove_from_learning();
|
||||||
}
|
}
|
||||||
card.queue = desired_queue;
|
card.queue = desired_queue;
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
pub mod bury_and_suspend;
|
pub mod bury_and_suspend;
|
||||||
pub(crate) mod congrats;
|
pub(crate) mod congrats;
|
||||||
pub mod cutoff;
|
pub mod cutoff;
|
||||||
|
mod reviews;
|
||||||
pub mod timespan;
|
pub mod timespan;
|
||||||
|
|
||||||
use chrono::FixedOffset;
|
use chrono::FixedOffset;
|
||||||
|
|
50
rslib/src/sched/reviews.rs
Normal file
50
rslib/src/sched/reviews.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
card::{Card, CardID, CardQueue, CardType},
|
||||||
|
collection::Collection,
|
||||||
|
deckconf::INITIAL_EASE_FACTOR,
|
||||||
|
err::Result,
|
||||||
|
};
|
||||||
|
use rand::distributions::{Distribution, Uniform};
|
||||||
|
|
||||||
|
impl Card {
|
||||||
|
fn schedule_as_review(&mut self, interval: u32, today: u32) {
|
||||||
|
self.remove_from_filtered_deck_before_reschedule();
|
||||||
|
self.interval = interval.max(1);
|
||||||
|
self.due = (today + interval) as i32;
|
||||||
|
self.ctype = CardType::Review;
|
||||||
|
self.queue = CardQueue::Review;
|
||||||
|
if self.ease_factor == 0 {
|
||||||
|
// unlike the old Python code, we leave the ease factor alone
|
||||||
|
// if it's already set
|
||||||
|
self.ease_factor = INITIAL_EASE_FACTOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collection {
|
||||||
|
pub fn reschedule_cards_as_reviews(
|
||||||
|
&mut self,
|
||||||
|
cids: &[CardID],
|
||||||
|
min_days: u32,
|
||||||
|
max_days: u32,
|
||||||
|
) -> Result<()> {
|
||||||
|
let usn = self.usn()?;
|
||||||
|
let today = self.timing_today()?.days_elapsed;
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let distribution = Uniform::from(min_days..=max_days);
|
||||||
|
self.transact(None, |col| {
|
||||||
|
col.set_search_table_to_card_ids(cids)?;
|
||||||
|
for mut card in col.storage.all_searched_cards()? {
|
||||||
|
let original = card.clone();
|
||||||
|
let interval = distribution.sample(&mut rng);
|
||||||
|
card.schedule_as_review(interval, today);
|
||||||
|
col.update_card(&mut card, &original, usn)?;
|
||||||
|
}
|
||||||
|
col.clear_searched_cards()?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue