mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
more type handling updates; don't munge counts on sync
In various parts of the code we need to get all cards of a given category (new, failed, etc) regardless of whether they're suspended, buried, etc. So we store the true type in the obsolete relativeDelay column and add in index for it, because it's cheaper than putting indices on reps & successive.
This commit is contained in:
parent
268d2645fd
commit
b69fd48768
8 changed files with 65 additions and 45 deletions
|
@ -60,7 +60,9 @@ cardsTable = Table(
|
|||
Column('noCount', Integer, nullable=False, default=0),
|
||||
# caching
|
||||
Column('spaceUntil', Float, nullable=False, default=0),
|
||||
Column('relativeDelay', Float, nullable=False, default=0), # obsolete
|
||||
# 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))
|
||||
|
@ -73,6 +75,7 @@ class Card(object):
|
|||
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()
|
||||
|
|
61
anki/deck.py
61
anki/deck.py
|
@ -68,7 +68,7 @@ SEARCH_FIELD = 6
|
|||
SEARCH_FIELD_EXISTS = 7
|
||||
SEARCH_QA = 8
|
||||
SEARCH_PHRASE_WB = 9
|
||||
DECK_VERSION = 49
|
||||
DECK_VERSION = 50
|
||||
|
||||
deckVarsTable = Table(
|
||||
'deckVars', metadata,
|
||||
|
@ -357,14 +357,12 @@ Card info: %d %d %d""" % (self.failedSoonCount, self.revCount, self.newCountToda
|
|||
else:
|
||||
where = " where " + lim
|
||||
self.s.statement("""
|
||||
update cards
|
||||
set type = (case
|
||||
when successive = 0 and reps != 0
|
||||
then 0 -- failed
|
||||
when successive != 0 and reps != 0
|
||||
then 1 -- review
|
||||
else 2 -- new
|
||||
end)""" + where)
|
||||
update cards set
|
||||
type = (case
|
||||
when successive then 1 when reps then 0 else 2 end),
|
||||
relativeDelay = (case
|
||||
when successive then 1 when reps then 0 else 2 end)
|
||||
""")
|
||||
# old-style suspended cards
|
||||
self.s.statement(
|
||||
"update cards set type = type - 3 where priority = 0 and type >= 0")
|
||||
|
@ -788,6 +786,7 @@ where id in """
|
|||
anki.cards.Card.updateStats(card, ease, oldState)
|
||||
# update type & ensure past cutoff
|
||||
card.type = self.cardType(card)
|
||||
card.relativeDelay = card.type
|
||||
if ease != 1:
|
||||
card.due = max(card.due, self.dueCutoff+1)
|
||||
# allow custom schedulers to munge the card
|
||||
|
@ -981,7 +980,7 @@ factor = 2.5, 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,
|
||||
spaceUntil = 0, type = 2, relativeDelay = 2,
|
||||
combinedDue = created, modified = :now, due = created
|
||||
where id in %s""" % ids2str(ids), now=time.time(), new=0)
|
||||
if self.newCardOrder == NEW_CARDS_RANDOM:
|
||||
|
@ -1006,7 +1005,7 @@ set due = :rand + ordinal,
|
|||
combinedDue = max(:rand + ordinal, spaceUntil),
|
||||
modified = :now
|
||||
where factId = :fid
|
||||
and type = 2""", data)
|
||||
and relativeDelay = 2""", data)
|
||||
|
||||
def orderNewCards(self):
|
||||
"Set 'due' to card creation time."
|
||||
|
@ -1015,7 +1014,7 @@ update cards set
|
|||
due = created,
|
||||
combinedDue = max(spaceUntil, created),
|
||||
modified = :now
|
||||
where type = 2""", now=time.time())
|
||||
where relativeDelay = 2""", now=time.time())
|
||||
|
||||
def rescheduleCards(self, ids, min, max):
|
||||
"Reset cards and schedule with new interval in days (min, max)."
|
||||
|
@ -1038,7 +1037,8 @@ reps = 1,
|
|||
successive = 1,
|
||||
yesCount = 1,
|
||||
firstAnswered = :t,
|
||||
type = 1
|
||||
type = 1,
|
||||
relativeDelay = 1
|
||||
where id = :id""", vals)
|
||||
self.flushMod()
|
||||
|
||||
|
@ -1048,8 +1048,9 @@ where id = :id""", vals)
|
|||
def nextDueMsg(self):
|
||||
next = self.earliestTime()
|
||||
if next:
|
||||
newCount = self.s.scalar(
|
||||
"select count() from cards where type = 2")
|
||||
# all new cards except suspended
|
||||
newCount = self.s.scalar("""
|
||||
select count() from cards where relativeDelay = 2 and type != -1""")
|
||||
newCardsTomorrow = min(newCount, self.newCardsPerDay)
|
||||
cards = self.cardsDueBy(time.time() + 86400)
|
||||
msg = _('''\
|
||||
|
@ -1216,15 +1217,14 @@ group by cardTags.cardId""" % limit)
|
|||
# Suspending
|
||||
##########################################################################
|
||||
|
||||
# when older clients are upgraded, we can move the code which touches
|
||||
# when older clients are upgraded, we can remove the code which touches
|
||||
# priorities & isDue
|
||||
|
||||
def suspendCards(self, ids):
|
||||
self.startProgress()
|
||||
self.s.statement("""
|
||||
update cards
|
||||
set type = (case
|
||||
when successive then -2 when reps then -3 else -1 end),
|
||||
set type = relativeDelay - 3,
|
||||
priority = -3, modified = :t, isDue=0
|
||||
where type >= 0 and id in %s""" % ids2str(ids), t=time.time())
|
||||
self.flushMod()
|
||||
|
@ -1234,7 +1234,7 @@ where type >= 0 and id in %s""" % ids2str(ids), t=time.time())
|
|||
def unsuspendCards(self, ids):
|
||||
self.startProgress()
|
||||
self.s.statement("""
|
||||
update cards set type = type + 3, priority=0, modified=:t
|
||||
update cards set type = relativeDelay, priority=0, modified=:t
|
||||
where type < 0 and id in %s""" %
|
||||
ids2str(ids), t=time.time())
|
||||
self.updatePriorities(ids)
|
||||
|
@ -1279,7 +1279,7 @@ select count(id) from cards where type < 0""")
|
|||
"Number of spaced new cards."
|
||||
return self.s.scalar("""
|
||||
select count(cards.id) from cards where
|
||||
type = 2 and combinedDue > :now
|
||||
relativeDelay = 2 and combinedDue > :now
|
||||
and due < :now""", now=time.time())
|
||||
|
||||
def isEmpty(self):
|
||||
|
@ -1298,7 +1298,7 @@ and due < :now""", now=time.time())
|
|||
def newCountAll(self):
|
||||
"All new cards, including spaced."
|
||||
return self.s.scalar(
|
||||
"select count(id) from cards where type = 2")
|
||||
"select count(id) from cards where relativeDelay = 2")
|
||||
|
||||
# Card predicates
|
||||
##########################################################################
|
||||
|
@ -3565,9 +3565,7 @@ update cards set type = type - 3 where type between 0 and 2 and priority = -3"""
|
|||
if ids:
|
||||
deck.updatePriorities(ids)
|
||||
deck.s.statement(
|
||||
"update cards set type = type - 3 where type between 3 and 5")
|
||||
deck.s.statement(
|
||||
"update cards set type = type - 6 where type between 6 and 8")
|
||||
"update cards set type = relativeDelay where type > 2")
|
||||
deck.s.commit()
|
||||
# determine starting factor for new cards
|
||||
deck.averageFactor = (deck.s.scalar(
|
||||
|
@ -3620,11 +3618,15 @@ update cards set type = type - 3 where type between 0 and 2 and priority = -3"""
|
|||
deck.s.statement("""
|
||||
create index if not exists ix_cards_typeCombined on cards
|
||||
(type, combinedDue)""")
|
||||
# failed cards, review early
|
||||
# scheduler-agnostic type
|
||||
deck.s.statement("""
|
||||
create index if not exists ix_cards_relativeDelay on cards
|
||||
(relativeDelay)""")
|
||||
# failed cards, review early - obsolete
|
||||
deck.s.statement("""
|
||||
create index if not exists ix_cards_duePriority on cards
|
||||
(type, isDue, combinedDue, priority)""")
|
||||
# check due
|
||||
# check due - obsolete
|
||||
deck.s.statement("""
|
||||
create index if not exists ix_cards_priorityDue on cards
|
||||
(type, isDue, priority, combinedDue)""")
|
||||
|
@ -4119,6 +4121,13 @@ nextFactor, reps, thinkingTime, yesCount, noCount from reviewHistory""")
|
|||
deck.rebuildTypes()
|
||||
deck.version = 49
|
||||
deck.s.commit()
|
||||
if deck.version < 50:
|
||||
# more new type handling
|
||||
deck.rebuildTypes()
|
||||
# add an index for relativeDelay (type cache)
|
||||
DeckStorage._addIndices(deck)
|
||||
deck.version = 50
|
||||
deck.s.commit()
|
||||
# executing a pragma here is very slow on large decks, so we store
|
||||
# our own record
|
||||
if not deck.getInt("pageSize") == 4096:
|
||||
|
|
|
@ -129,6 +129,7 @@ yesCount = 0,
|
|||
noCount = 0,
|
||||
spaceUntil = 0,
|
||||
type = 2,
|
||||
relativeDelay = 2,
|
||||
combinedDue = created,
|
||||
modified = :now
|
||||
""", now=time.time())
|
||||
|
|
|
@ -79,10 +79,10 @@ class DeckGraphs(object):
|
|||
t = time.time()
|
||||
young = self.deck.s.all("""
|
||||
select interval, combinedDue
|
||||
from cards where type in (0, 1) and interval <= 21""")
|
||||
from cards where relativeDelay between 0 and 1 and interval <= 21""")
|
||||
mature = self.deck.s.all("""
|
||||
select interval, combinedDue
|
||||
from cards where type = 1 and interval > 21""")
|
||||
from cards where relativeDelay = 1 and interval > 21""")
|
||||
|
||||
for (src, dest) in [(young, daysYoung),
|
||||
(mature, daysMature)]:
|
||||
|
|
|
@ -191,8 +191,8 @@ where factId in (%s)""" % ",".join([str(s) for s in factIds]))
|
|||
'cardModelId': cm.id,
|
||||
'ordinal': cm.ordinal,
|
||||
'question': u"",
|
||||
'answer': u"",
|
||||
'type': 2},cards[m]) for m in range(len(cards))]
|
||||
'answer': u""
|
||||
},cards[m]) for m in range(len(cards))]
|
||||
self.deck.s.execute(cardsTable.insert(),
|
||||
data)
|
||||
self.deck.updateProgress()
|
||||
|
@ -212,6 +212,14 @@ where factId in (%s)""" % ",".join([str(s) for s in factIds]))
|
|||
data['tags'] = u""
|
||||
self.cardIds.append(data['id'])
|
||||
data['combinedDue'] = data['due']
|
||||
if data.get('successive', 0):
|
||||
t = 1
|
||||
elif data.get('reps', 0):
|
||||
t = 0
|
||||
else:
|
||||
t = 2
|
||||
data['type'] = t
|
||||
data['relativeDelay'] = t
|
||||
return data
|
||||
|
||||
def stripInvalid(self, cards):
|
||||
|
|
|
@ -61,8 +61,6 @@ class Mnemosyne10Importer(Importer):
|
|||
card.reps = card.yesCount + card.noCount
|
||||
if item.cat.name != u"<default>":
|
||||
card.tags = item.cat.name.replace(" ", "_")
|
||||
if card.reps:
|
||||
card.type = 1
|
||||
cards.append(card)
|
||||
return cards
|
||||
|
||||
|
|
|
@ -539,7 +539,7 @@ class DeckStats(object):
|
|||
return (self.deck.s.scalar("""
|
||||
select count(id) from cards
|
||||
where combinedDue < :cutoff
|
||||
and priority > 0 and type in (0,1)""", cutoff=cutoff) or 0) / float(period)
|
||||
and priority > 0 and relativeDelay in (0,1)""", cutoff=cutoff) or 0) / float(period)
|
||||
|
||||
def getPastWorkloadPeriod(self, period):
|
||||
cutoff = time.time() - 86400 * period
|
||||
|
|
23
anki/sync.py
23
anki/sync.py
|
@ -491,9 +491,6 @@ where factId in %s""" % factIds))
|
|||
'spaceUntil': f[5] or "",
|
||||
'lastCardId': f[6]
|
||||
} for f in facts]
|
||||
self.deck.factCount += (len(facts) - self.deck.s.scalar(
|
||||
"select count(*) from facts where id in %s" %
|
||||
ids2str([f[0] for f in facts])))
|
||||
self.deck.s.execute("""
|
||||
insert or replace into facts
|
||||
(id, modelId, created, modified, tags, spaceUntil, lastCardId)
|
||||
|
@ -534,12 +531,21 @@ priority, interval, lastInterval, due, lastDue, factor,
|
|||
firstAnswered, reps, successive, averageTime, reviewTime, youngEase0,
|
||||
youngEase1, youngEase2, youngEase3, youngEase4, matureEase0,
|
||||
matureEase1, matureEase2, matureEase3, matureEase4, yesCount, noCount,
|
||||
question, answer, lastFactor, spaceUntil, type, combinedDue
|
||||
question, answer, lastFactor, spaceUntil, type, combinedDue, relativeDelay
|
||||
from cards where id in %s""" % ids2str(ids)))
|
||||
|
||||
def updateCards(self, cards):
|
||||
if not cards:
|
||||
return
|
||||
# FIXME: older clients won't send this, so this is temp compat code
|
||||
def getType(row):
|
||||
if len(row) > 37:
|
||||
return row[37]
|
||||
if row[15]:
|
||||
return 1
|
||||
elif row[14]:
|
||||
return 0
|
||||
return 2
|
||||
dlist = [{'id': c[0],
|
||||
'factId': c[1],
|
||||
'cardModelId': c[2],
|
||||
|
@ -576,10 +582,8 @@ from cards where id in %s""" % ids2str(ids)))
|
|||
'spaceUntil': c[33],
|
||||
'type': c[34],
|
||||
'combinedDue': c[35],
|
||||
'rd': getType(c)
|
||||
} for c in cards]
|
||||
self.deck.cardCount += (len(cards) - self.deck.s.scalar(
|
||||
"select count(*) from cards where id in %s" %
|
||||
ids2str([c[0] for c in cards])))
|
||||
self.deck.s.execute("""
|
||||
insert or replace into cards
|
||||
(id, factId, cardModelId, created, modified, tags, ordinal,
|
||||
|
@ -596,7 +600,7 @@ values
|
|||
:youngEase1, :youngEase2, :youngEase3, :youngEase4, :matureEase0,
|
||||
:matureEase1, :matureEase2, :matureEase3, :matureEase4, :yesCount,
|
||||
:noCount, :question, :answer, :lastFactor, :spaceUntil,
|
||||
:type, :combinedDue, 0, 0)""", dlist)
|
||||
:type, :combinedDue, :rd, 0)""", dlist)
|
||||
self.deck.s.statement(
|
||||
"delete from cardsDeleted where cardId in %s" %
|
||||
ids2str([c[0] for c in cards]))
|
||||
|
@ -841,9 +845,6 @@ where media.id in %s""" % sids, now=time.time())
|
|||
t = time.time()
|
||||
dlist = [{'id': c[0], 'factId': c[1], 'cardModelId': c[2],
|
||||
'ordinal': c[3], 'created': c[4], 't': t} for c in cards]
|
||||
self.deck.cardCount += (len(cards) - self.deck.s.scalar(
|
||||
"select count(*) from cards where id in %s" %
|
||||
ids2str([c[0] for c in cards])))
|
||||
# add any missing cards
|
||||
self.deck.s.statements("""
|
||||
insert or ignore into cards
|
||||
|
|
Loading…
Reference in a new issue