From 8c9c3489e53db25e4241162d1ff57315e05fda80 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 9 Dec 2011 03:31:50 +0900 Subject: [PATCH] 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 --- anki/collection.py | 4 -- anki/decks.py | 3 +- anki/sched.py | 129 +++++++++++++++++++++++++++----------------- tests/test_sched.py | 17 ------ 4 files changed, 81 insertions(+), 72 deletions(-) diff --git a/anki/collection.py b/anki/collection.py index 57da0d2fb..3c817462f 100644 --- a/anki/collection.py +++ b/anki/collection.py @@ -23,12 +23,8 @@ defaultConf = { 'activeDecks': [1], 'topDeck': 1, 'curDeck': 1, - 'revOrder': REV_CARDS_RANDOM, # other config 'nextPos': 1, - 'fontFamilies': [ - [u'MS 明朝',u'ヒラギノ明朝 Pro W3',u'Kochi Mincho', u'東風明朝'] - ], 'sortType': "noteFld", 'sortBackwards': False, } diff --git a/anki/decks.py b/anki/decks.py index df76b0c84..bb5ba588e 100644 --- a/anki/decks.py +++ b/anki/decks.py @@ -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, diff --git a/anki/sched.py b/anki/sched.py index f9aef0d25..cfa2a3d5e 100644 --- a/anki/sched.py +++ b/anki/sched.py @@ -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 - 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 -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 diff --git a/tests/test_sched.py b/tests/test_sched.py index d74f44926..50fd8ab78 100644 --- a/tests/test_sched.py +++ b/tests/test_sched.py @@ -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 -