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_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"),
}

View file

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

View file

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

View file

@ -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'],
)

View file

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

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.