From d6bce7c873438151388b00cacbeb39563310a885 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 24 May 2012 13:38:58 +0900 Subject: [PATCH] start on filter/cram updates, deck:filtered - cram decks no longer pull cards from other cram decks, as tests like is:due don't make sense on crammed decks - cram decks now default to not overriding the original deck's steps - remove obsolote dyn_failed sort order - have to fix this in upgrade - search/limit/order properties have been merged into a list, in case we may want to support multiple queries in the future (eg 10 cards from tag A, 20 cards from tag B) - added a resched option to disable boosting/failure - not yet implemented - added an unused flag to allow cards to persist in the cram deck after graduation. implementing this will take some work and won't come in 2.0 - instead of is:filtereddeck, use deck:filtered --- anki/consts.py | 10 +++---- anki/decks.py | 10 +++---- anki/find.py | 7 ++--- anki/sched.py | 67 +++++++++++++++++++++++++-------------------- tests/test_sched.py | 8 +++++- thirdparty/README | 4 ++- 6 files changed, 59 insertions(+), 47 deletions(-) diff --git a/anki/consts.py b/anki/consts.py index 107e6bf6e..aac07f45f 100644 --- a/anki/consts.py +++ b/anki/consts.py @@ -33,9 +33,8 @@ DYN_RANDOM = 1 DYN_SMALLINT = 2 DYN_BIGINT = 3 DYN_LAPSES = 4 -DYN_FAILED = 5 -DYN_ADDED = 6 -DYN_DUE = 7 +DYN_ADDED = 5 +DYN_DUE = 6 # model types MODEL_STD = 0 @@ -77,7 +76,6 @@ def dynOrderLabels(): 2: _("Increasing intervals"), 3: _("Decreasing intervals"), 4: _("Most lapses"), - 5: _("Failed today"), - 6: _("Order added"), - 7: _("Order due"), + 5: _("Order added"), + 6: _("Order due"), } diff --git a/anki/decks.py b/anki/decks.py index 3a2d6fb14..2e5646fd0 100644 --- a/anki/decks.py +++ b/anki/decks.py @@ -35,13 +35,13 @@ defaultDynamicDeck = { 'dyn': 1, 'desc': "", 'usn': 0, - 'delays': [1, 10], + 'delays': None, 'separate': True, - 'search': "", - 'limit': 100, - 'order': 0, # added in beta13 'resched': True, + # list of (search, limit, order); we only use first element for now + 'terms': [["", 100, 0]], + 'return': True, # currently unused } defaultConf = { @@ -142,7 +142,7 @@ class DeckManager(object): if deck['dyn']: # deleting a cramming deck returns cards to their previous deck # rather than deleting the cards - self.col.sched.remDyn(did) + self.col.sched.emptyDyn(did) if childrenToo: for name, id in self.children(did): self.rem(id, cardsToo) diff --git a/anki/find.py b/anki/find.py index 2d538aaca..375db644b 100644 --- a/anki/find.py +++ b/anki/find.py @@ -236,10 +236,6 @@ class Finder(object): return "type = %d" % n elif val == "suspended": return "c.queue = -1" - elif val == "regulardeck": - return "not c.odid" - elif val == "filterdeck": - return "c.odid" elif val == "due": return """ (c.queue in (2,3) and c.due <= %d) or @@ -312,6 +308,9 @@ class Finder(object): # if searching for all decks, skip if val == "*": return "skip" + # deck types + elif val == "filtered": + return "c.odid" def dids(did): if not did: return None diff --git a/anki/sched.py b/anki/sched.py index aa0091a5b..393f747b7 100644 --- a/anki/sched.py +++ b/anki/sched.py @@ -115,7 +115,7 @@ order by due""" % self._deckLimit(), return card.queue def answerButtons(self, card): - if not card.odid and card.odue: + if card.odue: conf = self._lapseConf(card) if len(conf['delays']) > 1: return 3 @@ -849,22 +849,29 @@ did = ? and queue = 2 and due <= ? limit ?""", did = did or self.col.decks.selected() deck = self.col.decks.get(did) assert deck['dyn'] - # move any existing cards back first - self.remDyn(did) - # gather card ids and sort - order = self._dynOrder(deck) - limit = " limit %d" % deck['limit'] - search = deck['search'] + " -is:suspended is:regulardeck" - try: - ids = self.col.findCards(search, order=order+limit) - except: - ids = [] - # move the cards over - self._moveToDyn(did, ids) + # move any existing cards back first, then fill + self.emptyDyn(did) + ids = self._fillDyn(deck) + if not ids: + return # and change to our new deck self.col.decks.select(did) + return ids - def remDyn(self, did, lim=None): + def _fillDyn(self, deck): + search, limit, order = deck['terms'][0] + orderlimit = self._dynOrder(order, limit) + search += " -is:suspended -deck:filtered" + try: + ids = self.col.findCards(search, order=orderlimit) + except: + ids = [] + return ids + # move the cards over + self._moveToDyn(deck['id'], ids) + return ids + + def emptyDyn(self, did, lim=None): if not lim: lim = "did = %s" % did # move out of cram queue @@ -875,28 +882,26 @@ due = odue, odue = 0, odid = 0, usn = ?, mod = ? where %s""" % lim, self.col.usn(), intTime()) def remFromDyn(self, cids): - self.remDyn(None, "id in %s and odid" % ids2str(cids)) + self.emptyDyn(None, "id in %s and odid" % ids2str(cids)) - def _dynOrder(self, deck): - o = deck['order'] + def _dynOrder(self, o, l): if o == DYN_OLDEST: - return "c.mod" + t = "c.mod" elif o == DYN_RANDOM: - return "random()" + t = "random()" elif o == DYN_SMALLINT: - return "ivl" + t = "ivl" elif o == DYN_BIGINT: - return "ivl desc" + t = "ivl desc" elif o == DYN_LAPSES: - return "lapses desc" -# elif o == DYN_FAILED: -# return """ -# and c.id in (select cid from revlog where ease = 1 and time > %d) -# order by c.mod""" % ((self.dayCutoff-86400)*1000) + t = "lapses desc" elif o == DYN_ADDED: - return "n.id" + t = "n.id" elif o == DYN_DUE: - return "c.due" + t = "c.due" + else: + raise Exception() + return t + " limit %d" % l def _moveToDyn(self, did, ids): deck = self.col.decks.get(did) @@ -966,12 +971,13 @@ did = ?, queue = %s, due = ?, mod = ?, usn = ? where id = ?""" % queue, data) return conf['new'] # dynamic deck; override some attributes, use original deck for others oconf = self.col.decks.confForDid(card.odid) + delays = conf['delays'] or oconf['new']['delays'] return dict( # original deck ints=oconf['new']['ints'], initialFactor=oconf['new']['initialFactor'], # overrides - delays=conf['delays'], + delays=delays, separate=conf['separate'], order=NEW_CARDS_DUE, perDay=self.reportLimit @@ -984,6 +990,7 @@ did = ?, queue = %s, due = ?, mod = ?, usn = ? where id = ?""" % queue, data) return conf['lapse'] # dynamic deck; override some attributes, use original deck for others oconf = self.col.decks.confForDid(card.odid) + delays = conf['delays'] or oconf['lapse']['delays'] return dict( # original deck minInt=oconf['lapse']['minInt'], @@ -991,7 +998,7 @@ did = ?, queue = %s, due = ?, mod = ?, usn = ? where id = ?""" % queue, data) leechAction=oconf['lapse']['leechAction'], mult=oconf['lapse']['mult'], # overrides - delays=conf['delays'], + delays=delays, resched=conf['resched'], ) diff --git a/tests/test_sched.py b/tests/test_sched.py index 7c7e488f3..86c65cb04 100644 --- a/tests/test_sched.py +++ b/tests/test_sched.py @@ -527,6 +527,12 @@ def test_cram(): assert d.sched.counts() == (1,0,0) # grab it and check estimates c = d.sched.getCard() + assert d.sched.answerButtons(c) == 2 + assert d.sched.nextIvl(c, 1) == 600 + assert d.sched.nextIvl(c, 2) == 138*60*60*24 + cram = d.decks.get(did) + cram['delays'] = [1, 10] + assert d.sched.answerButtons(c) == 3 assert d.sched.nextIvl(c, 1) == 60 assert d.sched.nextIvl(c, 2) == 600 assert d.sched.nextIvl(c, 3) == 138*60*60*24 @@ -618,7 +624,7 @@ def test_cram_rem(): assert c.type == c.queue == 1 assert c.due != oldDue # if we terminate cramming prematurely it should be set back to new - d.sched.remDyn(did) + d.sched.emptyDyn(did) c.load() assert c.type == c.queue == 0 assert c.due == oldDue diff --git a/thirdparty/README b/thirdparty/README index d1e94a0f1..a5ee1978d 100644 --- a/thirdparty/README +++ b/thirdparty/README @@ -1 +1,3 @@ -The files in this folder are covered by their respective licenses. +The files in this folder are covered by their respective licenses and are +provided to minimize external dependencies. The _portaudio.so files were +extracted from Ubuntu 32bit and 64 distributions - check md5s to confirm.