mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 15:02:21 -04:00
new cram code, remove refs to inactive cards
- cramming is now a separate scheduler type - correctly answering a card while cramming causes its scheduling to be changed in the standard review too - options to sort cards by earliest modified, ordered, random - render priority 0 obsolete, as it's all done at queue generation time now
This commit is contained in:
parent
30a6f04708
commit
94ad0cb95e
1 changed files with 131 additions and 20 deletions
151
anki/deck.py
151
anki/deck.py
|
@ -163,6 +163,7 @@ class Deck(object):
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def setupStandardScheduler(self):
|
def setupStandardScheduler(self):
|
||||||
|
self.getCardId = self._getCardId
|
||||||
self.fillFailedQueue = self._fillFailedQueue
|
self.fillFailedQueue = self._fillFailedQueue
|
||||||
self.fillRevQueue = self._fillRevQueue
|
self.fillRevQueue = self._fillRevQueue
|
||||||
self.fillNewQueue = self._fillNewQueue
|
self.fillNewQueue = self._fillNewQueue
|
||||||
|
@ -172,7 +173,9 @@ class Deck(object):
|
||||||
self.requeueCard = self._requeueCard
|
self.requeueCard = self._requeueCard
|
||||||
self.timeForNewCard = self._timeForNewCard
|
self.timeForNewCard = self._timeForNewCard
|
||||||
self.updateNewCountToday = self._updateNewCountToday
|
self.updateNewCountToday = self._updateNewCountToday
|
||||||
|
self.cardType = self._cardType
|
||||||
self.finishScheduler = None
|
self.finishScheduler = None
|
||||||
|
self.answerCard = self._answerCard
|
||||||
|
|
||||||
def fillQueues(self):
|
def fillQueues(self):
|
||||||
self.fillFailedQueue()
|
self.fillFailedQueue()
|
||||||
|
@ -197,11 +200,13 @@ class Deck(object):
|
||||||
return """
|
return """
|
||||||
and id in (select cardId from cardTags where
|
and id in (select cardId from cardTags where
|
||||||
tagId in %s and tagId not in %s)""" % (ids2str(yids), ids2str(nids))
|
tagId in %s and tagId not in %s)""" % (ids2str(yids), ids2str(nids))
|
||||||
else:
|
elif no:
|
||||||
nids = tagIds(self.s, no).values()
|
nids = tagIds(self.s, no).values()
|
||||||
return """
|
return """
|
||||||
and id not in (select cardId from cardTags where
|
and id not in (select cardId from cardTags where
|
||||||
tagId in %s)""" % (ids2str(nids))
|
tagId in %s)""" % (ids2str(nids))
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
def _rebuildFailedCount(self):
|
def _rebuildFailedCount(self):
|
||||||
self.failedSoonCount = self.s.scalar(
|
self.failedSoonCount = self.s.scalar(
|
||||||
|
@ -322,6 +327,15 @@ end)""" + where)
|
||||||
self.s.statement(
|
self.s.statement(
|
||||||
"update cards set type = type + 3 where priority <= 0")
|
"update cards set type = type + 3 where priority <= 0")
|
||||||
|
|
||||||
|
def _cardType(self, card):
|
||||||
|
"Return the type of the current card (what queue it's in)"
|
||||||
|
if self.cardIsNew(card):
|
||||||
|
return 2
|
||||||
|
elif card.successive == 0:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return 1
|
||||||
|
|
||||||
def updateCutoff(self):
|
def updateCutoff(self):
|
||||||
if self.getBool("perDay"):
|
if self.getBool("perDay"):
|
||||||
today = genToday(self) + datetime.timedelta(days=1)
|
today = genToday(self) + datetime.timedelta(days=1)
|
||||||
|
@ -410,6 +424,105 @@ select count() from cards where type = 2 and combinedDue < :now
|
||||||
def _updateLearnMoreCountToday(self):
|
def _updateLearnMoreCountToday(self):
|
||||||
self.newCountToday = self.newCount
|
self.newCountToday = self.newCount
|
||||||
|
|
||||||
|
# Cramming
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
def setupCramScheduler(self, active, order):
|
||||||
|
# need option to randomize
|
||||||
|
self.getCardId = self._getCramCardId
|
||||||
|
self.activeCramTags = active
|
||||||
|
self.cramOrder = order
|
||||||
|
self.rebuildNewCount = self._rebuildCramNewCount
|
||||||
|
self.rebuildRevCount = self._rebuildCramCount
|
||||||
|
self.rebuildFailedCount = self._rebuildFailedCramCount
|
||||||
|
self.fillRevQueue = self._fillCramQueue
|
||||||
|
self.fillFailedQueue = self._fillFailedCramQueue
|
||||||
|
self.finishScheduler = self.setupStandardScheduler
|
||||||
|
self.failedCramQueue = []
|
||||||
|
self.requeueCard = self._requeueCramCard
|
||||||
|
self.cardType = self._cramCardType
|
||||||
|
self.answerCard = self._answerCramCard
|
||||||
|
|
||||||
|
def _answerCramCard(self, card, ease):
|
||||||
|
if ease == 1:
|
||||||
|
if self._cramCardType(card) != 0:
|
||||||
|
self.failedSoonCount += 1
|
||||||
|
self.revCount -= 1
|
||||||
|
self.requeueCard(card, None)
|
||||||
|
self.failedCramQueue.insert(0, [card.id, card.factId])
|
||||||
|
else:
|
||||||
|
self._answerCard(card, ease)
|
||||||
|
|
||||||
|
def _getCramCardId(self, check=True):
|
||||||
|
self.checkDailyStats()
|
||||||
|
self.fillQueues()
|
||||||
|
if self.failedSoonCount >= self.failedCardMax:
|
||||||
|
return self.failedQueue[-1][0]
|
||||||
|
# card due for review?
|
||||||
|
if self.revNoSpaced():
|
||||||
|
return self.revQueue[-1][0]
|
||||||
|
if self.failedQueue:
|
||||||
|
return self.failedQueue[-1][0]
|
||||||
|
if check:
|
||||||
|
# check for expired cards, or new day rollover
|
||||||
|
self.updateCutoff()
|
||||||
|
return self.getCardId(check=False)
|
||||||
|
# if we're in a custom scheduler, we may need to switch back
|
||||||
|
if self.finishScheduler:
|
||||||
|
self.finishScheduler()
|
||||||
|
self.reset()
|
||||||
|
return self.getCardId()
|
||||||
|
|
||||||
|
def _cramCardType(self, card):
|
||||||
|
if self.revQueue and self.revQueue[-1][0] == card.id:
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _requeueCramCard(self, card, oldSuc):
|
||||||
|
if self._cramCardType(card) == 1:
|
||||||
|
self.revQueue.pop()
|
||||||
|
else:
|
||||||
|
self.failedCramQueue.pop()
|
||||||
|
|
||||||
|
def _rebuildCramNewCount(self):
|
||||||
|
self.newCount = 0
|
||||||
|
self.newCountToday = 0
|
||||||
|
|
||||||
|
def _cramCardLimit(self, active):
|
||||||
|
if isinstance(active, list):
|
||||||
|
return " and id in " + ids2str(active)
|
||||||
|
else:
|
||||||
|
yes = parseTags(active)
|
||||||
|
if yes:
|
||||||
|
yids = tagIds(self.s, yes).values()
|
||||||
|
return """
|
||||||
|
and id in (select cardId from cardTags where
|
||||||
|
tagId in %s)""" % (ids2str(yids))
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def _fillCramQueue(self):
|
||||||
|
if self.revCount and not self.revQueue:
|
||||||
|
self.revQueue = self.s.all("""
|
||||||
|
select id, factId from cards
|
||||||
|
where type in (0,1,2) %s
|
||||||
|
order by %s
|
||||||
|
limit %s""" % (self._cramCardLimit(self.activeCramTags),
|
||||||
|
self.cramOrder, self.queueLimit))
|
||||||
|
self.revQueue.reverse()
|
||||||
|
|
||||||
|
def _rebuildCramCount(self):
|
||||||
|
self.revCount = self.s.scalar("""
|
||||||
|
select count(*) from cards where type in (0,1,2) %s
|
||||||
|
""" % self._cramCardLimit(self.activeCramTags))
|
||||||
|
|
||||||
|
def _rebuildFailedCramCount(self):
|
||||||
|
self.failedSoonCount = len(self.failedCramQueue)
|
||||||
|
|
||||||
|
def _fillFailedCramQueue(self):
|
||||||
|
self.failedQueue = self.failedCramQueue
|
||||||
|
|
||||||
# Getting the next card
|
# Getting the next card
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
@ -419,7 +532,7 @@ select count() from cards where type = 2 and combinedDue < :now
|
||||||
if id:
|
if id:
|
||||||
return self.cardFromId(id, orm)
|
return self.cardFromId(id, orm)
|
||||||
|
|
||||||
def getCardId(self, check=True):
|
def _getCardId(self, check=True):
|
||||||
"Return the next due card id, or None."
|
"Return the next due card id, or None."
|
||||||
self.checkDailyStats()
|
self.checkDailyStats()
|
||||||
self.fillQueues()
|
self.fillQueues()
|
||||||
|
@ -555,12 +668,13 @@ where id in """
|
||||||
# Answering a card
|
# Answering a card
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def answerCard(self, card, ease):
|
def _answerCard(self, card, ease):
|
||||||
undoName = _("Answer Card")
|
undoName = _("Answer Card")
|
||||||
self.setUndoStart(undoName)
|
self.setUndoStart(undoName)
|
||||||
now = time.time()
|
now = time.time()
|
||||||
# old state
|
# old state
|
||||||
oldState = self.cardState(card)
|
oldState = self.cardState(card)
|
||||||
|
oldQueue = self.cardType(card)
|
||||||
lastDelaySecs = time.time() - card.combinedDue
|
lastDelaySecs = time.time() - card.combinedDue
|
||||||
lastDelay = lastDelaySecs / 86400.0
|
lastDelay = lastDelaySecs / 86400.0
|
||||||
oldSuc = card.successive
|
oldSuc = card.successive
|
||||||
|
@ -597,7 +711,7 @@ where factId = :fid and id != :id""", fid=card.factId, id=card.id) or 0
|
||||||
for (type, count) in self.s.all("""
|
for (type, count) in self.s.all("""
|
||||||
select type, count(type) from cards
|
select type, count(type) from cards
|
||||||
where factId = :fid and
|
where factId = :fid and
|
||||||
(combinedDue < :now or id = :cid)
|
combinedDue < :now and id != :cid
|
||||||
group by type""", fid=card.factId, cid=card.id, now=self.dueCutoff):
|
group by type""", fid=card.factId, cid=card.id, now=self.dueCutoff):
|
||||||
if type == 0:
|
if type == 0:
|
||||||
self.failedSoonCount -= count
|
self.failedSoonCount -= count
|
||||||
|
@ -605,9 +719,18 @@ group by type""", fid=card.factId, cid=card.id, now=self.dueCutoff):
|
||||||
self.revCount -= count
|
self.revCount -= count
|
||||||
elif type == 2:
|
elif type == 2:
|
||||||
self.newCount -= count
|
self.newCount -= count
|
||||||
# bump failed count if necessary
|
# adjust counts for current card
|
||||||
if ease == 1:
|
if ease == 1:
|
||||||
self.failedSoonCount += 1
|
self.failedSoonCount += 1
|
||||||
|
if oldQueue == 0:
|
||||||
|
if ease == 1:
|
||||||
|
self.failedSoonCount -= 1
|
||||||
|
else:
|
||||||
|
self.failedSoonCount -= 1
|
||||||
|
elif oldQueue == 1:
|
||||||
|
self.revCount -= 1
|
||||||
|
else:
|
||||||
|
self.newCount -= 1
|
||||||
# space other cards
|
# space other cards
|
||||||
self.s.statement("""
|
self.s.statement("""
|
||||||
update cards set
|
update cards set
|
||||||
|
@ -680,6 +803,8 @@ where id != :id and factId = :factId""",
|
||||||
factor = card.factor
|
factor = card.factor
|
||||||
# if shown early and not failed
|
# if shown early and not failed
|
||||||
if delay < 0 and card.successive:
|
if delay < 0 and card.successive:
|
||||||
|
# FIXME: this should recreate lastInterval from interval /
|
||||||
|
# lastFactor, or we lose delay information when reviewing early
|
||||||
interval = max(card.lastInterval, card.interval + delay)
|
interval = max(card.lastInterval, card.interval + delay)
|
||||||
if interval < self.midIntervalMin:
|
if interval < self.midIntervalMin:
|
||||||
interval = 0
|
interval = 0
|
||||||
|
@ -943,8 +1068,7 @@ and type in (0, 1)""", time=time)
|
||||||
up = {}
|
up = {}
|
||||||
for (type, pri) in ((self.lowPriority, 1),
|
for (type, pri) in ((self.lowPriority, 1),
|
||||||
(self.medPriority, 3),
|
(self.medPriority, 3),
|
||||||
(self.highPriority, 4),
|
(self.highPriority, 4)):
|
||||||
(self.suspended, 0)):
|
|
||||||
for tag in parseTags(type.lower()):
|
for tag in parseTags(type.lower()):
|
||||||
up[tag] = pri
|
up[tag] = pri
|
||||||
new = []
|
new = []
|
||||||
|
@ -972,7 +1096,6 @@ and type in (0, 1)""", time=time)
|
||||||
cards = self.s.all("""
|
cards = self.s.all("""
|
||||||
select cardTags.cardId,
|
select cardTags.cardId,
|
||||||
case
|
case
|
||||||
when min(tags.priority) = 0 then 0
|
|
||||||
when max(tags.priority) > 2 then max(tags.priority)
|
when max(tags.priority) > 2 then max(tags.priority)
|
||||||
when min(tags.priority) = 1 then 1
|
when min(tags.priority) = 1 then 1
|
||||||
else 2 end
|
else 2 end
|
||||||
|
@ -992,9 +1115,6 @@ group by cardTags.cardId""" % limit)
|
||||||
"update cards set priority = :pri %s where id in %s "
|
"update cards set priority = :pri %s where id in %s "
|
||||||
"and priority != :pri and priority >= -2") % (
|
"and priority != :pri and priority >= -2") % (
|
||||||
extra, ids2str(cs)), pri=pri, m=time.time())
|
extra, ids2str(cs)), pri=pri, m=time.time())
|
||||||
self.s.execute(
|
|
||||||
"update cards set type = type + 3 where type in (0,1,2) and "
|
|
||||||
"priority = 0").rowcount
|
|
||||||
self.s.execute(
|
self.s.execute(
|
||||||
"update cards set type = type - 3 where type in (3,4,5) and "
|
"update cards set type = type - 3 where type in (3,4,5) and "
|
||||||
"priority > 0").rowcount
|
"priority > 0").rowcount
|
||||||
|
@ -1138,15 +1258,6 @@ and due < :now""", now=time.time())
|
||||||
left += stats['failed'] * stats['dAverageTime'] * factor
|
left += stats['failed'] * stats['dAverageTime'] * factor
|
||||||
return left
|
return left
|
||||||
|
|
||||||
def queueForCard(self, card):
|
|
||||||
"Return the queue the current card is in."
|
|
||||||
if self.cardIsNew(card):
|
|
||||||
return "new"
|
|
||||||
elif card.successive == 0:
|
|
||||||
return "failed"
|
|
||||||
elif card.reps:
|
|
||||||
return "rev"
|
|
||||||
|
|
||||||
# Facts
|
# Facts
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue