diff --git a/anki/cards.py b/anki/cards.py
index a534fe687..5a7249e75 100644
--- a/anki/cards.py
+++ b/anki/cards.py
@@ -105,9 +105,9 @@ lapses=?, grade=?, cycles=?, edue=? where id = ?""",
def _getQA(self, reload=False):
if not self._qa or reload:
f = self.fact(); m = self.model()
- data = [self.id, f.id, m.id, self.gid, self.ord, f.stringTags(),
+ data = [self.id, f.id, m['id'], self.gid, self.ord, f.stringTags(),
f.joinedFields()]
- self._qa = self.deck._renderQA(self.model(), data)
+ self._qa = self.deck._renderQA(data)
return self._qa
def _withClass(self, txt, extra):
@@ -117,7 +117,7 @@ lapses=?, grade=?, cycles=?, edue=? where id = ?""",
"Fetch the model and fact."
if not self._rd or reload:
f = self.deck.getFact(self.fid)
- m = self.deck.getModel(f.mid)
+ m = self.deck.models.get(f.mid)
self._rd = [f, m]
return self._rd
@@ -128,10 +128,10 @@ lapses=?, grade=?, cycles=?, edue=? where id = ?""",
return self._reviewData()[1]
def template(self):
- return self._reviewData()[1].templates[self.ord]
+ return self._reviewData()[1]['tmpls'][self.ord]
def cssClass(self):
- return "cm%s-%s" % (hexifyID(self.model().id),
+ return "cm%s-%s" % (hexifyID(self.model()['id']),
hexifyID(self.template()['ord']))
def startTimer(self):
diff --git a/anki/deck.py b/anki/deck.py
index f379fa94b..9cf16a524 100644
--- a/anki/deck.py
+++ b/anki/deck.py
@@ -9,12 +9,13 @@ from anki.utils import parseTags, ids2str, hexifyID, \
splitFields
from anki.hooks import runHook, runFilter
from anki.sched import Scheduler
+from anki.models import ModelRegistry
from anki.media import MediaRegistry
from anki.consts import *
from anki.errors import AnkiError
import anki.latex # sets up hook
-import anki.cards, anki.facts, anki.models, anki.template, anki.cram, \
+import anki.cards, anki.facts, anki.template, anki.cram, \
anki.groups, anki.find
# Settings related to queue building. These may be loaded without the rest of
@@ -51,6 +52,8 @@ class _Deck(object):
self.path = db._path
self._lastSave = time.time()
self.clearUndo()
+ self.media = MediaRegistry(self)
+ self.models = ModelRegistry(self)
self.load()
if not self.crt:
d = datetime.datetime.today()
@@ -65,7 +68,6 @@ class _Deck(object):
self.lastSessionStart = 0
self._stdSched = Scheduler(self)
self.sched = self._stdSched
- self.media = MediaRegistry(self)
# check for improper shutdown
self.cleanup()
@@ -85,16 +87,16 @@ class _Deck(object):
self.lastSync,
self.qconf,
self.conf,
+ models,
self.groups,
- self.gconf,
- self.data) = self.db.first("""
+ self.gconf) = self.db.first("""
select crt, mod, scm, dty, syncName, lastSync,
-qconf, conf, groups, gconf, data from deck""")
+qconf, conf, models, groups, gconf from deck""")
self.qconf = simplejson.loads(self.qconf)
self.conf = simplejson.loads(self.conf)
self.groups = simplejson.loads(self.groups)
self.gconf = simplejson.loads(self.gconf)
- self.data = simplejson.loads(self.data)
+ self.models.load(models)
def flush(self, mod=None):
"Flush state to DB, updating mod time."
@@ -102,11 +104,14 @@ qconf, conf, groups, gconf, data from deck""")
self.db.execute(
"""update deck set
crt=?, mod=?, scm=?, dty=?, syncName=?, lastSync=?,
-qconf=?, conf=?, data=?""",
+qconf=?, conf=?, groups=?, gconf=?""",
self.crt, self.mod, self.scm, self.dty,
self.syncName, self.lastSync,
simplejson.dumps(self.qconf),
- simplejson.dumps(self.conf), simplejson.dumps(self.data))
+ simplejson.dumps(self.conf),
+ simplejson.dumps(self.groups),
+ simplejson.dumps(self.gconf))
+ self.models.flush()
def save(self, name=None, mod=None):
"Flush, commit DB, and take out another write lock."
@@ -187,15 +192,6 @@ qconf=?, conf=?, data=?""",
def getFact(self, id):
return anki.facts.Fact(self, id=id)
- def getModel(self, mid, cache=True):
- "Memoizes; call .reset() to reset cache."
- if cache and mid in self.modelCache:
- return self.modelCache[mid]
- m = anki.models.Model(self, mid)
- if cache:
- self.modelCache[mid] = m
- return m
-
# Utils
##########################################################################
@@ -227,7 +223,7 @@ qconf=?, conf=?, data=?""",
def newFact(self):
"Return a new fact with the current model."
- return anki.facts.Fact(self, self.currentModel())
+ return anki.facts.Fact(self, self.models.current())
def addFact(self, fact):
"Add a fact to the deck. Return number of new cards."
@@ -273,14 +269,14 @@ qconf=?, conf=?, data=?""",
"Return (active), non-empty templates."
ok = []
model = fact.model()
- for template in model.templates:
+ for template in model['tmpls']:
if template['actv'] or not checkActive:
# [cid, fid, mid, gid, ord, tags, flds]
- data = [1, 1, model.id, 1, template['ord'],
+ data = [1, 1, model['id'], 1, template['ord'],
"", fact.joinedFields()]
- now = self._renderQA(model, data)
+ now = self._renderQA(data)
data[6] = "\x1f".join([""]*len(fact.fields))
- empty = self._renderQA(model, data)
+ empty = self._renderQA(data)
if now['q'] == empty['q']:
continue
if not template['emptyAns']:
@@ -321,7 +317,7 @@ qconf=?, conf=?, data=?""",
elif type == 1:
cms = [c.template() for c in fact.cards()]
else:
- cms = fact.model().templates
+ cms = fact.model()['tmpls']
if not cms:
return []
cards = []
@@ -365,45 +361,6 @@ select id from facts where id in %s and id not in (select fid from cards)""" %
ids2str(fids))
self._delFacts(fids)
- # Models
- ##########################################################################
-
- def currentModel(self):
- return self.getModel(self.conf['currentModelId'])
-
- def models(self):
- "Return a dict of mid -> model."
- mods = {}
- for m in [self.getModel(id) for id in self.db.list(
- "select id from models")]:
- mods[m.id] = m
- return mods
-
- def addModel(self, model):
- self.modSchema()
- model.flush()
- self.conf['currentModelId'] = model.id
-
- def delModel(self, mid):
- "Delete MODEL, and all its cards/facts."
- self.modSchema()
- # delete facts/cards
- self.delCards(self.db.list("""
-select id from cards where fid in (select id from facts where mid = ?)""",
- mid))
- # then the model
- self.db.execute("delete from models where id = ?", mid)
- # GUI should ensure last model is not deleted
- if self.conf['currentModelId'] == mid:
- self.conf['currentModelId'] = self.db.scalar(
- "select id from models limit 1")
-
- def allCSS(self):
- return "\n".join(self.db.list("select css from models"))
-
- def modelId(self, name):
- return self.db.scalar("select id from models where name = ?", name)
-
# Field checksums and sorting fields
##########################################################################
@@ -414,17 +371,16 @@ select id from cards where fid in (select id from facts where mid = ?)""",
def updateFieldCache(self, fids, csum=True):
"Update field checksums and sort cache, after find&replace, etc."
sfids = ids2str(fids)
- mods = self.models()
r = []
r2 = []
for (fid, mid, flds) in self._fieldData(sfids):
fields = splitFields(flds)
- model = mods[mid]
+ model = self.models.get(mid)
if csum:
- for f in model.fields:
+ for f in model['flds']:
if f['uniq'] and fields[f['ord']]:
r.append((fid, mid, fieldChecksum(fields[f['ord']])))
- r2.append((stripHTML(fields[model.sortIdx()]), fid))
+ r2.append((stripHTML(fields[self.models.sortIdx(model)]), fid))
if csum:
self.db.execute("delete from fsums where fid in "+sfids)
self.db.executemany("insert into fsums values (?,?,?)", r)
@@ -445,17 +401,17 @@ select id from cards where fid in (select id from facts where mid = ?)""",
where = ""
else:
raise Exception()
- mods = self.models()
- return [self._renderQA(mods[row[2]], row)
+ return [self._renderQA(row)
for row in self._qaData(where)]
- def _renderQA(self, model, data):
+ def _renderQA(self, data):
"Returns hash of id, question, answer."
# data is [cid, fid, mid, gid, ord, tags, flds]
# unpack fields and create dict
flist = splitFields(data[6])
fields = {}
- for (name, (idx, conf)) in model.fieldMap().items():
+ model = self.models.get(data[2])
+ for (name, (idx, conf)) in self.models.fieldMap(model).items():
fields[name] = flist[idx]
if fields[name]:
fields[name] = '%s' % (
@@ -463,9 +419,9 @@ select id from cards where fid in (select id from facts where mid = ?)""",
else:
fields[name] = ""
fields['Tags'] = data[5]
- fields['Model'] = model.name
+ fields['Model'] = model['name']
fields['Group'] = self.groupName(data[3])
- template = model.templates[data[4]]
+ template = model['tmpls'][data[4]]
fields['Template'] = template['name']
# render q & a
d = dict(id=data[0])
@@ -473,7 +429,7 @@ select id from cards where fid in (select id from facts where mid = ?)""",
if type == "q":
format = format.replace("cloze:", "cq:")
else:
- if model.conf['clozectx']:
+ if model['clozectx']:
name = "cactx:"
else:
name = "ca:"
@@ -770,8 +726,8 @@ select id from facts where id not in (select distinct fid from cards)""")
self.db.execute("delete from tags")
self.updateFactTags()
# field cache
- for m in self.models().values():
- self.updateFieldCache(m.fids())
+ for m in self.models.all():
+ self.updateFieldCache(self.models.fids(m['id']))
# and finally, optimize
self.optimize()
newSize = os.stat(self.path)[stat.ST_SIZE]
diff --git a/anki/facts.py b/anki/facts.py
index 45898a9e5..361f84eb3 100644
--- a/anki/facts.py
+++ b/anki/facts.py
@@ -19,12 +19,12 @@ class Fact(object):
else:
self.id = timestampID(deck.db, "facts")
self._model = model
- self.gid = model.conf['gid']
- self.mid = model.id
+ self.gid = model['gid']
+ self.mid = model['id']
self.tags = []
- self.fields = [""] * len(self._model.fields)
+ self.fields = [""] * len(self._model['flds'])
self.data = ""
- self._fmap = self._model.fieldMap()
+ self._fmap = self.deck.models.fieldMap(self._model)
def load(self):
(self.mid,
@@ -36,12 +36,12 @@ class Fact(object):
select mid, gid, mod, tags, flds, data from facts where id = ?""", self.id)
self.fields = splitFields(self.fields)
self.tags = parseTags(self.tags)
- self._model = self.deck.getModel(self.mid)
- self._fmap = self._model.fieldMap()
+ self._model = self.deck.models.get(self.mid)
+ self._fmap = self.deck.models.fieldMap(self._model)
def flush(self):
self.mod = intTime()
- sfld = stripHTML(self.fields[self._model.sortIdx()])
+ sfld = stripHTML(self.fields[self.deck.models.sortIdx(self._model)])
tags = self.stringTags()
res = self.deck.db.execute("""
insert or replace into facts values (?, ?, ?, ?, ?, ?, ?, ?)""",
diff --git a/anki/find.py b/anki/find.py
index 5959111a6..9111f1fc2 100644
--- a/anki/find.py
+++ b/anki/find.py
@@ -20,8 +20,8 @@ SEARCH_GROUP = 7
def fieldNames(deck, downcase=True):
fields = set()
names = []
- for m in deck.models().values():
- for f in m.fields:
+ for m in deck.models.all():
+ for f in m['flds']:
if f['name'].lower() not in fields:
names.append(f['name'])
fields.add(f['name'].lower())
@@ -119,7 +119,7 @@ order by %s""" % (lim, sort)
elif type == SEARCH_FIELD:
self._findField(token, isNeg)
elif type == SEARCH_MODEL:
- self._findModel(token, isNeg, c)
+ self._findModel(token, isNeg)
elif type == SEARCH_GROUP:
self._findGroup(token, isNeg)
else:
@@ -182,12 +182,13 @@ order by %s""" % (lim, sort)
def _findFids(self, val):
self.lims['fact'].append("id in (%s)" % val)
- def _findModel(self, val, isNeg, c):
+ def _findModel(self, val, isNeg):
extra = "not" if isNeg else ""
- self.lims['fact'].append(
- "mid %s in (select id from models where name like :_mod_%d)" % (
- extra, c))
- self.lims['args']['_mod_%d'%c] = val
+ ids = []
+ for m in self.deck.models.all():
+ if m['name'].lower() == val:
+ ids.append(m['id'])
+ self.lims['fact'].append("mid %s in %s" % (extra, ids2str(ids)))
def _findGroup(self, val, isNeg):
extra = "!" if isNeg else ""
@@ -203,8 +204,8 @@ order by %s""" % (lim, sort)
except:
num = None
lims = []
- for m in self.deck.models().values():
- for t in m.templates:
+ for m in self.deck.models.all():
+ for t in m['tmpls']:
# ordinal number?
if num is not None and t['ord'] == num:
self.lims['card'].append("ord %s %d" % (comp, num))
@@ -212,8 +213,8 @@ order by %s""" % (lim, sort)
# template name?
elif t['name'].lower() == val.lower():
lims.append((
- "(fid in (select id from facts where mid = %d) "
- "and ord %s %d)") % (m.id, comp, t['ord']))
+ "(fid in (select id from facts where mid = %s) "
+ "and ord %s %d)") % (m['id'], comp, t['ord']))
found = True
if lims:
self.lims['card'].append("(" + " or ".join(lims) + ")")
@@ -226,10 +227,10 @@ order by %s""" % (lim, sort)
value = "%" + parts[1].replace("*", "%") + "%"
# find models that have that field
mods = {}
- for m in self.deck.models().values():
- for f in m.fields:
+ for m in self.deck.models.all():
+ for f in m['flds']:
if f['name'].lower() == field:
- mods[m.id] = (m, f['ord'])
+ mods[m['id']] = (m, f['ord'])
if not mods:
# nothing has that field
self.lims['valid'] = False
@@ -243,11 +244,11 @@ where mid in %s and flds like ? escape '\\'""" % (
ids2str(mods.keys())),
"%" if self.full else value):
flds = splitFields(flds)
- ord = mods[mid][1]
- str = flds[ord]
+ ord = mods[str(mid)][1]
+ strg = flds[ord]
if self.full:
- str = stripHTML(str)
- if re.search(regex, str):
+ strg = stripHTML(strg)
+ if re.search(regex, strg):
fids.append(id)
extra = "not" if isNeg else ""
self.lims['fact'].append("id %s in %s" % (extra, ids2str(fids)))
@@ -372,10 +373,10 @@ def findReplace(deck, fids, src, dst, regex=False, field=None, fold=True):
"Find and replace fields in a fact."
mmap = {}
if field:
- for m in deck.models().values():
- for f in m.fields:
+ for m in deck.models.all():
+ for f in m['flds']:
if f['name'] == field:
- mmap[m.id] = f['ord']
+ mmap[m['id']] = f['ord']
if not mmap:
return 0
# find and gather replacements
@@ -393,7 +394,7 @@ def findReplace(deck, fids, src, dst, regex=False, field=None, fold=True):
# does it match?
sflds = splitFields(flds)
if field:
- ord = mmap[mid]
+ ord = mmap[str(mid)]
sflds[ord] = repl(sflds[ord])
else:
for c in range(len(sflds)):
diff --git a/anki/latex.py b/anki/latex.py
index c31a11457..9cb52e1f0 100644
--- a/anki/latex.py
+++ b/anki/latex.py
@@ -71,9 +71,9 @@ def _latexFromHtml(deck, latex):
def _buildImg(deck, latex, fname, model):
# add header/footer
- latex = (model.conf["latexPre"] + "\n" +
+ latex = (model["latexPre"] + "\n" +
latex + "\n" +
- model.conf["latexPost"])
+ model["latexPost"])
# write into a temp file
log = open(namedtmp("latex_log.txt"), "w")
texfile = file(namedtmp("tmp.tex"), "w")
diff --git a/anki/models.py b/anki/models.py
index 05575abbd..aeb1bfa64 100644
--- a/anki/models.py
+++ b/anki/models.py
@@ -2,19 +2,19 @@
# Copyright: Damien Elmes
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-import simplejson
+import simplejson, copy
from anki.utils import intTime, hexifyID, joinFields, splitFields, ids2str, \
timestampID
from anki.lang import _
# Models
##########################################################################
-# gid may point to non-existent group
-defaultConf = {
+# careful not to add any lists/dicts/etc here, as they aren't deep copied
+defaultModel = {
+ 'css': "",
'sortf': 0,
'gid': 1,
- 'tags': [],
'clozectx': False,
'latexPre': """\
\\documentclass[12pt]{article}
@@ -56,82 +56,132 @@ defaultTemplate = {
'gid': None,
}
-class Model(object):
+class ModelRegistry(object):
- def __init__(self, deck, id=None):
+ # Saving/loading registry
+ #############################################################
+
+ def __init__(self, deck):
self.deck = deck
- if id:
- self.id = id
- self.load()
- else:
- self.id = timestampID(deck.db, "models")
- self.name = u""
- self.conf = defaultConf.copy()
- self.css = ""
- self.fields = []
- self.templates = []
- def load(self):
- (self.mod,
- self.name,
- self.fields,
- self.templates,
- self.conf,
- self.css) = self.deck.db.first("""
-select mod, name, flds, tmpls, conf, css from models where id = ?""", self.id)
- self.fields = simplejson.loads(self.fields)
- self.templates = simplejson.loads(self.templates)
- self.conf = simplejson.loads(self.conf)
+ def load(self, json):
+ "Load registry from JSON."
+ self.changed = False
+ self.models = simplejson.loads(json)
+
+ def save(self, m=None):
+ "Mark M modified if provided, and schedule registry flush."
+ if m:
+ m['mod'] = intTime()
+ m['css'] = self._css(m)
+ self.changed = True
def flush(self):
- self.mod = intTime()
- self.css = self.genCSS()
- ret = self.deck.db.execute("""
-insert or replace into models values (?, ?, ?, ?, ?, ?, ?)""",
- self.id, self.mod, self.name,
- simplejson.dumps(self.fields),
- simplejson.dumps(self.templates),
- simplejson.dumps(self.conf),
- self.css)
- self.id = ret.lastrowid
+ "Flush the registry if any models were changed."
+ if self.changed:
+ self.deck.db.execute("update deck set models = ?",
+ simplejson.dumps(self.models))
- def fids(self):
+ # Retrieving and creating models
+ #############################################################
+
+ def current(self):
+ "Get current model."
+ return self.get(self.deck.conf['currentModelId'])
+
+ def get(self, id):
+ "Get model with ID."
+ return self.models[str(id)]
+
+ def all(self):
+ "Get all models."
+ return self.models.values()
+
+ def byName(self, name):
+ "Get model with NAME."
+ for m in self.models.values():
+ if m['name'].lower() == name.lower():
+ return m
+
+ def new(self, name):
+ "Create a new model, save it in the registry, and return it."
+ # caller should call save() after modifying
+ m = defaultModel.copy()
+ m['name'] = name
+ m['mod'] = intTime()
+ m['flds'] = []
+ m['tmpls'] = []
+ m['tags'] = []
+ return self._add(m)
+
+ def del_(self, m):
+ "Delete model, and all its cards/facts."
+ self.deck.modSchema()
+ # delete facts/cards
+ self.deck.delCards(self.deck.db.list("""
+select id from cards where fid in (select id from facts where mid = ?)""",
+ m['id']))
+ # then the model
+ del self.models[m['id']]
+ self.save()
+ # GUI should ensure last model is not deleted
+ if self.deck.conf['currentModelId'] == m['id']:
+ self.deck.conf['currentModelId'] = int(self.models.keys()[0])
+
+ def _add(self, m):
+ self._setID(m)
+ self.models[m['id']] = m
+ self.save(m)
+ self.deck.conf['currentModelId'] = m['id']
+ return m
+
+ def _setID(self, m):
+ while 1:
+ id = str(intTime(1000))
+ if id not in self.models:
+ break
+ m['id'] = id
+
+ # Tools
+ ##################################################
+
+ def fids(self, m):
+ "Fact ids for M."
return self.deck.db.list(
- "select id from facts where mid = ?", self.id)
+ "select id from facts where mid = ?", m['id'])
- def useCount(self):
+ def useCount(self, m):
+ "Number of fact using M."
return self.deck.db.scalar(
- "select count() from facts where mid = ?", self.id)
+ "select count() from facts where mid = ?", m['id'])
+
+ def css(self):
+ "CSS for all models."
+ return "\n".join([m['css'] for m in self.all()])
# Copying
##################################################
- def copy(self):
- "Copy, flush and return."
- new = Model(self.deck, self.id)
- new.id = None
- new.name += _(" copy")
- new.fields = [f.copy() for f in self.fields]
- new.templates = [t.copy() for t in self.templates]
- new.flush()
- return new
+ def copy(self, m):
+ "Copy, save and return."
+ m2 = copy.deepcopy(m)
+ m2['name'] = _("%s copy") % m2['name']
+ return self._add(m2)
# CSS generation
##################################################
- def genCSS(self):
- if not self.id:
- return ""
+ def _css(self, m):
# fields
css = "".join(self._fieldCSS(
- ".fm%s-%s" % (hexifyID(self.id), hexifyID(f['ord'])),
+ ".fm%s-%s" % (hexifyID(m['id']), hexifyID(f['ord'])),
(f['font'], f['qsize'], f['qcol'], f['rtl'], f['pre']))
- for f in self.fields)
+ for f in m['flds'])
# templates
css += "".join(".cm%s-%s {text-align:%s;background:%s}\n" % (
- hexifyID(self.id), hexifyID(t['ord']),
+ hexifyID(m['id']), hexifyID(t['ord']),
("center", "left", "right")[t['align']], t['bg'])
- for t in self.templates)
+ for t in m['tmpls'])
return css
def _rewriteFont(self, font):
@@ -158,64 +208,66 @@ insert or replace into models values (?, ?, ?, ?, ?, ?, ?)""",
# Fields
##################################################
- def fieldMap(self):
+ def newField(self, name):
+ f = defaultField.copy()
+ f['name'] = name
+ return f
+
+ def fieldMap(self, m):
"Mapping of field name -> (ord, field)."
- return dict((f['name'], (f['ord'], f)) for f in self.fields)
+ return dict((f['name'], (f['ord'], f)) for f in m['flds'])
- def sortIdx(self):
- return self.conf['sortf']
+ def sortIdx(self, m):
+ return m['sortf']
- def setSortIdx(self, idx):
- assert idx >= 0 and idx < len(self.fields)
+ def setSortIdx(self, m, idx):
+ assert idx >= 0 and idx < len(m['flds'])
self.deck.modSchema()
- self.conf['sortf'] = idx
- self.deck.updateFieldCache(self.fids(), csum=False)
- self.flush()
+ m['sortf'] = idx
+ self.deck.updateFieldCache(self.fids(m), csum=False)
+ self.save(m)
- def newField(self):
- return defaultField.copy()
-
- def addField(self, field):
- self.fields.append(field)
- self._updateFieldOrds()
- self.flush()
+ def addField(self, m, field):
+ m['flds'].append(field)
+ self._updateFieldOrds(m)
+ self.save(m)
def add(fields):
fields.append("")
return fields
- self._transformFields(add)
+ self._transformFields(m, add)
- def delField(self, field):
- idx = self.fields.index(field)
- self.fields.remove(field)
- self._updateFieldOrds()
+ def delField(self, m, field):
+ idx = m['flds'].index(field)
+ m['flds'].remove(field)
+ self._updateFieldOrds(m)
def delete(fields):
del fields[idx]
return fields
- self._transformFields(delete)
- if idx == self.sortIdx():
+ self._transformFields(m, delete)
+ if idx == self.sortIdx(m):
# need to rebuild
- self.deck.updateFieldCache(self.fids(), csum=False)
- # flushes
- self.renameField(field, None)
+ self.deck.updateFieldCache(self.fids(m), csum=False)
+ # saves
+ self.renameField(m, field, None)
- def moveField(self, field, idx):
- oldidx = self.fields.index(field)
+ def moveField(self, m, field, idx):
+ oldidx = m['flds'].index(field)
if oldidx == idx:
return
- self.fields.remove(field)
- self.fields.insert(idx, field)
- self._updateFieldOrds()
- self.flush()
+ m['flds'].remove(field)
+ m['flds'].insert(idx, field)
+ self._updateFieldOrds(m)
+ self.save(m)
def move(fields, oldidx=oldidx):
val = fields[oldidx]
del fields[oldidx]
fields.insert(idx, val)
return fields
- self._transformFields(move)
+ self._transformFields(m, move)
- def renameField(self, field, newName):
+ def renameField(self, m, field, newName):
self.deck.modSchema()
- for t in self.templates:
+ for t in m['tmpls']:
types = ("{{%s}}", "{{text:%s}}", "{{#%s}}",
"{{^%s}}", "{{/%s}}")
for type in types:
@@ -226,77 +278,79 @@ insert or replace into models values (?, ?, ?, ?, ?, ?, ?)""",
repl = ""
t[fmt] = t[fmt].replace(type%field['name'], repl)
field['name'] = newName
- self.flush()
+ self.save(m)
- def _updateFieldOrds(self):
- for c, f in enumerate(self.fields):
+ def _updateFieldOrds(self, m):
+ for c, f in enumerate(m['flds']):
f['ord'] = c
- def _transformFields(self, fn):
+ def _transformFields(self, m, fn):
self.deck.modSchema()
r = []
for (id, flds) in self.deck.db.execute(
- "select id, flds from facts where mid = ?", self.id):
+ "select id, flds from facts where mid = ?", m['id']):
r.append((joinFields(fn(splitFields(flds))), id))
self.deck.db.executemany("update facts set flds = ? where id = ?", r)
# Templates
##################################################
- def newTemplate(self):
- return defaultTemplate.copy()
+ def newTemplate(self, name):
+ t = defaultTemplate.copy()
+ t['name'] = name
+ return t
- def addTemplate(self, template):
+ def addTemplate(self, m, template):
self.deck.modSchema()
- self.templates.append(template)
- self._updateTemplOrds()
- self.flush()
+ m['tmpls'].append(template)
+ self._updateTemplOrds(m)
+ self.save(m)
- def delTemplate(self, template):
+ def delTemplate(self, m, template):
self.deck.modSchema()
- ord = self.templates.index(template)
+ ord = m['tmpls'].index(template)
cids = self.deck.db.list("""
select c.id from cards c, facts f where c.fid=f.id and mid = ? and ord = ?""",
- self.id, ord)
+ m['id'], ord)
self.deck.delCards(cids)
# shift ordinals
self.deck.db.execute("""
update cards set ord = ord - 1 where fid in (select id from facts
-where mid = ?) and ord > ?""", self.id, ord)
- self.templates.remove(template)
- self._updateTemplOrds()
- self.flush()
+where mid = ?) and ord > ?""", m['id'], ord)
+ m['tmpls'].remove(template)
+ self._updateTemplOrds(m)
+ self.save(m)
- def _updateTemplOrds(self):
- for c, t in enumerate(self.templates):
+ def _updateTemplOrds(self, m):
+ for c, t in enumerate(m['tmpls']):
t['ord'] = c
- def moveTemplate(self, template, idx):
- oldidx = self.templates.index(template)
+ def moveTemplate(self, m, template, idx):
+ oldidx = m['tmpls'].index(template)
if oldidx == idx:
return
- oldidxs = dict((id(t), t['ord']) for t in self.templates)
- self.templates.remove(template)
- self.templates.insert(idx, template)
- self._updateTemplOrds()
+ oldidxs = dict((id(t), t['ord']) for t in m['tmpls'])
+ m['tmpls'].remove(template)
+ m['tmpls'].insert(idx, template)
+ self._updateTemplOrds(m)
# generate change map
map = []
- for t in self.templates:
+ for t in m['tmpls']:
map.append("when ord = %d then %d" % (oldidxs[id(t)], t['ord']))
# apply
- self.flush()
+ self.save(m)
self.deck.db.execute("""
update cards set ord = (case %s end) where fid in (
-select id from facts where mid = ?)""" % " ".join(map), self.id)
+select id from facts where mid = ?)""" % " ".join(map), m['id'])
# Model changing
##########################################################################
# - maps are ord->ord, and there should not be duplicate targets
# - newModel should be self if model is not changing
- def changeModel(self, fids, newModel, fmap, cmap):
+ def change(self, m, fids, newModel, fmap, cmap):
self.deck.modSchema()
- assert newModel.id == self.id or (fmap and cmap)
+ assert newModel['id'] == m['id'] or (fmap and cmap)
if fmap:
self._changeFacts(fids, newModel, fmap)
if cmap:
@@ -304,7 +358,7 @@ select id from facts where mid = ?)""" % " ".join(map), self.id)
def _changeFacts(self, fids, newModel, map):
d = []
- nfields = len(newModel.fields)
+ nfields = len(newModel['flds'])
for (fid, flds) in self.deck.db.execute(
"select id, flds from facts where id in "+ids2str(fids)):
newflds = {}
@@ -315,7 +369,7 @@ select id from facts where mid = ?)""" % " ".join(map), self.id)
for c in range(nfields):
flds.append(newflds.get(c, ""))
flds = joinFields(flds)
- d.append(dict(fid=fid, flds=flds, mid=newModel.id))
+ d.append(dict(fid=fid, flds=flds, mid=newModel['id']))
self.deck.db.executemany(
"update facts set flds=:flds, mid=:mid where id = :fid", d)
self.deck.updateFieldCache(fids)
diff --git a/anki/stats.py b/anki/stats.py
index 5227849f4..3ab8fb4b2 100644
--- a/anki/stats.py
+++ b/anki/stats.py
@@ -47,7 +47,7 @@ class CardStats(object):
self.addLine(_("Total Time"), self.time(total))
elif c.queue == 0:
self.addLine(_("Position"), c.due)
- self.addLine(_("Model"), c.model().name)
+ self.addLine(_("Model"), c.model()['name'])
self.addLine(_("Template"), c.template()['name'])
self.addLine(_("Current Group"), self.deck.groupName(c.gid))
self.addLine(_("Initial Group"), self.deck.groupName(c.fact().gid))
diff --git a/anki/stdmodels.py b/anki/stdmodels.py
index 54a719bf7..15c945d5d 100644
--- a/anki/stdmodels.py
+++ b/anki/stdmodels.py
@@ -2,7 +2,6 @@
# Copyright: Damien Elmes
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-from anki.models import Model
from anki.lang import _
models = []
@@ -10,55 +9,50 @@ models = []
# Basic
##########################################################################
-def BasicModel(deck):
- m = Model(deck)
- m.name = _("Basic")
- fm = m.newField()
- fm['name'] = _("Front")
+def addBasicModel(deck):
+ mm = deck.models
+ m = mm.new(_("Basic"))
+ fm = mm.newField(_("Front"))
fm['req'] = True
fm['uniq'] = True
- m.addField(fm)
- fm = m.newField()
- fm['name'] = _("Back")
- m.addField(fm)
- t = m.newTemplate()
- t['name'] = _("Forward")
+ mm.addField(m, fm)
+ fm = mm.newField(_("Back"))
+ mm.addField(m, fm)
+ t = mm.newTemplate(_("Forward"))
t['qfmt'] = "{{" + _("Front") + "}}"
t['afmt'] = "{{" + _("Back") + "}}"
- m.addTemplate(t)
- t = m.newTemplate()
- t['name'] = _("Reverse")
+ mm.addTemplate(m, t)
+ t = mm.newTemplate(_("Reverse"))
t['qfmt'] = "{{" + _("Back") + "}}"
t['afmt'] = "{{" + _("Front") + "}}"
t['actv'] = False
- m.addTemplate(t)
+ mm.addTemplate(m, t)
+ mm.save(m)
return m
-models.append((_("Basic"), BasicModel))
+models.append((_("Basic"), addBasicModel))
# Cloze
##########################################################################
-def ClozeModel(deck):
- m = Model(deck)
- m.name = _("Cloze")
- fm = m.newField()
- fm['name'] = _("Text")
+def addClozeModel(deck):
+ mm = deck.models
+ m = mm.new(_("Cloze"))
+ fm = mm.newField(_("Text"))
fm['req'] = True
fm['uniq'] = True
- m.addField(fm)
- fm = m.newField()
- fm['name'] = _("Notes")
- m.addField(fm)
+ mm.addField(m, fm)
+ fm = mm.newField(_("Notes"))
+ mm.addField(m, fm)
for i in range(8):
n = i+1
- t = m.newTemplate()
- t['name'] = _("Cloze") + " %d" % n
+ t = mm.newTemplate(_("Cloze") + " %d" % n)
t['qfmt'] = ("{{#cloze:%d:Text}}
{{cloze:%d:%s}}
"+
"{{/cloze:%d:Text}}") % (n, n, _("Text"), n)
t['afmt'] = ("{{cloze:%d:" + _("Text") + "}}") % n
t['afmt'] += "
{{" + _("Notes") + "}}"
- m.addTemplate(t)
+ mm.addTemplate(m, t)
+ mm.save(m)
return m
-models.append((_("Cloze"), ClozeModel))
+models.append((_("Cloze"), addClozeModel))
diff --git a/anki/storage.py b/anki/storage.py
index 312a4f1af..5599fcb55 100644
--- a/anki/storage.py
+++ b/anki/storage.py
@@ -9,7 +9,7 @@ from anki.lang import _
from anki.utils import intTime
from anki.db import DB
from anki.deck import _Deck
-from anki.stdmodels import BasicModel, ClozeModel
+from anki.stdmodels import addBasicModel, addClozeModel
from anki.errors import AnkiError
from anki.hooks import runHook
@@ -34,8 +34,8 @@ def Deck(path, queue=True, lock=True):
_upgradeDeck(deck, ver)
elif create:
# add in reverse order so basic is default
- deck.addModel(ClozeModel(deck))
- deck.addModel(BasicModel(deck))
+ addClozeModel(deck)
+ addBasicModel(deck)
deck.save()
if lock:
deck.lock()
@@ -67,9 +67,9 @@ create table if not exists deck (
lastSync integer not null,
qconf text not null,
conf text not null,
+ models text not null,
groups text not null,
- gconf text not null,
- data text not null
+ gconf text not null
);
create table if not exists cards (
@@ -108,16 +108,6 @@ create table if not exists fsums (
csum integer not null
);
-create table if not exists models (
- id integer primary key,
- mod integer not null,
- name text not null,
- flds text not null,
- tmpls text not null,
- conf text not null,
- css text not null
-);
-
create table if not exists graves (
time integer not null,
oid integer not null,
@@ -148,13 +138,14 @@ values(1,0,0,0,%(v)s,0,'',0,'','','','','');
import anki.groups
if setDeckConf:
db.execute("""
-update deck set qconf = ?, conf = ?, groups = ?, gconf = ?, data = ?""",
+update deck set qconf = ?, conf = ?, models = ?, groups = ?, gconf = ?""",
simplejson.dumps(anki.deck.defaultQconf),
simplejson.dumps(anki.deck.defaultConf),
+ "{}",
simplejson.dumps({'1': {'name': _("Default"), 'conf': 1,
'mod': intTime()}}),
- simplejson.dumps({'1': anki.groups.defaultConf}),
- "{}")
+ simplejson.dumps({'1': anki.groups.defaultConf}))
+
def _updateIndices(db):
"Add indices to the DB."
@@ -494,7 +485,7 @@ order by modelId, ordinal"""):
def _fixupModels(deck):
# rewrite model/template/field ids
- models = deck.models()
+ models = deck.models.all()
deck.db.execute("delete from models")
times = {}
for c, m in enumerate(models.values()):
diff --git a/anki/utils.py b/anki/utils.py
index a486f6091..18405de0b 100644
--- a/anki/utils.py
+++ b/anki/utils.py
@@ -171,7 +171,7 @@ def entsToTxt(html):
##############################################################################
def hexifyID(id):
- return "%x" % id
+ return "%x" % int(id)
def dehexifyID(id):
return int(id, 16)
diff --git a/tests/test_cards.py b/tests/test_cards.py
index 89658851a..8a9b39165 100644
--- a/tests/test_cards.py
+++ b/tests/test_cards.py
@@ -12,7 +12,7 @@ def test_genCards():
f['Front'] = u'1'
f['Back'] = u'2'
deck.addFact(f)
- cards = deck.genCards(f, f.model().templates)
+ cards = deck.genCards(f, f.model()['tmpls'])
assert len(cards) == 1
assert cards[0].ord == 1
assert deck.cardCount() == 2
@@ -23,7 +23,7 @@ def test_genCards():
f['Front'] = u'1'
f['Back'] = u'2'
deck.addFact(f)
- cards = deck.genCards(f, f.model().templates)
+ cards = deck.genCards(f, f.model()['tmpls'])
assert deck.cardCount() == 4
c = deck.db.list("select due from cards where fid = ?", f.id)
assert c[0] == c[1]
diff --git a/tests/test_deck.py b/tests/test_deck.py
index 1f429fba1..ec47fb591 100644
--- a/tests/test_deck.py
+++ b/tests/test_deck.py
@@ -2,7 +2,7 @@
import os, re, datetime
from tests.shared import assertException, getEmptyDeck, testDir
-from anki.stdmodels import BasicModel
+from anki.stdmodels import addBasicModel
from anki import Deck
@@ -53,8 +53,8 @@ def test_factAddDelete():
f = deck.newFact()
f['Front'] = u"one"; f['Back'] = u"two"
m = f.model()
- m.templates[1]['actv'] = True
- m.flush()
+ m['tmpls'][1]['actv'] = True
+ deck.models.save(m)
n = deck.addFact(f)
assert n == 2
# check q/a generation
@@ -65,7 +65,7 @@ def test_factAddDelete():
assert not p
# now let's make a duplicate and test uniqueness
f2 = deck.newFact()
- f2.model().fields[1]['req'] = True
+ f2.model()['flds'][1]['req'] = True
f2['Front'] = u"one"; f2['Back'] = u""
p = f2.problems()
assert p[0] == "unique"
@@ -102,15 +102,15 @@ def test_fieldChecksum():
"select csum from fsums") == int("4b0e5a4c", 16)
# turning off unique and modifying the fact should delete the sum
m = f.model()
- m.fields[0]['uniq'] = False
- m.flush()
+ m['flds'][0]['uniq'] = False
+ deck.models.save(m)
f.flush()
assert deck.db.scalar(
"select count() from fsums") == 0
# and turning on both should ensure two checksums generated
- m.fields[0]['uniq'] = True
- m.fields[1]['uniq'] = True
- m.flush()
+ m['flds'][0]['uniq'] = True
+ m['flds'][1]['uniq'] = True
+ deck.models.save(m)
f.flush()
assert deck.db.scalar(
"select count() from fsums") == 2
@@ -190,8 +190,8 @@ def test_addDelTags():
def test_timestamps():
deck = getEmptyDeck()
- assert len(deck.models()) == 2
+ assert len(deck.models.models) == 2
for i in range(100):
- deck.addModel(BasicModel(deck))
- assert len(deck.models()) == 102
-
+ addBasicModel(deck)
+ assert len(deck.models.models) == 102
+
diff --git a/tests/test_find.py b/tests/test_find.py
index 02fd42915..93bb0bbb8 100644
--- a/tests/test_find.py
+++ b/tests/test_find.py
@@ -25,7 +25,7 @@ def test_findCards():
f = deck.newFact()
f['Front'] = u'template test'
f['Back'] = u'foo bar'
- f.model().templates[1]['actv'] = True
+ f.model()['tmpls'][1]['actv'] = True
deck.addFact(f)
latestCardIds = [c.id for c in f.cards()]
# tag searches
diff --git a/tests/test_latex.py b/tests/test_latex.py
index 8e5af7467..6f6986124 100644
--- a/tests/test_latex.py
+++ b/tests/test_latex.py
@@ -2,7 +2,6 @@
import os
from tests.shared import assertException, getEmptyDeck
-from anki.stdmodels import BasicModel
from anki.utils import stripHTML, intTime
from anki.hooks import addHook
diff --git a/tests/test_models.py b/tests/test_models.py
index 71e887974..d6e60b24b 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -1,7 +1,6 @@
# coding: utf-8
from tests.shared import getEmptyDeck
-from anki.models import Model
from anki.utils import stripHTML
def test_modelDelete():
@@ -11,20 +10,20 @@ def test_modelDelete():
f['Back'] = u'2'
deck.addFact(f)
assert deck.cardCount() == 1
- deck.delModel(deck.conf['currentModelId'])
+ deck.models.del_(deck.models.get(deck.conf['currentModelId']))
assert deck.cardCount() == 0
def test_modelCopy():
deck = getEmptyDeck()
- m = deck.currentModel()
- m2 = m.copy()
- assert m2.name == "Basic copy"
- assert m2.id != m.id
- assert len(m2.fields) == 2
- assert len(m.fields) == 2
- assert len(m2.fields) == len(m.fields)
- assert len(m.templates) == 2
- assert len(m2.templates) == 2
+ m = deck.models.current()
+ m2 = deck.models.copy(m)
+ assert m2['name'] == "Basic copy"
+ assert m2['id'] != m['id']
+ assert len(m2['flds']) == 2
+ assert len(m['flds']) == 2
+ assert len(m2['flds']) == len(m['flds'])
+ assert len(m['tmpls']) == 2
+ assert len(m2['tmpls']) == 2
def test_fields():
d = getEmptyDeck()
@@ -32,50 +31,50 @@ def test_fields():
f['Front'] = u'1'
f['Back'] = u'2'
d.addFact(f)
- m = d.currentModel()
+ m = d.models.current()
# make sure renaming a field updates the templates
- m.renameField(m.fields[0], "NewFront")
- assert m.templates[0]['qfmt'] == "{{NewFront}}"
+ d.models.renameField(m, m['flds'][0], "NewFront")
+ assert m['tmpls'][0]['qfmt'] == "{{NewFront}}"
# add a field
- f = m.newField()
+ f = d.models.newField(m)
f['name'] = "foo"
- m.addField(f)
- assert d.getFact(m.fids()[0]).fields == ["1", "2", ""]
+ d.models.addField(m, f)
+ assert d.getFact(d.models.fids(m)[0]).fields == ["1", "2", ""]
# rename it
- m.renameField(f, "bar")
- assert d.getFact(m.fids()[0])['bar'] == ''
+ d.models.renameField(m, f, "bar")
+ assert d.getFact(d.models.fids(m)[0])['bar'] == ''
# delete back
- m.delField(m.fields[1])
- assert d.getFact(m.fids()[0]).fields == ["1", ""]
+ d.models.delField(m, m['flds'][1])
+ assert d.getFact(d.models.fids(m)[0]).fields == ["1", ""]
# move 0 -> 1
- m.moveField(m.fields[0], 1)
- assert d.getFact(m.fids()[0]).fields == ["", "1"]
+ d.models.moveField(m, m['flds'][0], 1)
+ assert d.getFact(d.models.fids(m)[0]).fields == ["", "1"]
# move 1 -> 0
- m.moveField(m.fields[1], 0)
- assert d.getFact(m.fids()[0]).fields == ["1", ""]
+ d.models.moveField(m, m['flds'][1], 0)
+ assert d.getFact(d.models.fids(m)[0]).fields == ["1", ""]
# add another and put in middle
- f = m.newField()
+ f = d.models.newField(m)
f['name'] = "baz"
- m.addField(f)
- f = d.getFact(m.fids()[0])
+ d.models.addField(m, f)
+ f = d.getFact(d.models.fids(m)[0])
f['baz'] = "2"
f.flush()
- assert d.getFact(m.fids()[0]).fields == ["1", "", "2"]
+ assert d.getFact(d.models.fids(m)[0]).fields == ["1", "", "2"]
# move 2 -> 1
- m.moveField(m.fields[2], 1)
- assert d.getFact(m.fids()[0]).fields == ["1", "2", ""]
+ d.models.moveField(m, m['flds'][2], 1)
+ assert d.getFact(d.models.fids(m)[0]).fields == ["1", "2", ""]
# move 0 -> 2
- m.moveField(m.fields[0], 2)
- assert d.getFact(m.fids()[0]).fields == ["2", "", "1"]
+ d.models.moveField(m, m['flds'][0], 2)
+ assert d.getFact(d.models.fids(m)[0]).fields == ["2", "", "1"]
# move 0 -> 1
- m.moveField(m.fields[0], 1)
- assert d.getFact(m.fids()[0]).fields == ["", "2", "1"]
+ d.models.moveField(m, m['flds'][0], 1)
+ assert d.getFact(d.models.fids(m)[0]).fields == ["", "2", "1"]
def test_templates():
d = getEmptyDeck()
- m = d.currentModel()
- m.templates[1]['actv'] = True
- m.flush()
+ m = d.models.current()
+ m['tmpls'][1]['actv'] = True
+ d.models.save(m)
f = d.newFact()
f['Front'] = u'1'
f['Back'] = u'2'
@@ -86,12 +85,12 @@ def test_templates():
assert c.ord == 0
assert c2.ord == 1
# switch templates
- m.moveTemplate(c.template(), 1)
+ d.models.moveTemplate(m, c.template(), 1)
c.load(); c2.load()
assert c.ord == 1
assert c2.ord == 0
# removing a template should delete its cards
- m.delTemplate(m.templates[0])
+ d.models.delTemplate(m, m['tmpls'][0])
assert d.cardCount() == 1
# and should have updated the other cards' ordinals
c = f.cards()[0]
@@ -100,9 +99,9 @@ def test_templates():
def test_text():
d = getEmptyDeck()
- m = d.currentModel()
- m.templates[0]['qfmt'] = "{{text:Front}}"
- m.flush()
+ m = d.models.current()
+ m['tmpls'][0]['qfmt'] = "{{text:Front}}"
+ d.models.save(m)
f = d.newFact()
f['Front'] = u'helloworld'
d.addFact(f)
@@ -110,9 +109,9 @@ def test_text():
def test_cloze():
d = getEmptyDeck()
- d.conf['currentModelId'] = d.modelId("Cloze")
+ d.conf['currentModelId'] = d.models.byName("Cloze")['id']
f = d.newFact()
- assert f.model().name == "Cloze"
+ assert f.model()['name'] == "Cloze"
# a cloze model with no clozes is empty
f['Text'] = u'nothing'
assert d.addFact(f) == 0
@@ -124,7 +123,7 @@ def test_cloze():
assert "world" in f.cards()[0].a()
assert "hello world" not in f.cards()[0].a()
# check context works too
- f.model().conf['clozectx'] = True
+ f.model()['clozectx'] = True
assert "hello world" in f.cards()[0].a()
# and with a comment
f = d.newFact()
@@ -143,16 +142,16 @@ def test_cloze():
assert "world bar" in c2.a()
# if there are multiple answers for a single cloze, they are given in a
# list
- f.model().conf['clozectx'] = False
+ f.model()['clozectx'] = False
f = d.newFact()
f['Text'] = "a {{c1::b}} {{c1::c}}"
assert d.addFact(f) == 1
assert "b, c" in (
f.cards()[0].a())
# clozes should be supported in sections too
- m = d.currentModel()
- m.templates[0]['qfmt'] = "{{#cloze:1:Text}}{{Notes}}{{/cloze:1:Text}}"
- m.flush()
+ m = d.models.current()
+ m['tmpls'][0]['qfmt'] = "{{#cloze:1:Text}}{{Notes}}{{/cloze:1:Text}}"
+ d.models.save(m)
f = d.newFact()
f['Text'] = "hello"
f['Notes'] = "world"
@@ -162,18 +161,18 @@ def test_cloze():
def test_modelChange():
deck = getEmptyDeck()
- basic = deck.getModel(deck.modelId("Basic"))
- cloze = deck.getModel(deck.modelId("Cloze"))
+ basic = deck.models.byName("Basic")
+ cloze = deck.models.byName("Cloze")
# enable second template and add a fact
- basic.templates[1]['actv'] = True
- basic.flush()
+ basic['tmpls'][1]['actv'] = True
+ deck.models.save(basic)
f = deck.newFact()
f['Front'] = u'f'
f['Back'] = u'b'
deck.addFact(f)
# switch fields
map = {0: 1, 1: 0}
- basic.changeModel([f.id], basic, map, None)
+ deck.models.change(basic, [f.id], basic, map, None)
f.load()
assert f['Front'] == 'b'
assert f['Back'] == 'f'
@@ -184,7 +183,7 @@ def test_modelChange():
assert stripHTML(c1.q()) == "f"
assert c0.ord == 0
assert c1.ord == 1
- basic.changeModel([f.id], basic, None, map)
+ deck.models.change(basic, [f.id], basic, None, map)
f.load(); c0.load(); c1.load()
assert stripHTML(c0.q()) == "f"
assert stripHTML(c1.q()) == "b"
@@ -194,7 +193,7 @@ def test_modelChange():
assert f.cards()[0].id == c1.id
# delete first card
map = {0: None, 1: 1}
- basic.changeModel([f.id], basic, None, map)
+ deck.models.change(basic, [f.id], basic, None, map)
f.load()
c0.load()
try:
@@ -206,7 +205,7 @@ def test_modelChange():
# an unmapped field becomes blank
assert f['Front'] == 'b'
assert f['Back'] == 'f'
- basic.changeModel([f.id], basic, map, None)
+ deck.models.change(basic, [f.id], basic, map, None)
f.load()
assert f['Front'] == ''
assert f['Back'] == 'f'
@@ -215,12 +214,21 @@ def test_modelChange():
f['Front'] = u'f2'
f['Back'] = u'b2'
deck.addFact(f)
- assert basic.useCount() == 2
- assert cloze.useCount() == 0
+ assert deck.models.useCount(basic) == 2
+ assert deck.models.useCount(cloze) == 0
map = {0: 0, 1: 1}
- basic.changeModel([f.id], cloze, map, map)
+ deck.models.change(basic, [f.id], cloze, map, map)
f.load()
assert f['Text'] == "f2"
assert f['Notes'] == "b2"
assert len(f.cards()) == 2
assert "b2" in f.cards()[0].a()
+
+def test_css():
+ deck = getEmptyDeck()
+ basic = deck.models.byName("Basic")
+ assert "arial" in basic['css']
+ assert "helvetica" not in basic['css']
+ basic['flds'][0]['font'] = "helvetica"
+ deck.models.save(basic)
+ assert "helvetica" in basic['css']
diff --git a/tests/test_sched.py b/tests/test_sched.py
index 720b01984..b329efe82 100644
--- a/tests/test_sched.py
+++ b/tests/test_sched.py
@@ -2,7 +2,6 @@
import time, copy
from tests.shared import assertException, getEmptyDeck
-from anki.stdmodels import BasicModel
from anki.utils import stripHTML, intTime
from anki.hooks import addHook
@@ -32,9 +31,9 @@ def test_new():
assert c.due >= t
# the default order should ensure siblings are not seen together, and
# should show all cards
- m = d.currentModel()
- m.templates[1]['actv'] = True
- m.flush()
+ m = d.models.current()
+ m['tmpls'][1]['actv'] = True
+ d.models.save(m)
f = d.newFact()
f['Front'] = u"2"; f['Back'] = u"2"
d.addFact(f)
@@ -50,15 +49,15 @@ def test_new():
def test_newOrder():
d = getEmptyDeck()
- m = d.currentModel()
+ m = d.models.current()
for i in range(50):
- t = m.newTemplate()
+ t = d.models.newTemplate(m)
t['name'] = str(i)
t['qfmt'] = "{{Front}}"
t['afmt'] = "{{Back}}"
t['actv'] = i > 25
- m.addTemplate(t)
- m.flush()
+ d.models.addTemplate(m, t)
+ d.models.save(m)
f = d.newFact()
f['Front'] = u'1'
f['Back'] = u'2'
@@ -495,19 +494,19 @@ def test_cramLimits():
def test_adjIvl():
d = getEmptyDeck()
# add two more templates and set second active
- m = d.currentModel()
- m.templates[1]['actv'] = True
- t = m.newTemplate()
+ m = d.models.current()
+ m['tmpls'][1]['actv'] = True
+ t = d.models.newTemplate(m)
t['name'] = "f2"
t['qfmt'] = "{{Front}}"
t['afmt'] = "{{Back}}"
- m.addTemplate(t)
- t = m.newTemplate()
+ d.models.addTemplate(m, t)
+ t = d.models.newTemplate(m)
t['name'] = "f3"
t['qfmt'] = "{{Front}}"
t['afmt'] = "{{Back}}"
- m.addTemplate(t)
- m.flush()
+ d.models.addTemplate(m, t)
+ d.models.save(m)
# create a new fact; it should have 4 cards
f = d.newFact()
f['Front'] = "1"; f['Back'] = "1"
@@ -560,14 +559,14 @@ def test_adjIvl():
def test_ordcycle():
d = getEmptyDeck()
# add two more templates and set second active
- m = d.currentModel()
- m.templates[1]['actv'] = True
- t = m.newTemplate()
+ m = d.models.current()
+ m['tmpls'][1]['actv'] = True
+ t = d.models.newTemplate(m)
t['name'] = "f2"
t['qfmt'] = "{{Front}}"
t['afmt'] = "{{Back}}"
- m.addTemplate(t)
- m.flush()
+ d.models.addTemplate(m, t)
+ d.models.save(m)
# create a new fact; it should have 4 cards
f = d.newFact()
f['Front'] = "1"; f['Back'] = "1"
diff --git a/tests/test_stats.py b/tests/test_stats.py
index a42202a9c..8d222c625 100644
--- a/tests/test_stats.py
+++ b/tests/test_stats.py
@@ -2,7 +2,6 @@
import time, copy, os
from tests.shared import assertException, getEmptyDeck
-from anki.stdmodels import BasicModel
from anki.utils import stripHTML, intTime
from anki.hooks import addHook
diff --git a/tests/test_sync.py b/tests/test_sync.py
index b04ed3d33..b3ed518e3 100644
--- a/tests/test_sync.py
+++ b/tests/test_sync.py
@@ -6,7 +6,6 @@ from tests.shared import assertException
from anki.errors import *
from anki import Deck
from anki.utils import intTime
-from anki.stdmodels import BasicModel
from anki.sync import SyncClient, SyncServer, HttpSyncServer, HttpSyncServerProxy
from anki.sync import copyLocalMedia
from anki.facts import Fact
diff --git a/tests/test_undo.py b/tests/test_undo.py
index 73861f385..755e80ee6 100644
--- a/tests/test_undo.py
+++ b/tests/test_undo.py
@@ -2,8 +2,6 @@
import time
from tests.shared import assertException, getEmptyDeck
-from anki.stdmodels import BasicModel
-
def test_op():
d = getEmptyDeck()
# should have no undo by default