diff --git a/anki/cards.py b/anki/cards.py index e215ed8ec..730fe4e32 100644 --- a/anki/cards.py +++ b/anki/cards.py @@ -118,7 +118,8 @@ streak=?, lapses=?, grade=?, cycles=? where id = ?""", self.timerStarted = time.time() def timeTaken(self): - return min(time.time() - self.timerStarted, MAX_TIMER) + "Time taken to answer card, in integer MS." + return int(min(time.time() - self.timerStarted, MAX_TIMER)*1000) # Questions and answers ########################################################################## diff --git a/anki/revlog.py b/anki/revlog.py deleted file mode 100644 index 2c1d47f11..000000000 --- a/anki/revlog.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright: Damien Elmes -# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html - -import time - -# Flags: 0=standard review, 1=reschedule due to cram, drill, etc -# Rep: Repetition number. The same number may appear twice if a card has been -# manually rescheduled or answered on multiple sites before a sync. -# -# We store the times in integer milliseconds to avoid an extra index on the -# primary key. - -def logReview(db, card, ease, flags=0): - db.execute(""" -insert into revlog values ( -:created, :cardId, :ease, :rep, :lastInterval, :interval, :factor, -:userTime, :flags)""", - created=int(time.time()*1000), cardId=card.id, ease=ease, rep=card.reps, - lastInterval=card.lastInterval, interval=card.interval, - factor=card.factor, userTime=int(card.userTime()*1000), - flags=flags) diff --git a/anki/sched.py b/anki/sched.py index e579d0eef..91f6f7f46 100644 --- a/anki/sched.py +++ b/anki/sched.py @@ -25,7 +25,9 @@ class Scheduler(object): self.checkDay() id = self.getCardId() if id: - return self.deck.getCard(id) + c = self.deck.getCard(id) + c.startTimer() + return c def reset(self): self.resetConf() @@ -167,8 +169,17 @@ limit %d""" % self.learnLimit, lim=self.dayCutoff) card.grade = 0 if card.grade >= len(conf['delays']): self.graduateLearnCard(card, conf) + return else: - card.due = time.time() + conf['delays'][card.grade]*60 + card.due = time.time() + self.delayForGrade(conf, card.grade) + try: + self.logLearn(card, ease, conf) + except: + time.sleep(0.01) + self.logLearn(card, ease, conf) + + def delayForGrade(self, conf, grade): + return conf['delays'][grade]*60 def learnConf(self, card): conf = self.confForCard(card) @@ -197,12 +208,23 @@ limit %d""" % self.learnLimit, lim=self.dayCutoff) def rescheduleAsReview(self, card, conf, int_): card.queue = 1 - card.factor = conf['initialFactor'] if int_: # new card card.type = 1 card.interval = int_ - print "handle log, etc" + card.factor = conf['initialFactor'] + print "logs for learning cards?" + else: + # failed card + pass + + def logLearn(self, card, ease, conf): + self.deck.db.execute( + "insert into revlog values (?,?,?,?,?,?,?,?,?)", + int(time.time()*1000), card.id, ease, card.cycles, + self.delayForGrade(conf, card.grade), + self.delayForGrade(conf, max(0, card.grade-1)), + 0, card.timeTaken(), 0) # Reviews ########################################################################## @@ -230,8 +252,8 @@ queue = 1 %s and due < :lim order by %s limit %d""" % ( return self.revQueue def revOrder(self): - return ("interval desc", - "interval", + return ("ivl desc", + "ivl", "due")[self.deck.qconf['revCardOrder']] # FIXME: rewrite @@ -253,8 +275,7 @@ queue = 1 %s and due < :lim order by %s limit %d""" % ( oldSuc = card.successive # update card details last = card.interval - card.interval = self.nextInterval(card, ease) - card.lastInterval = last + card.ivl = self.nextInterval(card, ease) if card.reps: # only update if card was not new card.lastDue = card.due @@ -329,6 +350,23 @@ and queue between 1 and 2""", # update local cache of seen facts self.spacedFacts[card.fid] = new +# Flags: 0=standard review, 1=reschedule due to cram, drill, etc +# Rep: Repetition number. The same number may appear twice if a card has been +# manually rescheduled or answered on multiple sites before a sync. +# +# We store the times in integer milliseconds to avoid an extra index on the +# primary key. + + def logReview(db, card, ease, flags=0): + db.execute(""" +insert into revlog values ( +:created, :cardId, :ease, :rep, :lastInterval, :interval, :factor, +:userTime, :flags)""", + created=int(time.time()*1000), cardId=card.id, ease=ease, rep=card.reps, + lastInterval=card.lastInterval, interval=card.interval, + factor=card.factor, userTime=int(card.userTime()*1000), + flags=flags) + # Interval management ########################################################################## diff --git a/anki/storage.py b/anki/storage.py index 83258bcc7..54045939f 100644 --- a/anki/storage.py +++ b/anki/storage.py @@ -151,7 +151,7 @@ create table if not exists revlog ( lastInt integer not null, factor integer not null, taken integer not null, - flags integer not null + type integer not null ); create table if not exists tags ( diff --git a/tests/test_sched.py b/tests/test_sched.py index 33b5a78b3..cfbbabe09 100644 --- a/tests/test_sched.py +++ b/tests/test_sched.py @@ -3,7 +3,7 @@ import time from tests.shared import assertException, getEmptyDeck from anki.stdmodels import BasicModel -from anki.utils import stripHTML +from anki.utils import stripHTML, intTime def test_basics(): d = getEmptyDeck() @@ -24,9 +24,11 @@ def test_new(): assert c.queue == 2 assert c.type == 2 # if we answer it, it should become a learn card + t = intTime() d.sched.answerCard(c, 1) assert c.queue == 0 assert c.type == 2 + assert c.due >= t # the default order should ensure siblings are not seen together, and # should show all cards m = d.currentModel() @@ -73,6 +75,12 @@ def test_learn(): # and it should be grade 1 now assert c.grade == 1 assert c.cycles == 2 + # check log is accurate + log = d.db.first("select * from revlog order by time desc") + assert log[2] == 2 + assert log[3] == 2 + assert log[4] == 180 + assert log[5] == 30 # pass again d.sched.answerCard(c, 2) # it should by due in 10 minutes