diff --git a/anki/deck.py b/anki/deck.py
index 6aa9babe2..1bc5d0c74 100644
--- a/anki/deck.py
+++ b/anki/deck.py
@@ -160,7 +160,7 @@ qconf=?, conf=?, data=?""",
return anki.models.Template(self, self.deck.db.first(
"select * from templates where id = ?", id))
- # unsorted
+ # Utils
##########################################################################
def nextID(self, type):
@@ -170,217 +170,14 @@ qconf=?, conf=?, data=?""",
return id
def reset(self):
+ "Rebuild the queue and reload data after DB modified."
self.sched.reset()
- # Times
- ##########################################################################
-
- def nextDueMsg(self):
- next = self.earliestTime()
- if next:
- # all new cards except suspended
- newCount = self.newCardsDueBy(self.dayCutoff + 86400)
- newCardsTomorrow = min(newCount, self.newCardsPerDay)
- cards = self.cardsDueBy(self.dayCutoff + 86400)
- msg = _('''\
-
-At this time tomorrow:
-%(wait)s
-%(new)s''') % {
- 'new': ngettext("There will be %d new card.",
- "There will be %d new cards.",
- newCardsTomorrow) % newCardsTomorrow,
- 'wait': ngettext("There will be %s review.",
- "There will be %s reviews.", cards) % cards,
- }
- if next > (self.dayCutoff+86400) and not newCardsTomorrow:
- msg = (_("The next review is in %s.") %
- self.earliestTimeStr())
- else:
- msg = _("No cards are due.")
- return msg
-
- def earliestTime(self):
- """Return the time of the earliest card.
-This may be in the past if the deck is not finished.
-If the deck has no (enabled) cards, return None.
-Ignore new cards."""
- earliestRev = self.db.scalar(self.cardLimit("revActive", "revInactive", """
-select due from cards c where queue = 1
-order by due
-limit 1"""))
- earliestFail = self.db.scalar(self.cardLimit("revActive", "revInactive", """
-select due+%d from cards c where queue = 0
-order by due
-limit 1""" % self.delay0))
- if earliestRev and earliestFail:
- return min(earliestRev, earliestFail)
- elif earliestRev:
- return earliestRev
- else:
- return earliestFail
-
- def earliestTimeStr(self, next=None):
- """Return the relative time to the earliest card as a string."""
- if next == None:
- next = self.earliestTime()
- if not next:
- return _("unknown")
- diff = next - time.time()
- return anki.utils.fmtTimeSpan(diff)
-
- def cardsDueBy(self, time):
- "Number of cards due at TIME. Ignore new cards"
- return self.db.scalar(
- self.cardLimit(
- "revActive", "revInactive",
- "select count(*) from cards c where queue between 0 and 1 "
- "and due < :lim"), lim=time)
-
- def newCardsDueBy(self, time):
- "Number of new cards due at TIME."
- return self.db.scalar(
- self.cardLimit(
- "newActive", "newInactive",
- "select count(*) from cards c where queue = 2 "
- "and due < :lim"), lim=time)
-
- def deckFinishedMsg(self):
- spaceSusp = ""
- c= self.spacedCardCount()
- if c:
- spaceSusp += ngettext(
- 'There is %d delayed card.',
- 'There are %d delayed cards.', c) % c
- c2 = self.hiddenCards()
- if c2:
- if spaceSusp:
- spaceSusp += "
"
- spaceSusp += _(
- "Some cards are inactive or suspended.")
- if spaceSusp:
- spaceSusp = "
" + spaceSusp
- return _('''\
-
-
Congratulations!
You have finished for now.
-%(next)s
-%(spaceSusp)s
-''') % {
- "next": self.nextDueMsg(),
- "spaceSusp": spaceSusp,
- }
-
- # Suspending
- ##########################################################################
-
- def suspendCards(self, ids):
- "Suspend cards."
- self.startProgress()
- self.db.execute("""
-update cards
-set queue = -1, mod = :t
-where id in %s""" % ids2str(ids), t=time.time())
- self.finishProgress()
-
- def unsuspendCards(self, ids):
- "Unsuspend cards."
- self.startProgress()
- self.db.execute("""
-update cards set queue = type, mod=:t
-where queue = -1 and id in %s""" %
- ids2str(ids), t=time.time())
- self.finishProgress()
-
- def buryFact(self, fact):
- "Bury all cards for fact until next session."
- for card in fact.cards:
- if card.queue in (0,1,2):
- card.queue = -2
-
- # Counts
- ##########################################################################
-
- def hiddenCards(self):
- "Assumes queue finished. True if some due cards have not been shown."
- return self.db.scalar("""
-select 1 from cards where due < :now
-and queue between 0 and 1 limit 1""", now=self.dayCutoff)
-
- def spacedCardCount(self):
- "Number of spaced cards."
- print "spacedCardCount"
- return 0
- return self.db.scalar("""
-select count(cards.id) from cards where
-due > :now and due < :now""", now=time.time())
-
- def isEmpty(self):
- return not self.cardCount
-
- def matureCardCount(self):
- return self.db.scalar(
- "select count(id) from cards where interval >= :t ",
- t=MATURE_THRESHOLD)
-
- def youngCardCount(self):
- return self.db.scalar(
- "select count(id) from cards where interval < :t "
- "and reps != 0", t=MATURE_THRESHOLD)
-
- def newCountAll(self):
- "All new cards, including spaced."
- return self.db.scalar(
- "select count(id) from cards where type = 2")
-
- def seenCardCount(self):
- return self.db.scalar(
- "select count(id) from cards where type between 0 and 1")
-
- # Card predicates
- ##########################################################################
-
- def cardState(self, card):
- if self.cardIsNew(card):
- return "new"
- elif card.interval > MATURE_THRESHOLD:
- return "mature"
- return "young"
-
- def cardIsNew(self, card):
- "True if a card has never been seen before."
- return card.reps == 0
-
- def cardIsYoung(self, card):
- "True if card is not new and not mature."
- return (not self.cardIsNew(card) and
- not self.cardIsMature(card))
-
- def cardIsMature(self, card):
- return card.interval >= MATURE_THRESHOLD
-
- # Stats
- ##########################################################################
-
- def getETA(self, stats):
- # rev + new cards first, account for failures
- import traceback; traceback.print_stack()
- count = stats['rev'] + stats['new']
- count *= 1 + stats['gYoungNo%'] / 100.0
- left = count * stats['dAverageTime']
- # failed - higher time per card for higher amount of cards
- failedBaseMulti = 1.5
- failedMod = 0.07
- failedBaseCount = 20
- factor = (failedBaseMulti +
- (failedMod * (stats['failed'] - failedBaseCount)))
- left += stats['failed'] * stats['dAverageTime'] * factor
- return left
-
# Facts
##########################################################################
def factCount(self):
- return self.db.scalar("select count() from facts")
+ return self.db.scalar("select count() from facts where crt != 0")
def newFact(self):
"Return a new fact with the current model."
@@ -517,9 +314,7 @@ select id from facts where id not in (select distinct fid from cards)""")
##########################################################################
def cardCount(self):
- all = self.db.scalar("select count() from cards")
- trash = self.db.scalar("select count() from cards where queue = -4")
- return all - trash
+ return self.db.scalar("select count() from cards where crt != 0")
def deleteCard(self, id):
"Delete a card given its id. Delete any unused facts."
diff --git a/anki/sched.py b/anki/sched.py
index debc72b1c..af3f482ac 100644
--- a/anki/sched.py
+++ b/anki/sched.py
@@ -587,3 +587,207 @@ order by due limit %d""" % self.queueLimit), lim=self.dayCutoff)
def _updateLearnMoreCountToday(self):
self.newCount = self.newAvail
+
+ # Times
+ ##########################################################################
+
+ def nextDueMsg(self):
+ next = self.earliestTime()
+ if next:
+ # all new cards except suspended
+ newCount = self.newCardsDueBy(self.dayCutoff + 86400)
+ newCardsTomorrow = min(newCount, self.newCardsPerDay)
+ cards = self.cardsDueBy(self.dayCutoff + 86400)
+ msg = _('''\
+
+At this time tomorrow:
+%(wait)s
+%(new)s''') % {
+ 'new': ngettext("There will be %d new card.",
+ "There will be %d new cards.",
+ newCardsTomorrow) % newCardsTomorrow,
+ 'wait': ngettext("There will be %s review.",
+ "There will be %s reviews.", cards) % cards,
+ }
+ if next > (self.dayCutoff+86400) and not newCardsTomorrow:
+ msg = (_("The next review is in %s.") %
+ self.earliestTimeStr())
+ else:
+ msg = _("No cards are due.")
+ return msg
+
+ def earliestTime(self):
+ """Return the time of the earliest card.
+This may be in the past if the deck is not finished.
+If the deck has no (enabled) cards, return None.
+Ignore new cards."""
+ earliestRev = self.db.scalar(self.cardLimit("revActive", "revInactive", """
+select due from cards c where queue = 1
+order by due
+limit 1"""))
+ earliestFail = self.db.scalar(self.cardLimit("revActive", "revInactive", """
+select due+%d from cards c where queue = 0
+order by due
+limit 1""" % self.delay0))
+ if earliestRev and earliestFail:
+ return min(earliestRev, earliestFail)
+ elif earliestRev:
+ return earliestRev
+ else:
+ return earliestFail
+
+ def earliestTimeStr(self, next=None):
+ """Return the relative time to the earliest card as a string."""
+ if next == None:
+ next = self.earliestTime()
+ if not next:
+ return _("unknown")
+ diff = next - time.time()
+ return anki.utils.fmtTimeSpan(diff)
+
+ def cardsDueBy(self, time):
+ "Number of cards due at TIME. Ignore new cards"
+ return self.db.scalar(
+ self.cardLimit(
+ "revActive", "revInactive",
+ "select count(*) from cards c where queue between 0 and 1 "
+ "and due < :lim"), lim=time)
+
+ def newCardsDueBy(self, time):
+ "Number of new cards due at TIME."
+ return self.db.scalar(
+ self.cardLimit(
+ "newActive", "newInactive",
+ "select count(*) from cards c where queue = 2 "
+ "and due < :lim"), lim=time)
+
+ def deckFinishedMsg(self):
+ spaceSusp = ""
+ c= self.spacedCardCount()
+ if c:
+ spaceSusp += ngettext(
+ 'There is %d delayed card.',
+ 'There are %d delayed cards.', c) % c
+ c2 = self.hiddenCards()
+ if c2:
+ if spaceSusp:
+ spaceSusp += "
"
+ spaceSusp += _(
+ "Some cards are inactive or suspended.")
+ if spaceSusp:
+ spaceSusp = "
" + spaceSusp
+ return _('''\
+
+
Congratulations!
You have finished for now.
+%(next)s
+%(spaceSusp)s
+''') % {
+ "next": self.nextDueMsg(),
+ "spaceSusp": spaceSusp,
+ }
+
+ # Suspending
+ ##########################################################################
+
+ def suspendCards(self, ids):
+ "Suspend cards."
+ self.startProgress()
+ self.db.execute("""
+update cards
+set queue = -1, mod = :t
+where id in %s""" % ids2str(ids), t=time.time())
+ self.finishProgress()
+
+ def unsuspendCards(self, ids):
+ "Unsuspend cards."
+ self.startProgress()
+ self.db.execute("""
+update cards set queue = type, mod=:t
+where queue = -1 and id in %s""" %
+ ids2str(ids), t=time.time())
+ self.finishProgress()
+
+ def buryFact(self, fact):
+ "Bury all cards for fact until next session."
+ for card in fact.cards:
+ if card.queue in (0,1,2):
+ card.queue = -2
+
+ # Counts
+ ##########################################################################
+
+ def hiddenCards(self):
+ "Assumes queue finished. True if some due cards have not been shown."
+ return self.db.scalar("""
+select 1 from cards where due < :now
+and queue between 0 and 1 limit 1""", now=self.dayCutoff)
+
+ def spacedCardCount(self):
+ "Number of spaced cards."
+ print "spacedCardCount"
+ return 0
+ return self.db.scalar("""
+select count(cards.id) from cards where
+due > :now and due < :now""", now=time.time())
+
+ def isEmpty(self):
+ return not self.cardCount
+
+ def matureCardCount(self):
+ return self.db.scalar(
+ "select count(id) from cards where interval >= :t ",
+ t=MATURE_THRESHOLD)
+
+ def youngCardCount(self):
+ return self.db.scalar(
+ "select count(id) from cards where interval < :t "
+ "and reps != 0", t=MATURE_THRESHOLD)
+
+ def newCountAll(self):
+ "All new cards, including spaced."
+ return self.db.scalar(
+ "select count(id) from cards where type = 2")
+
+ def seenCardCount(self):
+ return self.db.scalar(
+ "select count(id) from cards where type between 0 and 1")
+
+ # Card predicates
+ ##########################################################################
+
+ def cardState(self, card):
+ if self.cardIsNew(card):
+ return "new"
+ elif card.interval > MATURE_THRESHOLD:
+ return "mature"
+ return "young"
+
+ def cardIsNew(self, card):
+ "True if a card has never been seen before."
+ return card.reps == 0
+
+ def cardIsYoung(self, card):
+ "True if card is not new and not mature."
+ return (not self.cardIsNew(card) and
+ not self.cardIsMature(card))
+
+ def cardIsMature(self, card):
+ return card.interval >= MATURE_THRESHOLD
+
+ # Stats
+ ##########################################################################
+
+ def getETA(self, stats):
+ # rev + new cards first, account for failures
+ import traceback; traceback.print_stack()
+ count = stats['rev'] + stats['new']
+ count *= 1 + stats['gYoungNo%'] / 100.0
+ left = count * stats['dAverageTime']
+ # failed - higher time per card for higher amount of cards
+ failedBaseMulti = 1.5
+ failedMod = 0.07
+ failedBaseCount = 20
+ factor = (failedBaseMulti +
+ (failedMod * (stats['failed'] - failedBaseCount)))
+ left += stats['failed'] * stats['dAverageTime'] * factor
+ return left