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:
Damien Elmes 2011-03-18 11:01:26 +09:00
parent 6f5918e8cd
commit 22a72d82c6
4 changed files with 58 additions and 57 deletions

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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