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
This commit is contained in:
Damien Elmes 2012-05-24 13:38:58 +09:00
parent 44b4f18e6c
commit d6bce7c873
6 changed files with 59 additions and 47 deletions

View file

@ -33,9 +33,8 @@ DYN_RANDOM = 1
DYN_SMALLINT = 2 DYN_SMALLINT = 2
DYN_BIGINT = 3 DYN_BIGINT = 3
DYN_LAPSES = 4 DYN_LAPSES = 4
DYN_FAILED = 5 DYN_ADDED = 5
DYN_ADDED = 6 DYN_DUE = 6
DYN_DUE = 7
# model types # model types
MODEL_STD = 0 MODEL_STD = 0
@ -77,7 +76,6 @@ def dynOrderLabels():
2: _("Increasing intervals"), 2: _("Increasing intervals"),
3: _("Decreasing intervals"), 3: _("Decreasing intervals"),
4: _("Most lapses"), 4: _("Most lapses"),
5: _("Failed today"), 5: _("Order added"),
6: _("Order added"), 6: _("Order due"),
7: _("Order due"),
} }

View file

@ -35,13 +35,13 @@ defaultDynamicDeck = {
'dyn': 1, 'dyn': 1,
'desc': "", 'desc': "",
'usn': 0, 'usn': 0,
'delays': [1, 10], 'delays': None,
'separate': True, 'separate': True,
'search': "",
'limit': 100,
'order': 0,
# added in beta13 # added in beta13
'resched': True, 'resched': True,
# list of (search, limit, order); we only use first element for now
'terms': [["", 100, 0]],
'return': True, # currently unused
} }
defaultConf = { defaultConf = {
@ -142,7 +142,7 @@ class DeckManager(object):
if deck['dyn']: if deck['dyn']:
# deleting a cramming deck returns cards to their previous deck # deleting a cramming deck returns cards to their previous deck
# rather than deleting the cards # rather than deleting the cards
self.col.sched.remDyn(did) self.col.sched.emptyDyn(did)
if childrenToo: if childrenToo:
for name, id in self.children(did): for name, id in self.children(did):
self.rem(id, cardsToo) self.rem(id, cardsToo)

View file

@ -236,10 +236,6 @@ class Finder(object):
return "type = %d" % n return "type = %d" % n
elif val == "suspended": elif val == "suspended":
return "c.queue = -1" return "c.queue = -1"
elif val == "regulardeck":
return "not c.odid"
elif val == "filterdeck":
return "c.odid"
elif val == "due": elif val == "due":
return """ return """
(c.queue in (2,3) and c.due <= %d) or (c.queue in (2,3) and c.due <= %d) or
@ -312,6 +308,9 @@ class Finder(object):
# if searching for all decks, skip # if searching for all decks, skip
if val == "*": if val == "*":
return "skip" return "skip"
# deck types
elif val == "filtered":
return "c.odid"
def dids(did): def dids(did):
if not did: if not did:
return None return None

View file

@ -115,7 +115,7 @@ order by due""" % self._deckLimit(),
return card.queue return card.queue
def answerButtons(self, card): def answerButtons(self, card):
if not card.odid and card.odue: if card.odue:
conf = self._lapseConf(card) conf = self._lapseConf(card)
if len(conf['delays']) > 1: if len(conf['delays']) > 1:
return 3 return 3
@ -849,22 +849,29 @@ did = ? and queue = 2 and due <= ? limit ?""",
did = did or self.col.decks.selected() did = did or self.col.decks.selected()
deck = self.col.decks.get(did) deck = self.col.decks.get(did)
assert deck['dyn'] assert deck['dyn']
# move any existing cards back first # move any existing cards back first, then fill
self.remDyn(did) self.emptyDyn(did)
# gather card ids and sort ids = self._fillDyn(deck)
order = self._dynOrder(deck) if not ids:
limit = " limit %d" % deck['limit'] return
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)
# and change to our new deck # and change to our new deck
self.col.decks.select(did) 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: if not lim:
lim = "did = %s" % did lim = "did = %s" % did
# move out of cram queue # move out of cram queue
@ -875,28 +882,26 @@ due = odue, odue = 0, odid = 0, usn = ?, mod = ? where %s""" % lim,
self.col.usn(), intTime()) self.col.usn(), intTime())
def remFromDyn(self, cids): 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): def _dynOrder(self, o, l):
o = deck['order']
if o == DYN_OLDEST: if o == DYN_OLDEST:
return "c.mod" t = "c.mod"
elif o == DYN_RANDOM: elif o == DYN_RANDOM:
return "random()" t = "random()"
elif o == DYN_SMALLINT: elif o == DYN_SMALLINT:
return "ivl" t = "ivl"
elif o == DYN_BIGINT: elif o == DYN_BIGINT:
return "ivl desc" t = "ivl desc"
elif o == DYN_LAPSES: elif o == DYN_LAPSES:
return "lapses desc" t = "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)
elif o == DYN_ADDED: elif o == DYN_ADDED:
return "n.id" t = "n.id"
elif o == DYN_DUE: elif o == DYN_DUE:
return "c.due" t = "c.due"
else:
raise Exception()
return t + " limit %d" % l
def _moveToDyn(self, did, ids): def _moveToDyn(self, did, ids):
deck = self.col.decks.get(did) deck = self.col.decks.get(did)
@ -966,12 +971,13 @@ did = ?, queue = %s, due = ?, mod = ?, usn = ? where id = ?""" % queue, data)
return conf['new'] return conf['new']
# dynamic deck; override some attributes, use original deck for others # dynamic deck; override some attributes, use original deck for others
oconf = self.col.decks.confForDid(card.odid) oconf = self.col.decks.confForDid(card.odid)
delays = conf['delays'] or oconf['new']['delays']
return dict( return dict(
# original deck # original deck
ints=oconf['new']['ints'], ints=oconf['new']['ints'],
initialFactor=oconf['new']['initialFactor'], initialFactor=oconf['new']['initialFactor'],
# overrides # overrides
delays=conf['delays'], delays=delays,
separate=conf['separate'], separate=conf['separate'],
order=NEW_CARDS_DUE, order=NEW_CARDS_DUE,
perDay=self.reportLimit perDay=self.reportLimit
@ -984,6 +990,7 @@ did = ?, queue = %s, due = ?, mod = ?, usn = ? where id = ?""" % queue, data)
return conf['lapse'] return conf['lapse']
# dynamic deck; override some attributes, use original deck for others # dynamic deck; override some attributes, use original deck for others
oconf = self.col.decks.confForDid(card.odid) oconf = self.col.decks.confForDid(card.odid)
delays = conf['delays'] or oconf['lapse']['delays']
return dict( return dict(
# original deck # original deck
minInt=oconf['lapse']['minInt'], minInt=oconf['lapse']['minInt'],
@ -991,7 +998,7 @@ did = ?, queue = %s, due = ?, mod = ?, usn = ? where id = ?""" % queue, data)
leechAction=oconf['lapse']['leechAction'], leechAction=oconf['lapse']['leechAction'],
mult=oconf['lapse']['mult'], mult=oconf['lapse']['mult'],
# overrides # overrides
delays=conf['delays'], delays=delays,
resched=conf['resched'], resched=conf['resched'],
) )

View file

@ -527,6 +527,12 @@ def test_cram():
assert d.sched.counts() == (1,0,0) assert d.sched.counts() == (1,0,0)
# grab it and check estimates # grab it and check estimates
c = d.sched.getCard() 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, 1) == 60
assert d.sched.nextIvl(c, 2) == 600 assert d.sched.nextIvl(c, 2) == 600
assert d.sched.nextIvl(c, 3) == 138*60*60*24 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.type == c.queue == 1
assert c.due != oldDue assert c.due != oldDue
# if we terminate cramming prematurely it should be set back to new # if we terminate cramming prematurely it should be set back to new
d.sched.remDyn(did) d.sched.emptyDyn(did)
c.load() c.load()
assert c.type == c.queue == 0 assert c.type == c.queue == 0
assert c.due == oldDue assert c.due == oldDue

4
thirdparty/README vendored
View file

@ -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.