diff --git a/pylib/anki/cards.py b/pylib/anki/cards.py index a94f271be..2de6960e5 100644 --- a/pylib/anki/cards.py +++ b/pylib/anki/cards.py @@ -48,8 +48,8 @@ class Card: self.id = timestampID(col.db, "cards") self.did = 1 self.crt = intTime() - self.type = 0 - self.queue = 0 + self.type = CARD_TYPE_NEW + self.queue = QUEUE_TYPE_NEW self.ivl = 0 self.factor = 0 self.reps = 0 @@ -88,7 +88,11 @@ class Card: self.mod = intTime() self.usn = self.col.usn() # bug check - if self.queue == 2 and self.odue and not self.col.decks.isDyn(self.did): + if ( + self.queue == QUEUE_TYPE_REV + and self.odue + and not self.col.decks.isDyn(self.did) + ): hooks.card_odue_was_invalid() assert self.due < 4294967296 self.col.db.execute( @@ -120,7 +124,11 @@ insert or replace into cards values self.mod = intTime() self.usn = self.col.usn() # bug checks - if self.queue == 2 and self.odue and not self.col.decks.isDyn(self.did): + if ( + self.queue == QUEUE_TYPE_REV + and self.odue + and not self.col.decks.isDyn(self.did) + ): hooks.card_odue_was_invalid() assert self.due < 4294967296 self.col.db.execute( diff --git a/pylib/anki/consts.py b/pylib/anki/consts.py index 89e9ba13a..1d2b35910 100644 --- a/pylib/anki/consts.py +++ b/pylib/anki/consts.py @@ -14,6 +14,22 @@ NEW_CARDS_FIRST = 2 NEW_CARDS_RANDOM = 0 NEW_CARDS_DUE = 1 +# Queue types +QUEUE_TYPE_MANUALLY_BURIED = -3 +QUEUE_TYPE_SIBLING_BURIED = -2 +QUEUE_TYPE_SUSPENDED = -1 +QUEUE_TYPE_NEW = 0 +QUEUE_TYPE_LRN = 1 +QUEUE_TYPE_REV = 2 +QUEUE_TYPE_DAY_LEARN_RELEARN = 3 +QUEUE_TYPE_PREVIEW = 4 + +# Card types +CARD_TYPE_NEW = 0 +CARD_TYPE_LRN = 1 +CARD_TYPE_REV = 2 +CARD_TYPE_RELEARNING = 3 + # removal types REM_CARD = 0 REM_NOTE = 1 @@ -27,6 +43,10 @@ COUNT_REMAINING = 1 MEDIA_ADD = 0 MEDIA_REM = 1 +# Kind of decks +DECK_STD = 0 +DECK_DYN = 1 + # dynamic deck order DYN_OLDEST = 0 DYN_RANDOM = 1 @@ -55,6 +75,22 @@ SYNC_VER = 9 HELP_SITE = "http://ankisrs.net/docs/manual.html" +# Leech actions +LEECH_SUSPEND = 0 +LEECH_TAGONLY = 1 + +# Buttons +BUTTON_ONE = 1 +BUTTON_TWO = 2 +BUTTON_THREE = 3 +BUTTON_FOUR = 4 + +# Revlog types +REVLOG_LRN = 0 +REVLOG_REV = 1 +REVLOG_RELRN = 2 +REVLOG_CRAM = 3 + # Labels ########################################################################## diff --git a/pylib/anki/decks.py b/pylib/anki/decks.py index 605404d5b..47696ad8a 100644 --- a/pylib/anki/decks.py +++ b/pylib/anki/decks.py @@ -27,7 +27,7 @@ defaultDeck = { "conf": 1, "usn": 0, "desc": "", - "dyn": 0, # anki uses int/bool interchangably here + "dyn": DECK_STD, # anki uses int/bool interchangably here "collapsed": False, # added in beta11 "extendNew": 10, @@ -40,7 +40,7 @@ defaultDynamicDeck = { "lrnToday": [0, 0], "timeToday": [0, 0], "collapsed": False, - "dyn": 1, + "dyn": DECK_DYN, "desc": "", "usn": 0, "delays": None, @@ -71,7 +71,7 @@ defaultConf = { "minInt": 1, "leechFails": 8, # type 0=suspend, 1=tagonly - "leechAction": 0, + "leechAction": LEECH_SUSPEND, }, "rev": { "perDay": 200, diff --git a/pylib/anki/find.py b/pylib/anki/find.py index ccd6844d3..847d4e05f 100644 --- a/pylib/anki/find.py +++ b/pylib/anki/find.py @@ -240,7 +240,7 @@ select distinct(n.id) from cards c, notes n where c.nid=n.id and """ elif type == "cardDue": sort = "c.type, c.due" elif type == "cardEase": - sort = "c.type == 0, c.factor" + sort = f"c.type == {CARD_TYPE_NEW}, c.factor" elif type == "cardLapses": sort = "c.lapses" elif type == "cardIvl": @@ -271,18 +271,18 @@ select distinct(n.id) from cards c, notes n where c.nid=n.id and """ if val == "review": n = 2 elif val == "new": - n = 0 + n = CARD_TYPE_NEW else: - return "queue in (1, 3)" + return f"queue in ({QUEUE_TYPE_LRN}, {QUEUE_TYPE_DAY_LEARN_RELEARN})" return "type = %d" % n elif val == "suspended": return "c.queue = -1" elif val == "buried": - return "c.queue in (-2, -3)" + return f"c.queue in ({QUEUE_TYPE_SIBLING_BURIED}, {QUEUE_TYPE_MANUALLY_BURIED})" elif val == "due": - return """ -(c.queue in (2,3) and c.due <= %d) or -(c.queue = 1 and c.due <= %d)""" % ( + return f""" +(c.queue in ({QUEUE_TYPE_REV},{QUEUE_TYPE_DAY_LEARN_RELEARN}) and c.due <= %d) or +(c.queue = {QUEUE_TYPE_LRN} and c.due <= %d)""" % ( self.col.sched.today, self.col.sched.dayCutoff, ) @@ -349,7 +349,7 @@ select distinct(n.id) from cards c, notes n where c.nid=n.id and """ if prop == "due": val += self.col.sched.today # only valid for review/daily learning - q.append("(c.queue in (2,3))") + q.append(f"(c.queue in ({QUEUE_TYPE_REV},{QUEUE_TYPE_DAY_LEARN_RELEARN}))") elif prop == "ease": prop = "factor" val = int(val * 1000) diff --git a/pylib/anki/importing/anki2.py b/pylib/anki/importing/anki2.py index 46d54000c..50ba9cfab 100644 --- a/pylib/anki/importing/anki2.py +++ b/pylib/anki/importing/anki2.py @@ -6,6 +6,7 @@ import unicodedata from typing import Any, Dict, List, Optional, Tuple from anki.collection import _Collection +from anki.consts import * from anki.importing.base import Importer from anki.lang import _ from anki.storage import Collection @@ -343,7 +344,10 @@ class Anki2Importer(Importer): card[4] = intTime() card[5] = usn # review cards have a due date relative to collection - if card[7] in (2, 3) or card[6] == 2: + if ( + card[7] in (QUEUE_TYPE_REV, QUEUE_TYPE_DAY_LEARN_RELEARN) + or card[6] == CARD_TYPE_REV + ): card[8] -= aheadBy # odue needs updating too if card[14]: @@ -356,13 +360,13 @@ class Anki2Importer(Importer): card[8] = card[14] card[14] = 0 # queue - if card[6] == 1: # type - card[7] = 0 + if card[6] == CARD_TYPE_LRN: # type + card[7] = QUEUE_TYPE_NEW else: card[7] = card[6] # type - if card[6] == 1: - card[6] = 0 + if card[6] == CARD_TYPE_LRN: + card[6] = CARD_TYPE_NEW cards.append(card) # we need to import revlog, rewriting card ids and bumping usn for rev in self.src.db.execute("select * from revlog where cid = ?", scid): diff --git a/pylib/anki/importing/noteimp.py b/pylib/anki/importing/noteimp.py index eb11504cc..364d13bcd 100644 --- a/pylib/anki/importing/noteimp.py +++ b/pylib/anki/importing/noteimp.py @@ -52,9 +52,12 @@ class ForeignCard: # If the first field of the model is not in the map, the map is invalid. # The import mode is one of: -# 0: update if first field matches existing note -# 1: ignore if first field matches existing note -# 2: import even if first field matches existing note +# UPDATE_MODE: update if first field matches existing note +# IGNORE_MODE: ignore if first field matches existing note +# ADD_MODE: import even if first field matches existing note +UPDATE_MODE = 0 +IGNORE_MODE = 1 +ADD_MODE = 2 class NoteImporter(Importer): @@ -62,7 +65,7 @@ class NoteImporter(Importer): needMapper = True needDelimiter = False allowHTML = False - importMode = 0 + importMode = UPDATE_MODE mapping: Optional[List[str]] tagModified: Optional[str] @@ -153,7 +156,7 @@ class NoteImporter(Importer): self.log.append(_("Empty first field: %s") % " ".join(n.fields)) continue # earlier in import? - if fld0 in firsts and self.importMode != 2: + if fld0 in firsts and self.importMode != ADD_MODE: # duplicates in source file; log and ignore self.log.append(_("Appeared twice in file: %s") % fld0) continue @@ -168,16 +171,16 @@ class NoteImporter(Importer): if fld0 == sflds[0]: # duplicate found = True - if self.importMode == 0: + if self.importMode == UPDATE_MODE: data = self.updateData(n, id, sflds) if data: updates.append(data) updateLog.append(updateLogTxt % fld0) dupeCount += 1 found = True - elif self.importMode == 1: + elif self.importMode == IGNORE_MODE: dupeCount += 1 - elif self.importMode == 2: + elif self.importMode == ADD_MODE: # allow duplicates in this case if fld0 not in dupes: # only show message once, no matter how many @@ -214,9 +217,9 @@ class NoteImporter(Importer): ngettext("%d note updated", "%d notes updated", self.updateCount) % self.updateCount ) - if self.importMode == 0: + if self.importMode == UPDATE_MODE: unchanged = dupeCount - self.updateCount - elif self.importMode == 1: + elif self.importMode == IGNORE_MODE: unchanged = dupeCount else: unchanged = 0 diff --git a/pylib/anki/sched.py b/pylib/anki/sched.py index 2a77e38c2..3bc82972b 100644 --- a/pylib/anki/sched.py +++ b/pylib/anki/sched.py @@ -67,28 +67,28 @@ class Scheduler: self._burySiblings(card) card.reps += 1 # former is for logging new cards, latter also covers filt. decks - card.wasNew = card.type == 0 - wasNewQ = card.queue == 0 + card.wasNew = card.type == CARD_TYPE_NEW + wasNewQ = card.queue == QUEUE_TYPE_NEW if wasNewQ: # came from the new queue, move to learning - card.queue = 1 + card.queue = QUEUE_TYPE_LRN # if it was a new card, it's now a learning card - if card.type == 0: - card.type = 1 + if card.type == CARD_TYPE_NEW: + card.type = CARD_TYPE_LRN # init reps to graduation card.left = self._startingLeft(card) # dynamic? - if card.odid and card.type == 2: + if card.odid and card.type == CARD_TYPE_REV: if self._resched(card): # reviews get their ivl boosted on first sight card.ivl = self._dynIvlBoost(card) card.odue = self.today + card.ivl self._updateStats(card, "new") - if card.queue in (1, 3): + if card.queue in (QUEUE_TYPE_LRN, QUEUE_TYPE_DAY_LEARN_RELEARN): self._answerLrnCard(card, ease) if not wasNewQ: self._updateStats(card, "lrn") - elif card.queue == 2: + elif card.queue == QUEUE_TYPE_REV: self._answerRevCard(card, ease) self._updateStats(card, "rev") else: @@ -112,9 +112,9 @@ class Scheduler: "Return counts over next DAYS. Includes today." daysd = dict( self.col.db.all( - """ + f""" select due, count() from cards -where did in %s and queue = 2 +where did in %s and queue = {QUEUE_TYPE_REV} and due between ? and ? group by due order by due""" @@ -132,20 +132,20 @@ order by due""" return ret def countIdx(self, card): - if card.queue == 3: + if card.queue == QUEUE_TYPE_DAY_LEARN_RELEARN: return 1 return card.queue def answerButtons(self, card): if card.odue: # normal review in dyn deck? - if card.odid and card.queue == 2: + if card.odid and card.queue == QUEUE_TYPE_REV: return 4 conf = self._lrnConf(card) - if card.type in (0, 1) or len(conf["delays"]) > 1: + if card.type in (CARD_TYPE_NEW, CARD_TYPE_LRN) or len(conf["delays"]) > 1: return 3 return 2 - elif card.queue == 2: + elif card.queue == QUEUE_TYPE_REV: return 4 else: return 3 @@ -153,18 +153,25 @@ order by due""" def unburyCards(self): "Unbury cards." self.col.conf["lastUnburied"] = self.today - self.col.log(self.col.db.list("select id from cards where queue = -2")) - self.col.db.execute("update cards set queue=type where queue = -2") + self.col.log( + self.col.db.list( + f"select id from cards where queue = {QUEUE_TYPE_SIBLING_BURIED}" + ) + ) + self.col.db.execute( + f"update cards set queue=type where queue = {QUEUE_TYPE_SIBLING_BURIED}" + ) def unburyCardsForDeck(self): sids = ids2str(self.col.decks.active()) self.col.log( self.col.db.list( - "select id from cards where queue = -2 and did in %s" % sids + f"select id from cards where queue = {QUEUE_TYPE_SIBLING_BURIED} and did in %s" + % sids ) ) self.col.db.execute( - "update cards set mod=?,usn=?,queue=type where queue = -2 and did in %s" + f"update cards set mod=?,usn=?,queue=type where queue = {QUEUE_TYPE_SIBLING_BURIED} and did in %s" % sids, intTime(), self.col.usn(), @@ -348,9 +355,9 @@ order by due""" def _resetNewCount(self): cntFn = lambda did, lim: self.col.db.scalar( - """ + f""" select count() from (select 1 from cards where -did = ? and queue = 0 limit ?)""", +did = ? and queue = {QUEUE_TYPE_NEW} limit ?)""", did, lim, ) @@ -373,8 +380,8 @@ did = ? and queue = 0 limit ?)""", if lim: # fill the queue with the current did self._newQueue = self.col.db.list( - """ - select id from cards where did = ? and queue = 0 order by due,ord limit ?""", + f""" + select id from cards where did = ? and queue = {QUEUE_TYPE_NEW} order by due,ord limit ?""", did, lim, ) @@ -436,9 +443,9 @@ did = ? and queue = 0 limit ?)""", return 0 lim = min(lim, self.reportLimit) return self.col.db.scalar( - """ + f""" select count() from -(select 1 from cards where did = ? and queue = 0 limit ?)""", +(select 1 from cards where did = ? and queue = {QUEUE_TYPE_NEW} limit ?)""", did, lim, ) @@ -452,9 +459,9 @@ select count() from def totalNewForCurrentDeck(self): return self.col.db.scalar( - """ + f""" select count() from cards where id in ( -select id from cards where did in %s and queue = 0 limit ?)""" +select id from cards where did in %s and queue = {QUEUE_TYPE_NEW} limit ?)""" % ids2str(self.col.decks.active()), self.reportLimit, ) @@ -466,9 +473,9 @@ select id from cards where did in %s and queue = 0 limit ?)""" # sub-day self.lrnCount = ( self.col.db.scalar( - """ + f""" select sum(left/1000) from (select left from cards where -did in %s and queue = 1 and due < ? limit %d)""" +did in %s and queue = {QUEUE_TYPE_LRN} and due < ? limit %d)""" % (self._deckLimit(), self.reportLimit), self.dayCutoff, ) @@ -476,8 +483,8 @@ did in %s and queue = 1 and due < ? limit %d)""" ) # day self.lrnCount += self.col.db.scalar( - """ -select count() from cards where did in %s and queue = 3 + f""" +select count() from cards where did in %s and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit %d""" % (self._deckLimit(), self.reportLimit), self.today, @@ -496,9 +503,9 @@ and due <= ? limit %d""" if self._lrnQueue: return True self._lrnQueue = self.col.db.all( - """ + f""" select due, id from cards where -did in %s and queue = 1 and due < :lim +did in %s and queue = {QUEUE_TYPE_LRN} and due < :lim limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff, @@ -528,9 +535,9 @@ limit %d""" did = self._lrnDids[0] # fill the queue with the current did self._lrnDayQueue = self.col.db.list( - """ + f""" select id from cards where -did = ? and queue = 3 and due <= ? limit ?""", +did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""", did, self.today, self.queueLimit, @@ -556,25 +563,25 @@ did = ? and queue = 3 and due <= ? limit ?""", # ease 1=no, 2=yes, 3=remove conf = self._lrnConf(card) if card.odid and not card.wasNew: - type = 3 - elif card.type == 2: - type = 2 + type = REVLOG_CRAM + elif card.type == CARD_TYPE_REV: + type = REVLOG_RELRN else: - type = 0 + type = REVLOG_LRN leaving = False # lrnCount was decremented once when card was fetched lastLeft = card.left # immediate graduate? - if ease == 3: + if ease == BUTTON_THREE: self._rescheduleAsRev(card, conf, True) leaving = True # graduation time? - elif ease == 2 and (card.left % 1000) - 1 <= 0: + elif ease == BUTTON_TWO and (card.left % 1000) - 1 <= 0: self._rescheduleAsRev(card, conf, False) leaving = True else: # one step towards graduation - if ease == 2: + if ease == BUTTON_TWO: # decrement real left count and recalculate left today left = (card.left % 1000) - 1 card.left = self._leftToday(conf["delays"], left) * 1000 + left @@ -601,7 +608,7 @@ did = ? and queue = 3 and due <= ? limit ?""", # 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 - card.queue = 1 + card.queue = QUEUE_TYPE_LRN if self._lrnQueue and not self.revCount and not self.newCount: smallestDue = self._lrnQueue[0][0] card.due = max(card.due, smallestDue + 1) @@ -611,7 +618,7 @@ did = ? and queue = 3 and due <= ? limit ?""", # day learn queue ahead = ((card.due - self.dayCutoff) // 86400) + 1 card.due = self.today + ahead - card.queue = 3 + card.queue = QUEUE_TYPE_DAY_LEARN_RELEARN self._logLrn(card, ease, conf, leaving, type, lastLeft) def _delayForGrade(self, conf, left): @@ -627,13 +634,13 @@ did = ? and queue = 3 and due <= ? limit ?""", return delay * 60 def _lrnConf(self, card): - if card.type == 2: + if card.type == CARD_TYPE_REV: return self._lapseConf(card) else: return self._newConf(card) def _rescheduleAsRev(self, card, conf, early): - lapse = card.type == 2 + lapse = card.type == CARD_TYPE_REV if lapse: if self._resched(card): card.due = max(self.today + 1, card.odue) @@ -642,8 +649,8 @@ did = ? and queue = 3 and due <= ? limit ?""", card.odue = 0 else: self._rescheduleNew(card, conf, early) - card.queue = 2 - card.type = 2 + card.queue = QUEUE_TYPE_REV + card.type = CARD_TYPE_REV # if we were dynamic, graduating means moving back to the old deck resched = self._resched(card) if card.odid: @@ -652,11 +659,11 @@ did = ? and queue = 3 and due <= ? limit ?""", card.odid = 0 # if rescheduling is off, it needs to be set back to a new card if not resched and not lapse: - card.queue = card.type = 0 + card.queue = card.type = CARD_TYPE_NEW card.due = self.col.nextID("pos") def _startingLeft(self, card): - if card.type == 2: + if card.type == CARD_TYPE_REV: conf = self._lapseConf(card) else: conf = self._lrnConf(card) @@ -678,7 +685,7 @@ did = ? and queue = 3 and due <= ? limit ?""", return ok + 1 def _graduatingIvl(self, card, conf, early, adj=True): - if card.type == 2: + if card.type == CARD_TYPE_REV: # lapsed card being relearnt if card.odid: if conf["resched"]: @@ -736,25 +743,28 @@ did = ? and queue = 3 and due <= ? limit ?""", extra = " and did in " + ids2str(self.col.decks.allIds()) # review cards in relearning self.col.db.execute( - """ + f""" update cards set -due = odue, queue = 2, mod = %d, usn = %d, odue = 0 -where queue in (1,3) and type = 2 +due = odue, queue = {QUEUE_TYPE_REV}, mod = %d, usn = %d, odue = 0 +where queue in ({QUEUE_TYPE_LRN},{QUEUE_TYPE_DAY_LEARN_RELEARN}) and type = {CARD_TYPE_REV} %s """ % (intTime(), self.col.usn(), extra) ) # new cards in learning self.forgetCards( - self.col.db.list("select id from cards where queue in (1,3) %s" % extra) + self.col.db.list( + f"select id from cards where queue in ({QUEUE_TYPE_LRN},{QUEUE_TYPE_DAY_LEARN_RELEARN}) %s" + % extra + ) ) def _lrnForDeck(self, did): cnt = ( self.col.db.scalar( - """ + f""" select sum(left/1000) from -(select left from cards where did = ? and queue = 1 and due < ? limit ?)""", +(select left from cards where did = ? and queue = {QUEUE_TYPE_LRN} and due < ? limit ?)""", did, intTime() + self.col.conf["collapseTime"], self.reportLimit, @@ -762,9 +772,9 @@ select sum(left/1000) from or 0 ) return cnt + self.col.db.scalar( - """ + f""" select count() from -(select 1 from cards where did = ? and queue = 3 +(select 1 from cards where did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?)""", did, self.today, @@ -786,9 +796,9 @@ and due <= ? limit ?)""", def _revForDeck(self, did, lim): lim = min(lim, self.reportLimit) return self.col.db.scalar( - """ + f""" select count() from -(select 1 from cards where did = ? and queue = 2 +(select 1 from cards where did = ? and queue = {QUEUE_TYPE_REV} and due <= ? limit ?)""", did, self.today, @@ -798,9 +808,9 @@ and due <= ? limit ?)""", def _resetRevCount(self): def cntFn(did, lim): return self.col.db.scalar( - """ + f""" select count() from (select id from cards where -did = ? and queue = 2 and due <= ? limit %d)""" +did = ? and queue = {QUEUE_TYPE_REV} and due <= ? limit %d)""" % lim, did, self.today, @@ -824,9 +834,9 @@ did = ? and queue = 2 and due <= ? limit %d)""" if lim: # fill the queue with the current did self._revQueue = self.col.db.list( - """ + f""" select id from cards where -did = ? and queue = 2 and due <= ? limit ?""", +did = ? and queue = {QUEUE_TYPE_REV} and due <= ? limit ?""", did, self.today, lim, @@ -861,9 +871,9 @@ did = ? and queue = 2 and due <= ? limit ?""", def totalRevForCurrentDeck(self): return self.col.db.scalar( - """ + f""" select count() from cards where id in ( -select id from cards where did in %s and queue = 2 and due <= ? limit ?)""" +select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? limit ?)""" % ids2str(self.col.decks.active()), self.today, self.reportLimit, @@ -874,7 +884,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)""" def _answerRevCard(self, card, ease): delay = 0 - if ease == 1: + if ease == BUTTON_ONE: delay = self._rescheduleLapse(card) else: self._rescheduleRev(card, ease) @@ -893,7 +903,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)""" card.odue = card.due # if suspended as a leech, nothing to do delay = 0 - if self._checkLeech(card, conf) and card.queue == -1: + if self._checkLeech(card, conf) and card.queue == QUEUE_TYPE_SUSPENDED: return delay # if no relearning steps, nothing to do if not conf["delays"]: @@ -907,13 +917,13 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)""" # queue 1 if card.due < self.dayCutoff: self.lrnCount += card.left // 1000 - card.queue = 1 + card.queue = QUEUE_TYPE_LRN heappush(self._lrnQueue, (card.due, card.id)) else: # day learn queue ahead = ((card.due - self.dayCutoff) // 86400) + 1 card.due = self.today + ahead - card.queue = 3 + card.queue = QUEUE_TYPE_DAY_LEARN_RELEARN return delay def _nextLapseIvl(self, card, conf): @@ -946,7 +956,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)""" card.lastIvl, card.factor, card.timeTaken(), - 1, + REVLOG_REV, ) try: @@ -969,11 +979,11 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)""" ivl4 = self._constrainedIvl( (card.ivl + delay) * fct * conf["ease4"], conf, ivl3 ) - if ease == 2: + if ease == BUTTON_TWO: interval = ivl2 - elif ease == 3: + elif ease == BUTTON_THREE: interval = ivl3 - elif ease == 4: + elif ease == BUTTON_FOUR: interval = ivl4 # interval capped? return min(interval, conf["maxIvl"]) @@ -1058,9 +1068,9 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)""" self.col.log(self.col.db.list("select id from cards where %s" % lim)) # move out of cram queue self.col.db.execute( - """ -update cards set did = odid, queue = (case when type = 1 then 0 -else type end), type = (case when type = 1 then 0 else type end), + f""" +update cards set did = odid, queue = (case when type = {CARD_TYPE_LRN} then {QUEUE_TYPE_NEW} +else type end), type = (case when type = {CARD_TYPE_LRN} then {CARD_TYPE_NEW} else type end), due = odue, odue = 0, odid = 0, usn = ? where %s""" % lim, self.col.usn(), @@ -1088,7 +1098,7 @@ due = odue, odue = 0, odid = 0, usn = ? where %s""" t = "c.due" elif o == DYN_DUEPRIORITY: t = ( - "(case when queue=2 and due <= %d then (ivl / cast(%d-due+0.001 as real)) else 100000+due end)" + f"(case when queue={QUEUE_TYPE_REV} and due <= %d then (ivl / cast(%d-due+0.001 as real)) else 100000+due end)" % (self.today, self.today) ) else: @@ -1106,9 +1116,9 @@ due = odue, odue = 0, odid = 0, usn = ? where %s""" data.append((did, -100000 + c, u, id)) # due reviews stay in the review queue. careful: can't use # "odid or did", as sqlite converts to boolean - queue = """ -(case when type=2 and (case when odue then odue <= %d else due <= %d end) - then 2 else 0 end)""" + queue = f""" +(case when type={CARD_TYPE_REV} and (case when odue then odue <= %d else due <= %d end) + then {QUEUE_TYPE_REV} else {QUEUE_TYPE_NEW} end)""" queue %= (self.today, self.today) self.col.db.executemany( """ @@ -1121,7 +1131,7 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?""" ) def _dynIvlBoost(self, card): - assert card.odid and card.type == 2 + assert card.odid and card.type == CARD_TYPE_REV assert card.factor elapsed = card.ivl - (card.odue - self.today) factor = ((card.factor / 1000) + 1.2) / 2 @@ -1145,14 +1155,14 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?""" f.flush() # handle a = conf["leechAction"] - if a == 0: + if a == LEECH_SUSPEND: # if it has an old due, remove it from cram/relearning if card.odue: card.due = card.odue if card.odid: card.did = card.odid card.odue = card.odid = 0 - card.queue = -1 + card.queue = QUEUE_TYPE_SUSPENDED # notify UI hooks.card_did_leech(card) return True @@ -1311,7 +1321,7 @@ To study outside of the normal schedule, click the Custom Study button below.""" "True if there are any rev cards due." return self.col.db.scalar( ( - "select 1 from cards where did in %s and queue = 2 " + f"select 1 from cards where did in %s and queue = {QUEUE_TYPE_REV} " "and due <= ? limit 1" ) % self._deckLimit(), @@ -1321,14 +1331,18 @@ To study outside of the normal schedule, click the Custom Study button below.""" def newDue(self): "True if there are any new cards due." return self.col.db.scalar( - ("select 1 from cards where did in %s and queue = 0 " "limit 1") + ( + f"select 1 from cards where did in %s and queue = {QUEUE_TYPE_NEW} " + "limit 1" + ) % self._deckLimit() ) def haveBuried(self): sdids = ids2str(self.col.decks.active()) cnt = self.col.db.scalar( - "select 1 from cards where queue = -2 and did in %s limit 1" % sdids + f"select 1 from cards where queue = {QUEUE_TYPE_SIBLING_BURIED} and did in %s limit 1" + % sdids ) return not not cnt @@ -1347,9 +1361,9 @@ To study outside of the normal schedule, click the Custom Study button below.""" def nextIvl(self, card, ease): "Return the next interval for CARD, in seconds." - if card.queue in (0, 1, 3): + if card.queue in (QUEUE_TYPE_NEW, QUEUE_TYPE_LRN, QUEUE_TYPE_DAY_LEARN_RELEARN): return self._nextLrnIvl(card, ease) - elif ease == 1: + elif ease == BUTTON_ONE: # lapsed conf = self._lapseConf(card) if conf["delays"]: @@ -1364,10 +1378,10 @@ To study outside of the normal schedule, click the Custom Study button below.""" if card.queue == 0: card.left = self._startingLeft(card) conf = self._lrnConf(card) - if ease == 1: + if ease == BUTTON_ONE: # fail return self._delayForGrade(conf, len(conf["delays"])) - elif ease == 3: + elif ease == BUTTON_THREE: # early removal if not self._resched(card): return 0 @@ -1391,7 +1405,8 @@ To study outside of the normal schedule, click the Custom Study button below.""" self.remFromDyn(ids) self.removeLrn(ids) self.col.db.execute( - "update cards set queue=-1,mod=?,usn=? where id in " + ids2str(ids), + f"update cards set queue={QUEUE_TYPE_SUSPENDED},mod=?,usn=? where id in " + + ids2str(ids), intTime(), self.col.usn(), ) @@ -1401,7 +1416,7 @@ To study outside of the normal schedule, click the Custom Study button below.""" self.col.log(ids) self.col.db.execute( "update cards set queue=type,mod=?,usn=? " - "where queue = -1 and id in " + ids2str(ids), + f"where queue = {QUEUE_TYPE_SUSPENDED} and id in " + ids2str(ids), intTime(), self.col.usn(), ) @@ -1411,8 +1426,8 @@ To study outside of the normal schedule, click the Custom Study button below.""" self.remFromDyn(cids) self.removeLrn(cids) self.col.db.execute( - """ -update cards set queue=-2,mod=?,usn=? where id in """ + f""" +update cards set queue={QUEUE_TYPE_SIBLING_BURIED},mod=?,usn=? where id in """ + ids2str(cids), intTime(), self.col.usn(), @@ -1436,14 +1451,14 @@ update cards set queue=-2,mod=?,usn=? where id in """ buryRev = rconf.get("bury", True) # loop through and remove from queues for cid, queue in self.col.db.execute( - """ + f""" select id, queue from cards where nid=? and id!=? -and (queue=0 or (queue=2 and due<=?))""", +and (queue={QUEUE_TYPE_NEW} or (queue={QUEUE_TYPE_REV} and due<=?))""", card.nid, card.id, self.today, ): - if queue == 2: + if queue == QUEUE_TYPE_REV: if buryRev: toBury.append(cid) # if bury disabled, we still discard to give same-day spacing @@ -1462,7 +1477,8 @@ and (queue=0 or (queue=2 and due<=?))""", # then bury if toBury: self.col.db.execute( - "update cards set queue=-2,mod=?,usn=? where id in " + ids2str(toBury), + f"update cards set queue={QUEUE_TYPE_SIBLING_BURIED},mod=?,usn=? where id in " + + ids2str(toBury), intTime(), self.col.usn(), ) @@ -1475,11 +1491,14 @@ and (queue=0 or (queue=2 and due<=?))""", "Put cards at the end of the new queue." self.remFromDyn(ids) self.col.db.execute( - "update cards set type=0,queue=0,ivl=0,due=0,odue=0,factor=?" + f"update cards set type={CARD_TYPE_NEW},queue={QUEUE_TYPE_NEW},ivl=0,due=0,odue=0,factor=?" " where id in " + ids2str(ids), STARTING_FACTOR, ) - pmax = self.col.db.scalar("select max(due) from cards where type=0") or 0 + pmax = ( + self.col.db.scalar(f"select max(due) from cards where type={CARD_TYPE_NEW}") + or 0 + ) # takes care of mod + usn self.sortCards(ids, start=pmax + 1) self.col.log(ids) @@ -1503,8 +1522,8 @@ and (queue=0 or (queue=2 and due<=?))""", ) self.remFromDyn(ids) self.col.db.executemany( - """ -update cards set type=2,queue=2,ivl=:ivl,due=:due,odue=0, + f""" +update cards set type={CARD_TYPE_REV},queue={QUEUE_TYPE_REV},ivl=:ivl,due=:due,odue=0, usn=:usn,mod=:mod,factor=:fact where id=:id""", d, ) @@ -1515,11 +1534,12 @@ usn=:usn,mod=:mod,factor=:fact where id=:id""", sids = ids2str(ids) # we want to avoid resetting due number of existing new cards on export nonNew = self.col.db.list( - "select id from cards where id in %s and (queue != 0 or type != 0)" % sids + f"select id from cards where id in %s and (queue != {QUEUE_TYPE_NEW} or type != {CARD_TYPE_NEW})" + % sids ) # reset all cards self.col.db.execute( - "update cards set reps=0,lapses=0,odid=0,odue=0,queue=0" + f"update cards set reps=0,lapses=0,odid=0,odue=0,queue={QUEUE_TYPE_NEW}" " where id in %s" % sids ) # and forget any non-new cards, changing their due numbers @@ -1553,16 +1573,16 @@ usn=:usn,mod=:mod,factor=:fact where id=:id""", # shift? if shift: low = self.col.db.scalar( - "select min(due) from cards where due >= ? and type = 0 " + f"select min(due) from cards where due >= ? and type = {CARD_TYPE_NEW} " "and id not in %s" % scids, start, ) if low is not None: shiftby = high - low + 1 self.col.db.execute( - """ + f""" update cards set mod=?, usn=?, due=due+? where id not in %s -and due >= ? and queue = 0""" +and due >= ? and queue = {QUEUE_TYPE_NEW}""" % scids, now, self.col.usn(), @@ -1572,7 +1592,7 @@ and due >= ? and queue = 0""" # reorder cards d = [] for id, nid in self.col.db.execute( - "select id, nid from cards where type = 0 and id in " + scids + f"select id, nid from cards where type = {CARD_TYPE_NEW} and id in " + scids ): d.append(dict(now=now, due=due[nid], usn=self.col.usn(), cid=id)) self.col.db.executemany( diff --git a/pylib/anki/schedv2.py b/pylib/anki/schedv2.py index 8ce085f58..3a3e88846 100644 --- a/pylib/anki/schedv2.py +++ b/pylib/anki/schedv2.py @@ -22,13 +22,9 @@ from anki.rsbackend import SchedTimingToday from anki.utils import fmtTimeSpan, ids2str, intTime # card types: 0=new, 1=lrn, 2=rev, 3=relrn -CARD_TYPE_RELEARNING = 3 # queue types: 0=new, 1=(re)lrn, 2=rev, 3=day (re)lrn, # 4=preview, -1=suspended, -2=sibling buried, -3=manually buried -QUEUE_TYPE_PREVIEW = 4 -QUEUE_TYPE_DAY_LEARN_RELEARN = 3 -QUEUE_TYPE_SIBLING_BURIED = -2 -QUEUE_TYPE_MANUALLY_BURIED = -3 + # revlog types: 0=lrn, 1=rev, 2=relrn, 3=early review # positive revlog intervals are in days (rev), negative in seconds (lrn) # odue/odid store original due/did when cards moved to filtered deck @@ -95,18 +91,18 @@ class Scheduler: card.reps += 1 - if card.queue == 0: + if card.queue == QUEUE_TYPE_NEW: # came from the new queue, move to learning - card.queue = 1 - card.type = 1 + card.queue = QUEUE_TYPE_LRN + card.type = CARD_TYPE_LRN # init reps to graduation card.left = self._startingLeft(card) # update daily limit self._updateStats(card, "new") - if card.queue in (1, QUEUE_TYPE_DAY_LEARN_RELEARN): + if card.queue in (QUEUE_TYPE_LRN, QUEUE_TYPE_DAY_LEARN_RELEARN): self._answerLrnCard(card, ease) - elif card.queue == 2: + elif card.queue == QUEUE_TYPE_REV: self._answerRevCard(card, ease) # update daily limit self._updateStats(card, "rev") @@ -121,12 +117,13 @@ class Scheduler: def _answerCardPreview(self, card: Card, ease: int) -> None: assert 1 <= ease <= 2 - if ease == 1: + if ease == BUTTON_ONE: # repeat after delay card.queue = QUEUE_TYPE_PREVIEW card.due = intTime() + self._previewDelay(card) self.lrnCount += 1 else: + # BUTTON_TWO # restore original card state and remove from filtered deck self._restorePreviewCard(card) self._removeFromFiltered(card) @@ -142,9 +139,9 @@ class Scheduler: "Return counts over next DAYS. Includes today." daysd = dict( self.col.db.all( - """ + f""" select due, count() from cards -where did in %s and queue = 2 +where did in %s and queue = {QUEUE_TYPE_REV} and due between ? and ? group by due order by due""" @@ -368,9 +365,9 @@ order by due""" def _resetNewCount(self) -> None: cntFn = lambda did, lim: self.col.db.scalar( - """ + f""" select count() from (select 1 from cards where -did = ? and queue = 0 limit ?)""", +did = ? and queue = {QUEUE_TYPE_NEW} limit ?)""", did, lim, ) @@ -393,8 +390,8 @@ did = ? and queue = 0 limit ?)""", if lim: # fill the queue with the current did self._newQueue = self.col.db.list( - """ - select id from cards where did = ? and queue = 0 order by due,ord limit ?""", + f""" + select id from cards where did = ? and queue = {QUEUE_TYPE_NEW} order by due,ord limit ?""", did, lim, ) @@ -462,9 +459,9 @@ did = ? and queue = 0 limit ?)""", return 0 lim = min(lim, self.reportLimit) return self.col.db.scalar( - """ + f""" select count() from -(select 1 from cards where did = ? and queue = 0 limit ?)""", +(select 1 from cards where did = ? and queue = {QUEUE_TYPE_NEW} limit ?)""", did, lim, ) @@ -478,9 +475,9 @@ select count() from def totalNewForCurrentDeck(self) -> Any: return self.col.db.scalar( - """ + f""" select count() from cards where id in ( -select id from cards where did in %s and queue = 0 limit ?)""" +select id from cards where did in %s and queue = {QUEUE_TYPE_NEW} limit ?)""" % self._deckLimit(), self.reportLimit, ) @@ -504,8 +501,8 @@ select id from cards where did in %s and queue = 0 limit ?)""" # sub-day self.lrnCount = ( self.col.db.scalar( - """ -select count() from cards where did in %s and queue = 1 + f""" +select count() from cards where did in %s and queue = {QUEUE_TYPE_LRN} and due < ?""" % (self._deckLimit()), self._lrnCutoff, @@ -545,7 +542,7 @@ select count() from cards where did in %s and queue = {QUEUE_TYPE_PREVIEW} self._lrnQueue = self.col.db.all( f""" select due, id from cards where -did in %s and queue in (1,{QUEUE_TYPE_PREVIEW}) and due < :lim +did in %s and queue in ({QUEUE_TYPE_LRN},{QUEUE_TYPE_PREVIEW}) and due < :lim limit %d""" % (self._deckLimit(), self.reportLimit), lim=cutoff, @@ -606,28 +603,28 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""", def _answerLrnCard(self, card: Card, ease: int) -> None: conf = self._lrnConf(card) - if card.type in (2, CARD_TYPE_RELEARNING): - type = 2 + if card.type in (CARD_TYPE_REV, CARD_TYPE_RELEARNING): + type = REVLOG_RELRN else: - type = 0 + type = REVLOG_LRN # lrnCount was decremented once when card was fetched lastLeft = card.left leaving = False # immediate graduate? - if ease == 4: + if ease == BUTTON_FOUR: self._rescheduleAsRev(card, conf, True) leaving = True # next step? - elif ease == 3: + elif ease == BUTTON_THREE: # graduation time? if (card.left % 1000) - 1 <= 0: self._rescheduleAsRev(card, conf, False) leaving = True else: self._moveToNextStep(card, conf) - elif ease == 2: + elif ease == BUTTON_TWO: self._repeatStep(card, conf) else: # back to first step @@ -673,7 +670,7 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""", maxExtra = min(300, int(delay * 0.25)) fuzz = random.randrange(0, maxExtra) card.due = min(self.dayCutoff - 1, card.due + fuzz) - card.queue = 1 + card.queue = QUEUE_TYPE_LRN if card.due < (intTime() + self.col.conf["collapseTime"]): self.lrnCount += 1 # if the queue is not empty and there's nothing else to do, make @@ -714,13 +711,13 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""", return avg def _lrnConf(self, card: Card) -> Any: - if card.type in (2, CARD_TYPE_RELEARNING): + if card.type in (CARD_TYPE_REV, CARD_TYPE_RELEARNING): return self._lapseConf(card) else: return self._newConf(card) def _rescheduleAsRev(self, card: Card, conf: Dict[str, Any], early: bool) -> None: - lapse = card.type in (2, CARD_TYPE_RELEARNING) + lapse = card.type in (CARD_TYPE_REV, CARD_TYPE_RELEARNING) if lapse: self._rescheduleGraduatingLapse(card, early) @@ -735,8 +732,8 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""", if early: card.ivl += 1 card.due = self.today + card.ivl - card.queue = 2 - card.type = 2 + card.queue = QUEUE_TYPE_REV + card.type = CARD_TYPE_REV def _startingLeft(self, card: Card) -> int: if card.type == CARD_TYPE_RELEARNING: @@ -768,7 +765,7 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""", def _graduatingIvl( self, card: Card, conf: Dict[str, Any], early: bool, fuzz: bool = True ) -> Any: - if card.type in (2, CARD_TYPE_RELEARNING): + if card.type in (CARD_TYPE_REV, CARD_TYPE_RELEARNING): bonus = early and 1 or 0 return card.ivl + bonus if not early: @@ -786,7 +783,7 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""", card.ivl = self._graduatingIvl(card, conf, early) card.due = self.today + card.ivl card.factor = conf["initialFactor"] - card.type = card.queue = 2 + card.type = card.queue = QUEUE_TYPE_REV def _logLrn( self, @@ -801,7 +798,7 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""", if leaving: ivl = card.ivl else: - if ease == 2: + if ease == BUTTON_TWO: ivl = -self._delayForRepeatingGrade(conf, card.left) else: ivl = -self._delayForGrade(conf, card.left) @@ -830,9 +827,9 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""", def _lrnForDeck(self, did: int) -> Any: cnt = ( self.col.db.scalar( - """ + f""" select count() from -(select null from cards where did = ? and queue = 1 and due < ? limit ?)""", +(select null from cards where did = ? and queue = {QUEUE_TYPE_LRN} and due < ? limit ?)""", did, intTime() + self.col.conf["collapseTime"], self.reportLimit, @@ -883,9 +880,9 @@ and due <= ? limit ?)""", dids = [did] + self.col.decks.childDids(did, childMap) lim = min(lim, self.reportLimit) return self.col.db.scalar( - """ + f""" select count() from -(select 1 from cards where did in %s and queue = 2 +(select 1 from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? limit ?)""" % ids2str(dids), self.today, @@ -895,9 +892,9 @@ and due <= ? limit ?)""" def _resetRevCount(self) -> None: lim = self._currentRevLimit() self.revCount = self.col.db.scalar( - """ + f""" select count() from (select id from cards where -did in %s and queue = 2 and due <= ? limit ?)""" +did in %s and queue = {QUEUE_TYPE_REV} and due <= ? limit ?)""" % self._deckLimit(), self.today, lim, @@ -916,9 +913,9 @@ did in %s and queue = 2 and due <= ? limit ?)""" lim = min(self.queueLimit, self._currentRevLimit()) if lim: self._revQueue = self.col.db.list( - """ + f""" select id from cards where -did in %s and queue = 2 and due <= ? +did in %s and queue = {QUEUE_TYPE_REV} and due <= ? order by due, random() limit ?""" % self._deckLimit(), @@ -946,9 +943,9 @@ limit ?""" def totalRevForCurrentDeck(self) -> int: return self.col.db.scalar( - """ + f""" select count() from cards where id in ( -select id from cards where did in %s and queue = 2 and due <= ? limit ?)""" +select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? limit ?)""" % self._deckLimit(), self.today, self.reportLimit, @@ -960,9 +957,9 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)""" def _answerRevCard(self, card: Card, ease: int) -> None: delay = 0 early = bool(card.odid and (card.odue > self.today)) - type = early and 3 or 1 + type = early and REVLOG_CRAM or REVLOG_REV - if ease == 1: + if ease == BUTTON_ONE: delay = self._rescheduleLapse(card) else: self._rescheduleRev(card, ease, early) @@ -976,7 +973,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)""" card.lapses += 1 card.factor = max(1300, card.factor - 200) - suspended = self._checkLeech(card, conf) and card.queue == -1 + suspended = self._checkLeech(card, conf) and card.queue == QUEUE_TYPE_SUSPENDED if conf["delays"] and not suspended: card.type = CARD_TYPE_RELEARNING @@ -987,7 +984,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)""" self._rescheduleAsRev(card, conf, early=False) # need to reset the queue after rescheduling if suspended: - card.queue = -1 + card.queue = QUEUE_TYPE_SUSPENDED delay = 0 return delay @@ -1047,11 +1044,11 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)""" else: hardMin = 0 ivl2 = self._constrainedIvl(card.ivl * hardFactor, conf, hardMin, fuzz) - if ease == 2: + if ease == BUTTON_TWO: return ivl2 ivl3 = self._constrainedIvl((card.ivl + delay // 2) * fct, conf, ivl2, fuzz) - if ease == 3: + if ease == BUTTON_THREE: return ivl3 ivl4 = self._constrainedIvl( @@ -1101,7 +1098,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)""" # next interval for card when answered early+correctly def _earlyReviewIvl(self, card: Card, ease: int) -> int: - assert card.odid and card.type == 2 + assert card.odid and card.type == CARD_TYPE_REV assert card.factor assert ease > 1 @@ -1113,14 +1110,14 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)""" # early 3/4 reviews shouldn't decrease previous interval minNewIvl = 1 - if ease == 2: + if ease == BUTTON_TWO: factor = conf.get("hardFactor", 1.2) # hard cards shouldn't have their interval decreased by more than 50% # of the normal factor minNewIvl = factor / 2 - elif ease == 3: + elif ease == BUTTON_THREE: factor = card.factor / 1000 - else: # ease == 4: + else: # ease == BUTTON_FOUR: factor = card.factor / 1000 ease4 = conf["ease4"] # 1.3 -> 1.15 @@ -1213,7 +1210,7 @@ due = (case when odue>0 then odue else due end), odue = 0, odid = 0, usn = ? whe t = "n.id desc" elif o == DYN_DUEPRIORITY: t = ( - "(case when queue=2 and due <= %d then (ivl / cast(%d-due+0.001 as real)) else 100000+due end)" + f"(case when queue={QUEUE_TYPE_REV} and due <= %d then (ivl / cast(%d-due+0.001 as real)) else 100000+due end)" % (self.today, self.today) ) else: # DYN_DUE or unknown @@ -1231,7 +1228,7 @@ due = (case when odue>0 then odue else due end), odue = 0, odid = 0, usn = ? whe queue = "" if not deck["resched"]: - queue = ",queue=2" + queue = f",queue={QUEUE_TYPE_REV}" query = ( """ @@ -1260,9 +1257,9 @@ where id = ? # learning and relearning cards may be seconds-based or day-based; # other types map directly to queues - if card.type in (1, CARD_TYPE_RELEARNING): + if card.type in (CARD_TYPE_LRN, CARD_TYPE_RELEARNING): if card.odue > 1000000000: - card.queue = 1 + card.queue = QUEUE_TYPE_LRN else: card.queue = QUEUE_TYPE_DAY_LEARN_RELEARN else: @@ -1284,8 +1281,8 @@ where id = ? f.flush() # handle a = conf["leechAction"] - if a == 0: - card.queue = -1 + if a == LEECH_SUSPEND: + card.queue = QUEUE_TYPE_SUSPENDED # notify UI hooks.card_did_leech(card) return True @@ -1509,7 +1506,7 @@ To study outside of the normal schedule, click the Custom Study button below.""" "True if there are any rev cards due." return self.col.db.scalar( ( - "select 1 from cards where did in %s and queue = 2 " + f"select 1 from cards where did in %s and queue = {QUEUE_TYPE_REV} " "and due <= ? limit 1" ) % self._deckLimit(), @@ -1519,7 +1516,10 @@ To study outside of the normal schedule, click the Custom Study button below.""" def newDue(self) -> Any: "True if there are any new cards due." return self.col.db.scalar( - ("select 1 from cards where did in %s and queue = 0 " "limit 1") + ( + f"select 1 from cards where did in %s and queue = {QUEUE_TYPE_NEW} " + "limit 1" + ) % self._deckLimit() ) @@ -1557,14 +1557,14 @@ To study outside of the normal schedule, click the Custom Study button below.""" "Return the next interval for CARD, in seconds." # preview mode? if self._previewingCard(card): - if ease == 1: + if ease == BUTTON_ONE: return self._previewDelay(card) return 0 # (re)learning? - if card.queue in (0, 1, QUEUE_TYPE_DAY_LEARN_RELEARN): + if card.queue in (QUEUE_TYPE_NEW, QUEUE_TYPE_LRN, QUEUE_TYPE_DAY_LEARN_RELEARN): return self._nextLrnIvl(card, ease) - elif ease == 1: + elif ease == BUTTON_ONE: # lapse conf = self._lapseConf(card) if conf["delays"]: @@ -1580,17 +1580,17 @@ To study outside of the normal schedule, click the Custom Study button below.""" # this isn't easily extracted from the learn code def _nextLrnIvl(self, card: Card, ease: int) -> Any: - if card.queue == 0: + if card.queue == QUEUE_TYPE_NEW: card.left = self._startingLeft(card) conf = self._lrnConf(card) - if ease == 1: + if ease == BUTTON_ONE: # fail return self._delayForGrade(conf, len(conf["delays"])) - elif ease == 2: + elif ease == BUTTON_TWO: return self._delayForRepeatingGrade(conf, card.left) - elif ease == 4: + elif ease == BUTTON_FOUR: return self._graduatingIvl(card, conf, True, fuzz=False) * 86400 - else: # ease == 3 + else: # ease == BUTTON_THREE left = card.left % 1000 - 1 if left <= 0: # graduate @@ -1604,7 +1604,7 @@ To study outside of the normal schedule, click the Custom Study button below.""" # learning and relearning cards may be seconds-based or day-based; # other types map directly to queues _restoreQueueSnippet = f""" -queue = (case when type in (1,{CARD_TYPE_RELEARNING}) then +queue = (case when type in ({CARD_TYPE_LRN},{CARD_TYPE_RELEARNING}) then (case when (case when odue then odue else due end) > 1000000000 then 1 else {QUEUE_TYPE_DAY_LEARN_RELEARN} end) else @@ -1616,7 +1616,8 @@ end) "Suspend cards." self.col.log(ids) self.col.db.execute( - "update cards set queue=-1,mod=?,usn=? where id in " + ids2str(ids), + f"update cards set queue={QUEUE_TYPE_SUSPENDED},mod=?,usn=? where id in " + + ids2str(ids), intTime(), self.col.usn(), ) @@ -1625,7 +1626,9 @@ end) "Unsuspend cards." self.col.log(ids) self.col.db.execute( - ("update cards set %s,mod=?,usn=? " "where queue = -1 and id in %s") + ( + f"update cards set %s,mod=?,usn=? where queue = {QUEUE_TYPE_SUSPENDED} and id in %s" + ) % (self._restoreQueueSnippet, ids2str(ids)), intTime(), self.col.usn(), @@ -1646,7 +1649,7 @@ update cards set queue=?,mod=?,usn=? where id in """ def buryNote(self, nid) -> None: "Bury all cards for note until next session." cids = self.col.db.list( - "select id from cards where nid = ? and queue >= 0", nid + f"select id from cards where nid = ? and queue >= {QUEUE_TYPE_NEW}", nid ) self.buryCards(cids) @@ -1654,11 +1657,11 @@ update cards set queue=?,mod=?,usn=? where id in """ "Unbury all buried cards in all decks." self.col.log( self.col.db.list( - f"select id from cards where queue in (-2, {QUEUE_TYPE_MANUALLY_BURIED})" + f"select id from cards where queue in ({QUEUE_TYPE_SIBLING_BURIED}, {QUEUE_TYPE_MANUALLY_BURIED})" ) ) self.col.db.execute( - f"update cards set %s where queue in (-2, {QUEUE_TYPE_MANUALLY_BURIED})" + f"update cards set %s where queue in ({QUEUE_TYPE_SIBLING_BURIED}, {QUEUE_TYPE_MANUALLY_BURIED})" % self._restoreQueueSnippet ) @@ -1698,14 +1701,14 @@ update cards set queue=?,mod=?,usn=? where id in """ buryRev = rconf.get("bury", True) # loop through and remove from queues for cid, queue in self.col.db.execute( - """ + f""" select id, queue from cards where nid=? and id!=? -and (queue=0 or (queue=2 and due<=?))""", +and (queue={QUEUE_TYPE_NEW} or (queue={QUEUE_TYPE_REV} and due<=?))""", card.nid, card.id, self.today, ): - if queue == 2: + if queue == QUEUE_TYPE_REV: if buryRev: toBury.append(cid) # if bury disabled, we still discard to give same-day spacing @@ -1732,11 +1735,14 @@ and (queue=0 or (queue=2 and due<=?))""", "Put cards at the end of the new queue." self.remFromDyn(ids) self.col.db.execute( - "update cards set type=0,queue=0,ivl=0,due=0,odue=0,factor=?" + f"update cards set type={CARD_TYPE_NEW},queue={QUEUE_TYPE_NEW},ivl=0,due=0,odue=0,factor=?" " where id in " + ids2str(ids), STARTING_FACTOR, ) - pmax = self.col.db.scalar("select max(due) from cards where type=0") or 0 + pmax = ( + self.col.db.scalar(f"select max(due) from cards where type={CARD_TYPE_NEW}") + or 0 + ) # takes care of mod + usn self.sortCards(ids, start=pmax + 1) self.col.log(ids) @@ -1760,8 +1766,8 @@ and (queue=0 or (queue=2 and due<=?))""", ) self.remFromDyn(ids) self.col.db.executemany( - """ -update cards set type=2,queue=2,ivl=:ivl,due=:due,odue=0, + f""" +update cards set type={CARD_TYPE_REV},queue={QUEUE_TYPE_REV},ivl=:ivl,due=:due,odue=0, usn=:usn,mod=:mod,factor=:fact where id=:id""", d, ) @@ -1772,11 +1778,12 @@ usn=:usn,mod=:mod,factor=:fact where id=:id""", sids = ids2str(ids) # we want to avoid resetting due number of existing new cards on export nonNew = self.col.db.list( - "select id from cards where id in %s and (queue != 0 or type != 0)" % sids + f"select id from cards where id in %s and (queue != {QUEUE_TYPE_NEW} or type != {CARD_TYPE_NEW})" + % sids ) # reset all cards self.col.db.execute( - "update cards set reps=0,lapses=0,odid=0,odue=0,queue=0" + f"update cards set reps=0,lapses=0,odid=0,odue=0,queue={QUEUE_TYPE_NEW}" " where id in %s" % sids ) # and forget any non-new cards, changing their due numbers @@ -1817,16 +1824,16 @@ usn=:usn,mod=:mod,factor=:fact where id=:id""", # shift? if shift: low = self.col.db.scalar( - "select min(due) from cards where due >= ? and type = 0 " + f"select min(due) from cards where due >= ? and type = {CARD_TYPE_NEW} " "and id not in %s" % scids, start, ) if low is not None: shiftby = high - low + 1 self.col.db.execute( - """ + f""" update cards set mod=?, usn=?, due=due+? where id not in %s -and due >= ? and queue = 0""" +and due >= ? and queue = {QUEUE_TYPE_NEW}""" % scids, now, self.col.usn(), @@ -1836,7 +1843,7 @@ and due >= ? and queue = 0""" # reorder cards d = [] for id, nid in self.col.db.execute( - "select id, nid from cards where type = 0 and id in " + scids + f"select id, nid from cards where type = {CARD_TYPE_NEW} and id in " + scids ): d.append(dict(now=now, due=due[nid], usn=self.col.usn(), cid=id)) self.col.db.executemany( @@ -1874,11 +1881,11 @@ and due >= ? and queue = 0""" self.col.db.execute( f""" update cards set did = odid, queue = (case -when type = 1 then 0 -when type = {CARD_TYPE_RELEARNING} then 2 +when type = {CARD_TYPE_LRN} then {QUEUE_TYPE_NEW} +when type = {CARD_TYPE_RELEARNING} then {QUEUE_TYPE_REV} else type end), type = (case -when type = 1 then 0 -when type = {CARD_TYPE_RELEARNING} then 2 +when type = {CARD_TYPE_LRN} then {CARD_TYPE_NEW} +when type = {CARD_TYPE_RELEARNING} then {CARD_TYPE_REV} else type end), due = odue, odue = 0, odid = 0, usn = ? where odid != 0""", self.col.usn(), @@ -1890,8 +1897,8 @@ due = odue, odue = 0, odid = 0, usn = ? where odid != 0""", self.col.db.execute( f""" update cards set - due = odue, queue = 2, type = 2, mod = %d, usn = %d, odue = 0 - where queue in (1,{QUEUE_TYPE_DAY_LEARN_RELEARN}) and type in (2, {CARD_TYPE_RELEARNING}) + due = odue, queue = {QUEUE_TYPE_REV}, type = {CARD_TYPE_REV}, mod = %d, usn = %d, odue = 0 + where queue in ({QUEUE_TYPE_LRN},{QUEUE_TYPE_DAY_LEARN_RELEARN}) and type in ({CARD_TYPE_REV}, {CARD_TYPE_RELEARNING}) """ % (intTime(), self.col.usn()) ) @@ -1899,15 +1906,15 @@ due = odue, odue = 0, odid = 0, usn = ? where odid != 0""", self.col.db.execute( f""" update cards set - due = %d+ivl, queue = 2, type = 2, mod = %d, usn = %d, odue = 0 - where queue in (1,{QUEUE_TYPE_DAY_LEARN_RELEARN}) and type in (2, {CARD_TYPE_RELEARNING}) + due = %d+ivl, queue = {QUEUE_TYPE_REV}, type = {CARD_TYPE_REV}, mod = %d, usn = %d, odue = 0 + where queue in ({QUEUE_TYPE_LRN},{QUEUE_TYPE_DAY_LEARN_RELEARN}) and type in ({CARD_TYPE_REV}, {CARD_TYPE_RELEARNING}) """ % (self.today, intTime(), self.col.usn()) ) # remove new cards from learning self.forgetCards( self.col.db.list( - f"select id from cards where queue in (1,{QUEUE_TYPE_DAY_LEARN_RELEARN})" + f"select id from cards where queue in ({QUEUE_TYPE_LRN},{QUEUE_TYPE_DAY_LEARN_RELEARN})" ) ) @@ -1916,13 +1923,13 @@ due = odue, odue = 0, odid = 0, usn = ? where odid != 0""", self.col.db.execute( f""" update cards set type = (case -when type = 1 then 0 -when type in (2, {CARD_TYPE_RELEARNING}) then 2 +when type = {CARD_TYPE_LRN} then {CARD_TYPE_NEW} +when type in ({CARD_TYPE_REV}, {CARD_TYPE_RELEARNING}) then {CARD_TYPE_REV} else type end), due = (case when odue then odue else due end), odue = 0, mod = %d, usn = %d -where queue < 0""" +where queue < {QUEUE_TYPE_NEW}""" % (intTime(), self.col.usn()) ) @@ -1936,7 +1943,9 @@ where queue < 0""" # adding 'hard' in v2 scheduler means old ease entries need shifting # up or down def _remapLearningAnswers(self, sql: str) -> None: - self.col.db.execute("update revlog set %s and type in (0,2)" % sql) + self.col.db.execute( + f"update revlog set %s and type in ({CARD_TYPE_NEW},{CARD_TYPE_REV})" % sql + ) def moveToV1(self) -> None: self._emptyAllFiltered() diff --git a/pylib/anki/stats.py b/pylib/anki/stats.py index 3ea278475..c67951db1 100644 --- a/pylib/anki/stats.py +++ b/pylib/anki/stats.py @@ -6,12 +6,17 @@ import json import time from typing import Any, Dict, List, Optional, Tuple +from anki.consts import * from anki.lang import _, ngettext from anki.utils import fmtTimeSpan, ids2str # Card stats ########################################################################## +PERIOD_MONTH = 0 +PERIOD_YEAR = 1 +PERIOD_LIFE = 2 + class CardStats: def __init__(self, col, card) -> None: @@ -30,18 +35,18 @@ class CardStats: if first: self.addLine(_("First Review"), self.date(first / 1000)) self.addLine(_("Latest Review"), self.date(last / 1000)) - if c.type in (1, 2): - if c.odid or c.queue < 0: + if c.type in (CARD_TYPE_LRN, CARD_TYPE_REV): + if c.odid or c.queue < QUEUE_TYPE_NEW: next = None else: - if c.queue in (2, 3): + if c.queue in (QUEUE_TYPE_REV, QUEUE_TYPE_DAY_LEARN_RELEARN): next = time.time() + ((c.due - self.col.sched.today) * 86400) else: next = c.due next = self.date(next) if next: self.addLine(_("Due"), next) - if c.queue == 2: + if c.queue == QUEUE_TYPE_REV: self.addLine(_("Interval"), fmt(c.ivl * 86400)) self.addLine(_("Ease"), "%d%%" % (c.factor / 10.0)) self.addLine(_("Reviews"), "%d" % c.reps) @@ -52,7 +57,7 @@ class CardStats: if cnt: self.addLine(_("Average Time"), self.time(total / float(cnt))) self.addLine(_("Total Time"), self.time(total)) - elif c.queue == 0: + elif c.queue == QUEUE_TYPE_NEW: self.addLine(_("Position"), c.due) self.addLine(_("Card Type"), c.template()["name"]) self.addLine(_("Note Type"), c.model()["name"]) @@ -102,14 +107,14 @@ class CollectionStats: def __init__(self, col) -> None: self.col = col self._stats = None - self.type = 0 + self.type = PERIOD_MONTH self.width = 600 self.height = 200 self.wholeCollection = False # assumes jquery & plot are available in document - def report(self, type=0) -> str: - # 0=days, 1=weeks, 2=months + def report(self, type=PERIOD_MONTH) -> str: + # 0=month, 1=year, 2=deck life self.type = type from .statsbg import bg @@ -149,13 +154,13 @@ body {background-image: url(data:image/png;base64,%s); } if lim: lim = " and " + lim cards, thetime, failed, lrn, rev, relrn, filt = self.col.db.first( - """ + f""" select count(), sum(time)/1000, sum(case when ease = 1 then 1 else 0 end), /* failed */ -sum(case when type = 0 then 1 else 0 end), /* learning */ -sum(case when type = 1 then 1 else 0 end), /* review */ -sum(case when type = 2 then 1 else 0 end), /* relearn */ -sum(case when type = 3 then 1 else 0 end) /* filter */ +sum(case when type = {REVLOG_LRN} then 1 else 0 end), /* learning */ +sum(case when type = {REVLOG_REV} then 1 else 0 end), /* review */ +sum(case when type = {REVLOG_RELRN} then 1 else 0 end), /* relearn */ +sum(case when type = {REVLOG_CRAM} then 1 else 0 end) /* filter */ from revlog where id > ? """ + lim, (self.col.sched.dayCutoff - 86400) * 1000, @@ -215,9 +220,9 @@ from revlog where id > ? """ def get_start_end_chunk(self, by="review") -> Tuple[int, Optional[int], int]: start = 0 - if self.type == 0: + if self.type == PERIOD_MONTH: end, chunk = 31, 1 - elif self.type == 1: + elif self.type == PERIOD_YEAR: end, chunk = 52, 7 else: # self.type == 2: end = None @@ -279,8 +284,8 @@ from revlog where id > ? """ self._line(i, _("Total"), ngettext("%d review", "%d reviews", tot) % tot) self._line(i, _("Average"), self._avgDay(tot, num, _("reviews"))) tomorrow = self.col.db.scalar( - """ -select count() from cards where did in %s and queue in (2,3) + f""" +select count() from cards where did in %s and queue in ({QUEUE_TYPE_REV},{QUEUE_TYPE_DAY_LEARN_RELEARN}) and due = ?""" % self._limit(), self.col.sched.today + 1, @@ -296,12 +301,12 @@ and due = ?""" if end is not None: lim += " and day < %d" % end return self.col.db.all( - """ + f""" select (due-:today)/:chunk as day, sum(case when ivl < 21 then 1 else 0 end), -- yng sum(case when ivl >= 21 then 1 else 0 end) -- mtr from cards -where did in %s and queue in (2,3) +where did in %s and queue in ({QUEUE_TYPE_REV},{QUEUE_TYPE_DAY_LEARN_RELEARN}) %s group by day order by day""" % (self._limit(), lim), @@ -396,7 +401,7 @@ group by day order by day""" (10, colCram, _("Cram")), ), ) - if self.type == 0: + if self.type == PERIOD_MONTH: t = _("Minutes") convHours = False else: @@ -513,7 +518,7 @@ group by day order by day""" lim = "where " + " and ".join(lims) else: lim = "" - if self.type == 0: + if self.type == PERIOD_MONTH: tf = 60.0 # minutes else: tf = 3600.0 # hours @@ -543,25 +548,25 @@ group by day order by day""" lim = "where " + " and ".join(lims) else: lim = "" - if self.type == 0: + if self.type == PERIOD_MONTH: tf = 60.0 # minutes else: tf = 3600.0 # hours return self.col.db.all( - """ + f""" select (cast((id/1000.0 - :cut) / 86400.0 as int))/:chunk as day, -sum(case when type = 0 then 1 else 0 end), -- lrn count -sum(case when type = 1 and lastIvl < 21 then 1 else 0 end), -- yng count -sum(case when type = 1 and lastIvl >= 21 then 1 else 0 end), -- mtr count -sum(case when type = 2 then 1 else 0 end), -- lapse count -sum(case when type = 3 then 1 else 0 end), -- cram count -sum(case when type = 0 then time/1000.0 else 0 end)/:tf, -- lrn time +sum(case when type = {REVLOG_LRN} then 1 else 0 end), -- lrn count +sum(case when type = {REVLOG_REV} and lastIvl < 21 then 1 else 0 end), -- yng count +sum(case when type = {REVLOG_REV} and lastIvl >= 21 then 1 else 0 end), -- mtr count +sum(case when type = {REVLOG_RELRN} then 1 else 0 end), -- lapse count +sum(case when type = {REVLOG_CRAM} then 1 else 0 end), -- cram count +sum(case when type = {REVLOG_LRN} then time/1000.0 else 0 end)/:tf, -- lrn time -- yng + mtr time -sum(case when type = 1 and lastIvl < 21 then time/1000.0 else 0 end)/:tf, -sum(case when type = 1 and lastIvl >= 21 then time/1000.0 else 0 end)/:tf, -sum(case when type = 2 then time/1000.0 else 0 end)/:tf, -- lapse time -sum(case when type = 3 then time/1000.0 else 0 end)/:tf -- cram time +sum(case when type = {REVLOG_REV} and lastIvl < 21 then time/1000.0 else 0 end)/:tf, +sum(case when type = {REVLOG_REV} and lastIvl >= 21 then time/1000.0 else 0 end)/:tf, +sum(case when type = {REVLOG_RELRN} then time/1000.0 else 0 end)/:tf, -- lapse time +sum(case when type = {REVLOG_CRAM} then time/1000.0 else 0 end)/:tf -- cram time from revlog %s group by day order by day""" % lim, @@ -606,9 +611,9 @@ group by day order by day)""" for (grp, cnt) in ivls: tot += cnt totd.append((grp, tot / float(all) * 100)) - if self.type == 0: + if self.type == PERIOD_MONTH: ivlmax = 31 - elif self.type == 1: + elif self.type == PERIOD_YEAR: ivlmax = 52 else: ivlmax = max(5, ivls[-1][0]) @@ -643,9 +648,9 @@ group by day order by day)""" lim = "and grp <= %d" % end if end else "" data = [ self.col.db.all( - """ + f""" select ivl / :chunk as grp, count() from cards -where did in %s and queue = 2 %s +where did in %s and queue = {QUEUE_TYPE_REV} %s group by grp order by grp""" % (self._limit(), lim), @@ -656,8 +661,8 @@ order by grp""" data + list( self.col.db.first( - """ -select count(), avg(ivl), max(ivl) from cards where did in %s and queue = 2""" + f""" +select count(), avg(ivl), max(ivl) from cards where did in %s and queue = {QUEUE_TYPE_REV}""" % self._limit() ) ), @@ -675,9 +680,9 @@ select count(), avg(ivl), max(ivl) from cards where did in %s and queue = 2""" types = ("lrn", "yng", "mtr") eases = self._eases() for (type, ease, cnt) in eases: - if type == 1: + if type == CARD_TYPE_LRN: ease += 5 - elif type == 2: + elif type == CARD_TYPE_REV: ease += 10 n = types[type] d[n].append((ease, cnt)) @@ -714,7 +719,7 @@ select count(), avg(ivl), max(ivl) from cards where did in %s and queue = 2""" return txt def _easeInfo(self, eases) -> str: - types = {0: [0, 0], 1: [0, 0], 2: [0, 0]} + types = {PERIOD_MONTH: [0, 0], PERIOD_YEAR: [0, 0], PERIOD_LIFE: [0, 0]} for (type, ease, cnt) in eases: if ease == 1: types[type][0] += cnt @@ -759,12 +764,12 @@ select count(), avg(ivl), max(ivl) from cards where did in %s and queue = 2""" else: ease4repl = "ease" return self.col.db.all( - """ + f""" select (case -when type in (0,2) then 0 +when type in ({REVLOG_LRN},{REVLOG_RELRN}) then 0 when lastIvl < 21 then 1 else 2 end) as thetype, -(case when type in (0,2) and ease = 4 then %s else ease end), count() from revlog %s +(case when type in ({REVLOG_LRN},{REVLOG_RELRN}) and ease = 4 then %s else ease end), count() from revlog %s group by thetype, ease order by thetype, ease""" % (ease4repl, lim) @@ -853,13 +858,13 @@ order by thetype, ease""" if pd: lim += " and id > %d" % ((self.col.sched.dayCutoff - (86400 * pd)) * 1000) return self.col.db.all( - """ + f""" select 23 - ((cast((:cut - id/1000) / 3600.0 as int)) %% 24) as hour, sum(case when ease = 1 then 0 else 1 end) / cast(count() as float) * 100, count() -from revlog where type in (0,1,2) %s +from revlog where type in ({REVLOG_LRN},{REVLOG_REV},{REVLOG_RELRN}) %s group by hour having count() > 30 order by hour""" % lim, cut=self.col.sched.dayCutoff - (rolloverHour * 3600), @@ -929,23 +934,23 @@ when you answer "good" on a review.""" def _factors(self) -> Any: return self.col.db.first( - """ + f""" select min(factor) / 10.0, avg(factor) / 10.0, max(factor) / 10.0 -from cards where did in %s and queue = 2""" +from cards where did in %s and queue = {QUEUE_TYPE_REV}""" % self._limit() ) def _cards(self) -> Any: return self.col.db.first( - """ + f""" select -sum(case when queue=2 and ivl >= 21 then 1 else 0 end), -- mtr -sum(case when queue in (1,3) or (queue=2 and ivl < 21) then 1 else 0 end), -- yng/lrn -sum(case when queue=0 then 1 else 0 end), -- new -sum(case when queue<0 then 1 else 0 end) -- susp +sum(case when queue={QUEUE_TYPE_REV} and ivl >= 21 then 1 else 0 end), -- mtr +sum(case when queue in ({QUEUE_TYPE_LRN},{QUEUE_TYPE_DAY_LEARN_RELEARN}) or (queue={QUEUE_TYPE_REV} and ivl < 21) then 1 else 0 end), -- yng/lrn +sum(case when queue={QUEUE_TYPE_NEW} then 1 else 0 end), -- new +sum(case when queue<{QUEUE_TYPE_NEW} then 1 else 0 end) -- susp from cards where did in %s""" % self._limit() ) diff --git a/pylib/anki/storage.py b/pylib/anki/storage.py index b3099780e..3950e77a6 100644 --- a/pylib/anki/storage.py +++ b/pylib/anki/storage.py @@ -111,7 +111,7 @@ def _upgrade(col, ver) -> None: if ver < 3: # new deck properties for d in col.decks.all(): - d["dyn"] = 0 + d["dyn"] = DECK_STD d["collapsed"] = False col.decks.save(d) if ver < 4: diff --git a/pylib/tests/test_find.py b/pylib/tests/test_find.py index e7dc215de..98ccc20a4 100644 --- a/pylib/tests/test_find.py +++ b/pylib/tests/test_find.py @@ -1,6 +1,7 @@ # coding: utf-8 import pytest +from anki.consts import * from anki.find import Finder from tests.shared import getEmptyCol @@ -91,13 +92,13 @@ def test_findCards(): assert len(deck.findCards('"goats are"')) == 1 # card states c = f.cards()[0] - c.queue = c.type = 2 + c.queue = c.type = CARD_TYPE_REV assert deck.findCards("is:review") == [] c.flush() assert deck.findCards("is:review") == [c.id] assert deck.findCards("is:due") == [] c.due = 0 - c.queue = 2 + c.queue = QUEUE_TYPE_REV c.flush() assert deck.findCards("is:due") == [c.id] assert len(deck.findCards("-is:due")) == 4 diff --git a/pylib/tests/test_schedv1.py b/pylib/tests/test_schedv1.py index bd5de3f08..5613badf2 100644 --- a/pylib/tests/test_schedv1.py +++ b/pylib/tests/test_schedv1.py @@ -4,7 +4,7 @@ import copy import time from anki import hooks -from anki.consts import STARTING_FACTOR +from anki.consts import * from anki.utils import intTime from tests.shared import getEmptyCol as getEmptyColOrig @@ -46,13 +46,13 @@ def test_new(): # fetch it c = d.sched.getCard() assert c - assert c.queue == 0 - assert c.type == 0 + assert c.queue == QUEUE_TYPE_NEW + assert c.type == CARD_TYPE_NEW # if we answer it, it should become a learn card t = intTime() d.sched.answerCard(c, 1) - assert c.queue == 1 - assert c.type == 1 + assert c.queue == QUEUE_TYPE_LRN + assert c.type == CARD_TYPE_LRN assert c.due >= t # disabled for now, as the learn fudging makes this randomly fail @@ -163,11 +163,11 @@ def test_learn(): 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 + assert c.queue == QUEUE_TYPE_LRN + assert c.type == CARD_TYPE_LRN d.sched.answerCard(c, 2) - assert c.queue == 2 - assert c.type == 2 + assert c.queue == QUEUE_TYPE_REV + assert c.type == CARD_TYPE_REV # should be due tomorrow, with an interval of 1 assert c.due == d.sched.today + 1 assert c.ivl == 1 @@ -175,27 +175,27 @@ def test_learn(): c.type = 0 c.queue = 1 d.sched.answerCard(c, 3) - assert c.type == 2 - assert c.queue == 2 + assert c.type == CARD_TYPE_REV + assert c.queue == QUEUE_TYPE_REV assert checkRevIvl(d, c, 4) # revlog should have been updated each time assert d.db.scalar("select count() from revlog where type = 0") == 5 # now failed card handling - c.type = 2 + c.type = CARD_TYPE_REV c.queue = 1 c.odue = 123 d.sched.answerCard(c, 3) assert c.due == 123 - assert c.type == 2 - assert c.queue == 2 + assert c.type == CARD_TYPE_REV + assert c.queue == QUEUE_TYPE_REV # we should be able to remove manually, too - c.type = 2 + c.type = CARD_TYPE_REV c.queue = 1 c.odue = 321 c.flush() d.sched.removeLrn() c.load() - assert c.queue == 2 + assert c.queue == QUEUE_TYPE_REV assert c.due == 321 @@ -247,7 +247,7 @@ def test_learn_day(): # answering it will place it in queue 3 d.sched.answerCard(c, 2) assert c.due == d.sched.today + 1 - assert c.queue == 3 + assert c.queue == CARD_TYPE_RELEARNING assert not d.sched.getCard() # for testing, move it back a day c.due -= 1 @@ -259,7 +259,7 @@ def test_learn_day(): assert ni(c, 2) == 86400 * 2 # if we fail it, it should be back in the correct queue d.sched.answerCard(c, 1) - assert c.queue == 1 + assert c.queue == QUEUE_TYPE_LRN d.undo() d.reset() c = d.sched.getCard() @@ -271,7 +271,7 @@ def test_learn_day(): # the last pass should graduate it into a review card assert ni(c, 2) == 86400 d.sched.answerCard(c, 2) - assert c.queue == c.type == 2 + assert c.queue == CARD_TYPE_REV and c.type == QUEUE_TYPE_REV # if the lapse step is tomorrow, failing it should handle the counts # correctly c.due = 0 @@ -281,7 +281,7 @@ def test_learn_day(): d.sched._cardConf(c)["lapse"]["delays"] = [1440] c = d.sched.getCard() d.sched.answerCard(c, 1) - assert c.queue == 3 + assert c.queue == CARD_TYPE_RELEARNING assert d.sched.counts() == (0, 0, 0) @@ -294,8 +294,8 @@ def test_reviews(): d.addNote(f) # set the card up as a review card, due 8 days ago c = f.cards()[0] - c.type = 2 - c.queue = 2 + c.type = CARD_TYPE_REV + c.queue = QUEUE_TYPE_REV c.due = d.sched.today - 8 c.factor = STARTING_FACTOR c.reps = 3 @@ -311,7 +311,7 @@ def test_reviews(): d.reset() d.sched._cardConf(c)["lapse"]["delays"] = [2, 20] d.sched.answerCard(c, 1) - assert c.queue == 1 + assert c.queue == QUEUE_TYPE_LRN # it should be due tomorrow, with an interval of 1 assert c.odue == d.sched.today + 1 assert c.ivl == 1 @@ -333,7 +333,7 @@ def test_reviews(): c = copy.copy(cardcopy) c.flush() d.sched.answerCard(c, 2) - assert c.queue == 2 + assert c.queue == QUEUE_TYPE_REV # the new interval should be (100 + 8/4) * 1.2 = 122 assert checkRevIvl(d, c, 122) assert c.due == d.sched.today + c.ivl @@ -376,9 +376,9 @@ def test_reviews(): hooks.card_did_leech.append(onLeech) d.sched.answerCard(c, 1) assert hooked - assert c.queue == -1 + assert c.queue == QUEUE_TYPE_SUSPENDED c.load() - assert c.queue == -1 + assert c.queue == QUEUE_TYPE_SUSPENDED def test_button_spacing(): @@ -388,8 +388,8 @@ def test_button_spacing(): d.addNote(f) # 1 day ivl review card due now c = f.cards()[0] - c.type = 2 - c.queue = 2 + c.type = CARD_TYPE_REV + c.queue = QUEUE_TYPE_REV c.due = d.sched.today c.reps = 1 c.ivl = 1 @@ -412,7 +412,7 @@ def test_overdue_lapse(): d.addNote(f) # simulate a review that was lapsed and is now due for its normal review c = f.cards()[0] - c.type = 2 + c.type = CARD_TYPE_REV c.queue = 1 c.due = -1 c.odue = -1 @@ -492,7 +492,7 @@ def test_nextIvl(): assert ni(c, 3) == 4 * 86400 # lapsed cards ################################################## - c.type = 2 + c.type = CARD_TYPE_REV c.ivl = 100 c.factor = STARTING_FACTOR assert ni(c, 1) == 60 @@ -500,7 +500,7 @@ def test_nextIvl(): assert ni(c, 3) == 100 * 86400 # review cards ################################################## - c.queue = 2 + c.queue = QUEUE_TYPE_REV c.ivl = 100 c.factor = STARTING_FACTOR # failing it should put it at 60s @@ -551,20 +551,20 @@ def test_suspend(): # should cope with rev cards being relearnt c.due = 0 c.ivl = 100 - c.type = 2 - c.queue = 2 + c.type = CARD_TYPE_REV + c.queue = QUEUE_TYPE_REV c.flush() d.reset() c = d.sched.getCard() d.sched.answerCard(c, 1) assert c.due >= time.time() - assert c.queue == 1 - assert c.type == 2 + assert c.queue == QUEUE_TYPE_LRN + assert c.type == CARD_TYPE_REV d.sched.suspendCards([c.id]) d.sched.unsuspendCards([c.id]) c.load() - assert c.queue == 2 - assert c.type == 2 + assert c.queue == QUEUE_TYPE_REV + assert c.type == CARD_TYPE_REV assert c.due == 1 # should cope with cards in cram decks c.due = 1 @@ -587,7 +587,8 @@ def test_cram(): d.addNote(f) c = f.cards()[0] c.ivl = 100 - c.type = c.queue = 2 + c.queue = CARD_TYPE_REV + c.type = QUEUE_TYPE_REV # due in 25 days, so it's been waiting 75 days c.due = d.sched.today + 25 c.mod = 1 @@ -622,7 +623,7 @@ def test_cram(): # int(75*1.85) = 138 assert c.ivl == 138 assert c.odue == 138 - assert c.queue == 1 + assert c.queue == QUEUE_TYPE_LRN # should be logged as a cram rep assert d.db.scalar("select type from revlog order by id desc limit 1") == 3 # check ivls again @@ -634,7 +635,7 @@ def test_cram(): d.sched.answerCard(c, 2) assert c.ivl == 138 assert c.due == 138 - assert c.queue == 2 + assert c.queue == QUEUE_TYPE_REV # and it will have moved back to the previous deck assert c.did == 1 # cram the deck again @@ -702,12 +703,12 @@ def test_cram_rem(): c = d.sched.getCard() d.sched.answerCard(c, 2) # answering the card will put it in the learning queue - assert c.type == c.queue == 1 + assert c.type == CARD_TYPE_LRN and c.queue == QUEUE_TYPE_LRN assert c.due != oldDue # if we terminate cramming prematurely it should be set back to new d.sched.emptyDyn(did) c.load() - assert c.type == c.queue == 0 + assert c.type == CARD_TYPE_NEW and c.queue == QUEUE_TYPE_NEW assert c.due == oldDue @@ -731,10 +732,11 @@ def test_cram_resched(): assert ni(c, 3) == 0 assert d.sched.nextIvlStr(c, 3) == "(end)" d.sched.answerCard(c, 3) - assert c.queue == c.type == 0 + assert c.type == CARD_TYPE_NEW and c.queue == QUEUE_TYPE_NEW # undue reviews should also be unaffected c.ivl = 100 - c.type = c.queue = 2 + c.queue = CARD_TYPE_REV + c.type = QUEUE_TYPE_REV c.due = d.sched.today + 25 c.factor = STARTING_FACTOR c.flush() @@ -911,8 +913,8 @@ def test_repCounts(): f["Front"] = "three" d.addNote(f) c = f.cards()[0] - c.type = 2 - c.queue = 2 + c.type = CARD_TYPE_REV + c.queue = QUEUE_TYPE_REV c.due = d.sched.today c.flush() d.reset() @@ -929,8 +931,8 @@ def test_timing(): f["Front"] = "num" + str(i) d.addNote(f) c = f.cards()[0] - c.type = 2 - c.queue = 2 + c.type = CARD_TYPE_REV + c.queue = QUEUE_TYPE_REV c.due = 0 c.flush() # fail the first one @@ -941,7 +943,7 @@ def test_timing(): d.sched.answerCard(c, 1) # the next card should be another review c = d.sched.getCard() - assert c.queue == 2 + assert c.queue == QUEUE_TYPE_REV # but if we wait for a second, the failed card should come back orig_time = time.time @@ -950,7 +952,7 @@ def test_timing(): time.time = adjusted_time c = d.sched.getCard() - assert c.queue == 1 + assert c.queue == QUEUE_TYPE_LRN time.time = orig_time @@ -982,7 +984,7 @@ def test_deckDue(): d.addNote(f) # make it a review card c = f.cards()[0] - c.queue = 2 + c.queue = QUEUE_TYPE_REV c.due = 0 c.flush() # add one more with a new deck @@ -1100,8 +1102,8 @@ def test_forget(): f["Front"] = "one" d.addNote(f) c = f.cards()[0] - c.queue = 2 - c.type = 2 + c.queue = QUEUE_TYPE_REV + c.type = CARD_TYPE_REV c.ivl = 100 c.due = 0 c.flush() @@ -1122,7 +1124,7 @@ def test_resched(): c.load() assert c.due == d.sched.today assert c.ivl == 1 - assert c.queue == c.type == 2 + assert c.queue == CARD_TYPE_REV and c.type == QUEUE_TYPE_REV d.sched.reschedCards([c.id], 1, 1) c.load() assert c.due == d.sched.today + 1 @@ -1136,8 +1138,8 @@ def test_norelearn(): f["Front"] = "one" d.addNote(f) c = f.cards()[0] - c.type = 2 - c.queue = 2 + c.type = CARD_TYPE_REV + c.queue = QUEUE_TYPE_REV c.due = 0 c.factor = STARTING_FACTOR c.reps = 3 @@ -1158,8 +1160,8 @@ def test_failmult(): f["Back"] = "two" d.addNote(f) c = f.cards()[0] - c.type = 2 - c.queue = 2 + c.type = CARD_TYPE_REV + c.queue = QUEUE_TYPE_REV c.ivl = 100 c.due = d.sched.today - c.ivl c.factor = STARTING_FACTOR diff --git a/pylib/tests/test_schedv2.py b/pylib/tests/test_schedv2.py index 2f3865ff5..cbee96c2a 100644 --- a/pylib/tests/test_schedv2.py +++ b/pylib/tests/test_schedv2.py @@ -4,7 +4,7 @@ import copy import time from anki import hooks -from anki.consts import STARTING_FACTOR +from anki.consts import * from anki.utils import intTime from tests.shared import getEmptyCol as getEmptyColOrig @@ -57,13 +57,13 @@ def test_new(): # fetch it c = d.sched.getCard() assert c - assert c.queue == 0 - assert c.type == 0 + assert c.queue == QUEUE_TYPE_NEW + assert c.type == CARD_TYPE_NEW # if we answer it, it should become a learn card t = intTime() d.sched.answerCard(c, 1) - assert c.queue == 1 - assert c.type == 1 + assert c.queue == QUEUE_TYPE_LRN + assert c.type == CARD_TYPE_LRN assert c.due >= t # disabled for now, as the learn fudging makes this randomly fail @@ -176,11 +176,11 @@ def test_learn(): 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 + assert c.queue == QUEUE_TYPE_LRN + assert c.type == CARD_TYPE_LRN d.sched.answerCard(c, 3) - assert c.queue == 2 - assert c.type == 2 + assert c.queue == QUEUE_TYPE_REV + assert c.type == CARD_TYPE_REV # should be due tomorrow, with an interval of 1 assert c.due == d.sched.today + 1 assert c.ivl == 1 @@ -188,8 +188,8 @@ def test_learn(): c.type = 0 c.queue = 1 d.sched.answerCard(c, 4) - assert c.type == 2 - assert c.queue == 2 + assert c.type == CARD_TYPE_REV + assert c.queue == QUEUE_TYPE_REV assert checkRevIvl(d, c, 4) # revlog should have been updated each time assert d.db.scalar("select count() from revlog where type = 0") == 5 @@ -203,20 +203,21 @@ def test_relearn(): c = f.cards()[0] c.ivl = 100 c.due = d.sched.today - c.type = c.queue = 2 + c.queue = CARD_TYPE_REV + c.type = QUEUE_TYPE_REV c.flush() # fail the card d.reset() c = d.sched.getCard() d.sched.answerCard(c, 1) - assert c.queue == 1 - assert c.type == 3 + assert c.queue == QUEUE_TYPE_LRN + assert c.type == CARD_TYPE_RELEARNING assert c.ivl == 1 # immediately graduate it d.sched.answerCard(c, 4) - assert c.queue == c.type == 2 + assert c.queue == CARD_TYPE_REV and c.type == QUEUE_TYPE_REV assert c.ivl == 2 assert c.due == d.sched.today + c.ivl @@ -229,7 +230,8 @@ def test_relearn_no_steps(): c = f.cards()[0] c.ivl = 100 c.due = d.sched.today - c.type = c.queue = 2 + c.queue = CARD_TYPE_REV + c.type = QUEUE_TYPE_REV c.flush() conf = d.decks.confForDid(1) @@ -240,7 +242,7 @@ def test_relearn_no_steps(): d.reset() c = d.sched.getCard() d.sched.answerCard(c, 1) - assert c.type == c.queue == 2 + assert c.queue == CARD_TYPE_REV and c.type == QUEUE_TYPE_REV def test_learn_collapsed(): @@ -291,7 +293,7 @@ def test_learn_day(): # answering it will place it in queue 3 d.sched.answerCard(c, 3) assert c.due == d.sched.today + 1 - assert c.queue == 3 + assert c.queue == QUEUE_TYPE_DAY_LEARN_RELEARN assert not d.sched.getCard() # for testing, move it back a day c.due -= 1 @@ -303,7 +305,7 @@ def test_learn_day(): assert ni(c, 3) == 86400 * 2 # if we fail it, it should be back in the correct queue d.sched.answerCard(c, 1) - assert c.queue == 1 + assert c.queue == QUEUE_TYPE_LRN d.undo() d.reset() c = d.sched.getCard() @@ -315,7 +317,7 @@ def test_learn_day(): # the last pass should graduate it into a review card assert ni(c, 3) == 86400 d.sched.answerCard(c, 3) - assert c.queue == c.type == 2 + assert c.queue == CARD_TYPE_REV and c.type == QUEUE_TYPE_REV # if the lapse step is tomorrow, failing it should handle the counts # correctly c.due = 0 @@ -325,7 +327,7 @@ def test_learn_day(): d.sched._cardConf(c)["lapse"]["delays"] = [1440] c = d.sched.getCard() d.sched.answerCard(c, 1) - assert c.queue == 3 + assert c.queue == QUEUE_TYPE_DAY_LEARN_RELEARN assert d.sched.counts() == (0, 0, 0) @@ -338,8 +340,8 @@ def test_reviews(): d.addNote(f) # set the card up as a review card, due 8 days ago c = f.cards()[0] - c.type = 2 - c.queue = 2 + c.type = CARD_TYPE_REV + c.queue = QUEUE_TYPE_REV c.due = d.sched.today - 8 c.factor = STARTING_FACTOR c.reps = 3 @@ -355,7 +357,7 @@ def test_reviews(): c.flush() d.reset() d.sched.answerCard(c, 2) - assert c.queue == 2 + assert c.queue == QUEUE_TYPE_REV # the new interval should be (100) * 1.2 = 120 assert checkRevIvl(d, c, 120) assert c.due == d.sched.today + c.ivl @@ -398,9 +400,9 @@ def test_reviews(): hooks.card_did_leech.append(onLeech) d.sched.answerCard(c, 1) assert hooked - assert c.queue == -1 + assert c.queue == QUEUE_TYPE_SUSPENDED c.load() - assert c.queue == -1 + assert c.queue == QUEUE_TYPE_SUSPENDED def test_review_limits(): @@ -432,7 +434,8 @@ def test_review_limits(): # make them reviews c = f.cards()[0] - c.queue = c.type = 2 + c.queue = CARD_TYPE_REV + c.type = QUEUE_TYPE_REV c.due = 0 c.flush() @@ -474,8 +477,8 @@ def test_button_spacing(): d.addNote(f) # 1 day ivl review card due now c = f.cards()[0] - c.type = 2 - c.queue = 2 + c.type = CARD_TYPE_REV + c.queue = QUEUE_TYPE_REV c.due = d.sched.today c.reps = 1 c.ivl = 1 @@ -503,7 +506,7 @@ def test_overdue_lapse(): d.addNote(f) # simulate a review that was lapsed and is now due for its normal review c = f.cards()[0] - c.type = 2 + c.type = CARD_TYPE_REV c.queue = 1 c.due = -1 c.odue = -1 @@ -586,7 +589,7 @@ def test_nextIvl(): assert ni(c, 4) == 4 * 86400 # lapsed cards ################################################## - c.type = 2 + c.type = CARD_TYPE_REV c.ivl = 100 c.factor = STARTING_FACTOR assert ni(c, 1) == 60 @@ -594,7 +597,7 @@ def test_nextIvl(): assert ni(c, 4) == 101 * 86400 # review cards ################################################## - c.queue = 2 + c.queue = QUEUE_TYPE_REV c.ivl = 100 c.factor = STARTING_FACTOR # failing it should put it at 60s @@ -624,25 +627,25 @@ def test_bury(): # burying d.sched.buryCards([c.id], manual=True) # pylint: disable=unexpected-keyword-arg c.load() - assert c.queue == -3 + assert c.queue == QUEUE_TYPE_MANUALLY_BURIED d.sched.buryCards([c2.id], manual=False) # pylint: disable=unexpected-keyword-arg c2.load() - assert c2.queue == -2 + assert c2.queue == QUEUE_TYPE_SIBLING_BURIED d.reset() assert not d.sched.getCard() d.sched.unburyCardsForDeck(type="manual") # pylint: disable=unexpected-keyword-arg c.load() - assert c.queue == 0 + assert c.queue == QUEUE_TYPE_NEW c2.load() - assert c2.queue == -2 + assert c2.queue == QUEUE_TYPE_SIBLING_BURIED d.sched.unburyCardsForDeck( # pylint: disable=unexpected-keyword-arg type="siblings" ) c2.load() - assert c2.queue == 0 + assert c2.queue == QUEUE_TYPE_NEW d.sched.buryCards([c.id, c2.id]) d.sched.unburyCardsForDeck(type="all") # pylint: disable=unexpected-keyword-arg @@ -671,21 +674,21 @@ def test_suspend(): # should cope with rev cards being relearnt c.due = 0 c.ivl = 100 - c.type = 2 - c.queue = 2 + c.type = CARD_TYPE_REV + c.queue = QUEUE_TYPE_REV c.flush() d.reset() c = d.sched.getCard() d.sched.answerCard(c, 1) assert c.due >= time.time() due = c.due - assert c.queue == 1 - assert c.type == 3 + assert c.queue == QUEUE_TYPE_LRN + assert c.type == CARD_TYPE_RELEARNING d.sched.suspendCards([c.id]) d.sched.unsuspendCards([c.id]) c.load() - assert c.queue == 1 - assert c.type == 3 + assert c.queue == QUEUE_TYPE_LRN + assert c.type == CARD_TYPE_RELEARNING assert c.due == due # should cope with cards in cram decks c.due = 1 @@ -709,7 +712,8 @@ def test_filt_reviewing_early_normal(): d.addNote(f) c = f.cards()[0] c.ivl = 100 - c.type = c.queue = 2 + c.queue = CARD_TYPE_REV + c.type = QUEUE_TYPE_REV # due in 25 days, so it's been waiting 75 days c.due = d.sched.today + 25 c.mod = 1 @@ -740,7 +744,7 @@ def test_filt_reviewing_early_normal(): assert c.due == d.sched.today + c.ivl assert not c.odue # should not be in learning - assert c.queue == 2 + assert c.queue == QUEUE_TYPE_REV # should be logged as a cram rep assert d.db.scalar("select type from revlog order by id desc limit 1") == 3 @@ -771,11 +775,11 @@ def test_filt_keep_lrn_state(): d.sched.answerCard(c, 1) - assert c.type == c.queue == 1 + assert c.type == CARD_TYPE_LRN and c.queue == QUEUE_TYPE_LRN assert c.left == 3003 d.sched.answerCard(c, 3) - assert c.type == c.queue == 1 + assert c.type == CARD_TYPE_LRN and c.queue == QUEUE_TYPE_LRN # create a dynamic deck and refresh it did = d.decks.newDyn("Cram") @@ -784,7 +788,7 @@ def test_filt_keep_lrn_state(): # card should still be in learning state c.load() - assert c.type == c.queue == 1 + assert c.type == CARD_TYPE_LRN and c.queue == QUEUE_TYPE_LRN assert c.left == 2002 # should be able to advance learning steps @@ -795,7 +799,7 @@ def test_filt_keep_lrn_state(): # emptying the deck preserves learning state d.sched.emptyDyn(did) c.load() - assert c.type == c.queue == 1 + assert c.type == CARD_TYPE_LRN and c.queue == QUEUE_TYPE_LRN assert c.left == 1001 assert c.due - intTime() > 60 * 60 @@ -833,9 +837,9 @@ def test_preview(): # passing it will remove it d.sched.answerCard(c2, 2) - assert c2.queue == 0 + assert c2.queue == QUEUE_TYPE_NEW assert c2.reps == 0 - assert c2.type == 0 + assert c2.type == CARD_TYPE_NEW # the other card should appear again c = d.sched.getCard() @@ -844,9 +848,9 @@ def test_preview(): # emptying the filtered deck should restore card d.sched.emptyDyn(did) c.load() - assert c.queue == 0 + assert c.queue == QUEUE_TYPE_NEW assert c.reps == 0 - assert c.type == 0 + assert c.type == CARD_TYPE_NEW def test_ordcycle(): @@ -943,8 +947,8 @@ def test_repCounts(): f["Front"] = "three" d.addNote(f) c = f.cards()[0] - c.type = 2 - c.queue = 2 + c.type = CARD_TYPE_REV + c.queue = QUEUE_TYPE_REV c.due = d.sched.today c.flush() d.reset() @@ -961,8 +965,8 @@ def test_timing(): f["Front"] = "num" + str(i) d.addNote(f) c = f.cards()[0] - c.type = 2 - c.queue = 2 + c.type = CARD_TYPE_REV + c.queue = QUEUE_TYPE_REV c.due = 0 c.flush() # fail the first one @@ -971,13 +975,13 @@ def test_timing(): d.sched.answerCard(c, 1) # the next card should be another review c2 = d.sched.getCard() - assert c2.queue == 2 + assert c2.queue == QUEUE_TYPE_REV # if the failed card becomes due, it should show first c.due = time.time() - 1 c.flush() d.reset() c = d.sched.getCard() - assert c.queue == 1 + assert c.queue == QUEUE_TYPE_LRN def test_collapse(): @@ -1008,7 +1012,7 @@ def test_deckDue(): d.addNote(f) # make it a review card c = f.cards()[0] - c.queue = 2 + c.queue = QUEUE_TYPE_REV c.due = 0 c.flush() # add one more with a new deck @@ -1126,8 +1130,8 @@ def test_forget(): f["Front"] = "one" d.addNote(f) c = f.cards()[0] - c.queue = 2 - c.type = 2 + c.queue = QUEUE_TYPE_REV + c.type = CARD_TYPE_REV c.ivl = 100 c.due = 0 c.flush() @@ -1148,7 +1152,7 @@ def test_resched(): c.load() assert c.due == d.sched.today assert c.ivl == 1 - assert c.queue == c.type == 2 + assert c.queue == QUEUE_TYPE_REV and c.type == CARD_TYPE_REV d.sched.reschedCards([c.id], 1, 1) c.load() assert c.due == d.sched.today + 1 @@ -1162,8 +1166,8 @@ def test_norelearn(): f["Front"] = "one" d.addNote(f) c = f.cards()[0] - c.type = 2 - c.queue = 2 + c.type = CARD_TYPE_REV + c.queue = QUEUE_TYPE_REV c.due = 0 c.factor = STARTING_FACTOR c.reps = 3 @@ -1184,8 +1188,8 @@ def test_failmult(): f["Back"] = "two" d.addNote(f) c = f.cards()[0] - c.type = 2 - c.queue = 2 + c.type = CARD_TYPE_REV + c.queue = QUEUE_TYPE_REV c.ivl = 100 c.due = d.sched.today - c.ivl c.factor = STARTING_FACTOR @@ -1217,8 +1221,8 @@ def test_moveVersions(): # the move to v2 should reset it to new col.changeSchedulerVer(2) c.load() - assert c.queue == 0 - assert c.type == 0 + assert c.queue == QUEUE_TYPE_NEW + assert c.type == CARD_TYPE_NEW # fail it again, and manually bury it col.reset() @@ -1226,19 +1230,19 @@ def test_moveVersions(): col.sched.answerCard(c, 1) col.sched.buryCards([c.id]) c.load() - assert c.queue == -3 + assert c.queue == QUEUE_TYPE_MANUALLY_BURIED # revert to version 1 col.changeSchedulerVer(1) # card should have moved queues c.load() - assert c.queue == -2 + assert c.queue == QUEUE_TYPE_SIBLING_BURIED # and it should be new again when unburied col.sched.unburyCards() c.load() - assert c.queue == c.type == 0 + assert c.type == CARD_TYPE_NEW and c.queue == QUEUE_TYPE_NEW # make sure relearning cards transition correctly to v1 col.changeSchedulerVer(2) @@ -1269,7 +1273,7 @@ def test_negativeDueFilter(): d.addNote(f) c = f.cards()[0] c.due = -5 - c.queue = 2 + c.queue = QUEUE_TYPE_REV c.ivl = 5 c.flush() diff --git a/pylib/tests/test_undo.py b/pylib/tests/test_undo.py index d37a8baa6..89e0fac0e 100644 --- a/pylib/tests/test_undo.py +++ b/pylib/tests/test_undo.py @@ -55,18 +55,18 @@ def test_review(): # answer assert d.sched.counts() == (1, 0, 0) c = d.sched.getCard() - assert c.queue == 0 + assert c.queue == QUEUE_TYPE_NEW d.sched.answerCard(c, 3) assert c.left == 1001 assert d.sched.counts() == (0, 1, 0) - assert c.queue == 1 + assert c.queue == QUEUE_TYPE_LRN # undo assert d.undoName() d.undo() d.reset() assert d.sched.counts() == (1, 0, 0) c.load() - assert c.queue == 0 + assert c.queue == QUEUE_TYPE_NEW assert c.left != 1001 assert not d.undoName() # we should be able to undo multiple answers too diff --git a/qt/aqt/browser.py b/qt/aqt/browser.py index 4f2c09dba..b46d723ec 100644 --- a/qt/aqt/browser.py +++ b/qt/aqt/browser.py @@ -350,11 +350,13 @@ class DataModel(QAbstractTableModel): def nextDue(self, c, index): if c.odid: return _("(filtered)") - elif c.queue == 1: + elif c.queue == QUEUE_TYPE_LRN: date = c.due - elif c.queue == 0 or c.type == 0: + elif c.queue == QUEUE_TYPE_NEW or c.type == CARD_TYPE_NEW: return str(c.due) - elif c.queue in (2, 3) or (c.type == 2 and c.queue < 0): + elif c.queue in (QUEUE_TYPE_REV, QUEUE_TYPE_DAY_LEARN_RELEARN) or ( + c.type == CARD_TYPE_REV and c.queue < 0 + ): date = time.time() + ((c.due - self.col.sched.today) * 86400) else: return "" @@ -1446,9 +1448,9 @@ border: 1px solid #000; padding: 3px; '>%s""" import anki.stats as st fmt = "%s" - if type == 0: + if type == CARD_TYPE_NEW: tstr = fmt % (st.colLearn, tstr) - elif type == 1: + elif type == CARD_TYPE_LRN: tstr = fmt % (st.colMature, tstr) elif type == 2: tstr = fmt % (st.colRelearn, tstr) @@ -1965,7 +1967,8 @@ update cards set usn=?, mod=?, did=? where id in """ def _reposition(self): cids = self.selectedCards() cids2 = self.col.db.list( - "select id from cards where type = 0 and id in " + ids2str(cids) + f"select id from cards where type = {CARD_TYPE_NEW} and id in " + + ids2str(cids) ) if not cids2: return showInfo(_("Only new cards can be repositioned.")) @@ -1974,7 +1977,7 @@ update cards set usn=?, mod=?, did=? where id in """ frm = aqt.forms.reposition.Ui_Dialog() frm.setupUi(d) (pmin, pmax) = self.col.db.first( - "select min(due), max(due) from cards where type=0 and odid=0" + f"select min(due), max(due) from cards where type={CARD_TYPE_NEW} and odid=0" ) pmin = pmin or 0 pmax = pmax or 0 diff --git a/qt/aqt/customstudy.py b/qt/aqt/customstudy.py index 8416fd212..ffe8571c1 100644 --- a/qt/aqt/customstudy.py +++ b/qt/aqt/customstudy.py @@ -32,17 +32,17 @@ class CustomStudy(QDialog): f.setupUi(self) self.setWindowModality(Qt.WindowModal) self.setupSignals() - f.radio1.click() + f.radioNew.click() self.exec_() def setupSignals(self): f = self.form - f.radio1.clicked.connect(lambda: self.onRadioChange(1)) - f.radio2.clicked.connect(lambda: self.onRadioChange(2)) - f.radio3.clicked.connect(lambda: self.onRadioChange(3)) - f.radio4.clicked.connect(lambda: self.onRadioChange(4)) - f.radio5.clicked.connect(lambda: self.onRadioChange(5)) - f.radio6.clicked.connect(lambda: self.onRadioChange(6)) + f.radioNew.clicked.connect(lambda: self.onRadioChange(RADIO_NEW)) + f.radioRev.clicked.connect(lambda: self.onRadioChange(RADIO_REV)) + f.radioForgot.clicked.connect(lambda: self.onRadioChange(RADIO_FORGOT)) + f.radioAhead.clicked.connect(lambda: self.onRadioChange(RADIO_AHEAD)) + f.radioPreview.clicked.connect(lambda: self.onRadioChange(RADIO_PREVIEW)) + f.radioCram.clicked.connect(lambda: self.onRadioChange(RADIO_CRAM)) def onRadioChange(self, idx): f = self.form diff --git a/qt/designer/customstudy.ui b/qt/designer/customstudy.ui index a2638641f..e2fae9dc9 100644 --- a/qt/designer/customstudy.ui +++ b/qt/designer/customstudy.ui @@ -17,42 +17,42 @@ - + Review ahead - + Review forgotten cards - + Increase today's new card limit - + Increase today's review card limit - + Study by card state or tag - + Preview new cards @@ -163,11 +163,12 @@ - radio1 - radio2 - radio3 - radio4 - radio6 + radioNew + radioRev + radioForgot + radioAhead + radioPreview + radioCram spin buttonBox