mirror of
https://github.com/ankitects/anki.git
synced 2025-11-06 20:57:13 -05:00
encode daily steps in left
When a user has learning steps that extend past the daily cutoff, we end up counting them all instead of only the ones that would be done today. In order to avoid this without expensive calculations or db schema changes, we calculate the number of steps until the daily cutoff and pack it into the left column, as totalLeft + leftToday*1000.
This commit is contained in:
parent
33407a6043
commit
32fde2a072
6 changed files with 45 additions and 19 deletions
|
|
@ -42,10 +42,10 @@ MODEL_STD = 0
|
|||
MODEL_CLOZE = 1
|
||||
|
||||
# deck schema & syncing vars
|
||||
SCHEMA_VERSION = 9
|
||||
SCHEMA_VERSION = 10
|
||||
SYNC_ZIP_SIZE = int(2.5*1024*1024)
|
||||
SYNC_URL = os.environ.get("SYNC_URL") or "https://beta.ankiweb.net/sync/"
|
||||
SYNC_VER = 3
|
||||
SYNC_VER = 4
|
||||
|
||||
# Labels
|
||||
##########################################################################
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class Scheduler(object):
|
|||
if card:
|
||||
idx = self.countIdx(card)
|
||||
if idx == 1:
|
||||
counts[1] += card.left
|
||||
counts[1] += card.left/1000
|
||||
else:
|
||||
counts[idx] += 1
|
||||
return tuple(counts)
|
||||
|
|
@ -388,7 +388,7 @@ select count() from
|
|||
|
||||
def _resetLrnCount(self):
|
||||
self.lrnCount = self.col.db.scalar("""
|
||||
select sum(left) from (select left from cards where
|
||||
select sum(left/1000) from (select left from cards where
|
||||
did in %s and queue = 1 and due < ? limit %d)""" % (
|
||||
self._deckLimit(), self.reportLimit),
|
||||
self.dayCutoff) or 0
|
||||
|
|
@ -418,7 +418,7 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff)
|
|||
if self._lrnQueue[0][0] < cutoff:
|
||||
id = heappop(self._lrnQueue)[1]
|
||||
card = self.col.getCard(id)
|
||||
self.lrnCount -= card.left
|
||||
self.lrnCount -= card.left/1000
|
||||
return card
|
||||
|
||||
def _answerLrnCard(self, card, ease):
|
||||
|
|
@ -438,13 +438,15 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff)
|
|||
self._rescheduleAsRev(card, conf, True)
|
||||
leaving = True
|
||||
# graduation time?
|
||||
elif ease == 2 and card.left-1 <= 0:
|
||||
elif ease == 2 and (card.left%1000)-1 <= 0:
|
||||
self._rescheduleAsRev(card, conf, False)
|
||||
leaving = True
|
||||
else:
|
||||
# one step towards graduation
|
||||
if ease == 2:
|
||||
card.left -= 1
|
||||
# decrement real left count and recalculate left today
|
||||
left = (card.left % 1000) - 1
|
||||
card.left = self._leftToday(conf['delays'], left)*1000 + left
|
||||
# failed
|
||||
else:
|
||||
card.left = self._startingLeft(card)
|
||||
|
|
@ -462,7 +464,7 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff)
|
|||
delay *= random.uniform(1, 1.25)
|
||||
card.due = int(time.time() + delay)
|
||||
if card.due < self.dayCutoff:
|
||||
self.lrnCount += card.left
|
||||
self.lrnCount += card.left/1000
|
||||
# if the queue is not empty and there's nothing else to do, make
|
||||
# sure we don't put it at the head of the queue and end up showing
|
||||
# it twice in a row
|
||||
|
|
@ -473,6 +475,7 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff)
|
|||
self._logLrn(card, ease, conf, leaving, type, lastLeft)
|
||||
|
||||
def _delayForGrade(self, conf, left):
|
||||
left = left % 1000
|
||||
try:
|
||||
delay = conf['delays'][-left]
|
||||
except IndexError:
|
||||
|
|
@ -501,7 +504,22 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff)
|
|||
|
||||
def _startingLeft(self, card):
|
||||
conf = self._lrnConf(card)
|
||||
return len(conf['delays'])
|
||||
tot = len(conf['delays'])
|
||||
tod = self._leftToday(conf['delays'], tot)
|
||||
return tot + tod*1000
|
||||
|
||||
def _leftToday(self, delays, left, now=None):
|
||||
"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 += delays[i]*60
|
||||
if now > self.dayCutoff:
|
||||
break
|
||||
ok = i
|
||||
return ok+1
|
||||
|
||||
def _graduatingIvl(self, card, conf, early, adj=True):
|
||||
if card.type == 2:
|
||||
|
|
@ -565,7 +583,7 @@ where queue = 1 and type = 2
|
|||
def _lrnForDeck(self, did):
|
||||
return self.col.db.scalar(
|
||||
"""
|
||||
select sum(left) from
|
||||
select sum(left/1000) from
|
||||
(select left from cards where did = ? and queue = 1 and due < ? limit ?)""",
|
||||
did, intTime() + self.col.conf['collapseTime'], self.reportLimit) or 0
|
||||
|
||||
|
|
@ -662,8 +680,9 @@ did = ? and queue = 2 and due <= ? limit ?""",
|
|||
card.odue = card.due
|
||||
card.due = int(self._delayForGrade(conf, 0) + time.time())
|
||||
card.left = len(conf['delays'])
|
||||
card.left += self._leftToday(conf['delays'], card.left)*1000
|
||||
card.queue = 1
|
||||
self.lrnCount += card.left
|
||||
self.lrnCount += card.left/1000
|
||||
# leech?
|
||||
if not self._checkLeech(card, conf) and conf['delays']:
|
||||
heappush(self._lrnQueue, (card.due, card.id))
|
||||
|
|
@ -1024,7 +1043,7 @@ your short-term review workload will become."""))
|
|||
# early removal
|
||||
return self._graduatingIvl(card, conf, True, adj=False) * 86400
|
||||
else:
|
||||
left = card.left - 1
|
||||
left = card.left%1000 - 1
|
||||
if left <= 0:
|
||||
# graduate
|
||||
return self._graduatingIvl(card, conf, False, adj=False) * 86400
|
||||
|
|
|
|||
|
|
@ -135,6 +135,10 @@ def _upgrade(col, ver):
|
|||
if changed:
|
||||
col.media.db.commit()
|
||||
col.db.execute("update col set ver = 9")
|
||||
if ver < 10:
|
||||
col.db.execute("""
|
||||
update cards set left = left + left*1000 where queue = 1""")
|
||||
col.db.execute("update col set ver = 10")
|
||||
|
||||
def _upgradeClozeModel(col, m):
|
||||
m['type'] = MODEL_CLOZE
|
||||
|
|
|
|||
|
|
@ -682,7 +682,7 @@ and ord = ? limit 1""", m['id'], t['ord']):
|
|||
for t in ("cards", "notes", "models", "media"):
|
||||
col.db.execute("drop table if exists %sDeleted" % t)
|
||||
# and failed cards
|
||||
left = len(col.decks.confForDid(1)['new']['delays'])
|
||||
left = len(col.decks.confForDid(1)['new']['delays'])*1001
|
||||
col.db.execute("update cards set odue = ?, left=? where type = 1",
|
||||
col.sched.today+1, left)
|
||||
# and due cards
|
||||
|
|
|
|||
|
|
@ -112,7 +112,8 @@ def test_learn():
|
|||
# fail it
|
||||
d.sched.answerCard(c, 1)
|
||||
# it should have three reps left to graduation
|
||||
assert c.left == 3
|
||||
assert c.left%1000 == 3
|
||||
assert c.left/1000 == 3
|
||||
# it should by due in 30 seconds
|
||||
t = round(c.due - time.time())
|
||||
assert t >= 25 and t <= 40
|
||||
|
|
@ -120,7 +121,8 @@ def test_learn():
|
|||
d.sched.answerCard(c, 2)
|
||||
# it should by due in 3 minutes
|
||||
assert round(c.due - time.time()) in (179, 180)
|
||||
assert c.left == 2
|
||||
assert c.left%1000 == 2
|
||||
assert c.left/1000 == 2
|
||||
# check log is accurate
|
||||
log = d.db.first("select * from revlog order by id desc")
|
||||
assert log[3] == 2
|
||||
|
|
@ -130,7 +132,8 @@ def test_learn():
|
|||
d.sched.answerCard(c, 2)
|
||||
# it should by due in 10 minutes
|
||||
assert round(c.due - time.time()) in (599, 600)
|
||||
assert c.left == 1
|
||||
assert c.left%1000 == 1
|
||||
assert c.left/1000 == 1
|
||||
# the next pass should graduate the card
|
||||
assert c.queue == 1
|
||||
assert c.type == 1
|
||||
|
|
@ -299,7 +302,7 @@ def test_overdue_lapse():
|
|||
c.due = -1
|
||||
c.odue = -1
|
||||
c.factor = 2500
|
||||
c.left = 2
|
||||
c.left = 2002
|
||||
c.ivl = 0
|
||||
c.flush()
|
||||
d.sched._clearOverdue = False
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ def test_review():
|
|||
c = d.sched.getCard()
|
||||
assert c.queue == 0
|
||||
d.sched.answerCard(c, 2)
|
||||
assert c.left == 1
|
||||
assert c.left == 1001
|
||||
assert d.sched.counts() == (0, 1, 0)
|
||||
assert c.queue == 1
|
||||
# undo
|
||||
|
|
@ -58,7 +58,7 @@ def test_review():
|
|||
assert d.sched.counts() == (1, 0, 0)
|
||||
c.load()
|
||||
assert c.queue == 0
|
||||
assert c.left != 1
|
||||
assert c.left != 1001
|
||||
assert not d.undoName()
|
||||
# we should be able to undo multiple answers too
|
||||
f['Front'] = u"two"
|
||||
|
|
|
|||
Loading…
Reference in a new issue