cloze refactor wip

This commit is contained in:
Damien Elmes 2012-04-18 16:07:42 +09:00
parent 20ecd7359d
commit 41fa9a9896
6 changed files with 76 additions and 55 deletions

View file

@ -4,6 +4,7 @@
import time
from anki.utils import intTime, hexifyID, timestampID
from anki.consts import *
# Cards
##########################################################################
@ -129,7 +130,11 @@ lapses=?, left=?, odue=?, odid=?, did=? where id = ?""",
return self.col.models.get(self.note().mid)
def template(self):
return self.model()['tmpls'][self.ord]
m = self.model()
if m['type'] == MODEL_STD:
return self.model()['tmpls'][self.ord]
else:
return self.model()['tmpls'][0]
def startTimer(self):
self.timerStarted = time.time()

View file

@ -266,9 +266,19 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""",
"Return (active), non-empty templates."
model = note.model()
avail = self.models.availOrds(model, joinFields(note.fields))
return self._tmplsFromOrds(model, avail)
def _tmplsFromOrds(self, model, avail):
ok = []
for t in model['tmpls']:
if t['ord'] in avail:
if model['type'] == MODEL_STD:
for t in model['tmpls']:
if t['ord'] in avail:
ok.append(t)
else:
# cloze - generate temporary templates from first
for ord in avail:
t = copy.copy(model['tmpls'][0])
t['ord'] = ord
ok.append(t)
return ok
@ -304,20 +314,21 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""",
model = self.models.get(mid)
avail = self.models.availOrds(model, flds)
did = dids.get(nid) or model['did']
for t in model['tmpls']:
# add any missing cards
for t in self._tmplsFromOrds(model, avail):
doHave = nid in have and t['ord'] in have[nid]
# if have ord but empty, add cid to remove list
# (may not have nid if generating before any cards added)
if doHave and t['ord'] not in avail:
rem.append(have[nid][t['ord']])
# if missing ord and is available, generate
if not doHave and t['ord'] in avail:
if not doHave:
# we'd like to use the same due# as sibling cards, but we
# can't retrieve that quickly, so we give it a new id
# instead
data.append((ts, nid, t['did'] or did, t['ord'],
now, usn, self.nextID("pos")))
ts += 1
# note any cards that need removing
if nid in have:
for ord, id in have[nid].items():
if ord not in avail:
rem.append(id)
# bulk update
self.db.executemany("""
insert into cards values (?,?,?,?,?,?,0,0,?,0,0,0,0,0,0,0,0,"")""",
@ -440,20 +451,28 @@ select id from notes where id in %s and id not in (select nid from cards)""" %
flist = splitFields(data[6])
fields = {}
model = self.models.get(data[2])
firstName = None
for (name, (idx, conf)) in self.models.fieldMap(model).items():
if idx == 0:
firstName = name
fields[name] = flist[idx]
fields['Tags'] = data[5]
fields['Type'] = model['name']
fields['Deck'] = self.decks.name(data[3])
template = model['tmpls'][data[4]]
if model['type'] == MODEL_STD:
template = model['tmpls'][data[4]]
else:
template = model['tmpls'][0]
fields['Card'] = template['name']
# render q & a
d = dict(id=data[0])
for (type, format) in (("q", template['qfmt']), ("a", template['afmt'])):
if type == "q":
format = format.replace("cloze:", "cq:")
format = format.replace("{{Cloze}}", "{{cq:%d:%s}}" % (
data[4]+1, firstName))
else:
format = format.replace("cloze:", "ca:")
format = format.replace("{{Cloze}}", "{{ca:%d:%s}}" % (
data[4]+1, firstName))
fields = runFilter("mungeFields", fields, model, data, self)
html = anki.template.render(format, fields)
d[type] = runFilter(

View file

@ -37,11 +37,15 @@ DYN_FAILED = 5
DYN_ADDED = 6
DYN_DUE = 7
# model types
MODEL_STD = 0
MODEL_CLOZE = 1
# deck schema & syncing vars
SCHEMA_VERSION = 3
SCHEMA_VERSION = 4
SYNC_ZIP_SIZE = int(2.5*1024*1024)
SYNC_URL = os.environ.get("SYNC_URL") or "https://beta.ankiweb.net/sync/"
SYNC_VER = 1
SYNC_VER = 2
# Labels
##########################################################################

View file

@ -28,6 +28,7 @@ defaultModel = {
'mod': 0,
'usn': 0,
'vers': [],
'type': MODEL_STD,
}
defaultField = {
@ -425,34 +426,21 @@ select id from notes where mid = ?)""" % " ".join(map),
##########################################################################
def _updateRequired(self, m):
if m['type'] == MODEL_CLOZE:
# nothing to do
return
req = []
flds = [f['name'] for f in m['flds']]
cloze = False
for t in m['tmpls']:
ret = self._reqForTemplate(m, flds, t)
if ret[2]:
cloze = True
req.append((t['ord'], ret[0], ret[1], ret[2]))
req.append((t['ord'], ret[0], ret[1]))
m['req'] = req
m['cloze'] = cloze
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)
return 'all', [], reqstrs
for f in flds:
a.append(cloze if cloze else "1")
a.append("1")
b.append("")
data = [1, 1, m['id'], 1, t['ord'], "", joinFields(a)]
full = self.col._renderQA(data)['q']
@ -472,7 +460,7 @@ select id from notes where mid = ?)""" % " ".join(map),
if self.col._renderQA(data)['q'] == empty:
req.append(i)
if req:
return type, req, reqstrs
return type, req
# if there are no required fields, switch to any mode
type = 'any'
req = []
@ -483,15 +471,17 @@ select id from notes where mid = ?)""" % " ".join(map),
# 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
return type, req
def availOrds(self, m, flds):
"Given a joined field string, return available template ordinals."
if m['type'] == MODEL_CLOZE:
return self._availClozeOrds(m, flds)
fields = {}
for c, f in enumerate(splitFields(flds)):
fields[c] = f.strip()
avail = []
for ord, type, req, reqstrs in m['req']:
for ord, type, req in m['req']:
# unsatisfiable template
if type == "none":
continue
@ -514,18 +504,14 @@ select id from notes where mid = ?)""" % " ".join(map),
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 not ok:
continue
avail.append(ord)
return avail
def _availClozeOrds(self, m, flds):
ret = [int(m)-1 for m in re.findall(
"{{c(\d)::[^}]*?}}", splitFields(flds)[0])]
return list(set(ret))
# Sync handling
##########################################################################

