mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 08:46:37 -04:00
cache required fields, drop emptyAns
For importing and the deck creation wizard, we need to be able to generate thousands of cards efficiently. So instead of requiring the creation of a fact and rendering it, we cache the required fields and cloze references in the model. Also, emptyAns is dropped, as people can achieve the same behaviour by adding the required answer fields as conditional to the question. Todo: refactor genCards() to work in bulk, handle cloze edits intelligently (prompt to delete invalid references, create new cards as necessary)
This commit is contained in:
parent
8bb26aaf74
commit
a4e8eb8b74
3 changed files with 76 additions and 19 deletions
22
anki/deck.py
22
anki/deck.py
|
@ -5,7 +5,7 @@
|
|||
import time, os, random, re, stat, simplejson, datetime, copy, shutil
|
||||
from anki.lang import _, ngettext
|
||||
from anki.utils import ids2str, hexifyID, checksum, fieldChecksum, stripHTML, \
|
||||
intTime, splitFields
|
||||
intTime, splitFields, joinFields
|
||||
from anki.hooks import runHook, runFilter
|
||||
from anki.sched import Scheduler
|
||||
from anki.models import ModelManager
|
||||
|
@ -272,20 +272,12 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""",
|
|||
"Return (active), non-empty templates."
|
||||
ok = []
|
||||
model = fact.model()
|
||||
for template in model['tmpls']:
|
||||
if template['actv'] or not checkActive:
|
||||
# [cid, fid, mid, gid, ord, tags, flds]
|
||||
data = [1, 1, model['id'], 1, template['ord'],
|
||||
"", fact.joinedFields()]
|
||||
now = self._renderQA(data)
|
||||
data[6] = "\x1f".join([""]*len(fact.fields))
|
||||
empty = self._renderQA(data)
|
||||
if now['q'] == empty['q']:
|
||||
continue
|
||||
if not template['emptyAns']:
|
||||
if now['a'] == empty['a']:
|
||||
continue
|
||||
ok.append(template)
|
||||
avail = self.models.availOrds(model, joinFields(fact.fields))
|
||||
ok = []
|
||||
for t in model['tmpls']:
|
||||
if t['actv'] or not checkActive:
|
||||
if t['ord'] in avail:
|
||||
ok.append(t)
|
||||
return ok
|
||||
|
||||
def genCards(self, fact, templates):
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# Copyright: Damien Elmes <anki@ichi2.net>
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
import simplejson, copy
|
||||
import simplejson, copy, re
|
||||
from anki.utils import intTime, hexifyID, joinFields, splitFields, ids2str, \
|
||||
timestampID, fieldChecksum
|
||||
from anki.lang import _
|
||||
|
@ -56,7 +56,6 @@ defaultTemplate = {
|
|||
'hideQ': False,
|
||||
'align': 0,
|
||||
'bg': "#fff",
|
||||
'emptyAns': True,
|
||||
'typeAns': None,
|
||||
'gid': None,
|
||||
}
|
||||
|
@ -80,6 +79,7 @@ class ModelManager(object):
|
|||
m['mod'] = intTime()
|
||||
m['usn'] = self.deck.usn()
|
||||
m['css'] = self._css(m)
|
||||
self._updateRequired(m)
|
||||
self.changed = True
|
||||
|
||||
def flush(self):
|
||||
|
@ -430,3 +430,69 @@ select id from facts where mid = ?)""" % " ".join(map),
|
|||
for t in m['tmpls']:
|
||||
s += t['name']
|
||||
return fieldChecksum(s)
|
||||
|
||||
# Required field/text cache
|
||||
##########################################################################
|
||||
|
||||
def _updateRequired(self, m):
|
||||
req = []
|
||||
flds = [f['name'] for f in m['flds']]
|
||||
for t in m['tmpls']:
|
||||
ret = self._reqForTemplate(m, flds, t)
|
||||
req.append((t['ord'], ret[0], ret[1]))
|
||||
m['req'] = req
|
||||
|
||||
def _reqForTemplate(self, m, flds, t):
|
||||
a = []
|
||||
b = []
|
||||
cloze = "cloze" in t['qfmt']
|
||||
reqstrs = []
|
||||
if cloze:
|
||||
# need a cloze-specific filler
|
||||
cloze = ""
|
||||
nums = re.findall("\{\{#cloze:(\d+):", t['qfmt'])
|
||||
for n in nums:
|
||||
n = int(n)
|
||||
cloze += "{{c%d::foo}}" % n
|
||||
# record that we require a specific string for generation
|
||||
reqstrs.append("{{c%d::" % n)
|
||||
for f in flds:
|
||||
a.append(cloze if cloze else "1")
|
||||
b.append("")
|
||||
data = [1, 1, m['id'], 1, t['ord'], "", joinFields(b)]
|
||||
empty = self.deck._renderQA(data)['q']
|
||||
start = a
|
||||
req = []
|
||||
for i in range(len(flds)):
|
||||
a = start[:]
|
||||
a[i] = ""
|
||||
# blank out this field
|
||||
data[6] = joinFields(a)
|
||||
# if the result is same as empty, field is required
|
||||
if self.deck._renderQA(data)['q'] == empty:
|
||||
req.append(i)
|
||||
return req, reqstrs
|
||||
|
||||
def availOrds(self, m, flds):
|
||||
"Given a joined field string, return available template ordinals."
|
||||
have = {}
|
||||
for c, f in enumerate(splitFields(flds)):
|
||||
have[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:
|
||||
continue
|
||||
for s in reqstrs:
|
||||
if s not in flds:
|
||||
# required cloze string was missing
|
||||
ok = False
|
||||
break
|
||||
if ok:
|
||||
avail.append(ord)
|
||||
return avail
|
||||
|
|
|
@ -375,7 +375,7 @@ order by ordinal""", mid)):
|
|||
tmpls = []
|
||||
for c, row in enumerate(db.all("""
|
||||
select name, active, qformat, aformat, questionInAnswer,
|
||||
questionAlign, lastFontColour, allowEmptyAnswer, typeAnswer from cardModels
|
||||
questionAlign, lastFontColour, typeAnswer from cardModels
|
||||
where modelId = ?
|
||||
order by ordinal""", mid)):
|
||||
conf = dconf.copy()
|
||||
|
@ -386,7 +386,6 @@ order by ordinal""", mid)):
|
|||
conf['hideQ'],
|
||||
conf['align'],
|
||||
conf['bg'],
|
||||
conf['emptyAns'],
|
||||
conf['typeAns']) = row
|
||||
conf['ord'] = c
|
||||
# convert the field name to an ordinal
|
||||
|
|
Loading…
Reference in a new issue