mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
type hints for scheduler
This commit is contained in:
parent
a02d203604
commit
9c16d59086
2 changed files with 151 additions and 148 deletions
135
anki/sched.py
135
anki/sched.py
|
@ -13,9 +13,10 @@ from anki.utils import ids2str, intTime, fmtTimeSpan
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
from anki.hooks import runHook
|
from anki.hooks import runHook
|
||||||
from typing import Any, List, Optional, Tuple, TypeVar
|
|
||||||
|
|
||||||
_T = TypeVar('_T')
|
from anki.cards import Card
|
||||||
|
#from anki.collection import _Collection
|
||||||
|
from typing import Any, Callable, Dict, List, Optional, Union, Tuple
|
||||||
|
|
||||||
# queue types: 0=new/cram, 1=lrn, 2=rev, 3=day lrn, -1=suspended, -2=buried
|
# queue types: 0=new/cram, 1=lrn, 2=rev, 3=day lrn, -1=suspended, -2=buried
|
||||||
# revlog types: 0=lrn, 1=rev, 2=relrn, 3=cram
|
# revlog types: 0=lrn, 1=rev, 2=relrn, 3=cram
|
||||||
|
@ -57,7 +58,7 @@ class Scheduler:
|
||||||
self._resetNew()
|
self._resetNew()
|
||||||
self._haveQueues = True
|
self._haveQueues = True
|
||||||
|
|
||||||
def answerCard(self, card, ease) -> None:
|
def answerCard(self, card: Card, ease: int) -> None:
|
||||||
self.col.log()
|
self.col.log()
|
||||||
assert 1 <= ease <= 4
|
assert 1 <= ease <= 4
|
||||||
self.col.markReview(card)
|
self.col.markReview(card)
|
||||||
|
@ -96,7 +97,7 @@ class Scheduler:
|
||||||
card.usn = self.col.usn()
|
card.usn = self.col.usn()
|
||||||
card.flushSched()
|
card.flushSched()
|
||||||
|
|
||||||
def counts(self, card=None) -> tuple:
|
def counts(self, card: None = None) -> tuple:
|
||||||
counts = [self.newCount, self.lrnCount, self.revCount]
|
counts = [self.newCount, self.lrnCount, self.revCount]
|
||||||
if card:
|
if card:
|
||||||
idx = self.countIdx(card)
|
idx = self.countIdx(card)
|
||||||
|
@ -124,12 +125,12 @@ order by due""" % self._deckLimit(),
|
||||||
ret = [x[1] for x in sorted(daysd.items())]
|
ret = [x[1] for x in sorted(daysd.items())]
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def countIdx(self, card) -> Any:
|
def countIdx(self, card: Card) -> Any:
|
||||||
if card.queue == 3:
|
if card.queue == 3:
|
||||||
return 1
|
return 1
|
||||||
return card.queue
|
return card.queue
|
||||||
|
|
||||||
def answerButtons(self, card) -> int:
|
def answerButtons(self, card: Card) -> int:
|
||||||
if card.odue:
|
if card.odue:
|
||||||
# normal review in dyn deck?
|
# normal review in dyn deck?
|
||||||
if card.odid and card.queue == 2:
|
if card.odid and card.queue == 2:
|
||||||
|
@ -163,7 +164,7 @@ order by due""" % self._deckLimit(),
|
||||||
# Rev/lrn/time daily stats
|
# Rev/lrn/time daily stats
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _updateStats(self, card, type, cnt=1) -> None:
|
def _updateStats(self, card: Card, type: str, cnt: int = 1) -> None:
|
||||||
key = type+"Today"
|
key = type+"Today"
|
||||||
for g in ([self.col.decks.get(card.did)] +
|
for g in ([self.col.decks.get(card.did)] +
|
||||||
self.col.decks.parents(card.did)):
|
self.col.decks.parents(card.did)):
|
||||||
|
@ -182,7 +183,7 @@ order by due""" % self._deckLimit(),
|
||||||
g['revToday'][1] -= rev
|
g['revToday'][1] -= rev
|
||||||
self.col.decks.save(g)
|
self.col.decks.save(g)
|
||||||
|
|
||||||
def _walkingCount(self, limFn=None, cntFn=None) -> Any:
|
def _walkingCount(self, limFn: Optional[Callable] = None, cntFn: Optional[Callable] = None) -> Any:
|
||||||
tot = 0
|
tot = 0
|
||||||
pcounts = {}
|
pcounts = {}
|
||||||
# for each of the active decks
|
# for each of the active decks
|
||||||
|
@ -253,7 +254,7 @@ order by due""" % self._deckLimit(),
|
||||||
def deckDueTree(self) -> Any:
|
def deckDueTree(self) -> Any:
|
||||||
return self._groupChildren(self.deckDueList())
|
return self._groupChildren(self.deckDueList())
|
||||||
|
|
||||||
def _groupChildren(self, grps) -> Tuple[Tuple[Any, Any, Any, Any, Any, Any], ...]:
|
def _groupChildren(self, grps: List[List]) -> Tuple[Tuple[Any, Any, Any, Any, Any, Any], ...]:
|
||||||
# first, split the group names into components
|
# first, split the group names into components
|
||||||
for g in grps:
|
for g in grps:
|
||||||
g[0] = g[0].split("::")
|
g[0] = g[0].split("::")
|
||||||
|
@ -262,7 +263,7 @@ order by due""" % self._deckLimit(),
|
||||||
# then run main function
|
# then run main function
|
||||||
return self._groupChildrenMain(grps)
|
return self._groupChildrenMain(grps)
|
||||||
|
|
||||||
def _groupChildrenMain(self, grps) -> Tuple[Tuple[Any, Any, Any, Any, Any, Any], ...]:
|
def _groupChildrenMain(self, grps: List[List[Union[List[str], int]]]) -> Tuple[Tuple[Any, Any, Any, Any, Any, Any], ...]:
|
||||||
tree = []
|
tree = []
|
||||||
# group and recurse
|
# group and recurse
|
||||||
def key(grp):
|
def key(grp):
|
||||||
|
@ -395,7 +396,7 @@ did = ? and queue = 0 limit ?)""", did, lim)
|
||||||
elif self.newCardModulus:
|
elif self.newCardModulus:
|
||||||
return self.reps and self.reps % self.newCardModulus == 0
|
return self.reps and self.reps % self.newCardModulus == 0
|
||||||
|
|
||||||
def _deckNewLimit(self, did, fn=None) -> Any:
|
def _deckNewLimit(self, did: int, fn: Optional[Callable] = None) -> Any:
|
||||||
if not fn:
|
if not fn:
|
||||||
fn = self._deckNewLimitSingle
|
fn = self._deckNewLimitSingle
|
||||||
sel = self.col.decks.get(did)
|
sel = self.col.decks.get(did)
|
||||||
|
@ -409,7 +410,7 @@ did = ? and queue = 0 limit ?)""", did, lim)
|
||||||
lim = min(rem, lim)
|
lim = min(rem, lim)
|
||||||
return lim
|
return lim
|
||||||
|
|
||||||
def _newForDeck(self, did, lim) -> Any:
|
def _newForDeck(self, did: int, lim: int) -> Any:
|
||||||
"New count for a single deck."
|
"New count for a single deck."
|
||||||
if not lim:
|
if not lim:
|
||||||
return 0
|
return 0
|
||||||
|
@ -418,7 +419,7 @@ did = ? and queue = 0 limit ?)""", did, lim)
|
||||||
select count() from
|
select count() from
|
||||||
(select 1 from cards where did = ? and queue = 0 limit ?)""", did, lim)
|
(select 1 from cards where did = ? and queue = 0 limit ?)""", did, lim)
|
||||||
|
|
||||||
def _deckNewLimitSingle(self, g) -> Any:
|
def _deckNewLimitSingle(self, g: Dict[str, Any]) -> Any:
|
||||||
"Limit for deck without parent limits."
|
"Limit for deck without parent limits."
|
||||||
if g['dyn']:
|
if g['dyn']:
|
||||||
return self.reportLimit
|
return self.reportLimit
|
||||||
|
@ -468,7 +469,7 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff)
|
||||||
self._lrnQueue.sort()
|
self._lrnQueue.sort()
|
||||||
return self._lrnQueue
|
return self._lrnQueue
|
||||||
|
|
||||||
def _getLrnCard(self, collapse=False) -> Any:
|
def _getLrnCard(self, collapse: bool = False) -> Any:
|
||||||
if self._fillLrn():
|
if self._fillLrn():
|
||||||
cutoff = time.time()
|
cutoff = time.time()
|
||||||
if collapse:
|
if collapse:
|
||||||
|
@ -509,10 +510,10 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
self.lrnCount -= 1
|
self.lrnCount -= 1
|
||||||
return self.col.getCard(self._lrnDayQueue.pop())
|
return self.col.getCard(self._lrnDayQueue.pop())
|
||||||
|
|
||||||
def _answerLrnCard(self, card, ease) -> None:
|
def _answerLrnCard(self, card: Card, ease: int) -> None:
|
||||||
# ease 1=no, 2=yes, 3=remove
|
# ease 1=no, 2=yes, 3=remove
|
||||||
conf = self._lrnConf(card)
|
conf = self._lrnConf(card)
|
||||||
if card.odid and not card.wasNew:
|
if card.odid and not card.wasNew: # type: ignore
|
||||||
type = 3
|
type = 3
|
||||||
elif card.type == 2:
|
elif card.type == 2:
|
||||||
type = 2
|
type = 2
|
||||||
|
@ -571,7 +572,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
card.queue = 3
|
card.queue = 3
|
||||||
self._logLrn(card, ease, conf, leaving, type, lastLeft)
|
self._logLrn(card, ease, conf, leaving, type, lastLeft)
|
||||||
|
|
||||||
def _delayForGrade(self, conf, left) -> Any:
|
def _delayForGrade(self, conf: Dict[str, Any], left: int) -> Any:
|
||||||
left = left % 1000
|
left = left % 1000
|
||||||
try:
|
try:
|
||||||
delay = conf['delays'][-left]
|
delay = conf['delays'][-left]
|
||||||
|
@ -583,13 +584,13 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
delay = 1
|
delay = 1
|
||||||
return delay*60
|
return delay*60
|
||||||
|
|
||||||
def _lrnConf(self, card) -> Any:
|
def _lrnConf(self, card: Card) -> Any:
|
||||||
if card.type == 2:
|
if card.type == 2:
|
||||||
return self._lapseConf(card)
|
return self._lapseConf(card)
|
||||||
else:
|
else:
|
||||||
return self._newConf(card)
|
return self._newConf(card)
|
||||||
|
|
||||||
def _rescheduleAsRev(self, card, conf, early) -> None:
|
def _rescheduleAsRev(self, card: Card, conf: Dict[str, Any], early: bool) -> None:
|
||||||
lapse = card.type == 2
|
lapse = card.type == 2
|
||||||
if lapse:
|
if lapse:
|
||||||
if self._resched(card):
|
if self._resched(card):
|
||||||
|
@ -612,7 +613,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
card.queue = card.type = 0
|
card.queue = card.type = 0
|
||||||
card.due = self.col.nextID("pos")
|
card.due = self.col.nextID("pos")
|
||||||
|
|
||||||
def _startingLeft(self, card) -> int:
|
def _startingLeft(self, card: Card) -> int:
|
||||||
if card.type == 2:
|
if card.type == 2:
|
||||||
conf = self._lapseConf(card)
|
conf = self._lapseConf(card)
|
||||||
else:
|
else:
|
||||||
|
@ -621,7 +622,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
tod = self._leftToday(conf['delays'], tot)
|
tod = self._leftToday(conf['delays'], tot)
|
||||||
return tot + tod*1000
|
return tot + tod*1000
|
||||||
|
|
||||||
def _leftToday(self, delays, left, now=None) -> int:
|
def _leftToday(self, delays: Any, left: int, now: None = None) -> int:
|
||||||
"The number of steps that can be completed by the day cutoff."
|
"The number of steps that can be completed by the day cutoff."
|
||||||
if not now:
|
if not now:
|
||||||
now = intTime()
|
now = intTime()
|
||||||
|
@ -634,7 +635,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
ok = i
|
ok = i
|
||||||
return ok+1
|
return ok+1
|
||||||
|
|
||||||
def _graduatingIvl(self, card, conf, early, adj=True) -> Any:
|
def _graduatingIvl(self, card: Card, conf: Dict[str, Any], early: bool, adj: bool = True) -> Any:
|
||||||
if card.type == 2:
|
if card.type == 2:
|
||||||
# lapsed card being relearnt
|
# lapsed card being relearnt
|
||||||
if card.odid:
|
if card.odid:
|
||||||
|
@ -652,13 +653,13 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
else:
|
else:
|
||||||
return ideal
|
return ideal
|
||||||
|
|
||||||
def _rescheduleNew(self, card, conf, early) -> None:
|
def _rescheduleNew(self, card: Card, conf: Dict[str, Any], early: bool) -> None:
|
||||||
"Reschedule a new card that's graduated for the first time."
|
"Reschedule a new card that's graduated for the first time."
|
||||||
card.ivl = self._graduatingIvl(card, conf, early)
|
card.ivl = self._graduatingIvl(card, conf, early)
|
||||||
card.due = self.today+card.ivl
|
card.due = self.today+card.ivl
|
||||||
card.factor = conf['initialFactor']
|
card.factor = conf['initialFactor']
|
||||||
|
|
||||||
def _logLrn(self, card, ease, conf, leaving, type, lastLeft) -> None:
|
def _logLrn(self, card: Card, ease: int, conf: Dict[str, Any], leaving: bool, type: int, lastLeft: int) -> None:
|
||||||
lastIvl = -(self._delayForGrade(conf, lastLeft))
|
lastIvl = -(self._delayForGrade(conf, lastLeft))
|
||||||
ivl = card.ivl if leaving else -(self._delayForGrade(conf, card.left))
|
ivl = card.ivl if leaving else -(self._delayForGrade(conf, card.left))
|
||||||
def log():
|
def log():
|
||||||
|
@ -673,7 +674,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
log()
|
log()
|
||||||
|
|
||||||
def removeLrn(self, ids=None) -> None:
|
def removeLrn(self, ids: Optional[List[int]] = None) -> None:
|
||||||
"Remove cards from the learning queues."
|
"Remove cards from the learning queues."
|
||||||
if ids:
|
if ids:
|
||||||
extra = " and id in "+ids2str(ids)
|
extra = " and id in "+ids2str(ids)
|
||||||
|
@ -692,7 +693,7 @@ where queue in (1,3) and type = 2
|
||||||
self.forgetCards(self.col.db.list(
|
self.forgetCards(self.col.db.list(
|
||||||
"select id from cards where queue in (1,3) %s" % extra))
|
"select id from cards where queue in (1,3) %s" % extra))
|
||||||
|
|
||||||
def _lrnForDeck(self, did) -> Any:
|
def _lrnForDeck(self, did: int) -> Any:
|
||||||
cnt = self.col.db.scalar(
|
cnt = self.col.db.scalar(
|
||||||
"""
|
"""
|
||||||
select sum(left/1000) from
|
select sum(left/1000) from
|
||||||
|
@ -708,16 +709,16 @@ and due <= ? limit ?)""",
|
||||||
# Reviews
|
# Reviews
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _deckRevLimit(self, did) -> Any:
|
def _deckRevLimit(self, did: int) -> Any:
|
||||||
return self._deckNewLimit(did, self._deckRevLimitSingle)
|
return self._deckNewLimit(did, self._deckRevLimitSingle)
|
||||||
|
|
||||||
def _deckRevLimitSingle(self, d) -> Any:
|
def _deckRevLimitSingle(self, d: Dict[str, Any]) -> Any:
|
||||||
if d['dyn']:
|
if d['dyn']:
|
||||||
return self.reportLimit
|
return self.reportLimit
|
||||||
c = self.col.decks.confForDid(d['id'])
|
c = self.col.decks.confForDid(d['id'])
|
||||||
return max(0, c['rev']['perDay'] - d['revToday'][1])
|
return max(0, c['rev']['perDay'] - d['revToday'][1])
|
||||||
|
|
||||||
def _revForDeck(self, did, lim) -> Any:
|
def _revForDeck(self, did: int, lim: int) -> Any:
|
||||||
lim = min(lim, self.reportLimit)
|
lim = min(lim, self.reportLimit)
|
||||||
return self.col.db.scalar(
|
return self.col.db.scalar(
|
||||||
"""
|
"""
|
||||||
|
@ -792,7 +793,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
# Answering a review card
|
# Answering a review card
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _answerRevCard(self, card, ease) -> None:
|
def _answerRevCard(self, card: Card, ease: int) -> None:
|
||||||
delay = 0
|
delay = 0
|
||||||
if ease == 1:
|
if ease == 1:
|
||||||
delay = self._rescheduleLapse(card)
|
delay = self._rescheduleLapse(card)
|
||||||
|
@ -800,7 +801,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
self._rescheduleRev(card, ease)
|
self._rescheduleRev(card, ease)
|
||||||
self._logRev(card, ease, delay)
|
self._logRev(card, ease, delay)
|
||||||
|
|
||||||
def _rescheduleLapse(self, card) -> Any:
|
def _rescheduleLapse(self, card: Card) -> Any:
|
||||||
conf = self._lapseConf(card)
|
conf = self._lapseConf(card)
|
||||||
card.lastIvl = card.ivl
|
card.lastIvl = card.ivl
|
||||||
if self._resched(card):
|
if self._resched(card):
|
||||||
|
@ -836,10 +837,10 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
card.queue = 3
|
card.queue = 3
|
||||||
return delay
|
return delay
|
||||||
|
|
||||||
def _nextLapseIvl(self, card, conf) -> Any:
|
def _nextLapseIvl(self, card: Card, conf: Dict[str, Any]) -> Any:
|
||||||
return max(conf['minInt'], int(card.ivl*conf['mult']))
|
return max(conf['minInt'], int(card.ivl*conf['mult']))
|
||||||
|
|
||||||
def _rescheduleRev(self, card, ease) -> None:
|
def _rescheduleRev(self, card: Card, ease: int) -> None:
|
||||||
# update interval
|
# update interval
|
||||||
card.lastIvl = card.ivl
|
card.lastIvl = card.ivl
|
||||||
if self._resched(card):
|
if self._resched(card):
|
||||||
|
@ -854,7 +855,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
card.odid = 0
|
card.odid = 0
|
||||||
card.odue = 0
|
card.odue = 0
|
||||||
|
|
||||||
def _logRev(self, card, ease, delay) -> None:
|
def _logRev(self, card: Card, ease: int, delay: Union[int, float]) -> None:
|
||||||
def log():
|
def log():
|
||||||
self.col.db.execute(
|
self.col.db.execute(
|
||||||
"insert into revlog values (?,?,?,?,?,?,?,?,?)",
|
"insert into revlog values (?,?,?,?,?,?,?,?,?)",
|
||||||
|
@ -871,7 +872,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
# Interval management
|
# Interval management
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _nextRevIvl(self, card, ease) -> Any:
|
def _nextRevIvl(self, card: Card, ease: int) -> Any:
|
||||||
"Ideal next interval for CARD, given EASE."
|
"Ideal next interval for CARD, given EASE."
|
||||||
delay = self._daysLate(card)
|
delay = self._daysLate(card)
|
||||||
conf = self._revConf(card)
|
conf = self._revConf(card)
|
||||||
|
@ -889,11 +890,11 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
# interval capped?
|
# interval capped?
|
||||||
return min(interval, conf['maxIvl'])
|
return min(interval, conf['maxIvl'])
|
||||||
|
|
||||||
def _fuzzedIvl(self, ivl) -> int:
|
def _fuzzedIvl(self, ivl: int) -> int:
|
||||||
min, max = self._fuzzIvlRange(ivl)
|
min, max = self._fuzzIvlRange(ivl)
|
||||||
return random.randint(min, max)
|
return random.randint(min, max)
|
||||||
|
|
||||||
def _fuzzIvlRange(self, ivl) -> List:
|
def _fuzzIvlRange(self, ivl: int) -> List:
|
||||||
if ivl < 2:
|
if ivl < 2:
|
||||||
return [1, 1]
|
return [1, 1]
|
||||||
elif ivl == 2:
|
elif ivl == 2:
|
||||||
|
@ -908,22 +909,22 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
fuzz = max(fuzz, 1)
|
fuzz = max(fuzz, 1)
|
||||||
return [ivl-fuzz, ivl+fuzz]
|
return [ivl-fuzz, ivl+fuzz]
|
||||||
|
|
||||||
def _constrainedIvl(self, ivl, conf, prev) -> int:
|
def _constrainedIvl(self, ivl: float, conf: Dict[str, Any], prev: int) -> int:
|
||||||
"Integer interval after interval factor and prev+1 constraints applied."
|
"Integer interval after interval factor and prev+1 constraints applied."
|
||||||
new = ivl * conf.get('ivlFct', 1)
|
new = ivl * conf.get('ivlFct', 1)
|
||||||
return int(max(new, prev+1))
|
return int(max(new, prev+1))
|
||||||
|
|
||||||
def _daysLate(self, card) -> Any:
|
def _daysLate(self, card: Card) -> Any:
|
||||||
"Number of days later than scheduled."
|
"Number of days later than scheduled."
|
||||||
due = card.odue if card.odid else card.due
|
due = card.odue if card.odid else card.due
|
||||||
return max(0, self.today - due)
|
return max(0, self.today - due)
|
||||||
|
|
||||||
def _updateRevIvl(self, card, ease) -> None:
|
def _updateRevIvl(self, card: Card, ease: int) -> None:
|
||||||
idealIvl = self._nextRevIvl(card, ease)
|
idealIvl = self._nextRevIvl(card, ease)
|
||||||
card.ivl = min(max(self._adjRevIvl(card, idealIvl), card.ivl+1),
|
card.ivl = min(max(self._adjRevIvl(card, idealIvl), card.ivl+1),
|
||||||
self._revConf(card)['maxIvl'])
|
self._revConf(card)['maxIvl'])
|
||||||
|
|
||||||
def _adjRevIvl(self, card, idealIvl) -> int:
|
def _adjRevIvl(self, card: Card, idealIvl: int) -> int:
|
||||||
if self._spreadRev:
|
if self._spreadRev:
|
||||||
idealIvl = self._fuzzedIvl(idealIvl)
|
idealIvl = self._fuzzedIvl(idealIvl)
|
||||||
return idealIvl
|
return idealIvl
|
||||||
|
@ -931,7 +932,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
# Dynamic deck handling
|
# Dynamic deck handling
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def rebuildDyn(self, did=None) -> Any:
|
def rebuildDyn(self, did: Optional[int] = None) -> Any:
|
||||||
"Rebuild a dynamic deck."
|
"Rebuild a dynamic deck."
|
||||||
did = did or self.col.decks.selected()
|
did = did or self.col.decks.selected()
|
||||||
deck = self.col.decks.get(did)
|
deck = self.col.decks.get(did)
|
||||||
|
@ -945,7 +946,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
self.col.decks.select(did)
|
self.col.decks.select(did)
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
def _fillDyn(self, deck) -> Any:
|
def _fillDyn(self, deck: Dict[str, Any]) -> Any:
|
||||||
search, limit, order = deck['terms'][0]
|
search, limit, order = deck['terms'][0]
|
||||||
orderlimit = self._dynOrder(order, limit)
|
orderlimit = self._dynOrder(order, limit)
|
||||||
if search.strip():
|
if search.strip():
|
||||||
|
@ -961,7 +962,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
self._moveToDyn(deck['id'], ids)
|
self._moveToDyn(deck['id'], ids)
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
def emptyDyn(self, did, lim=None) -> None:
|
def emptyDyn(self, did: Optional[int], lim: Optional[str] = None) -> None:
|
||||||
if not lim:
|
if not lim:
|
||||||
lim = "did = %s" % did
|
lim = "did = %s" % did
|
||||||
self.col.log(self.col.db.list("select id from cards where %s" % lim))
|
self.col.log(self.col.db.list("select id from cards where %s" % lim))
|
||||||
|
@ -972,10 +973,10 @@ else type end), type = (case when type = 1 then 0 else type end),
|
||||||
due = odue, odue = 0, odid = 0, usn = ? where %s""" % lim,
|
due = odue, odue = 0, odid = 0, usn = ? where %s""" % lim,
|
||||||
self.col.usn())
|
self.col.usn())
|
||||||
|
|
||||||
def remFromDyn(self, cids) -> None:
|
def remFromDyn(self, cids: List[int]) -> None:
|
||||||
self.emptyDyn(None, "id in %s and odid" % ids2str(cids))
|
self.emptyDyn(None, "id in %s and odid" % ids2str(cids))
|
||||||
|
|
||||||
def _dynOrder(self, o, l) -> str:
|
def _dynOrder(self, o: int, l: int) -> str:
|
||||||
if o == DYN_OLDEST:
|
if o == DYN_OLDEST:
|
||||||
t = "(select max(id) from revlog where cid=c.id)"
|
t = "(select max(id) from revlog where cid=c.id)"
|
||||||
elif o == DYN_RANDOM:
|
elif o == DYN_RANDOM:
|
||||||
|
@ -1000,7 +1001,7 @@ due = odue, odue = 0, odid = 0, usn = ? where %s""" % lim,
|
||||||
t = "c.due"
|
t = "c.due"
|
||||||
return t + " limit %d" % l
|
return t + " limit %d" % l
|
||||||
|
|
||||||
def _moveToDyn(self, did, ids) -> None:
|
def _moveToDyn(self, did: int, ids: List[int]) -> None:
|
||||||
deck = self.col.decks.get(did)
|
deck = self.col.decks.get(did)
|
||||||
data = []
|
data = []
|
||||||
t = intTime(); u = self.col.usn()
|
t = intTime(); u = self.col.usn()
|
||||||
|
@ -1019,7 +1020,7 @@ odid = (case when odid then odid else did end),
|
||||||
odue = (case when odue then odue else due end),
|
odue = (case when odue then odue else due end),
|
||||||
did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
||||||
|
|
||||||
def _dynIvlBoost(self, card) -> Any:
|
def _dynIvlBoost(self, card: Card) -> Any:
|
||||||
assert card.odid and card.type == 2
|
assert card.odid and card.type == 2
|
||||||
assert card.factor
|
assert card.factor
|
||||||
elapsed = card.ivl - (card.odue - self.today)
|
elapsed = card.ivl - (card.odue - self.today)
|
||||||
|
@ -1031,7 +1032,7 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
||||||
# Leeches
|
# Leeches
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _checkLeech(self, card, conf) -> Optional[bool]:
|
def _checkLeech(self, card: Card, conf: Dict[str, Any]) -> Optional[bool]:
|
||||||
"Leech handler. True if card was a leech."
|
"Leech handler. True if card was a leech."
|
||||||
lf = conf['leechFails']
|
lf = conf['leechFails']
|
||||||
if not lf:
|
if not lf:
|
||||||
|
@ -1060,10 +1061,10 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
||||||
# Tools
|
# Tools
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _cardConf(self, card) -> Any:
|
def _cardConf(self, card: Card) -> Any:
|
||||||
return self.col.decks.confForDid(card.did)
|
return self.col.decks.confForDid(card.did)
|
||||||
|
|
||||||
def _newConf(self, card) -> Any:
|
def _newConf(self, card: Card) -> Any:
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
# normal deck
|
# normal deck
|
||||||
if not card.odid:
|
if not card.odid:
|
||||||
|
@ -1083,7 +1084,7 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
||||||
perDay=self.reportLimit
|
perDay=self.reportLimit
|
||||||
)
|
)
|
||||||
|
|
||||||
def _lapseConf(self, card) -> Any:
|
def _lapseConf(self, card: Card) -> Any:
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
# normal deck
|
# normal deck
|
||||||
if not card.odid:
|
if not card.odid:
|
||||||
|
@ -1102,7 +1103,7 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
||||||
resched=conf['resched'],
|
resched=conf['resched'],
|
||||||
)
|
)
|
||||||
|
|
||||||
def _revConf(self, card) -> Any:
|
def _revConf(self, card: Card) -> Any:
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
# normal deck
|
# normal deck
|
||||||
if not card.odid:
|
if not card.odid:
|
||||||
|
@ -1113,7 +1114,7 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
||||||
def _deckLimit(self) -> str:
|
def _deckLimit(self) -> str:
|
||||||
return ids2str(self.col.decks.active())
|
return ids2str(self.col.decks.active())
|
||||||
|
|
||||||
def _resched(self, card) -> Any:
|
def _resched(self, card: Card) -> Any:
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
if not conf['dyn']:
|
if not conf['dyn']:
|
||||||
return True
|
return True
|
||||||
|
@ -1206,7 +1207,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
# Next time reports
|
# Next time reports
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def nextIvlStr(self, card, ease, short=False) -> Any:
|
def nextIvlStr(self, card: Card, ease: int, short: bool = False) -> Any:
|
||||||
"Return the next interval for CARD as a string."
|
"Return the next interval for CARD as a string."
|
||||||
ivl = self.nextIvl(card, ease)
|
ivl = self.nextIvl(card, ease)
|
||||||
if not ivl:
|
if not ivl:
|
||||||
|
@ -1216,7 +1217,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
s = "<"+s
|
s = "<"+s
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def nextIvl(self, card, ease) -> Any:
|
def nextIvl(self, card: Card, ease: int) -> Any:
|
||||||
"Return the next interval for CARD, in seconds."
|
"Return the next interval for CARD, in seconds."
|
||||||
if card.queue in (0,1,3):
|
if card.queue in (0,1,3):
|
||||||
return self._nextLrnIvl(card, ease)
|
return self._nextLrnIvl(card, ease)
|
||||||
|
@ -1231,7 +1232,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
return self._nextRevIvl(card, ease)*86400
|
return self._nextRevIvl(card, ease)*86400
|
||||||
|
|
||||||
# this isn't easily extracted from the learn code
|
# this isn't easily extracted from the learn code
|
||||||
def _nextLrnIvl(self, card, ease) -> Any:
|
def _nextLrnIvl(self, card: Card, ease: int) -> Any:
|
||||||
if card.queue == 0:
|
if card.queue == 0:
|
||||||
card.left = self._startingLeft(card)
|
card.left = self._startingLeft(card)
|
||||||
conf = self._lrnConf(card)
|
conf = self._lrnConf(card)
|
||||||
|
@ -1256,7 +1257,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
# Suspending
|
# Suspending
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def suspendCards(self, ids) -> None:
|
def suspendCards(self, ids: List[int]) -> None:
|
||||||
"Suspend cards."
|
"Suspend cards."
|
||||||
self.col.log(ids)
|
self.col.log(ids)
|
||||||
self.remFromDyn(ids)
|
self.remFromDyn(ids)
|
||||||
|
@ -1265,7 +1266,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
"update cards set queue=-1,mod=?,usn=? where id in "+
|
"update cards set queue=-1,mod=?,usn=? where id in "+
|
||||||
ids2str(ids), intTime(), self.col.usn())
|
ids2str(ids), intTime(), self.col.usn())
|
||||||
|
|
||||||
def unsuspendCards(self, ids) -> None:
|
def unsuspendCards(self, ids: List[int]) -> None:
|
||||||
"Unsuspend cards."
|
"Unsuspend cards."
|
||||||
self.col.log(ids)
|
self.col.log(ids)
|
||||||
self.col.db.execute(
|
self.col.db.execute(
|
||||||
|
@ -1273,7 +1274,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
"where queue = -1 and id in "+ ids2str(ids),
|
"where queue = -1 and id in "+ ids2str(ids),
|
||||||
intTime(), self.col.usn())
|
intTime(), self.col.usn())
|
||||||
|
|
||||||
def buryCards(self, cids) -> None:
|
def buryCards(self, cids: List[int]) -> None:
|
||||||
self.col.log(cids)
|
self.col.log(cids)
|
||||||
self.remFromDyn(cids)
|
self.remFromDyn(cids)
|
||||||
self.removeLrn(cids)
|
self.removeLrn(cids)
|
||||||
|
@ -1281,7 +1282,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
update cards set queue=-2,mod=?,usn=? where id in """+ids2str(cids),
|
update cards set queue=-2,mod=?,usn=? where id in """+ids2str(cids),
|
||||||
intTime(), self.col.usn())
|
intTime(), self.col.usn())
|
||||||
|
|
||||||
def buryNote(self, nid) -> None:
|
def buryNote(self, nid: int) -> None:
|
||||||
"Bury all cards for note until next session."
|
"Bury all cards for note until next session."
|
||||||
cids = self.col.db.list(
|
cids = self.col.db.list(
|
||||||
"select id from cards where nid = ? and queue >= 0", nid)
|
"select id from cards where nid = ? and queue >= 0", nid)
|
||||||
|
@ -1290,7 +1291,7 @@ update cards set queue=-2,mod=?,usn=? where id in """+ids2str(cids),
|
||||||
# Sibling spacing
|
# Sibling spacing
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _burySiblings(self, card) -> None:
|
def _burySiblings(self, card: Card) -> None:
|
||||||
toBury = []
|
toBury = []
|
||||||
nconf = self._newConf(card)
|
nconf = self._newConf(card)
|
||||||
buryNew = nconf.get("bury", True)
|
buryNew = nconf.get("bury", True)
|
||||||
|
@ -1327,7 +1328,7 @@ and (queue=0 or (queue=2 and due<=?))""",
|
||||||
# Resetting
|
# Resetting
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def forgetCards(self, ids) -> None:
|
def forgetCards(self, ids: List[int]) -> None:
|
||||||
"Put cards at the end of the new queue."
|
"Put cards at the end of the new queue."
|
||||||
self.remFromDyn(ids)
|
self.remFromDyn(ids)
|
||||||
self.col.db.execute(
|
self.col.db.execute(
|
||||||
|
@ -1339,7 +1340,7 @@ and (queue=0 or (queue=2 and due<=?))""",
|
||||||
self.sortCards(ids, start=pmax+1)
|
self.sortCards(ids, start=pmax+1)
|
||||||
self.col.log(ids)
|
self.col.log(ids)
|
||||||
|
|
||||||
def reschedCards(self, ids, imin, imax) -> None:
|
def reschedCards(self, ids: List[int], imin: int, imax: int) -> None:
|
||||||
"Put cards in review queue with a new interval in days (min, max)."
|
"Put cards in review queue with a new interval in days (min, max)."
|
||||||
d = []
|
d = []
|
||||||
t = self.today
|
t = self.today
|
||||||
|
@ -1374,7 +1375,7 @@ usn=:usn,mod=:mod,factor=:fact where id=:id""",
|
||||||
# Repositioning new cards
|
# Repositioning new cards
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def sortCards(self, cids, start=1, step=1, shuffle=False, shift=False) -> None:
|
def sortCards(self, cids: List[int], start: int = 1, step: int = 1, shuffle: bool = False, shift: bool = False) -> None:
|
||||||
scids = ids2str(cids)
|
scids = ids2str(cids)
|
||||||
now = intTime()
|
now = intTime()
|
||||||
nids = []
|
nids = []
|
||||||
|
@ -1414,11 +1415,11 @@ and due >= ? and queue = 0""" % scids, now, self.col.usn(), shiftby, low)
|
||||||
self.col.db.executemany(
|
self.col.db.executemany(
|
||||||
"update cards set due=:due,mod=:now,usn=:usn where id = :cid", d)
|
"update cards set due=:due,mod=:now,usn=:usn where id = :cid", d)
|
||||||
|
|
||||||
def randomizeCards(self, did) -> None:
|
def randomizeCards(self, did: int) -> None:
|
||||||
cids = self.col.db.list("select id from cards where did = ?", did)
|
cids = self.col.db.list("select id from cards where did = ?", did)
|
||||||
self.sortCards(cids, shuffle=True)
|
self.sortCards(cids, shuffle=True)
|
||||||
|
|
||||||
def orderCards(self, did) -> None:
|
def orderCards(self, did: int) -> None:
|
||||||
cids = self.col.db.list("select id from cards where did = ? order by id", did)
|
cids = self.col.db.list("select id from cards where did = ? order by id", did)
|
||||||
self.sortCards(cids)
|
self.sortCards(cids)
|
||||||
|
|
||||||
|
|
164
anki/schedv2.py
164
anki/schedv2.py
|
@ -13,7 +13,6 @@ from anki.utils import ids2str, intTime, fmtTimeSpan
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
from anki.hooks import runHook
|
from anki.hooks import runHook
|
||||||
from typing import Any, List, Optional, Tuple
|
|
||||||
|
|
||||||
# card types: 0=new, 1=lrn, 2=rev, 3=relrn
|
# card types: 0=new, 1=lrn, 2=rev, 3=relrn
|
||||||
# queue types: 0=new, 1=(re)lrn, 2=rev, 3=day (re)lrn,
|
# queue types: 0=new, 1=(re)lrn, 2=rev, 3=day (re)lrn,
|
||||||
|
@ -22,6 +21,9 @@ from typing import Any, List, Optional, Tuple
|
||||||
# positive revlog intervals are in days (rev), negative in seconds (lrn)
|
# positive revlog intervals are in days (rev), negative in seconds (lrn)
|
||||||
# odue/odid store original due/did when cards moved to filtered deck
|
# odue/odid store original due/did when cards moved to filtered deck
|
||||||
|
|
||||||
|
from anki.cards import Card
|
||||||
|
#from anki.collection import _Collection
|
||||||
|
from typing import Any, Callable, Dict, List, Optional, Union, Tuple
|
||||||
class Scheduler:
|
class Scheduler:
|
||||||
name = "std2"
|
name = "std2"
|
||||||
haveCustomStudy = True
|
haveCustomStudy = True
|
||||||
|
@ -59,7 +61,7 @@ class Scheduler:
|
||||||
self._resetNew()
|
self._resetNew()
|
||||||
self._haveQueues = True
|
self._haveQueues = True
|
||||||
|
|
||||||
def answerCard(self, card, ease) -> None:
|
def answerCard(self, card: Card, ease: int) -> None:
|
||||||
self.col.log()
|
self.col.log()
|
||||||
assert 1 <= ease <= 4
|
assert 1 <= ease <= 4
|
||||||
assert 0 <= card.queue <= 4
|
assert 0 <= card.queue <= 4
|
||||||
|
@ -74,7 +76,7 @@ class Scheduler:
|
||||||
card.usn = self.col.usn()
|
card.usn = self.col.usn()
|
||||||
card.flushSched()
|
card.flushSched()
|
||||||
|
|
||||||
def _answerCard(self, card, ease) -> None:
|
def _answerCard(self, card: Card, ease: int) -> None:
|
||||||
if self._previewingCard(card):
|
if self._previewingCard(card):
|
||||||
self._answerCardPreview(card, ease)
|
self._answerCardPreview(card, ease)
|
||||||
return
|
return
|
||||||
|
@ -104,7 +106,7 @@ class Scheduler:
|
||||||
if card.odue:
|
if card.odue:
|
||||||
card.odue = 0
|
card.odue = 0
|
||||||
|
|
||||||
def _answerCardPreview(self, card, ease) -> None:
|
def _answerCardPreview(self, card: Card, ease: int) -> None:
|
||||||
assert 1 <= ease <= 2
|
assert 1 <= ease <= 2
|
||||||
|
|
||||||
if ease == 1:
|
if ease == 1:
|
||||||
|
@ -117,7 +119,7 @@ class Scheduler:
|
||||||
self._restorePreviewCard(card)
|
self._restorePreviewCard(card)
|
||||||
self._removeFromFiltered(card)
|
self._removeFromFiltered(card)
|
||||||
|
|
||||||
def counts(self, card=None) -> tuple:
|
def counts(self, card: None = None) -> tuple:
|
||||||
counts = [self.newCount, self.lrnCount, self.revCount]
|
counts = [self.newCount, self.lrnCount, self.revCount]
|
||||||
if card:
|
if card:
|
||||||
idx = self.countIdx(card)
|
idx = self.countIdx(card)
|
||||||
|
@ -142,12 +144,12 @@ order by due""" % self._deckLimit(),
|
||||||
ret = [x[1] for x in sorted(daysd.items())]
|
ret = [x[1] for x in sorted(daysd.items())]
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def countIdx(self, card) -> Any:
|
def countIdx(self, card: Card) -> Any:
|
||||||
if card.queue in (3,4):
|
if card.queue in (3,4):
|
||||||
return 1
|
return 1
|
||||||
return card.queue
|
return card.queue
|
||||||
|
|
||||||
def answerButtons(self, card) -> int:
|
def answerButtons(self, card: Card) -> int:
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
if card.odid and not conf['resched']:
|
if card.odid and not conf['resched']:
|
||||||
return 2
|
return 2
|
||||||
|
@ -156,7 +158,7 @@ order by due""" % self._deckLimit(),
|
||||||
# Rev/lrn/time daily stats
|
# Rev/lrn/time daily stats
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _updateStats(self, card, type, cnt=1) -> None:
|
def _updateStats(self, card: Card, type: str, cnt: int = 1) -> None:
|
||||||
key = type+"Today"
|
key = type+"Today"
|
||||||
for g in ([self.col.decks.get(card.did)] +
|
for g in ([self.col.decks.get(card.did)] +
|
||||||
self.col.decks.parents(card.did)):
|
self.col.decks.parents(card.did)):
|
||||||
|
@ -175,7 +177,7 @@ order by due""" % self._deckLimit(),
|
||||||
g['revToday'][1] -= rev
|
g['revToday'][1] -= rev
|
||||||
self.col.decks.save(g)
|
self.col.decks.save(g)
|
||||||
|
|
||||||
def _walkingCount(self, limFn=None, cntFn=None) -> Any:
|
def _walkingCount(self, limFn: Optional[Callable] = None, cntFn: Optional[Callable] = None) -> Any:
|
||||||
tot = 0
|
tot = 0
|
||||||
pcounts = {}
|
pcounts = {}
|
||||||
# for each of the active decks
|
# for each of the active decks
|
||||||
|
@ -249,7 +251,7 @@ order by due""" % self._deckLimit(),
|
||||||
def deckDueTree(self) -> Any:
|
def deckDueTree(self) -> Any:
|
||||||
return self._groupChildren(self.deckDueList())
|
return self._groupChildren(self.deckDueList())
|
||||||
|
|
||||||
def _groupChildren(self, grps) -> Tuple[Tuple[Any, Any, Any, Any, Any, Any], ...]:
|
def _groupChildren(self, grps: List[List]) -> Tuple[Tuple[Any, Any, Any, Any, Any, Any], ...]:
|
||||||
# first, split the group names into components
|
# first, split the group names into components
|
||||||
for g in grps:
|
for g in grps:
|
||||||
g[0] = g[0].split("::")
|
g[0] = g[0].split("::")
|
||||||
|
@ -258,7 +260,7 @@ order by due""" % self._deckLimit(),
|
||||||
# then run main function
|
# then run main function
|
||||||
return self._groupChildrenMain(grps)
|
return self._groupChildrenMain(grps)
|
||||||
|
|
||||||
def _groupChildrenMain(self, grps) -> Tuple[Tuple[Any, Any, Any, Any, Any, Any], ...]:
|
def _groupChildrenMain(self, grps: List[List[Union[List[str], int]]]) -> Tuple[Tuple[Any, Any, Any, Any, Any, Any], ...]:
|
||||||
tree = []
|
tree = []
|
||||||
# group and recurse
|
# group and recurse
|
||||||
def key(grp):
|
def key(grp):
|
||||||
|
@ -402,7 +404,7 @@ did = ? and queue = 0 limit ?)""", did, lim)
|
||||||
elif self.newCardModulus:
|
elif self.newCardModulus:
|
||||||
return self.reps and self.reps % self.newCardModulus == 0
|
return self.reps and self.reps % self.newCardModulus == 0
|
||||||
|
|
||||||
def _deckNewLimit(self, did, fn=None) -> Any:
|
def _deckNewLimit(self, did: int, fn: None = None) -> Any:
|
||||||
if not fn:
|
if not fn:
|
||||||
fn = self._deckNewLimitSingle
|
fn = self._deckNewLimitSingle
|
||||||
sel = self.col.decks.get(did)
|
sel = self.col.decks.get(did)
|
||||||
|
@ -416,7 +418,7 @@ did = ? and queue = 0 limit ?)""", did, lim)
|
||||||
lim = min(rem, lim)
|
lim = min(rem, lim)
|
||||||
return lim
|
return lim
|
||||||
|
|
||||||
def _newForDeck(self, did, lim) -> Any:
|
def _newForDeck(self, did: int, lim: int) -> Any:
|
||||||
"New count for a single deck."
|
"New count for a single deck."
|
||||||
if not lim:
|
if not lim:
|
||||||
return 0
|
return 0
|
||||||
|
@ -425,7 +427,7 @@ did = ? and queue = 0 limit ?)""", did, lim)
|
||||||
select count() from
|
select count() from
|
||||||
(select 1 from cards where did = ? and queue = 0 limit ?)""", did, lim)
|
(select 1 from cards where did = ? and queue = 0 limit ?)""", did, lim)
|
||||||
|
|
||||||
def _deckNewLimitSingle(self, g) -> Any:
|
def _deckNewLimitSingle(self, g: Dict[str, Any]) -> Any:
|
||||||
"Limit for deck without parent limits."
|
"Limit for deck without parent limits."
|
||||||
if g['dyn']:
|
if g['dyn']:
|
||||||
return self.dynReportLimit
|
return self.dynReportLimit
|
||||||
|
@ -443,14 +445,14 @@ select id from cards where did in %s and queue = 0 limit ?)"""
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
# scan for any newly due learning cards every minute
|
# scan for any newly due learning cards every minute
|
||||||
def _updateLrnCutoff(self, force) -> bool:
|
def _updateLrnCutoff(self, force: bool) -> bool:
|
||||||
nextCutoff = intTime() + self.col.conf['collapseTime']
|
nextCutoff = intTime() + self.col.conf['collapseTime']
|
||||||
if nextCutoff - self._lrnCutoff > 60 or force:
|
if nextCutoff - self._lrnCutoff > 60 or force:
|
||||||
self._lrnCutoff = nextCutoff
|
self._lrnCutoff = nextCutoff
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _maybeResetLrn(self, force) -> None:
|
def _maybeResetLrn(self, force: bool) -> None:
|
||||||
if self._updateLrnCutoff(force):
|
if self._updateLrnCutoff(force):
|
||||||
self._resetLrn()
|
self._resetLrn()
|
||||||
|
|
||||||
|
@ -493,7 +495,7 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=cutoff)
|
||||||
self._lrnQueue.sort()
|
self._lrnQueue.sort()
|
||||||
return self._lrnQueue
|
return self._lrnQueue
|
||||||
|
|
||||||
def _getLrnCard(self, collapse=False) -> Any:
|
def _getLrnCard(self, collapse: bool = False) -> Any:
|
||||||
self._maybeResetLrn(force=collapse and self.lrnCount == 0)
|
self._maybeResetLrn(force=collapse and self.lrnCount == 0)
|
||||||
if self._fillLrn():
|
if self._fillLrn():
|
||||||
cutoff = time.time()
|
cutoff = time.time()
|
||||||
|
@ -535,7 +537,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
self.lrnCount -= 1
|
self.lrnCount -= 1
|
||||||
return self.col.getCard(self._lrnDayQueue.pop())
|
return self.col.getCard(self._lrnDayQueue.pop())
|
||||||
|
|
||||||
def _answerLrnCard(self, card, ease) -> None:
|
def _answerLrnCard(self, card: Card, ease: int) -> None:
|
||||||
conf = self._lrnConf(card)
|
conf = self._lrnConf(card)
|
||||||
if card.type in (2,3):
|
if card.type in (2,3):
|
||||||
type = 2
|
type = 2
|
||||||
|
@ -566,11 +568,11 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
|
|
||||||
self._logLrn(card, ease, conf, leaving, type, lastLeft)
|
self._logLrn(card, ease, conf, leaving, type, lastLeft)
|
||||||
|
|
||||||
def _updateRevIvlOnFail(self, card, conf) -> None:
|
def _updateRevIvlOnFail(self, card: Card, conf: Dict[str, Any]) -> None:
|
||||||
card.lastIvl = card.ivl
|
card.lastIvl = card.ivl
|
||||||
card.ivl = self._lapseIvl(card, conf)
|
card.ivl = self._lapseIvl(card, conf)
|
||||||
|
|
||||||
def _moveToFirstStep(self, card, conf) -> Any:
|
def _moveToFirstStep(self, card: Card, conf: Dict[str, Any]) -> Any:
|
||||||
card.left = self._startingLeft(card)
|
card.left = self._startingLeft(card)
|
||||||
|
|
||||||
# relearning card?
|
# relearning card?
|
||||||
|
@ -579,18 +581,18 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
|
|
||||||
return self._rescheduleLrnCard(card, conf)
|
return self._rescheduleLrnCard(card, conf)
|
||||||
|
|
||||||
def _moveToNextStep(self, card, conf) -> None:
|
def _moveToNextStep(self, card: Card, conf: Dict[str, Any]) -> None:
|
||||||
# decrement real left count and recalculate left today
|
# decrement real left count and recalculate left today
|
||||||
left = (card.left % 1000) - 1
|
left = (card.left % 1000) - 1
|
||||||
card.left = self._leftToday(conf['delays'], left)*1000 + left
|
card.left = self._leftToday(conf['delays'], left)*1000 + left
|
||||||
|
|
||||||
self._rescheduleLrnCard(card, conf)
|
self._rescheduleLrnCard(card, conf)
|
||||||
|
|
||||||
def _repeatStep(self, card, conf) -> None:
|
def _repeatStep(self, card: Card, conf: Dict[str, Any]) -> None:
|
||||||
delay = self._delayForRepeatingGrade(conf, card.left)
|
delay = self._delayForRepeatingGrade(conf, card.left)
|
||||||
self._rescheduleLrnCard(card, conf, delay=delay)
|
self._rescheduleLrnCard(card, conf, delay=delay)
|
||||||
|
|
||||||
def _rescheduleLrnCard(self, card, conf, delay=None) -> Any:
|
def _rescheduleLrnCard(self, card: Card, conf: Dict[str, Any], delay: Optional[int] = None) -> Any:
|
||||||
# normal delay for the current step?
|
# normal delay for the current step?
|
||||||
if delay is None:
|
if delay is None:
|
||||||
delay = self._delayForGrade(conf, card.left)
|
delay = self._delayForGrade(conf, card.left)
|
||||||
|
@ -620,7 +622,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
card.queue = 3
|
card.queue = 3
|
||||||
return delay
|
return delay
|
||||||
|
|
||||||
def _delayForGrade(self, conf, left) -> Any:
|
def _delayForGrade(self, conf: Dict[str, Any], left: int) -> Any:
|
||||||
left = left % 1000
|
left = left % 1000
|
||||||
try:
|
try:
|
||||||
delay = conf['delays'][-left]
|
delay = conf['delays'][-left]
|
||||||
|
@ -632,7 +634,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
delay = 1
|
delay = 1
|
||||||
return delay*60
|
return delay*60
|
||||||
|
|
||||||
def _delayForRepeatingGrade(self, conf, left) -> Any:
|
def _delayForRepeatingGrade(self, conf: Dict[str, Any], left: int) -> Any:
|
||||||
# halfway between last and next
|
# halfway between last and next
|
||||||
delay1 = self._delayForGrade(conf, left)
|
delay1 = self._delayForGrade(conf, left)
|
||||||
if len(conf['delays']) > 1:
|
if len(conf['delays']) > 1:
|
||||||
|
@ -642,13 +644,13 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
avg = (delay1+max(delay1, delay2))//2
|
avg = (delay1+max(delay1, delay2))//2
|
||||||
return avg
|
return avg
|
||||||
|
|
||||||
def _lrnConf(self, card) -> Any:
|
def _lrnConf(self, card: Card) -> Any:
|
||||||
if card.type in (2, 3):
|
if card.type in (2, 3):
|
||||||
return self._lapseConf(card)
|
return self._lapseConf(card)
|
||||||
else:
|
else:
|
||||||
return self._newConf(card)
|
return self._newConf(card)
|
||||||
|
|
||||||
def _rescheduleAsRev(self, card, conf, early) -> None:
|
def _rescheduleAsRev(self, card: Card, conf: Dict[str, Any], early: bool) -> None:
|
||||||
lapse = card.type in (2,3)
|
lapse = card.type in (2,3)
|
||||||
|
|
||||||
if lapse:
|
if lapse:
|
||||||
|
@ -660,14 +662,14 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
if card.odid:
|
if card.odid:
|
||||||
self._removeFromFiltered(card)
|
self._removeFromFiltered(card)
|
||||||
|
|
||||||
def _rescheduleGraduatingLapse(self, card, early=False) -> None:
|
def _rescheduleGraduatingLapse(self, card: Card, early: bool = False) -> None:
|
||||||
if early:
|
if early:
|
||||||
card.ivl += 1
|
card.ivl += 1
|
||||||
card.due = self.today+card.ivl
|
card.due = self.today+card.ivl
|
||||||
card.queue = 2
|
card.queue = 2
|
||||||
card.type = 2
|
card.type = 2
|
||||||
|
|
||||||
def _startingLeft(self, card) -> int:
|
def _startingLeft(self, card: Card) -> int:
|
||||||
if card.type == 3:
|
if card.type == 3:
|
||||||
conf = self._lapseConf(card)
|
conf = self._lapseConf(card)
|
||||||
else:
|
else:
|
||||||
|
@ -676,7 +678,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
tod = self._leftToday(conf['delays'], tot)
|
tod = self._leftToday(conf['delays'], tot)
|
||||||
return tot + tod*1000
|
return tot + tod*1000
|
||||||
|
|
||||||
def _leftToday(self, delays, left, now=None) -> int:
|
def _leftToday(self, delays: Union[List[int], List[Union[float, int]]], left: int, now: None = None) -> int:
|
||||||
"The number of steps that can be completed by the day cutoff."
|
"The number of steps that can be completed by the day cutoff."
|
||||||
if not now:
|
if not now:
|
||||||
now = intTime()
|
now = intTime()
|
||||||
|
@ -689,7 +691,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
ok = i
|
ok = i
|
||||||
return ok+1
|
return ok+1
|
||||||
|
|
||||||
def _graduatingIvl(self, card, conf, early, fuzz=True) -> Any:
|
def _graduatingIvl(self, card: Card, conf: Dict[str, Any], early: bool, fuzz: bool = True) -> Any:
|
||||||
if card.type in (2,3):
|
if card.type in (2,3):
|
||||||
bonus = early and 1 or 0
|
bonus = early and 1 or 0
|
||||||
return card.ivl + bonus
|
return card.ivl + bonus
|
||||||
|
@ -703,14 +705,14 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
ideal = self._fuzzedIvl(ideal)
|
ideal = self._fuzzedIvl(ideal)
|
||||||
return ideal
|
return ideal
|
||||||
|
|
||||||
def _rescheduleNew(self, card, conf, early) -> None:
|
def _rescheduleNew(self, card: Card, conf: Dict[str, Any], early: bool) -> None:
|
||||||
"Reschedule a new card that's graduated for the first time."
|
"Reschedule a new card that's graduated for the first time."
|
||||||
card.ivl = self._graduatingIvl(card, conf, early)
|
card.ivl = self._graduatingIvl(card, conf, early)
|
||||||
card.due = self.today+card.ivl
|
card.due = self.today+card.ivl
|
||||||
card.factor = conf['initialFactor']
|
card.factor = conf['initialFactor']
|
||||||
card.type = card.queue = 2
|
card.type = card.queue = 2
|
||||||
|
|
||||||
def _logLrn(self, card, ease, conf, leaving, type, lastLeft) -> None:
|
def _logLrn(self, card: Card, ease: int, conf: Dict[str, Any], leaving: bool, type: int, lastLeft: int) -> None:
|
||||||
lastIvl = -(self._delayForGrade(conf, lastLeft))
|
lastIvl = -(self._delayForGrade(conf, lastLeft))
|
||||||
ivl = card.ivl if leaving else -(self._delayForGrade(conf, card.left))
|
ivl = card.ivl if leaving else -(self._delayForGrade(conf, card.left))
|
||||||
def log():
|
def log():
|
||||||
|
@ -725,7 +727,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
log()
|
log()
|
||||||
|
|
||||||
def _lrnForDeck(self, did) -> Any:
|
def _lrnForDeck(self, did: int) -> Any:
|
||||||
cnt = self.col.db.scalar(
|
cnt = self.col.db.scalar(
|
||||||
"""
|
"""
|
||||||
select count() from
|
select count() from
|
||||||
|
@ -745,7 +747,7 @@ and due <= ? limit ?)""",
|
||||||
d = self.col.decks.get(self.col.decks.selected(), default=False)
|
d = self.col.decks.get(self.col.decks.selected(), default=False)
|
||||||
return self._deckRevLimitSingle(d)
|
return self._deckRevLimitSingle(d)
|
||||||
|
|
||||||
def _deckRevLimitSingle(self, d, parentLimit=None) -> Any:
|
def _deckRevLimitSingle(self, d: Dict[str, Any], parentLimit: Optional[int] = None) -> Any:
|
||||||
# invalid deck selected?
|
# invalid deck selected?
|
||||||
if not d:
|
if not d:
|
||||||
return 0
|
return 0
|
||||||
|
@ -766,7 +768,7 @@ and due <= ? limit ?)""",
|
||||||
lim = min(lim, self._deckRevLimitSingle(parent, parentLimit=lim))
|
lim = min(lim, self._deckRevLimitSingle(parent, parentLimit=lim))
|
||||||
return lim
|
return lim
|
||||||
|
|
||||||
def _revForDeck(self, did, lim, childMap) -> Any:
|
def _revForDeck(self, did: int, lim: int, childMap: Dict[int, Any]) -> Any:
|
||||||
dids = [did] + self.col.decks.childDids(did, childMap)
|
dids = [did] + self.col.decks.childDids(did, childMap)
|
||||||
lim = min(lim, self.reportLimit)
|
lim = min(lim, self.reportLimit)
|
||||||
return self.col.db.scalar(
|
return self.col.db.scalar(
|
||||||
|
@ -829,7 +831,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
# Answering a review card
|
# Answering a review card
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _answerRevCard(self, card, ease) -> None:
|
def _answerRevCard(self, card: Card, ease: int) -> None:
|
||||||
delay = 0
|
delay = 0
|
||||||
early = card.odid and (card.odue > self.today)
|
early = card.odid and (card.odue > self.today)
|
||||||
type = early and 3 or 1
|
type = early and 3 or 1
|
||||||
|
@ -841,7 +843,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
|
|
||||||
self._logRev(card, ease, delay, type)
|
self._logRev(card, ease, delay, type)
|
||||||
|
|
||||||
def _rescheduleLapse(self, card) -> Any:
|
def _rescheduleLapse(self, card: Card) -> Any:
|
||||||
conf = self._lapseConf(card)
|
conf = self._lapseConf(card)
|
||||||
|
|
||||||
card.lapses += 1
|
card.lapses += 1
|
||||||
|
@ -863,11 +865,11 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
|
|
||||||
return delay
|
return delay
|
||||||
|
|
||||||
def _lapseIvl(self, card, conf) -> Any:
|
def _lapseIvl(self, card: Card, conf: Dict[str, Any]) -> Any:
|
||||||
ivl = max(1, conf['minInt'], int(card.ivl*conf['mult']))
|
ivl = max(1, conf['minInt'], int(card.ivl*conf['mult']))
|
||||||
return ivl
|
return ivl
|
||||||
|
|
||||||
def _rescheduleRev(self, card, ease, early) -> None:
|
def _rescheduleRev(self, card: Card, ease: int, early: Union[bool, int]) -> None:
|
||||||
# update interval
|
# update interval
|
||||||
card.lastIvl = card.ivl
|
card.lastIvl = card.ivl
|
||||||
if early:
|
if early:
|
||||||
|
@ -882,7 +884,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
# card leaves filtered deck
|
# card leaves filtered deck
|
||||||
self._removeFromFiltered(card)
|
self._removeFromFiltered(card)
|
||||||
|
|
||||||
def _logRev(self, card, ease, delay, type) -> None:
|
def _logRev(self, card: Card, ease: int, delay: int, type: int) -> None:
|
||||||
def log():
|
def log():
|
||||||
self.col.db.execute(
|
self.col.db.execute(
|
||||||
"insert into revlog values (?,?,?,?,?,?,?,?,?)",
|
"insert into revlog values (?,?,?,?,?,?,?,?,?)",
|
||||||
|
@ -899,7 +901,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
# Interval management
|
# Interval management
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _nextRevIvl(self, card, ease, fuzz) -> int:
|
def _nextRevIvl(self, card: Card, ease: int, fuzz: bool) -> int:
|
||||||
"Next review interval for CARD, given EASE."
|
"Next review interval for CARD, given EASE."
|
||||||
delay = self._daysLate(card)
|
delay = self._daysLate(card)
|
||||||
conf = self._revConf(card)
|
conf = self._revConf(card)
|
||||||
|
@ -921,11 +923,11 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
(card.ivl + delay) * fct * conf['ease4'], conf, ivl3, fuzz)
|
(card.ivl + delay) * fct * conf['ease4'], conf, ivl3, fuzz)
|
||||||
return ivl4
|
return ivl4
|
||||||
|
|
||||||
def _fuzzedIvl(self, ivl) -> int:
|
def _fuzzedIvl(self, ivl: int) -> int:
|
||||||
min, max = self._fuzzIvlRange(ivl)
|
min, max = self._fuzzIvlRange(ivl)
|
||||||
return random.randint(min, max)
|
return random.randint(min, max)
|
||||||
|
|
||||||
def _fuzzIvlRange(self, ivl) -> List:
|
def _fuzzIvlRange(self, ivl: int) -> List:
|
||||||
if ivl < 2:
|
if ivl < 2:
|
||||||
return [1, 1]
|
return [1, 1]
|
||||||
elif ivl == 2:
|
elif ivl == 2:
|
||||||
|
@ -940,7 +942,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
fuzz = max(fuzz, 1)
|
fuzz = max(fuzz, 1)
|
||||||
return [ivl-fuzz, ivl+fuzz]
|
return [ivl-fuzz, ivl+fuzz]
|
||||||
|
|
||||||
def _constrainedIvl(self, ivl, conf, prev, fuzz) -> int:
|
def _constrainedIvl(self, ivl: Union[int, float], conf: Dict[str, Any], prev: int, fuzz: bool) -> int:
|
||||||
ivl = int(ivl * conf.get('ivlFct', 1))
|
ivl = int(ivl * conf.get('ivlFct', 1))
|
||||||
if fuzz:
|
if fuzz:
|
||||||
ivl = self._fuzzedIvl(ivl)
|
ivl = self._fuzzedIvl(ivl)
|
||||||
|
@ -948,19 +950,19 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
ivl = min(ivl, conf['maxIvl'])
|
ivl = min(ivl, conf['maxIvl'])
|
||||||
return int(ivl)
|
return int(ivl)
|
||||||
|
|
||||||
def _daysLate(self, card) -> Any:
|
def _daysLate(self, card: Card) -> Any:
|
||||||
"Number of days later than scheduled."
|
"Number of days later than scheduled."
|
||||||
due = card.odue if card.odid else card.due
|
due = card.odue if card.odid else card.due
|
||||||
return max(0, self.today - due)
|
return max(0, self.today - due)
|
||||||
|
|
||||||
def _updateRevIvl(self, card, ease) -> None:
|
def _updateRevIvl(self, card: Card, ease: int) -> None:
|
||||||
card.ivl = self._nextRevIvl(card, ease, fuzz=True)
|
card.ivl = self._nextRevIvl(card, ease, fuzz=True)
|
||||||
|
|
||||||
def _updateEarlyRevIvl(self, card, ease) -> None:
|
def _updateEarlyRevIvl(self, card: Card, ease: int) -> None:
|
||||||
card.ivl = self._earlyReviewIvl(card, ease)
|
card.ivl = self._earlyReviewIvl(card, ease)
|
||||||
|
|
||||||
# next interval for card when answered early+correctly
|
# next interval for card when answered early+correctly
|
||||||
def _earlyReviewIvl(self, card, ease) -> int:
|
def _earlyReviewIvl(self, card: Card, ease: int) -> int:
|
||||||
assert card.odid and card.type == 2
|
assert card.odid and card.type == 2
|
||||||
assert card.factor
|
assert card.factor
|
||||||
assert ease > 1
|
assert ease > 1
|
||||||
|
@ -998,7 +1000,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
# Dynamic deck handling
|
# Dynamic deck handling
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def rebuildDyn(self, did=None) -> Optional[int]:
|
def rebuildDyn(self, did: Optional[int] = None) -> Optional[int]:
|
||||||
"Rebuild a dynamic deck."
|
"Rebuild a dynamic deck."
|
||||||
did = did or self.col.decks.selected()
|
did = did or self.col.decks.selected()
|
||||||
deck = self.col.decks.get(did)
|
deck = self.col.decks.get(did)
|
||||||
|
@ -1012,7 +1014,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
self.col.decks.select(did)
|
self.col.decks.select(did)
|
||||||
return cnt
|
return cnt
|
||||||
|
|
||||||
def _fillDyn(self, deck) -> int:
|
def _fillDyn(self, deck: Dict[str, Any]) -> int:
|
||||||
start = -100000
|
start = -100000
|
||||||
total = 0
|
total = 0
|
||||||
for search, limit, order in deck['terms']:
|
for search, limit, order in deck['terms']:
|
||||||
|
@ -1030,7 +1032,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
total += len(ids)
|
total += len(ids)
|
||||||
return total
|
return total
|
||||||
|
|
||||||
def emptyDyn(self, did, lim=None) -> None:
|
def emptyDyn(self, did: Optional[int], lim: Optional[str] = None) -> None:
|
||||||
if not lim:
|
if not lim:
|
||||||
lim = "did = %s" % did
|
lim = "did = %s" % did
|
||||||
self.col.log(self.col.db.list("select id from cards where %s" % lim))
|
self.col.log(self.col.db.list("select id from cards where %s" % lim))
|
||||||
|
@ -1041,10 +1043,10 @@ due = (case when odue>0 then odue else due end), odue = 0, odid = 0, usn = ? whe
|
||||||
self._restoreQueueSnippet, lim),
|
self._restoreQueueSnippet, lim),
|
||||||
self.col.usn())
|
self.col.usn())
|
||||||
|
|
||||||
def remFromDyn(self, cids) -> None:
|
def remFromDyn(self, cids: List[int]) -> None:
|
||||||
self.emptyDyn(None, "id in %s and odid" % ids2str(cids))
|
self.emptyDyn(None, "id in %s and odid" % ids2str(cids))
|
||||||
|
|
||||||
def _dynOrder(self, o, l) -> str:
|
def _dynOrder(self, o: int, l: int) -> str:
|
||||||
if o == DYN_OLDEST:
|
if o == DYN_OLDEST:
|
||||||
t = "(select max(id) from revlog where cid=c.id)"
|
t = "(select max(id) from revlog where cid=c.id)"
|
||||||
elif o == DYN_RANDOM:
|
elif o == DYN_RANDOM:
|
||||||
|
@ -1066,7 +1068,7 @@ due = (case when odue>0 then odue else due end), odue = 0, odid = 0, usn = ? whe
|
||||||
t = "c.due, c.ord"
|
t = "c.due, c.ord"
|
||||||
return t + " limit %d" % l
|
return t + " limit %d" % l
|
||||||
|
|
||||||
def _moveToDyn(self, did, ids, start=-100000) -> None:
|
def _moveToDyn(self, did: int, ids: List[int], start: int = -100000) -> None:
|
||||||
deck = self.col.decks.get(did)
|
deck = self.col.decks.get(did)
|
||||||
data = []
|
data = []
|
||||||
u = self.col.usn()
|
u = self.col.usn()
|
||||||
|
@ -1090,13 +1092,13 @@ where id = ?
|
||||||
""" % queue
|
""" % queue
|
||||||
self.col.db.executemany(query, data)
|
self.col.db.executemany(query, data)
|
||||||
|
|
||||||
def _removeFromFiltered(self, card) -> None:
|
def _removeFromFiltered(self, card: Card) -> None:
|
||||||
if card.odid:
|
if card.odid:
|
||||||
card.did = card.odid
|
card.did = card.odid
|
||||||
card.odue = 0
|
card.odue = 0
|
||||||
card.odid = 0
|
card.odid = 0
|
||||||
|
|
||||||
def _restorePreviewCard(self, card) -> None:
|
def _restorePreviewCard(self, card: Card) -> None:
|
||||||
assert card.odid
|
assert card.odid
|
||||||
|
|
||||||
card.due = card.odue
|
card.due = card.odue
|
||||||
|
@ -1114,7 +1116,7 @@ where id = ?
|
||||||
# Leeches
|
# Leeches
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _checkLeech(self, card, conf) -> Optional[bool]:
|
def _checkLeech(self, card: Card, conf: Dict[str, Any]) -> Optional[bool]:
|
||||||
"Leech handler. True if card was a leech."
|
"Leech handler. True if card was a leech."
|
||||||
lf = conf['leechFails']
|
lf = conf['leechFails']
|
||||||
if not lf:
|
if not lf:
|
||||||
|
@ -1137,10 +1139,10 @@ where id = ?
|
||||||
# Tools
|
# Tools
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _cardConf(self, card) -> Any:
|
def _cardConf(self, card: Card) -> Any:
|
||||||
return self.col.decks.confForDid(card.did)
|
return self.col.decks.confForDid(card.did)
|
||||||
|
|
||||||
def _newConf(self, card) -> Any:
|
def _newConf(self, card: Card) -> Any:
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
# normal deck
|
# normal deck
|
||||||
if not card.odid:
|
if not card.odid:
|
||||||
|
@ -1159,7 +1161,7 @@ where id = ?
|
||||||
perDay=self.reportLimit
|
perDay=self.reportLimit
|
||||||
)
|
)
|
||||||
|
|
||||||
def _lapseConf(self, card) -> Any:
|
def _lapseConf(self, card: Card) -> Any:
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
# normal deck
|
# normal deck
|
||||||
if not card.odid:
|
if not card.odid:
|
||||||
|
@ -1177,7 +1179,7 @@ where id = ?
|
||||||
resched=conf['resched'],
|
resched=conf['resched'],
|
||||||
)
|
)
|
||||||
|
|
||||||
def _revConf(self, card) -> Any:
|
def _revConf(self, card: Card) -> Any:
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
# normal deck
|
# normal deck
|
||||||
if not card.odid:
|
if not card.odid:
|
||||||
|
@ -1188,11 +1190,11 @@ where id = ?
|
||||||
def _deckLimit(self) -> str:
|
def _deckLimit(self) -> str:
|
||||||
return ids2str(self.col.decks.active())
|
return ids2str(self.col.decks.active())
|
||||||
|
|
||||||
def _previewingCard(self, card) -> Any:
|
def _previewingCard(self, card: Card) -> Any:
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
return conf['dyn'] and not conf['resched']
|
return conf['dyn'] and not conf['resched']
|
||||||
|
|
||||||
def _previewDelay(self, card) -> Any:
|
def _previewDelay(self, card: Card) -> Any:
|
||||||
return self._cardConf(card).get("previewDelay", 10)*60
|
return self._cardConf(card).get("previewDelay", 10)*60
|
||||||
|
|
||||||
# Daily cutoff
|
# Daily cutoff
|
||||||
|
@ -1310,7 +1312,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
# Next time reports
|
# Next time reports
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def nextIvlStr(self, card, ease, short=False) -> Any:
|
def nextIvlStr(self, card: Card, ease: int, short: bool = False) -> Any:
|
||||||
"Return the next interval for CARD as a string."
|
"Return the next interval for CARD as a string."
|
||||||
ivl = self.nextIvl(card, ease)
|
ivl = self.nextIvl(card, ease)
|
||||||
if not ivl:
|
if not ivl:
|
||||||
|
@ -1320,7 +1322,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
s = "<"+s
|
s = "<"+s
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def nextIvl(self, card, ease) -> Any:
|
def nextIvl(self, card: Card, ease: int) -> Any:
|
||||||
"Return the next interval for CARD, in seconds."
|
"Return the next interval for CARD, in seconds."
|
||||||
# preview mode?
|
# preview mode?
|
||||||
if self._previewingCard(card):
|
if self._previewingCard(card):
|
||||||
|
@ -1346,7 +1348,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
return self._nextRevIvl(card, ease, fuzz=False)*86400
|
return self._nextRevIvl(card, ease, fuzz=False)*86400
|
||||||
|
|
||||||
# this isn't easily extracted from the learn code
|
# this isn't easily extracted from the learn code
|
||||||
def _nextLrnIvl(self, card, ease) -> Any:
|
def _nextLrnIvl(self, card: Card, ease: int) -> Any:
|
||||||
if card.queue == 0:
|
if card.queue == 0:
|
||||||
card.left = self._startingLeft(card)
|
card.left = self._startingLeft(card)
|
||||||
conf = self._lrnConf(card)
|
conf = self._lrnConf(card)
|
||||||
|
@ -1378,14 +1380,14 @@ else
|
||||||
end)
|
end)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def suspendCards(self, ids) -> None:
|
def suspendCards(self, ids: List[int]) -> None:
|
||||||
"Suspend cards."
|
"Suspend cards."
|
||||||
self.col.log(ids)
|
self.col.log(ids)
|
||||||
self.col.db.execute(
|
self.col.db.execute(
|
||||||
"update cards set queue=-1,mod=?,usn=? where id in "+
|
"update cards set queue=-1,mod=?,usn=? where id in "+
|
||||||
ids2str(ids), intTime(), self.col.usn())
|
ids2str(ids), intTime(), self.col.usn())
|
||||||
|
|
||||||
def unsuspendCards(self, ids) -> None:
|
def unsuspendCards(self, ids: List[int]) -> None:
|
||||||
"Unsuspend cards."
|
"Unsuspend cards."
|
||||||
self.col.log(ids)
|
self.col.log(ids)
|
||||||
self.col.db.execute(
|
self.col.db.execute(
|
||||||
|
@ -1393,7 +1395,7 @@ end)
|
||||||
"where queue = -1 and id in %s") % (self._restoreQueueSnippet, ids2str(ids)),
|
"where queue = -1 and id in %s") % (self._restoreQueueSnippet, ids2str(ids)),
|
||||||
intTime(), self.col.usn())
|
intTime(), self.col.usn())
|
||||||
|
|
||||||
def buryCards(self, cids, manual=True) -> None:
|
def buryCards(self, cids: List[int], manual: bool = True) -> None:
|
||||||
queue = manual and -3 or -2
|
queue = manual and -3 or -2
|
||||||
self.col.log(cids)
|
self.col.log(cids)
|
||||||
self.col.db.execute("""
|
self.col.db.execute("""
|
||||||
|
@ -1413,7 +1415,7 @@ update cards set queue=?,mod=?,usn=? where id in """+ids2str(cids),
|
||||||
self.col.db.execute(
|
self.col.db.execute(
|
||||||
"update cards set %s where queue in (-2, -3)" % self._restoreQueueSnippet)
|
"update cards set %s where queue in (-2, -3)" % self._restoreQueueSnippet)
|
||||||
|
|
||||||
def unburyCardsForDeck(self, type="all") -> None:
|
def unburyCardsForDeck(self, type: str = "all") -> None:
|
||||||
if type == "all":
|
if type == "all":
|
||||||
queue = "queue in (-2, -3)"
|
queue = "queue in (-2, -3)"
|
||||||
elif type == "manual":
|
elif type == "manual":
|
||||||
|
@ -1434,7 +1436,7 @@ update cards set queue=?,mod=?,usn=? where id in """+ids2str(cids),
|
||||||
# Sibling spacing
|
# Sibling spacing
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _burySiblings(self, card) -> None:
|
def _burySiblings(self, card: Card) -> None:
|
||||||
toBury = []
|
toBury = []
|
||||||
nconf = self._newConf(card)
|
nconf = self._newConf(card)
|
||||||
buryNew = nconf.get("bury", True)
|
buryNew = nconf.get("bury", True)
|
||||||
|
@ -1468,7 +1470,7 @@ and (queue=0 or (queue=2 and due<=?))""",
|
||||||
# Resetting
|
# Resetting
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def forgetCards(self, ids) -> None:
|
def forgetCards(self, ids: List[int]) -> None:
|
||||||
"Put cards at the end of the new queue."
|
"Put cards at the end of the new queue."
|
||||||
self.remFromDyn(ids)
|
self.remFromDyn(ids)
|
||||||
self.col.db.execute(
|
self.col.db.execute(
|
||||||
|
@ -1480,7 +1482,7 @@ and (queue=0 or (queue=2 and due<=?))""",
|
||||||
self.sortCards(ids, start=pmax+1)
|
self.sortCards(ids, start=pmax+1)
|
||||||
self.col.log(ids)
|
self.col.log(ids)
|
||||||
|
|
||||||
def reschedCards(self, ids, imin, imax) -> None:
|
def reschedCards(self, ids: List[int], imin: int, imax: int) -> None:
|
||||||
"Put cards in review queue with a new interval in days (min, max)."
|
"Put cards in review queue with a new interval in days (min, max)."
|
||||||
d = []
|
d = []
|
||||||
t = self.today
|
t = self.today
|
||||||
|
@ -1496,7 +1498,7 @@ usn=:usn,mod=:mod,factor=:fact where id=:id""",
|
||||||
d)
|
d)
|
||||||
self.col.log(ids)
|
self.col.log(ids)
|
||||||
|
|
||||||
def resetCards(self, ids) -> None:
|
def resetCards(self, ids: List[int]) -> None:
|
||||||
"Completely reset cards for export."
|
"Completely reset cards for export."
|
||||||
sids = ids2str(ids)
|
sids = ids2str(ids)
|
||||||
# we want to avoid resetting due number of existing new cards on export
|
# we want to avoid resetting due number of existing new cards on export
|
||||||
|
@ -1515,7 +1517,7 @@ usn=:usn,mod=:mod,factor=:fact where id=:id""",
|
||||||
# Repositioning new cards
|
# Repositioning new cards
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def sortCards(self, cids, start=1, step=1, shuffle=False, shift=False) -> None:
|
def sortCards(self, cids: List[int], start: int = 1, step: int = 1, shuffle: bool = False, shift: bool = False) -> None:
|
||||||
scids = ids2str(cids)
|
scids = ids2str(cids)
|
||||||
now = intTime()
|
now = intTime()
|
||||||
nids = []
|
nids = []
|
||||||
|
@ -1555,11 +1557,11 @@ and due >= ? and queue = 0""" % scids, now, self.col.usn(), shiftby, low)
|
||||||
self.col.db.executemany(
|
self.col.db.executemany(
|
||||||
"update cards set due=:due,mod=:now,usn=:usn where id = :cid", d)
|
"update cards set due=:due,mod=:now,usn=:usn where id = :cid", d)
|
||||||
|
|
||||||
def randomizeCards(self, did) -> None:
|
def randomizeCards(self, did: int) -> None:
|
||||||
cids = self.col.db.list("select id from cards where did = ?", did)
|
cids = self.col.db.list("select id from cards where did = ?", did)
|
||||||
self.sortCards(cids, shuffle=True)
|
self.sortCards(cids, shuffle=True)
|
||||||
|
|
||||||
def orderCards(self, did) -> None:
|
def orderCards(self, did: int) -> None:
|
||||||
cids = self.col.db.list("select id from cards where did = ? order by id", did)
|
cids = self.col.db.list("select id from cards where did = ? order by id", did)
|
||||||
self.sortCards(cids)
|
self.sortCards(cids)
|
||||||
|
|
||||||
|
@ -1571,7 +1573,7 @@ and due >= ? and queue = 0""" % scids, now, self.col.usn(), shiftby, low)
|
||||||
self.orderCards(did)
|
self.orderCards(did)
|
||||||
|
|
||||||
# for post-import
|
# for post-import
|
||||||
def maybeRandomizeDeck(self, did=None) -> None:
|
def maybeRandomizeDeck(self, did: Optional[int] = None) -> None:
|
||||||
if not did:
|
if not did:
|
||||||
did = self.col.decks.selected()
|
did = self.col.decks.selected()
|
||||||
conf = self.col.decks.confForDid(did)
|
conf = self.col.decks.confForDid(did)
|
||||||
|
@ -1594,7 +1596,7 @@ else type end),
|
||||||
due = odue, odue = 0, odid = 0, usn = ? where odid != 0""",
|
due = odue, odue = 0, odid = 0, usn = ? where odid != 0""",
|
||||||
self.col.usn())
|
self.col.usn())
|
||||||
|
|
||||||
def _removeAllFromLearning(self, schedVer=2) -> None:
|
def _removeAllFromLearning(self, schedVer: int = 2) -> None:
|
||||||
# remove review cards from relearning
|
# remove review cards from relearning
|
||||||
if schedVer == 1:
|
if schedVer == 1:
|
||||||
self.col.db.execute("""
|
self.col.db.execute("""
|
||||||
|
@ -1630,7 +1632,7 @@ where queue < 0""" % (intTime(), self.col.usn()))
|
||||||
|
|
||||||
# adding 'hard' in v2 scheduler means old ease entries need shifting
|
# adding 'hard' in v2 scheduler means old ease entries need shifting
|
||||||
# up or down
|
# up or down
|
||||||
def _remapLearningAnswers(self, sql) -> None:
|
def _remapLearningAnswers(self, sql: str) -> None:
|
||||||
self.col.db.execute("update revlog set %s and type in (0,2)" % sql)
|
self.col.db.execute("update revlog set %s and type in (0,2)" % sql)
|
||||||
|
|
||||||
def moveToV1(self) -> None:
|
def moveToV1(self) -> None:
|
||||||
|
|
Loading…
Reference in a new issue