View file

@ -3,6 +3,7 @@
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from anki.lang import _
from anki.consts import MODEL_CLOZE
models = []
@ -31,20 +32,21 @@ models.append((_("Basic"), addBasicModel))
def addClozeModel(col):
mm = col.models
m = mm.new(_("Cloze"))
fm = mm.newField(("Text"))
m['type'] = MODEL_CLOZE
fm = mm.newField(_("Text"))
mm.addField(m, fm)
for i in range(8):
n = i+1
t = mm.newTemplate(_("Cloze") + " %d" % n)
fmt = "{{cloze:%d:Text}}" % n
t['css'] += """
fm = mm.newField(_("Extra"))
mm.addField(m, fm)
t = mm.newTemplate(_("Cloze"))
fmt = "{{Cloze}}"
t['css'] += """
.cloze {
font-weight: bold;
color: blue;
}"""
t['qfmt'] = fmt
t['afmt'] = fmt
mm.addTemplate(m, t)
t['qfmt'] = fmt
t['afmt'] = fmt + "<br>\n{{%s}}" % _("Extra")
mm.addTemplate(m, t)
mm.add(m)
return m

View file

@ -44,7 +44,6 @@ def Collection(path, lock=True, server=False, sync=True):
col.lock()
return col
# no upgrades necessary at the moment
def _upgradeSchema(db):
ver = db.scalar("select ver from col")
if ver == SCHEMA_VERSION:
@ -81,6 +80,12 @@ def _upgrade(col, ver):
d['dyn'] = 0
d['collapsed'] = False
col.decks.save(d)
if ver < 4:
for m in col.models.all():
if not "{{cloze::" in m['tmpls'][0]['qfmt']:
m['type'] = MODEL_STD
else:
pass
# Creating a new collection
######################################################################