diff --git a/pylib/anki/sched.py b/pylib/anki/sched.py index 997227b3816..13c7cb45553 100644 --- a/pylib/anki/sched.py +++ b/pylib/anki/sched.py @@ -43,6 +43,8 @@ def __init__( # pylint: disable=super-init-not-called self.today: Optional[int] = None self._haveQueues = False self._updateCutoff() + self._next_card = None + self._current_card = None def answerCard(self, card: Card, ease: int) -> None: self.col.log() @@ -280,10 +282,11 @@ def _fillLrn(self) -> Union[bool, List[Any]]: self._lrnQueue = self.col.db.all( f""" select due, id from cards where -did in %s and queue = {QUEUE_TYPE_LRN} and due < ? +did in %s and queue = {QUEUE_TYPE_LRN} and due < ? and nid != ? limit %d""" % (self._deckLimit(), self.reportLimit), self.dayCutoff, + self._current_card_nid(), ) for i in range(len(self._lrnQueue)): self._lrnQueue[i] = (self._lrnQueue[i][0], self._lrnQueue[i][1]) @@ -566,9 +569,10 @@ def _fillRev(self) -> Optional[bool]: self._revQueue = self.col.db.list( f""" select id from cards where -did = ? and queue = {QUEUE_TYPE_REV} and due <= ? limit ?""", +did = ? and queue = {QUEUE_TYPE_REV} and due <= ? and nid != ? limit ?""", did, self.today, + self._current_card_nid(), lim, ) if self._revQueue: @@ -974,7 +978,7 @@ def buryCards(self, cids: List[int]) -> None: # type: ignore[override] ########################################################################## def _burySiblings(self, card: Card) -> None: - toBury = [] + toBury: List[int] = [] nconf = self._newConf(card) buryNew = nconf.get("bury", True) rconf = self._revConf(card) @@ -989,21 +993,9 @@ def _burySiblings(self, card: Card) -> None: self.today, ): if queue == QUEUE_TYPE_REV: - if buryRev: - toBury.append(cid) - # if bury disabled, we still discard to give same-day spacing - try: - self._revQueue.remove(cid) - except ValueError: - pass + self._actual_bury_siblings(buryRev, toBury, cid, self._revQueue) else: - # if bury disabled, we still discard to give same-day spacing - if buryNew: - toBury.append(cid) - try: - self._newQueue.remove(cid) - except ValueError: - pass + self._actual_bury_siblings(buryNew, toBury, cid, self._newQueue) # then bury if toBury: self.col.db.execute( diff --git a/pylib/anki/schedv2.py b/pylib/anki/schedv2.py index b4ba43c5616..68a748b4bfb 100644 --- a/pylib/anki/schedv2.py +++ b/pylib/anki/schedv2.py @@ -35,6 +35,8 @@ class Scheduler: haveCustomStudy = True _burySiblingsOnAnswer = True revCount: int + _next_card: Optional[Card] + _current_card: Optional[Card] def __init__(self, col: anki.storage._Collection) -> None: self.col = col.weakref() @@ -46,13 +48,13 @@ def __init__(self, col: anki.storage._Collection) -> None: self._haveQueues = False self._lrnCutoff = 0 self._updateCutoff() + self._next_card = None + self._current_card = None def getCard(self) -> Optional[Card]: """Pop the next card from the queue. None if finished.""" - self._checkDay() - if not self._haveQueues: - self.reset() - card = self._getCard() + card = self.load_next_card(False) + self._next_card = None if card: self.col.log(card) if not self._burySiblingsOnAnswer: @@ -62,13 +64,45 @@ def getCard(self) -> Optional[Card]: return card return None + def load_next_card(self, qa): + """ set _next_card to next card and returns it. + + qa -- whether to precompute question and answer. Useful to win time while the user review""" + self._checkDay() + if not self._haveQueues: + self.reset() + if self._next_card is None: + self._next_card = self._getCard() + while ( + self._next_card is not None + and self._next_card.nid == self._current_card_nid() + ): + self._next_card = self._getCard() + if self._next_card and qa: + self._next_card.answer() + return self._next_card + def reset(self) -> None: + self._next_card = None self._updateCutoff() self._resetLrn() self._resetRev() self._resetNew() self._haveQueues = True + def set_current_card(self, card: Card): + """card -- it and its sibling won't be put in queue nor scheduled as next card""" + self._current_card = card + if card is None: + return + self._burySiblings(card) + + def _current_card_nid(self): + if self._current_card is None: + return 0 + else: + return self._current_card.nid + def answerCard(self, card: Card, ease: int) -> None: self.col.log() assert 1 <= ease <= 4 @@ -389,8 +423,9 @@ def _fillNew(self) -> Optional[bool]: # fill the queue with the current did self._newQueue = self.col.db.list( f""" - select id from cards where did = ? and queue = {QUEUE_TYPE_NEW} order by due,ord limit ?""", + select id from cards where did = ? and queue = {QUEUE_TYPE_NEW} and nid != ? order by due,ord limit ?""", did, + self._current_card_nid(), lim, ) if self._newQueue: @@ -542,10 +577,11 @@ def _fillLrn(self) -> Union[bool, List[Any]]: self._lrnQueue = self.col.db.all( # type: ignore f""" select due, id from cards where -did in %s and queue in ({QUEUE_TYPE_LRN},{QUEUE_TYPE_PREVIEW}) and due < ? +did in %s and queue in ({QUEUE_TYPE_LRN},{QUEUE_TYPE_PREVIEW}) and due < ? and nid != ? limit %d""" % (self._deckLimit(), self.reportLimit), cutoff, + self._current_card_nid(), ) for i in range(len(self._lrnQueue)): self._lrnQueue[i] = (self._lrnQueue[i][0], self._lrnQueue[i][1]) @@ -578,9 +614,10 @@ def _fillLrnDay(self) -> Optional[bool]: self._lrnDayQueue = self.col.db.list( f""" select id from cards where -did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""", +did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? and nid != ? limit ?""", did, self.today, + self._current_card_nid(), self.queueLimit, ) if self._lrnDayQueue: @@ -912,11 +949,12 @@ def _fillRev(self) -> Any: self._revQueue = self.col.db.list( f""" select id from cards where -did in %s and queue = {QUEUE_TYPE_REV} and due <= ? +did in %s and queue = {QUEUE_TYPE_REV} and due <= ? and nid != ? order by due, random() limit ?""" % self._deckLimit(), self.today, + self._current_card_nid(), lim, ) @@ -1695,8 +1733,18 @@ def unburyCardsForDeck(self, type: str = "all") -> None: # Sibling spacing ########################################################################## + def _actual_bury_siblings(self, bury: bool, toBury: List[int], cid: int, queue: List[int]): + if bury: + toBury.append(cid) + try: + queue.remove(cid) + except ValueError: + pass + if self._next_card and self._next_card.id == cid: + self._next_card = None + def _burySiblings(self, card: Card) -> None: - toBury = [] + toBury : List[int] = [] nconf = self._newConf(card) buryNew = nconf.get("bury", True) rconf = self._revConf(card) @@ -1711,21 +1759,9 @@ def _burySiblings(self, card: Card) -> None: self.today, ): if queue == QUEUE_TYPE_REV: - if buryRev: - toBury.append(cid) - # if bury disabled, we still discard to give same-day spacing - try: - self._revQueue.remove(cid) - except ValueError: - pass + self._actual_bury_siblings(buryRev, toBury, cid, self._revQueue) else: - # if bury disabled, we still discard to give same-day spacing - if buryNew: - toBury.append(cid) - try: - self._newQueue.remove(cid) - except ValueError: - pass + self._actual_bury_siblings(buryNew, toBury, cid, self._newQueue) # then bury if toBury: self.buryCards(toBury, manual=False) diff --git a/qt/aqt/reviewer.py b/qt/aqt/reviewer.py index 40e4bdef5d9..16a1d44af9d 100644 --- a/qt/aqt/reviewer.py +++ b/qt/aqt/reviewer.py @@ -110,12 +110,15 @@ def nextCard(self) -> None: c = self.mw.col.sched.getCard() self.card = c if not c: + self.mw.col.sched.set_current_card(None) self.mw.moveToState("overview") return if self._reps is None or self._reps % 100 == 0: # we recycle the webview periodically so webkit can free memory self._initWeb() self._showQuestion() + self.mw.col.sched.set_current_card(self.card) + self.mw.col.sched.load_next_card(True) # Audio ##########################################################################