mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 15:02:21 -04:00
minor refactor of count handling
- drop lrnCount; rename lrnRepCount to lrnCount - on card fetch, decr count by card.left - drop cardCounts(), rename repCounts() to just counts() - fix lrn count bugs
This commit is contained in:
parent
e7101faff7
commit
8308c79fa6
5 changed files with 77 additions and 110 deletions
|
@ -29,11 +29,10 @@ class Scheduler(object):
|
|||
def getCard(self):
|
||||
"Pop the next card from the queue. None if finished."
|
||||
self._checkDay()
|
||||
id = self._getCardId()
|
||||
if id:
|
||||
c = self.col.getCard(id)
|
||||
c.startTimer()
|
||||
return c
|
||||
card = self._getCard()
|
||||
if card:
|
||||
card.startTimer()
|
||||
return card
|
||||
|
||||
def reset(self):
|
||||
self._updateCutoff()
|
||||
|
@ -52,7 +51,6 @@ class Scheduler(object):
|
|||
card.queue = 1
|
||||
card.type = 1
|
||||
card.left = self._startingLeft(card)
|
||||
self.lrnRepCount += card.left
|
||||
self._updateStats(card, 'new')
|
||||
if card.queue == 1:
|
||||
self._answerLrnCard(card, ease)
|
||||
|
@ -68,10 +66,7 @@ class Scheduler(object):
|
|||
card.usn = self.col.usn()
|
||||
card.flushSched()
|
||||
|
||||
def repCounts(self):
|
||||
return (self.newCount, self.lrnRepCount, self.revCount)
|
||||
|
||||
def cardCounts(self):
|
||||
def counts(self):
|
||||
return (self.newCount, self.lrnCount, self.revCount)
|
||||
|
||||
def dueForecast(self, days=7):
|
||||
|
@ -175,23 +170,23 @@ order by due""" % self._deckLimit(),
|
|||
# Getting the next card
|
||||
##########################################################################
|
||||
|
||||
def _getCardId(self):
|
||||
def _getCard(self):
|
||||
"Return the next due card id, or None."
|
||||
# learning card due?
|
||||
id = self._getLrnCard()
|
||||
if id:
|
||||
return id
|
||||
c = self._getLrnCard()
|
||||
if c:
|
||||
return c
|
||||
# new first, or time for one?
|
||||
if self._timeForNewCard():
|
||||
return self._getNewCard()
|
||||
# card due for review?
|
||||
id = self._getRevCard()
|
||||
if id:
|
||||
return id
|
||||
c = self._getRevCard()
|
||||
if c:
|
||||
return c
|
||||
# new cards left?
|
||||
id = self._getNewCard()
|
||||
if id:
|
||||
return id
|
||||
c = self._getNewCard()
|
||||
if c:
|
||||
return c
|
||||
# collapse or finish
|
||||
return self._getLrnCard(collapse=True)
|
||||
|
||||
|
@ -266,7 +261,7 @@ select id, due from cards where did = ? and queue = 0 limit ?""", did, lim)
|
|||
# we only have one note in the queue; stop rotating
|
||||
break
|
||||
self.newCount -= 1
|
||||
return id
|
||||
return self.col.getCard(id)
|
||||
|
||||
def _updateNewCardRatio(self):
|
||||
if self.col.decks.top()['newSpread'] == NEW_CARDS_DISTRIBUTE:
|
||||
|
@ -316,12 +311,11 @@ select id, due from cards where did = ? and queue = 0 limit ?""", did, lim)
|
|||
##########################################################################
|
||||
|
||||
def _resetLrnCount(self):
|
||||
(self.lrnCount, self.lrnRepCount) = self.col.db.first("""
|
||||
select count(), sum(left) from (select left from cards where
|
||||
self.lrnCount = self.col.db.scalar("""
|
||||
select sum(left) from (select left from cards where
|
||||
did in %s and queue = 1 and due < ? limit %d)""" % (
|
||||
self._deckLimit(), self.reportLimit),
|
||||
self.dayCutoff)
|
||||
self.lrnRepCount = self.lrnRepCount or 0
|
||||
self.dayCutoff) or 0
|
||||
|
||||
def _resetLrn(self):
|
||||
self._resetLrnCount()
|
||||
|
@ -347,9 +341,9 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff)
|
|||
cutoff += self.col.decks.top()['collapseTime']
|
||||
if self._lrnQueue[0][0] < cutoff:
|
||||
id = heappop(self._lrnQueue)[1]
|
||||
self.lrnCount -= 1
|
||||
self.lrnRepCount -= 1
|
||||
return id
|
||||
card = self.col.getCard(id)
|
||||
self.lrnCount -= card.left
|
||||
return card
|
||||
|
||||
def _answerLrnCard(self, card, ease):
|
||||
# ease 1=no, 2=yes, 3=remove
|
||||
|
@ -359,29 +353,30 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff)
|
|||
else:
|
||||
type = 0
|
||||
leaving = False
|
||||
# lrnCount was decremented once when card was fetched
|
||||
lastLeft = card.left
|
||||
# immediate graduate?
|
||||
if ease == 3:
|
||||
self._rescheduleAsRev(card, conf, True)
|
||||
self.lrnRepCount -= lastLeft
|
||||
leaving = True
|
||||
# graduation time?
|
||||
elif ease == 2 and card.left-1 <= 0:
|
||||
self._rescheduleAsRev(card, conf, False)
|
||||
self.lrnRepCount -= 1
|
||||
leaving = True
|
||||
else:
|
||||
# one step towards graduation
|
||||
if ease == 2:
|
||||
card.left -= 1
|
||||
self.lrnRepCount -= 1
|
||||
# failed
|
||||
else:
|
||||
card.left = self._startingLeft(card)
|
||||
self.lrnRepCount += card.left - lastLeft
|
||||
self.lrnCount += card.left
|
||||
delay = self._delayForGrade(conf, card.left)
|
||||
if card.due < time.time():
|
||||
# not collapsed; add some randomness
|
||||
delay *= random.uniform(1, 1.25)
|
||||
card.due = int(time.time() + delay)
|
||||
heappush(self._lrnQueue, (card.due, card.id))
|
||||
self.lrnCount += 1
|
||||
self._logLrn(card, ease, conf, leaving, type, lastLeft)
|
||||
|
||||
def _delayForGrade(self, conf, left):
|
||||
|
@ -501,7 +496,7 @@ did in %s and queue = 2 and due <= :lim %s limit %d""" % (
|
|||
def _getRevCard(self):
|
||||
if self._fillRev():
|
||||
self.revCount -= 1
|
||||
return self._revQueue.pop()
|
||||
return self.col.getCard(self._revQueue.pop())
|
||||
|
||||
def _revOrder(self):
|
||||
if self.col.conf['revOrder']:
|
||||
|
@ -531,8 +526,7 @@ did in %s and queue = 2 and due <= :lim %s limit %d""" % (
|
|||
card.due = int(self._delayForGrade(conf, 0) + time.time())
|
||||
card.left = len(conf['delays'])
|
||||
card.queue = 1
|
||||
self.lrnCount += 1
|
||||
self.lrnRepCount += card.left
|
||||
self.lrnCount += card.left
|
||||
# leech?
|
||||
if not self._checkLeech(card, conf) and conf['relearn']:
|
||||
heappush(self._lrnQueue, (card.due, card.id))
|
||||
|
|
|
@ -140,7 +140,7 @@ select count() from notes where id not in (select distinct nid from cards)""")
|
|||
assert m['usn'] != -1
|
||||
self.col.sched.reset()
|
||||
return [
|
||||
list(self.col.sched.repCounts()),
|
||||
list(self.col.sched.counts()),
|
||||
self.col.db.scalar("select count() from cards"),
|
||||
self.col.db.scalar("select count() from notes"),
|
||||
self.col.db.scalar("select count() from revlog"),
|
||||
|
|
|
@ -426,10 +426,10 @@ def test_cram():
|
|||
conf = d.sched._lrnConf(c)
|
||||
conf['reset'] = False
|
||||
conf['resched'] = False
|
||||
assert d.sched.cardCounts() == (1, 0, 0)
|
||||
assert d.sched.counts() == (1, 0, 0)
|
||||
c = d.sched.getCard()
|
||||
d.sched._cardConf(c)['cram']['delays'] = [0.5, 3, 10]
|
||||
assert d.sched.cardCounts() == (0, 0, 0)
|
||||
assert d.sched.counts() == (0, 0, 0)
|
||||
# check that estimates work
|
||||
assert d.sched.nextIvl(c, 1) == 30
|
||||
assert d.sched.nextIvl(c, 2) == 180
|
||||
|
@ -439,7 +439,7 @@ def test_cram():
|
|||
d.sched.answerCard(c, 1)
|
||||
assert c.ivl == 100
|
||||
# and should have incremented lrn count
|
||||
assert d.sched.cardCounts()[1] == 1
|
||||
assert d.sched.counts()[1] == 1
|
||||
# reset ivl for exit test, and pass card
|
||||
d.sched.answerCard(c, 2)
|
||||
delta = c.due - time.time()
|
||||
|
@ -453,7 +453,7 @@ def test_cram():
|
|||
assert c.due == d.sched.today + c.ivl
|
||||
# and if the queue is reset, it shouldn't appear in the new queue again
|
||||
d.reset()
|
||||
assert d.sched.cardCounts() == (0, 0, 0)
|
||||
assert d.sched.counts() == (0, 0, 0)
|
||||
# now try again with ivl rescheduling
|
||||
c = copy.copy(cardcopy)
|
||||
c.flush()
|
||||
|
@ -484,7 +484,7 @@ def test_cram():
|
|||
# users should be able to cram entire deck too
|
||||
d.conf['decks'] = []
|
||||
d.cramDecks()
|
||||
assert d.sched.cardCounts()[0] > 0
|
||||
assert d.sched.counts()[0] > 0
|
||||
|
||||
def test_cramLimits():
|
||||
d = getEmptyDeck()
|
||||
|
@ -500,29 +500,29 @@ def test_cramLimits():
|
|||
# the default cram should return all three
|
||||
d.conf['decks'] = [1]
|
||||
d.cramDecks()
|
||||
assert d.sched.cardCounts()[0] == 3
|
||||
assert d.sched.counts()[0] == 3
|
||||
# if we start from the day after tomorrow, it should be 2
|
||||
d.cramDecks(min=1)
|
||||
assert d.sched.cardCounts()[0] == 2
|
||||
assert d.sched.counts()[0] == 2
|
||||
# or after 2 days
|
||||
d.cramDecks(min=2)
|
||||
assert d.sched.cardCounts()[0] == 1
|
||||
assert d.sched.counts()[0] == 1
|
||||
# we may get nothing
|
||||
d.cramDecks(min=3)
|
||||
assert d.sched.cardCounts()[0] == 0
|
||||
assert d.sched.counts()[0] == 0
|
||||
# tomorrow(0) + dayAfter(1) = 2
|
||||
d.cramDecks(max=1)
|
||||
assert d.sched.cardCounts()[0] == 2
|
||||
assert d.sched.counts()[0] == 2
|
||||
# if max is tomorrow, we get only one
|
||||
d.cramDecks(max=0)
|
||||
assert d.sched.cardCounts()[0] == 1
|
||||
assert d.sched.counts()[0] == 1
|
||||
# both should work
|
||||
d.cramDecks(min=0, max=0)
|
||||
assert d.sched.cardCounts()[0] == 1
|
||||
assert d.sched.counts()[0] == 1
|
||||
d.cramDecks(min=1, max=1)
|
||||
assert d.sched.cardCounts()[0] == 1
|
||||
assert d.sched.counts()[0] == 1
|
||||
d.cramDecks(min=0, max=1)
|
||||
assert d.sched.cardCounts()[0] == 2
|
||||
assert d.sched.counts()[0] == 2
|
||||
|
||||
def test_adjIvl():
|
||||
d = getEmptyDeck()
|
||||
|
@ -617,54 +617,27 @@ def test_ordcycle():
|
|||
assert d.sched.getCard().ord == 1
|
||||
assert d.sched.getCard().ord == 2
|
||||
|
||||
def test_cardcounts():
|
||||
d = getEmptyDeck()
|
||||
# add a second deck
|
||||
grp = d.decks.id("Default::new deck")
|
||||
# for each card type
|
||||
for type in range(3):
|
||||
# and each of the decks
|
||||
for did in (1,grp):
|
||||
# create a new note
|
||||
f = d.newNote()
|
||||
f['Front'] = u"one"
|
||||
d.addNote(f)
|
||||
c = f.cards()[0]
|
||||
# set type/did
|
||||
c.type = type
|
||||
c.queue = type
|
||||
c.did = did
|
||||
c.due = 0
|
||||
c.flush()
|
||||
d.reset()
|
||||
# with the default settings, there's no count limit
|
||||
assert d.sched.cardCounts() == (2,2,2)
|
||||
# check limit to one deck
|
||||
d.decks.select(grp)
|
||||
d.reset()
|
||||
assert d.sched.cardCounts() == (1,1,1)
|
||||
|
||||
def test_counts_idx():
|
||||
d = getEmptyDeck()
|
||||
f = d.newNote()
|
||||
f['Front'] = u"one"; f['Back'] = u"two"
|
||||
d.addNote(f)
|
||||
d.reset()
|
||||
assert d.sched.cardCounts() == (1, 0, 0)
|
||||
assert d.sched.counts() == (1, 0, 0)
|
||||
c = d.sched.getCard()
|
||||
# counter's been decremented but idx indicates 1
|
||||
assert d.sched.cardCounts() == (0, 0, 0)
|
||||
assert d.sched.counts() == (0, 0, 0)
|
||||
assert d.sched.countIdx(c) == 0
|
||||
# answer to move to learn queue
|
||||
d.sched.answerCard(c, 1)
|
||||
assert d.sched.cardCounts() == (0, 1, 0)
|
||||
assert d.sched.counts() == (0, 2, 0)
|
||||
# fetching again will decrement the count
|
||||
c = d.sched.getCard()
|
||||
assert d.sched.cardCounts() == (0, 0, 0)
|
||||
assert d.sched.counts() == (0, 0, 0)
|
||||
assert d.sched.countIdx(c) == 1
|
||||
# answering should add it back again
|
||||
d.sched.answerCard(c, 1)
|
||||
assert d.sched.cardCounts() == (0, 1, 0)
|
||||
assert d.sched.counts() == (0, 2, 0)
|
||||
|
||||
def test_repCounts():
|
||||
d = getEmptyDeck()
|
||||
|
@ -673,37 +646,37 @@ def test_repCounts():
|
|||
d.addNote(f)
|
||||
d.reset()
|
||||
# lrnReps should be accurate on pass/fail
|
||||
assert d.sched.repCounts() == (1, 0, 0)
|
||||
assert d.sched.counts() == (1, 0, 0)
|
||||
d.sched.answerCard(d.sched.getCard(), 1)
|
||||
assert d.sched.repCounts() == (0, 2, 0)
|
||||
assert d.sched.counts() == (0, 2, 0)
|
||||
d.sched.answerCard(d.sched.getCard(), 1)
|
||||
assert d.sched.repCounts() == (0, 2, 0)
|
||||
assert d.sched.counts() == (0, 2, 0)
|
||||
d.sched.answerCard(d.sched.getCard(), 2)
|
||||
assert d.sched.repCounts() == (0, 1, 0)
|
||||
assert d.sched.counts() == (0, 1, 0)
|
||||
d.sched.answerCard(d.sched.getCard(), 1)
|
||||
assert d.sched.repCounts() == (0, 2, 0)
|
||||
assert d.sched.counts() == (0, 2, 0)
|
||||
d.sched.answerCard(d.sched.getCard(), 2)
|
||||
assert d.sched.repCounts() == (0, 1, 0)
|
||||
assert d.sched.counts() == (0, 1, 0)
|
||||
d.sched.answerCard(d.sched.getCard(), 2)
|
||||
assert d.sched.repCounts() == (0, 0, 0)
|
||||
assert d.sched.counts() == (0, 0, 0)
|
||||
f = d.newNote()
|
||||
f['Front'] = u"two"
|
||||
d.addNote(f)
|
||||
d.reset()
|
||||
# initial pass should be correct too
|
||||
d.sched.answerCard(d.sched.getCard(), 2)
|
||||
assert d.sched.repCounts() == (0, 1, 0)
|
||||
assert d.sched.counts() == (0, 1, 0)
|
||||
d.sched.answerCard(d.sched.getCard(), 1)
|
||||
assert d.sched.repCounts() == (0, 2, 0)
|
||||
assert d.sched.counts() == (0, 2, 0)
|
||||
d.sched.answerCard(d.sched.getCard(), 3)
|
||||
assert d.sched.repCounts() == (0, 0, 0)
|
||||
assert d.sched.counts() == (0, 0, 0)
|
||||
# immediate graduate should work
|
||||
f = d.newNote()
|
||||
f['Front'] = u"three"
|
||||
d.addNote(f)
|
||||
d.reset()
|
||||
d.sched.answerCard(d.sched.getCard(), 3)
|
||||
assert d.sched.repCounts() == (0, 0, 0)
|
||||
assert d.sched.counts() == (0, 0, 0)
|
||||
# and failing a review should too
|
||||
f = d.newNote()
|
||||
f['Front'] = u"three"
|
||||
|
@ -714,9 +687,9 @@ def test_repCounts():
|
|||
c.due = d.sched.today
|
||||
c.flush()
|
||||
d.reset()
|
||||
assert d.sched.repCounts() == (0, 0, 1)
|
||||
assert d.sched.counts() == (0, 0, 1)
|
||||
d.sched.answerCard(d.sched.getCard(), 1)
|
||||
assert d.sched.repCounts() == (0, 2, 0)
|
||||
assert d.sched.counts() == (0, 2, 0)
|
||||
|
||||
def test_timing():
|
||||
d = getEmptyDeck()
|
||||
|
@ -836,7 +809,7 @@ def test_deckFlow():
|
|||
d.addNote(f)
|
||||
# should get top level one first, then ::1, then ::2
|
||||
d.reset()
|
||||
assert d.sched.cardCounts() == (3,0,0)
|
||||
assert d.sched.counts() == (3,0,0)
|
||||
for i in "one", "three", "two":
|
||||
c = d.sched.getCard()
|
||||
assert c.note()['Front'] == i
|
||||
|
@ -889,10 +862,10 @@ def test_forget():
|
|||
c.queue = 2; c.type = 2; c.ivl = 100; c.due = 0
|
||||
c.flush()
|
||||
d.reset()
|
||||
assert d.sched.cardCounts() == (0, 0, 1)
|
||||
assert d.sched.counts() == (0, 0, 1)
|
||||
d.sched.forgetCards([c.id])
|
||||
d.reset()
|
||||
assert d.sched.cardCounts() == (1, 0, 0)
|
||||
assert d.sched.counts() == (1, 0, 0)
|
||||
|
||||
def test_resched():
|
||||
d = getEmptyDeck()
|
||||
|
@ -918,12 +891,12 @@ def test_revlim():
|
|||
d.addNote(f)
|
||||
d.db.execute("update cards set due = 0, queue = 2, type = 2")
|
||||
d.reset()
|
||||
assert d.sched.repCounts()[2] == 20
|
||||
assert d.sched.counts()[2] == 20
|
||||
for i in range(5):
|
||||
d.sched.answerCard(d.sched.getCard(), 3)
|
||||
assert d.sched.repCounts()[2] == 15
|
||||
assert d.sched.counts()[2] == 15
|
||||
t = d.decks.top()
|
||||
t['revLim'] = 10
|
||||
d.reset()
|
||||
assert d.sched.repCounts()[2] == 5
|
||||
assert d.sched.counts()[2] == 5
|
||||
|
||||
|
|
|
@ -44,18 +44,18 @@ def test_review():
|
|||
d.reset()
|
||||
assert not d.undoName()
|
||||
# answer
|
||||
assert d.sched.cardCounts() == (1, 0, 0)
|
||||
assert d.sched.counts() == (1, 0, 0)
|
||||
c = d.sched.getCard()
|
||||
assert c.queue == 0
|
||||
d.sched.answerCard(c, 2)
|
||||
assert c.left == 1
|
||||
assert d.sched.cardCounts() == (0, 1, 0)
|
||||
assert d.sched.counts() == (0, 1, 0)
|
||||
assert c.queue == 1
|
||||
# undo
|
||||
assert d.undoName()
|
||||
d.undo()
|
||||
d.reset()
|
||||
assert d.sched.cardCounts() == (1, 0, 0)
|
||||
assert d.sched.counts() == (1, 0, 0)
|
||||
c.load()
|
||||
assert c.queue == 0
|
||||
assert c.left != 1
|
||||
|
@ -64,18 +64,18 @@ def test_review():
|
|||
f['Front'] = u"two"
|
||||
d.addNote(f)
|
||||
d.reset()
|
||||
assert d.sched.cardCounts() == (2, 0, 0)
|
||||
assert d.sched.counts() == (2, 0, 0)
|
||||
c = d.sched.getCard()
|
||||
d.sched.answerCard(c, 2)
|
||||
c = d.sched.getCard()
|
||||
d.sched.answerCard(c, 2)
|
||||
assert d.sched.cardCounts() == (0, 2, 0)
|
||||
assert d.sched.counts() == (0, 2, 0)
|
||||
d.undo()
|
||||
d.reset()
|
||||
assert d.sched.cardCounts() == (1, 1, 0)
|
||||
assert d.sched.counts() == (1, 1, 0)
|
||||
d.undo()
|
||||
d.reset()
|
||||
assert d.sched.cardCounts() == (2, 0, 0)
|
||||
assert d.sched.counts() == (2, 0, 0)
|
||||
# performing a normal op will clear the review queue
|
||||
c = d.sched.getCard()
|
||||
d.sched.answerCard(c, 2)
|
||||
|
|
|
@ -28,7 +28,7 @@ def test_upgrade():
|
|||
# 3 new, 2 failed, 1 due
|
||||
deck.reset()
|
||||
deck.conf['counts'] = COUNT_REMAINING
|
||||
assert deck.sched.cardCounts() == (3,2,1)
|
||||
assert deck.sched.counts() == (3,4,1)
|
||||
# now's a good time to test the integrity check too
|
||||
deck.fixIntegrity()
|
||||
# c = deck.sched.getCard()
|
||||
|
|
Loading…
Reference in a new issue