diff --git a/anki/consts.py b/anki/consts.py index b3595dc9b..107e6bf6e 100644 --- a/anki/consts.py +++ b/anki/consts.py @@ -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 ########################################################################## diff --git a/anki/sched.py b/anki/sched.py index 997d9fd2e..0803bf112 100644 --- a/anki/sched.py +++ b/anki/sched.py @@ -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 diff --git a/anki/storage.py b/anki/storage.py index 2abf12884..5696eb729 100644 --- a/anki/storage.py +++ b/anki/storage.py @@ -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 diff --git a/anki/upgrade.py b/anki/upgrade.py index 2143535cb..c6dbcbb6c 100644 --- a/anki/upgrade.py +++ b/anki/upgrade.py @@ -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 diff --git a/tests/test_sched.py b/tests/test_sched.py index f0aa38ed1..a563ad275 100644 --- a/tests/test_sched.py +++ b/tests/test_sched.py @@ -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 diff --git a/tests/test_undo.py b/tests/test_undo.py index ff0886645..abd91909b 100644 --- a/tests/test_undo.py +++ b/tests/test_undo.py @@ -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"