mirror of
https://github.com/ankitects/anki.git
synced 2025-11-10 22:57:11 -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
|
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
|
||||||
|
|
|
||||||
|
|
@ -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,42 +439,76 @@ 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']:
|
||||||
ok = True
|
# unsatisfiable template
|
||||||
for f in req:
|
if type == "none":
|
||||||
if not have[f]:
|
|
||||||
# missing and was required
|
|
||||||
ok = False
|
|
||||||
break
|
|
||||||
if not ok:
|
|
||||||
continue
|
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:
|
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:
|
||||||
avail.append(ord)
|
continue
|
||||||
|
avail.append(ord)
|
||||||
return avail
|
return avail
|
||||||
|
|
||||||
# Sync handling
|
# Sync handling
|
||||||
|
|
|
||||||
|
|
@ -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']
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue