mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 06:52:21 -04:00
review early, learn more, tweak finished msg
This commit is contained in:
parent
9ac3926786
commit
3670a0cb7b
1 changed files with 102 additions and 28 deletions
128
anki/deck.py
128
anki/deck.py
|
@ -50,7 +50,7 @@ decksTable = Table(
|
||||||
Column('created', Float, nullable=False, default=time.time),
|
Column('created', Float, nullable=False, default=time.time),
|
||||||
Column('modified', Float, nullable=False, default=time.time),
|
Column('modified', Float, nullable=False, default=time.time),
|
||||||
Column('description', UnicodeText, nullable=False, default=u""),
|
Column('description', UnicodeText, nullable=False, default=u""),
|
||||||
Column('version', Integer, nullable=False, default=20),
|
Column('version', Integer, nullable=False, default=21),
|
||||||
Column('currentModelId', Integer, ForeignKey("models.id")),
|
Column('currentModelId', Integer, ForeignKey("models.id")),
|
||||||
# syncing
|
# syncing
|
||||||
Column('syncName', UnicodeText),
|
Column('syncName', UnicodeText),
|
||||||
|
@ -121,6 +121,9 @@ class Deck(object):
|
||||||
self.sessionStartReps = 0
|
self.sessionStartReps = 0
|
||||||
self.sessionStartTime = 0
|
self.sessionStartTime = 0
|
||||||
self.lastSessionStart = 0
|
self.lastSessionStart = 0
|
||||||
|
self.reviewedAheadCards = []
|
||||||
|
self.extraNewCards = 0
|
||||||
|
self.reviewEarly = False
|
||||||
|
|
||||||
def modifiedSinceSave(self):
|
def modifiedSinceSave(self):
|
||||||
return self.modified > self.lastLoaded
|
return self.modified > self.lastLoaded
|
||||||
|
@ -156,10 +159,30 @@ class Deck(object):
|
||||||
id = self._maybeGetNewCard()
|
id = self._maybeGetNewCard()
|
||||||
if id:
|
if id:
|
||||||
return id
|
return id
|
||||||
# display failed cards early
|
# review ahead?
|
||||||
if self.collapseTime or not self.delay0:
|
if self.reviewEarly:
|
||||||
|
id = self.getCardIdAhead()
|
||||||
|
if id:
|
||||||
|
return id
|
||||||
|
else:
|
||||||
|
self.resetAfterReviewEarly()
|
||||||
|
self.checkDue()
|
||||||
|
# display failed cards early/last
|
||||||
|
if self._showFailedLast():
|
||||||
id = self.s.scalar(
|
id = self.s.scalar(
|
||||||
"select id from failedCards limit 1")
|
"select id from failedCards limit 1")
|
||||||
|
if id:
|
||||||
|
return id
|
||||||
|
|
||||||
|
def getCardIdAhead(self):
|
||||||
|
"Return the first card that would become due."
|
||||||
|
t = time.time()
|
||||||
|
id = self.s.scalar("""
|
||||||
|
select id from cards
|
||||||
|
where priority in (1,2,3,4)
|
||||||
|
order by priority desc, combinedDue
|
||||||
|
limit 1""")
|
||||||
|
#print "ahead", time.time() -t, id
|
||||||
return id
|
return id
|
||||||
|
|
||||||
# Get card: helper functions
|
# Get card: helper functions
|
||||||
|
@ -207,6 +230,9 @@ class Deck(object):
|
||||||
return self.s.scalar(
|
return self.s.scalar(
|
||||||
"select id from %s limit 1" % self.revCardTable())
|
"select id from %s limit 1" % self.revCardTable())
|
||||||
|
|
||||||
|
def _showFailedLast(self):
|
||||||
|
return self.collapseTime or not self.delay0
|
||||||
|
|
||||||
def cardFromId(self, id, orm=False):
|
def cardFromId(self, id, orm=False):
|
||||||
"Given a card ID, return a card, and start the card timer."
|
"Given a card ID, return a card, and start the card timer."
|
||||||
if orm:
|
if orm:
|
||||||
|
@ -293,10 +319,15 @@ where factId in (select factId from %s limit 60))""" % (new, new))
|
||||||
self.setUndoStart(undoName)
|
self.setUndoStart(undoName)
|
||||||
now = time.time()
|
now = time.time()
|
||||||
oldState = self.cardState(card)
|
oldState = self.cardState(card)
|
||||||
lastDelay = max(0, (time.time() - card.due) / 86400.0)
|
lastDelaySecs = time.time() - card.combinedDue
|
||||||
|
lastDelay = lastDelaySecs / 86400.0
|
||||||
|
oldSuc = card.successive
|
||||||
# update card details
|
# update card details
|
||||||
card.lastInterval = card.interval
|
last = card.interval
|
||||||
card.interval = self.nextInterval(card, ease)
|
card.interval = self.nextInterval(card, ease)
|
||||||
|
if lastDelay >= 0:
|
||||||
|
# keep last interval if reviewing early
|
||||||
|
card.lastInterval = last
|
||||||
card.lastDue = card.due
|
card.lastDue = card.due
|
||||||
card.due = self.nextDue(card, ease, oldState)
|
card.due = self.nextDue(card, ease, oldState)
|
||||||
card.isDue = 0
|
card.isDue = 0
|
||||||
|
@ -337,6 +368,11 @@ isDue = 0
|
||||||
where id != :id and factId = :factId""",
|
where id != :id and factId = :factId""",
|
||||||
id=card.id, space=space, now=now, factId=card.factId)
|
id=card.id, space=space, now=now, factId=card.factId)
|
||||||
card.spaceUntil = 0
|
card.spaceUntil = 0
|
||||||
|
# temp suspend if learning ahead
|
||||||
|
if lastDelay < 0:
|
||||||
|
if oldSuc or lastDelaySecs > self.delay0 or not self._showFailedLast():
|
||||||
|
card.priority = 0
|
||||||
|
self.reviewedAheadCards.append(card.id)
|
||||||
# card stats
|
# card stats
|
||||||
anki.cards.Card.updateStats(card, ease, oldState)
|
anki.cards.Card.updateStats(card, ease, oldState)
|
||||||
card.toDB(self.s)
|
card.toDB(self.s)
|
||||||
|
@ -348,6 +384,9 @@ where id != :id and factId = :factId""",
|
||||||
entry.writeSQL(self.s)
|
entry.writeSQL(self.s)
|
||||||
self.modified = now
|
self.modified = now
|
||||||
self.setUndoEnd(undoName)
|
self.setUndoEnd(undoName)
|
||||||
|
# decrease card boost
|
||||||
|
if self.extraNewCards:
|
||||||
|
self.extraNewCards -= 1
|
||||||
|
|
||||||
# Interval management
|
# Interval management
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -355,9 +394,16 @@ where id != :id and factId = :factId""",
|
||||||
def nextInterval(self, card, ease):
|
def nextInterval(self, card, ease):
|
||||||
"Return the next interval for CARD given EASE."
|
"Return the next interval for CARD given EASE."
|
||||||
delay = self._adjustedDelay(card, ease)
|
delay = self._adjustedDelay(card, ease)
|
||||||
return self._nextInterval(card.interval, card.factor, delay, ease)
|
return self._nextInterval(card, delay, ease)
|
||||||
|
|
||||||
def _nextInterval(self, interval, factor, delay, ease):
|
def _nextInterval(self, card, delay, ease):
|
||||||
|
interval = card.interval
|
||||||
|
factor = card.factor
|
||||||
|
if delay < 0:
|
||||||
|
interval = card.lastInterval + ((interval - abs(delay)) / 2.0)
|
||||||
|
delay = 0
|
||||||
|
if interval < self.midIntervalMin:
|
||||||
|
interval = 0
|
||||||
# if interval is less than mid interval, use presets
|
# if interval is less than mid interval, use presets
|
||||||
if ease == 1:
|
if ease == 1:
|
||||||
interval *= self.delay2
|
interval *= self.delay2
|
||||||
|
@ -430,7 +476,10 @@ where id != :id and factId = :factId""",
|
||||||
"Return an adjusted delay value for CARD based on EASE."
|
"Return an adjusted delay value for CARD based on EASE."
|
||||||
if self.cardIsNew(card):
|
if self.cardIsNew(card):
|
||||||
return 0
|
return 0
|
||||||
return max(0, (time.time() - card.due) / 86400.0)
|
if card.combinedDue <= time.time():
|
||||||
|
return (time.time() - card.due) / 86400.0
|
||||||
|
else:
|
||||||
|
return (time.time() - card.combinedDue) / 86400.0
|
||||||
|
|
||||||
def resetCards(self, ids):
|
def resetCards(self, ids):
|
||||||
"Reset progress on cards in IDS."
|
"Reset progress on cards in IDS."
|
||||||
|
@ -502,7 +551,7 @@ type = 0 and isDue = 1 and combinedDue <= :now""", now=time.time())
|
||||||
stmt % 2, now=time.time()).rowcount
|
stmt % 2, now=time.time()).rowcount
|
||||||
self.newCountToday = max(min(
|
self.newCountToday = max(min(
|
||||||
self.newCount, self.newCardsPerDay -
|
self.newCount, self.newCardsPerDay -
|
||||||
self.newCardsToday()), 0)
|
self.newCardsToday()), 0) + self.extraNewCards
|
||||||
|
|
||||||
def rebuildQueue(self):
|
def rebuildQueue(self):
|
||||||
"Update relative delays based on current time."
|
"Update relative delays based on current time."
|
||||||
|
@ -538,6 +587,17 @@ type = 0 and isDue = 1 and combinedDue <= :now""", now=time.time())
|
||||||
if genToday(self) != self._dailyStats.day:
|
if genToday(self) != self._dailyStats.day:
|
||||||
self._dailyStats = dailyStats(self)
|
self._dailyStats = dailyStats(self)
|
||||||
|
|
||||||
|
def cardsDueSoon(self, ratio=0.1, minInt=0, maxInt=0):
|
||||||
|
"Return ids of cards near their expiration date."
|
||||||
|
#FIXME: implement
|
||||||
|
pass
|
||||||
|
|
||||||
|
def resetAfterReviewEarly(self):
|
||||||
|
self.updatePriorities(self.reviewedAheadCards)
|
||||||
|
self.reviewedAheadCards = []
|
||||||
|
self.reviewEarly = False
|
||||||
|
self.flushMod()
|
||||||
|
|
||||||
# Times
|
# Times
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
@ -547,15 +607,15 @@ type = 0 and isDue = 1 and combinedDue <= :now""", now=time.time())
|
||||||
newCardsTomorrow = min(self.newCount, self.newCardsPerDay)
|
newCardsTomorrow = min(self.newCount, self.newCardsPerDay)
|
||||||
msg = _('''\
|
msg = _('''\
|
||||||
At the same time tomorrow:<br><br>
|
At the same time tomorrow:<br><br>
|
||||||
- There will be <b>%(wait)d</b> cards waiting for review<br>
|
There will be <b>%(wait)d</b> cards waiting for review.<br>
|
||||||
- There will be <b>%(new)d</b>
|
There will be <b>%(new)d</b> new cards waiting.''') % {
|
||||||
<a href="http://ichi2.net/anki/wiki/DeckProperties#NewCards">
|
|
||||||
new cards</a> waiting''') % {
|
|
||||||
'new': newCardsTomorrow,
|
'new': newCardsTomorrow,
|
||||||
'wait': self.cardsDueBy(time.time() + 86400)
|
'wait': self.cardsDueBy(time.time() + 86400)
|
||||||
}
|
}
|
||||||
if next - time.time() > 86400 and not newCardsTomorrow:
|
if self.spacedCardCount():
|
||||||
msg = (_("The next card will be shown in <b>%s</b>") %
|
msg = _("Spaced cards will be shown soon.")
|
||||||
|
elif next - time.time() > 86400 and not newCardsTomorrow:
|
||||||
|
msg = (_("The next card will be shown in <b>%s</b>.") %
|
||||||
self.earliestTimeStr())
|
self.earliestTimeStr())
|
||||||
else:
|
else:
|
||||||
msg = _("No cards are due.")
|
msg = _("No cards are due.")
|
||||||
|
@ -586,20 +646,31 @@ select count(id) from cards where combinedDue < :time
|
||||||
and priority in (1,2,3,4) and type in (0, 1)""", time=time)
|
and priority in (1,2,3,4) and type in (0, 1)""", time=time)
|
||||||
|
|
||||||
def deckFinishedMsg(self):
|
def deckFinishedMsg(self):
|
||||||
|
spaceSusp = ""
|
||||||
|
c = self.spacedCardCount()
|
||||||
|
if c:
|
||||||
|
spaceSusp += '''
|
||||||
|
There are <b>%d</b>
|
||||||
|
<a href="http://ichi2.net/anki/wiki/Key_Terms_and_Concepts#head-59a81e35b6afb23930005e943068945214d194b3">
|
||||||
|
spaced</a> cards.''' % c
|
||||||
|
c2 = self.suspendedCardCount()
|
||||||
|
if c2:
|
||||||
|
if c:
|
||||||
|
spaceSusp += "<br>"
|
||||||
|
spaceSusp += '''
|
||||||
|
There are <b>%d</b>
|
||||||
|
<a href="http://ichi2.net/anki/wiki/Key_Terms_and_Concepts#head-37d2db274e6caa23aef55e29655a6b806901774b">
|
||||||
|
suspended</a> cards.''' % c2
|
||||||
|
if spaceSusp:
|
||||||
|
spaceSusp = "<br><br>" + spaceSusp
|
||||||
return _('''\
|
return _('''\
|
||||||
<div style="white-space: normal;">
|
<div style="white-space: normal;">
|
||||||
<h1>Congratulations!</h1>You have finished the deck for now.<br><br>
|
<h1>Congratulations!</h1>You have finished for now.<br><br>
|
||||||
%(next)s
|
%(next)s
|
||||||
<br><br>
|
%(spaceSusp)s
|
||||||
- There are <b>%(waiting)d</b>
|
</div>''') % {
|
||||||
<a href="http://ichi2.net/anki/wiki/Key_Terms_and_Concepts#head-59a81e35b6afb23930005e943068945214d194b3">
|
|
||||||
spaced</a> cards.<br>
|
|
||||||
- There are <b>%(suspended)d</b>
|
|
||||||
<a href="http://ichi2.net/anki/wiki/Key_Terms_and_Concepts#head-37d2db274e6caa23aef55e29655a6b806901774b">
|
|
||||||
suspended</a> cards.</div>''') % {
|
|
||||||
"next": self.nextDueMsg(),
|
"next": self.nextDueMsg(),
|
||||||
"suspended": self.suspendedCardCount(),
|
"spaceSusp": spaceSusp,
|
||||||
"waiting": self.spacedCardCount()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Priorities
|
# Priorities
|
||||||
|
@ -2234,13 +2305,16 @@ where interval < 1""")
|
||||||
deck.sessionRepLimit = 0
|
deck.sessionRepLimit = 0
|
||||||
deck.version = 19
|
deck.version = 19
|
||||||
deck.s.commit()
|
deck.s.commit()
|
||||||
deck.s.statement("vacuum")
|
|
||||||
deck.s.statement("analyze")
|
|
||||||
if deck.version < 20:
|
if deck.version < 20:
|
||||||
DeckStorage._addViews(deck)
|
DeckStorage._addViews(deck)
|
||||||
DeckStorage._addIndices(deck)
|
DeckStorage._addIndices(deck)
|
||||||
deck.version = 20
|
deck.version = 20
|
||||||
deck.s.commit()
|
deck.s.commit()
|
||||||
|
if deck.version < 21:
|
||||||
|
deck.s.statement("vacuum")
|
||||||
|
deck.s.statement("analyze")
|
||||||
|
deck.version = 21
|
||||||
|
deck.s.commit()
|
||||||
return deck
|
return deck
|
||||||
_upgradeDeck = staticmethod(_upgradeDeck)
|
_upgradeDeck = staticmethod(_upgradeDeck)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue