diff --git a/anki/sched.py b/anki/sched.py index 21c2f3175..ed3892949 100644 --- a/anki/sched.py +++ b/anki/sched.py @@ -52,6 +52,7 @@ 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) @@ -66,12 +67,11 @@ class Scheduler(object): card.mod = intTime() card.usn = self.deck.usn() card.flushSched() - # if nothing more to study, rebuild queue - if self.counts() == (0,0,0): - self.reset() - def counts(self): - "Does not include fetched but unanswered." + def repCounts(self): + return (self.newCount, self.lrnRepCount, self.revCount) + + def cardCounts(self): return (self.newCount, self.lrnCount, self.revCount) def dueForecast(self, days=7): @@ -209,7 +209,6 @@ order by due""" % self._groupLimit(), def _resetNewCount(self): self.newCount = 0 - self.newRepCount = 0 pcounts = {} # for each of the active groups for gid in self.deck.groups.active(): @@ -236,8 +235,6 @@ gid = ? and queue = 0 limit ?)""", gid, lim) pcounts[gid] = lim - cnt # and add to running total self.newCount += cnt - conf = self.deck.groups.conf(gid) - self.newRepCount += cnt * len(conf['new']['delays']) def _resetNew(self): self._resetNewCount() @@ -335,7 +332,7 @@ select id, due from cards where gid = ? and queue = 0 limit ?""", gid, lim) select count(), sum(left) from (select left from cards where gid in %s and queue = 1 and due < ? limit %d)""" % ( self._groupLimit(), self.reportLimit), - intTime() + self.deck.groups.top()['collapseTime']) + self.dayCutoff) self.lrnRepCount = self.lrnRepCount or 0 def _resetLrn(self): @@ -376,24 +373,26 @@ limit %d""" % (self._groupLimit(), self.reportLimit), lim=self.dayCutoff) lastLeft = card.left if ease == 3: self._rescheduleAsRev(card, conf, True) + self.lrnRepCount -= lastLeft leaving = True elif ease == 2 and card.left-1 <= 0: self._rescheduleAsRev(card, conf, False) + self.lrnRepCount -= 1 leaving = True else: if ease == 2: card.left -= 1 + self.lrnRepCount -= 1 else: card.left = self._startingLeft(card) + self.lrnRepCount += card.left - lastLeft 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)) - # if it's due within the cutoff, increment count - if delay <= self.deck.groups.top()['collapseTime']: - self.lrnCount += 1 + self.lrnCount += 1 self._logLrn(card, ease, conf, leaving, type, lastLeft) def _delayForGrade(self, conf, left): @@ -542,6 +541,7 @@ gid in %s and queue = 2 and due <= :lim %s limit %d""" % ( card.left = len(conf['delays']) card.queue = 1 self.lrnCount += 1 + self.lrnRepCount += card.left # leech? if not self._checkLeech(card, conf) and conf['relearn']: heappush(self._lrnQueue, (card.due, card.id)) @@ -785,18 +785,6 @@ gid in %s and queue = 2 and due <= :lim %s limit %d""" % ( self.deck.db.list("select id from cards where fid = ?", fid)) self.deck.db.execute("update cards set queue = -2 where fid = ?", fid) - # Counts - ########################################################################## - - def timeToday(self): - "Time spent learning today, in seconds." - t = self.deck.groups.top() - return t['timeToday'][1] / 1000 - - def repsToday(self): - "Number of cards answered today." - return sum(self.counts()) - # Resetting ########################################################################## diff --git a/tests/test_deck.py b/tests/test_deck.py index f488972c3..2325923a7 100644 --- a/tests/test_deck.py +++ b/tests/test_deck.py @@ -127,7 +127,7 @@ def test_upgrade(): assert d.hour == 4 and d.minute == 0 # 3 new, 2 failed, 1 due deck.conf['counts'] = COUNT_REMAINING - assert deck.sched.counts() == (3,2,1) + assert deck.sched.cardCounts() == (3,2,1) # now's a good time to test the integrity check too deck.fixIntegrity() diff --git a/tests/test_sched.py b/tests/test_sched.py index a4d3c87c9..ba357ca6c 100644 --- a/tests/test_sched.py +++ b/tests/test_sched.py @@ -192,7 +192,7 @@ def test_reviews(): f = d.newFact() f['Front'] = u"one"; f['Back'] = u"two" d.addFact(f) - # set the card up as a review card, due yesterday + # set the card up as a review card, due 8 days ago c = f.cards()[0] c.type = 2 c.queue = 2 @@ -358,13 +358,6 @@ def test_misc(): d.sched.onClose() d.reset() assert d.sched.getCard() - # counts - assert d.sched.timeToday() == 0 - assert d.sched.repsToday() == 0 - c.timerStarted = time.time() - 10 - d.sched.answerCard(c, 2) - assert d.sched.timeToday() > 0 - assert d.sched.repsToday() == 1 def test_suspend(): d = getEmptyDeck() @@ -419,10 +412,10 @@ def test_cram(): conf = d.sched._lrnConf(c) conf['reset'] = False conf['resched'] = False - assert d.sched.counts() == (1, 0, 0) + assert d.sched.cardCounts() == (1, 0, 0) c = d.sched.getCard() d.sched._cardConf(c)['cram']['delays'] = [0.5, 3, 10] - assert d.sched.counts() == (0, 0, 0) + assert d.sched.cardCounts() == (0, 0, 0) # check that estimates work assert d.sched.nextIvl(c, 1) == 30 assert d.sched.nextIvl(c, 2) == 180 @@ -432,7 +425,7 @@ def test_cram(): d.sched.answerCard(c, 1) assert c.ivl == 100 # and should have incremented lrn count - assert d.sched.counts()[1] == 1 + assert d.sched.cardCounts()[1] == 1 # reset ivl for exit test, and pass card d.sched.answerCard(c, 2) delta = c.due - time.time() @@ -446,7 +439,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.counts() == (0, 0, 0) + assert d.sched.cardCounts() == (0, 0, 0) # now try again with ivl rescheduling c = copy.copy(cardcopy) c.flush() @@ -477,7 +470,7 @@ def test_cram(): # users should be able to cram entire deck too d.conf['groups'] = [] d.cramGroups() - assert d.sched.counts()[0] > 0 + assert d.sched.cardCounts()[0] > 0 def test_cramLimits(): d = getEmptyDeck() @@ -493,29 +486,29 @@ def test_cramLimits(): # the default cram should return all three d.conf['groups'] = [1] d.cramGroups() - assert d.sched.counts()[0] == 3 + assert d.sched.cardCounts()[0] == 3 # if we start from the day after tomorrow, it should be 2 d.cramGroups(min=1) - assert d.sched.counts()[0] == 2 + assert d.sched.cardCounts()[0] == 2 # or after 2 days d.cramGroups(min=2) - assert d.sched.counts()[0] == 1 + assert d.sched.cardCounts()[0] == 1 # we may get nothing d.cramGroups(min=3) - assert d.sched.counts()[0] == 0 + assert d.sched.cardCounts()[0] == 0 # tomorrow(0) + dayAfter(1) = 2 d.cramGroups(max=1) - assert d.sched.counts()[0] == 2 + assert d.sched.cardCounts()[0] == 2 # if max is tomorrow, we get only one d.cramGroups(max=0) - assert d.sched.counts()[0] == 1 + assert d.sched.cardCounts()[0] == 1 # both should work d.cramGroups(min=0, max=0) - assert d.sched.counts()[0] == 1 + assert d.sched.cardCounts()[0] == 1 d.cramGroups(min=1, max=1) - assert d.sched.counts()[0] == 1 + assert d.sched.cardCounts()[0] == 1 d.cramGroups(min=0, max=1) - assert d.sched.counts()[0] == 2 + assert d.sched.cardCounts()[0] == 2 def test_adjIvl(): d = getEmptyDeck() @@ -604,7 +597,7 @@ def test_ordcycle(): assert d.sched.getCard().ord == 1 assert d.sched.getCard().ord == 2 -def test_counts_down(): +def test_cardcounts(): d = getEmptyDeck() # add a second group grp = d.groups.id("Default::new group") @@ -625,11 +618,11 @@ def test_counts_down(): c.flush() d.reset() # with the default settings, there's no count limit - assert d.sched.counts() == (2,2,2) + assert d.sched.cardCounts() == (2,2,2) # check limit to one group d.groups.select(grp) d.reset() - assert d.sched.counts() == (1,1,1) + assert d.sched.cardCounts() == (1,1,1) def test_counts_idx(): d = getEmptyDeck() @@ -637,21 +630,73 @@ def test_counts_idx(): f['Front'] = u"one"; f['Back'] = u"two" d.addFact(f) d.reset() - assert d.sched.counts() == (1, 0, 0) + assert d.sched.cardCounts() == (1, 0, 0) c = d.sched.getCard() # counter's been decremented but idx indicates 1 - assert d.sched.counts() == (0, 0, 0) + assert d.sched.cardCounts() == (0, 0, 0) assert d.sched.countIdx(c) == 0 # answer to move to learn queue d.sched.answerCard(c, 1) - assert d.sched.counts() == (0, 1, 0) + assert d.sched.cardCounts() == (0, 1, 0) # fetching again will decrement the count c = d.sched.getCard() - assert d.sched.counts() == (0, 0, 0) + assert d.sched.cardCounts() == (0, 0, 0) assert d.sched.countIdx(c) == 1 # answering should add it back again d.sched.answerCard(c, 1) - assert d.sched.counts() == (0, 1, 0) + assert d.sched.cardCounts() == (0, 1, 0) + +def test_repCounts(): + d = getEmptyDeck() + f = d.newFact() + f['Front'] = u"one" + d.addFact(f) + d.reset() + # lrnReps should be accurate on pass/fail + assert d.sched.repCounts() == (1, 0, 0) + d.sched.answerCard(d.sched.getCard(), 1) + assert d.sched.repCounts() == (0, 2, 0) + d.sched.answerCard(d.sched.getCard(), 1) + assert d.sched.repCounts() == (0, 2, 0) + d.sched.answerCard(d.sched.getCard(), 2) + assert d.sched.repCounts() == (0, 1, 0) + d.sched.answerCard(d.sched.getCard(), 1) + assert d.sched.repCounts() == (0, 2, 0) + d.sched.answerCard(d.sched.getCard(), 2) + assert d.sched.repCounts() == (0, 1, 0) + d.sched.answerCard(d.sched.getCard(), 2) + assert d.sched.repCounts() == (0, 0, 0) + f = d.newFact() + f['Front'] = u"two" + d.addFact(f) + d.reset() + # initial pass should be correct too + d.sched.answerCard(d.sched.getCard(), 2) + assert d.sched.repCounts() == (0, 1, 0) + d.sched.answerCard(d.sched.getCard(), 1) + assert d.sched.repCounts() == (0, 2, 0) + d.sched.answerCard(d.sched.getCard(), 3) + assert d.sched.repCounts() == (0, 0, 0) + # immediate graduate should work + f = d.newFact() + f['Front'] = u"three" + d.addFact(f) + d.reset() + d.sched.answerCard(d.sched.getCard(), 3) + assert d.sched.repCounts() == (0, 0, 0) + # and failing a review should too + f = d.newFact() + f['Front'] = u"three" + d.addFact(f) + c = f.cards()[0] + c.type = 2 + c.queue = 2 + c.due = d.sched.today + c.flush() + d.reset() + assert d.sched.repCounts() == (0, 0, 1) + d.sched.answerCard(d.sched.getCard(), 1) + assert d.sched.repCounts() == (0, 2, 0) def test_timing(): d = getEmptyDeck() @@ -761,7 +806,7 @@ def test_groupFlow(): d.addFact(f) # should get top level one first, then ::1, then ::2 d.reset() - assert d.sched.counts() == (3,0,0) + assert d.sched.cardCounts() == (3,0,0) for i in "one", "three", "two": c = d.sched.getCard() assert c.fact()['Front'] == i @@ -814,10 +859,10 @@ def test_forget(): c.queue = 2; c.type = 2; c.ivl = 100; c.due = 0 c.flush() d.reset() - assert d.sched.counts() == (0, 0, 1) + assert d.sched.cardCounts() == (0, 0, 1) d.sched.forgetCards([c.id]) d.reset() - assert d.sched.counts() == (1, 0, 0) + assert d.sched.cardCounts() == (1, 0, 0) def test_resched(): d = getEmptyDeck() diff --git a/tests/test_undo.py b/tests/test_undo.py index 370f05bb6..870c92fa6 100644 --- a/tests/test_undo.py +++ b/tests/test_undo.py @@ -44,18 +44,18 @@ def test_review(): d.reset() assert not d.undoName() # answer - assert d.sched.counts() == (1, 0, 0) + assert d.sched.cardCounts() == (1, 0, 0) c = d.sched.getCard() assert c.queue == 0 d.sched.answerCard(c, 2) assert c.left == 1 - assert d.sched.counts() == (0, 1, 0) + assert d.sched.cardCounts() == (0, 1, 0) assert c.queue == 1 # undo assert d.undoName() d.undo() d.reset() - assert d.sched.counts() == (1, 0, 0) + assert d.sched.cardCounts() == (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.addFact(f) d.reset() - assert d.sched.counts() == (2, 0, 0) + assert d.sched.cardCounts() == (2, 0, 0) c = d.sched.getCard() d.sched.answerCard(c, 2) c = d.sched.getCard() d.sched.answerCard(c, 2) - assert d.sched.counts() == (0, 2, 0) + assert d.sched.cardCounts() == (0, 2, 0) d.undo() d.reset() - assert d.sched.counts() == (1, 1, 0) + assert d.sched.cardCounts() == (1, 1, 0) d.undo() d.reset() - assert d.sched.counts() == (2, 0, 0) + assert d.sched.cardCounts() == (2, 0, 0) # performing a normal op will clear the review queue c = d.sched.getCard() d.sched.answerCard(c, 2)