mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
cloze refactor wip
This commit is contained in:
parent
20ecd7359d
commit
41fa9a9896
6 changed files with 76 additions and 55 deletions
|
@ -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):
|
||||
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()
|
||||
|
|
|
@ -266,10 +266,20 @@ 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 = []
|
||||
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
|
||||
|
||||
def genCards(self, nids):
|
||||
|
@ -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])
|
||||
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(
|
||||
|
|
|
@ -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
|
||||
##########################################################################
|
||||
|
|
|
@ -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
|
||||
##########################################################################
|
||||
|
||||
|
|
|
@ -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,19 +32,20 @@ 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
|
||||
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
|
||||
t['afmt'] = fmt + "<br>\n{{%s}}" % _("Extra")
|
||||
mm.addTemplate(m, t)
|
||||
mm.add(m)
|
||||
return m
|
||||
|
|
|
@ -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
|
||||
######################################################################
|
||||
|
|
Loading…
Reference in a new issue