mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
use deck tree for new/review count calculation
- wins back the performance lost by the decks and dconf not being in memory, and the overhead of serializing data for DB calls - card counts are no longer capped to 1000 - learn counts are currently still calculated separately - can't merge v1 counts without changing the existing behaviour - partially rendering the tree may yield more savings
This commit is contained in:
parent
da43d22aa5
commit
8b57a61746
3 changed files with 35 additions and 99 deletions
|
@ -462,25 +462,12 @@ and due <= ? limit ?)""",
|
||||||
lim,
|
lim,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _resetRevCount(self) -> None:
|
|
||||||
def cntFn(did, lim):
|
|
||||||
return self.col.db.scalar(
|
|
||||||
f"""
|
|
||||||
select count() from (select id from cards where
|
|
||||||
did = ? and queue = {QUEUE_TYPE_REV} and due <= ? limit %d)"""
|
|
||||||
% lim,
|
|
||||||
did,
|
|
||||||
self.today,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.revCount = self._walkingCount(self._deckRevLimitSingle, cntFn)
|
|
||||||
|
|
||||||
def _resetRev(self) -> None:
|
def _resetRev(self) -> None:
|
||||||
self._resetRevCount()
|
|
||||||
self._revQueue: List[Any] = []
|
self._revQueue: List[Any] = []
|
||||||
self._revDids = self.col.decks.active()[:]
|
self._revDids = self.col.decks.active()[:]
|
||||||
|
|
||||||
def _fillRev(self) -> Optional[bool]:
|
def _fillRev(self, recursing=False) -> bool:
|
||||||
|
"True if a review card can be fetched."
|
||||||
if self._revQueue:
|
if self._revQueue:
|
||||||
return True
|
return True
|
||||||
if not self.revCount:
|
if not self.revCount:
|
||||||
|
@ -514,14 +501,15 @@ did = ? and queue = {QUEUE_TYPE_REV} and due <= ? limit ?""",
|
||||||
return True
|
return True
|
||||||
# nothing left in the deck; move to next
|
# nothing left in the deck; move to next
|
||||||
self._revDids.pop(0)
|
self._revDids.pop(0)
|
||||||
if self.revCount:
|
|
||||||
# if we didn't get a card but the count is non-zero,
|
|
||||||
# we need to check again for any cards that were
|
|
||||||
# removed from the queue but not buried
|
|
||||||
self._resetRev()
|
|
||||||
return self._fillRev()
|
|
||||||
|
|
||||||
return None
|
# if we didn't get a card but the count is non-zero,
|
||||||
|
# we need to check again for any cards that were
|
||||||
|
# removed from the queue but not buried
|
||||||
|
if recursing:
|
||||||
|
print("bug: fillRev()")
|
||||||
|
return False
|
||||||
|
self._resetRev()
|
||||||
|
return self._fillRev(recursing=True)
|
||||||
|
|
||||||
# Answering a review card
|
# Answering a review card
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
|
@ -62,6 +62,7 @@ class Scheduler:
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
self.col.decks.update_active()
|
self.col.decks.update_active()
|
||||||
self._updateCutoff()
|
self._updateCutoff()
|
||||||
|
self._reset_counts()
|
||||||
self._resetLrn()
|
self._resetLrn()
|
||||||
self._resetRev()
|
self._resetRev()
|
||||||
self._resetNew()
|
self._resetNew()
|
||||||
|
@ -126,6 +127,17 @@ class Scheduler:
|
||||||
self._restorePreviewCard(card)
|
self._restorePreviewCard(card)
|
||||||
self._removeFromFiltered(card)
|
self._removeFromFiltered(card)
|
||||||
|
|
||||||
|
def _reset_counts(self):
|
||||||
|
tree = self.deck_due_tree()
|
||||||
|
node = self.col.decks.find_deck_in_tree(tree, int(self.col.conf["curDeck"]))
|
||||||
|
if not node:
|
||||||
|
print("invalid current deck")
|
||||||
|
self.newCount = 0
|
||||||
|
self.revCount = 0
|
||||||
|
else:
|
||||||
|
self.newCount = node.new_count
|
||||||
|
self.revCount = node.review_count
|
||||||
|
|
||||||
def counts(self, card: Optional[Card] = None) -> Tuple[int, int, int]:
|
def counts(self, card: Optional[Card] = None) -> Tuple[int, int, int]:
|
||||||
counts = [self.newCount, self.lrnCount, self.revCount]
|
counts = [self.newCount, self.lrnCount, self.revCount]
|
||||||
if card:
|
if card:
|
||||||
|
@ -195,41 +207,6 @@ order by due"""
|
||||||
self._update_stats(g, "rev", -rev)
|
self._update_stats(g, "rev", -rev)
|
||||||
self.col.decks.save(g)
|
self.col.decks.save(g)
|
||||||
|
|
||||||
def _walkingCount(
|
|
||||||
self,
|
|
||||||
limFn: Optional[Callable[[Any], Optional[int]]] = None,
|
|
||||||
cntFn: Optional[Callable[[int, int], int]] = None,
|
|
||||||
) -> int:
|
|
||||||
tot = 0
|
|
||||||
pcounts: Dict[int, int] = {}
|
|
||||||
# for each of the active decks
|
|
||||||
nameMap = self.col.decks.nameMap()
|
|
||||||
for did in self.col.decks.active():
|
|
||||||
# early alphas were setting the active ids as a str
|
|
||||||
did = int(did)
|
|
||||||
# get the individual deck's limit
|
|
||||||
lim = limFn(self.col.decks.get(did))
|
|
||||||
if not lim:
|
|
||||||
continue
|
|
||||||
# check the parents
|
|
||||||
parents = self.col.decks.parents(did, nameMap)
|
|
||||||
for p in parents:
|
|
||||||
# add if missing
|
|
||||||
if p["id"] not in pcounts:
|
|
||||||
pcounts[p["id"]] = limFn(p)
|
|
||||||
# take minimum of child and parent
|
|
||||||
lim = min(pcounts[p["id"]], lim)
|
|
||||||
# see how many cards we actually have
|
|
||||||
cnt = cntFn(did, lim)
|
|
||||||
# if non-zero, decrement from parent counts
|
|
||||||
for p in parents:
|
|
||||||
pcounts[p["id"]] -= cnt
|
|
||||||
# we may also be a parent
|
|
||||||
pcounts[did] = lim - cnt
|
|
||||||
# and add to running total
|
|
||||||
tot += cnt
|
|
||||||
return tot
|
|
||||||
|
|
||||||
# Deck list
|
# Deck list
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
@ -289,23 +266,12 @@ order by due"""
|
||||||
# New cards
|
# New cards
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _resetNewCount(self) -> None:
|
|
||||||
cntFn = lambda did, lim: self.col.db.scalar(
|
|
||||||
f"""
|
|
||||||
select count() from (select 1 from cards where
|
|
||||||
did = ? and queue = {QUEUE_TYPE_NEW} limit ?)""",
|
|
||||||
did,
|
|
||||||
lim,
|
|
||||||
)
|
|
||||||
self.newCount = self._walkingCount(self._deckNewLimitSingle, cntFn)
|
|
||||||
|
|
||||||
def _resetNew(self) -> None:
|
def _resetNew(self) -> None:
|
||||||
self._resetNewCount()
|
|
||||||
self._newDids = self.col.decks.active()[:]
|
self._newDids = self.col.decks.active()[:]
|
||||||
self._newQueue: List[int] = []
|
self._newQueue: List[int] = []
|
||||||
self._updateNewCardRatio()
|
self._updateNewCardRatio()
|
||||||
|
|
||||||
def _fillNew(self) -> Optional[bool]:
|
def _fillNew(self, recursing=False) -> bool:
|
||||||
if self._newQueue:
|
if self._newQueue:
|
||||||
return True
|
return True
|
||||||
if not self.newCount:
|
if not self.newCount:
|
||||||
|
@ -326,13 +292,15 @@ did = ? and queue = {QUEUE_TYPE_NEW} limit ?)""",
|
||||||
return True
|
return True
|
||||||
# nothing left in the deck; move to next
|
# nothing left in the deck; move to next
|
||||||
self._newDids.pop(0)
|
self._newDids.pop(0)
|
||||||
if self.newCount:
|
|
||||||
# if we didn't get a card but the count is non-zero,
|
# if we didn't get a card but the count is non-zero,
|
||||||
# we need to check again for any cards that were
|
# we need to check again for any cards that were
|
||||||
# removed from the queue but not buried
|
# removed from the queue but not buried
|
||||||
self._resetNew()
|
if recursing:
|
||||||
return self._fillNew()
|
print("bug: fillNew()")
|
||||||
return None
|
return False
|
||||||
|
self._resetNew()
|
||||||
|
return self._fillNew(recursing=True)
|
||||||
|
|
||||||
def _getNewCard(self) -> Optional[Card]:
|
def _getNewCard(self) -> Optional[Card]:
|
||||||
if self._fillNew():
|
if self._fillNew():
|
||||||
|
@ -814,22 +782,11 @@ and due <= ? limit ?)"""
|
||||||
lim,
|
lim,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _resetRevCount(self) -> None:
|
|
||||||
lim = self._currentRevLimit()
|
|
||||||
self.revCount = self.col.db.scalar(
|
|
||||||
f"""
|
|
||||||
select count() from (select id from cards where
|
|
||||||
did in %s and queue = {QUEUE_TYPE_REV} and due <= ? limit ?)"""
|
|
||||||
% self._deckLimit(),
|
|
||||||
self.today,
|
|
||||||
lim,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _resetRev(self) -> None:
|
def _resetRev(self) -> None:
|
||||||
self._resetRevCount()
|
|
||||||
self._revQueue: List[int] = []
|
self._revQueue: List[int] = []
|
||||||
|
|
||||||
def _fillRev(self) -> Any:
|
def _fillRev(self, recursing=False) -> bool:
|
||||||
|
"True if a review card can be fetched."
|
||||||
if self._revQueue:
|
if self._revQueue:
|
||||||
return True
|
return True
|
||||||
if not self.revCount:
|
if not self.revCount:
|
||||||
|
@ -853,12 +810,7 @@ limit ?"""
|
||||||
self._revQueue.reverse()
|
self._revQueue.reverse()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if self.revCount:
|
return False
|
||||||
# if we didn't get a card but the count is non-zero,
|
|
||||||
# we need to check again for any cards that were
|
|
||||||
# removed from the queue but not buried
|
|
||||||
self._resetRev()
|
|
||||||
return self._fillRev()
|
|
||||||
|
|
||||||
def _getRevCard(self) -> Optional[Card]:
|
def _getRevCard(self) -> Optional[Card]:
|
||||||
if self._fillRev():
|
if self._fillRev():
|
||||||
|
|
|
@ -204,10 +204,6 @@ to their original deck."""
|
||||||
def _table(self):
|
def _table(self):
|
||||||
counts = list(self.mw.col.sched.counts())
|
counts = list(self.mw.col.sched.counts())
|
||||||
finished = not sum(counts)
|
finished = not sum(counts)
|
||||||
if self.mw.col.schedVer() == 1:
|
|
||||||
for n in range(len(counts)):
|
|
||||||
if counts[n] >= 1000:
|
|
||||||
counts[n] = "1000+"
|
|
||||||
but = self.mw.button
|
but = self.mw.button
|
||||||
if finished:
|
if finished:
|
||||||
return '<div style="white-space: pre-wrap;">%s</div>' % (
|
return '<div style="white-space: pre-wrap;">%s</div>' % (
|
||||||
|
|
Loading…
Reference in a new issue