change get_queued_cards() to no longer return congrats info
This commit is contained in:
parent
1d2e89d206
commit
57ec4cc7b5
@ -2,8 +2,9 @@
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
"""
|
||||
This file contains experimental scheduler changes, and is not currently
|
||||
used by Anki.
|
||||
This file contains experimental scheduler changes:
|
||||
|
||||
https://betas.ankiweb.net/2021-scheduler.html
|
||||
|
||||
It uses the same DB schema as the V2 scheduler, and 'schedVer' remains
|
||||
as '2' internally.
|
||||
@ -11,7 +12,7 @@ as '2' internally.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Literal, Sequence, Tuple, Union
|
||||
from typing import List, Literal, Sequence, Tuple
|
||||
|
||||
import anki._backend.backend_pb2 as _pb
|
||||
from anki.cards import Card, CardId
|
||||
@ -19,7 +20,6 @@ from anki.collection import OpChanges
|
||||
from anki.consts import *
|
||||
from anki.decks import DeckId
|
||||
from anki.errors import DBError
|
||||
from anki.scheduler.base import CongratsInfo
|
||||
from anki.scheduler.legacy import SchedulerBaseWithLegacy
|
||||
from anki.types import assert_exhaustive
|
||||
from anki.utils import intTime
|
||||
@ -44,19 +44,11 @@ class Scheduler(SchedulerBaseWithLegacy):
|
||||
*,
|
||||
fetch_limit: int = 1,
|
||||
intraday_learning_only: bool = False,
|
||||
) -> Union[QueuedCards, CongratsInfo]:
|
||||
"Returns one or more card ids, or the congratulations screen info."
|
||||
info = self.col._backend.get_queued_cards(
|
||||
) -> QueuedCards:
|
||||
"Returns zero or more pending cards, and the remaining counts. Idempotent."
|
||||
return self.col._backend.get_queued_cards(
|
||||
fetch_limit=fetch_limit, intraday_learning_only=intraday_learning_only
|
||||
)
|
||||
kind = info.WhichOneof("value")
|
||||
if kind == "queued_cards":
|
||||
return info.queued_cards
|
||||
elif kind == "congrats_info":
|
||||
return info.congrats_info
|
||||
else:
|
||||
assert_exhaustive(kind)
|
||||
assert False
|
||||
|
||||
def next_states(self, card_id: CardId) -> NextStates:
|
||||
"New states corresponding to each answer button press."
|
||||
@ -111,29 +103,23 @@ class Scheduler(SchedulerBaseWithLegacy):
|
||||
|
||||
def getCard(self) -> Optional[Card]:
|
||||
"""Fetch the next card from the queue. None if finished."""
|
||||
response = self.get_queued_cards()
|
||||
if isinstance(response, QueuedCards):
|
||||
backend_card = response.cards[0].card
|
||||
try:
|
||||
queued_card = self.get_queued_cards().cards[0]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
card = Card(self.col)
|
||||
card._load_from_backend_card(backend_card)
|
||||
card._load_from_backend_card(queued_card.card)
|
||||
card.startTimer()
|
||||
return card
|
||||
else:
|
||||
return None
|
||||
|
||||
def _is_finished(self) -> bool:
|
||||
"Don't use this, it is a stop-gap until this code is refactored."
|
||||
info = self.get_queued_cards()
|
||||
return isinstance(info, CongratsInfo)
|
||||
return not self.get_queued_cards().cards
|
||||
|
||||
def counts(self, card: Optional[Card] = None) -> Tuple[int, int, int]:
|
||||
info = self.get_queued_cards()
|
||||
if isinstance(info, CongratsInfo):
|
||||
counts = [0, 0, 0]
|
||||
else:
|
||||
counts = [info.new_count, info.learning_count, info.review_count]
|
||||
|
||||
return tuple(counts) # type: ignore
|
||||
return (info.new_count, info.learning_count, info.review_count)
|
||||
|
||||
@property
|
||||
def newCount(self) -> int:
|
||||
|
@ -11,25 +11,13 @@ import re
|
||||
import unicodedata as ucd
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum, auto
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
List,
|
||||
Literal,
|
||||
Match,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
from typing import Any, Callable, List, Literal, Match, Optional, Sequence, Tuple, cast
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
from anki import hooks
|
||||
from anki.cards import Card, CardId
|
||||
from anki.collection import Config, OpChanges, OpChangesWithCount
|
||||
from anki.scheduler import CongratsInfo
|
||||
from anki.scheduler.v3 import CardAnswer, NextStates, QueuedCards
|
||||
from anki.scheduler.v3 import Scheduler as V3Scheduler
|
||||
from anki.tags import MARKED_TAG
|
||||
@ -240,7 +228,7 @@ class Reviewer:
|
||||
def _get_next_v3_card(self) -> None:
|
||||
assert isinstance(self.mw.col.sched, V3Scheduler)
|
||||
output = self.mw.col.sched.get_queued_cards()
|
||||
if isinstance(output, CongratsInfo):
|
||||
if not output.cards:
|
||||
return
|
||||
self._v3 = V3CardInfo.from_queue(output)
|
||||
self.card = Card(self.mw.col, backend_card=self._v3.top_card().card)
|
||||
|
@ -132,7 +132,7 @@ service SchedulingService {
|
||||
rpc StateIsLeech(SchedulingState) returns (Bool);
|
||||
rpc AnswerCard(CardAnswer) returns (OpChanges);
|
||||
rpc UpgradeScheduler(Empty) returns (Empty);
|
||||
rpc GetQueuedCards(GetQueuedCardsIn) returns (GetQueuedCardsOut);
|
||||
rpc GetQueuedCards(GetQueuedCardsIn) returns (QueuedCards);
|
||||
}
|
||||
|
||||
service DecksService {
|
||||
@ -1563,13 +1563,6 @@ message QueuedCards {
|
||||
uint32 review_count = 4;
|
||||
}
|
||||
|
||||
message GetQueuedCardsOut {
|
||||
oneof value {
|
||||
QueuedCards queued_cards = 1;
|
||||
CongratsInfoOut congrats_info = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message OpChanges {
|
||||
bool card = 1;
|
||||
bool note = 2;
|
||||
|
@ -176,9 +176,10 @@ impl SchedulingService for Backend {
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
fn get_queued_cards(&self, input: pb::GetQueuedCardsIn) -> Result<pb::GetQueuedCardsOut> {
|
||||
fn get_queued_cards(&self, input: pb::GetQueuedCardsIn) -> Result<pb::QueuedCards> {
|
||||
self.with_col(|col| {
|
||||
col.get_queued_cards(input.fetch_limit as usize, input.intraday_learning_only)
|
||||
.map(Into::into)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -693,7 +693,7 @@ mod test {
|
||||
col.storage.db_scalar::<u32>("select count() from graves")?,
|
||||
0
|
||||
);
|
||||
assert_eq!(col.next_card()?.is_some(), false);
|
||||
assert_eq!(col.get_next_card()?.is_some(), false);
|
||||
Ok(())
|
||||
};
|
||||
|
||||
@ -704,7 +704,7 @@ mod test {
|
||||
col.storage.db_scalar::<u32>("select count() from graves")?,
|
||||
0
|
||||
);
|
||||
assert_eq!(col.next_card()?.is_some(), true);
|
||||
assert_eq!(col.get_next_card()?.is_some(), true);
|
||||
Ok(())
|
||||
};
|
||||
|
||||
@ -732,7 +732,7 @@ mod test {
|
||||
col.storage.db_scalar::<u32>("select count() from graves")?,
|
||||
3
|
||||
);
|
||||
assert_eq!(col.next_card()?.is_some(), false);
|
||||
assert_eq!(col.get_next_card()?.is_some(), false);
|
||||
Ok(())
|
||||
};
|
||||
|
||||
|
@ -401,7 +401,7 @@ pub mod test_helpers {
|
||||
where
|
||||
F: FnOnce(&NextCardStates) -> CardState,
|
||||
{
|
||||
let queued = self.next_card()?.unwrap();
|
||||
let queued = self.get_next_card()?.unwrap();
|
||||
let new_state = get_state(&queued.next_states);
|
||||
self.answer_card(&CardAnswer {
|
||||
card_id: queued.card.id,
|
||||
|
@ -37,7 +37,7 @@ impl QueueEntry {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub(crate) enum QueueEntryKind {
|
||||
pub enum QueueEntryKind {
|
||||
New,
|
||||
Learning,
|
||||
Review,
|
||||
|
@ -16,7 +16,7 @@ pub(crate) use main::{MainQueueEntry, MainQueueEntryKind};
|
||||
|
||||
use self::undo::QueueUpdate;
|
||||
use super::{states::NextCardStates, timing::SchedTimingToday};
|
||||
use crate::{backend_proto as pb, prelude::*, timestamp::TimestampSecs};
|
||||
use crate::{prelude::*, timestamp::TimestampSecs};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CardQueues {
|
||||
@ -32,26 +32,81 @@ pub(crate) struct CardQueues {
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) struct Counts {
|
||||
pub struct Counts {
|
||||
pub new: usize,
|
||||
pub learning: usize,
|
||||
pub review: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct QueuedCard {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct QueuedCard {
|
||||
pub card: Card,
|
||||
pub kind: QueueEntryKind,
|
||||
pub next_states: NextCardStates,
|
||||
}
|
||||
|
||||
pub(crate) struct QueuedCards {
|
||||
#[derive(Debug)]
|
||||
pub struct QueuedCards {
|
||||
pub cards: Vec<QueuedCard>,
|
||||
pub new_count: usize,
|
||||
pub learning_count: usize,
|
||||
pub review_count: usize,
|
||||
}
|
||||
|
||||
impl Collection {
|
||||
pub fn get_next_card(&mut self) -> Result<Option<QueuedCard>> {
|
||||
self.get_queued_cards(1, false)
|
||||
.map(|queued| queued.cards.get(0).cloned())
|
||||
}
|
||||
|
||||
pub fn get_queued_cards(
|
||||
&mut self,
|
||||
fetch_limit: usize,
|
||||
intraday_learning_only: bool,
|
||||
) -> Result<QueuedCards> {
|
||||
let queues = self.get_queues()?;
|
||||
let counts = queues.counts();
|
||||
let entries: Vec<_> = if intraday_learning_only {
|
||||
queues
|
||||
.intraday_now_iter()
|
||||
.chain(queues.intraday_ahead_iter())
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
} else {
|
||||
queues.iter().take(fetch_limit).collect()
|
||||
};
|
||||
let cards: Vec<_> = entries
|
||||
.into_iter()
|
||||
.map(|entry| {
|
||||
let card = self
|
||||
.storage
|
||||
.get_card(entry.card_id())?
|
||||
.ok_or(AnkiError::NotFound)?;
|
||||
if card.mtime != entry.mtime() {
|
||||
return Err(AnkiError::invalid_input(
|
||||
"bug: card modified without updating queue",
|
||||
));
|
||||
}
|
||||
|
||||
// fixme: pass in card instead of id
|
||||
let next_states = self.get_next_card_states(card.id)?;
|
||||
|
||||
Ok(QueuedCard {
|
||||
card,
|
||||
next_states,
|
||||
kind: entry.kind(),
|
||||
})
|
||||
})
|
||||
.collect::<Result<_>>()?;
|
||||
Ok(QueuedCards {
|
||||
cards,
|
||||
new_count: counts.new,
|
||||
learning_count: counts.learning,
|
||||
review_count: counts.review,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl CardQueues {
|
||||
/// An iterator over the card queues, in the order the cards will
|
||||
/// be presented.
|
||||
@ -103,26 +158,6 @@ impl CardQueues {
|
||||
}
|
||||
|
||||
impl Collection {
|
||||
pub(crate) fn get_queued_cards(
|
||||
&mut self,
|
||||
fetch_limit: usize,
|
||||
intraday_learning_only: bool,
|
||||
) -> Result<pb::GetQueuedCardsOut> {
|
||||
if let Some(next_cards) = self.next_cards(fetch_limit, intraday_learning_only)? {
|
||||
Ok(pb::GetQueuedCardsOut {
|
||||
value: Some(pb::get_queued_cards_out::Value::QueuedCards(
|
||||
next_cards.into(),
|
||||
)),
|
||||
})
|
||||
} else {
|
||||
Ok(pb::GetQueuedCardsOut {
|
||||
value: Some(pb::get_queued_cards_out::Value::CongratsInfo(
|
||||
self.congrats_info()?,
|
||||
)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// This is automatically done when transact() is called for everything
|
||||
/// except card answers, so unless you are modifying state outside of a
|
||||
/// transaction, you probably don't need this.
|
||||
@ -177,74 +212,13 @@ impl Collection {
|
||||
|
||||
Ok(self.state.card_queues.as_mut().unwrap())
|
||||
}
|
||||
|
||||
fn next_cards(
|
||||
&mut self,
|
||||
fetch_limit: usize,
|
||||
intraday_learning_only: bool,
|
||||
) -> Result<Option<QueuedCards>> {
|
||||
let queues = self.get_queues()?;
|
||||
let counts = queues.counts();
|
||||
let entries: Vec<_> = if intraday_learning_only {
|
||||
queues
|
||||
.intraday_now_iter()
|
||||
.chain(queues.intraday_ahead_iter())
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
} else {
|
||||
queues.iter().take(fetch_limit).collect()
|
||||
};
|
||||
if entries.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
let cards: Vec<_> = entries
|
||||
.into_iter()
|
||||
.map(|entry| {
|
||||
let card = self
|
||||
.storage
|
||||
.get_card(entry.card_id())?
|
||||
.ok_or(AnkiError::NotFound)?;
|
||||
if card.mtime != entry.mtime() {
|
||||
return Err(AnkiError::invalid_input(
|
||||
"bug: card modified without updating queue",
|
||||
));
|
||||
}
|
||||
|
||||
// fixme: pass in card instead of id
|
||||
let next_states = self.get_next_card_states(card.id)?;
|
||||
|
||||
Ok(QueuedCard {
|
||||
card,
|
||||
next_states,
|
||||
kind: entry.kind(),
|
||||
})
|
||||
})
|
||||
.collect::<Result<_>>()?;
|
||||
Ok(Some(QueuedCards {
|
||||
cards,
|
||||
new_count: counts.new,
|
||||
learning_count: counts.learning,
|
||||
review_count: counts.review,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test helpers
|
||||
#[cfg(test)]
|
||||
impl Collection {
|
||||
pub(crate) fn next_card(&mut self) -> Result<Option<QueuedCard>> {
|
||||
Ok(self
|
||||
.next_cards(1, false)?
|
||||
.map(|mut resp| resp.cards.pop().unwrap()))
|
||||
}
|
||||
|
||||
fn get_queue_single(&mut self) -> Result<QueuedCards> {
|
||||
self.next_cards(1, false)?.ok_or(AnkiError::NotFound)
|
||||
}
|
||||
|
||||
pub(crate) fn counts(&mut self) -> [usize; 3] {
|
||||
self.get_queue_single()
|
||||
self.get_queued_cards(1, false)
|
||||
.map(|q| [q.new_count, q.learning_count, q.review_count])
|
||||
.unwrap_or([0; 3])
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ mod test {
|
||||
col.storage.update_deck_conf(&conf)?;
|
||||
|
||||
// get the first card
|
||||
let queued = col.next_card()?.unwrap();
|
||||
let queued = col.get_next_card()?.unwrap();
|
||||
let cid = queued.card.id;
|
||||
let sibling_cid = col.storage.all_card_ids_of_note_in_order(nid)?[1];
|
||||
|
||||
@ -152,7 +152,7 @@ mod test {
|
||||
let deck = col.get_deck(DeckId(1))?.unwrap();
|
||||
assert_eq!(deck.common.review_studied, 1);
|
||||
|
||||
assert_eq!(col.next_card()?.is_some(), false);
|
||||
assert_eq!(col.get_next_card()?.is_some(), false);
|
||||
|
||||
Ok(())
|
||||
};
|
||||
@ -177,7 +177,7 @@ mod test {
|
||||
|
||||
let deck = col.get_deck(DeckId(1))?.unwrap();
|
||||
assert_eq!(deck.common.review_studied, 0);
|
||||
assert_eq!(col.next_card()?.is_some(), true);
|
||||
assert_eq!(col.get_next_card()?.is_some(), true);
|
||||
assert_eq!(col.counts(), [0, 0, 1]);
|
||||
|
||||
Ok(())
|
||||
|
@ -135,7 +135,7 @@ fn fuzz_range(interval: f32, factor: f32, minimum: f32) -> (f32, f32) {
|
||||
(interval - delta, interval + delta + 1.0)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NextCardStates {
|
||||
pub current: CardState,
|
||||
pub again: CardState,
|
||||
|
Loading…
Reference in New Issue
Block a user