mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
preserve learning/filtered state when suspending/burying
add new card type of 3 so we can distinguish cards in relearning from normal reviews
This commit is contained in:
parent
ba87fc7736
commit
62c1fa4a17
2 changed files with 66 additions and 80 deletions
109
anki/sched.py
109
anki/sched.py
|
@ -14,7 +14,9 @@ from anki.lang import _
|
|||
from anki.consts import *
|
||||
from anki.hooks import runHook
|
||||
|
||||
# queue types: 0=new/cram, 1=lrn, 2=rev, 3=day lrn, -1=suspended, -2=buried
|
||||
# card types: 0=new, 1=lrn, 2=rev, 3=relrn
|
||||
# queue types: 0=new, 1=(re)lrn, 2=rev, 3=day (re)lrn,
|
||||
# 4=preview, -1=suspended, -2=buried
|
||||
# revlog types: 0=lrn, 1=rev, 2=relrn, 3=cram
|
||||
# positive revlog intervals are in days (rev), negative in seconds (lrn)
|
||||
# odue/odid store original due/did when cards moved to filtered deck
|
||||
|
@ -147,23 +149,6 @@ order by due""" % self._deckLimit(),
|
|||
return 2
|
||||
return 4
|
||||
|
||||
def unburyCards(self):
|
||||
"Unbury cards."
|
||||
self.col.conf['lastUnburied'] = self.today
|
||||
self.col.log(
|
||||
self.col.db.list("select id from cards where queue = -2"))
|
||||
self.col.db.execute(
|
||||
"update cards set queue=type where queue = -2")
|
||||
|
||||
def unburyCardsForDeck(self):
|
||||
sids = ids2str(self.col.decks.active())
|
||||
self.col.log(
|
||||
self.col.db.list("select id from cards where queue = -2 and did in %s"
|
||||
% sids))
|
||||
self.col.db.execute(
|
||||
"update cards set mod=?,usn=?,queue=type where queue = -2 and did in %s"
|
||||
% sids, intTime(), self.col.usn())
|
||||
|
||||
# Rev/lrn/time daily stats
|
||||
##########################################################################
|
||||
|
||||
|
@ -562,19 +547,17 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
|||
card.left = self._startingLeft(card)
|
||||
|
||||
card.lastIvl = card.ivl
|
||||
if card.type == 2:
|
||||
# review card that will move to relearning
|
||||
|
||||
# relearning card?
|
||||
if card.type in (2,3):
|
||||
card.ivl = self._lapseIvl(card, self._lrnConf(card))
|
||||
|
||||
if card.queue not in (1,3):
|
||||
# moving from review queue?
|
||||
if card.type == 2:
|
||||
card.type = 3
|
||||
card.lapses += 1
|
||||
card.factor = max(1300, card.factor-200)
|
||||
|
||||
# if no relearning steps, reschedule as review immediately
|
||||
if not conf['delays']:
|
||||
self._rescheduleAsRev(card, conf, False)
|
||||
return
|
||||
|
||||
return self._rescheduleLrnCard(card, conf)
|
||||
|
||||
def _moveToNextStep(self, card, conf):
|
||||
|
@ -634,13 +617,13 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
|||
return avg
|
||||
|
||||
def _lrnConf(self, card):
|
||||
if card.type == 2:
|
||||
if card.type in (2, 3):
|
||||
return self._lapseConf(card)
|
||||
else:
|
||||
return self._newConf(card)
|
||||
|
||||
def _rescheduleAsRev(self, card, conf, early):
|
||||
lapse = card.type == 2
|
||||
lapse = card.type in (2,3)
|
||||
|
||||
if lapse:
|
||||
self._rescheduleGraduatingLapse(card)
|
||||
|
@ -654,6 +637,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
|||
def _rescheduleGraduatingLapse(self, card):
|
||||
card.due = self.today+card.ivl
|
||||
card.queue = 2
|
||||
card.type = 2
|
||||
|
||||
def _startingLeft(self, card):
|
||||
if card.type == 2:
|
||||
|
@ -712,26 +696,6 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
|||
time.sleep(0.01)
|
||||
log()
|
||||
|
||||
def removeLrn(self, ids=None):
|
||||
"Remove cards from the learning queues."
|
||||
if ids:
|
||||
extra = " and id in "+ids2str(ids)
|
||||
else:
|
||||
# benchmarks indicate it's about 10x faster to search all decks
|
||||
# with the index than scan the table
|
||||
extra = " and did in "+ids2str(self.col.decks.allIds())
|
||||
# review cards in relearning
|
||||
self.col.db.execute("""
|
||||
update cards set
|
||||
due = ivl+?, queue = 2, mod = %d, usn = %d,
|
||||
odue = 0 -- older anki versions set odue when cards in relearning
|
||||
where queue in (1,3) and type = 2
|
||||
%s
|
||||
""" % (intTime(), self.col.usn(), extra), self.today)
|
||||
# new cards in learning
|
||||
self.forgetCards(self.col.db.list(
|
||||
"select id from cards where queue in (1,3) %s" % extra))
|
||||
|
||||
def _lrnForDeck(self, did):
|
||||
cnt = self.col.db.scalar(
|
||||
"""
|
||||
|
@ -846,7 +810,11 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
|||
|
||||
def _rescheduleLapse(self, card):
|
||||
conf = self._lapseConf(card)
|
||||
delay = self._moveToFirstStep(card, conf)
|
||||
if conf['delays']:
|
||||
delay = self._moveToFirstStep(card, conf)
|
||||
else:
|
||||
# no relearning steps
|
||||
self._rescheduleAsRev(card, conf, early=False)
|
||||
|
||||
# if suspended as a leech, nothing to do
|
||||
if self._checkLeech(card, conf) and card.queue == -1:
|
||||
|
@ -1033,9 +1001,11 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
|||
lim = "did = %s" % did
|
||||
self.col.log(self.col.db.list("select id from cards where %s" % lim))
|
||||
|
||||
# update queue in preview case
|
||||
self.col.db.execute("""
|
||||
update cards set did = odid,
|
||||
due = odue, odue = 0, odid = 0, usn = ? where %s""" % lim,
|
||||
update cards set did = odid, %s,
|
||||
due = odue, odue = 0, odid = 0, usn = ? where %s""" % (
|
||||
self._restoreQueueSnippet, lim),
|
||||
self.col.usn())
|
||||
|
||||
def remFromDyn(self, cids):
|
||||
|
@ -1346,14 +1316,22 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
|||
else:
|
||||
return self._delayForGrade(conf, left)
|
||||
|
||||
# Suspending
|
||||
# Suspending & burying
|
||||
##########################################################################
|
||||
|
||||
# learning and relearning cards may be seconds-based or day-based;
|
||||
# other types map directly to queues
|
||||
_restoreQueueSnippet = """
|
||||
queue = (case when type in (1,3) then
|
||||
(case when (case when odue then odue else due end) > 1000000000 then 1 else 3 end)
|
||||
else
|
||||
type
|
||||
end)
|
||||
"""
|
||||
|
||||
def suspendCards(self, ids):
|
||||
"Suspend cards."
|
||||
self.col.log(ids)
|
||||
self.remFromDyn(ids)
|
||||
self.removeLrn(ids)
|
||||
self.col.db.execute(
|
||||
"update cards set queue=-1,mod=?,usn=? where id in "+
|
||||
ids2str(ids), intTime(), self.col.usn())
|
||||
|
@ -1362,14 +1340,12 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
|||
"Unsuspend cards."
|
||||
self.col.log(ids)
|
||||
self.col.db.execute(
|
||||
"update cards set queue=type,mod=?,usn=? "
|
||||
"where queue = -1 and id in "+ ids2str(ids),
|
||||
("update cards set %s,mod=?,usn=? "
|
||||
"where queue = -1 and id in %s") % (self._restoreQueueSnippet, ids2str(ids)),
|
||||
intTime(), self.col.usn())
|
||||
|
||||
def buryCards(self, cids):
|
||||
self.col.log(cids)
|
||||
self.remFromDyn(cids)
|
||||
self.removeLrn(cids)
|
||||
self.col.db.execute("""
|
||||
update cards set queue=-2,mod=?,usn=? where id in """+ids2str(cids),
|
||||
intTime(), self.col.usn())
|
||||
|
@ -1380,6 +1356,23 @@ update cards set queue=-2,mod=?,usn=? where id in """+ids2str(cids),
|
|||
"select id from cards where nid = ? and queue >= 0", nid)
|
||||
self.buryCards(cids)
|
||||
|
||||
def unburyCards(self):
|
||||
"Unbury cards."
|
||||
self.col.conf['lastUnburied'] = self.today
|
||||
self.col.log(
|
||||
self.col.db.list("select id from cards where queue = -2"))
|
||||
self.col.db.execute(
|
||||
"update cards set %s where queue = -2" % self._restoreQueueSnippet)
|
||||
|
||||
def unburyCardsForDeck(self):
|
||||
sids = ids2str(self.col.decks.active())
|
||||
self.col.log(
|
||||
self.col.db.list("select id from cards where queue = -2 and did in %s"
|
||||
% sids))
|
||||
self.col.db.execute(
|
||||
"update cards set mod=?,usn=?,%s where queue = -2 and did in %s"
|
||||
% (self._restoreQueueSnippet, sids), intTime(), self.col.usn())
|
||||
|
||||
# Sibling spacing
|
||||
##########################################################################
|
||||
|
||||
|
|
|
@ -182,7 +182,7 @@ def test_relearn():
|
|||
c = d.sched.getCard()
|
||||
d.sched.answerCard(c, 1)
|
||||
assert c.queue == 1
|
||||
assert c.type == 2
|
||||
assert c.type == 3
|
||||
assert c.ivl == 1
|
||||
|
||||
# immediately graduate it
|
||||
|
@ -191,17 +191,6 @@ def test_relearn():
|
|||
assert c.ivl == 1
|
||||
assert c.due == d.sched.today + c.ivl
|
||||
|
||||
# if forced out of learning, it should have the correct due date
|
||||
c.ivl = 100
|
||||
c.due = d.sched.today
|
||||
c.flush()
|
||||
c = d.sched.getCard()
|
||||
d.sched.answerCard(c, 1)
|
||||
assert c.due > intTime()
|
||||
d.sched.removeLrn([c.id])
|
||||
c.load()
|
||||
assert c.due == d.sched.today + c.ivl
|
||||
|
||||
def test_learn_collapsed():
|
||||
d = getEmptyCol()
|
||||
# add 2 notes
|
||||
|
@ -525,14 +514,15 @@ def test_suspend():
|
|||
c = d.sched.getCard()
|
||||
d.sched.answerCard(c, 1)
|
||||
assert c.due >= time.time()
|
||||
due = c.due
|
||||
assert c.queue == 1
|
||||
assert c.type == 2
|
||||
assert c.type == 3
|
||||
d.sched.suspendCards([c.id])
|
||||
d.sched.unsuspendCards([c.id])
|
||||
c.load()
|
||||
assert c.queue == 2
|
||||
assert c.type == 2
|
||||
assert c.due == 1
|
||||
assert c.queue == 1
|
||||
assert c.type == 3
|
||||
assert c.due == due
|
||||
# should cope with cards in cram decks
|
||||
c.due = 1
|
||||
c.flush()
|
||||
|
@ -543,8 +533,9 @@ def test_suspend():
|
|||
assert c.did != 1
|
||||
d.sched.suspendCards([c.id])
|
||||
c.load()
|
||||
assert c.due == 1
|
||||
assert c.did == 1
|
||||
assert c.due != 1
|
||||
assert c.did != 1
|
||||
assert c.odue == 1
|
||||
|
||||
def test_filt_reviewing_early_normal():
|
||||
d = getEmptyCol()
|
||||
|
@ -660,15 +651,17 @@ def test_preview():
|
|||
|
||||
# passing it will remove it
|
||||
d.sched.answerCard(c2, 2)
|
||||
assert c2.queue == 0
|
||||
assert c2.reps == 0
|
||||
assert c2.type == 0
|
||||
|
||||
# the other card should appear again
|
||||
c = d.sched.getCard()
|
||||
assert c.id == orig.id
|
||||
|
||||
# remove it
|
||||
d.sched.answerCard(c, 2)
|
||||
|
||||
# ensure it's in the same state as it started
|
||||
# emptying the filtered deck should restore card
|
||||
d.sched.emptyDyn(did)
|
||||
c.load()
|
||||
assert c.queue == 0
|
||||
assert c.reps == 0
|
||||
assert c.type == 0
|
||||
|
|
Loading…
Reference in a new issue