From bd477de1a904079889dd6de52250e6cc9d031b37 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 18 Mar 2011 13:21:11 +0900 Subject: [PATCH] implement cram still to do: - altering intervals at cram exit - tidying up --- anki/cram.py | 84 +++++++++++++++++++++++++++++++++++++++++---- anki/deck.py | 2 +- anki/groups.py | 6 +++- anki/sched.py | 2 +- tests/test_sched.py | 40 ++++++++++++++++----- 5 files changed, 116 insertions(+), 18 deletions(-) diff --git a/anki/cram.py b/anki/cram.py index 9247ab36e..9ba5608e8 100644 --- a/anki/cram.py +++ b/anki/cram.py @@ -2,9 +2,12 @@ # Copyright: Damien Elmes # License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html -from anki.utils import ids2str +from anki.utils import ids2str, intTime from anki.sched import Scheduler +# The order arg should be the opposite of what you want. So if you want +# modified ascending, pass in 'mod desc'. + class CramScheduler(Scheduler): name = "cram" @@ -14,11 +17,80 @@ class CramScheduler(Scheduler): self.order = order self.reset() + def counts(self): + return (self.newCount, self.lrnCount, 0) + def reset(self): - pass + self._resetConf() + self._resetLrn() + self._resetNew() + self._resetRev() - def getCard(self): - pass + def answerCard(self, card, ease): + if card.queue == 2: + card.queue = 1 + card.edue = card.due + if card.queue == 1: + self._answerLrnCard(card, ease) + else: + raise Exception("Invalid queue") + card.mod = intTime() + card.flushSched() + + def countIdx(self, card): + if card.queue == 2: + return 0 + else: + return 1 + + # Fetching + ########################################################################## + + def _resetNew(self): + self.newQueue = self.db.list(""" +select id from cards where queue = 2 +and gid in %s order by %s limit %d""" % (ids2str(self.gids), + self.order, + self.reportLimit)) + self.newCount = len(self.newQueue) + + def _resetRev(self): + self.revQueue = [] + self.revCount = 0 + + def _timeForNewCard(self): + return True + + def _getNewCard(self): + if self.newQueue: + id = self.newQueue.pop() + self.newCount -= 1 + return id + + # Answering + ########################################################################## + + def _rescheduleAsRev(self, card, conf, early): + Scheduler._rescheduleAsRev(self, card, conf, early) + card.ivl = self._graduatingIvl(card, conf, early) + card.due = self.today + card.ivl + # temporarily suspend it + card.queue = -3 + + def _graduatingIvl(self, card, conf, early): + if conf['resched']: + print "fixme" + return card.ivl + else: + return card.ivl + + def _lrnConf(self, card): + return self._cardConf(card)['cram'] + + # Next time reports + ########################################################################## + + def nextIvl(self, card, ease): + "Return the next interval for CARD, in seconds." + return self._nextLrnIvl(card, ease) - def answerCard(self): - pass diff --git a/anki/deck.py b/anki/deck.py index d8f750949..1e22ec18e 100644 --- a/anki/deck.py +++ b/anki/deck.py @@ -655,7 +655,7 @@ select conf from gconf where id = (select gcid from groups where id = ?)""", self.sched.onClose() self.sched = self._stdSched - def cramGroups(self, gids, order="mod"): + def cramGroups(self, gids, order="mod desc"): self.stdSched() self.sched = anki.cram.CramScheduler(self, gids, order) diff --git a/anki/groups.py b/anki/groups.py index 9b9320117..908106a86 100644 --- a/anki/groups.py +++ b/anki/groups.py @@ -23,11 +23,15 @@ defaultConf = { # one of [suspend], [tagonly] 'leechAction': ["suspend"], }, + 'cram': { + 'delays': [0.5, 3, 10], + 'resched': True, + }, 'rev': { 'ease4': 1.3, 'fuzz': 0.05, 'minSpace': 1, - } + }, } class GroupConfig(object): diff --git a/anki/sched.py b/anki/sched.py index 9e6f380a9..9fe32f22c 100644 --- a/anki/sched.py +++ b/anki/sched.py @@ -57,7 +57,7 @@ class Scheduler(object): "Does not include fetched but unanswered." return (self.newCount, self.lrnCount, self.revCount) - def cardQueue(self, card): + def countIdx(self, card): return card.queue def onClose(self): diff --git a/tests/test_sched.py b/tests/test_sched.py index 69bbd7d9a..f25adc6f8 100644 --- a/tests/test_sched.py +++ b/tests/test_sched.py @@ -1,6 +1,6 @@ # coding: utf-8 -import time +import time, copy from tests.shared import assertException, getEmptyDeck from anki.stdmodels import BasicModel from anki.utils import stripHTML, intTime @@ -152,7 +152,6 @@ def test_reviews(): c.startTimer() c.flush() # save it for later use as well - import copy cardcopy = copy.copy(c) # failing it should put it in the learn queue with the default options ################################################## @@ -270,7 +269,7 @@ def test_nextIvl(): assert ni(c, 3) == 21600000 # (* 100 2.5 1.3 86400)28080000.0 assert ni(c, 4) == 28080000 - print d.sched.nextIvlStr(c, 4) == "10.8 months" + assert d.sched.nextIvlStr(c, 4) == "10.8 months" def test_misc(): d = getEmptyDeck() @@ -308,9 +307,32 @@ def test_cram(): f = d.newFact() f['Front'] = u"one" d.addFact(f) - f = d.newFact() - f['Front'] = u"two" - d.addFact(f) - d.cramGroups(1) - - + c = f.cards()[0] + c.ivl = 100 + c.type = c.queue = 2 + c.due = d.sched.today + 50 + c.mod = 1 + c.flush() + d.cramGroups([1]) + assert d.sched.counts() == (1, 0, 0) + c = d.sched.getCard() + assert d.sched.counts() == (0, 0, 0) + # check that estimates work + assert d.sched.nextIvl(c, 1) == 30 + assert d.sched.nextIvl(c, 2) == 180 + print "fixme" + print d.sched.nextIvl(c, 3) == 86400*100 + # answer it + d.sched.answerCard(c, 2) + delta = c.due - time.time() + assert delta > 175 and delta <= 180 + # another two answers should reschedule it + assert c.queue == 1 + d.sched.answerCard(c, 2) + d.sched.answerCard(c, 2) + assert c.queue == -3 + print "fixme" + assert c.ivl == 100 + # and if the queue is reset, it shouldn't appear in the new queue again + d.reset() + assert d.sched.counts() == (0, 0, 0)