top level groups

As discussed on the forums, moving to a single collection requires moving some
deck-level configuration into groups so users can have different settings like
new cards/day for each top level item.

Also:
- store id in groups
- add mod time to gconf updates
- move the limiting code that's not specific to scheduling into groups.py
- store the current model id per top level group
This commit is contained in:
Damien Elmes 2011-09-07 01:31:46 +09:00
parent 5179d82f7f
commit de8a5b69ed
11 changed files with 175 additions and 104 deletions

View file

@ -20,19 +20,12 @@ import anki.cards, anki.facts, anki.template, anki.cram, anki.find
defaultConf = { defaultConf = {
# scheduling options # scheduling options
'groups': [], 'activeGroups': [],
'newPerDay': 20, 'topGroup': 1,
'newToday': [0, 0], # currentDay, count 'curGroup': None,
'newTodayOrder': NEW_TODAY_ORD,
'newOrder': NEW_CARDS_DUE,
'newSpread': NEW_CARDS_DISTRIBUTE,
'revOrder': REV_CARDS_RANDOM, 'revOrder': REV_CARDS_RANDOM,
'collapseTime': 1200,
'repLim': 0,
'timeLim': 600,
# other config # other config
'nextPos': 1, 'nextPos': 1,
'mediaURL': "",
'fontFamilies': [ 'fontFamilies': [
[u' 明朝',u'ヒラギノ明朝 Pro W3',u'Kochi Mincho', u'東風明朝'] [u' 明朝',u'ヒラギノ明朝 Pro W3',u'Kochi Mincho', u'東風明朝']
], ],
@ -228,7 +221,7 @@ crt=?, mod=?, scm=?, dty=?, syncName=?, lastSync=?, conf=?""",
return 0 return 0
fact.flush() fact.flush()
# randomize? # randomize?
if self.randomNew(): if self.models.randomNew():
due = self._randPos() due = self._randPos()
else: else:
due = self.nextID("pos") due = self.nextID("pos")
@ -284,7 +277,7 @@ crt=?, mod=?, scm=?, dty=?, syncName=?, lastSync=?, conf=?""",
"Generate cards for templates if cards not empty. Return cards." "Generate cards for templates if cards not empty. Return cards."
cards = [] cards = []
# if random mode, determine insertion point # if random mode, determine insertion point
if self.randomNew(): if self.models.randomNew():
# if this fact has existing new cards, use their due time # if this fact has existing new cards, use their due time
due = self.db.scalar( due = self.db.scalar(
"select due from cards where fid = ? and queue = 0", fact.id) "select due from cards where fid = ? and queue = 0", fact.id)
@ -331,9 +324,6 @@ crt=?, mod=?, scm=?, dty=?, syncName=?, lastSync=?, conf=?""",
card.flush() card.flush()
return card return card
def randomNew(self):
return self.conf['newOrder'] == NEW_CARDS_RANDOM
# Cards # Cards
########################################################################## ##########################################################################

View file

@ -4,7 +4,27 @@
import simplejson import simplejson
from anki.utils import intTime from anki.utils import intTime
from anki.consts import *
# fixmes:
# - make sure lists like new[delays] are not being shared by multiple groups
# - make sure all children have parents (create as necessary)
# - when renaming a group, top level properties should be added or removed as
# appropriate
# configuration only available to top level groups
defaultTopConf = {
'newPerDay': 20,
'newToday': [0, 0], # currentDay, count
'newTodayOrder': NEW_TODAY_ORD,
'newSpread': NEW_CARDS_DISTRIBUTE,
'collapseTime': 1200,
'repLim': 0,
'timeLim': 600,
'curModel': None,
}
# configuration available to all groups
defaultConf = { defaultConf = {
'new': { 'new': {
'delays': [1, 10], 'delays': [1, 10],
@ -33,11 +53,7 @@ defaultConf = {
'minSpace': 1, 'minSpace': 1,
}, },
'maxTaken': 60, 'maxTaken': 60,
} 'mod': 0,
defaultData = {
'activeTags': None,
'inactiveTags': None,
} }
class GroupManager(object): class GroupManager(object):
@ -54,6 +70,7 @@ class GroupManager(object):
self.changed = False self.changed = False
def save(self, g): def save(self, g):
"Can be called with either a group or a group configuration."
g['mod'] = intTime() g['mod'] = intTime()
self.changed = True self.changed = True
@ -73,12 +90,20 @@ class GroupManager(object):
return int(id) return int(id)
if not create: if not create:
return None return None
g = dict(name=name, conf=1, mod=intTime()) if "::" not in name:
# if it's a top level group, it gets the top level config
g = defaultTopConf.copy()
else:
# not top level. calling code should ensure parents already exist?
g = {}
g['name'] = name
g['conf'] = 1
while 1: while 1:
id = str(intTime(1000)) id = intTime(1000)
if id in self.groups: if str(id) in self.groups:
continue continue
self.groups[id] = g g['id'] = id
self.groups[str(id)] = g
self.save(g) self.save(g)
return int(id) return int(id)
@ -89,11 +114,15 @@ class GroupManager(object):
self.deck.db.execute("delete from groups where id = ?", gid) self.deck.db.execute("delete from groups where id = ?", gid)
print "fixme: loop through models and update stale gid references" print "fixme: loop through models and update stale gid references"
def all(self): def allNames(self):
"An unsorted list of all group names." "An unsorted list of all group names."
return [x['name'] for x in self.groups.values()] return [x['name'] for x in self.groups.values()]
# Utils def all(self):
"A list of all groups."
return self.groups.values()
# Group utils
############################################################# #############################################################
def name(self, gid): def name(self, gid):
@ -102,6 +131,46 @@ class GroupManager(object):
def conf(self, gid): def conf(self, gid):
return self.gconf[str(self.groups[str(gid)]['conf'])] return self.gconf[str(self.groups[str(gid)]['conf'])]
def get(self, gid):
return self.groups[str(gid)]
def setGroup(self, cids, gid): def setGroup(self, cids, gid):
self.db.execute( self.db.execute(
"update cards set gid = ? where id in "+ids2str(cids), gid) "update cards set gid = ? where id in "+ids2str(cids), gid)
# Group selection
#############################################################
def top(self):
"The current top level group as an object, and marks as modified."
g = self.get(self.deck.conf['topGroup'])
self.save(g)
return g
def active(self):
"The currrently active gids."
return self.deck.conf['activeGroups']
def selected(self):
"The currently selected gid, or None if whole collection."
return self.deck.conf['curGroup']
def select(self, gid):
"Select a new group. If gid is None, select whole collection."
if not gid:
self.deck.conf['topGroup'] = 1
self.deck.conf['curGroup'] = None
self.deck.conf['activeGroups'] = []
return
# save the top level group
name = self.groups[str(gid)]['name']
path = name.split("::")
self.deck.conf['topGroup'] = self.id(path[0])
# current group
self.deck.conf['curGroup'] = gid
# and active groups (current + all children)
actv = [gid]
for g in self.all():
if g['name'].startswith(name + "::"):
actv.append(g['id'])
self.deck.conf['activeGroups'] = actv

View file

@ -6,6 +6,7 @@ import simplejson, copy
from anki.utils import intTime, hexifyID, joinFields, splitFields, ids2str, \ from anki.utils import intTime, hexifyID, joinFields, splitFields, ids2str, \
timestampID timestampID
from anki.lang import _ from anki.lang import _
from anki.consts import *
# Models # Models
########################################################################## ##########################################################################
@ -16,6 +17,7 @@ defaultModel = {
'sortf': 0, 'sortf': 0,
'gid': 1, 'gid': 1,
'clozectx': False, 'clozectx': False,
'newOrder': NEW_CARDS_DUE,
'latexPre': """\ 'latexPre': """\
\\documentclass[12pt]{article} \\documentclass[12pt]{article}
\\special{papersize=3in,5in} \\special{papersize=3in,5in}
@ -87,7 +89,13 @@ class ModelManager(object):
def current(self): def current(self):
"Get current model." "Get current model."
return self.get(self.deck.conf['currentModelId']) try:
return self.get(self.deck.groups.top()['curModel'])
except:
return self.models.values()[0]
def setCurrent(self, m):
self.deck.groups.top()['curModel'] = m['id']
def get(self, id): def get(self, id):
"Get model with ID." "Get model with ID."
@ -117,6 +125,7 @@ class ModelManager(object):
def rem(self, m): def rem(self, m):
"Delete model, and all its cards/facts." "Delete model, and all its cards/facts."
self.deck.modSchema() self.deck.modSchema()
current = self.current()['id'] == m['id']
# delete facts/cards # delete facts/cards
self.deck.remCards(self.deck.db.list(""" self.deck.remCards(self.deck.db.list("""
select id from cards where fid in (select id from facts where mid = ?)""", select id from cards where fid in (select id from facts where mid = ?)""",
@ -125,14 +134,14 @@ select id from cards where fid in (select id from facts where mid = ?)""",
del self.models[m['id']] del self.models[m['id']]
self.save() self.save()
# GUI should ensure last model is not deleted # GUI should ensure last model is not deleted
if self.deck.conf['currentModelId'] == m['id']: if current:
self.deck.conf['currentModelId'] = int(self.models.keys()[0]) self.setCurrent(self.models.values()[0])
def _add(self, m): def _add(self, m):
self._setID(m) self._setID(m)
self.models[m['id']] = m self.models[m['id']] = m
self.save(m) self.save(m)
self.deck.conf['currentModelId'] = m['id'] self.setCurrent(m)
return m return m
def _setID(self, m): def _setID(self, m):
@ -155,6 +164,9 @@ select id from cards where fid in (select id from facts where mid = ?)""",
return self.deck.db.scalar( return self.deck.db.scalar(
"select count() from facts where mid = ?", m['id']) "select count() from facts where mid = ?", m['id'])
def randomNew(self):
return self.current()['newOrder'] == NEW_CARDS_RANDOM
# Copying # Copying
################################################## ##################################################

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.conf['newToday'][1] += 1 self.deck.groups.top()['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:
@ -98,20 +98,6 @@ order by due""" % self._groupLimit(),
# Counts # Counts
########################################################################## ##########################################################################
def selCounts(self):
"Return counts for selected groups, without building queue."
self._resetCounts()
return self.counts()
def allCounts(self):
"Return counts for all groups, without building queue."
conf = self.deck.conf['groups']
if conf:
self.deck.conf['groups'] = []
self._resetCounts()
self.deck.conf['groups'] = conf
return self.counts()
def _resetCounts(self): def _resetCounts(self):
self._updateCutoff() self._updateCutoff()
self._resetLrnCount() self._resetLrnCount()
@ -212,7 +198,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.conf l = self.deck.groups.top()
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]
@ -241,7 +227,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.conf['newTodayOrder'] == NEW_TODAY_ORD: if self.deck.groups.top()['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())
@ -253,7 +239,7 @@ queue = 0 %s order by due limit %d""" % (self._groupLimit(),
return id return id
def _updateNewCardRatio(self): def _updateNewCardRatio(self):
if self.deck.conf['newSpread'] == NEW_CARDS_DISTRIBUTE: if self.deck.groups.top()['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)
@ -267,9 +253,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.conf['newSpread'] == NEW_CARDS_LAST: if self.deck.groups.top()['newSpread'] == NEW_CARDS_LAST:
return False return False
elif self.deck.conf['newSpread'] == NEW_CARDS_FIRST: elif self.deck.groups.top()['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
@ -282,7 +268,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.conf['collapseTime']) intTime() + self.deck.groups.top()['collapseTime'])
def _resetLrn(self): def _resetLrn(self):
self.lrnQueue = self.deck.db.all(""" self.lrnQueue = self.deck.db.all("""
@ -294,7 +280,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.conf['collapseTime'] cutoff += self.deck.groups.top()['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
@ -327,7 +313,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.conf['collapseTime']: if delay <= self.deck.groups.top()['collapseTime']:
self.lrnCount += 1 self.lrnCount += 1
self._logLrn(card, ease, conf, leaving, type) self._logLrn(card, ease, conf, leaving, type)
@ -578,7 +564,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.conf['groups'] l = self.deck.groups.active()
if not l: if not l:
# everything # everything
return "" return ""
@ -605,16 +591,10 @@ queue = 2 %s and due <= :lim order by %s limit %d""" % (
def finishedMsg(self): def finishedMsg(self):
return ( return (
"<h1>"+_("Congratulations!")+"</h1>"+ "<h1>"+_("Congratulations!")+"</h1>"+
self._finishedSubtitle()+ _("You have finished the selected groups for now.") +
"<br><br>"+ "<br><br>"+
self._nextDueMsg()) self._nextDueMsg())
def _finishedSubtitle(self):
if self.deck.conf['groups']:
return _("You have finished the selected groups for now.")
else:
return _("You have finished the deck for now.")
def _nextDueMsg(self): def _nextDueMsg(self):
line = [] line = []
rev = self.revTomorrow() + self.lrnTomorrow() rev = self.revTomorrow() + self.lrnTomorrow()
@ -650,7 +630,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.conf['newPerDay'] lim = self.deck.groups.top()['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))
@ -764,7 +744,7 @@ queue = 2 %s and due <= :lim order by %s limit %d""" % (
self.deck.db.execute( self.deck.db.execute(
"update cards set type=0, queue=0, ivl=0 where id in "+ids2str(ids)) "update cards set type=0, queue=0, ivl=0 where id in "+ids2str(ids))
pmax = self.deck.db.scalar("select max(due) from cards where type=0") pmax = self.deck.db.scalar("select max(due) from cards where type=0")
self.sortCards(ids, start=pmax+1, shuffle=self.deck.randomNew()) self.sortCards(ids, start=pmax+1, shuffle=self.deck.models.randomNew())
def reschedCards(self, ids, imin, imax): def reschedCards(self, ids, imin, imax):
"Put cards in review queue with a new interval in days (min, max)." "Put cards in review queue with a new interval in days (min, max)."
@ -816,6 +796,8 @@ and due >= ?""" % scids, now, shiftby, low)
self.deck.db.executemany( self.deck.db.executemany(
"update cards set due = :due, mod = :now where id = :cid""", d) "update cards set due = :due, mod = :now where id = :cid""", d)
# fixme: because it's a model property now, these should be done on a
# per-model basis
def randomizeCards(self): def randomizeCards(self):
self.sortCards(self.deck.db.list("select id from cards"), shuffle=True) self.sortCards(self.deck.db.list("select id from cards"), shuffle=True)

View file

@ -675,7 +675,7 @@ $(function () {
return "" return ""
def _revlogLimit(self): def _revlogLimit(self):
lim = self.deck.conf['groups'] lim = self.deck.groups.active()
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

@ -131,11 +131,15 @@ values(1,0,0,0,%(v)s,0,'',0,'','{}','','','{}');
import anki.deck import anki.deck
import anki.groups import anki.groups
if setDeckConf: if setDeckConf:
g = anki.groups.defaultTopConf.copy()
g['id'] = 1
g['name'] = _("Default")
g['conf'] = 1
g['mod'] = intTime()
db.execute(""" db.execute("""
update deck set conf = ?, groups = ?, gconf = ?""", update deck set conf = ?, groups = ?, gconf = ?""",
simplejson.dumps(anki.deck.defaultConf), simplejson.dumps(anki.deck.defaultConf),
simplejson.dumps({'1': {'name': _("Default"), 'conf': 1, simplejson.dumps({'1': g}),
'mod': intTime()}}),
simplejson.dumps({'1': anki.groups.defaultConf})) simplejson.dumps({'1': anki.groups.defaultConf}))
def _updateIndices(db): def _updateIndices(db):
@ -339,28 +343,29 @@ def _migrateDeckTbl(db):
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 # prepare a group to store the old deck options
import anki.groups
g = anki.groups.defaultTopConf.copy()
g['id'] = 1
g['name'] = _("Default")
g['conf'] = 1
g['mod'] = intTime()
# and deck conf
conf = anki.deck.defaultConf.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
conf['newSpread'] = db.scalar( g['newSpread'] = db.scalar("select newCardSpacing from decks")
"select newCardSpacing from decks") g['newPerDay'] = db.scalar("select newCardsPerDay from decks")
conf['newOrder'] = db.scalar( g['repLim'] = db.scalar("select sessionRepLimit from decks")
"select newCardOrder from decks") g['timeLim'] = db.scalar("select sessionTimeLimit from decks")
conf['newPerDay'] = db.scalar(
"select newCardsPerDay from decks") # this needs to be placed in the model later on
# fetch remaining settings from decks table conf['oldNewOrder'] = db.scalar("select newCardOrder from decks")
data = {}
keys = ("sessionRepLimit", "sessionTimeLimit")
for k in keys:
conf[k] = db.scalar("select %s from decks" % k)
# random and due options merged
conf['revOrder'] = 2
# no reverse option anymore # no reverse option anymore
conf['newOrder'] = min(1, conf['newOrder']) conf['oldNewOrder'] = min(1, conf['oldNewOrder'])
# 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():
@ -368,10 +373,9 @@ insert or replace into deck select id, cast(created as int), :t,
pass pass
else: else:
conf[k] = v conf[k] = v
import anki.groups
db.execute("update deck set conf=:c,groups=:g,gconf=:gc", db.execute("update deck set conf=:c,groups=:g,gconf=:gc",
c=simplejson.dumps(conf), c=simplejson.dumps(conf),
g=simplejson.dumps({'1': {'name': _("Default"), 'conf': 1}}), g=simplejson.dumps({'1': g}),
gc=simplejson.dumps({'1': anki.groups.defaultConf})) gc=simplejson.dumps({'1': anki.groups.defaultConf}))
# clean up # clean up
db.execute("drop table decks") db.execute("drop table decks")
@ -479,10 +483,12 @@ def _postSchemaUpgrade(deck):
"Handle the rest of the upgrade to 2.0." "Handle the rest of the upgrade to 2.0."
import anki.deck import anki.deck
# make sure we have a current model id # make sure we have a current model id
deck.conf['currentModelId'] = deck.models.models.keys()[0] deck.models.setCurrent(deck.models.models.values()[0])
# regenerate css # regenerate css, and set new card order
for m in deck.models.all(): for m in deck.models.all():
m['newOrder'] = deck.conf['oldNewOrder']
deck.models.save(m) deck.models.save(m)
del deck.conf['oldNewOrder']
# fix creation time # fix creation time
deck.sched._updateCutoff() deck.sched._updateCutoff()
d = datetime.datetime.today() d = datetime.datetime.today()
@ -521,7 +527,7 @@ update cards set due = cast(
((due-:stamp)/86400) as int)+:today where type = 2 ((due-:stamp)/86400) as int)+:today where type = 2
""", stamp=deck.sched.dayCutoff, today=deck.sched.today) """, stamp=deck.sched.dayCutoff, today=deck.sched.today)
# possibly re-randomize # possibly re-randomize
if deck.randomNew(): if deck.models.randomNew():
deck.sched.randomizeCards() deck.sched.randomizeCards()
# update insertion id # update insertion id
deck.conf['nextPos'] = deck.db.scalar("select max(id) from facts")+1 deck.conf['nextPos'] = deck.db.scalar("select max(id) from facts")+1

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.conf['newOrder'] = NEW_CARDS_RANDOM deck.models.current()['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'
@ -74,6 +74,6 @@ def test_misc():
f['Back'] = u'2' f['Back'] = u'2'
d.addFact(f) d.addFact(f)
c = f.cards()[0] c = f.cards()[0]
id = d.conf['currentModelId'] id = d.models.current()['id']
assert c.cssClass() == "cm%s-0" % hexifyID(id) assert c.cssClass() == "cm%s-0" % hexifyID(id)
assert c.template()['ord'] == 0 assert c.template()['ord'] == 0

View file

@ -137,13 +137,30 @@ def test_groups():
# it should have an id of 1 # it should have an id of 1
assert deck.groups.name(1) assert deck.groups.name(1)
# create a new group # create a new group
g = deck.groups.id("new group") parentId = deck.groups.id("new group")
assert g assert parentId
assert len(deck.groups.groups) == 2 assert len(deck.groups.groups) == 2
# should get the same id # should get the same id
assert deck.groups.id("new group") == g assert deck.groups.id("new group") == parentId
# by default, everything should be shown # by default, everything should be shown
assert not deck.conf['groups'] assert not deck.groups.selected()
assert not deck.groups.active()
# and the default group is used
assert deck.groups.top()['id'] == 1
# we can select the default explicitly
deck.groups.select(1)
assert deck.groups.selected() == 1
assert deck.groups.active() == [1]
assert deck.groups.top()['id'] == 1
# let's create a child and select that
childId = deck.groups.id("new group::child")
deck.groups.select(childId)
assert deck.groups.selected() == childId
assert deck.groups.active() == [childId]
assert deck.groups.top()['id'] == parentId
# if we select the parent, the child gets included
deck.groups.select(parentId)
assert sorted(deck.groups.active()) == [parentId, childId]
def test_selective(): def test_selective():
deck = getEmptyDeck() deck = getEmptyDeck()

View file

@ -10,7 +10,7 @@ def test_modelDelete():
f['Back'] = u'2' f['Back'] = u'2'
deck.addFact(f) deck.addFact(f)
assert deck.cardCount() == 1 assert deck.cardCount() == 1
deck.models.rem(deck.models.get(deck.conf['currentModelId'])) deck.models.rem(deck.models.current())
assert deck.cardCount() == 0 assert deck.cardCount() == 0
def test_modelCopy(): def test_modelCopy():
@ -109,7 +109,7 @@ def test_text():
def test_cloze(): def test_cloze():
d = getEmptyDeck() d = getEmptyDeck()
d.conf['currentModelId'] = d.models.byName("Cloze")['id'] d.models.setCurrent(d.models.byName("Cloze"))
f = d.newFact() f = d.newFact()
assert f.model()['name'] == "Cloze" assert f.model()['name'] == "Cloze"
# a cloze model with no clozes is empty # a cloze model with no clozes is empty

View file

@ -581,11 +581,11 @@ def test_ordcycle():
def test_counts(): def test_counts():
d = getEmptyDeck() d = getEmptyDeck()
# add a second group # add a second group
assert d.groups.id("new group") grp = d.groups.id("new group")
# for each card type # for each card type
for type in range(3): for type in range(3):
# and each of the groups # and each of the groups
for gid in (1,2): for gid in (1,grp):
# create a new fact # create a new fact
f = d.newFact() f = d.newFact()
f['Front'] = u"one" f['Front'] = u"one"
@ -601,13 +601,9 @@ 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.conf['groups'] = [1] d.groups.select(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
assert d.sched.allCounts() == (2,2,2)
assert d.sched.selCounts() == (1,1,1)
assert d.sched.allCounts() == (2,2,2)
def test_counts2(): def test_counts2():
d = getEmptyDeck() d = getEmptyDeck()

View file

@ -7,9 +7,8 @@ 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.conf['repLim'] == 0
d.save("studyopts") d.save("studyopts")
d.conf['repLim'] = 10 d.conf['revOrder'] = 5
# 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 +16,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.conf['repLim'] == 0 assert d.conf['revOrder'] != 5
# 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"