// Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html mod answering; mod states; use super::Backend; pub(super) use crate::pb::scheduler::scheduler_service::Service as SchedulerService; use crate::{ pb, prelude::*, scheduler::{ new::NewCardDueOrder, states::{CardState, SchedulingStates}, }, stats::studied_today, }; impl SchedulerService for Backend { /// This behaves like _updateCutoff() in older code - it also unburies at the start of /// a new day. fn sched_timing_today( &self, _input: pb::generic::Empty, ) -> Result { self.with_col(|col| { let timing = col.timing_today()?; col.unbury_if_day_rolled_over(timing)?; Ok(timing.into()) }) } /// Fetch data from DB and return rendered string. fn studied_today(&self, _input: pb::generic::Empty) -> Result { self.with_col(|col| col.studied_today().map(Into::into)) } /// Message rendering only, for old graphs. fn studied_today_message( &self, input: pb::scheduler::StudiedTodayMessageRequest, ) -> Result { Ok(studied_today(input.cards, input.seconds as f32, &self.tr).into()) } fn update_stats(&self, input: pb::scheduler::UpdateStatsRequest) -> Result { self.with_col(|col| { col.transact_no_undo(|col| { let today = col.current_due_day(0)?; let usn = col.usn()?; col.update_deck_stats(today, usn, input).map(Into::into) }) }) } fn extend_limits( &self, input: pb::scheduler::ExtendLimitsRequest, ) -> Result { self.with_col(|col| { col.transact_no_undo(|col| { let today = col.current_due_day(0)?; let usn = col.usn()?; col.extend_limits( today, usn, input.deck_id.into(), input.new_delta, input.review_delta, ) .map(Into::into) }) }) } fn counts_for_deck_today( &self, input: pb::decks::DeckId, ) -> Result { self.with_col(|col| col.counts_for_deck_today(input.did.into())) } fn congrats_info( &self, _input: pb::generic::Empty, ) -> Result { self.with_col(|col| col.congrats_info()) } fn restore_buried_and_suspended_cards( &self, input: pb::cards::CardIds, ) -> Result { let cids: Vec<_> = input.into(); self.with_col(|col| col.unbury_or_unsuspend_cards(&cids).map(Into::into)) } fn unbury_deck( &self, input: pb::scheduler::UnburyDeckRequest, ) -> Result { self.with_col(|col| { col.unbury_deck(input.deck_id.into(), input.mode()) .map(Into::into) }) } fn bury_or_suspend_cards( &self, input: pb::scheduler::BuryOrSuspendCardsRequest, ) -> Result { self.with_col(|col| { let mode = input.mode(); let cids = if input.card_ids.is_empty() { col.storage .card_ids_of_notes(&input.note_ids.into_newtype(NoteId))? } else { input.card_ids.into_newtype(CardId) }; col.bury_or_suspend_cards(&cids, mode).map(Into::into) }) } fn empty_filtered_deck(&self, input: pb::decks::DeckId) -> Result { self.with_col(|col| col.empty_filtered_deck(input.did.into()).map(Into::into)) } fn rebuild_filtered_deck( &self, input: pb::decks::DeckId, ) -> Result { self.with_col(|col| col.rebuild_filtered_deck(input.did.into()).map(Into::into)) } fn schedule_cards_as_new( &self, input: pb::scheduler::ScheduleCardsAsNewRequest, ) -> Result { self.with_col(|col| { let cids = input.card_ids.into_newtype(CardId); col.reschedule_cards_as_new( &cids, input.log, input.restore_position, input.reset_counts, input .context .and_then(pb::scheduler::schedule_cards_as_new_request::Context::from_i32), ) .map(Into::into) }) } fn schedule_cards_as_new_defaults( &self, input: pb::scheduler::ScheduleCardsAsNewDefaultsRequest, ) -> Result { self.with_col(|col| Ok(col.reschedule_cards_as_new_defaults(input.context()))) } fn set_due_date( &self, input: pb::scheduler::SetDueDateRequest, ) -> Result { let config = input.config_key.map(|v| v.key().into()); let days = input.days; let cids = input.card_ids.into_newtype(CardId); self.with_col(|col| col.set_due_date(&cids, &days, config).map(Into::into)) } fn sort_cards( &self, input: pb::scheduler::SortCardsRequest, ) -> Result { let cids = input.card_ids.into_newtype(CardId); let (start, step, random, shift) = ( input.starting_from, input.step_size, input.randomize, input.shift_existing, ); let order = if random { NewCardDueOrder::Random } else { NewCardDueOrder::Preserve }; self.with_col(|col| { col.sort_cards(&cids, start, step, order, shift) .map(Into::into) }) } fn reposition_defaults( &self, _input: pb::generic::Empty, ) -> Result { self.with_col(|col| Ok(col.reposition_defaults())) } fn sort_deck( &self, input: pb::scheduler::SortDeckRequest, ) -> Result { self.with_col(|col| { col.sort_deck_legacy(input.deck_id.into(), input.randomize) .map(Into::into) }) } fn get_scheduling_states( &self, input: pb::cards::CardId, ) -> Result { let cid: CardId = input.into(); self.with_col(|col| col.get_scheduling_states(cid)) .map(Into::into) } fn describe_next_states( &self, input: pb::scheduler::SchedulingStates, ) -> Result { let states: SchedulingStates = input.into(); self.with_col(|col| col.describe_next_states(states)) .map(Into::into) } fn state_is_leech(&self, input: pb::scheduler::SchedulingState) -> Result { let state: CardState = input.into(); Ok(state.leeched().into()) } fn answer_card(&self, input: pb::scheduler::CardAnswer) -> Result { self.with_col(|col| col.answer_card(&mut input.into())) .map(Into::into) } fn upgrade_scheduler(&self, _input: pb::generic::Empty) -> Result { self.with_col(|col| col.transact_no_undo(|col| col.upgrade_to_v2_scheduler())) .map(Into::into) } fn get_queued_cards( &self, input: pb::scheduler::GetQueuedCardsRequest, ) -> Result { self.with_col(|col| { col.get_queued_cards(input.fetch_limit as usize, input.intraday_learning_only) .map(Into::into) }) } fn custom_study( &self, input: pb::scheduler::CustomStudyRequest, ) -> Result { self.with_col(|col| col.custom_study(input)).map(Into::into) } fn custom_study_defaults( &self, input: pb::scheduler::CustomStudyDefaultsRequest, ) -> Result { self.with_col(|col| col.custom_study_defaults(input.deck_id.into())) } } impl From for pb::scheduler::SchedTimingTodayResponse { fn from( t: crate::scheduler::timing::SchedTimingToday, ) -> pb::scheduler::SchedTimingTodayResponse { pb::scheduler::SchedTimingTodayResponse { days_elapsed: t.days_elapsed, next_day_at: t.next_day_at.0, } } }