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], 'activeDecks': [1],
'topDeck': 1, 'topDeck': 1,
'curDeck': 1, 'curDeck': 1,
'revOrder': REV_CARDS_RANDOM,
# other config # other config
'nextPos': 1, 'nextPos': 1,
'fontFamilies': [
[u' 明朝',u'ヒラギノ明朝 Pro W3',u'Kochi Mincho', u'東風明朝']
],
'sortType': "noteFld", 'sortType': "noteFld",
'sortBackwards': False, 'sortBackwards': False,
} }

View file

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

View file

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

View file

@ -883,20 +883,3 @@ def test_resched():
assert c.due == d.sched.today+1 assert c.due == d.sched.today+1
assert c.ivl == +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