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

View file

@ -62,14 +62,14 @@ class ModelManager(object):
self.changed = False self.changed = False
self.models = simplejson.loads(json) 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." "Mark M modified if provided, and schedule registry flush."
if m and m['id']: if m and m['id']:
m['mod'] = intTime() m['mod'] = intTime()
m['usn'] = self.col.usn() m['usn'] = self.col.usn()
self._updateRequired(m) self._updateRequired(m)
if gencards: if templates:
self.col.genCards(self.nids(m)) self._syncTemplates(m)
self.changed = True self.changed = True
def flush(self): 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), select id from notes where mid = ?)""" % " ".join(map),
self.col.usn(), intTime(), m['id']) self.col.usn(), intTime(), m['id'])
def _syncTemplates(self, m):
rem = self.col.genCards(self.nids(m))
self.col.remCards(rem)
# Model changing # Model changing
########################################################################## ##########################################################################
# - maps are ord->ord, and there should not be duplicate targets # - 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 cloze = False
for t in m['tmpls']: for t in m['tmpls']:
ret = self._reqForTemplate(m, flds, t) ret = self._reqForTemplate(m, flds, t)
if ret[1]: if ret[2]:
cloze = True cloze = True
req.append((t['ord'], ret[0], ret[1])) req.append((t['ord'], ret[0], ret[1], ret[2]))
m['req'] = req m['req'] = req
m['cloze'] = cloze m['cloze'] = cloze
@ -435,41 +439,75 @@ select id from notes where mid = ?)""" % " ".join(map),
for f in flds: for f in flds:
a.append(cloze if cloze else "1") a.append(cloze if cloze else "1")
b.append("") 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)] data = [1, 1, m['id'], 1, t['ord'], "", joinFields(b)]
empty = self.col._renderQA(data)['q'] 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 = [] req = []
for i in range(len(flds)): for i in range(len(flds)):
a = start[:] tmp = a[:]
a[i] = "" tmp[i] = ""
# blank out this field data[6] = joinFields(tmp)
data[6] = joinFields(a)
# if the result is same as empty, field is required # if the result is same as empty, field is required
if self.col._renderQA(data)['q'] == empty: if self.col._renderQA(data)['q'] == empty:
req.append(i) 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): def availOrds(self, m, flds):
"Given a joined field string, return available template ordinals." "Given a joined field string, return available template ordinals."
have = {} fields = {}
for c, f in enumerate(splitFields(flds)): for c, f in enumerate(splitFields(flds)):
have[c] = f.strip() fields[c] = f.strip()
avail = [] avail = []
for ord, req, reqstrs in m['req']: for ord, type, req, reqstrs in m['req']:
# unsatisfiable template
if type == "none":
continue
# AND requirement?
elif type == "all":
ok = True ok = True
for f in req: for idx in req:
if not have[f]: if not fields[idx]:
# missing and was required # missing and was required
ok = False ok = False
break break
if not ok: if not ok:
continue 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: for s in reqstrs:
if s not in flds: if s not in flds:
# required cloze string was missing # required cloze string was missing
ok = False ok = False
break break
if ok: if not ok:
continue
avail.append(ord) avail.append(ord)
return avail return avail

View file

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

View file

@ -53,3 +53,28 @@ def test_misc():
c = f.cards()[0] c = f.cards()[0]
id = d.models.current()['id'] id = d.models.current()['id']
assert c.template()['ord'] == 0 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 assert deck.cardCount() == 1
# but when templates are edited such as in the card layout screen, it # but when templates are edited such as in the card layout screen, it
# should generate cards on close # should generate cards on close
mm.save(m, gencards=True) mm.save(m, templates=True)
assert deck.cardCount() == 2 assert deck.cardCount() == 2
# creating new notes should use both cards # creating new notes should use both cards
f = deck.newNote() f = deck.newNote()