change failed card handling

The old delay1 behaviour isn't easy to achieve with the queue code, as we only
refresh the queue when it's emptied, and if the user has delay1 set to say 9
hours, failed mature cards sitting in the queue could prevent subsequent young
failures from being displayed. Instead we convert delay1 to a count in days in
which to offset failed mature cards. 0 means the same time as delay0, 1 means
show the card a day later, and so on. This means users will lose the ability
to delay mature cards for x number of minutes more than young cards, but a
scan of AnkiOnline decks indicates that's not often done.

We also need to use a separate cutoff for failed cards, since we need to be
able to display them as they expire if the user has disabled per-day
scheduling.

And instead of marking cards as due in the future, we set their due time to
the current time, and move the delay0 calculation to getCardId(). This means
that if the user changes their failed card settings from say 1 hour to 10
minutes, the changes apply to the currently failed cards and not just cards
failed in the future.
This commit is contained in:
Damien Elmes 2010-11-13 21:15:41 +09:00
parent b69fd48768
commit 7502fba5ce

View file

@ -98,7 +98,8 @@ decksTable = Table(
Column('easyIntervalMax', Float, nullable=False, default=9.0),
# delays on failure
Column('delay0', Integer, nullable=False, default=600),
Column('delay1', Integer, nullable=False, default=600),
# days to delay mature fails
Column('delay1', Integer, nullable=False, default=0),
Column('delay2', Float, nullable=False, default=0.0),
# collapsing future cards
Column('collapseTime', Integer, nullable=False, default=1),
@ -239,7 +240,7 @@ class Deck(object):
self.cardLimit(
"revActive", "revInactive",
"select count(*) from cards c where type = 0 "
"and combinedDue < :lim"), lim=self.dueCutoff)
"and combinedDue < :lim"), lim=self.failedCutoff)
def _rebuildRevCount(self):
self.revCount = self.s.scalar(
@ -268,7 +269,7 @@ class Deck(object):
"revActive", "revInactive", """
select c.id, factId, combinedDue from cards c where
type = 0 and combinedDue < :lim order by combinedDue
limit %d""" % self.queueLimit), lim=self.dueCutoff)
limit %d""" % self.queueLimit), lim=self.failedCutoff)
self.failedQueue.reverse()
def _fillRevQueue(self):
@ -380,18 +381,20 @@ when successive then 1 when reps then 0 else 2 end)
return 2
def updateCutoff(self):
d = datetime.datetime.utcfromtimestamp(
time.time() - self.utcOffset) + datetime.timedelta(days=1)
d = datetime.datetime(d.year, d.month, d.day)
newday = self.utcOffset - time.timezone
d += datetime.timedelta(seconds=newday)
cutoff = time.mktime(d.timetuple())
# cutoff must not be in the past
while cutoff < time.time():
cutoff += 86400
# cutoff must not be more than 24 hours in the future
cutoff = min(time.time() + 86400, cutoff)
self.failedCutoff = cutoff
if self.getBool("perDay"):
d = datetime.datetime.utcfromtimestamp(
time.time() - self.utcOffset) + datetime.timedelta(days=1)
d = datetime.datetime(d.year, d.month, d.day)
newday = self.utcOffset - time.timezone
d += datetime.timedelta(seconds=newday)
self.dueCutoff = time.mktime(d.timetuple())
# cutoff must not be in the past
while self.dueCutoff < time.time():
self.dueCutoff += 86400
# cutoff must not be more than 24 hours in the future
self.dueCutoff = min(time.time() + 86400, self.dueCutoff)
self.dueCutoff = cutoff
else:
self.dueCutoff = time.time()
@ -614,8 +617,9 @@ limit %s""" % (self.cramOrder, self.queueLimit)))
self.updateNewCountToday()
if self.failedQueue:
# failed card due?
if self.delay0 and self.failedQueue[-1][2] < time.time():
return self.failedQueue[-1][0]
if self.delay0:
if self.failedQueue[-1][2] < time.time() + self.delay0:
return self.failedQueue[-1][0]
# failed card queue too big?
if (self.failedCardMax and
self.failedSoonCount >= self.failedCardMax):
@ -941,9 +945,9 @@ where id != :id and factId = :factId""",
"Return time when CARD will expire given EASE."
if ease == 1:
if oldState == "mature":
due = self.delay1
due = 0 + self.delay1*86400
else:
due = self.delay0
due = 0
else:
due = card.interval * 86400.0
return due + time.time()
@ -2878,10 +2882,10 @@ where key = :key""", key=key, value=value):
elif idx == 4:
d = 259200
self.delay0 = d
self.delay1 = d
self.delay1 = 0
def getFailedCardPolicy(self):
if self.delay0 != self.delay1:
if self.delay1:
return 5
d = self.delay0
if self.collapseTime == 1:
@ -3559,6 +3563,13 @@ update cards set type = type - 3 where type between 0 and 2 and priority = -3"""
if deck.getBool("perDay"):
deck.hardIntervalMin = max(1.0, deck.hardIntervalMin)
deck.hardIntervalMax = max(1.1, deck.hardIntervalMax)
# - new delay1 handling
if deck.delay0 == deck.delay1:
deck.delay1 = 0
elif deck.delay1 >= 28800:
deck.delay1 = 1
else:
deck.delay1 = 0
# unsuspend buried/rev early - can remove priorities in the future
ids = deck.s.column0(
"select id from cards where type > 2 or priority between -2 and -1")