mirror of
https://github.com/ankitects/anki.git
synced 2025-11-10 14:47:12 -05:00
make sure we add + delete cards after template changes
This commit is contained in:
parent
9bcfc9e1d4
commit
8be0e6cccd
5 changed files with 102 additions and 33 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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']
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue