mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 06:22:22 -04:00
refactor cards
Cards had developed quite a lot of cruft from incremental changes, and a number of important attributes were stored in names that had no bearing to their actual use. Added: - position, which new cards will be sorted on in the future - flags, which is reserved for future use Renamed: - type to queue - relativeDelay to type - noCount to lapses Removed: - all new/young/matureEase counts; the information is in the revlog - firstAnswered, lastDue, lastFactor, averageTime and totalTime for the same reason - isDue, spaceUntil and combinedDue, because they are no longer used. Spaced cards will be implemented differently in a coming commit. - priority - yesCount, because it can be inferred from reps & lapses - tags; they've been stored in facts for a long time now Also compatibility with deck versions less than 65 has been dropped, so decks will need to be upgraded to 1.2 before they can be upgraded by the dev code. All shared decks are on 1.2, so this should hopefully not be a problem.
This commit is contained in:
parent
f828393de3
commit
9aa2f8dc40
8 changed files with 235 additions and 440 deletions
214
anki/cards.py
214
anki/cards.py
|
@ -14,72 +14,50 @@ MAX_TIMER = 60
|
||||||
# Cards
|
# Cards
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
# Type: 0=lapsed, 1=due, 2=new, 3=drilled
|
||||||
|
# Queue: under normal circumstances, same as type.
|
||||||
|
# -1=suspended, -2=user buried, -3=sched buried (rev early, etc)
|
||||||
|
# Ordinal: card template # for fact
|
||||||
|
# Position: sorting position, only for new cards
|
||||||
|
# Flags: unused; reserved for future use
|
||||||
|
|
||||||
cardsTable = Table(
|
cardsTable = Table(
|
||||||
'cards', metadata,
|
'cards', metadata,
|
||||||
Column('id', Integer, primary_key=True),
|
Column('id', Integer, primary_key=True),
|
||||||
Column('factId', Integer, ForeignKey("facts.id"), nullable=False),
|
Column('factId', Integer, ForeignKey("facts.id"), nullable=False),
|
||||||
Column('cardModelId', Integer, ForeignKey("cardModels.id"), nullable=False),
|
Column('cardModelId', Integer, ForeignKey("cardModels.id"), nullable=False),
|
||||||
|
# general
|
||||||
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('tags', UnicodeText, nullable=False, default=u""),
|
|
||||||
Column('ordinal', Integer, nullable=False),
|
|
||||||
# q/a cached - changed on fact update
|
|
||||||
Column('question', UnicodeText, nullable=False, default=u""),
|
Column('question', UnicodeText, nullable=False, default=u""),
|
||||||
Column('answer', UnicodeText, nullable=False, default=u""),
|
Column('answer', UnicodeText, nullable=False, default=u""),
|
||||||
Column('priority', Integer, nullable=False, default=2), # obsolete
|
Column('flags', Integer, nullable=False, default=0),
|
||||||
Column('interval', Float, nullable=False, default=0),
|
# ordering
|
||||||
|
Column('ordinal', Integer, nullable=False),
|
||||||
|
Column('position', Integer, nullable=False),
|
||||||
|
# scheduling data
|
||||||
|
Column('type', Integer, nullable=False, default=2),
|
||||||
|
Column('queue', Integer, nullable=False, default=2),
|
||||||
Column('lastInterval', Float, nullable=False, default=0),
|
Column('lastInterval', Float, nullable=False, default=0),
|
||||||
Column('due', Float, nullable=False, default=time.time),
|
Column('interval', Float, nullable=False, default=0),
|
||||||
Column('lastDue', Float, nullable=False, default=0),
|
Column('due', Float, nullable=False),
|
||||||
Column('factor', Float, nullable=False, default=2.5),
|
Column('factor', Float, nullable=False, default=2.5),
|
||||||
Column('lastFactor', Float, nullable=False, default=2.5),
|
# counters
|
||||||
Column('firstAnswered', Float, nullable=False, default=0),
|
|
||||||
# stats
|
|
||||||
Column('reps', Integer, nullable=False, default=0),
|
Column('reps', Integer, nullable=False, default=0),
|
||||||
Column('successive', Integer, nullable=False, default=0),
|
Column('successive', Integer, nullable=False, default=0),
|
||||||
Column('averageTime', Float, nullable=False, default=0),
|
Column('lapses', Integer, nullable=False, default=0))
|
||||||
Column('reviewTime', Float, nullable=False, default=0),
|
|
||||||
Column('youngEase0', Integer, nullable=False, default=0),
|
|
||||||
Column('youngEase1', Integer, nullable=False, default=0),
|
|
||||||
Column('youngEase2', Integer, nullable=False, default=0),
|
|
||||||
Column('youngEase3', Integer, nullable=False, default=0),
|
|
||||||
Column('youngEase4', Integer, nullable=False, default=0),
|
|
||||||
Column('matureEase0', Integer, nullable=False, default=0),
|
|
||||||
Column('matureEase1', Integer, nullable=False, default=0),
|
|
||||||
Column('matureEase2', Integer, nullable=False, default=0),
|
|
||||||
Column('matureEase3', Integer, nullable=False, default=0),
|
|
||||||
Column('matureEase4', Integer, nullable=False, default=0),
|
|
||||||
# this duplicates the above data, because there's no way to map imported
|
|
||||||
# data to the above
|
|
||||||
Column('yesCount', Integer, nullable=False, default=0),
|
|
||||||
Column('noCount', Integer, nullable=False, default=0),
|
|
||||||
# obsolete
|
|
||||||
Column('spaceUntil', Float, nullable=False, default=0),
|
|
||||||
# relativeDelay is reused as type without scheduling (ie, it remains 0-2
|
|
||||||
# even if card is suspended, etc)
|
|
||||||
Column('relativeDelay', Float, nullable=False, default=0),
|
|
||||||
Column('isDue', Boolean, nullable=False, default=0), # obsolete
|
|
||||||
Column('type', Integer, nullable=False, default=2),
|
|
||||||
Column('combinedDue', Integer, nullable=False, default=0))
|
|
||||||
|
|
||||||
class Card(object):
|
class Card(object):
|
||||||
"A card."
|
|
||||||
|
|
||||||
def __init__(self, fact=None, cardModel=None, created=None):
|
def __init__(self, fact=None, cardModel=None, created=None):
|
||||||
self.tags = u""
|
|
||||||
self.id = genID()
|
self.id = genID()
|
||||||
# new cards start as new & due
|
|
||||||
self.type = 2
|
|
||||||
self.relativeDelay = self.type
|
|
||||||
self.timerStarted = False
|
|
||||||
self.timerStopped = False
|
|
||||||
self.modified = time.time()
|
self.modified = time.time()
|
||||||
if created:
|
if created:
|
||||||
self.created = created
|
self.created = created
|
||||||
self.due = created
|
self.due = created
|
||||||
else:
|
else:
|
||||||
self.due = self.modified
|
self.due = self.modified
|
||||||
self.combinedDue = self.due
|
self.position = self.due
|
||||||
if fact:
|
if fact:
|
||||||
self.fact = fact
|
self.fact = fact
|
||||||
if cardModel:
|
if cardModel:
|
||||||
|
@ -87,13 +65,27 @@ class Card(object):
|
||||||
# for non-orm use
|
# for non-orm use
|
||||||
self.cardModelId = cardModel.id
|
self.cardModelId = cardModel.id
|
||||||
self.ordinal = cardModel.ordinal
|
self.ordinal = cardModel.ordinal
|
||||||
|
# timer
|
||||||
|
self.timerStarted = None
|
||||||
|
|
||||||
|
def setModified(self):
|
||||||
|
self.modified = time.time()
|
||||||
|
|
||||||
|
def startTimer(self):
|
||||||
|
self.timerStarted = time.time()
|
||||||
|
|
||||||
|
def userTime(self):
|
||||||
|
return min(time.time() - self.timerStarted, MAX_TIMER)
|
||||||
|
|
||||||
|
# Questions and answers
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
def rebuildQA(self, deck, media=True):
|
def rebuildQA(self, deck, media=True):
|
||||||
# format qa
|
# format qa
|
||||||
d = {}
|
d = {}
|
||||||
for f in self.fact.model.fieldModels:
|
for f in self.fact.model.fieldModels:
|
||||||
d[f.name] = (f.id, self.fact[f.name])
|
d[f.name] = (f.id, self.fact[f.name])
|
||||||
qa = formatQA(None, self.fact.modelId, d, self.splitTags(),
|
qa = formatQA(None, self.fact.modelId, d, self._splitTags(),
|
||||||
self.cardModel, deck)
|
self.cardModel, deck)
|
||||||
# find old media references
|
# find old media references
|
||||||
files = {}
|
files = {}
|
||||||
|
@ -119,25 +111,6 @@ class Card(object):
|
||||||
updateMediaCount(deck, f, cnt)
|
updateMediaCount(deck, f, cnt)
|
||||||
self.setModified()
|
self.setModified()
|
||||||
|
|
||||||
def setModified(self):
|
|
||||||
self.modified = time.time()
|
|
||||||
|
|
||||||
def startTimer(self):
|
|
||||||
self.timerStarted = time.time()
|
|
||||||
|
|
||||||
def stopTimer(self):
|
|
||||||
self.timerStopped = time.time()
|
|
||||||
|
|
||||||
def thinkingTime(self):
|
|
||||||
return (self.timerStopped or time.time()) - self.timerStarted
|
|
||||||
|
|
||||||
def totalTime(self):
|
|
||||||
return time.time() - self.timerStarted
|
|
||||||
|
|
||||||
def genFuzz(self):
|
|
||||||
"Generate a random offset to spread intervals."
|
|
||||||
self.fuzz = random.uniform(0.95, 1.05)
|
|
||||||
|
|
||||||
def htmlQuestion(self, type="question", align=True):
|
def htmlQuestion(self, type="question", align=True):
|
||||||
div = '''<div class="card%s" id="cm%s%s">%s</div>''' % (
|
div = '''<div class="card%s" id="cm%s%s">%s</div>''' % (
|
||||||
type[0], type[0], hexifyID(self.cardModelId),
|
type[0], type[0], hexifyID(self.cardModelId),
|
||||||
|
@ -158,52 +131,14 @@ class Card(object):
|
||||||
def htmlAnswer(self, align=True):
|
def htmlAnswer(self, align=True):
|
||||||
return self.htmlQuestion(type="answer", align=align)
|
return self.htmlQuestion(type="answer", align=align)
|
||||||
|
|
||||||
def updateStats(self, ease, state):
|
def _splitTags(self):
|
||||||
self.reps += 1
|
|
||||||
if ease > 1:
|
|
||||||
self.successive += 1
|
|
||||||
else:
|
|
||||||
self.successive = 0
|
|
||||||
delay = min(self.totalTime(), MAX_TIMER)
|
|
||||||
self.reviewTime += delay
|
|
||||||
if self.averageTime:
|
|
||||||
self.averageTime = (self.averageTime + delay) / 2.0
|
|
||||||
else:
|
|
||||||
self.averageTime = delay
|
|
||||||
# we don't track first answer for cards
|
|
||||||
if state == "new":
|
|
||||||
state = "young"
|
|
||||||
# update ease and yes/no count
|
|
||||||
attr = state + "Ease%d" % ease
|
|
||||||
setattr(self, attr, getattr(self, attr) + 1)
|
|
||||||
if ease < 2:
|
|
||||||
self.noCount += 1
|
|
||||||
else:
|
|
||||||
self.yesCount += 1
|
|
||||||
if not self.firstAnswered:
|
|
||||||
self.firstAnswered = time.time()
|
|
||||||
self.setModified()
|
|
||||||
|
|
||||||
def splitTags(self):
|
|
||||||
return (self.fact.tags, self.fact.model.tags, self.cardModel.name)
|
return (self.fact.tags, self.fact.model.tags, self.cardModel.name)
|
||||||
|
|
||||||
def allTags(self):
|
# Non-ORM
|
||||||
"Non-canonified string of all tags."
|
##########################################################################
|
||||||
return (self.fact.tags + "," +
|
|
||||||
self.fact.model.tags)
|
|
||||||
|
|
||||||
def hasTag(self, tag):
|
|
||||||
return findTag(tag, parseTags(self.allTags()))
|
|
||||||
|
|
||||||
def fromDB(self, s, id):
|
def fromDB(self, s, id):
|
||||||
r = s.first("""select
|
r = s.first("""select * from cards where id = :id""", id=id)
|
||||||
id, factId, cardModelId, created, modified, tags, ordinal, question, answer,
|
|
||||||
priority, interval, lastInterval, due, lastDue, factor,
|
|
||||||
lastFactor, firstAnswered, reps, successive, averageTime, reviewTime,
|
|
||||||
youngEase0, youngEase1, youngEase2, youngEase3, youngEase4,
|
|
||||||
matureEase0, matureEase1, matureEase2, matureEase3, matureEase4,
|
|
||||||
yesCount, noCount, spaceUntil, isDue, type, combinedDue
|
|
||||||
from cards where id = :id""", id=id)
|
|
||||||
if not r:
|
if not r:
|
||||||
return
|
return
|
||||||
(self.id,
|
(self.id,
|
||||||
|
@ -211,74 +146,42 @@ from cards where id = :id""", id=id)
|
||||||
self.cardModelId,
|
self.cardModelId,
|
||||||
self.created,
|
self.created,
|
||||||
self.modified,
|
self.modified,
|
||||||
self.tags,
|
|
||||||
self.ordinal,
|
|
||||||
self.question,
|
self.question,
|
||||||
self.answer,
|
self.answer,
|
||||||
self.priority,
|
self.flags,
|
||||||
self.interval,
|
self.ordinal,
|
||||||
|
self.position,
|
||||||
|
self.type,
|
||||||
|
self.queue,
|
||||||
self.lastInterval,
|
self.lastInterval,
|
||||||
|
self.interval,
|
||||||
self.due,
|
self.due,
|
||||||
self.lastDue,
|
|
||||||
self.factor,
|
self.factor,
|
||||||
self.lastFactor,
|
|
||||||
self.firstAnswered,
|
|
||||||
self.reps,
|
self.reps,
|
||||||
self.successive,
|
self.successive,
|
||||||
self.averageTime,
|
self.lapses) = r
|
||||||
self.reviewTime,
|
|
||||||
self.youngEase0,
|
|
||||||
self.youngEase1,
|
|
||||||
self.youngEase2,
|
|
||||||
self.youngEase3,
|
|
||||||
self.youngEase4,
|
|
||||||
self.matureEase0,
|
|
||||||
self.matureEase1,
|
|
||||||
self.matureEase2,
|
|
||||||
self.matureEase3,
|
|
||||||
self.matureEase4,
|
|
||||||
self.yesCount,
|
|
||||||
self.noCount,
|
|
||||||
self.spaceUntil,
|
|
||||||
self.isDue,
|
|
||||||
self.type,
|
|
||||||
self.combinedDue) = r
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def toDB(self, s):
|
def toDB(self, s):
|
||||||
"Write card to DB."
|
|
||||||
s.execute("""update cards set
|
s.execute("""update cards set
|
||||||
|
factId=:factId,
|
||||||
|
cardModelId=:cardModelId,
|
||||||
|
created=:created,
|
||||||
modified=:modified,
|
modified=:modified,
|
||||||
tags=:tags,
|
question=:question,
|
||||||
interval=:interval,
|
answer=:answer,
|
||||||
|
flags=:flags,
|
||||||
|
ordinal=:ordinal,
|
||||||
|
position=:position,
|
||||||
|
type=:type,
|
||||||
|
queue=:queue,
|
||||||
lastInterval=:lastInterval,
|
lastInterval=:lastInterval,
|
||||||
|
interval=:interval,
|
||||||
due=:due,
|
due=:due,
|
||||||
lastDue=:lastDue,
|
|
||||||
factor=:factor,
|
factor=:factor,
|
||||||
lastFactor=:lastFactor,
|
|
||||||
firstAnswered=:firstAnswered,
|
|
||||||
reps=:reps,
|
reps=:reps,
|
||||||
successive=:successive,
|
successive=:successive,
|
||||||
averageTime=:averageTime,
|
lapses=:lapses
|
||||||
reviewTime=:reviewTime,
|
|
||||||
youngEase0=:youngEase0,
|
|
||||||
youngEase1=:youngEase1,
|
|
||||||
youngEase2=:youngEase2,
|
|
||||||
youngEase3=:youngEase3,
|
|
||||||
youngEase4=:youngEase4,
|
|
||||||
matureEase0=:matureEase0,
|
|
||||||
matureEase1=:matureEase1,
|
|
||||||
matureEase2=:matureEase2,
|
|
||||||
matureEase3=:matureEase3,
|
|
||||||
matureEase4=:matureEase4,
|
|
||||||
yesCount=:yesCount,
|
|
||||||
noCount=:noCount,
|
|
||||||
spaceUntil = :spaceUntil,
|
|
||||||
isDue = 0,
|
|
||||||
type = :type,
|
|
||||||
combinedDue = :combinedDue,
|
|
||||||
relativeDelay = :relativeDelay,
|
|
||||||
priority = :priority
|
|
||||||
where id=:id""", self.__dict__)
|
where id=:id""", self.__dict__)
|
||||||
|
|
||||||
mapper(Card, cardsTable, properties={
|
mapper(Card, cardsTable, properties={
|
||||||
|
@ -292,7 +195,6 @@ mapper(Fact, factsTable, properties={
|
||||||
'fields': relation(Field, backref="fact", order_by=Field.ordinal),
|
'fields': relation(Field, backref="fact", order_by=Field.ordinal),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
# Card deletions
|
# Card deletions
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
216
anki/deck.py
216
anki/deck.py
|
@ -14,7 +14,7 @@ from anki.utils import parseTags, tidyHTML, genID, ids2str, hexifyID, \
|
||||||
from anki.revlog import logReview
|
from anki.revlog import logReview
|
||||||
from anki.models import Model, CardModel, formatQA
|
from anki.models import Model, CardModel, formatQA
|
||||||
from anki.fonts import toPlatformFont
|
from anki.fonts import toPlatformFont
|
||||||
from anki.tags import initTagTables, tagIds
|
from anki.tags import initTagTables, tagIds, tagId
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
from anki.hooks import runHook, hookEmpty
|
from anki.hooks import runHook, hookEmpty
|
||||||
|
@ -242,22 +242,22 @@ where time > :t""", t=self.failedCutoff-86400)
|
||||||
self.failedSoonCount = self.db.scalar(
|
self.failedSoonCount = self.db.scalar(
|
||||||
self.cardLimit(
|
self.cardLimit(
|
||||||
"revActive", "revInactive",
|
"revActive", "revInactive",
|
||||||
"select count(*) from cards c where type = 0 "
|
"select count(*) from cards c where queue = 0 "
|
||||||
"and combinedDue < :lim"), lim=self.failedCutoff)
|
"and due < :lim"), lim=self.failedCutoff)
|
||||||
|
|
||||||
def _rebuildRevCount(self):
|
def _rebuildRevCount(self):
|
||||||
self.revCount = self.db.scalar(
|
self.revCount = self.db.scalar(
|
||||||
self.cardLimit(
|
self.cardLimit(
|
||||||
"revActive", "revInactive",
|
"revActive", "revInactive",
|
||||||
"select count(*) from cards c where type = 1 "
|
"select count(*) from cards c where queue = 1 "
|
||||||
"and combinedDue < :lim"), lim=self.dueCutoff)
|
"and due < :lim"), lim=self.dueCutoff)
|
||||||
|
|
||||||
def _rebuildNewCount(self):
|
def _rebuildNewCount(self):
|
||||||
self.newAvail = self.db.scalar(
|
self.newAvail = self.db.scalar(
|
||||||
self.cardLimit(
|
self.cardLimit(
|
||||||
"newActive", "newInactive",
|
"newActive", "newInactive",
|
||||||
"select count(*) from cards c where type = 2 "
|
"select count(*) from cards c where queue = 2 "
|
||||||
"and combinedDue < :lim"), lim=self.dueCutoff)
|
"and due < :lim"), lim=self.dueCutoff)
|
||||||
self.updateNewCountToday()
|
self.updateNewCountToday()
|
||||||
self.spacedCards = []
|
self.spacedCards = []
|
||||||
|
|
||||||
|
@ -271,8 +271,8 @@ where time > :t""", t=self.failedCutoff-86400)
|
||||||
self.failedQueue = self.db.all(
|
self.failedQueue = self.db.all(
|
||||||
self.cardLimit(
|
self.cardLimit(
|
||||||
"revActive", "revInactive", """
|
"revActive", "revInactive", """
|
||||||
select c.id, factId, combinedDue from cards c where
|
select c.id, factId, due from cards c where
|
||||||
type = 0 and combinedDue < :lim order by combinedDue
|
queue = 0 and due < :lim order by due
|
||||||
limit %d""" % self.queueLimit), lim=self.failedCutoff)
|
limit %d""" % self.queueLimit), lim=self.failedCutoff)
|
||||||
self.failedQueue.reverse()
|
self.failedQueue.reverse()
|
||||||
|
|
||||||
|
@ -282,7 +282,7 @@ limit %d""" % self.queueLimit), lim=self.failedCutoff)
|
||||||
self.cardLimit(
|
self.cardLimit(
|
||||||
"revActive", "revInactive", """
|
"revActive", "revInactive", """
|
||||||
select c.id, factId from cards c where
|
select c.id, factId from cards c where
|
||||||
type = 1 and combinedDue < :lim order by %s
|
queue = 1 and due < :lim order by %s
|
||||||
limit %d""" % (self.revOrder(), self.queueLimit)), lim=self.dueCutoff)
|
limit %d""" % (self.revOrder(), self.queueLimit)), lim=self.dueCutoff)
|
||||||
self.revQueue.reverse()
|
self.revQueue.reverse()
|
||||||
|
|
||||||
|
@ -292,7 +292,7 @@ limit %d""" % (self.revOrder(), self.queueLimit)), lim=self.dueCutoff)
|
||||||
self.cardLimit(
|
self.cardLimit(
|
||||||
"newActive", "newInactive", """
|
"newActive", "newInactive", """
|
||||||
select c.id, factId from cards c where
|
select c.id, factId from cards c where
|
||||||
type = 2 and combinedDue < :lim order by %s
|
queue = 2 and due < :lim order by %s
|
||||||
limit %d""" % (self.newOrder(), self.queueLimit)), lim=self.dueCutoff)
|
limit %d""" % (self.newOrder(), self.queueLimit)), lim=self.dueCutoff)
|
||||||
self.newQueue.reverse()
|
self.newQueue.reverse()
|
||||||
|
|
||||||
|
@ -365,7 +365,7 @@ New type: %s""" % (self.failedSoonCount, self.revCount, self.newCount,
|
||||||
def revOrder(self):
|
def revOrder(self):
|
||||||
return ("interval desc",
|
return ("interval desc",
|
||||||
"interval",
|
"interval",
|
||||||
"combinedDue",
|
"due",
|
||||||
"factId, ordinal")[self.revCardOrder]
|
"factId, ordinal")[self.revCardOrder]
|
||||||
|
|
||||||
def newOrder(self):
|
def newOrder(self):
|
||||||
|
@ -375,18 +375,15 @@ New type: %s""" % (self.failedSoonCount, self.revCount, self.newCount,
|
||||||
|
|
||||||
def rebuildTypes(self):
|
def rebuildTypes(self):
|
||||||
"Rebuild the type cache. Only necessary on upgrade."
|
"Rebuild the type cache. Only necessary on upgrade."
|
||||||
# set canonical type first
|
# set type first
|
||||||
self.db.statement("""
|
self.db.statement("""
|
||||||
update cards set
|
update cards set type = (case
|
||||||
relativeDelay = (case
|
|
||||||
when successive then 1 when reps then 0 else 2 end)
|
when successive then 1 when reps then 0 else 2 end)
|
||||||
""")
|
""")
|
||||||
# then current type based on that
|
# then queue
|
||||||
self.db.statement("""
|
self.db.statement("""
|
||||||
update cards set
|
update cards set queue = type
|
||||||
type = (case
|
when queue != -1""")
|
||||||
when type >= 0 then relativeDelay else relativeDelay - 3 end)
|
|
||||||
""")
|
|
||||||
|
|
||||||
def updateAllFieldChecksums(self):
|
def updateAllFieldChecksums(self):
|
||||||
# zero out
|
# zero out
|
||||||
|
@ -490,12 +487,12 @@ when type >= 0 then relativeDelay else relativeDelay - 3 end)
|
||||||
def _reviewEarlyPreSave(self, card, ease):
|
def _reviewEarlyPreSave(self, card, ease):
|
||||||
if ease > 1:
|
if ease > 1:
|
||||||
# prevent it from appearing in next queue fill
|
# prevent it from appearing in next queue fill
|
||||||
card.type += 6
|
card.queue = -3
|
||||||
|
|
||||||
def resetAfterReviewEarly(self):
|
def resetAfterReviewEarly(self):
|
||||||
"Put temporarily suspended cards back into play. Caller must .reset()"
|
"Put temporarily suspended cards back into play. Caller must .reset()"
|
||||||
self.db.statement(
|
self.db.statement(
|
||||||
"update cards set type = type - 6 where type between 6 and 8")
|
"update cards set queue = type where queue = -3")
|
||||||
|
|
||||||
def _onReviewEarlyFinished(self):
|
def _onReviewEarlyFinished(self):
|
||||||
# clean up buried cards
|
# clean up buried cards
|
||||||
|
@ -508,7 +505,7 @@ when type >= 0 then relativeDelay else relativeDelay - 3 end)
|
||||||
self.revCount = self.db.scalar(
|
self.revCount = self.db.scalar(
|
||||||
self.cardLimit(
|
self.cardLimit(
|
||||||
"revActive", "revInactive", """
|
"revActive", "revInactive", """
|
||||||
select count() from cards c where type = 1 and combinedDue > :now
|
select count() from cards c where queue = 1 and due > :now
|
||||||
"""), now=self.dueCutoff)
|
"""), now=self.dueCutoff)
|
||||||
|
|
||||||
def _fillRevEarlyQueue(self):
|
def _fillRevEarlyQueue(self):
|
||||||
|
@ -516,8 +513,8 @@ select count() from cards c where type = 1 and combinedDue > :now
|
||||||
self.revQueue = self.db.all(
|
self.revQueue = self.db.all(
|
||||||
self.cardLimit(
|
self.cardLimit(
|
||||||
"revActive", "revInactive", """
|
"revActive", "revInactive", """
|
||||||
select id, factId from cards c where type = 1 and combinedDue > :lim
|
select id, factId from cards c where queue = 1 and due > :lim
|
||||||
order by combinedDue limit %d""" % self.queueLimit), lim=self.dueCutoff)
|
order by due limit %d""" % self.queueLimit), lim=self.dueCutoff)
|
||||||
self.revQueue.reverse()
|
self.revQueue.reverse()
|
||||||
|
|
||||||
# Learn more
|
# Learn more
|
||||||
|
@ -533,8 +530,8 @@ order by combinedDue limit %d""" % self.queueLimit), lim=self.dueCutoff)
|
||||||
self.newAvail = self.db.scalar(
|
self.newAvail = self.db.scalar(
|
||||||
self.cardLimit(
|
self.cardLimit(
|
||||||
"newActive", "newInactive",
|
"newActive", "newInactive",
|
||||||
"select count(*) from cards c where type = 2 "
|
"select count(*) from cards c where queue = 2 "
|
||||||
"and combinedDue < :lim"), lim=self.dueCutoff)
|
"and due < :lim"), lim=self.dueCutoff)
|
||||||
self.spacedCards = []
|
self.spacedCards = []
|
||||||
|
|
||||||
def _updateLearnMoreCountToday(self):
|
def _updateLearnMoreCountToday(self):
|
||||||
|
@ -566,7 +563,7 @@ order by combinedDue limit %d""" % self.queueLimit), lim=self.dueCutoff)
|
||||||
def _cramPreSave(self, card, ease):
|
def _cramPreSave(self, card, ease):
|
||||||
# prevent it from appearing in next queue fill
|
# prevent it from appearing in next queue fill
|
||||||
card.lastInterval = self.cramLastInterval
|
card.lastInterval = self.cramLastInterval
|
||||||
card.type += 6
|
card.type = -3
|
||||||
|
|
||||||
def _spaceCramCards(self, card):
|
def _spaceCramCards(self, card):
|
||||||
self.spacedFacts[card.factId] = time.time() + self.newSpacing
|
self.spacedFacts[card.factId] = time.time() + self.newSpacing
|
||||||
|
@ -634,7 +631,7 @@ order by combinedDue limit %d""" % self.queueLimit), lim=self.dueCutoff)
|
||||||
self.revQueue = self.db.all(self.cardLimit(
|
self.revQueue = self.db.all(self.cardLimit(
|
||||||
self.activeCramTags, "", """
|
self.activeCramTags, "", """
|
||||||
select id, factId from cards c
|
select id, factId from cards c
|
||||||
where type between 0 and 2
|
where queue between 0 and 2
|
||||||
order by %s
|
order by %s
|
||||||
limit %s""" % (self.cramOrder, self.queueLimit)))
|
limit %s""" % (self.cramOrder, self.queueLimit)))
|
||||||
self.revQueue.reverse()
|
self.revQueue.reverse()
|
||||||
|
@ -642,7 +639,7 @@ limit %s""" % (self.cramOrder, self.queueLimit)))
|
||||||
def _rebuildCramCount(self):
|
def _rebuildCramCount(self):
|
||||||
self.revCount = self.db.scalar(self.cardLimit(
|
self.revCount = self.db.scalar(self.cardLimit(
|
||||||
self.activeCramTags, "",
|
self.activeCramTags, "",
|
||||||
"select count(*) from cards c where type between 0 and 2"))
|
"select count(*) from cards c where queue between 0 and 2"))
|
||||||
|
|
||||||
def _rebuildFailedCramCount(self):
|
def _rebuildFailedCramCount(self):
|
||||||
self.failedSoonCount = len(self.failedCramQueue)
|
self.failedSoonCount = len(self.failedCramQueue)
|
||||||
|
@ -754,7 +751,7 @@ limit %s""" % (self.cramOrder, self.queueLimit)))
|
||||||
if not card.fromDB(self.db, id):
|
if not card.fromDB(self.db, id):
|
||||||
return
|
return
|
||||||
card.deck = self
|
card.deck = self
|
||||||
card.genFuzz()
|
#card.genFuzz()
|
||||||
card.startTimer()
|
card.startTimer()
|
||||||
return card
|
return card
|
||||||
|
|
||||||
|
@ -768,7 +765,7 @@ limit %s""" % (self.cramOrder, self.queueLimit)))
|
||||||
# old state
|
# old state
|
||||||
oldState = self.cardState(card)
|
oldState = self.cardState(card)
|
||||||
oldQueue = self.cardQueue(card)
|
oldQueue = self.cardQueue(card)
|
||||||
lastDelaySecs = time.time() - card.combinedDue
|
lastDelaySecs = time.time() - card.due
|
||||||
lastDelay = lastDelaySecs / 86400.0
|
lastDelay = lastDelaySecs / 86400.0
|
||||||
oldSuc = card.successive
|
oldSuc = card.successive
|
||||||
# update card details
|
# update card details
|
||||||
|
@ -779,8 +776,6 @@ limit %s""" % (self.cramOrder, self.queueLimit)))
|
||||||
# only update if card was not new
|
# only update if card was not new
|
||||||
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.lastFactor = card.factor
|
|
||||||
card.spaceUntil = 0
|
card.spaceUntil = 0
|
||||||
if not self.finishScheduler:
|
if not self.finishScheduler:
|
||||||
# don't update factor in custom schedulers
|
# don't update factor in custom schedulers
|
||||||
|
@ -798,17 +793,17 @@ limit %s""" % (self.cramOrder, self.queueLimit)))
|
||||||
else:
|
else:
|
||||||
self.newAvail -= 1
|
self.newAvail -= 1
|
||||||
# card stats
|
# card stats
|
||||||
anki.cards.Card.updateStats(card, ease, oldState)
|
self.updateCardStats(card, ease, oldState)
|
||||||
# update type & ensure past cutoff
|
# update type & ensure past cutoff
|
||||||
card.type = self.cardType(card)
|
card.type = self.cardType(card)
|
||||||
card.relativeDelay = card.type
|
card.queue = card.type
|
||||||
if ease != 1:
|
if ease != 1:
|
||||||
card.due = max(card.due, self.dueCutoff+1)
|
card.due = max(card.due, self.dueCutoff+1)
|
||||||
# allow custom schedulers to munge the card
|
# allow custom schedulers to munge the card
|
||||||
if self.answerPreSave:
|
if self.answerPreSave:
|
||||||
self.answerPreSave(card, ease)
|
self.answerPreSave(card, ease)
|
||||||
# save
|
# save
|
||||||
card.combinedDue = card.due
|
card.due = card.due
|
||||||
card.toDB(self.db)
|
card.toDB(self.db)
|
||||||
# review history
|
# review history
|
||||||
print "make sure flags is set correctly when reviewing early"
|
print "make sure flags is set correctly when reviewing early"
|
||||||
|
@ -824,28 +819,39 @@ limit %s""" % (self.cramOrder, self.queueLimit)))
|
||||||
runHook("cardAnswered", card.id, isLeech)
|
runHook("cardAnswered", card.id, isLeech)
|
||||||
self.setUndoEnd(undoName)
|
self.setUndoEnd(undoName)
|
||||||
|
|
||||||
|
def updateCardStats(self, card, ease, state):
|
||||||
|
card.reps += 1
|
||||||
|
if ease == 1:
|
||||||
|
card.successive = 0
|
||||||
|
card.lapses += 1
|
||||||
|
else:
|
||||||
|
card.successive += 1
|
||||||
|
# if not card.firstAnswered:
|
||||||
|
# card.firstAnswered = time.time()
|
||||||
|
card.setModified()
|
||||||
|
|
||||||
def _spaceCards(self, card):
|
def _spaceCards(self, card):
|
||||||
new = time.time() + self.newSpacing
|
new = time.time() + self.newSpacing
|
||||||
self.db.statement("""
|
self.db.statement("""
|
||||||
update cards set
|
update cards set
|
||||||
combinedDue = (case
|
due = (case
|
||||||
when type = 1 then combinedDue + 86400 * (case
|
when queue = 1 then due + 86400 * (case
|
||||||
when interval*:rev < 1 then 0
|
when interval*:rev < 1 then 0
|
||||||
else interval*:rev
|
else interval*:rev
|
||||||
end)
|
end)
|
||||||
when type = 2 then :new
|
when queue = 2 then :new
|
||||||
end),
|
end),
|
||||||
modified = :now, isDue = 0
|
modified = :now
|
||||||
where id != :id and factId = :factId
|
where id != :id and factId = :factId
|
||||||
and combinedDue < :cut
|
and due < :cut
|
||||||
and type between 1 and 2""",
|
and queue between 1 and 2""",
|
||||||
id=card.id, now=time.time(), factId=card.factId,
|
id=card.id, now=time.time(), factId=card.factId,
|
||||||
cut=self.dueCutoff, new=new, rev=self.revSpacing)
|
cut=self.dueCutoff, new=new, rev=self.revSpacing)
|
||||||
# update local cache of seen facts
|
# update local cache of seen facts
|
||||||
self.spacedFacts[card.factId] = new
|
self.spacedFacts[card.factId] = new
|
||||||
|
|
||||||
def isLeech(self, card):
|
def isLeech(self, card):
|
||||||
no = card.noCount
|
no = card.lapses
|
||||||
fmax = self.getInt('leechFails')
|
fmax = self.getInt('leechFails')
|
||||||
if not fmax:
|
if not fmax:
|
||||||
return
|
return
|
||||||
|
@ -885,8 +891,6 @@ and type between 1 and 2""",
|
||||||
factor = card.factor
|
factor = card.factor
|
||||||
# if cramming / reviewing early
|
# if cramming / reviewing early
|
||||||
if delay < 0:
|
if delay < 0:
|
||||||
# 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
|
||||||
|
@ -950,7 +954,7 @@ and type between 1 and 2""",
|
||||||
|
|
||||||
def updateFactor(self, card, ease):
|
def updateFactor(self, card, ease):
|
||||||
"Update CARD's factor based on EASE."
|
"Update CARD's factor based on EASE."
|
||||||
card.lastFactor = card.factor
|
print "update cardIsBeingLearnt()"
|
||||||
if not card.reps:
|
if not card.reps:
|
||||||
# card is new, inherit beginning factor
|
# card is new, inherit beginning factor
|
||||||
card.factor = self.averageFactor
|
card.factor = self.averageFactor
|
||||||
|
@ -967,24 +971,26 @@ and type between 1 and 2""",
|
||||||
"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
|
||||||
if card.reps and not card.successive:
|
if card.due <= time.time():
|
||||||
return 0
|
return (time.time() - card.due) / 86400.0
|
||||||
if card.combinedDue <= self.dueCutoff:
|
|
||||||
return (self.dueCutoff - card.due) / 86400.0
|
|
||||||
else:
|
else:
|
||||||
return (self.dueCutoff - card.combinedDue) / 86400.0
|
return (time.time() - card.due) / 86400.0
|
||||||
|
|
||||||
def resetCards(self, ids):
|
def resetCards(self, ids=None):
|
||||||
"Reset progress on cards in IDS."
|
"Reset progress on cards in IDS."
|
||||||
self.db.statement("""
|
print "position in resetCards()"
|
||||||
update cards set interval = :new, lastInterval = 0, lastDue = 0,
|
sql = """
|
||||||
factor = 2.5, reps = 0, successive = 0, averageTime = 0, reviewTime = 0,
|
update cards set modified=:now, position=0, type=2, queue=2, lastInterval=0,
|
||||||
youngEase0 = 0, youngEase1 = 0, youngEase2 = 0, youngEase3 = 0,
|
interval=0, due=created, factor=2.5, reps=0, successive=0, lapses=0, flags=0"""
|
||||||
youngEase4 = 0, matureEase0 = 0, matureEase1 = 0, matureEase2 = 0,
|
sql2 = "delete from revlog"
|
||||||
matureEase3 = 0,matureEase4 = 0, yesCount = 0, noCount = 0,
|
if ids is None:
|
||||||
spaceUntil = 0, type = 2, relativeDelay = 2,
|
lim = ""
|
||||||
combinedDue = created, modified = :now, due = created, isDue = 0
|
else:
|
||||||
where id in %s""" % ids2str(ids), now=time.time(), new=0)
|
sids = ids2str(ids)
|
||||||
|
sql += " where id in "+sids
|
||||||
|
sql2 += " where cardId in "+sids
|
||||||
|
self.db.statement(sql, now=time.time())
|
||||||
|
self.db.statement(sql2)
|
||||||
if self.newCardOrder == NEW_CARDS_RANDOM:
|
if self.newCardOrder == NEW_CARDS_RANDOM:
|
||||||
# we need to re-randomize now
|
# we need to re-randomize now
|
||||||
self.randomizeNewCards(ids)
|
self.randomizeNewCards(ids)
|
||||||
|
@ -1004,19 +1010,17 @@ where id in %s""" % ids2str(ids), now=time.time(), new=0)
|
||||||
self.db.statements("""
|
self.db.statements("""
|
||||||
update cards
|
update cards
|
||||||
set due = :rand + ordinal,
|
set due = :rand + ordinal,
|
||||||
combinedDue = :rand + ordinal,
|
|
||||||
modified = :now
|
modified = :now
|
||||||
where factId = :fid
|
where factId = :fid
|
||||||
and relativeDelay = 2""", data)
|
and type = 2""", data)
|
||||||
|
|
||||||
def orderNewCards(self):
|
def orderNewCards(self):
|
||||||
"Set 'due' to card creation time."
|
"Set 'due' to card creation time."
|
||||||
self.db.statement("""
|
self.db.statement("""
|
||||||
update cards set
|
update cards set
|
||||||
due = created,
|
due = created,
|
||||||
combinedDue = created,
|
|
||||||
modified = :now
|
modified = :now
|
||||||
where relativeDelay = 2""", now=time.time())
|
where type = 2""", now=time.time())
|
||||||
|
|
||||||
def rescheduleCards(self, ids, min, max):
|
def rescheduleCards(self, ids, min, max):
|
||||||
"Reset cards and schedule with new interval in days (min, max)."
|
"Reset cards and schedule with new interval in days (min, max)."
|
||||||
|
@ -1034,14 +1038,12 @@ where relativeDelay = 2""", now=time.time())
|
||||||
update cards set
|
update cards set
|
||||||
interval = :int,
|
interval = :int,
|
||||||
due = :due,
|
due = :due,
|
||||||
combinedDue = :due,
|
|
||||||
reps = 1,
|
reps = 1,
|
||||||
successive = 1,
|
successive = 1,
|
||||||
yesCount = 1,
|
yesCount = 1,
|
||||||
firstAnswered = :t,
|
firstAnswered = :t,
|
||||||
|
queue = 1,
|
||||||
type = 1,
|
type = 1,
|
||||||
relativeDelay = 1,
|
|
||||||
isDue = 0
|
|
||||||
where id = :id""", vals)
|
where id = :id""", vals)
|
||||||
self.flushMod()
|
self.flushMod()
|
||||||
|
|
||||||
|
@ -1079,12 +1081,12 @@ This may be in the past if the deck is not finished.
|
||||||
If the deck has no (enabled) cards, return None.
|
If the deck has no (enabled) cards, return None.
|
||||||
Ignore new cards."""
|
Ignore new cards."""
|
||||||
earliestRev = self.db.scalar(self.cardLimit("revActive", "revInactive", """
|
earliestRev = self.db.scalar(self.cardLimit("revActive", "revInactive", """
|
||||||
select combinedDue from cards c where type = 1
|
select due from cards c where queue = 1
|
||||||
order by combinedDue
|
order by due
|
||||||
limit 1"""))
|
limit 1"""))
|
||||||
earliestFail = self.db.scalar(self.cardLimit("revActive", "revInactive", """
|
earliestFail = self.db.scalar(self.cardLimit("revActive", "revInactive", """
|
||||||
select combinedDue+%d from cards c where type = 0
|
select due+%d from cards c where queue = 0
|
||||||
order by combinedDue
|
order by due
|
||||||
limit 1""" % self.delay0))
|
limit 1""" % self.delay0))
|
||||||
if earliestRev and earliestFail:
|
if earliestRev and earliestFail:
|
||||||
return min(earliestRev, earliestFail)
|
return min(earliestRev, earliestFail)
|
||||||
|
@ -1107,16 +1109,16 @@ limit 1""" % self.delay0))
|
||||||
return self.db.scalar(
|
return self.db.scalar(
|
||||||
self.cardLimit(
|
self.cardLimit(
|
||||||
"revActive", "revInactive",
|
"revActive", "revInactive",
|
||||||
"select count(*) from cards c where type between 0 and 1 "
|
"select count(*) from cards c where queue between 0 and 1 "
|
||||||
"and combinedDue < :lim"), lim=time)
|
"and due < :lim"), lim=time)
|
||||||
|
|
||||||
def newCardsDueBy(self, time):
|
def newCardsDueBy(self, time):
|
||||||
"Number of new cards due at TIME."
|
"Number of new cards due at TIME."
|
||||||
return self.db.scalar(
|
return self.db.scalar(
|
||||||
self.cardLimit(
|
self.cardLimit(
|
||||||
"newActive", "newInactive",
|
"newActive", "newInactive",
|
||||||
"select count(*) from cards c where type = 2 "
|
"select count(*) from cards c where queue = 2 "
|
||||||
"and combinedDue < :lim"), lim=time)
|
"and due < :lim"), lim=time)
|
||||||
|
|
||||||
def deckFinishedMsg(self):
|
def deckFinishedMsg(self):
|
||||||
spaceSusp = ""
|
spaceSusp = ""
|
||||||
|
@ -1151,9 +1153,8 @@ limit 1""" % self.delay0))
|
||||||
self.startProgress()
|
self.startProgress()
|
||||||
self.db.statement("""
|
self.db.statement("""
|
||||||
update cards
|
update cards
|
||||||
set type = relativeDelay - 3,
|
set queue = -1, modified = :t
|
||||||
modified = :t
|
where id in %s""" % ids2str(ids), t=time.time())
|
||||||
where type >= 0 and id in %s""" % ids2str(ids), t=time.time())
|
|
||||||
self.flushMod()
|
self.flushMod()
|
||||||
self.finishProgress()
|
self.finishProgress()
|
||||||
|
|
||||||
|
@ -1161,8 +1162,8 @@ where type >= 0 and id in %s""" % ids2str(ids), t=time.time())
|
||||||
"Unsuspend cards. Caller must .reset()"
|
"Unsuspend cards. Caller must .reset()"
|
||||||
self.startProgress()
|
self.startProgress()
|
||||||
self.db.statement("""
|
self.db.statement("""
|
||||||
update cards set type = relativeDelay, modified=:t
|
update cards set queue = type, modified=:t
|
||||||
where type between -3 and -1 and id in %s""" %
|
where queue = -1 and id in %s""" %
|
||||||
ids2str(ids), t=time.time())
|
ids2str(ids), t=time.time())
|
||||||
self.flushMod()
|
self.flushMod()
|
||||||
self.finishProgress()
|
self.finishProgress()
|
||||||
|
@ -1170,8 +1171,8 @@ where type between -3 and -1 and id in %s""" %
|
||||||
def buryFact(self, fact):
|
def buryFact(self, fact):
|
||||||
"Bury all cards for fact until next session. Caller must .reset()"
|
"Bury all cards for fact until next session. Caller must .reset()"
|
||||||
for card in fact.cards:
|
for card in fact.cards:
|
||||||
if card.type in (0,1,2):
|
if card.queue in (0,1,2):
|
||||||
card.type += 3
|
card.queue = -2
|
||||||
self.flushMod()
|
self.flushMod()
|
||||||
|
|
||||||
# Counts
|
# Counts
|
||||||
|
@ -1180,14 +1181,16 @@ where type between -3 and -1 and id in %s""" %
|
||||||
def hiddenCards(self):
|
def hiddenCards(self):
|
||||||
"Assumes queue finished. True if some due cards have not been shown."
|
"Assumes queue finished. True if some due cards have not been shown."
|
||||||
return self.db.scalar("""
|
return self.db.scalar("""
|
||||||
select 1 from cards where combinedDue < :now
|
select 1 from cards where due < :now
|
||||||
and type between 0 and 1 limit 1""", now=self.dueCutoff)
|
and queue between 0 and 1 limit 1""", now=self.dueCutoff)
|
||||||
|
|
||||||
def spacedCardCount(self):
|
def spacedCardCount(self):
|
||||||
"Number of spaced cards."
|
"Number of spaced cards."
|
||||||
|
print "spacedCardCount"
|
||||||
|
return 0
|
||||||
return self.db.scalar("""
|
return self.db.scalar("""
|
||||||
select count(cards.id) from cards where
|
select count(cards.id) from cards where
|
||||||
combinedDue > :now and due < :now""", now=time.time())
|
due > :now and due < :now""", now=time.time())
|
||||||
|
|
||||||
def isEmpty(self):
|
def isEmpty(self):
|
||||||
return not self.cardCount
|
return not self.cardCount
|
||||||
|
@ -1205,11 +1208,11 @@ combinedDue > :now and due < :now""", now=time.time())
|
||||||
def newCountAll(self):
|
def newCountAll(self):
|
||||||
"All new cards, including spaced."
|
"All new cards, including spaced."
|
||||||
return self.db.scalar(
|
return self.db.scalar(
|
||||||
"select count(id) from cards where relativeDelay = 2")
|
"select count(id) from cards where type = 2")
|
||||||
|
|
||||||
def seenCardCount(self):
|
def seenCardCount(self):
|
||||||
return self.db.scalar(
|
return self.db.scalar(
|
||||||
"select count(id) from cards where relativeDelay between 0 and 1")
|
"select count(id) from cards where type between 0 and 1")
|
||||||
|
|
||||||
# Card predicates
|
# Card predicates
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -1225,10 +1228,6 @@ combinedDue > :now and due < :now""", now=time.time())
|
||||||
"True if a card has never been seen before."
|
"True if a card has never been seen before."
|
||||||
return card.reps == 0
|
return card.reps == 0
|
||||||
|
|
||||||
def cardIsBeingLearnt(self, card):
|
|
||||||
"True if card should use present intervals."
|
|
||||||
return card.lastInterval < 7
|
|
||||||
|
|
||||||
def cardIsYoung(self, card):
|
def cardIsYoung(self, card):
|
||||||
"True if card is not new and not mature."
|
"True if card is not new and not mature."
|
||||||
return (not self.cardIsNew(card) and
|
return (not self.cardIsNew(card) and
|
||||||
|
@ -1290,7 +1289,6 @@ combinedDue > :now and due < :now""", now=time.time())
|
||||||
card = anki.cards.Card(fact, cardModel, created)
|
card = anki.cards.Card(fact, cardModel, created)
|
||||||
if isRandom:
|
if isRandom:
|
||||||
card.due = due
|
card.due = due
|
||||||
card.combinedDue = due
|
|
||||||
self.flushMod()
|
self.flushMod()
|
||||||
cards.append(card)
|
cards.append(card)
|
||||||
# update card q/a
|
# update card q/a
|
||||||
|
@ -1981,6 +1979,13 @@ and cards.factId = facts.id""")
|
||||||
select id, tags from facts
|
select id, tags from facts
|
||||||
where id in %s""" % ids2str(ids))
|
where id in %s""" % ids2str(ids))
|
||||||
|
|
||||||
|
def cardHasTag(self, card, tag):
|
||||||
|
id = tagId(self.db, tag, create=False)
|
||||||
|
if id:
|
||||||
|
return self.db.scalar(
|
||||||
|
"select 1 from cardTags where cardId = :c and tagId = :t",
|
||||||
|
c=card.id, t=id)
|
||||||
|
|
||||||
# Tags: caching
|
# Tags: caching
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
@ -2542,20 +2547,21 @@ select cardId from cardTags where cardTags.tagId in %s""" % ids2str(ids)
|
||||||
n = 0
|
n = 0
|
||||||
qquery += "select id from cards where type = %d" % n
|
qquery += "select id from cards where type = %d" % n
|
||||||
elif token == "delayed":
|
elif token == "delayed":
|
||||||
|
print "delayed"
|
||||||
qquery += ("select id from cards where "
|
qquery += ("select id from cards where "
|
||||||
"due < %d and combinedDue > %d and "
|
"due < %d and due > %d and "
|
||||||
"type in (0,1,2)") % (
|
"type in (0,1,2)") % (
|
||||||
self.dueCutoff, self.dueCutoff)
|
self.dueCutoff, self.dueCutoff)
|
||||||
elif token == "suspended":
|
elif token == "suspended":
|
||||||
qquery += ("select id from cards where "
|
qquery += ("select id from cards where "
|
||||||
"type between -3 and -1")
|
"queue = -1")
|
||||||
elif token == "leech":
|
elif token == "leech":
|
||||||
qquery += (
|
qquery += (
|
||||||
"select id from cards where noCount >= (select value "
|
"select id from cards where noCount >= (select value "
|
||||||
"from deckvars where key = 'leechFails')")
|
"from deckvars where key = 'leechFails')")
|
||||||
else: # due
|
else: # due
|
||||||
qquery += ("select id from cards where "
|
qquery += ("select id from cards where "
|
||||||
"type in (0,1) and combinedDue < %d") % self.dueCutoff
|
"queue between 0 and 1 and due < %d") % self.dueCutoff
|
||||||
elif type == SEARCH_FID:
|
elif type == SEARCH_FID:
|
||||||
if fidquery:
|
if fidquery:
|
||||||
if isNeg:
|
if isNeg:
|
||||||
|
@ -3436,17 +3442,15 @@ seq > :s and seq <= :e order by seq desc""", s=start, e=end)
|
||||||
def updateDynamicIndices(self):
|
def updateDynamicIndices(self):
|
||||||
indices = {
|
indices = {
|
||||||
'intervalDesc':
|
'intervalDesc':
|
||||||
'(type, interval desc, factId, combinedDue)',
|
'(queue, interval desc, factId, due)',
|
||||||
'intervalAsc':
|
'intervalAsc':
|
||||||
'(type, interval, factId, combinedDue)',
|
'(queue, interval, factId, due)',
|
||||||
'randomOrder':
|
'randomOrder':
|
||||||
'(type, factId, ordinal, combinedDue)',
|
'(queue, factId, ordinal, due)',
|
||||||
# new cards are sorted by due, not combinedDue, so that even if
|
|
||||||
# they are spaced, they retain their original sort order
|
|
||||||
'dueAsc':
|
'dueAsc':
|
||||||
'(type, due, factId, combinedDue)',
|
'(queue, position, factId, due)',
|
||||||
'dueDesc':
|
'dueDesc':
|
||||||
'(type, due desc, factId, combinedDue)',
|
'(queue, position desc, factId, due)',
|
||||||
}
|
}
|
||||||
# determine required
|
# determine required
|
||||||
required = []
|
required = []
|
||||||
|
@ -3532,7 +3536,7 @@ class DeckStorage(object):
|
||||||
metadata.create_all(engine)
|
metadata.create_all(engine)
|
||||||
deck = DeckStorage._init(s)
|
deck = DeckStorage._init(s)
|
||||||
else:
|
else:
|
||||||
ver = upgradeSchema(s)
|
ver = upgradeSchema(engine, s)
|
||||||
# add any possibly new tables if we're upgrading
|
# add any possibly new tables if we're upgrading
|
||||||
if ver < DECK_VERSION:
|
if ver < DECK_VERSION:
|
||||||
metadata.create_all(engine)
|
metadata.create_all(engine)
|
||||||
|
@ -3601,7 +3605,7 @@ class DeckStorage(object):
|
||||||
deck.delay1 = 0
|
deck.delay1 = 0
|
||||||
# unsuspend buried/rev early
|
# unsuspend buried/rev early
|
||||||
deck.db.statement(
|
deck.db.statement(
|
||||||
"update cards set type = relativeDelay where type > 2")
|
"update cards set queue = type where queue between -3 and -2")
|
||||||
deck.db.commit()
|
deck.db.commit()
|
||||||
# check if deck has been moved, and disable syncing
|
# check if deck has been moved, and disable syncing
|
||||||
deck.checkSyncHash()
|
deck.checkSyncHash()
|
||||||
|
|
|
@ -94,38 +94,7 @@ class AnkiExporter(Exporter):
|
||||||
res = server.applyPayload(payload)
|
res = server.applyPayload(payload)
|
||||||
if not self.includeSchedulingInfo:
|
if not self.includeSchedulingInfo:
|
||||||
self.deck.updateProgress()
|
self.deck.updateProgress()
|
||||||
self.newDeck.db.statement("""
|
self.newDeck.resetCards()
|
||||||
delete from revlog""")
|
|
||||||
self.newDeck.db.statement("""
|
|
||||||
update cards set
|
|
||||||
interval = 0,
|
|
||||||
lastInterval = 0,
|
|
||||||
due = created,
|
|
||||||
lastDue = 0,
|
|
||||||
factor = 2.5,
|
|
||||||
firstAnswered = 0,
|
|
||||||
reps = 0,
|
|
||||||
successive = 0,
|
|
||||||
averageTime = 0,
|
|
||||||
reviewTime = 0,
|
|
||||||
youngEase0 = 0,
|
|
||||||
youngEase1 = 0,
|
|
||||||
youngEase2 = 0,
|
|
||||||
youngEase3 = 0,
|
|
||||||
youngEase4 = 0,
|
|
||||||
matureEase0 = 0,
|
|
||||||
matureEase1 = 0,
|
|
||||||
matureEase2 = 0,
|
|
||||||
matureEase3 = 0,
|
|
||||||
matureEase4 = 0,
|
|
||||||
yesCount = 0,
|
|
||||||
noCount = 0,
|
|
||||||
spaceUntil = 0,
|
|
||||||
type = 2,
|
|
||||||
relativeDelay = 2,
|
|
||||||
combinedDue = created,
|
|
||||||
modified = :now
|
|
||||||
""", now=time.time())
|
|
||||||
# media
|
# media
|
||||||
if self.includeMedia:
|
if self.includeMedia:
|
||||||
server.deck.mediaPrefix = ""
|
server.deck.mediaPrefix = ""
|
||||||
|
|
|
@ -65,11 +65,11 @@ class DeckGraphs(object):
|
||||||
self.endOfDay = self.deck.failedCutoff
|
self.endOfDay = self.deck.failedCutoff
|
||||||
t = time.time()
|
t = time.time()
|
||||||
young = """
|
young = """
|
||||||
select interval, combinedDue from cards c
|
select interval, due from cards c
|
||||||
where relativeDelay between 0 and 1 and type >= 0 and interval <= 21"""
|
where queue between 0 and 1 and interval <= 21"""
|
||||||
mature = """
|
mature = """
|
||||||
select interval, combinedDue
|
select interval, due
|
||||||
from cards c where relativeDelay = 1 and type >= 0 and interval > 21"""
|
from cards c where queue = 1 and interval > 21"""
|
||||||
if self.selective:
|
if self.selective:
|
||||||
young = self.deck._cardLimit("revActive", "revInactive",
|
young = self.deck._cardLimit("revActive", "revInactive",
|
||||||
young)
|
young)
|
||||||
|
@ -238,8 +238,13 @@ group by day order by day
|
||||||
days = {}
|
days = {}
|
||||||
fig = Figure(figsize=(self.width, self.height), dpi=self.dpi)
|
fig = Figure(figsize=(self.width, self.height), dpi=self.dpi)
|
||||||
limit = self.endOfDay - (numdays) * 86400
|
limit = self.endOfDay - (numdays) * 86400
|
||||||
|
if attr == "created":
|
||||||
res = self.deck.db.column0("select %s from cards where %s >= %f" %
|
res = self.deck.db.column0("select %s from cards where %s >= %f" %
|
||||||
(attr, attr, limit))
|
(attr, attr, limit))
|
||||||
|
else:
|
||||||
|
# firstAnswered
|
||||||
|
res = self.deck.db.column0(
|
||||||
|
"select time from revlog where rep = 1")
|
||||||
for r in res:
|
for r in res:
|
||||||
d = int((r - self.endOfDay) / 86400.0)
|
d = int((r - self.endOfDay) / 86400.0)
|
||||||
days[d] = days.get(d, 0) + 1
|
days[d] = days.get(d, 0) + 1
|
||||||
|
|
|
@ -307,7 +307,7 @@ where factId in (%s)""" % ",".join([str(s) for s in factIds]))
|
||||||
else:
|
else:
|
||||||
t = 2
|
t = 2
|
||||||
data['type'] = t
|
data['type'] = t
|
||||||
data['relativeDelay'] = t
|
data['queue'] = t
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def stripInvalid(self, cards):
|
def stripInvalid(self, cards):
|
||||||
|
|
|
@ -28,5 +28,5 @@ insert into revlog values (
|
||||||
:userTime, :flags)""",
|
:userTime, :flags)""",
|
||||||
created=time.time(), cardId=card.id, ease=ease, rep=card.reps,
|
created=time.time(), cardId=card.id, ease=ease, rep=card.reps,
|
||||||
lastInterval=card.lastInterval, interval=card.interval,
|
lastInterval=card.lastInterval, interval=card.interval,
|
||||||
factor=card.factor, userTime=card.thinkingTime(),
|
factor=card.factor, userTime=card.userTime(),
|
||||||
flags=flags)
|
flags=flags)
|
||||||
|
|
|
@ -24,11 +24,13 @@ class CardStats(object):
|
||||||
fmtFloat = anki.utils.fmtFloat
|
fmtFloat = anki.utils.fmtFloat
|
||||||
self.txt = "<table>"
|
self.txt = "<table>"
|
||||||
self.addLine(_("Added"), self.strTime(c.created))
|
self.addLine(_("Added"), self.strTime(c.created))
|
||||||
if c.firstAnswered:
|
first = self.deck.db.scalar(
|
||||||
self.addLine(_("First Review"), self.strTime(c.firstAnswered))
|
"select time from revlog where rep = 1 and cardId = :id", id=c.id)
|
||||||
|
if first:
|
||||||
|
self.addLine(_("First Review"), self.strTime(first))
|
||||||
self.addLine(_("Changed"), self.strTime(c.modified))
|
self.addLine(_("Changed"), self.strTime(c.modified))
|
||||||
if c.reps:
|
if c.reps:
|
||||||
next = time.time() - c.combinedDue
|
next = time.time() - c.due
|
||||||
if next > 0:
|
if next > 0:
|
||||||
next = _("%s ago") % fmt(next)
|
next = _("%s ago") % fmt(next)
|
||||||
else:
|
else:
|
||||||
|
@ -36,20 +38,14 @@ class CardStats(object):
|
||||||
self.addLine(_("Due"), next)
|
self.addLine(_("Due"), next)
|
||||||
self.addLine(_("Interval"), fmt(c.interval * 86400))
|
self.addLine(_("Interval"), fmt(c.interval * 86400))
|
||||||
self.addLine(_("Ease"), fmtFloat(c.factor, point=2))
|
self.addLine(_("Ease"), fmtFloat(c.factor, point=2))
|
||||||
if c.lastDue:
|
|
||||||
last = _("%s ago") % fmt(time.time() - c.lastDue)
|
|
||||||
self.addLine(_("Last Due"), last)
|
|
||||||
if c.interval != c.lastInterval:
|
|
||||||
# don't show the last interval if it hasn't been updated yet
|
|
||||||
self.addLine(_("Last Interval"), fmt(c.lastInterval * 86400))
|
|
||||||
self.addLine(_("Last Ease"), fmtFloat(c.lastFactor, point=2))
|
|
||||||
if c.reps:
|
if c.reps:
|
||||||
self.addLine(_("Reviews"), "%d/%d (s=%d)" % (
|
self.addLine(_("Reviews"), "%d/%d (s=%d)" % (
|
||||||
c.yesCount, c.reps, c.successive))
|
c.reps-c.lapses, c.reps, c.successive))
|
||||||
avg = fmt(c.averageTime, point=2)
|
(cnt, total) = self.deck.db.first(
|
||||||
self.addLine(_("Average Time"),avg)
|
"select count(), sum(userTime) from revlog where cardId = :id", id=c.id)
|
||||||
total = fmt(c.reviewTime, point=2)
|
if cnt:
|
||||||
self.addLine(_("Total Time"), total)
|
self.addLine(_("Average Time"), fmt(total / float(cnt), point=2))
|
||||||
|
self.addLine(_("Total Time"), fmt(total, point=2))
|
||||||
self.addLine(_("Model Tags"), c.fact.model.tags)
|
self.addLine(_("Model Tags"), c.fact.model.tags)
|
||||||
self.addLine(_("Card Template") + " "*5, c.cardModel.name)
|
self.addLine(_("Card Template") + " "*5, c.cardModel.name)
|
||||||
self.txt += "</table>"
|
self.txt += "</table>"
|
||||||
|
@ -244,10 +240,10 @@ class DeckStats(object):
|
||||||
return (all, yes, yes/float(all)*100)
|
return (all, yes, yes/float(all)*100)
|
||||||
|
|
||||||
def getYoungCorrect(self):
|
def getYoungCorrect(self):
|
||||||
return self.getMatureCorrect("lastInterval <= 21 and reps != 1")
|
return self.getMatureCorrect("lastInterval <= 21 and rep != 1")
|
||||||
|
|
||||||
def getNewCorrect(self):
|
def getNewCorrect(self):
|
||||||
return self.getMatureCorrect("reps = 1")
|
return self.getMatureCorrect("rep = 1")
|
||||||
|
|
||||||
def getDaysReviewed(self, start, finish):
|
def getDaysReviewed(self, start, finish):
|
||||||
today = self.deck.failedCutoff
|
today = self.deck.failedCutoff
|
||||||
|
@ -308,14 +304,14 @@ where time >= :x and time <= :y""",x=x,y=y, off=self.deck.utcOffset)
|
||||||
return self.deck.db.scalar(
|
return self.deck.db.scalar(
|
||||||
"select sum(1/round(max(interval, 1)+0.5)) from cards "
|
"select sum(1/round(max(interval, 1)+0.5)) from cards "
|
||||||
"where cards.reps > 0 "
|
"where cards.reps > 0 "
|
||||||
"and type >= 0") or 0
|
"and queue != -1") or 0
|
||||||
|
|
||||||
def getWorkloadPeriod(self, period):
|
def getWorkloadPeriod(self, period):
|
||||||
cutoff = time.time() + 86400 * period
|
cutoff = time.time() + 86400 * period
|
||||||
return (self.deck.db.scalar("""
|
return (self.deck.db.scalar("""
|
||||||
select count(id) from cards
|
select count(id) from cards
|
||||||
where combinedDue < :cutoff
|
where due < :cutoff
|
||||||
and type >= 0 and relativeDelay in (0,1)""", cutoff=cutoff) or 0) / float(period)
|
and queue != -1 and type between 0 and 1""", cutoff=cutoff) or 0) / float(period)
|
||||||
|
|
||||||
def getPastWorkloadPeriod(self, period):
|
def getPastWorkloadPeriod(self, period):
|
||||||
cutoff = time.time() - 86400 * period
|
cutoff = time.time() - 86400 * period
|
||||||
|
|
155
anki/upgrade.py
155
anki/upgrade.py
|
@ -2,12 +2,13 @@
|
||||||
# Copyright: Damien Elmes <anki@ichi2.net>
|
# Copyright: Damien Elmes <anki@ichi2.net>
|
||||||
# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html
|
# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html
|
||||||
|
|
||||||
DECK_VERSION = 74
|
DECK_VERSION = 75
|
||||||
|
|
||||||
|
from anki.db import *
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
from anki.media import rebuildMediaDir
|
from anki.media import rebuildMediaDir
|
||||||
|
|
||||||
def upgradeSchema(s):
|
def upgradeSchema(engine, s):
|
||||||
"Alter tables prior to ORM initialization."
|
"Alter tables prior to ORM initialization."
|
||||||
ver = s.scalar("select version from decks limit 1")
|
ver = s.scalar("select version from decks limit 1")
|
||||||
# add a checksum to fields
|
# add a checksum to fields
|
||||||
|
@ -18,19 +19,36 @@ def upgradeSchema(s):
|
||||||
"not null default ''")
|
"not null default ''")
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
if ver < 75:
|
||||||
|
# copy cards into new temporary table
|
||||||
|
sql = s.scalar(
|
||||||
|
"select sql from sqlite_master where name = 'cards'")
|
||||||
|
sql = sql.replace("TABLE cards", "temporary table cards2")
|
||||||
|
s.execute(sql)
|
||||||
|
s.execute("insert into cards2 select * from cards")
|
||||||
|
# drop the old cards table and create the new one
|
||||||
|
s.execute("drop table cards")
|
||||||
|
import cards
|
||||||
|
metadata.create_all(engine, tables=[cards.cardsTable])
|
||||||
|
# move data across and delete temp table
|
||||||
|
s.execute("""
|
||||||
|
insert into cards select id, factId, cardModelId, created, modified,
|
||||||
|
question, answer, 0, ordinal, 0, relativeDelay, type, lastInterval, interval,
|
||||||
|
due, factor, reps, successive, noCount from cards2""")
|
||||||
|
s.execute("drop table cards2")
|
||||||
return ver
|
return ver
|
||||||
|
|
||||||
def updateIndices(deck):
|
def updateIndices(deck):
|
||||||
"Add indices to the DB."
|
"Add indices to the DB."
|
||||||
# counts, failed cards
|
# due counts, failed card queue
|
||||||
deck.db.statement("""
|
deck.db.statement("""
|
||||||
create index if not exists ix_cards_typeCombined on cards
|
create index if not exists ix_cards_queueDue on cards
|
||||||
(type, combinedDue, factId)""")
|
(queue, due, factId)""")
|
||||||
# scheduler-agnostic type
|
# counting cards of a given type
|
||||||
deck.db.statement("""
|
deck.db.statement("""
|
||||||
create index if not exists ix_cards_relativeDelay on cards
|
create index if not exists ix_cards_type on cards
|
||||||
(relativeDelay)""")
|
(type)""")
|
||||||
# index on modified, to speed up sync summaries
|
# sync summaries
|
||||||
deck.db.statement("""
|
deck.db.statement("""
|
||||||
create index if not exists ix_cards_modified on cards
|
create index if not exists ix_cards_modified on cards
|
||||||
(modified)""")
|
(modified)""")
|
||||||
|
@ -88,114 +106,8 @@ def upgradeDeck(deck):
|
||||||
oldmod = deck.modified
|
oldmod = deck.modified
|
||||||
else:
|
else:
|
||||||
prog = False
|
prog = False
|
||||||
if deck.version < 43:
|
|
||||||
raise Exception("oldDeckVersion")
|
|
||||||
if deck.version < 44:
|
|
||||||
# leaner indices
|
|
||||||
deck.db.statement("drop index if exists ix_cards_factId")
|
|
||||||
deck.version = 44
|
|
||||||
deck.db.commit()
|
|
||||||
if deck.version < 48:
|
|
||||||
deck.updateFieldCache(deck.db.column0("select id from facts"))
|
|
||||||
deck.version = 48
|
|
||||||
deck.db.commit()
|
|
||||||
if deck.version < 52:
|
|
||||||
dname = deck.name()
|
|
||||||
sname = deck.syncName
|
|
||||||
if sname and dname != sname:
|
|
||||||
deck.notify(_("""\
|
|
||||||
When syncing, Anki now uses the same deck name on the server as the deck \
|
|
||||||
name on your computer. Because you had '%(dname)s' set to sync to \
|
|
||||||
'%(sname)s' on the server, syncing has been temporarily disabled.
|
|
||||||
|
|
||||||
If you want to keep your changes to the online version, please use \
|
|
||||||
File>Download>Personal Deck to download the online version.
|
|
||||||
|
|
||||||
If you want to keep the version on your computer, please enable \
|
|
||||||
syncing again via Settings>Deck Properties>Synchronisation.
|
|
||||||
|
|
||||||
If you have syncing disabled in the preferences, you can ignore \
|
|
||||||
this message. (ERR-0101)""") % {
|
|
||||||
'sname':sname, 'dname':dname})
|
|
||||||
deck.disableSyncing()
|
|
||||||
elif sname:
|
|
||||||
deck.enableSyncing()
|
|
||||||
deck.version = 52
|
|
||||||
deck.db.commit()
|
|
||||||
if deck.version < 53:
|
|
||||||
if deck.getBool("perDay"):
|
|
||||||
if deck.hardIntervalMin == 0.333:
|
|
||||||
deck.hardIntervalMin = max(1.0, deck.hardIntervalMin)
|
|
||||||
deck.hardIntervalMax = max(1.1, deck.hardIntervalMax)
|
|
||||||
deck.version = 53
|
|
||||||
deck.db.commit()
|
|
||||||
if deck.version < 54:
|
|
||||||
# broken versions of the DB orm die if this is a bool with a
|
|
||||||
# non-int value
|
|
||||||
deck.db.statement("update fieldModels set editFontFamily = 1");
|
|
||||||
deck.version = 54
|
|
||||||
deck.db.commit()
|
|
||||||
if deck.version < 61:
|
|
||||||
# do our best to upgrade templates to the new style
|
|
||||||
txt = '''\
|
|
||||||
<span style="font-family: %s; font-size: %spx; color: %s; white-space: pre-wrap;">%s</span>'''
|
|
||||||
for m in deck.models:
|
|
||||||
unstyled = []
|
|
||||||
for fm in m.fieldModels:
|
|
||||||
# find which fields had explicit formatting
|
|
||||||
if fm.quizFontFamily or fm.quizFontSize or fm.quizFontColour:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
unstyled.append(fm.name)
|
|
||||||
# fill out missing info
|
|
||||||
fm.quizFontFamily = fm.quizFontFamily or u"Arial"
|
|
||||||
fm.quizFontSize = fm.quizFontSize or 20
|
|
||||||
fm.quizFontColour = fm.quizFontColour or "#000000"
|
|
||||||
fm.editFontSize = fm.editFontSize or 20
|
|
||||||
unstyled = set(unstyled)
|
|
||||||
for cm in m.cardModels:
|
|
||||||
# embed the old font information into card templates
|
|
||||||
cm.qformat = txt % (
|
|
||||||
cm.questionFontFamily,
|
|
||||||
cm.questionFontSize,
|
|
||||||
cm.questionFontColour,
|
|
||||||
cm.qformat)
|
|
||||||
cm.aformat = txt % (
|
|
||||||
cm.answerFontFamily,
|
|
||||||
cm.answerFontSize,
|
|
||||||
cm.answerFontColour,
|
|
||||||
cm.aformat)
|
|
||||||
# escape fields that had no previous styling
|
|
||||||
for un in unstyled:
|
|
||||||
cm.qformat = cm.qformat.replace("%("+un+")s", "{{{%s}}}"%un)
|
|
||||||
cm.aformat = cm.aformat.replace("%("+un+")s", "{{{%s}}}"%un)
|
|
||||||
# rebuild q/a for the above & because latex has changed
|
|
||||||
for m in deck.models:
|
|
||||||
deck.updateCardsFromModel(m, dirty=False)
|
|
||||||
# rebuild the media db based on new format
|
|
||||||
rebuildMediaDir(deck, dirty=False)
|
|
||||||
deck.version = 61
|
|
||||||
deck.db.commit()
|
|
||||||
if deck.version < 62:
|
|
||||||
# updated indices
|
|
||||||
deck.db.statement("drop index if exists ix_cards_typeCombined")
|
|
||||||
updateIndices(deck)
|
|
||||||
deck.version = 62
|
|
||||||
deck.db.commit()
|
|
||||||
if deck.version < 64:
|
|
||||||
# remove old static indices, as all clients should be libanki1.2+
|
|
||||||
for d in ("ix_cards_duePriority",
|
|
||||||
"ix_cards_priorityDue"):
|
|
||||||
deck.db.statement("drop index if exists %s" % d)
|
|
||||||
deck.version = 64
|
|
||||||
deck.db.commit()
|
|
||||||
# note: we keep the priority index for now
|
|
||||||
if deck.version < 65:
|
if deck.version < 65:
|
||||||
# we weren't correctly setting relativeDelay when answering cards
|
raise Exception("oldDeckVersion")
|
||||||
# in previous versions, so ensure everything is set correctly
|
|
||||||
deck.rebuildTypes()
|
|
||||||
deck.version = 65
|
|
||||||
deck.db.commit()
|
|
||||||
# skip a few to allow for updates to stable tree
|
# skip a few to allow for updates to stable tree
|
||||||
if deck.version < 70:
|
if deck.version < 70:
|
||||||
# update dynamic indices given we don't use priority anymore
|
# update dynamic indices given we don't use priority anymore
|
||||||
|
@ -228,7 +140,6 @@ this message. (ERR-0101)""") % {
|
||||||
deck.db.commit()
|
deck.db.commit()
|
||||||
if deck.version < 73:
|
if deck.version < 73:
|
||||||
# remove stats, as it's all in the revlog now
|
# remove stats, as it's all in the revlog now
|
||||||
deck.db.statement("drop index if exists ix_stats_typeDay")
|
|
||||||
deck.db.statement("drop table if exists stats")
|
deck.db.statement("drop table if exists stats")
|
||||||
deck.version = 73
|
deck.version = 73
|
||||||
deck.db.commit()
|
deck.db.commit()
|
||||||
|
@ -245,7 +156,15 @@ min(thinkingTime, 60), 0 from reviewHistory""")
|
||||||
deck.db.statement("drop index if exists ix_cards_priority")
|
deck.db.statement("drop index if exists ix_cards_priority")
|
||||||
deck.version = 74
|
deck.version = 74
|
||||||
deck.db.commit()
|
deck.db.commit()
|
||||||
|
if deck.version < 75:
|
||||||
|
# suspended cards don't use ranges anymore
|
||||||
|
deck.db.execute("update cards set queue=-1 where queue between -3 and -1")
|
||||||
|
deck.db.execute("update cards set queue=-2 where queue between 3 and 5")
|
||||||
|
deck.db.execute("update cards set queue=-3 where queue between 6 and 8")
|
||||||
|
# new indices for new cards table
|
||||||
|
updateIndices(deck)
|
||||||
|
deck.version = 75
|
||||||
|
deck.db.commit()
|
||||||
|
|
||||||
# executing a pragma here is very slow on large decks, so we store
|
# executing a pragma here is very slow on large decks, so we store
|
||||||
# our own record
|
# our own record
|
||||||
|
|
Loading…
Reference in a new issue