move reschedCards() to backend

This commit is contained in:
Damien Elmes 2020-09-02 14:41:46 +10:00
parent 8f9037cf0f
commit 39212a38aa
8 changed files with 86 additions and 25 deletions

View file

@ -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;
}

View file

@ -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."

View file

@ -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
//----------------------------------------------- //-----------------------------------------------

View file

@ -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;

View file

@ -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)?;
} }
} }

View file

@ -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;

View file

@ -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;

View 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(())
})
}
}