From da0fb0c5551c4f1949ee3b3b966c82059b3149b5 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Mon, 21 Mar 2011 08:52:37 +0900 Subject: [PATCH] space exiting new cards; fix 1 fact freeze --- anki/sched.py | 22 ++++++++---- tests/test_sched.py | 87 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 6 deletions(-) diff --git a/anki/sched.py b/anki/sched.py index 68cd13d33..cfcebe4a1 100644 --- a/anki/sched.py +++ b/anki/sched.py @@ -126,8 +126,13 @@ queue = 0 %s order by due limit %d""" % (self._groupLimit('new'), (id, due) = self.newQueue.pop() # move any siblings to the end? if self.deck.qconf['newTodayOrder'] == NEW_TODAY_ORD: + n = len(self.newQueue) while self.newQueue and self.newQueue[-1][1] == due: self.newQueue.insert(0, self.newQueue.pop()) + n -= 1 + if not n: + # we only have one fact in the queue; stop rotating + break self.newCount -= 1 return id @@ -219,13 +224,14 @@ limit %d""" % self.reportLimit, lim=self.dayCutoff) def _graduatingIvl(self, card, conf, early): if not early: # graduate - return conf['ints'][0] + ideal = conf['ints'][0] elif card.cycles: # remove - return conf['ints'][2] + ideal = conf['ints'][2] else: # first time bonus - return conf['ints'][1] + ideal = conf['ints'][1] + return self._adjRevIvl(card, ideal) def _rescheduleNew(self, card, conf, early): card.ivl = self._graduatingIvl(card, conf, early) @@ -374,6 +380,10 @@ queue = 2 %s and due <= :lim order by %s limit %d""" % ( def _updateRevIvl(self, card, ease): "Update CARD's interval, trying to avoid siblings." idealIvl = self._nextRevIvl(card, ease) + card.ivl = self._adjRevIvl(card, idealIvl) + + def _adjRevIvl(self, card, idealIvl): + "Given IDEALIVL, return an IVL away from siblings." idealDue = self.today + idealIvl conf = self._cardConf(card)['rev'] # find sibling positions @@ -381,12 +391,12 @@ queue = 2 %s and due <= :lim order by %s limit %d""" % ( "select due from cards where fid = ? and queue = 2" " and id != ?", card.fid, card.id) if not dues or idealDue not in dues: - card.ivl = idealIvl + return idealIvl else: leeway = max(conf['minSpace'], int(idealIvl * conf['fuzz'])) + fudge = 0 # do we have any room to adjust the interval? if leeway: - fudge = 0 # loop through possible due dates for an empty one for diff in range(1, leeway+1): # ensure we're due at least tomorrow @@ -396,7 +406,7 @@ queue = 2 %s and due <= :lim order by %s limit %d""" % ( elif (idealDue + diff) not in dues: fudge = diff break - card.ivl = idealIvl + fudge + return idealIvl + fudge # Leeches ########################################################################## diff --git a/tests/test_sched.py b/tests/test_sched.py index f0d745def..419f58e16 100644 --- a/tests/test_sched.py +++ b/tests/test_sched.py @@ -409,3 +409,90 @@ def test_cramLimits(): assert d.sched.counts()[0] == 1 d.cramGroups([1], min=0, max=1) assert d.sched.counts()[0] == 2 + +def test_adjIvl(): + d = getEmptyDeck() + # add two more templates and set second active + m = d.currentModel() + m.templates[1]['actv'] = True + t = m.newTemplate() + t['name'] = "f2" + t['qfmt'] = "{{Front}}" + t['afmt'] = "{{Back}}" + m.addTemplate(t) + t = m.newTemplate() + t['name'] = "f3" + t['qfmt'] = "{{Front}}" + t['afmt'] = "{{Back}}" + m.addTemplate(t) + m.flush() + # create a new fact; it should have 4 cards + f = d.newFact() + f['Front'] = "1"; f['Back'] = "1" + d.addFact(f) + assert d.cardCount() == 4 + d.reset() + # immediately remove first; it should get ideal ivl + c = d.sched.getCard() + d.sched.answerCard(c, 3) + assert c.ivl == 7 + # with the default settings, second card should be -1 + c = d.sched.getCard() + d.sched.answerCard(c, 3) + assert c.ivl == 6 + # and third +1 + c = d.sched.getCard() + d.sched.answerCard(c, 3) + assert c.ivl == 8 + # fourth exceeds default settings, so gets ideal again + c = d.sched.getCard() + d.sched.answerCard(c, 3) + assert c.ivl == 7 + # try again with another fact + f = d.newFact() + f['Front'] = "2"; f['Back'] = "2" + d.addFact(f) + d.reset() + # set a minSpacing of 0 + conf = d.sched._cardConf(c) + conf['rev']['minSpace'] = 0 + # first card gets ideal + c = d.sched.getCard() + d.sched.answerCard(c, 3) + assert c.ivl == 7 + # and second too, because it's below the threshold + c = d.sched.getCard() + d.sched.answerCard(c, 3) + assert c.ivl == 7 + # if we increase the ivl minSpace isn't needed + conf['new']['ints'][1] = 20 + # ideal.. + c = d.sched.getCard() + d.sched.answerCard(c, 3) + assert c.ivl == 20 + # adjusted + c = d.sched.getCard() + d.sched.answerCard(c, 3) + assert c.ivl == 19 + +def test_ordcycle(): + d = getEmptyDeck() + # add two more templates and set second active + m = d.currentModel() + m.templates[1]['actv'] = True + t = m.newTemplate() + t['name'] = "f2" + t['qfmt'] = "{{Front}}" + t['afmt'] = "{{Back}}" + m.addTemplate(t) + m.flush() + # create a new fact; it should have 4 cards + f = d.newFact() + f['Front'] = "1"; f['Back'] = "1" + d.addFact(f) + assert d.cardCount() == 3 + d.reset() + # ordinals should arrive in order + assert d.sched.getCard().ord == 0 + assert d.sched.getCard().ord == 1 + assert d.sched.getCard().ord == 2