From ffaf7ffc667a06c7568f1a7fcf89cfc2a509f016 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 9 Mar 2012 06:56:07 +0900 Subject: [PATCH] removing cram decks, estimates, failure handling --- anki/decks.py | 22 ++++++++++++------- anki/sched.py | 24 ++++++++++++++++----- tests/test_sched.py | 51 +++++++++++++++++++++++++++++++-------------- 3 files changed, 68 insertions(+), 29 deletions(-) diff --git a/anki/decks.py b/anki/decks.py index 0965b24cd..901bf59c8 100644 --- a/anki/decks.py +++ b/anki/decks.py @@ -122,14 +122,20 @@ class DeckManager(object): # do nothing else if doesn't exist if not str(did) in self.decks: return - # delete children first - if childrenToo: - # we don't want to delete children when syncing - for name, id in self.children(did): - self.rem(id, cardsToo) - # delete cards too? - if cardsToo: - self.col.remCards(self.cids(did)) + deck = self.get(did) + if deck.get('cram'): + # deleting a cramming deck returns cards to their previous deck + # rather than deleting the cards + self.col.sched.remCram(did) + else: + # delete children first + if childrenToo: + # we don't want to delete children when syncing + for name, id in self.children(did): + self.rem(id, cardsToo) + # delete cards too? + if cardsToo: + self.col.remCards(self.cids(did)) # delete the deck and add a grave del self.decks[str(did)] # ensure we have an active deck diff --git a/anki/sched.py b/anki/sched.py index 1fa3c68cc..1e63b2d86 100644 --- a/anki/sched.py +++ b/anki/sched.py @@ -64,10 +64,7 @@ class Scheduler(object): # cramming? if self._cramming and card.type == 2: # reviews get their ivl boosted on first sight - elapsed = card.ivl - card.odue - self.today - assert card.factor - factor = ((card.factor/1000.0)+1.2)/2.0 - card.ivl = int(max(card.ivl, elapsed * factor, 1))+1 + card.ivl = self._cramIvlBoost(card) card.odue = self.today + card.ivl self._updateStats(card, 'new') if card.queue == 1: @@ -437,6 +434,10 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff) # failed else: card.left = self._startingLeft(card) + if self._cramming: + print "fixme: configurable failure handling" + card.ivl = 1 + card.odue = self.today + 1 self.lrnCount += card.left delay = self._delayForGrade(conf, card.left) if card.due < time.time(): @@ -485,6 +486,8 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff) def _graduatingIvl(self, card, conf, early, adj=True): if card.type == 2: # lapsed card being relearnt + if self._cramming: + return self._cramIvlBoost(card) return card.ivl if not early: # graduate @@ -739,6 +742,11 @@ did = ? and queue = 2 and due <= ? %s limit ?""" % order, # and change to our new deck self.col.decks.select(did) + def remCram(self, did): + self.col.db.execute(""" +update cards set did = odid, queue = type, due = odue, odue = 0, odid = 0, +usn = ?, mod = ? where did = ?""", self.col.usn(), intTime(), did) + def _cramOrder(self, order): if order == "due": return "order by c.due" @@ -770,6 +778,13 @@ did = ? and queue = 2 and due <= ? %s limit ?""" % order, update cards set odid = did, odue = due, did = ?, queue = 0, due = ?, mod = ?, usn = ? where id = ?""", data) + def _cramIvlBoost(self, card): + assert self._cramming and card.type == 2 + assert card.factor + elapsed = card.ivl - card.odue - self.today + factor = ((card.factor/1000.0)+1.2)/2.0 + return int(max(card.ivl, elapsed * factor, 1)) + # Leeches ########################################################################## @@ -893,7 +908,6 @@ your short-term review workload will become.""")) # this isn't easily extracted from the learn code def _nextLrnIvl(self, card, ease): if card.queue == 0: - card.type = 1 card.left = self._startingLeft(card) conf = self._lrnConf(card) if ease == 1: diff --git a/tests/test_sched.py b/tests/test_sched.py index 397372c42..5ff1e527a 100644 --- a/tests/test_sched.py +++ b/tests/test_sched.py @@ -453,33 +453,52 @@ def test_cram(): assert sorted(d.sched.deckDueList())[0][3] == 1 # and should appear in the counts assert d.sched.counts() == (1,0,0) - # grab it and make one step + # grab it and check estimates c = d.sched.getCard() + assert d.sched.nextIvl(c, 1) == 60 + assert d.sched.nextIvl(c, 2) == 600 + assert d.sched.nextIvl(c, 3) == 138*60*60*24 d.sched.answerCard(c, 2) # elapsed time was 75 days # factor = 2.5+1.2/2 = 1.85 - # int(75*1.85)+1 = 139 - assert c.ivl == 139 - assert c.odue == 139 + # int(75*1.85) = 138 + assert c.ivl == 138 + assert c.odue == 138 assert c.queue == 1 + # check ivls again + assert d.sched.nextIvl(c, 1) == 60 + assert d.sched.nextIvl(c, 2) == 138*60*60*24 + assert d.sched.nextIvl(c, 3) == 138*60*60*24 # when it graduates, due is updated c = d.sched.getCard() d.sched.answerCard(c, 2) - assert c.ivl == 139 - assert c.due == 139 + assert c.ivl == 138 + assert c.due == 138 assert c.queue == 2 # and it will have moved back to the previous deck assert c.did == 1 - - - # card will have moved b - #assert sorted(d.sched.deckDueList())[0][3] == 1 - - return - # check that estimates work - assert d.sched.nextIvl(c, 1) == 30 - assert d.sched.nextIvl(c, 2) == 180 - assert d.sched.nextIvl(c, 3) == 86400*100 + # cram the deck again + d.sched.cram("") + d.reset() + c = d.sched.getCard() + # check ivls again - passing should be idempotent + assert d.sched.nextIvl(c, 1) == 60 + assert d.sched.nextIvl(c, 2) == 600 + assert d.sched.nextIvl(c, 3) == 138*60*60*24 + d.sched.answerCard(c, 2) + assert c.ivl == 138 + assert c.odue == 138 + # fail + d.sched.answerCard(c, 1) + assert d.sched.nextIvl(c, 1) == 60 + assert d.sched.nextIvl(c, 2) == 600 + assert d.sched.nextIvl(c, 3) == 86400 + # delete the deck, returning the card mid-study + d.decks.rem(d.decks.selected()) + assert len(d.sched.deckDueList()) == 1 + c.load() + assert c.ivl == 1 + assert c.due == d.sched.today+1 def test_adjIvl(): d = getEmptyDeck()