mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
drop some unused code
This commit is contained in:
parent
3af5221895
commit
f82cf0cd4b
1 changed files with 33 additions and 499 deletions
|
@ -540,505 +540,6 @@ limit ?"""
|
|||
def _deckLimit(self) -> str:
|
||||
return ids2str(self.col.decks.active())
|
||||
|
||||
# Answering (re)learning cards
|
||||
##########################################################################
|
||||
|
||||
def _newConf(self, card: Card) -> Any:
|
||||
conf = self._cardConf(card)
|
||||
# normal deck
|
||||
if not card.odid:
|
||||
return conf["new"]
|
||||
# dynamic deck; override some attributes, use original deck for others
|
||||
oconf = self.col.decks.confForDid(card.odid)
|
||||
return dict(
|
||||
# original deck
|
||||
ints=oconf["new"]["ints"],
|
||||
initialFactor=oconf["new"]["initialFactor"],
|
||||
bury=oconf["new"].get("bury", True),
|
||||
delays=oconf["new"]["delays"],
|
||||
# overrides
|
||||
order=NEW_CARDS_DUE,
|
||||
perDay=self.reportLimit,
|
||||
)
|
||||
|
||||
def _lapseConf(self, card: Card) -> Any:
|
||||
conf = self._cardConf(card)
|
||||
# normal deck
|
||||
if not card.odid:
|
||||
return conf["lapse"]
|
||||
# dynamic deck; override some attributes, use original deck for others
|
||||
oconf = self.col.decks.confForDid(card.odid)
|
||||
return dict(
|
||||
# original deck
|
||||
minInt=oconf["lapse"]["minInt"],
|
||||
leechFails=oconf["lapse"]["leechFails"],
|
||||
leechAction=oconf["lapse"]["leechAction"],
|
||||
mult=oconf["lapse"]["mult"],
|
||||
delays=oconf["lapse"]["delays"],
|
||||
# overrides
|
||||
resched=conf["resched"],
|
||||
)
|
||||
|
||||
def _answerLrnCard(self, card: Card, ease: int) -> None:
|
||||
conf = self._lrnConf(card)
|
||||
if card.type in (CARD_TYPE_REV, CARD_TYPE_RELEARNING):
|
||||
type = REVLOG_RELRN
|
||||
else:
|
||||
type = REVLOG_LRN
|
||||
# lrnCount was decremented once when card was fetched
|
||||
lastLeft = card.left
|
||||
|
||||
leaving = False
|
||||
|
||||
# immediate graduate?
|
||||
if ease == BUTTON_FOUR:
|
||||
self._rescheduleAsRev(card, conf, True)
|
||||
leaving = True
|
||||
# next step?
|
||||
elif ease == BUTTON_THREE:
|
||||
# graduation time?
|
||||
if (card.left % 1000) - 1 <= 0:
|
||||
self._rescheduleAsRev(card, conf, False)
|
||||
leaving = True
|
||||
else:
|
||||
self._moveToNextStep(card, conf)
|
||||
elif ease == BUTTON_TWO:
|
||||
self._repeatStep(card, conf)
|
||||
else:
|
||||
# back to first step
|
||||
self._moveToFirstStep(card, conf)
|
||||
|
||||
self._logLrn(card, ease, conf, leaving, type, lastLeft)
|
||||
|
||||
def _updateRevIvlOnFail(self, card: Card, conf: QueueConfig) -> None:
|
||||
card.lastIvl = card.ivl
|
||||
card.ivl = self._lapseIvl(card, conf)
|
||||
|
||||
def _moveToFirstStep(self, card: Card, conf: QueueConfig) -> Any:
|
||||
card.left = self._startingLeft(card)
|
||||
|
||||
# relearning card?
|
||||
if card.type == CARD_TYPE_RELEARNING:
|
||||
self._updateRevIvlOnFail(card, conf)
|
||||
|
||||
return self._rescheduleLrnCard(card, conf)
|
||||
|
||||
def _moveToNextStep(self, card: Card, conf: QueueConfig) -> None:
|
||||
# decrement real left count and recalculate left today
|
||||
left = (card.left % 1000) - 1
|
||||
card.left = self._leftToday(conf["delays"], left) * 1000 + left
|
||||
|
||||
self._rescheduleLrnCard(card, conf)
|
||||
|
||||
def _repeatStep(self, card: Card, conf: QueueConfig) -> None:
|
||||
delay = self._delayForRepeatingGrade(conf, card.left)
|
||||
self._rescheduleLrnCard(card, conf, delay=delay)
|
||||
|
||||
def _rescheduleLrnCard(
|
||||
self, card: Card, conf: QueueConfig, delay: Optional[int] = None
|
||||
) -> Any:
|
||||
# normal delay for the current step?
|
||||
if delay is None:
|
||||
delay = self._delayForGrade(conf, card.left)
|
||||
|
||||
card.due = int(time.time() + delay)
|
||||
# due today?
|
||||
if card.due < self.dayCutoff:
|
||||
# add some randomness, up to 5 minutes or 25%
|
||||
maxExtra = min(300, int(delay * 0.25))
|
||||
fuzz = random.randrange(0, max(1, maxExtra))
|
||||
card.due = min(self.dayCutoff - 1, card.due + fuzz)
|
||||
card.queue = QUEUE_TYPE_LRN
|
||||
else:
|
||||
# the card is due in one or more days, so we need to use the
|
||||
# day learn queue
|
||||
ahead = ((card.due - self.dayCutoff) // 86400) + 1
|
||||
card.due = self.today + ahead
|
||||
card.queue = QUEUE_TYPE_DAY_LEARN_RELEARN
|
||||
return delay
|
||||
|
||||
def _delayForGrade(self, conf: QueueConfig, left: int) -> int:
|
||||
left = left % 1000
|
||||
try:
|
||||
delay = conf["delays"][-left]
|
||||
except IndexError:
|
||||
if conf["delays"]:
|
||||
delay = conf["delays"][0]
|
||||
else:
|
||||
# user deleted final step; use dummy value
|
||||
delay = 1
|
||||
return int(delay * 60)
|
||||
|
||||
def _delayForRepeatingGrade(self, conf: QueueConfig, left: int) -> Any:
|
||||
# halfway between last and next
|
||||
delay1 = self._delayForGrade(conf, left)
|
||||
if len(conf["delays"]) > 1:
|
||||
delay2 = self._delayForGrade(conf, left - 1)
|
||||
else:
|
||||
delay2 = delay1 * 2
|
||||
avg = (delay1 + max(delay1, delay2)) // 2
|
||||
return avg
|
||||
|
||||
def _lrnConf(self, card: Card) -> Any:
|
||||
if card.type in (CARD_TYPE_REV, CARD_TYPE_RELEARNING):
|
||||
return self._lapseConf(card)
|
||||
else:
|
||||
return self._newConf(card)
|
||||
|
||||
def _rescheduleAsRev(self, card: Card, conf: QueueConfig, early: bool) -> None:
|
||||
lapse = card.type in (CARD_TYPE_REV, CARD_TYPE_RELEARNING)
|
||||
|
||||
if lapse:
|
||||
self._rescheduleGraduatingLapse(card, early)
|
||||
else:
|
||||
self._rescheduleNew(card, conf, early)
|
||||
|
||||
# if we were dynamic, graduating means moving back to the old deck
|
||||
if card.odid:
|
||||
self._removeFromFiltered(card)
|
||||
|
||||
def _rescheduleGraduatingLapse(self, card: Card, early: bool = False) -> None:
|
||||
if early:
|
||||
card.ivl += 1
|
||||
card.due = self.today + card.ivl
|
||||
card.queue = QUEUE_TYPE_REV
|
||||
card.type = CARD_TYPE_REV
|
||||
|
||||
def _startingLeft(self, card: Card) -> int:
|
||||
if card.type == CARD_TYPE_RELEARNING:
|
||||
conf = self._lapseConf(card)
|
||||
else:
|
||||
conf = self._lrnConf(card)
|
||||
tot = len(conf["delays"])
|
||||
tod = self._leftToday(conf["delays"], tot)
|
||||
return tot + tod * 1000
|
||||
|
||||
def _leftToday(
|
||||
self,
|
||||
delays: List[int],
|
||||
left: int,
|
||||
now: Optional[int] = None,
|
||||
) -> int:
|
||||
"The number of steps that can be completed by the day cutoff."
|
||||
if not now:
|
||||
now = intTime()
|
||||
delays = delays[-left:]
|
||||
ok = 0
|
||||
for i in range(len(delays)):
|
||||
now += int(delays[i] * 60)
|
||||
if now > self.dayCutoff:
|
||||
break
|
||||
ok = i
|
||||
return ok + 1
|
||||
|
||||
def _graduatingIvl(
|
||||
self, card: Card, conf: QueueConfig, early: bool, fuzz: bool = True
|
||||
) -> Any:
|
||||
if card.type in (CARD_TYPE_REV, CARD_TYPE_RELEARNING):
|
||||
bonus = early and 1 or 0
|
||||
return card.ivl + bonus
|
||||
if not early:
|
||||
# graduate
|
||||
ideal = conf["ints"][0]
|
||||
else:
|
||||
# early remove
|
||||
ideal = conf["ints"][1]
|
||||
if fuzz:
|
||||
ideal = self._fuzzedIvl(ideal)
|
||||
return ideal
|
||||
|
||||
def _rescheduleNew(self, card: Card, conf: QueueConfig, early: bool) -> None:
|
||||
"Reschedule a new card that's graduated for the first time."
|
||||
card.ivl = self._graduatingIvl(card, conf, early)
|
||||
card.due = self.today + card.ivl
|
||||
card.factor = conf["initialFactor"]
|
||||
card.type = card.queue = QUEUE_TYPE_REV
|
||||
|
||||
def _logLrn(
|
||||
self,
|
||||
card: Card,
|
||||
ease: int,
|
||||
conf: QueueConfig,
|
||||
leaving: bool,
|
||||
type: int,
|
||||
lastLeft: int,
|
||||
) -> None:
|
||||
lastIvl = -(self._delayForGrade(conf, lastLeft))
|
||||
if leaving:
|
||||
ivl = card.ivl
|
||||
else:
|
||||
if ease == BUTTON_TWO:
|
||||
ivl = -self._delayForRepeatingGrade(conf, card.left)
|
||||
else:
|
||||
ivl = -self._delayForGrade(conf, card.left)
|
||||
|
||||
def log() -> None:
|
||||
self.col.db.execute(
|
||||
"insert into revlog values (?,?,?,?,?,?,?,?,?)",
|
||||
int(time.time() * 1000),
|
||||
card.id,
|
||||
self.col.usn(),
|
||||
ease,
|
||||
ivl,
|
||||
lastIvl,
|
||||
card.factor,
|
||||
card.timeTaken(),
|
||||
type,
|
||||
)
|
||||
|
||||
try:
|
||||
log()
|
||||
except:
|
||||
# duplicate pk; retry in 10ms
|
||||
time.sleep(0.01)
|
||||
log()
|
||||
|
||||
# note: when adding revlog entries in the future, make sure undo
|
||||
# code deletes the entries
|
||||
def _answerCardPreview(self, card: Card, ease: int) -> None:
|
||||
assert 1 <= ease <= 2
|
||||
|
||||
if ease == BUTTON_ONE:
|
||||
# repeat after delay
|
||||
card.queue = QUEUE_TYPE_PREVIEW
|
||||
card.due = intTime() + self._previewDelay(card)
|
||||
else:
|
||||
# BUTTON_TWO
|
||||
# restore original card state and remove from filtered deck
|
||||
self._restorePreviewCard(card)
|
||||
self._removeFromFiltered(card)
|
||||
|
||||
def _previewingCard(self, card: Card) -> Any:
|
||||
conf = self._cardConf(card)
|
||||
return conf["dyn"] and not conf["resched"]
|
||||
|
||||
def _previewDelay(self, card: Card) -> Any:
|
||||
return self._cardConf(card).get("previewDelay", 10) * 60
|
||||
|
||||
def _removeFromFiltered(self, card: Card) -> None:
|
||||
if card.odid:
|
||||
card.did = card.odid
|
||||
card.odue = 0
|
||||
card.odid = 0
|
||||
|
||||
def _restorePreviewCard(self, card: Card) -> None:
|
||||
assert card.odid
|
||||
|
||||
card.due = card.odue
|
||||
|
||||
# learning and relearning cards may be seconds-based or day-based;
|
||||
# other types map directly to queues
|
||||
if card.type in (CARD_TYPE_LRN, CARD_TYPE_RELEARNING):
|
||||
if card.odue > 1000000000:
|
||||
card.queue = QUEUE_TYPE_LRN
|
||||
else:
|
||||
card.queue = QUEUE_TYPE_DAY_LEARN_RELEARN
|
||||
else:
|
||||
card.queue = card.type
|
||||
|
||||
# Answering a review card
|
||||
##########################################################################
|
||||
|
||||
def _revConf(self, card: Card) -> QueueConfig:
|
||||
conf = self._cardConf(card)
|
||||
# normal deck
|
||||
if not card.odid:
|
||||
return conf["rev"]
|
||||
# dynamic deck
|
||||
return self.col.decks.confForDid(card.odid)["rev"]
|
||||
|
||||
def _answerRevCard(self, card: Card, ease: int) -> None:
|
||||
delay = 0
|
||||
early = bool(card.odid and (card.odue > self.today))
|
||||
type = early and REVLOG_CRAM or REVLOG_REV
|
||||
|
||||
if ease == BUTTON_ONE:
|
||||
delay = self._rescheduleLapse(card)
|
||||
else:
|
||||
self._rescheduleRev(card, ease, early)
|
||||
|
||||
hooks.schedv2_did_answer_review_card(card, ease, early)
|
||||
self._logRev(card, ease, delay, type)
|
||||
|
||||
def _rescheduleLapse(self, card: Card) -> Any:
|
||||
conf = self._lapseConf(card)
|
||||
|
||||
card.lapses += 1
|
||||
card.factor = max(1300, card.factor - 200)
|
||||
|
||||
suspended = self._checkLeech(card, conf) and card.queue == QUEUE_TYPE_SUSPENDED
|
||||
|
||||
if conf["delays"] and not suspended:
|
||||
card.type = CARD_TYPE_RELEARNING
|
||||
delay = self._moveToFirstStep(card, conf)
|
||||
else:
|
||||
# no relearning steps
|
||||
self._updateRevIvlOnFail(card, conf)
|
||||
self._rescheduleAsRev(card, conf, early=False)
|
||||
# need to reset the queue after rescheduling
|
||||
if suspended:
|
||||
card.queue = QUEUE_TYPE_SUSPENDED
|
||||
delay = 0
|
||||
|
||||
return delay
|
||||
|
||||
def _lapseIvl(self, card: Card, conf: QueueConfig) -> Any:
|
||||
ivl = max(1, conf["minInt"], int(card.ivl * conf["mult"]))
|
||||
return ivl
|
||||
|
||||
def _rescheduleRev(self, card: Card, ease: int, early: bool) -> None:
|
||||
# update interval
|
||||
card.lastIvl = card.ivl
|
||||
if early:
|
||||
self._updateEarlyRevIvl(card, ease)
|
||||
else:
|
||||
self._updateRevIvl(card, ease)
|
||||
|
||||
# then the rest
|
||||
card.factor = max(1300, card.factor + [-150, 0, 150][ease - 2])
|
||||
card.due = self.today + card.ivl
|
||||
|
||||
# card leaves filtered deck
|
||||
self._removeFromFiltered(card)
|
||||
|
||||
def _logRev(self, card: Card, ease: int, delay: int, type: int) -> None:
|
||||
def log() -> None:
|
||||
self.col.db.execute(
|
||||
"insert into revlog values (?,?,?,?,?,?,?,?,?)",
|
||||
int(time.time() * 1000),
|
||||
card.id,
|
||||
self.col.usn(),
|
||||
ease,
|
||||
-delay or card.ivl,
|
||||
card.lastIvl,
|
||||
card.factor,
|
||||
card.timeTaken(),
|
||||
type,
|
||||
)
|
||||
|
||||
try:
|
||||
log()
|
||||
except:
|
||||
# duplicate pk; retry in 10ms
|
||||
time.sleep(0.01)
|
||||
log()
|
||||
|
||||
def _nextRevIvl(self, card: Card, ease: int, fuzz: bool) -> int:
|
||||
"Next review interval for CARD, given EASE."
|
||||
delay = self._daysLate(card)
|
||||
conf = self._revConf(card)
|
||||
fct = card.factor / 1000
|
||||
hardFactor = conf.get("hardFactor", 1.2)
|
||||
if hardFactor > 1:
|
||||
hardMin = card.ivl
|
||||
else:
|
||||
hardMin = 0
|
||||
ivl2 = self._constrainedIvl(card.ivl * hardFactor, conf, hardMin, fuzz)
|
||||
if ease == BUTTON_TWO:
|
||||
return ivl2
|
||||
|
||||
ivl3 = self._constrainedIvl((card.ivl + delay // 2) * fct, conf, ivl2, fuzz)
|
||||
if ease == BUTTON_THREE:
|
||||
return ivl3
|
||||
|
||||
ivl4 = self._constrainedIvl(
|
||||
(card.ivl + delay) * fct * conf["ease4"], conf, ivl3, fuzz
|
||||
)
|
||||
return ivl4
|
||||
|
||||
def _fuzzedIvl(self, ivl: int) -> int:
|
||||
min, max = self._fuzzIvlRange(ivl)
|
||||
return random.randint(min, max)
|
||||
|
||||
def _fuzzIvlRange(self, ivl: int) -> List[int]:
|
||||
if ivl < 2:
|
||||
return [1, 1]
|
||||
elif ivl == 2:
|
||||
return [2, 3]
|
||||
elif ivl < 7:
|
||||
fuzz = int(ivl * 0.25)
|
||||
elif ivl < 30:
|
||||
fuzz = max(2, int(ivl * 0.15))
|
||||
else:
|
||||
fuzz = max(4, int(ivl * 0.05))
|
||||
# fuzz at least a day
|
||||
fuzz = max(fuzz, 1)
|
||||
return [ivl - fuzz, ivl + fuzz]
|
||||
|
||||
def _constrainedIvl(
|
||||
self, ivl: float, conf: QueueConfig, prev: int, fuzz: bool
|
||||
) -> int:
|
||||
ivl = int(ivl * conf.get("ivlFct", 1))
|
||||
if fuzz:
|
||||
ivl = self._fuzzedIvl(ivl)
|
||||
ivl = max(ivl, prev + 1, 1)
|
||||
ivl = min(ivl, conf["maxIvl"])
|
||||
return int(ivl)
|
||||
|
||||
def _daysLate(self, card: Card) -> int:
|
||||
"Number of days later than scheduled."
|
||||
due = card.odue if card.odid else card.due
|
||||
return max(0, self.today - due)
|
||||
|
||||
def _updateRevIvl(self, card: Card, ease: int) -> None:
|
||||
card.ivl = self._nextRevIvl(card, ease, fuzz=True)
|
||||
|
||||
def _updateEarlyRevIvl(self, card: Card, ease: int) -> None:
|
||||
card.ivl = self._earlyReviewIvl(card, ease)
|
||||
|
||||
# next interval for card when answered early+correctly
|
||||
def _earlyReviewIvl(self, card: Card, ease: int) -> int:
|
||||
assert card.odid and card.type == CARD_TYPE_REV
|
||||
assert card.factor
|
||||
assert ease > 1
|
||||
|
||||
elapsed = card.ivl - (card.odue - self.today)
|
||||
|
||||
conf = self._revConf(card)
|
||||
|
||||
easyBonus = 1
|
||||
# early 3/4 reviews shouldn't decrease previous interval
|
||||
minNewIvl = 1
|
||||
|
||||
if ease == BUTTON_TWO:
|
||||
factor = conf.get("hardFactor", 1.2)
|
||||
# hard cards shouldn't have their interval decreased by more than 50%
|
||||
# of the normal factor
|
||||
minNewIvl = factor / 2
|
||||
elif ease == BUTTON_THREE:
|
||||
factor = card.factor / 1000
|
||||
else: # ease == BUTTON_FOUR:
|
||||
factor = card.factor / 1000
|
||||
ease4 = conf["ease4"]
|
||||
# 1.3 -> 1.15
|
||||
easyBonus = ease4 - (ease4 - 1) / 2
|
||||
|
||||
ivl = max(elapsed * factor, 1)
|
||||
|
||||
# cap interval decreases
|
||||
ivl = max(card.ivl * minNewIvl, ivl) * easyBonus
|
||||
|
||||
ivl = self._constrainedIvl(ivl, conf, prev=0, fuzz=False)
|
||||
|
||||
return ivl
|
||||
|
||||
# Daily limits
|
||||
##########################################################################
|
||||
|
||||
def update_stats(
|
||||
self,
|
||||
deck_id: int,
|
||||
new_delta: int = 0,
|
||||
review_delta: int = 0,
|
||||
milliseconds_delta: int = 0,
|
||||
) -> None:
|
||||
self.col._backend.update_stats(
|
||||
deck_id=deck_id,
|
||||
new_delta=new_delta,
|
||||
review_delta=review_delta,
|
||||
millisecond_delta=milliseconds_delta,
|
||||
)
|
||||
|
||||
def counts_for_deck_today(self, deck_id: int) -> CountsForDeckToday:
|
||||
return self.col._backend.counts_for_deck_today(deck_id)
|
||||
|
||||
|
@ -1340,6 +841,10 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l
|
|||
del d["col"]
|
||||
return f"{super().__repr__()} {pprint.pformat(d, width=300)}"
|
||||
|
||||
# unit tests
|
||||
def _fuzzIvlRange(self, ivl: int) -> Tuple[int, int]:
|
||||
return (ivl, ivl)
|
||||
|
||||
# Legacy aliases and helpers
|
||||
##########################################################################
|
||||
|
||||
|
@ -1412,6 +917,20 @@ due = (case when odue>0 then odue else due end), odue = 0, odid = 0, usn = ? whe
|
|||
def remFromDyn(self, cids: List[int]) -> None:
|
||||
self.emptyDyn(None, f"id in {ids2str(cids)} and odid")
|
||||
|
||||
def update_stats(
|
||||
self,
|
||||
deck_id: int,
|
||||
new_delta: int = 0,
|
||||
review_delta: int = 0,
|
||||
milliseconds_delta: int = 0,
|
||||
) -> None:
|
||||
self.col._backend.update_stats(
|
||||
deck_id=deck_id,
|
||||
new_delta=new_delta,
|
||||
review_delta=review_delta,
|
||||
millisecond_delta=milliseconds_delta,
|
||||
)
|
||||
|
||||
def _updateStats(self, card: Card, type: str, cnt: int = 1) -> None:
|
||||
did = card.did
|
||||
if type == "new":
|
||||
|
@ -1428,6 +947,21 @@ due = (case when odue>0 then odue else due end), odue = 0, odid = 0, usn = ? whe
|
|||
)
|
||||
return from_json_bytes(self.col._backend.deck_tree_legacy())[5]
|
||||
|
||||
def _newConf(self, card: Card) -> QueueConfig:
|
||||
return self._home_config(card)["new"]
|
||||
|
||||
def _lapseConf(self, card: Card) -> QueueConfig:
|
||||
return self._home_config(card)["lapse"]
|
||||
|
||||
def _revConf(self, card: Card) -> QueueConfig:
|
||||
return self._home_config(card)["rev"]
|
||||
|
||||
def _lrnConf(self, card: Card) -> QueueConfig:
|
||||
if card.type in (CARD_TYPE_REV, CARD_TYPE_RELEARNING):
|
||||
return self._lapseConf(card)
|
||||
else:
|
||||
return self._newConf(card)
|
||||
|
||||
unsuspendCards = unsuspend_cards
|
||||
buryCards = bury_cards
|
||||
suspendCards = suspend_cards
|
||||
|
|
Loading…
Reference in a new issue