port changeModel()

This commit is contained in:
Damien Elmes 2011-04-14 00:05:37 +09:00
parent 1baa7bb5b6
commit 98a63285e1
2 changed files with 102 additions and 128 deletions

View file

@ -3,7 +3,7 @@
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import simplejson import simplejson
from anki.utils import intTime, hexifyID, joinFields, splitFields from anki.utils import intTime, hexifyID, joinFields, splitFields, ids2str
from anki.lang import _ from anki.lang import _
# Models # Models
@ -278,68 +278,45 @@ select id from facts where mid = ?)""" % " ".join(map), self.id)
# Model changing # Model changing
########################################################################## ##########################################################################
# - maps are ord->ord, and there should not be duplicate targets
# - newModel should be self if model is not changing
# - interface should ensure there's at least one remaining card
def changeModel(self, fids, newModel, fieldMap, cardMap): def changeModel(self, fids, newModel, fmap, cmap):
raise Exception() self.deck.modSchema()
self.modSchema() assert newModel.id == self.id or (fmap and cmap)
sfids = ids2str(fids) if fmap:
# field remapping self._changeFacts(fids, newModel, fmap)
if fieldMap: if cmap:
seen = {} self._changeCards(fids, newModel, cmap)
for (old, new) in fieldMap.items():
seen[new] = 1 def _changeFacts(self, fids, newModel, map):
if new: d = []
# can rename nfields = len(newModel.fields)
self.db.execute(""" for (fid, flds) in self.deck.db.execute(
update fdata set "select id, flds from facts where id in "+ids2str(fids)):
fmid = :new, newflds = {}
ord = :ord flds = splitFields(flds)
where fmid = :old for old, new in map.items():
and fid in %s""" % sfids, new=new.id, ord=new.ord, old=old.id) newflds[new] = flds[old]
else: flds = []
# no longer used for c in range(nfields):
self.db.execute(""" flds.append(newflds.get(c, ""))
delete from fdata where fid in %s flds = joinFields(flds)
and fmid = :id""" % sfids, id=old.id) d.append(dict(fid=fid, flds=flds, mid=newModel.id))
# new self.deck.db.executemany(
for field in newModel.fields: "update facts set flds=:flds, mid=:mid where id = :fid", d)
if field not in seen: self.deck.updateFieldCache(fids)
d = [{'fid': f,
'fmid': field.id, def _changeCards(self, fids, newModel, map):
'ord': field.ord} d = []
for f in fids] deleted = []
self.db.executemany(''' for (cid, ord) in self.deck.db.execute(
insert into fdata "select id, ord from cards where fid in "+ids2str(fids)):
(fid, fmid, ord, value) if map[ord] is not None:
values d.append(dict(cid=cid, new=map[ord]))
(:fid, :fmid, :ord, "")''', d) else:
# fact modtime deleted.append(cid)
self.db.execute(""" self.deck.db.executemany(
update facts set "update cards set ord=:new where id=:cid", d)
mod = :t, self.deck.delCards(deleted)
mid = :id
where id in %s""" % sfids, t=time.time(), id=newModel.id)
# template remapping
toChange = []
for (old, new) in cardMap.items():
if not new:
# delete
self.db.execute("""
delete from cards
where tid = :cid and
fid in %s""" % sfids, cid=old.id)
elif old != new:
# gather ids so we can rename x->y and y->x
ids = self.db.list("""
select id from cards where
tid = :id and fid in %s""" % sfids, id=old.id)
toChange.append((new, ids))
for (new, ids) in toChange:
self.db.execute("""
update cards set
tid = :new,
ord = :ord
where id in %s""" % ids2str(ids), new=new.id, ord=new.ord)
cardIds = self.db.list(
"select id from cards where fid in %s" %
ids2str(fids))

View file

@ -148,69 +148,66 @@ def test_cloze():
assert d.addFact(f) == 1 assert d.addFact(f) == 1
def test_modelChange(): def test_modelChange():
print "model change"
return
deck = getEmptyDeck() deck = getEmptyDeck()
m2 = deck.currentModel() basic = deck.getModel(1)
# taken from jp support plugin cloze = deck.getModel(2)
m1 = Model(deck) # enable second template and add a fact
m1.name = "Japanese" basic.templates[1]['actv'] = True
# field 1 basic.flush()
fm = m1.newField()
fm['name'] = "Expression"
fm['req'] = True
fm['uniq'] = True
m1.addField(fm)
# field2
fm = m1.newField()
fm['name'] = "Meaning"
m1.addField(fm)
# field3
fm = m1.newField()
fm['name'] = "Reading"
m1.addField(fm)
# template1
t = Template(deck)
t.name = "Recognition"
t.qfmt = "{{Expression}}"
t.afmt = "{{Reading}}<br>{{Meaning}}"
m1.addTemplate(t)
# template2
t = Template(deck)
t.name = "Recall"
t.qfmt = "{{Meaning}}"
t.afmt = "{{Expression}}<br>{{Reading}}"
#t.active = False
m1.addTemplate(t)
deck.addModel(m1)
# add some facts
f = deck.newFact() f = deck.newFact()
f['Expression'] = u'e' f['Front'] = u'f'
f['Meaning'] = u'm' f['Back'] = u'b'
f['Reading'] = u'r'
deck.addFact(f) deck.addFact(f)
f2 = deck.newFact() # switch fields
f2['Expression'] = u'e2' map = {0: 1, 1: 0}
f2['Meaning'] = u'm2' basic.changeModel([f.id], basic, map, None)
f2['Reading'] = u'r2' f.load()
deck.addFact(f2) assert f['Front'] == 'b'
assert f['Back'] == 'f'
# convert to basic # switch cards
assert deck.modelUseCount(m1) == 2 c0 = f.cards()[0]
assert deck.modelUseCount(m2) == 0 c1 = f.cards()[1]
assert deck.cardCount() == 4 assert stripHTML(c0.q()) == "b"
assert deck.factCount() == 2 assert stripHTML(c1.q()) == "f"
fmap = {m1.fields[0]: m2.fields[0], assert c0.ord == 0
m1.fields[1]: None, assert c1.ord == 1
m1.fields[2]: m2.fields[1]} basic.changeModel([f.id], basic, None, map)
cmap = {m1.templates[0]: m2.templates[0], f.load(); c0.load(); c1.load()
m1.templates[1]: None} assert stripHTML(c0.q()) == "f"
deck.changeModel([f.id], m2, fmap, cmap) assert stripHTML(c1.q()) == "b"
assert deck.modelUseCount(m1) == 1 assert c0.ord == 1
assert deck.modelUseCount(m2) == 1 assert c1.ord == 0
assert deck.cardCount() == 3 # .cards() returns cards in order
assert deck.factCount() == 2 assert f.cards()[0].id == c1.id
c = deck.getCard(deck.db.scalar("select id from cards where fid = ?", f.id)) # delete first card
assert stripHTML(c.q()) == u"e" map = {0: None, 1: 1}
assert stripHTML(c.a()) == u"r" basic.changeModel([f.id], basic, None, map)
f.load()
c0.load()
try:
c1.load()
assert 0
except TypeError:
pass
assert len(f.cards()) == 1
# an unmapped field becomes blank
assert f['Front'] == 'b'
assert f['Back'] == 'f'
basic.changeModel([f.id], basic, map, None)
f.load()
assert f['Front'] == ''
assert f['Back'] == 'f'
# another fact to try model conversion
f = deck.newFact()
f['Front'] = u'f2'
f['Back'] = u'b2'
deck.addFact(f)
assert basic.useCount() == 2
assert cloze.useCount() == 0
map = {0: 0, 1: 1}
basic.changeModel([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()