mirror of
https://github.com/ankitects/anki.git
synced 2025-11-09 14:17:13 -05:00
next time reports and associated unit tests
This commit is contained in:
parent
9e46db5b3e
commit
5e0cc2ff5d
2 changed files with 94 additions and 17 deletions
|
|
@ -6,7 +6,7 @@ import time, datetime, simplejson, random
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from heapq import *
|
from heapq import *
|
||||||
#from anki.cards import Card
|
#from anki.cards import Card
|
||||||
from anki.utils import parseTags, ids2str, intTime
|
from anki.utils import parseTags, ids2str, intTime, fmtTimeSpan
|
||||||
from anki.lang import _, ngettext
|
from anki.lang import _, ngettext
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
from anki.hooks import runHook
|
from anki.hooks import runHook
|
||||||
|
|
@ -211,18 +211,20 @@ limit %d""" % self.reportLimit, lim=self.dayCutoff)
|
||||||
card.queue = 1
|
card.queue = 1
|
||||||
card.type = 1
|
card.type = 1
|
||||||
|
|
||||||
def _rescheduleNew(self, card, conf, early):
|
def _graduatingIvl(self, card, conf, early):
|
||||||
if not early:
|
if not early:
|
||||||
# graduate
|
# graduate
|
||||||
int_ = conf['ints'][0]
|
return conf['ints'][0]
|
||||||
elif card.cycles:
|
elif card.cycles:
|
||||||
# remove
|
# remove
|
||||||
int_ = conf['ints'][2]
|
return conf['ints'][2]
|
||||||
else:
|
else:
|
||||||
# first time bonus
|
# first time bonus
|
||||||
int_ = conf['ints'][1]
|
return conf['ints'][1]
|
||||||
card.ivl = int_
|
|
||||||
card.due = self.today+int_
|
def _rescheduleNew(self, card, conf, early):
|
||||||
|
card.ivl = self._graduatingIvl(card, conf, early)
|
||||||
|
card.due = self.today+card.ivl
|
||||||
card.factor = conf['initialFactor']
|
card.factor = conf['initialFactor']
|
||||||
|
|
||||||
def _logLearn(self, card, ease, conf, leaving):
|
def _logLearn(self, card, ease, conf, leaving):
|
||||||
|
|
@ -304,7 +306,7 @@ queue = 1 %s and due <= :lim order by %s limit %d""" % (
|
||||||
card.streak = 0
|
card.streak = 0
|
||||||
card.lapses += 1
|
card.lapses += 1
|
||||||
card.lastIvl = card.ivl
|
card.lastIvl = card.ivl
|
||||||
card.ivl = int(card.ivl*conf['mult']) + 1
|
card.ivl = self._nextLapseIvl(card, conf)
|
||||||
card.factor = max(1300, card.factor-200)
|
card.factor = max(1300, card.factor-200)
|
||||||
card.due = card.edue = self.today + card.ivl
|
card.due = card.edue = self.today + card.ivl
|
||||||
# put back in the learn queue?
|
# put back in the learn queue?
|
||||||
|
|
@ -314,11 +316,14 @@ queue = 1 %s and due <= :lim order by %s limit %d""" % (
|
||||||
# leech?
|
# leech?
|
||||||
self._checkLeech(card, conf)
|
self._checkLeech(card, conf)
|
||||||
|
|
||||||
|
def _nextLapseIvl(self, card, conf):
|
||||||
|
return int(card.ivl*conf['mult']) + 1
|
||||||
|
|
||||||
def _rescheduleReview(self, card, ease):
|
def _rescheduleReview(self, card, ease):
|
||||||
card.streak += 1
|
card.streak += 1
|
||||||
# update interval
|
# update interval
|
||||||
card.lastIvl = card.ivl
|
card.lastIvl = card.ivl
|
||||||
self._updateInterval(card, ease)
|
self._updateRevIvl(card, ease)
|
||||||
# then the rest
|
# then the rest
|
||||||
card.factor = max(1300, card.factor+[-150, 0, 150][ease-2])
|
card.factor = max(1300, card.factor+[-150, 0, 150][ease-2])
|
||||||
card.due = self.today + card.ivl
|
card.due = self.today + card.ivl
|
||||||
|
|
@ -342,7 +347,7 @@ queue = 1 %s and due <= :lim order by %s limit %d""" % (
|
||||||
# Interval management
|
# Interval management
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def nextInterval(self, card, ease):
|
def _nextRevIvl(self, card, ease):
|
||||||
"Ideal next interval for CARD, given EASE."
|
"Ideal next interval for CARD, given EASE."
|
||||||
delay = self._daysLate(card)
|
delay = self._daysLate(card)
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
|
|
@ -356,18 +361,13 @@ queue = 1 %s and due <= :lim order by %s limit %d""" % (
|
||||||
# must be at least one day greater than previous interval
|
# must be at least one day greater than previous interval
|
||||||
return max(card.ivl+1, int(interval))
|
return max(card.ivl+1, int(interval))
|
||||||
|
|
||||||
def nextIntervalStr(self, card, ease, short=False):
|
|
||||||
"Return the next interval for CARD given EASE as a string."
|
|
||||||
int = self.nextInterval(card, ease)
|
|
||||||
return anki.utils.fmtTimeSpan(int*86400, short=short)
|
|
||||||
|
|
||||||
def _daysLate(self, card):
|
def _daysLate(self, card):
|
||||||
"Number of days later than scheduled."
|
"Number of days later than scheduled."
|
||||||
return max(0, self.today - card.due)
|
return max(0, self.today - card.due)
|
||||||
|
|
||||||
def _updateInterval(self, card, ease):
|
def _updateRevIvl(self, card, ease):
|
||||||
"Update CARD's interval, trying to avoid siblings."
|
"Update CARD's interval, trying to avoid siblings."
|
||||||
idealIvl = self.nextInterval(card, ease)
|
idealIvl = self._nextRevIvl(card, ease)
|
||||||
idealDue = self.today + idealIvl
|
idealDue = self.today + idealIvl
|
||||||
conf = self._cardConf(card)['rev']
|
conf = self._cardConf(card)['rev']
|
||||||
# find sibling positions
|
# find sibling positions
|
||||||
|
|
@ -520,6 +520,45 @@ queue = 1 %s and due <= :lim order by %s limit %d""" % (
|
||||||
"select count() from (select id from cards where "
|
"select count() from (select id from cards where "
|
||||||
"queue = 2 limit %d)" % lim)
|
"queue = 2 limit %d)" % lim)
|
||||||
|
|
||||||
|
# Next time reports
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
def nextIvlStr(self, card, ease, short=False):
|
||||||
|
"Return the next interval for CARD as a string."
|
||||||
|
return fmtTimeSpan(
|
||||||
|
self.nextIvl(card, ease), short=short)
|
||||||
|
|
||||||
|
def nextIvl(self, card, ease):
|
||||||
|
"Return the next interval for CARD, in seconds."
|
||||||
|
if card.queue in (0,2):
|
||||||
|
# in learning
|
||||||
|
return self._nextLrnIvl(card, ease)
|
||||||
|
elif ease == 1:
|
||||||
|
# lapsed
|
||||||
|
conf = self._cardConf(card)['lapse']
|
||||||
|
return self._nextLapseIvl(card, conf)*86400
|
||||||
|
else:
|
||||||
|
# review
|
||||||
|
return self._nextRevIvl(card, ease)*86400
|
||||||
|
|
||||||
|
# this isn't easily extracted from the learn code
|
||||||
|
def _nextLrnIvl(self, card, ease):
|
||||||
|
conf = self._learnConf(card)
|
||||||
|
if ease == 1:
|
||||||
|
# grade 0
|
||||||
|
return self._delayForGrade(conf, 0)
|
||||||
|
elif ease == 3:
|
||||||
|
# early removal
|
||||||
|
return self._graduatingIvl(card, conf, True) * 86400
|
||||||
|
else:
|
||||||
|
grade = card.grade + 1
|
||||||
|
if grade >= len(conf['delays']):
|
||||||
|
# graduate
|
||||||
|
return self._graduatingIvl(card, conf, False) * 86400
|
||||||
|
else:
|
||||||
|
# next level
|
||||||
|
return self._delayForGrade(conf, grade)
|
||||||
|
|
||||||
# Suspending
|
# Suspending
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -231,3 +231,41 @@ def test_finished():
|
||||||
d.sched.answerCard(c, 3)
|
d.sched.answerCard(c, 3)
|
||||||
# nothing should be due tomorrow, as it's due in a week
|
# nothing should be due tomorrow, as it's due in a week
|
||||||
assert "No cards are due" in d.sched.finishedMsg()
|
assert "No cards are due" in d.sched.finishedMsg()
|
||||||
|
|
||||||
|
def test_nextIvl():
|
||||||
|
d = getEmptyDeck()
|
||||||
|
f = d.newFact()
|
||||||
|
f['Front'] = u"one"; f['Back'] = u"two"
|
||||||
|
d.addFact(f)
|
||||||
|
c = f.cards()[0]
|
||||||
|
# cards in learning
|
||||||
|
##################################################
|
||||||
|
ni = d.sched.nextIvl
|
||||||
|
assert ni(c, 1) == 30
|
||||||
|
assert ni(c, 2) == 180
|
||||||
|
# immediate removal is 7 days
|
||||||
|
assert ni(c, 3) == 7*86400
|
||||||
|
c.cycles = 1
|
||||||
|
c.grade = 1
|
||||||
|
assert ni(c, 1) == 30
|
||||||
|
assert ni(c, 2) == 600
|
||||||
|
# no first time bonus
|
||||||
|
assert ni(c, 3) == 4*86400
|
||||||
|
c.grade = 2
|
||||||
|
# normal graduation is tomorrow
|
||||||
|
assert ni(c, 2) == 1*86400
|
||||||
|
assert ni(c, 3) == 4*86400
|
||||||
|
# review cards
|
||||||
|
##################################################
|
||||||
|
c.queue = 1
|
||||||
|
c.ivl = 100
|
||||||
|
c.factor = 2500
|
||||||
|
# failing it puts it at tomorrow
|
||||||
|
assert ni(c, 1) == 1*86400
|
||||||
|
# (* 100 1.2 86400)10368000.0
|
||||||
|
assert ni(c, 2) == 10368000
|
||||||
|
# (* 100 2.5 86400)21600000.0
|
||||||
|
assert ni(c, 3) == 21600000
|
||||||
|
# (* 100 2.5 1.3 86400)28080000.0
|
||||||
|
assert ni(c, 4) == 28080000
|
||||||
|
print d.sched.nextIvlStr(c, 4) == "10.8 months"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue