make sure we add + delete cards after template changes

This commit is contained in:
Damien Elmes 2011-12-11 23:35:59 +09:00
parent 9bcfc9e1d4
commit 8be0e6cccd
5 changed files with 102 additions and 33 deletions

View file

@ -292,26 +292,31 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""",
return ok
def genCards(self, nids):
"Generate cards for non-empty templates."
"Generate cards for non-empty templates, return ids to remove."
# build map of (nid,ord) so we don't create dupes
snids = ids2str(nids)
have = {}
for nid, ord in self.db.execute(
"select nid, ord from cards where nid in "+snids):
have[(nid,ord)] = True
for id, nid, ord in self.db.execute(
"select id, nid, ord from cards where nid in "+snids):
if nid not in have:
have[nid] = {}
have[nid][ord] = id
# build cards for each note
data = []
ts = maxID(self.db)
now = intTime()
rem = []
for nid, mid, did, flds in self.db.execute(
"select id, mid, did, flds from notes where id in "+snids):
model = self.models.get(mid)
avail = self.models.availOrds(model, flds)
ok = []
for t in model['tmpls']:
if (nid,t['ord']) in have:
continue
if t['ord'] in avail:
# if have ord but empty, add cid to remove list
if t['ord'] in have[nid] and t['ord'] not in avail:
rem.append(have[nid][t['ord']])
# if missing ord and is available, generate
if t['ord'] not in have[nid] and t['ord'] in avail:
data.append((ts, nid, t['did'] or did, t['ord'],
now, nid))
ts += 1
@ -319,6 +324,7 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""",
self.db.executemany("""
insert into cards values (?,?,?,?,?,-1,0,0,?,0,0,0,0,0,0,0,"")""",
data)
return rem
# type 0 - when previewing in add dialog, only non-empty
# type 1 - when previewing edit, only existing

View file

@ -62,14 +62,14 @@ class ModelManager(object):
self.changed = False
self.models = simplejson.loads(json)
def save(self, m=None, gencards=False):
def save(self, m=None, templates=False):
"Mark M modified if provided, and schedule registry flush."
if m and m['id']:
m['mod'] = intTime()
m['usn'] = self.col.usn()
self._updateRequired(m)
if gencards:
self.col.genCards(self.nids(m))
if templates:
self._syncTemplates(m)
self.changed = True
def flush(self):
@ -346,6 +346,10 @@ update cards set ord = (case %s end),usn=?,mod=? where nid in (
select id from notes where mid = ?)""" % " ".join(map),
self.col.usn(), intTime(), m['id'])
def _syncTemplates(self, m):
rem = self.col.genCards(self.nids(m))
self.col.remCards(rem)
# Model changing
##########################################################################
# - maps are ord->ord, and there should not be duplicate targets
@ -412,9 +416,9 @@ select id from notes where mid = ?)""" % " ".join(map),
cloze = False
for t in m['tmpls']:
ret = self._reqForTemplate(m, flds, t)
if ret[1]:
if ret[2]:
cloze = True
req.append((t['ord'], ret[0], ret[1]))
req.append((t['ord'], ret[0], ret[1], ret[2]))
m['req'] = req
m['cloze'] = cloze
@ -435,42 +439,76 @@ select id from notes where mid = ?)""" % " ".join(map),
for f in flds:
a.append(cloze if cloze else "1")
b.append("")
data = [1, 1, m['id'], 1, t['ord'], "", joinFields(a)]
full = self.col._renderQA(data)['q']
data = [1, 1, m['id'], 1, t['ord'], "", joinFields(b)]
empty = self.col._renderQA(data)['q']
start = a
# if full and empty are the same, the template is invalid and there is
# no way to satisfy it
if full == empty:
return "none", [], []
type = 'all'
req = []
for i in range(len(flds)):
a = start[:]
a[i] = ""
# blank out this field
data[6] = joinFields(a)
tmp = a[:]
tmp[i] = ""
data[6] = joinFields(tmp)
# if the result is same as empty, field is required
if self.col._renderQA(data)['q'] == empty:
req.append(i)
return req, reqstrs
if req:
return type, req, reqstrs
# if there are no required fields, switch to any mode
type = 'any'
req = []
for i in range(len(flds)):
tmp = b[:]
tmp[i] = "1"
data[6] = joinFields(tmp)
# if not the same as empty, this field can make the card non-blank
if self.col._renderQA(data)['q'] != empty:
req.append(i)
return type, req, reqstrs
def availOrds(self, m, flds):
"Given a joined field string, return available template ordinals."
have = {}
fields = {}
for c, f in enumerate(splitFields(flds)):
have[c] = f.strip()
fields[c] = f.strip()
avail = []
for ord, req, reqstrs in m['req']:
ok = True
for f in req:
if not have[f]:
# missing and was required
ok = False
break
if not ok:
for ord, type, req, reqstrs in m['req']:
# unsatisfiable template
if type == "none":
continue
# AND requirement?
elif type == "all":
ok = True
for idx in req:
if not fields[idx]:
# missing and was required
ok = False
break
if not ok:
continue
# OR requirement?
elif type == "any":
ok = False
for idx in req:
if fields[idx]:
ok = True
break
if not ok:
continue
# extra cloze requirement?
ok = True
for s in reqstrs:
if s not in flds:
# required cloze string was missing
ok = False
break
if ok:
avail.append(ord)
if not ok:
continue
avail.append(ord)
return avail
# Sync handling

View file

@ -436,10 +436,10 @@ order by ordinal""", mid)):
"white-space:pre-wrap",
]
if f['rtl']:
attrs.append("direction:rtl;unicode-bidi:embed")
attrs.append("direction:rtl; unicode-bidi:embed")
attrs.append()
styles[f['name']] = '<span style="%s">{{%s}}</span>' % (
";".join(attrs), f['name'])
"; ".join(attrs), f['name'])
# obsolete
del f['qcol']
del f['qsize']

View file

@ -53,3 +53,28 @@ def test_misc():
c = f.cards()[0]
id = d.models.current()['id']
assert c.template()['ord'] == 0
def test_genrem():
d = getEmptyDeck()
f = d.newNote()
f['Front'] = u'1'
f['Back'] = u''
d.addNote(f)
assert len(f.cards()) == 1
m = d.models.current()
mm = d.models
# adding a new template should automatically create cards
t = mm.newTemplate("rev")
t['qfmt'] = '{{Front}}'
t['afmt'] = ""
mm.addTemplate(m, t)
mm.save(m, templates=True)
assert len(f.cards()) == 2
# if the template is changed to remove cards, they'll be removed
t['qfmt'] = "{{Back}}"
mm.save(m, templates=True)
assert len(f.cards()) == 1
# if we add to the note, a card should be automatically generated
f['Back'] = "1"
f.flush()
assert len(f.cards()) == 2

View file

@ -59,7 +59,7 @@ def test_noteAddDelete():
assert deck.cardCount() == 1
# but when templates are edited such as in the card layout screen, it
# should generate cards on close
mm.save(m, gencards=True)
mm.save(m, templates=True)
assert deck.cardCount() == 2
# creating new notes should use both cards
f = deck.newNote()