implement cram

still to do:
- altering intervals at cram exit
- tidying up
This commit is contained in:
Damien Elmes 2011-03-18 13:21:11 +09:00
parent 308846aa93
commit bd477de1a9
5 changed files with 116 additions and 18 deletions

View file

@ -2,9 +2,12 @@
# Copyright: Damien Elmes <anki@ichi2.net> # Copyright: Damien Elmes <anki@ichi2.net>
# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html # 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 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): class CramScheduler(Scheduler):
name = "cram" name = "cram"
@ -14,11 +17,80 @@ class CramScheduler(Scheduler):
self.order = order self.order = order
self.reset() self.reset()
def counts(self):
return (self.newCount, self.lrnCount, 0)
def reset(self): def reset(self):
pass self._resetConf()
self._resetLrn()
self._resetNew()
self._resetRev()
def getCard(self): def answerCard(self, card, ease):
pass 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

View file

@ -655,7 +655,7 @@ select conf from gconf where id = (select gcid from groups where id = ?)""",
self.sched.onClose() self.sched.onClose()
self.sched = self._stdSched self.sched = self._stdSched
def cramGroups(self, gids, order="mod"): def cramGroups(self, gids, order="mod desc"):
self.stdSched() self.stdSched()
self.sched = anki.cram.CramScheduler(self, gids, order) self.sched = anki.cram.CramScheduler(self, gids, order)

View file

@ -23,11 +23,15 @@ defaultConf = {
# one of [suspend], [tagonly] # one of [suspend], [tagonly]
'leechAction': ["suspend"], 'leechAction': ["suspend"],
}, },
'cram': {
'delays': [0.5, 3, 10],
'resched': True,
},
'rev': { 'rev': {
'ease4': 1.3, 'ease4': 1.3,
'fuzz': 0.05, 'fuzz': 0.05,
'minSpace': 1, 'minSpace': 1,
} },
} }
class GroupConfig(object): class GroupConfig(object):

View file

@ -57,7 +57,7 @@ class Scheduler(object):
"Does not include fetched but unanswered." "Does not include fetched but unanswered."
return (self.newCount, self.lrnCount, self.revCount) return (self.newCount, self.lrnCount, self.revCount)
def cardQueue(self, card): def countIdx(self, card):
return card.queue return card.queue
def onClose(self): def onClose(self):

View file

@ -1,6 +1,6 @@
# coding: utf-8 # coding: utf-8
import time import time, copy
from tests.shared import assertException, getEmptyDeck from tests.shared import assertException, getEmptyDeck
from anki.stdmodels import BasicModel from anki.stdmodels import BasicModel
from anki.utils import stripHTML, intTime from anki.utils import stripHTML, intTime
@ -152,7 +152,6 @@ def test_reviews():
c.startTimer() c.startTimer()
c.flush() c.flush()
# save it for later use as well # save it for later use as well
import copy
cardcopy = copy.copy(c) cardcopy = copy.copy(c)
# failing it should put it in the learn queue with the default options # 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 assert ni(c, 3) == 21600000
# (* 100 2.5 1.3 86400)28080000.0 # (* 100 2.5 1.3 86400)28080000.0
assert ni(c, 4) == 28080000 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(): def test_misc():
d = getEmptyDeck() d = getEmptyDeck()
@ -308,9 +307,32 @@ def test_cram():
f = d.newFact() f = d.newFact()
f['Front'] = u"one" f['Front'] = u"one"
d.addFact(f) d.addFact(f)
f = d.newFact() c = f.cards()[0]
f['Front'] = u"two" c.ivl = 100
d.addFact(f) c.type = c.queue = 2
d.cramGroups(1) 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)