merge deck.qconf and deck.conf

This commit is contained in:
Damien Elmes 2011-08-28 14:17:33 +09:00
parent a30836445e
commit 6a00419ebc
8 changed files with 47 additions and 57 deletions

View file

@ -18,9 +18,8 @@ from anki.errors import AnkiError
import anki.latex # sets up hook import anki.latex # sets up hook
import anki.cards, anki.facts, anki.template, anki.cram, anki.find import anki.cards, anki.facts, anki.template, anki.cram, anki.find
# Settings related to queue building. These may be loaded without the rest of defaultConf = {
# the config to check due counts faster on mobile clients. # scheduling options
defaultQconf = {
'groups': [], 'groups': [],
'newPerDay': 20, 'newPerDay': 20,
'newToday': [0, 0], # currentDay, count 'newToday': [0, 0], # currentDay, count
@ -31,10 +30,7 @@ defaultQconf = {
'collapseTime': 1200, 'collapseTime': 1200,
'repLim': 0, 'repLim': 0,
'timeLim': 600, 'timeLim': 600,
} # other config
# other options
defaultConf = {
'nextPos': 1, 'nextPos': 1,
'mediaURL': "", 'mediaURL': "",
'fontFamilies': [ 'fontFamilies': [
@ -86,15 +82,13 @@ class _Deck(object):
self.dty, self.dty,
self.syncName, self.syncName,
self.lastSync, self.lastSync,
self.qconf,
self.conf, self.conf,
models, models,
groups, groups,
gconf, gconf,
tags) = self.db.first(""" tags) = self.db.first("""
select crt, mod, scm, dty, syncName, lastSync, select crt, mod, scm, dty, syncName, lastSync,
qconf, conf, models, groups, gconf, tags from deck""") conf, models, groups, gconf, tags from deck""")
self.qconf = simplejson.loads(self.qconf)
self.conf = simplejson.loads(self.conf) self.conf = simplejson.loads(self.conf)
self.models.load(models) self.models.load(models)
self.groups.load(groups, gconf) self.groups.load(groups, gconf)
@ -105,11 +99,9 @@ qconf, conf, models, groups, gconf, tags from deck""")
self.mod = intTime() if mod is None else mod self.mod = intTime() if mod is None else mod
self.db.execute( self.db.execute(
"""update deck set """update deck set
crt=?, mod=?, scm=?, dty=?, syncName=?, lastSync=?, crt=?, mod=?, scm=?, dty=?, syncName=?, lastSync=?, conf=?""",
qconf=?, conf=?""",
self.crt, self.mod, self.scm, self.dty, self.crt, self.mod, self.scm, self.dty,
self.syncName, self.lastSync, self.syncName, self.lastSync,
simplejson.dumps(self.qconf),
simplejson.dumps(self.conf)) simplejson.dumps(self.conf))
self.models.flush() self.models.flush()
self.groups.flush() self.groups.flush()
@ -165,9 +157,11 @@ qconf=?, conf=?""",
return not self.syncingEnabled() or self.scm > self.lastSync return not self.syncingEnabled() or self.scm > self.lastSync
def setDirty(self): def setDirty(self):
"Signal there are temp. suspended cards that need cleaning up on close."
self.dty = True self.dty = True
def cleanup(self): def cleanup(self):
"Unsuspend any temporarily suspended cards."
if self.dty: if self.dty:
self.sched.onClose() self.sched.onClose()
self.dty = False self.dty = False
@ -338,7 +332,7 @@ qconf=?, conf=?""",
return card return card
def randomNew(self): def randomNew(self):
return self.qconf['newOrder'] == NEW_CARDS_RANDOM return self.conf['newOrder'] == NEW_CARDS_RANDOM
# Cards # Cards
########################################################################## ##########################################################################

View file

@ -49,7 +49,7 @@ class Scheduler(object):
# put it in the learn queue # put it in the learn queue
card.queue = 1 card.queue = 1
card.type = 1 card.type = 1
self.deck.qconf['newToday'][1] += 1 self.deck.conf['newToday'][1] += 1
if card.queue == 1: if card.queue == 1:
self._answerLrnCard(card, ease) self._answerLrnCard(card, ease)
elif card.queue == 2: elif card.queue == 2:
@ -125,11 +125,11 @@ order by time desc limit 10)""")
def allCounts(self): def allCounts(self):
"Return counts for all groups, without building queue." "Return counts for all groups, without building queue."
conf = self.deck.qconf['groups'] conf = self.deck.conf['groups']
if conf: if conf:
self.deck.qconf['groups'] = [] self.deck.conf['groups'] = []
self._resetCounts() self._resetCounts()
self.deck.qconf['groups'] = conf self.deck.conf['groups'] = conf
return self.counts() return self.counts()
def _resetCounts(self): def _resetCounts(self):
@ -232,7 +232,7 @@ from cards group by gid""", self.today):
# FIXME: need to keep track of reps for timebox and new card introduction # FIXME: need to keep track of reps for timebox and new card introduction
def _resetNewCount(self): def _resetNewCount(self):
l = self.deck.qconf l = self.deck.conf
if l['newToday'][0] != self.today: if l['newToday'][0] != self.today:
# it's a new day; reset counts # it's a new day; reset counts
l['newToday'] = [self.today, 0] l['newToday'] = [self.today, 0]
@ -261,7 +261,7 @@ queue = 0 %s order by due limit %d""" % (self._groupLimit(),
if self.newQueue: if self.newQueue:
(id, due) = self.newQueue.pop() (id, due) = self.newQueue.pop()
# move any siblings to the end? # move any siblings to the end?
if self.deck.qconf['newTodayOrder'] == NEW_TODAY_ORD: if self.deck.conf['newTodayOrder'] == NEW_TODAY_ORD:
n = len(self.newQueue) n = len(self.newQueue)
while self.newQueue and self.newQueue[-1][1] == due: while self.newQueue and self.newQueue[-1][1] == due:
self.newQueue.insert(0, self.newQueue.pop()) self.newQueue.insert(0, self.newQueue.pop())
@ -273,7 +273,7 @@ queue = 0 %s order by due limit %d""" % (self._groupLimit(),
return id return id
def _updateNewCardRatio(self): def _updateNewCardRatio(self):
if self.deck.qconf['newSpread'] == NEW_CARDS_DISTRIBUTE: if self.deck.conf['newSpread'] == NEW_CARDS_DISTRIBUTE:
if self.newCount: if self.newCount:
self.newCardModulus = ( self.newCardModulus = (
(self.newCount + self.revCount) / self.newCount) (self.newCount + self.revCount) / self.newCount)
@ -287,9 +287,9 @@ queue = 0 %s order by due limit %d""" % (self._groupLimit(),
"True if it's time to display a new card when distributing." "True if it's time to display a new card when distributing."
if not self.newCount: if not self.newCount:
return False return False
if self.deck.qconf['newSpread'] == NEW_CARDS_LAST: if self.deck.conf['newSpread'] == NEW_CARDS_LAST:
return False return False
elif self.deck.qconf['newSpread'] == NEW_CARDS_FIRST: elif self.deck.conf['newSpread'] == NEW_CARDS_FIRST:
return True return True
elif self.newCardModulus: elif self.newCardModulus:
return self.reps and self.reps % self.newCardModulus == 0 return self.reps and self.reps % self.newCardModulus == 0
@ -302,7 +302,7 @@ queue = 0 %s order by due limit %d""" % (self._groupLimit(),
select count() from (select id from cards where select count() from (select id from cards where
queue = 1 %s and due < ? limit %d)""" % ( queue = 1 %s and due < ? limit %d)""" % (
self._groupLimit(), self.reportLimit), self._groupLimit(), self.reportLimit),
intTime() + self.deck.qconf['collapseTime']) intTime() + self.deck.conf['collapseTime'])
def _resetLrn(self): def _resetLrn(self):
self.lrnQueue = self.deck.db.all(""" self.lrnQueue = self.deck.db.all("""
@ -314,7 +314,7 @@ limit %d""" % (self._groupLimit(), self.reportLimit), lim=self.dayCutoff)
if self.lrnQueue: if self.lrnQueue:
cutoff = time.time() cutoff = time.time()
if collapse: if collapse:
cutoff += self.deck.qconf['collapseTime'] cutoff += self.deck.conf['collapseTime']
if self.lrnQueue[0][0] < cutoff: if self.lrnQueue[0][0] < cutoff:
id = heappop(self.lrnQueue)[1] id = heappop(self.lrnQueue)[1]
self.lrnCount -= 1 self.lrnCount -= 1
@ -347,7 +347,7 @@ limit %d""" % (self._groupLimit(), self.reportLimit), lim=self.dayCutoff)
card.due = int(time.time() + delay) card.due = int(time.time() + delay)
heappush(self.lrnQueue, (card.due, card.id)) heappush(self.lrnQueue, (card.due, card.id))
# if it's due within the cutoff, increment count # if it's due within the cutoff, increment count
if delay <= self.deck.qconf['collapseTime']: if delay <= self.deck.conf['collapseTime']:
self.lrnCount += 1 self.lrnCount += 1
self._logLrn(card, ease, conf, leaving, type) self._logLrn(card, ease, conf, leaving, type)
@ -440,7 +440,7 @@ select id from cards where
queue = 2 %s and due <= :lim order by %s limit %d""" % ( queue = 2 %s and due <= :lim order by %s limit %d""" % (
self._groupLimit(), self._revOrder(), self.queueLimit), self._groupLimit(), self._revOrder(), self.queueLimit),
lim=self.today) lim=self.today)
if self.deck.qconf['revOrder'] == REV_CARDS_RANDOM: if self.deck.conf['revOrder'] == REV_CARDS_RANDOM:
r = random.Random() r = random.Random()
r.seed(self.today) r.seed(self.today)
r.shuffle(self.revQueue) r.shuffle(self.revQueue)
@ -461,7 +461,7 @@ queue = 2 %s and due <= :lim order by %s limit %d""" % (
def _revOrder(self): def _revOrder(self):
return ("ivl desc", return ("ivl desc",
"ivl", "ivl",
"due")[self.deck.qconf['revOrder']] "due")[self.deck.conf['revOrder']]
# Answering a review card # Answering a review card
########################################################################## ##########################################################################
@ -598,7 +598,7 @@ queue = 2 %s and due <= :lim order by %s limit %d""" % (
return self.deck.groups.conf(card.gid) return self.deck.groups.conf(card.gid)
def _groupLimit(self): def _groupLimit(self):
l = self.deck.qconf['groups'] l = self.deck.conf['groups']
if not l: if not l:
# everything # everything
return "" return ""
@ -630,7 +630,7 @@ queue = 2 %s and due <= :lim order by %s limit %d""" % (
self._nextDueMsg()) self._nextDueMsg())
def _finishedSubtitle(self): def _finishedSubtitle(self):
if self.deck.qconf['groups']: if self.deck.conf['groups']:
return _("You have finished the selected groups for now.") return _("You have finished the selected groups for now.")
else: else:
return _("You have finished the deck for now.") return _("You have finished the deck for now.")
@ -670,7 +670,7 @@ queue = 2 %s and due <= :lim order by %s limit %d""" % (
def newTomorrow(self): def newTomorrow(self):
"Number of new cards tomorrow." "Number of new cards tomorrow."
lim = self.deck.qconf['newPerDay'] lim = self.deck.conf['newPerDay']
return self.deck.db.scalar( return self.deck.db.scalar(
"select count() from (select id from cards where " "select count() from (select id from cards where "
"queue = 0 %s limit %d)" % (self._groupLimit(), lim)) "queue = 0 %s limit %d)" % (self._groupLimit(), lim))
@ -760,7 +760,7 @@ queue = 2 %s and due <= :lim order by %s limit %d""" % (
def updateDynamicIndices(self): def updateDynamicIndices(self):
# determine required columns # determine required columns
required = [] required = []
if self.deck.qconf['revOrder'] in ( if self.deck.conf['revOrder'] in (
REV_CARDS_OLD_FIRST, REV_CARDS_NEW_FIRST): REV_CARDS_OLD_FIRST, REV_CARDS_NEW_FIRST):
required.append("interval") required.append("interval")
cols = ["queue", "due", "gid"] + required cols = ["queue", "due", "gid"] + required

View file

@ -675,7 +675,7 @@ $(function () {
return "" return ""
def _revlogLimit(self): def _revlogLimit(self):
lim = self.deck.qconf['groups'] lim = self.deck.conf['groups']
if self.selective and lim: if self.selective and lim:
return ("cid in (select id from cards where gid in %s)" % return ("cid in (select id from cards where gid in %s)" %
ids2str(lim)) ids2str(lim))

View file

@ -65,7 +65,6 @@ create table if not exists deck (
dty integer not null, dty integer not null,
syncName text not null, syncName text not null,
lastSync integer not null, lastSync integer not null,
qconf text not null,
conf text not null, conf text not null,
models text not null, models text not null,
groups text not null, groups text not null,
@ -127,14 +126,13 @@ create table if not exists revlog (
); );
insert or ignore into deck insert or ignore into deck
values(1,0,0,0,%(v)s,0,'',0,'','','{}','','','{}'); values(1,0,0,0,%(v)s,0,'',0,'','{}','','','{}');
""" % ({'v':CURRENT_VERSION})) """ % ({'v':CURRENT_VERSION}))
import anki.deck import anki.deck
import anki.groups import anki.groups
if setDeckConf: if setDeckConf:
db.execute(""" db.execute("""
update deck set qconf = ?, conf = ?, groups = ?, gconf = ?""", update deck set conf = ?, groups = ?, gconf = ?""",
simplejson.dumps(anki.deck.defaultQconf),
simplejson.dumps(anki.deck.defaultConf), simplejson.dumps(anki.deck.defaultConf),
simplejson.dumps({'1': {'name': _("Default"), 'conf': 1, simplejson.dumps({'1': {'name': _("Default"), 'conf': 1,
'mod': intTime()}}), 'mod': intTime()}}),
@ -339,30 +337,29 @@ def _migrateDeckTbl(db):
db.execute(""" db.execute("""
insert or replace into deck select id, cast(created as int), :t, insert or replace into deck select id, cast(created as int), :t,
:t, 99, 0, ifnull(syncName, ""), cast(lastSync as int), :t, 99, 0, ifnull(syncName, ""), cast(lastSync as int),
"", "", "", "", "", "" from decks""", t=intTime()) "", "", "", "", "" from decks""", t=intTime())
# update selective study # update selective study
qconf = anki.deck.defaultQconf.copy() conf = anki.deck.defaultConf.copy()
# delete old selective study settings, which we can't auto-upgrade easily # delete old selective study settings, which we can't auto-upgrade easily
keys = ("newActive", "newInactive", "revActive", "revInactive") keys = ("newActive", "newInactive", "revActive", "revInactive")
for k in keys: for k in keys:
db.execute("delete from deckVars where key=:k", k=k) db.execute("delete from deckVars where key=:k", k=k)
# copy other settings, ignoring deck order as there's a new default # copy other settings, ignoring deck order as there's a new default
qconf['newSpread'] = db.scalar( conf['newSpread'] = db.scalar(
"select newCardSpacing from decks") "select newCardSpacing from decks")
qconf['newOrder'] = db.scalar( conf['newOrder'] = db.scalar(
"select newCardOrder from decks") "select newCardOrder from decks")
qconf['newPerDay'] = db.scalar( conf['newPerDay'] = db.scalar(
"select newCardsPerDay from decks") "select newCardsPerDay from decks")
# fetch remaining settings from decks table # fetch remaining settings from decks table
conf = anki.deck.defaultConf.copy()
data = {} data = {}
keys = ("sessionRepLimit", "sessionTimeLimit") keys = ("sessionRepLimit", "sessionTimeLimit")
for k in keys: for k in keys:
conf[k] = db.scalar("select %s from decks" % k) conf[k] = db.scalar("select %s from decks" % k)
# random and due options merged # random and due options merged
qconf['revOrder'] = 2 conf['revOrder'] = 2
# no reverse option anymore # no reverse option anymore
qconf['newOrder'] = min(1, qconf['newOrder']) conf['newOrder'] = min(1, conf['newOrder'])
# add any deck vars and save # add any deck vars and save
dkeys = ("hexCache", "cssCache") dkeys = ("hexCache", "cssCache")
for (k, v) in db.execute("select * from deckVars").fetchall(): for (k, v) in db.execute("select * from deckVars").fetchall():
@ -371,8 +368,7 @@ insert or replace into deck select id, cast(created as int), :t,
else: else:
conf[k] = v conf[k] = v
import anki.groups import anki.groups
db.execute("update deck set qconf = :l,conf = :c,groups=:g,gconf=:gc", db.execute("update deck set conf=:c,groups=:g,gconf=:gc",
l=simplejson.dumps(qconf),
c=simplejson.dumps(conf), c=simplejson.dumps(conf),
g=simplejson.dumps({'1': {'name': _("Default"), 'conf': 1}}), g=simplejson.dumps({'1': {'name': _("Default"), 'conf': 1}}),
gc=simplejson.dumps({'1': anki.groups.defaultConf})) gc=simplejson.dumps({'1': anki.groups.defaultConf}))

View file

@ -18,7 +18,7 @@ def test_genCards():
assert deck.cardCount() == 2 assert deck.cardCount() == 2
assert cards[0].due == f.id assert cards[0].due == f.id
# should work on random mode too # should work on random mode too
deck.qconf['newOrder'] = NEW_CARDS_RANDOM deck.conf['newOrder'] = NEW_CARDS_RANDOM
f = deck.newFact() f = deck.newFact()
f['Front'] = u'1' f['Front'] = u'1'
f['Back'] = u'2' f['Back'] = u'2'

View file

@ -143,7 +143,7 @@ def test_groups():
# should get the same id # should get the same id
assert deck.groups.id("new group") == g assert deck.groups.id("new group") == g
# by default, everything should be shown # by default, everything should be shown
assert not deck.qconf['groups'] assert not deck.conf['groups']
def test_selective(): def test_selective():
deck = getEmptyDeck() deck = getEmptyDeck()

View file

@ -65,7 +65,7 @@ def test_newOrder():
d.addFact(f) d.addFact(f)
# generate second half # generate second half
d.db.execute("update cards set gid = random()") d.db.execute("update cards set gid = random()")
d.qconf['newPerDay'] = 100 d.conf['newPerDay'] = 100
d.reset() d.reset()
# cards should be sorted by id # cards should be sorted by id
assert d.sched.newQueue == list(reversed(sorted(d.sched.newQueue))) assert d.sched.newQueue == list(reversed(sorted(d.sched.newQueue)))
@ -387,7 +387,7 @@ def test_cram():
c.startTimer() c.startTimer()
c.flush() c.flush()
cardcopy = copy.copy(c) cardcopy = copy.copy(c)
d.qconf['groups'] = [1] d.conf['groups'] = [1]
d.cramGroups() d.cramGroups()
# first, test with initial intervals preserved # first, test with initial intervals preserved
conf = d.sched._lrnConf(c) conf = d.sched._lrnConf(c)
@ -449,7 +449,7 @@ def test_cram():
assert c.ivl == 1 assert c.ivl == 1
assert c.due == d.sched.today + 1 assert c.due == d.sched.today + 1
# users should be able to cram entire deck too # users should be able to cram entire deck too
d.qconf['groups'] = [] d.conf['groups'] = []
d.cramGroups() d.cramGroups()
assert d.sched.counts()[0] > 0 assert d.sched.counts()[0] > 0
@ -465,7 +465,7 @@ def test_cramLimits():
c.due = d.sched.today + 1 + i c.due = d.sched.today + 1 + i
c.flush() c.flush()
# the default cram should return all three # the default cram should return all three
d.qconf['groups'] = [1] d.conf['groups'] = [1]
d.cramGroups() d.cramGroups()
assert d.sched.counts()[0] == 3 assert d.sched.counts()[0] == 3
# if we start from the day after tomorrow, it should be 2 # if we start from the day after tomorrow, it should be 2
@ -601,7 +601,7 @@ def test_counts():
# with the default settings, there's no count limit # with the default settings, there's no count limit
assert d.sched.counts() == (2,2,2) assert d.sched.counts() == (2,2,2)
# check limit to one group # check limit to one group
d.qconf['groups'] = [1] d.conf['groups'] = [1]
d.reset() d.reset()
assert d.sched.counts() == (1,1,1) assert d.sched.counts() == (1,1,1)
# we don't need to build the queue to get the counts # we don't need to build the queue to get the counts

View file

@ -7,9 +7,9 @@ def test_op():
# should have no undo by default # should have no undo by default
assert not d.undoName() assert not d.undoName()
# let's adjust a study option # let's adjust a study option
assert d.qconf['repLim'] == 0 assert d.conf['repLim'] == 0
d.save("studyopts") d.save("studyopts")
d.qconf['repLim'] = 10 d.conf['repLim'] = 10
# it should be listed as undoable # it should be listed as undoable
assert d.undoName() == "studyopts" assert d.undoName() == "studyopts"
# with about 5 minutes until it's clobbered # with about 5 minutes until it's clobbered
@ -17,7 +17,7 @@ def test_op():
# undoing should restore the old value # undoing should restore the old value
d.undo() d.undo()
assert not d.undoName() assert not d.undoName()
assert d.qconf['repLim'] == 0 assert d.conf['repLim'] == 0
# an (auto)save will clear the undo # an (auto)save will clear the undo
d.save("foo") d.save("foo")
assert d.undoName() == "foo" assert d.undoName() == "foo"