mirror of
https://github.com/ankitects/anki.git
synced 2025-11-09 14:17:13 -05:00
generalize into 'dynamic decks'
- search and limits are embedded in the deck - decks can be refreshed - they have the option to treat due reviews normally rather than cram them - some options are inherited from the original deck, others taken from the dynamic deck
This commit is contained in:
parent
f6b2e69669
commit
da07e15a87
4 changed files with 140 additions and 76 deletions
|
|
@ -128,9 +128,6 @@ lapses=?, left=?, odue=?, did=? where id = ?""",
|
||||||
def model(self):
|
def model(self):
|
||||||
return self.col.models.get(self.note().mid)
|
return self.col.models.get(self.note().mid)
|
||||||
|
|
||||||
def deckConf(self):
|
|
||||||
return self.col.decks.confForDid(self.did)
|
|
||||||
|
|
||||||
def template(self):
|
def template(self):
|
||||||
return self.model()['tmpls'][self.ord]
|
return self.model()['tmpls'][self.ord]
|
||||||
|
|
||||||
|
|
@ -140,4 +137,5 @@ lapses=?, left=?, odue=?, did=? where id = ?""",
|
||||||
def timeTaken(self):
|
def timeTaken(self):
|
||||||
"Time taken to answer card, in integer MS."
|
"Time taken to answer card, in integer MS."
|
||||||
total = int((time.time() - self.timerStarted)*1000)
|
total = int((time.time() - self.timerStarted)*1000)
|
||||||
return min(total, self.deckConf()['maxTaken']*1000)
|
conf = self.col.decks.confForDid(self.odid or self.did)
|
||||||
|
return min(total, conf['maxTaken']*1000)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,24 @@ defaultDeck = {
|
||||||
'conf': 1,
|
'conf': 1,
|
||||||
'usn': 0,
|
'usn': 0,
|
||||||
'desc': "",
|
'desc': "",
|
||||||
|
'dyn': 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultDynamicDeck = {
|
||||||
|
'newToday': [0, 0],
|
||||||
|
'revToday': [0, 0],
|
||||||
|
'lrnToday': [0, 0],
|
||||||
|
'timeToday': [0, 0],
|
||||||
|
'dyn': 1,
|
||||||
|
'desc': "",
|
||||||
|
'usn': 0,
|
||||||
|
'delays': [1, 10],
|
||||||
|
'separate': True,
|
||||||
|
'fmult': 0,
|
||||||
|
'cramRev': True,
|
||||||
|
'search': "",
|
||||||
|
'limit': 100,
|
||||||
|
'order': 'oldestSeen',
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConf = {
|
defaultConf = {
|
||||||
|
|
@ -39,13 +57,6 @@ defaultConf = {
|
||||||
# type 0=suspend, 1=tagonly
|
# type 0=suspend, 1=tagonly
|
||||||
'leechAction': 0,
|
'leechAction': 0,
|
||||||
},
|
},
|
||||||
'cram': {
|
|
||||||
'delays': [1, 5, 10],
|
|
||||||
'resched': True,
|
|
||||||
'reset': True,
|
|
||||||
'mult': 0,
|
|
||||||
'minInt': 1,
|
|
||||||
},
|
|
||||||
'rev': {
|
'rev': {
|
||||||
'perDay': 100,
|
'perDay': 100,
|
||||||
'ease4': 1.3,
|
'ease4': 1.3,
|
||||||
|
|
@ -91,7 +102,7 @@ class DeckManager(object):
|
||||||
# Deck save/load
|
# Deck save/load
|
||||||
#############################################################
|
#############################################################
|
||||||
|
|
||||||
def id(self, name, create=True):
|
def id(self, name, create=True, type=defaultDeck):
|
||||||
"Add a deck with NAME. Reuse deck if already exists. Return id as int."
|
"Add a deck with NAME. Reuse deck if already exists. Return id as int."
|
||||||
name = name.replace("'", "").replace('"', '')
|
name = name.replace("'", "").replace('"', '')
|
||||||
for id, g in self.decks.items():
|
for id, g in self.decks.items():
|
||||||
|
|
@ -99,7 +110,7 @@ class DeckManager(object):
|
||||||
return int(id)
|
return int(id)
|
||||||
if not create:
|
if not create:
|
||||||
return None
|
return None
|
||||||
g = copy.deepcopy(defaultDeck)
|
g = copy.deepcopy(type)
|
||||||
if "::" in name:
|
if "::" in name:
|
||||||
# not top level; ensure all parents exist
|
# not top level; ensure all parents exist
|
||||||
self._ensureParents(name)
|
self._ensureParents(name)
|
||||||
|
|
@ -123,10 +134,11 @@ class DeckManager(object):
|
||||||
if not str(did) in self.decks:
|
if not str(did) in self.decks:
|
||||||
return
|
return
|
||||||
deck = self.get(did)
|
deck = self.get(did)
|
||||||
if deck.get('cram'):
|
print "fixme: add dyn to old decks"
|
||||||
|
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.remCram(did)
|
self.col.sched.remDyn(did)
|
||||||
else:
|
else:
|
||||||
# delete children first
|
# delete children first
|
||||||
if childrenToo:
|
if childrenToo:
|
||||||
|
|
@ -233,7 +245,13 @@ class DeckManager(object):
|
||||||
return self.dconf.values()
|
return self.dconf.values()
|
||||||
|
|
||||||
def confForDid(self, did):
|
def confForDid(self, did):
|
||||||
return self.getConf(self.get(did)['conf'])
|
deck = self.get(did)
|
||||||
|
if 'conf' in deck:
|
||||||
|
conf = self.getConf(deck['conf'])
|
||||||
|
conf['dyn'] = False
|
||||||
|
return conf
|
||||||
|
# dynamic decks have embedded conf
|
||||||
|
return deck
|
||||||
|
|
||||||
def getConf(self, confId):
|
def getConf(self, confId):
|
||||||
return self.dconf[str(confId)]
|
return self.dconf[str(confId)]
|
||||||
|
|
@ -370,3 +388,10 @@ class DeckManager(object):
|
||||||
for c in self.allConf():
|
for c in self.allConf():
|
||||||
c['usn'] = 0
|
c['usn'] = 0
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
# Dynamic decks
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
def newDyn(self, name):
|
||||||
|
"Return a new dynamic deck and set it as the current deck."
|
||||||
|
return self.id(name, type=defaultDynamicDeck)
|
||||||
|
|
|
||||||
155
anki/sched.py
155
anki/sched.py
|
|
@ -28,7 +28,6 @@ class Scheduler(object):
|
||||||
self.reportLimit = 1000
|
self.reportLimit = 1000
|
||||||
# fixme: replace reps with deck based counts
|
# fixme: replace reps with deck based counts
|
||||||
self.reps = 0
|
self.reps = 0
|
||||||
self._cramming = False
|
|
||||||
self._updateCutoff()
|
self._updateCutoff()
|
||||||
|
|
||||||
def getCard(self):
|
def getCard(self):
|
||||||
|
|
@ -41,7 +40,6 @@ class Scheduler(object):
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
deck = self.col.decks.current()
|
deck = self.col.decks.current()
|
||||||
self._cramming = deck.get('cram')
|
|
||||||
self._updateCutoff()
|
self._updateCutoff()
|
||||||
self._resetLrn()
|
self._resetLrn()
|
||||||
self._resetRev()
|
self._resetRev()
|
||||||
|
|
@ -61,10 +59,10 @@ class Scheduler(object):
|
||||||
card.type = 1
|
card.type = 1
|
||||||
# init reps to graduation
|
# init reps to graduation
|
||||||
card.left = self._startingLeft(card)
|
card.left = self._startingLeft(card)
|
||||||
# cramming?
|
# dynamic?
|
||||||
if self._cramming and card.type == 2:
|
if card.odid and card.type == 2:
|
||||||
# reviews get their ivl boosted on first sight
|
# reviews get their ivl boosted on first sight
|
||||||
card.ivl = self._cramIvlBoost(card)
|
card.ivl = self._dynIvlBoost(card)
|
||||||
card.odue = self.today + card.ivl
|
card.odue = self.today + card.ivl
|
||||||
self._updateStats(card, 'new')
|
self._updateStats(card, 'new')
|
||||||
if card.queue == 1:
|
if card.queue == 1:
|
||||||
|
|
@ -306,7 +304,7 @@ select id, due from cards where did = ? and queue = 0 limit ?""", did, lim)
|
||||||
(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.confForDid(self._newDids[0])
|
conf = self.col.decks.confForDid(self._newDids[0])
|
||||||
if conf['new']['separate']:
|
if conf['dyn'] or conf['new']['separate']:
|
||||||
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:
|
||||||
self._newQueue.insert(0, self._newQueue.pop())
|
self._newQueue.insert(0, self._newQueue.pop())
|
||||||
|
|
@ -332,8 +330,6 @@ select id, due from cards where did = ? and queue = 0 limit ?""", did, lim)
|
||||||
"True if it's time to display a new card when distributing."
|
"True if it's time to display a new card when distributing."
|
||||||
if not self.newCount:
|
if not self.newCount:
|
||||||
return False
|
return False
|
||||||
if self._cramming:
|
|
||||||
return True
|
|
||||||
if self.col.conf['newSpread'] == NEW_CARDS_LAST:
|
if self.col.conf['newSpread'] == NEW_CARDS_LAST:
|
||||||
return False
|
return False
|
||||||
elif self.col.conf['newSpread'] == NEW_CARDS_FIRST:
|
elif self.col.conf['newSpread'] == NEW_CARDS_FIRST:
|
||||||
|
|
@ -366,7 +362,7 @@ select count() from
|
||||||
|
|
||||||
def _deckNewLimitSingle(self, g):
|
def _deckNewLimitSingle(self, g):
|
||||||
"Limit for deck without parent limits."
|
"Limit for deck without parent limits."
|
||||||
if self._cramming:
|
if g['dyn']:
|
||||||
return self.reportLimit
|
return self.reportLimit
|
||||||
c = self.col.decks.confForDid(g['id'])
|
c = self.col.decks.confForDid(g['id'])
|
||||||
return max(0, c['new']['perDay'] - g['newToday'][1])
|
return max(0, c['new']['perDay'] - g['newToday'][1])
|
||||||
|
|
@ -434,7 +430,7 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff)
|
||||||
# failed
|
# failed
|
||||||
else:
|
else:
|
||||||
card.left = self._startingLeft(card)
|
card.left = self._startingLeft(card)
|
||||||
if self._cramming:
|
if card.odid:
|
||||||
print "fixme: configurable failure handling"
|
print "fixme: configurable failure handling"
|
||||||
card.ivl = 1
|
card.ivl = 1
|
||||||
card.odue = self.today + 1
|
card.odue = self.today + 1
|
||||||
|
|
@ -463,9 +459,9 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff)
|
||||||
def _lrnConf(self, card):
|
def _lrnConf(self, card):
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
if card.type == 2:
|
if card.type == 2:
|
||||||
return conf['lapse']
|
return self._lapseConf(card)
|
||||||
else:
|
else:
|
||||||
return conf['new']
|
return self._newConf(card)
|
||||||
|
|
||||||
def _rescheduleAsRev(self, card, conf, early):
|
def _rescheduleAsRev(self, card, conf, early):
|
||||||
if card.type == 2:
|
if card.type == 2:
|
||||||
|
|
@ -474,20 +470,21 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff)
|
||||||
self._rescheduleNew(card, conf, early)
|
self._rescheduleNew(card, conf, early)
|
||||||
card.queue = 2
|
card.queue = 2
|
||||||
card.type = 2
|
card.type = 2
|
||||||
# if we were cramming, graduating means moving back to the old deck
|
# if we were dynamic, graduating means moving back to the old deck
|
||||||
if self._cramming:
|
if card.odid:
|
||||||
card.did = card.odid
|
card.did = card.odid
|
||||||
card.odue = 0
|
card.odue = 0
|
||||||
card.odid = 0
|
card.odid = 0
|
||||||
|
|
||||||
def _startingLeft(self, card):
|
def _startingLeft(self, card):
|
||||||
return len(self._cardConf(card)['new']['delays'])
|
conf = self._newConf(card)
|
||||||
|
return len(conf['delays'])
|
||||||
|
|
||||||
def _graduatingIvl(self, card, conf, early, adj=True):
|
def _graduatingIvl(self, card, conf, early, adj=True):
|
||||||
if card.type == 2:
|
if card.type == 2:
|
||||||
# lapsed card being relearnt
|
# lapsed card being relearnt
|
||||||
if self._cramming:
|
if card.odid:
|
||||||
return self._cramIvlBoost(card)
|
return self._dynIvlBoost(card)
|
||||||
return card.ivl
|
return card.ivl
|
||||||
if not early:
|
if not early:
|
||||||
# graduate
|
# graduate
|
||||||
|
|
@ -547,6 +544,8 @@ select sum(left) from
|
||||||
return self._deckNewLimit(did, self._deckRevLimitSingle)
|
return self._deckNewLimit(did, self._deckRevLimitSingle)
|
||||||
|
|
||||||
def _deckRevLimitSingle(self, d):
|
def _deckRevLimitSingle(self, d):
|
||||||
|
if d['dyn']:
|
||||||
|
return self.reportLimit
|
||||||
c = self.col.decks.confForDid(d['id'])
|
c = self.col.decks.confForDid(d['id'])
|
||||||
return max(0, c['rev']['perDay'] - d['revToday'][1])
|
return max(0, c['rev']['perDay'] - d['revToday'][1])
|
||||||
|
|
||||||
|
|
@ -606,6 +605,8 @@ did = ? and queue = 2 and due <= ? %s limit ?""" % order,
|
||||||
|
|
||||||
def _revOrder(self, did):
|
def _revOrder(self, did):
|
||||||
d = self.col.decks.confForDid(did)
|
d = self.col.decks.confForDid(did)
|
||||||
|
if d['dyn']:
|
||||||
|
return ""
|
||||||
o = d['rev']['order']
|
o = d['rev']['order']
|
||||||
if o:
|
if o:
|
||||||
return "order by %s" % ("ivl desc", "ivl")[o-1]
|
return "order by %s" % ("ivl desc", "ivl")[o-1]
|
||||||
|
|
@ -622,7 +623,7 @@ did = ? and queue = 2 and due <= ? %s limit ?""" % order,
|
||||||
self._logRev(card, ease)
|
self._logRev(card, ease)
|
||||||
|
|
||||||
def _rescheduleLapse(self, card):
|
def _rescheduleLapse(self, card):
|
||||||
conf = self._cardConf(card)['lapse']
|
conf = self._lapseConf(card)
|
||||||
card.lapses += 1
|
card.lapses += 1
|
||||||
card.lastIvl = card.ivl
|
card.lastIvl = card.ivl
|
||||||
card.ivl = self._nextLapseIvl(card, conf)
|
card.ivl = self._nextLapseIvl(card, conf)
|
||||||
|
|
@ -722,64 +723,66 @@ did = ? and queue = 2 and due <= ? %s limit ?""" % order,
|
||||||
break
|
break
|
||||||
return idealIvl + fudge
|
return idealIvl + fudge
|
||||||
|
|
||||||
# Creation of cramming decks
|
# Dynamic deck handling
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
# type is one of all, due, rev
|
def rebuildDyn(self, did=None):
|
||||||
# order is one of due, added, random, relative, lapses
|
"Rebuild a dynamic deck."
|
||||||
def cram(self, search, type="all", order="due",
|
did = did or self.col.decks.selected()
|
||||||
limit=100, sched=[1, 10]):
|
deck = self.col.decks.get(did)
|
||||||
|
assert deck['dyn']
|
||||||
# gather card ids and sort
|
# gather card ids and sort
|
||||||
order = self._cramOrder(order)
|
order = self._dynOrder(deck)
|
||||||
limit = " limit %d" % limit
|
limit = " limit %d" % deck['limit']
|
||||||
ids = self.col.findCards(search, order=order+limit)
|
ids = self.col.findCards(deck['search'], order=order+limit)
|
||||||
if not order:
|
if deck['order'] == "random":
|
||||||
random.shuffle(ids)
|
random.shuffle(ids)
|
||||||
# get a cram deck
|
|
||||||
did = self._cramDeck()
|
|
||||||
# move the cards over
|
# move the cards over
|
||||||
self._moveToCram(did, ids)
|
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)
|
||||||
|
|
||||||
def remCram(self, did):
|
def remDyn(self, did):
|
||||||
self.col.db.execute("""
|
self.col.db.execute("""
|
||||||
update cards set did = odid, queue = type, due = odue, odue = 0, odid = 0,
|
update cards set did = odid, queue = type, due = odue, odue = 0, odid = 0,
|
||||||
usn = ?, mod = ? where did = ?""", self.col.usn(), intTime(), did)
|
usn = ?, mod = ? where did = ?""", self.col.usn(), intTime(), did)
|
||||||
|
|
||||||
def _cramOrder(self, order):
|
def _dynOrder(self, deck):
|
||||||
if order == "due":
|
o = deck['order']
|
||||||
return "order by c.due"
|
if o == "oldestSeen":
|
||||||
elif order == "added":
|
return "order by c.mod"
|
||||||
return "order by n.id"
|
# elif o == "added":
|
||||||
elif order == "random":
|
# return "order by n.id"
|
||||||
return ""
|
# elif o == "random":
|
||||||
elif order == "relative":
|
# return ""
|
||||||
pass
|
# elif o == "relative":
|
||||||
elif order == "lapses":
|
# pass
|
||||||
return "order by lapses desc"
|
# elif o == "lapses":
|
||||||
elif order == "failed":
|
# return "order by lapses desc"
|
||||||
pass
|
# elif o == "failed":
|
||||||
|
# pass
|
||||||
|
|
||||||
def _cramDeck(self):
|
def _moveToDyn(self, did, ids):
|
||||||
did = self.col.decks.id(_("Cram"))
|
|
||||||
deck = self.col.decks.get(did)
|
deck = self.col.decks.get(did)
|
||||||
# mark it as a cram deck
|
|
||||||
deck['cram'] = True
|
|
||||||
self.col.decks.save(deck)
|
|
||||||
return did
|
|
||||||
|
|
||||||
def _moveToCram(self, did, ids):
|
|
||||||
data = []
|
data = []
|
||||||
t = intTime(); u = self.col.usn()
|
t = intTime(); u = self.col.usn()
|
||||||
for c, id in enumerate(ids):
|
for c, id in enumerate(ids):
|
||||||
data.append((did, c, t, u, id))
|
data.append((did, c, t, u, id))
|
||||||
|
if deck['cramRev']:
|
||||||
|
# everything in the new queue
|
||||||
|
queue = "0"
|
||||||
|
else:
|
||||||
|
# due reviews stay in the review queue
|
||||||
|
queue = "(case when queue=2 and due <= %d then 2 else 0 end)"
|
||||||
|
queue %= self.today
|
||||||
self.col.db.executemany("""
|
self.col.db.executemany("""
|
||||||
update cards set odid = did, odue = due, did = ?, queue = 0, due = ?,
|
update cards set
|
||||||
mod = ?, usn = ? where id = ?""", data)
|
odid = (case when odid then odid else did end),
|
||||||
|
odue = (case when odue then odue else due end),
|
||||||
|
did = ?, queue = %s, due = ?, mod = ?, usn = ? where id = ?""" % queue, data)
|
||||||
|
|
||||||
def _cramIvlBoost(self, card):
|
def _dynIvlBoost(self, card):
|
||||||
assert self._cramming and card.type == 2
|
assert card.odid and card.type == 2
|
||||||
assert card.factor
|
assert card.factor
|
||||||
elapsed = card.ivl - card.odue - self.today
|
elapsed = card.ivl - card.odue - self.today
|
||||||
factor = ((card.factor/1000.0)+1.2)/2.0
|
factor = ((card.factor/1000.0)+1.2)/2.0
|
||||||
|
|
@ -815,6 +818,42 @@ mod = ?, usn = ? where id = ?""", data)
|
||||||
def _cardConf(self, card):
|
def _cardConf(self, card):
|
||||||
return self.col.decks.confForDid(card.did)
|
return self.col.decks.confForDid(card.did)
|
||||||
|
|
||||||
|
def _newConf(self, card):
|
||||||
|
conf = self._cardConf(card)
|
||||||
|
# normal deck
|
||||||
|
if not card.odid:
|
||||||
|
return conf['new']
|
||||||
|
# dynamic deck; override some attributes, use original deck for others
|
||||||
|
oconf = self.col.decks.confForDid(card.odid)
|
||||||
|
return dict(
|
||||||
|
# original deck
|
||||||
|
ints=oconf['new']['ints'],
|
||||||
|
initialFactor=oconf['new']['initialFactor'],
|
||||||
|
# overrides
|
||||||
|
delays=conf['delays'],
|
||||||
|
separate=conf['separate'],
|
||||||
|
order=NEW_CARDS_DUE,
|
||||||
|
perDay=self.reportLimit
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _lapseConf(self, card):
|
||||||
|
conf = self._cardConf(card)
|
||||||
|
# normal deck
|
||||||
|
if not card.odid:
|
||||||
|
return conf['lapse']
|
||||||
|
# dynamic deck; override some attributes, use original deck for others
|
||||||
|
oconf = self.col.decks.confForDid(card.odid)
|
||||||
|
return dict(
|
||||||
|
# original deck
|
||||||
|
minInt=oconf['lapse']['minInt'],
|
||||||
|
leechFails=oconf['lapse']['leechFails'],
|
||||||
|
leechAction=oconf['lapse']['leechAction'],
|
||||||
|
# overrides
|
||||||
|
delays=conf['delays'],
|
||||||
|
mult=conf['fmult'],
|
||||||
|
)
|
||||||
|
|
||||||
def _deckLimit(self):
|
def _deckLimit(self):
|
||||||
return ids2str(self.col.decks.active())
|
return ids2str(self.col.decks.active())
|
||||||
|
|
||||||
|
|
@ -897,7 +936,7 @@ your short-term review workload will become."""))
|
||||||
return self._nextLrnIvl(card, ease)
|
return self._nextLrnIvl(card, ease)
|
||||||
elif ease == 1:
|
elif ease == 1:
|
||||||
# lapsed
|
# lapsed
|
||||||
conf = self._cardConf(card)['lapse']
|
conf = self._lapseConf(card)
|
||||||
if conf['delays']:
|
if conf['delays']:
|
||||||
return conf['delays'][0]*60
|
return conf['delays'][0]*60
|
||||||
return self._nextLapseIvl(card, conf)*86400
|
return self._nextLapseIvl(card, conf)*86400
|
||||||
|
|
@ -953,6 +992,7 @@ your short-term review workload will become."""))
|
||||||
|
|
||||||
def forgetCards(self, ids):
|
def forgetCards(self, ids):
|
||||||
"Put cards at the end of the new queue."
|
"Put cards at the end of the new queue."
|
||||||
|
print "fixme: make sure this works with dynamic decks, and mv too"
|
||||||
self.col.db.execute(
|
self.col.db.execute(
|
||||||
"update cards set type=0,queue=0,ivl=0 where id in "+ids2str(ids))
|
"update cards set type=0,queue=0,ivl=0 where id in "+ids2str(ids))
|
||||||
pmax = self.col.db.scalar(
|
pmax = self.col.db.scalar(
|
||||||
|
|
@ -1024,4 +1064,3 @@ and due >= ?""" % scids, now, self.col.usn(), shiftby, low)
|
||||||
self.randomizeCards(did)
|
self.randomizeCards(did)
|
||||||
else:
|
else:
|
||||||
self.orderCards(did)
|
self.orderCards(did)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -447,7 +447,9 @@ def test_cram():
|
||||||
d.reset()
|
d.reset()
|
||||||
assert d.sched.counts() == (0,0,0)
|
assert d.sched.counts() == (0,0,0)
|
||||||
cardcopy = copy.copy(c)
|
cardcopy = copy.copy(c)
|
||||||
d.sched.cram("")
|
# create a dynamic deck and refresh it
|
||||||
|
did = d.decks.newDyn("Cram")
|
||||||
|
d.sched.rebuildDyn(did)
|
||||||
d.reset()
|
d.reset()
|
||||||
# should appear as new in the deck list
|
# should appear as new in the deck list
|
||||||
assert sorted(d.sched.deckDueList())[0][3] == 1
|
assert sorted(d.sched.deckDueList())[0][3] == 1
|
||||||
|
|
@ -478,7 +480,7 @@ def test_cram():
|
||||||
# and it will have moved back to the previous deck
|
# and it will have moved back to the previous deck
|
||||||
assert c.did == 1
|
assert c.did == 1
|
||||||
# cram the deck again
|
# cram the deck again
|
||||||
d.sched.cram("")
|
d.sched.rebuildDyn(did)
|
||||||
d.reset()
|
d.reset()
|
||||||
c = d.sched.getCard()
|
c = d.sched.getCard()
|
||||||
# check ivls again - passing should be idempotent
|
# check ivls again - passing should be idempotent
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue