mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 22:42:25 -04:00
give card types a more logical order
they are now 0=new, 1=learning, 2=due, to reflect the natural progression
This commit is contained in:
parent
6f5918e8cd
commit
22a72d82c6
4 changed files with 58 additions and 57 deletions
|
@ -10,9 +10,9 @@ MAX_TIMER = 60
|
||||||
# Cards
|
# Cards
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
# Type: 0=learning, 1=due, 2=new
|
# Type: 0=new, 1=learning, 2=due
|
||||||
# Queue: 0=learning, 1=due, 2=new
|
# Queue: same as above, and:
|
||||||
# -1=suspended, -2=user buried, -3=sched buried
|
# -1=suspended, -2=user buried, -3=sched buried, -4=deleted
|
||||||
# Due is used differently for different queues.
|
# Due is used differently for different queues.
|
||||||
# - new queue: fact id or random int
|
# - new queue: fact id or random int
|
||||||
# - rev queue: integer day
|
# - rev queue: integer day
|
||||||
|
@ -32,8 +32,8 @@ class Card(object):
|
||||||
self.id = None
|
self.id = None
|
||||||
self.gid = 1
|
self.gid = 1
|
||||||
self.crt = intTime()
|
self.crt = intTime()
|
||||||
self.type = 2
|
self.type = 0
|
||||||
self.queue = 2
|
self.queue = 0
|
||||||
self.ivl = 0
|
self.ivl = 0
|
||||||
self.factor = 0
|
self.factor = 0
|
||||||
self.reps = 0
|
self.reps = 0
|
||||||
|
|
|
@ -256,7 +256,7 @@ select id from facts where id not in (select distinct fid from cards)""")
|
||||||
if self.qconf['newCardOrder'] == NEW_CARDS_RANDOM:
|
if self.qconf['newCardOrder'] == NEW_CARDS_RANDOM:
|
||||||
# if this fact has existing new cards, use their due time
|
# if this fact has existing new cards, use their due time
|
||||||
due = self.db.scalar(
|
due = self.db.scalar(
|
||||||
"select due from cards where fid = ? and queue = 2", fact.id)
|
"select due from cards where fid = ? and queue = 0", fact.id)
|
||||||
due = due or random.randrange(1, 1000000)
|
due = due or random.randrange(1, 1000000)
|
||||||
else:
|
else:
|
||||||
due = fact.id
|
due = fact.id
|
||||||
|
|
|
@ -42,13 +42,12 @@ class Scheduler(object):
|
||||||
|
|
||||||
def answerCard(self, card, ease):
|
def answerCard(self, card, ease):
|
||||||
if card.queue == 0:
|
if card.queue == 0:
|
||||||
self._answerLearnCard(card, ease)
|
|
||||||
elif card.queue == 1:
|
|
||||||
self._answerRevCard(card, ease)
|
|
||||||
elif card.queue == 2:
|
|
||||||
# put it in the learn queue
|
# put it in the learn queue
|
||||||
card.queue = 0
|
card.queue = 1
|
||||||
|
if card.queue == 1:
|
||||||
self._answerLearnCard(card, ease)
|
self._answerLearnCard(card, ease)
|
||||||
|
elif card.queue == 2:
|
||||||
|
self._answerRevCard(card, ease)
|
||||||
else:
|
else:
|
||||||
raise Exception("Invalid queue")
|
raise Exception("Invalid queue")
|
||||||
card.mod = intTime()
|
card.mod = intTime()
|
||||||
|
@ -56,7 +55,7 @@ class Scheduler(object):
|
||||||
|
|
||||||
def counts(self):
|
def counts(self):
|
||||||
"Does not include fetched but unanswered."
|
"Does not include fetched but unanswered."
|
||||||
return (self.learnCount, self.revCount, self.newCount)
|
return (self.newCount, self.learnCount, self.revCount)
|
||||||
|
|
||||||
def cardQueue(self, card):
|
def cardQueue(self, card):
|
||||||
return card.queue
|
return card.queue
|
||||||
|
@ -110,14 +109,14 @@ class Scheduler(object):
|
||||||
else:
|
else:
|
||||||
self.newCount = self.db.scalar("""
|
self.newCount = self.db.scalar("""
|
||||||
select count() from (select id from cards where
|
select count() from (select id from cards where
|
||||||
queue = 2 %s limit %d)""" % (self._groupLimit('new'), lim))
|
queue = 0 %s limit %d)""" % (self._groupLimit('new'), lim))
|
||||||
|
|
||||||
def _resetNew(self):
|
def _resetNew(self):
|
||||||
self._resetNewCount()
|
self._resetNewCount()
|
||||||
lim = min(self.queueLimit, self.newCount)
|
lim = min(self.queueLimit, self.newCount)
|
||||||
self.newQueue = self.db.all("""
|
self.newQueue = self.db.all("""
|
||||||
select id, due from cards where
|
select id, due from cards where
|
||||||
queue = 2 %s order by due limit %d""" % (self._groupLimit('new'),
|
queue = 0 %s order by due limit %d""" % (self._groupLimit('new'),
|
||||||
lim))
|
lim))
|
||||||
self.newQueue.reverse()
|
self.newQueue.reverse()
|
||||||
self._updateNewCardRatio()
|
self._updateNewCardRatio()
|
||||||
|
@ -159,14 +158,14 @@ queue = 2 %s order by due limit %d""" % (self._groupLimit('new'),
|
||||||
|
|
||||||
def _resetLearnCount(self):
|
def _resetLearnCount(self):
|
||||||
self.learnCount = self.db.scalar(
|
self.learnCount = self.db.scalar(
|
||||||
"select count() from cards where queue = 0 and due < ?",
|
"select count() from cards where queue = 1 and due < ?",
|
||||||
intTime() + self.deck.qconf['collapseTime'])
|
intTime() + self.deck.qconf['collapseTime'])
|
||||||
|
|
||||||
def _resetLearn(self):
|
def _resetLearn(self):
|
||||||
self._resetLearnCount()
|
self._resetLearnCount()
|
||||||
self.learnQueue = self.db.all("""
|
self.learnQueue = self.db.all("""
|
||||||
select due, id from cards where
|
select due, id from cards where
|
||||||
queue = 0 and due < :lim order by due
|
queue = 1 and due < :lim order by due
|
||||||
limit %d""" % self.reportLimit, lim=self.dayCutoff)
|
limit %d""" % self.reportLimit, lim=self.dayCutoff)
|
||||||
|
|
||||||
def _getLearnCard(self, collapse=False):
|
def _getLearnCard(self, collapse=False):
|
||||||
|
@ -208,18 +207,18 @@ limit %d""" % self.reportLimit, lim=self.dayCutoff)
|
||||||
def _learnConf(self, card):
|
def _learnConf(self, card):
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
if card.type == 2:
|
if card.type == 2:
|
||||||
return conf['new']
|
|
||||||
else:
|
|
||||||
return conf['lapse']
|
return conf['lapse']
|
||||||
|
else:
|
||||||
|
return conf['new']
|
||||||
|
|
||||||
def _rescheduleAsReview(self, card, conf, early):
|
def _rescheduleAsReview(self, card, conf, early):
|
||||||
if card.type == 1:
|
if card.type == 2:
|
||||||
# failed; put back entry due
|
# failed; put back entry due
|
||||||
card.due = card.edue
|
card.due = card.edue
|
||||||
else:
|
else:
|
||||||
self._rescheduleNew(card, conf, early)
|
self._rescheduleNew(card, conf, early)
|
||||||
card.queue = 1
|
card.queue = 2
|
||||||
card.type = 1
|
card.type = 2
|
||||||
|
|
||||||
def _graduatingIvl(self, card, conf, early):
|
def _graduatingIvl(self, card, conf, early):
|
||||||
if not early:
|
if not early:
|
||||||
|
@ -258,8 +257,8 @@ limit %d""" % self.reportLimit, lim=self.dayCutoff)
|
||||||
"Remove failed cards from the learning queue."
|
"Remove failed cards from the learning queue."
|
||||||
self.deck.db.execute("""
|
self.deck.db.execute("""
|
||||||
update cards set
|
update cards set
|
||||||
due = edue, queue = 1
|
due = edue, queue = 2
|
||||||
where queue = 0 and type = 1
|
where queue = 1 and type = 2
|
||||||
""")
|
""")
|
||||||
|
|
||||||
# Reviews
|
# Reviews
|
||||||
|
@ -268,7 +267,7 @@ where queue = 0 and type = 1
|
||||||
def _resetReviewCount(self):
|
def _resetReviewCount(self):
|
||||||
self.revCount = self.db.scalar("""
|
self.revCount = self.db.scalar("""
|
||||||
select count() from (select id from cards where
|
select count() from (select id from cards where
|
||||||
queue = 1 %s and due <= :lim limit %d)""" % (
|
queue = 2 %s and due <= :lim limit %d)""" % (
|
||||||
self._groupLimit("rev"), self.reportLimit),
|
self._groupLimit("rev"), self.reportLimit),
|
||||||
lim=self.today)
|
lim=self.today)
|
||||||
|
|
||||||
|
@ -276,7 +275,7 @@ queue = 1 %s and due <= :lim limit %d)""" % (
|
||||||
self._resetReviewCount()
|
self._resetReviewCount()
|
||||||
self.revQueue = self.db.list("""
|
self.revQueue = self.db.list("""
|
||||||
select id from cards where
|
select id from cards where
|
||||||
queue = 1 %s and due <= :lim order by %s limit %d""" % (
|
queue = 2 %s and due <= :lim order by %s limit %d""" % (
|
||||||
self._groupLimit("rev"), self._revOrder(), self.queueLimit),
|
self._groupLimit("rev"), self._revOrder(), self.queueLimit),
|
||||||
lim=self.today)
|
lim=self.today)
|
||||||
if self.deck.qconf['revCardOrder'] == REV_CARDS_RANDOM:
|
if self.deck.qconf['revCardOrder'] == REV_CARDS_RANDOM:
|
||||||
|
@ -321,7 +320,7 @@ queue = 1 %s and due <= :lim order by %s limit %d""" % (
|
||||||
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?
|
||||||
if conf['relearn']:
|
if conf['relearn']:
|
||||||
card.queue = 0
|
card.queue = 1
|
||||||
self.learnCount += 1
|
self.learnCount += 1
|
||||||
# leech?
|
# leech?
|
||||||
self._checkLeech(card, conf)
|
self._checkLeech(card, conf)
|
||||||
|
@ -382,7 +381,7 @@ queue = 1 %s and due <= :lim order by %s limit %d""" % (
|
||||||
conf = self._cardConf(card)['rev']
|
conf = self._cardConf(card)['rev']
|
||||||
# find sibling positions
|
# find sibling positions
|
||||||
dues = self.db.list(
|
dues = self.db.list(
|
||||||
"select due from cards where fid = ? and queue = 1"
|
"select due from cards where fid = ? and queue = 2"
|
||||||
" and id != ?", card.fid, card.id)
|
" and id != ?", card.fid, card.id)
|
||||||
if not dues or idealDue not in dues:
|
if not dues or idealDue not in dues:
|
||||||
card.ivl = idealIvl
|
card.ivl = idealIvl
|
||||||
|
@ -508,13 +507,13 @@ queue = 1 %s and due <= :lim order by %s limit %d""" % (
|
||||||
def lrnTomorrow(self):
|
def lrnTomorrow(self):
|
||||||
"Number of cards in the learning queue due tomorrow."
|
"Number of cards in the learning queue due tomorrow."
|
||||||
return self.db.scalar(
|
return self.db.scalar(
|
||||||
"select count() from cards where queue = 0 and due < ?",
|
"select count() from cards where queue = 1 and due < ?",
|
||||||
self.dayCutoff+86400)
|
self.dayCutoff+86400)
|
||||||
|
|
||||||
def revTomorrow(self):
|
def revTomorrow(self):
|
||||||
"Number of reviews due tomorrow."
|
"Number of reviews due tomorrow."
|
||||||
return self.db.scalar(
|
return self.db.scalar(
|
||||||
"select count() from cards where queue = 1 and due = ?"+
|
"select count() from cards where queue = 2 and due = ?"+
|
||||||
self._groupLimit("rev"),
|
self._groupLimit("rev"),
|
||||||
self.today+1)
|
self.today+1)
|
||||||
|
|
||||||
|
@ -523,7 +522,7 @@ queue = 1 %s and due <= :lim order by %s limit %d""" % (
|
||||||
lim = self.deck.qconf['newPerDay']
|
lim = self.deck.qconf['newPerDay']
|
||||||
return self.db.scalar(
|
return self.db.scalar(
|
||||||
"select count() from (select id from cards where "
|
"select count() from (select id from cards where "
|
||||||
"queue = 2 limit %d)" % lim)
|
"queue = 0 limit %d)" % lim)
|
||||||
|
|
||||||
# Next time reports
|
# Next time reports
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -535,7 +534,7 @@ queue = 1 %s and due <= :lim order by %s limit %d""" % (
|
||||||
|
|
||||||
def nextIvl(self, card, ease):
|
def nextIvl(self, card, ease):
|
||||||
"Return the next interval for CARD, in seconds."
|
"Return the next interval for CARD, in seconds."
|
||||||
if card.queue in (0,2):
|
if card.queue in (0,1):
|
||||||
# in learning
|
# in learning
|
||||||
return self._nextLrnIvl(card, ease)
|
return self._nextLrnIvl(card, ease)
|
||||||
elif ease == 1:
|
elif ease == 1:
|
||||||
|
|
|
@ -22,13 +22,13 @@ def test_new():
|
||||||
# fetch it
|
# fetch it
|
||||||
c = d.sched.getCard()
|
c = d.sched.getCard()
|
||||||
assert c
|
assert c
|
||||||
assert c.queue == 2
|
assert c.queue == 0
|
||||||
assert c.type == 2
|
assert c.type == 0
|
||||||
# if we answer it, it should become a learn card
|
# if we answer it, it should become a learn card
|
||||||
t = intTime()
|
t = intTime()
|
||||||
d.sched.answerCard(c, 1)
|
d.sched.answerCard(c, 1)
|
||||||
assert c.queue == 0
|
assert c.queue == 1
|
||||||
assert c.type == 2
|
assert c.type == 0
|
||||||
assert c.due >= t
|
assert c.due >= t
|
||||||
# the default order should ensure siblings are not seen together, and
|
# the default order should ensure siblings are not seen together, and
|
||||||
# should show all cards
|
# should show all cards
|
||||||
|
@ -55,7 +55,7 @@ def test_learn():
|
||||||
f['Front'] = u"one"; f['Back'] = u"two"
|
f['Front'] = u"one"; f['Back'] = u"two"
|
||||||
f = d.addFact(f)
|
f = d.addFact(f)
|
||||||
# set as a learn card and rebuild queues
|
# set as a learn card and rebuild queues
|
||||||
d.db.execute("update cards set queue=0, type=2")
|
d.db.execute("update cards set queue=0, type=0")
|
||||||
d.reset()
|
d.reset()
|
||||||
# sched.getCard should return it, since it's due in the past
|
# sched.getCard should return it, since it's due in the past
|
||||||
c = d.sched.getCard()
|
c = d.sched.getCard()
|
||||||
|
@ -90,46 +90,47 @@ def test_learn():
|
||||||
assert c.grade == 2
|
assert c.grade == 2
|
||||||
assert c.cycles == 3
|
assert c.cycles == 3
|
||||||
# the next pass should graduate the card
|
# the next pass should graduate the card
|
||||||
assert c.queue == 0
|
|
||||||
assert c.type == 2
|
|
||||||
d.sched.answerCard(c, 2)
|
|
||||||
assert c.queue == 1
|
assert c.queue == 1
|
||||||
assert c.type == 1
|
assert c.type == 0
|
||||||
|
d.sched.answerCard(c, 2)
|
||||||
|
assert c.queue == 2
|
||||||
|
assert c.type == 2
|
||||||
# should be due tomorrow, with an interval of 1
|
# should be due tomorrow, with an interval of 1
|
||||||
assert c.due == d.sched.today+1
|
assert c.due == d.sched.today+1
|
||||||
assert c.ivl == 1
|
assert c.ivl == 1
|
||||||
# let's try early removal bonus
|
# let's try early removal bonus
|
||||||
c.type = 2
|
c.type = 0
|
||||||
c.queue = 0
|
c.queue = 1
|
||||||
c.cycles = 0
|
c.cycles = 0
|
||||||
d.sched.answerCard(c, 3)
|
d.sched.answerCard(c, 3)
|
||||||
assert c.type == 1
|
assert c.type == 2
|
||||||
assert c.ivl == 7
|
assert c.ivl == 7
|
||||||
# or normal removal
|
# or normal removal
|
||||||
c.type = 2
|
c.type = 0
|
||||||
c.queue = 0
|
c.queue = 1
|
||||||
c.cycles = 1
|
c.cycles = 1
|
||||||
d.sched.answerCard(c, 3)
|
d.sched.answerCard(c, 3)
|
||||||
assert c.type == 1
|
assert c.type == 2
|
||||||
|
assert c.queue == 2
|
||||||
assert c.ivl == 4
|
assert c.ivl == 4
|
||||||
# revlog should have been updated each time
|
# revlog should have been updated each time
|
||||||
d.db.scalar("select count() from revlog where type = 0") == 6
|
d.db.scalar("select count() from revlog where type = 0") == 6
|
||||||
# now failed card handling
|
# now failed card handling
|
||||||
c.type = 1
|
c.type = 2
|
||||||
c.queue = 0
|
c.queue = 1
|
||||||
c.edue = 123
|
c.edue = 123
|
||||||
d.sched.answerCard(c, 3)
|
d.sched.answerCard(c, 3)
|
||||||
assert c.due == 123
|
assert c.due == 123
|
||||||
assert c.type == 1
|
assert c.type == 2
|
||||||
assert c.queue == 1
|
assert c.queue == 2
|
||||||
# we should be able to remove manually, too
|
# we should be able to remove manually, too
|
||||||
c.type = 1
|
c.type = 2
|
||||||
c.queue = 0
|
c.queue = 1
|
||||||
c.edue = 321
|
c.edue = 321
|
||||||
c.flush()
|
c.flush()
|
||||||
d.sched.removeFailed()
|
d.sched.removeFailed()
|
||||||
c.load()
|
c.load()
|
||||||
assert c.queue == 1
|
assert c.queue == 2
|
||||||
assert c.due == 321
|
assert c.due == 321
|
||||||
|
|
||||||
def test_reviews():
|
def test_reviews():
|
||||||
|
@ -140,8 +141,8 @@ def test_reviews():
|
||||||
d.addFact(f)
|
d.addFact(f)
|
||||||
# set the card up as a review card, due yesterday
|
# set the card up as a review card, due yesterday
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
c.type = 1
|
c.type = 2
|
||||||
c.queue = 1
|
c.queue = 2
|
||||||
c.due = d.sched.today - 8
|
c.due = d.sched.today - 8
|
||||||
c.factor = 2500
|
c.factor = 2500
|
||||||
c.reps = 3
|
c.reps = 3
|
||||||
|
@ -156,7 +157,7 @@ def test_reviews():
|
||||||
# failing it should put it in the learn queue with the default options
|
# failing it should put it in the learn queue with the default options
|
||||||
##################################################
|
##################################################
|
||||||
d.sched.answerCard(c, 1)
|
d.sched.answerCard(c, 1)
|
||||||
assert c.queue == 0
|
assert c.queue == 1
|
||||||
# it should be due tomorrow, with an interval of 1
|
# it should be due tomorrow, with an interval of 1
|
||||||
assert c.due == d.sched.today + 1
|
assert c.due == d.sched.today + 1
|
||||||
assert c.ivl == 1
|
assert c.ivl == 1
|
||||||
|
@ -171,6 +172,7 @@ def test_reviews():
|
||||||
c = copy.copy(cardcopy)
|
c = copy.copy(cardcopy)
|
||||||
c.flush()
|
c.flush()
|
||||||
d.sched.answerCard(c, 2)
|
d.sched.answerCard(c, 2)
|
||||||
|
assert c.queue == 2
|
||||||
# the new interval should be (100 + 8/4) * 1.2 = 122
|
# the new interval should be (100 + 8/4) * 1.2 = 122
|
||||||
assert c.ivl == 122
|
assert c.ivl == 122
|
||||||
assert c.due == d.sched.today + 122
|
assert c.due == d.sched.today + 122
|
||||||
|
@ -257,7 +259,7 @@ def test_nextIvl():
|
||||||
assert ni(c, 3) == 4*86400
|
assert ni(c, 3) == 4*86400
|
||||||
# review cards
|
# review cards
|
||||||
##################################################
|
##################################################
|
||||||
c.queue = 1
|
c.queue = 2
|
||||||
c.ivl = 100
|
c.ivl = 100
|
||||||
c.factor = 2500
|
c.factor = 2500
|
||||||
# failing it puts it at tomorrow
|
# failing it puts it at tomorrow
|
||||||
|
|
Loading…
Reference in a new issue