fetch reviews in subdecks incrementally like new cards

- allows separate review order for different decks
- makes new card and rev card handling consistent
- for users who find it confusing to have cards from different decks mixed in
  and thus click on each deck in turn, they can now just select the parent
  deck and have it work as expected
- for users who want their cards mixed together randomly, they can keep the
  cards in a single deck
This commit is contained in:
Damien Elmes 2011-12-09 03:31:50 +09:00
parent d94f6d2011
commit 8c9c3489e5
4 changed files with 81 additions and 72 deletions

View file

@ -23,12 +23,8 @@ defaultConf = {
'activeDecks': [1],
'topDeck': 1,
'curDeck': 1,
'revOrder': REV_CARDS_RANDOM,
# other config
'nextPos': 1,
'fontFamilies': [
[u' 明朝',u'ヒラギノ明朝 Pro W3',u'Kochi Mincho', u'東風明朝']
],
'sortType': "noteFld",
'sortBackwards': False,
}

View file

@ -34,7 +34,6 @@ defaultDeck = {
# configuration only available to top level decks
defaultTopConf = {
'revLim': 100,
'newSpread': NEW_CARDS_DISTRIBUTE,
'collapseTime': 1200,
'repLim': 0,
@ -69,10 +68,12 @@ defaultConf = {
'minInt': 1,
},
'rev': {
'perDay': 100,
'ease4': 1.3,
'fuzz': 0.05,
'minSpace': 1,
'fi': [0.1, 0.1],
'order': REV_CARDS_RANDOM,
},
'maxTaken': 60,
'mod': 0,

View file

@ -119,6 +119,34 @@ order by due""" % self._deckLimit(),
g[key][1] += cnt
self.col.decks.save(g)
def _walkingCount(self, limFn=None, cntFn=None):
tot = 0
pcounts = {}
# for each of the active decks
for did in self.col.decks.active():
# get the individual deck's limit
lim = limFn(self.col.decks.get(did))
if not lim:
continue
# check the parents
parents = self.col.decks.parents(did)
for p in parents:
# add if missing
if p['id'] not in pcounts:
pcounts[p['id']] = limFn(p)
# take minimum of child and parent
lim = min(pcounts[p['id']], lim)
# see how many cards we actually have
cnt = cntFn(did, lim)
# if non-zero, decrement from parent counts
for p in parents:
pcounts[p['id']] -= cnt
# we may also be a parent
pcounts[did] = lim - cnt
# and add to running total
tot += cnt
return tot
# Deck list
##########################################################################
@ -201,37 +229,14 @@ order by due""" % self._deckLimit(),
##########################################################################
def _resetNewCount(self):
self.newCount = 0
pcounts = {}
# for each of the active decks
for did in self.col.decks.active():
# get the individual deck's limit
lim = self._deckNewLimitSingle(self.col.decks.get(did))
if not lim:
continue
# check the parents
parents = self.col.decks.parents(did)
for p in parents:
# add if missing
if p['id'] not in pcounts:
pcounts[p['id']] = self._deckNewLimitSingle(p)
# take minimum of child and parent
lim = min(pcounts[p['id']], lim)
# see how many cards we actually have
cnt = self.col.db.scalar("""
cntFn = lambda did, lim: self.col.db.scalar("""
select count() from (select 1 from cards where
did = ? and queue = 0 limit ?)""", did, lim)
# if non-zero, decrement from parent counts
for p in parents:
pcounts[p['id']] -= cnt
# we may also be a parent
pcounts[did] = lim - cnt
# and add to running total
self.newCount += cnt
self.newCount = self._walkingCount(self._deckNewLimitSingle, cntFn)
def _resetNew(self):
self._resetNewCount()
self.newDids = self.col.decks.active()
self._newDids = self.col.decks.active()
self._newQueue = []
self._updateNewCardRatio()
@ -240,8 +245,8 @@ did = ? and queue = 0 limit ?)""", did, lim)
return True
if not self.newCount:
return False
while self.newDids:
did = self.newDids[0]
while self._newDids:
did = self._newDids[0]
lim = min(self.queueLimit, self._deckNewLimit(did))
if lim:
# fill the queue with the current did
@ -251,14 +256,14 @@ select id, due from cards where did = ? and queue = 0 limit ?""", did, lim)
self._newQueue.reverse()
return True
# nothing left in the deck; move to next
self.newDids.pop(0)
self._newDids.pop(0)
def _getNewCard(self):
if not self._fillNew():
return
(id, due) = self._newQueue.pop()
# move any siblings to the end?
conf = self.col.decks.conf(self.newDids[0])
conf = self.col.decks.conf(self._newDids[0])
if conf['new']['order'] == NEW_TODAY_ORD:
n = len(self._newQueue)
while self._newQueue and self._newQueue[-1][1] == due:
@ -298,12 +303,14 @@ select id, due from cards where did = ? and queue = 0 limit ?""", did, lim)
return self.col.db.scalar(
"select 1 from cards where did = ? and queue = 0 limit 1", did)
def _deckNewLimit(self, did):
def _deckNewLimit(self, did, fn=None):
if not fn:
fn = self._deckNewLimitSingle
sel = self.col.decks.get(did)
lim = -1
# for the deck and each of its parents
for g in [sel] + self.col.decks.parents(did):
rem = self._deckNewLimitSingle(g)
rem = fn(g)
if lim == -1:
lim = rem
else:
@ -465,39 +472,59 @@ where queue = 1 and type = 2
# Reviews
##########################################################################
def _deckRevLimit(self, did):
return self._deckNewLimit(did, self._deckRevLimitSingle)
def _deckRevLimitSingle(self, d):
c = self.col.decks.conf(d['id'])
return max(0, c['rev']['perDay'] - d['revToday'][1])
def _deckHasRev(self, did):
if not self._deckRevLimit(did):
return False
return self.col.db.scalar(
"select 1 from cards where did = ? and queue = 2 "
"and due <= ? limit 1",
did, self.today)
"and due <= ? limit 1", did, self.today)
def _resetRevCount(self):
top = self.col.decks.top()
lim = min(self.reportLimit,
max(0, top['revLim'] - top['revToday'][1]))
self.revCount = self.col.db.scalar("""
def cntFn(did, lim):
return self.col.db.scalar("""
select count() from (select id from cards where
did in %s and queue = 2 and due <= :day limit %d)""" % (
self._deckLimit(), lim), day=self.today)
did = ? and queue = 2 and due <= ? limit %d)""" % lim,
did, self.today)
self.revCount = self._walkingCount(
self._deckRevLimitSingle, cntFn)
def _resetRev(self):
self._resetRevCount()
self._revQueue = []
self._revDids = self.col.decks.active()
def _fillRev(self):
if not self.revCount:
return False
if self._revQueue:
return True
if not self.revCount:
return False
while self._revDids:
did = self._newDids[0]
lim = min(self.queueLimit, self._deckRevLimit(did))
order = self._revOrder(did)
if lim:
# fill the queue with the current did
self._revQueue = self.col.db.list("""
select id from cards where
did in %s and queue = 2 and due <= :lim %s limit %d""" % (
self._deckLimit(), self._revOrder(), self.queueLimit),
lim=self.today)
if not self.col.conf['revOrder']:
did = ? and queue = 2 and due <= ? %s limit ?""" % order,
did, self.today, lim)
if self._revQueue:
return True
# nothing left in the deck; move to next
self._newDids.pop(0)
if not order:
r = random.Random()
r.seed(self.today)
r.shuffle(self._revQueue)
else:
self._revQueue.reverse()
return True
def _getRevCard(self):
@ -505,9 +532,11 @@ did in %s and queue = 2 and due <= :lim %s limit %d""" % (
self.revCount -= 1
return self.col.getCard(self._revQueue.pop())
def _revOrder(self):
if self.col.conf['revOrder']:
return "order by %s" % ("ivl desc", "ivl")[self.col.conf['revOrder']-1]
def _revOrder(self, did):
d = self.col.decks.conf(did)
o = d['rev']['order']
if o:
return "order by %s" % ("ivl desc", "ivl")[o-1]
return ""
# Answering a review card

View file

@ -883,20 +883,3 @@ def test_resched():
assert c.due == d.sched.today+1
assert c.ivl == +1
def test_revlim():
d = getEmptyDeck()
for i in range(20):
f = d.newNote()
f['Front'] = str(i)
d.addNote(f)
d.db.execute("update cards set due = 0, queue = 2, type = 2")
d.reset()
assert d.sched.counts()[2] == 20
for i in range(5):
d.sched.answerCard(d.sched.getCard(), 3)
assert d.sched.counts()[2] == 15
t = d.decks.top()
t['revLim'] = 10
d.reset()
assert d.sched.counts()[2] == 5