mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
customizable revision order, refactor failed cards handling
This commit is contained in:
parent
75a61a00cc
commit
0b52e2f0ec
1 changed files with 106 additions and 44 deletions
150
anki/deck.py
150
anki/deck.py
|
@ -34,6 +34,10 @@ PRIORITY_NONE = 0
|
||||||
MATURE_THRESHOLD = 21
|
MATURE_THRESHOLD = 21
|
||||||
NEW_CARDS_LAST = 1
|
NEW_CARDS_LAST = 1
|
||||||
NEW_CARDS_DISTRIBUTE = 0
|
NEW_CARDS_DISTRIBUTE = 0
|
||||||
|
REV_CARDS_OLD_FIRST = 0
|
||||||
|
REV_CARDS_NEW_FIRST = 1
|
||||||
|
REV_CARDS_DUE_FIRST = 2
|
||||||
|
REV_CARDS_RANDOM = 3
|
||||||
|
|
||||||
# parts of the code assume we only have one deck
|
# parts of the code assume we only have one deck
|
||||||
decksTable = Table(
|
decksTable = Table(
|
||||||
|
@ -42,7 +46,7 @@ decksTable = Table(
|
||||||
Column('created', Float, nullable=False, default=time.time),
|
Column('created', Float, nullable=False, default=time.time),
|
||||||
Column('modified', Float, nullable=False, default=time.time),
|
Column('modified', Float, nullable=False, default=time.time),
|
||||||
Column('description', UnicodeText, nullable=False, default=u""),
|
Column('description', UnicodeText, nullable=False, default=u""),
|
||||||
Column('version', Integer, nullable=False, default=16),
|
Column('version', Integer, nullable=False, default=17),
|
||||||
Column('currentModelId', Integer, ForeignKey("models.id")),
|
Column('currentModelId', Integer, ForeignKey("models.id")),
|
||||||
# syncing
|
# syncing
|
||||||
Column('syncName', UnicodeText),
|
Column('syncName', UnicodeText),
|
||||||
|
@ -86,7 +90,9 @@ decksTable = Table(
|
||||||
Column('failedNowCount', Integer, nullable=False, default=0),
|
Column('failedNowCount', Integer, nullable=False, default=0),
|
||||||
Column('failedSoonCount', Integer, nullable=False, default=0),
|
Column('failedSoonCount', Integer, nullable=False, default=0),
|
||||||
Column('revCount', Integer, nullable=False, default=0),
|
Column('revCount', Integer, nullable=False, default=0),
|
||||||
Column('newCount', Integer, nullable=False, default=0))
|
Column('newCount', Integer, nullable=False, default=0),
|
||||||
|
# rev order
|
||||||
|
Column('revCardOrder', Integer, nullable=False, default=0))
|
||||||
|
|
||||||
class Deck(object):
|
class Deck(object):
|
||||||
"Top-level object. Manages facts, cards and scheduling information."
|
"Top-level object. Manages facts, cards and scheduling information."
|
||||||
|
@ -124,11 +130,11 @@ class Deck(object):
|
||||||
"Return the next due card id, or None."
|
"Return the next due card id, or None."
|
||||||
# failed card due?
|
# failed card due?
|
||||||
if self.failedNowCount:
|
if self.failedNowCount:
|
||||||
return self.s.scalar("select id from failedCardsNow limit 1")
|
return self.s.scalar("select id from failedCards limit 1")
|
||||||
# failed card queue too big?
|
# failed card queue too big?
|
||||||
if self.failedSoonCount >= self.failedCardMax:
|
if self.failedSoonCount >= self.failedCardMax:
|
||||||
return self.s.scalar(
|
return self.s.scalar(
|
||||||
"select id from failedCardsSoon limit 1")
|
"select id from failedCards limit 1")
|
||||||
# distribute new cards?
|
# distribute new cards?
|
||||||
if self.newCardSpacing == NEW_CARDS_DISTRIBUTE:
|
if self.newCardSpacing == NEW_CARDS_DISTRIBUTE:
|
||||||
if self._timeForNewCard():
|
if self._timeForNewCard():
|
||||||
|
@ -137,7 +143,7 @@ class Deck(object):
|
||||||
return id
|
return id
|
||||||
# card due for review?
|
# card due for review?
|
||||||
if self.revCount:
|
if self.revCount:
|
||||||
return self.s.scalar("select id from revCards limit 1")
|
return self._getRevCard()
|
||||||
# new cards left?
|
# new cards left?
|
||||||
id = self._maybeGetNewCard()
|
id = self._maybeGetNewCard()
|
||||||
if id:
|
if id:
|
||||||
|
@ -145,7 +151,7 @@ class Deck(object):
|
||||||
# display failed cards early
|
# display failed cards early
|
||||||
if self.collapseTime:
|
if self.collapseTime:
|
||||||
id = self.s.scalar(
|
id = self.s.scalar(
|
||||||
"select id from failedCardsSoon limit 1")
|
"select id from failedCards limit 1")
|
||||||
return id
|
return id
|
||||||
|
|
||||||
# Get card: helper functions
|
# Get card: helper functions
|
||||||
|
@ -178,6 +184,21 @@ class Deck(object):
|
||||||
return self.s.scalar(
|
return self.s.scalar(
|
||||||
"select id from acqCardsOrdered limit 1")
|
"select id from acqCardsOrdered limit 1")
|
||||||
|
|
||||||
|
def _getRevCard(self):
|
||||||
|
"Return the next review card id."
|
||||||
|
if self.revCardOrder == 0:
|
||||||
|
return self.s.scalar(
|
||||||
|
"select id from revCardsOld limit 1")
|
||||||
|
elif self.revCardOrder == 1:
|
||||||
|
return self.s.scalar(
|
||||||
|
"select id from revCardsNew limit 1")
|
||||||
|
elif self.revCardOrder == 2:
|
||||||
|
return self.s.scalar(
|
||||||
|
"select id from revCardsDue limit 1")
|
||||||
|
else:
|
||||||
|
return self.s.scalar(
|
||||||
|
"select id from revCardsRandom limit 1")
|
||||||
|
|
||||||
def cardFromId(self, id, orm=False):
|
def cardFromId(self, id, orm=False):
|
||||||
"Given a card ID, return a card, and start the card timer."
|
"Given a card ID, return a card, and start the card timer."
|
||||||
if orm:
|
if orm:
|
||||||
|
@ -241,9 +262,19 @@ type, due, interval, factor, priority from """
|
||||||
new = "acqCardsRandom"
|
new = "acqCardsRandom"
|
||||||
else:
|
else:
|
||||||
new = "acqCardsOrdered"
|
new = "acqCardsOrdered"
|
||||||
|
if self.revCardOrder == 0:
|
||||||
|
rev = "revCardsOld"
|
||||||
|
elif self.revCardOrder == 1:
|
||||||
|
rev = "revCardsNew"
|
||||||
|
elif self.revCardOrder == 2:
|
||||||
|
rev = "revCardsDue"
|
||||||
|
else:
|
||||||
|
rev = "revCardsRandom"
|
||||||
d = {}
|
d = {}
|
||||||
d['fail'] = self.s.all(sel + "failedCardsNow limit 100")
|
d['fail'] = self.s.all(sel + """
|
||||||
d['rev'] = self.s.all(sel + "revCards limit 30")
|
cards where type = 0 and isDue = 1 and
|
||||||
|
combinedDue <= :now limit 30""", now=time.time())
|
||||||
|
d['rev'] = self.s.all(sel + rev + " limit 30")
|
||||||
if self.newCountToday:
|
if self.newCountToday:
|
||||||
d['acq'] = self.s.all(sel + """
|
d['acq'] = self.s.all(sel + """
|
||||||
%s where factId in (select distinct factId from cards
|
%s where factId in (select distinct factId from cards
|
||||||
|
@ -251,7 +282,7 @@ where factId in (select factId from %s limit 60))""" % (new, new))
|
||||||
else:
|
else:
|
||||||
d['acq'] = []
|
d['acq'] = []
|
||||||
if (not d['fail'] and not d['rev'] and not d['acq']):
|
if (not d['fail'] and not d['rev'] and not d['acq']):
|
||||||
d['fail'] = self.s.all(sel + "failedCardsSoon limit 100")
|
d['fail'] = self.s.all(sel + "failedCards limit 100")
|
||||||
return d
|
return d
|
||||||
|
|
||||||
# Answering a card
|
# Answering a card
|
||||||
|
@ -290,7 +321,7 @@ select type, count(type) from cards
|
||||||
where factId = :fid and isDue = 1
|
where factId = :fid and isDue = 1
|
||||||
group by type""", fid=card.factId):
|
group by type""", fid=card.factId):
|
||||||
if type == 0:
|
if type == 0:
|
||||||
self.failedNowCount -= count
|
self.failedSoonCount -= count
|
||||||
elif type == 1:
|
elif type == 1:
|
||||||
self.revCount -= count
|
self.revCount -= count
|
||||||
else:
|
else:
|
||||||
|
@ -435,11 +466,12 @@ end)""" + where)
|
||||||
self.cardCount = self.s.scalar("select count(*) from cards")
|
self.cardCount = self.s.scalar("select count(*) from cards")
|
||||||
self.factCount = self.s.scalar("select count(*) from facts")
|
self.factCount = self.s.scalar("select count(*) from facts")
|
||||||
# due counts
|
# due counts
|
||||||
self.failedNowCount = self.s.scalar(
|
|
||||||
"select count(*) from failedCardsNow")
|
|
||||||
self.failedSoonCount = cardCount = self.s.scalar(
|
self.failedSoonCount = cardCount = self.s.scalar(
|
||||||
"select count(*) from failedCardsSoon")
|
"select count(*) from failedCards")
|
||||||
self.revCount = self.s.scalar("select count(*) from revCards")
|
self.failedNowCount = self.s.scalar("""
|
||||||
|
select count(*) from cards where type = 0 and isDue = 1
|
||||||
|
and combinedDue <= :t""", t=time.time())
|
||||||
|
self.revCount = self.s.scalar("select count(*) from revCardsOld")
|
||||||
self.newCount = self.s.scalar("select count(*) from acqCardsOrdered")
|
self.newCount = self.s.scalar("select count(*) from acqCardsOrdered")
|
||||||
|
|
||||||
def checkDue(self):
|
def checkDue(self):
|
||||||
|
@ -449,20 +481,19 @@ end)""" + where)
|
||||||
stmt = """
|
stmt = """
|
||||||
update cards set
|
update cards set
|
||||||
isDue = 1 where type = %d and isDue = 0 and
|
isDue = 1 where type = %d and isDue = 0 and
|
||||||
priority in (1,2,3,4) and combinedDue < :now"""
|
priority in (1,2,3,4) and combinedDue <= :now"""
|
||||||
self.failedNowCount += self.s.statement(
|
# failed cards
|
||||||
stmt % 0, now=time.time()).rowcount
|
self.failedSoonCount += self.s.statement(
|
||||||
|
stmt % 0, now=time.time()+self.delay0).rowcount
|
||||||
|
self.failedNowCount = self.s.scalar("""
|
||||||
|
select count(*) from cards where
|
||||||
|
type = 0 and isDue = 1 and combinedDue <= :now""", now=time.time())
|
||||||
|
# review
|
||||||
self.revCount += self.s.statement(
|
self.revCount += self.s.statement(
|
||||||
stmt % 1, now=time.time()).rowcount
|
stmt % 1, now=time.time()).rowcount
|
||||||
|
# new
|
||||||
self.newCount += self.s.statement(
|
self.newCount += self.s.statement(
|
||||||
stmt % 2, now=time.time()).rowcount
|
stmt % 2, now=time.time()).rowcount
|
||||||
self.failedSoonCount = (self.failedNowCount +
|
|
||||||
self.s.scalar("""
|
|
||||||
select count(*) from cards where
|
|
||||||
type = 0 and isDue = 0 and priority in (1,2,3,4)
|
|
||||||
and combinedDue <= (select delay0 +
|
|
||||||
strftime("%s", "now")+1 from decks)"""))
|
|
||||||
# new card handling
|
|
||||||
self.newCountToday = max(min(
|
self.newCountToday = max(min(
|
||||||
self.newCount, self.newCardsPerDay -
|
self.newCount, self.newCardsPerDay -
|
||||||
self.newCardsToday()), 0)
|
self.newCardsToday()), 0)
|
||||||
|
@ -1622,6 +1653,9 @@ alter table decks add column failedSoonCount integer not null default 0""")
|
||||||
alter table decks add column revCount integer not null default 0""")
|
alter table decks add column revCount integer not null default 0""")
|
||||||
s.execute("""
|
s.execute("""
|
||||||
alter table decks add column newCount integer not null default 0""")
|
alter table decks add column newCount integer not null default 0""")
|
||||||
|
if ver < 17:
|
||||||
|
s.execute("""
|
||||||
|
alter table decks add column revCardOrder integer not null default 0""")
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
deck = s.query(Deck).get(1)
|
deck = s.query(Deck).get(1)
|
||||||
|
@ -1693,9 +1727,12 @@ alter table decks add column newCount integer not null default 0""")
|
||||||
create index if not exists ix_cards_combinedDue on cards
|
create index if not exists ix_cards_combinedDue on cards
|
||||||
(type, isDue, combinedDue, priority)""")
|
(type, isDue, combinedDue, priority)""")
|
||||||
deck.s.statement("""
|
deck.s.statement("""
|
||||||
create index if not exists ix_cards_revisionOrder on cards
|
create index if not exists ix_cards_revOldOrder on cards
|
||||||
(type, isDue, priority desc, interval desc)""")
|
(type, isDue, priority desc, interval desc)""")
|
||||||
deck.s.statement("""
|
deck.s.statement("""
|
||||||
|
create index if not exists ix_cards_revNewOrder on cards
|
||||||
|
(type, isDue, priority desc, interval)""")
|
||||||
|
deck.s.statement("""
|
||||||
create index if not exists ix_cards_newRandomOrder on cards
|
create index if not exists ix_cards_newRandomOrder on cards
|
||||||
(type, isDue, priority desc, factId, ordinal)""")
|
(type, isDue, priority desc, factId, ordinal)""")
|
||||||
deck.s.statement("""
|
deck.s.statement("""
|
||||||
|
@ -1733,36 +1770,42 @@ create index if not exists ix_mediaDeleted_factId on mediaDeleted (mediaId)""")
|
||||||
s = deck.s
|
s = deck.s
|
||||||
# old views
|
# old views
|
||||||
s.statement("drop view if exists failedCards")
|
s.statement("drop view if exists failedCards")
|
||||||
s.statement("drop view if exists acqCards")
|
s.statement("drop view if exists revCardsOld")
|
||||||
s.statement("drop view if exists futureCards")
|
s.statement("drop view if exists revCardsNew")
|
||||||
s.statement("drop view if exists typedCards")
|
s.statement("drop view if exists revCardsDue")
|
||||||
s.statement("drop view if exists failedCards")
|
s.statement("drop view if exists revCardsRandom")
|
||||||
s.statement("drop view if exists failedCardsNow")
|
|
||||||
s.statement("drop view if exists failedCardsSoon")
|
|
||||||
s.statement("drop view if exists revCards")
|
|
||||||
s.statement("drop view if exists acqCardsRandom")
|
s.statement("drop view if exists acqCardsRandom")
|
||||||
s.statement("drop view if exists acqCardsOrdered")
|
s.statement("drop view if exists acqCardsOrdered")
|
||||||
|
# failed cards
|
||||||
s.statement("""
|
s.statement("""
|
||||||
create view failedCardsNow as
|
create view failedCards as
|
||||||
select * from cards
|
select * from cards
|
||||||
where type = 0 and isDue = 1
|
where type = 0 and isDue = 1
|
||||||
and +priority in (1,2,3,4)
|
|
||||||
order by type, isDue, combinedDue
|
order by type, isDue, combinedDue
|
||||||
""")
|
""")
|
||||||
|
# rev cards
|
||||||
s.statement("""
|
s.statement("""
|
||||||
create view failedCardsSoon as
|
create view revCardsOld as
|
||||||
select * from cards
|
|
||||||
where type = 0 and +priority in (1,2,3,4)
|
|
||||||
and combinedDue <= (select delay0 +
|
|
||||||
strftime("%s", "now")+1 from decks where id = 1)
|
|
||||||
order by type, isDue, combinedDue
|
|
||||||
""")
|
|
||||||
s.statement("""
|
|
||||||
create view revCards as
|
|
||||||
select * from cards
|
select * from cards
|
||||||
where type = 1 and isDue = 1
|
where type = 1 and isDue = 1
|
||||||
order by priority desc, interval desc""")
|
order by priority desc, interval desc""")
|
||||||
s.statement("""
|
s.statement("""
|
||||||
|
create view revCardsNew as
|
||||||
|
select * from cards
|
||||||
|
where type = 1 and isDue = 1
|
||||||
|
order by priority desc, interval""")
|
||||||
|
s.statement("""
|
||||||
|
create view revCardsDue as
|
||||||
|
select * from cards
|
||||||
|
where type = 1 and isDue = 1
|
||||||
|
order by priority desc, combinedDue""")
|
||||||
|
s.statement("""
|
||||||
|
create view revCardsRandom as
|
||||||
|
select * from cards
|
||||||
|
where type = 1 and isDue = 1
|
||||||
|
order by priority desc, factId, ordinal""")
|
||||||
|
# new cards
|
||||||
|
s.statement("""
|
||||||
create view acqCardsRandom as
|
create view acqCardsRandom as
|
||||||
select * from cards
|
select * from cards
|
||||||
where type = 2 and isDue = 1
|
where type = 2 and isDue = 1
|
||||||
|
@ -1933,8 +1976,19 @@ where interval < 1""")
|
||||||
deck.delay2 = 0.0
|
deck.delay2 = 0.0
|
||||||
deck.version = 15
|
deck.version = 15
|
||||||
if deck.version < 16:
|
if deck.version < 16:
|
||||||
DeckStorage._addViews(deck)
|
#DeckStorage._addViews(deck)
|
||||||
deck.version = 16
|
deck.version = 16
|
||||||
|
if deck.version < 17:
|
||||||
|
deck.s.statement("drop view if exists acqCards")
|
||||||
|
deck.s.statement("drop view if exists futureCards")
|
||||||
|
deck.s.statement("drop view if exists revCards")
|
||||||
|
deck.s.statement("drop view if exists typedCards")
|
||||||
|
deck.s.statement("drop view if exists failedCardsNow")
|
||||||
|
deck.s.statement("drop view if exists failedCardsSoon")
|
||||||
|
deck.s.statement("drop index if exists ix_cards_revisionOrder")
|
||||||
|
# add new views
|
||||||
|
DeckStorage._addViews(deck)
|
||||||
|
deck.version = 17
|
||||||
deck.s.commit()
|
deck.s.commit()
|
||||||
return deck
|
return deck
|
||||||
_upgradeDeck = staticmethod(_upgradeDeck)
|
_upgradeDeck = staticmethod(_upgradeDeck)
|
||||||
|
@ -2001,3 +2055,11 @@ def newCardSchedulingLabels():
|
||||||
0: _("Spread new cards out through reviews"),
|
0: _("Spread new cards out through reviews"),
|
||||||
1: _("Show new cards after all other cards"),
|
1: _("Show new cards after all other cards"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def revCardOrderLabels():
|
||||||
|
return {
|
||||||
|
0: _("Review oldest cards first"),
|
||||||
|
1: _("Review newest cards first"),
|
||||||
|
2: _("Review cards in order due"),
|
||||||
|
3: _("Review cards in random order"),
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue