mirror of
https://github.com/ankitects/anki.git
synced 2025-09-25 01:06:35 -04:00
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:
parent
d94f6d2011
commit
8c9c3489e5
4 changed files with 81 additions and 72 deletions
|
@ -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'MS 明朝',u'ヒラギノ明朝 Pro W3',u'Kochi Mincho', u'東風明朝']
|
|
||||||
],
|
|
||||||
'sortType': "noteFld",
|
'sortType': "noteFld",
|
||||||
'sortBackwards': False,
|
'sortBackwards': False,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
129
anki/sched.py
129
anki/sched.py
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue