model changing support

This commit is contained in:
Damien Elmes 2009-02-04 20:43:54 +09:00
parent b1835ff71b
commit d44c3792e9
2 changed files with 118 additions and 80 deletions

View file

@ -1108,81 +1108,6 @@ facts.id = cards.factId""", id=model.id))
if not self.modelUseCount(model): if not self.modelUseCount(model):
self.deleteModel(model) self.deleteModel(model)
def modelsGroupedByName(self):
"Return hash of name -> [id, cardModelIds, fieldIds]"
l = self.s.all("select name, id from models where source = 0"
" order by created")
models = {}
for m in l:
cms = self.s.column0("""
select id from cardModels where modelId = :id order by ordinal""", id=m[1])
fms = self.s.column0("""
select id from fieldModels where modelId = :id order by ordinal""", id=m[1])
if m[0] in models:
models[m[0]].append((m[1], cms, fms))
else:
models[m[0]] = [(m[1], cms, fms)]
return models
def canMergeModels(self):
models = self.modelsGroupedByName()
toProcess = []
msg = ""
for (name, ids) in models.items():
if len(ids) > 1:
cms = len(ids[0][1])
fms = len(ids[0][2])
for id in ids[1:]:
if len(id[1]) != cms:
msg = (_(
"Model '%s' has wrong card template count") % name)
break
if len(id[2]) != fms:
msg = (_(
"Model '%s' has wrong field count") % name)
break
toProcess.append((name, ids))
if msg:
return ("no", msg)
return ("ok", toProcess)
def mergeModels(self, toProcess):
"Merge models. Caller must call refresh()."
for (name, ids) in toProcess:
(id1, cms1, fms1) = ids[0]
for (id2, cms2, fms2) in ids[1:]:
self.mergeModel((id1, cms1, fms1),
(id2, cms2, fms2))
def mergeModel(self, m1, m2):
"Given two model ids, merge m2 into m1."
(id1, cms1, fms1) = m1
(id2, cms2, fms2) = m2
self.s.flush()
# cards
for n in range(len(cms1)):
self.s.statement("""
update cards set
modified = strftime("%s", "now"),
cardModelId = :new where cardModelId = :old""",
new=cms1[n], old=cms2[n])
# facts
self.s.statement("""
update facts set
modified = strftime("%s", "now"),
modelId = :new where modelId = :old""",
new=id1, old=id2)
# fields
for n in range(len(fms1)):
self.s.statement("""
update fields set
fieldModelId = :new where fieldModelId = :old""",
new=fms1[n], old=fms2[n])
# delete m2
model = [m for m in self.models if m.id == id2][0]
self.deleteModel(model)
self.refresh()
def rebuildCSS(self): def rebuildCSS(self):
# css for all fields # css for all fields
def _genCSS(prefix, row): def _genCSS(prefix, row):
@ -1228,6 +1153,79 @@ select id, lastFontColour from cardModels""")])
self.addModel(m) self.addModel(m)
return m return m
def changeModel(self, factIds, newModel, fieldMap, cardMap):
"Caller must call reset."
self.s.flush()
fids = ids2str(factIds)
changed = False
# field remapping
if fieldMap:
changed = True
self.startProgress(len(fieldMap)+1, title)
seen = {}
for (old, new) in fieldMap.items():
self.updateProgress(_("Changing fields..."))
seen[new] = 1
if new:
# can rename
self.s.statement("""
update fields set
fieldModelId = :new,
ordinal = :ord
where fieldModelId = :old
and factId in %s""" % fids, new=new.id, ord=new.ordinal, old=old.id)
else:
# no longer used
self.s.statement("""
delete from fields where factId in %s
and fieldModelId = :id""" % fids, id=old.id)
# new
for field in newModel.fieldModels:
self.updateProgress()
if field not in seen:
d = [{'id': genID(),
'fid': f,
'fmid': field.id,
'ord': field.ordinal}
for f in factIds]
self.s.statements('''
insert into fields
(id, factId, fieldModelId, ordinal, value)
values
(:id, :fid, :fmid, :ord, ""''', d)
# fact modtime
self.updateProgress()
self.s.statement("""
update facts set
modified = :t,
modelId = :id
where id in %s""" % fids, t=time.time(), id=newModel.id)
self.finishProgress()
# template remapping
self.startProgress(len(cardMap)+2)
for (old, new) in cardMap.items():
self.updateProgress(_("Changing cards..."))
if not new:
# delete
self.s.statement("""
delete from cards
where cardModelId = :cid and
factId in %s""" % fids, cid=old.id)
elif old != new:
# change
self.s.statement("""
update cards set
cardModelId = :new,
ordinal = :ord
where cardModelId = :old
and factId in %s""" % fids, new=new.id, old=old.id, ord=new.ordinal)
self.updateProgress()
self.updateCardQACacheFromIds(factIds, type="facts")
self.flushMod()
self.updateProgress()
self.rebuildCounts()
self.finishProgress()
# Fields # Fields
########################################################################## ##########################################################################
@ -1331,15 +1329,15 @@ facts.modelId = :id""", id=model.id)
def updateCardQACacheFromIds(self, ids, type="cards"): def updateCardQACacheFromIds(self, ids, type="cards"):
"Given a list of card or fact ids, update q/a cache." "Given a list of card or fact ids, update q/a cache."
if type == "cards": if type == "facts":
col = "c.id" # convert to card ids
else: ids = self.s.column0(
col = "f.id" "select id from cards where factId in %s" % ids2str(ids))
rows = self.s.all(""" rows = self.s.all("""
select c.id, c.cardModelId, f.id, f.modelId select c.id, c.cardModelId, f.id, f.modelId
from cards as c, facts as f from cards as c, facts as f
where c.factId = f.id where c.factId = f.id
and %s in %s""" % (col, ids2str(ids))) and c.id in %s""" % ids2str(ids))
self.updateCardQACache(rows) self.updateCardQACache(rows)
def updateCardQACache(self, ids, dirty=True): def updateCardQACache(self, ids, dirty=True):

View file

@ -8,6 +8,7 @@ from anki import DeckStorage
from anki.db import * from anki.db import *
from anki.models import FieldModel from anki.models import FieldModel
from anki.stdmodels import JapaneseModel, BasicModel from anki.stdmodels import JapaneseModel, BasicModel
from anki.utils import stripHTML
newPath = None newPath = None
newModified = None newModified = None
@ -171,3 +172,42 @@ def test_media():
pass pass
deck.saveAs(path) deck.saveAs(path)
assert os.path.exists("/tmp/saveAs2.media/%s" % sum) assert os.path.exists("/tmp/saveAs2.media/%s" % sum)
def test_modelChange():
deck = DeckStorage.Deck()
m1 = JapaneseModel()
m1.cardModels[1].active = True
deck.addModel(m1)
f = deck.newFact()
f['Expression'] = u'e'
f['Meaning'] = u'm'
f['Reading'] = u'r'
f = deck.addFact(f)
f2 = deck.newFact()
f2['Expression'] = u'e2'
f2['Meaning'] = u'm2'
f2['Reading'] = u'r2'
deck.addFact(f2)
m2 = BasicModel()
m2.cardModels[1].active = True
deck.addModel(m2)
# convert to basic
assert deck.modelUseCount(m1) == 2
assert deck.modelUseCount(m2) == 0
assert deck.cardCount == 4
assert deck.factCount == 2
fmap = {m1.fieldModels[0]: m2.fieldModels[0],
m1.fieldModels[1]: None,
m1.fieldModels[2]: m2.fieldModels[1]}
cmap = {m1.cardModels[0]: m2.cardModels[0],
m1.cardModels[1]: None}
deck.changeModel([f.id], m2, fmap, cmap)
assert deck.modelUseCount(m1) == 1
assert deck.modelUseCount(m2) == 1
assert deck.cardCount == 3
assert deck.factCount == 2
(q, a) = deck.s.first("""
select question, answer from cards where factId = :id""",
id=f.id)
assert stripHTML(q) == u"e"
assert stripHTML(a) == u"r"