mirror of
https://github.com/ankitects/anki.git
synced 2025-09-21 15:32:23 -04:00
template and cloze changes
While writing the documentation I realized that the default templates were somewhat overwhelming. So I've moved the default settings into the card css, and moved the css into a separate attribute which gets combined with the question and answer templates. Also: - Detect cloze references directly rather than the conditional wrapper - Add the cloze css to the template
This commit is contained in:
parent
619a58216a
commit
9e35e4acf2
6 changed files with 37 additions and 44 deletions
|
@ -101,10 +101,13 @@ lapses=?, left=?, edue=? where id = ?""",
|
||||||
self.left, self.edue, self.id)
|
self.left, self.edue, self.id)
|
||||||
|
|
||||||
def q(self, reload=False):
|
def q(self, reload=False):
|
||||||
return self._getQA(reload)['q']
|
return self.css() + self._getQA(reload)['q']
|
||||||
|
|
||||||
def a(self):
|
def a(self):
|
||||||
return self._getQA()['a']
|
return self.css() + self._getQA()['a']
|
||||||
|
|
||||||
|
def css(self):
|
||||||
|
return "<style>%s</style>" % self.template()['css']
|
||||||
|
|
||||||
def _getQA(self, reload=False):
|
def _getQA(self, reload=False):
|
||||||
if not self._qa or reload:
|
if not self._qa or reload:
|
||||||
|
|
|
@ -49,6 +49,15 @@ defaultTemplate = {
|
||||||
'qfmt': "",
|
'qfmt': "",
|
||||||
'afmt': "",
|
'afmt': "",
|
||||||
'did': None,
|
'did': None,
|
||||||
|
'css': """\
|
||||||
|
.card {
|
||||||
|
font-family: arial;
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
color: black;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
class ModelManager(object):
|
class ModelManager(object):
|
||||||
|
@ -432,7 +441,7 @@ select id from notes where mid = ?)""" % " ".join(map),
|
||||||
if cloze:
|
if cloze:
|
||||||
# need a cloze-specific filler
|
# need a cloze-specific filler
|
||||||
cloze = ""
|
cloze = ""
|
||||||
nums = re.findall("\{\{#cloze:(\d+):", t['qfmt'])
|
nums = re.findall("\{\{cloze:(\d+):", t['qfmt'])
|
||||||
for n in nums:
|
for n in nums:
|
||||||
n = int(n)
|
n = int(n)
|
||||||
cloze += "{{c%d::foo}}" % n
|
cloze += "{{c%d::foo}}" % n
|
||||||
|
|
|
@ -6,32 +6,19 @@ from anki.lang import _
|
||||||
|
|
||||||
models = []
|
models = []
|
||||||
|
|
||||||
header = """\
|
|
||||||
<style>
|
|
||||||
.card {
|
|
||||||
text-align:center;
|
|
||||||
background-color:white;
|
|
||||||
}
|
|
||||||
</style>\n\n"""
|
|
||||||
|
|
||||||
def field(name, family="arial", size=20):
|
|
||||||
return """\
|
|
||||||
<span style="font-family:%s; font-size:%spx; color:black; \
|
|
||||||
white-space:pre-wrap;">{{%s}}</span>\n""" % (family, size, name)
|
|
||||||
|
|
||||||
# Basic
|
# Basic
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def addBasicModel(col):
|
def addBasicModel(col):
|
||||||
mm = col.models
|
mm = col.models
|
||||||
m = mm.new(_("Basic")) #2 field note"))
|
m = mm.new(_("Basic"))
|
||||||
fm = mm.newField(_("Front"))
|
fm = mm.newField(_("Front"))
|
||||||
mm.addField(m, fm)
|
mm.addField(m, fm)
|
||||||
fm = mm.newField(_("Back"))
|
fm = mm.newField(_("Back"))
|
||||||
mm.addField(m, fm)
|
mm.addField(m, fm)
|
||||||
t = mm.newTemplate(_("Forward"))
|
t = mm.newTemplate(_("Forward"))
|
||||||
t['qfmt'] = header + field(_("Front"))
|
t['qfmt'] = "{{Front}}"
|
||||||
t['afmt'] = t['qfmt'] + "\n\n<hr id=answerStart>\n\n" + field(_("Back"))
|
t['afmt'] = t['qfmt'] + "\n\n<hr id=answer>\n\n{{Back}}"
|
||||||
mm.addTemplate(m, t)
|
mm.addTemplate(m, t)
|
||||||
mm.add(m)
|
mm.add(m)
|
||||||
return m
|
return m
|
||||||
|
@ -51,10 +38,14 @@ def addClozeModel(col):
|
||||||
for i in range(8):
|
for i in range(8):
|
||||||
n = i+1
|
n = i+1
|
||||||
t = mm.newTemplate(_("Cloze") + " %d" % n)
|
t = mm.newTemplate(_("Cloze") + " %d" % n)
|
||||||
t['qfmt'] = header + ("{{#cloze:%d:Text}}\n"+
|
fmt = "{{cloze:%d:Text}}%%s" % n
|
||||||
field("cloze:%d:Text" % n)+
|
t['css'] += """
|
||||||
"{{/cloze:%d:Text}}") % (n, n)
|
.cloze {
|
||||||
t['afmt'] = t['qfmt'] + "<br>\n"+field("Notes")
|
font-weight: bold;
|
||||||
|
color: blue;
|
||||||
|
}"""
|
||||||
|
t['qfmt'] = fmt % ""
|
||||||
|
t['afmt'] = fmt % "\n{{Notes}}"
|
||||||
mm.addTemplate(m, t)
|
mm.addTemplate(m, t)
|
||||||
mm.add(m)
|
mm.add(m)
|
||||||
return m
|
return m
|
||||||
|
|
|
@ -138,10 +138,7 @@ class Template(object):
|
||||||
# {{{ functions just like {{ in anki
|
# {{{ functions just like {{ in anki
|
||||||
@modifier('{')
|
@modifier('{')
|
||||||
def render_tag(self, tag_name, context):
|
def render_tag(self, tag_name, context):
|
||||||
raw = get_or_attr(context, tag_name, '')
|
return render_unescaped(tag_name, context)
|
||||||
if not raw and raw is not 0:
|
|
||||||
return ''
|
|
||||||
return raw
|
|
||||||
|
|
||||||
@modifier('!')
|
@modifier('!')
|
||||||
def render_comment(self, tag_name=None, context=None):
|
def render_comment(self, tag_name=None, context=None):
|
||||||
|
@ -152,6 +149,7 @@ class Template(object):
|
||||||
def render_unescaped(self, tag_name=None, context=None):
|
def render_unescaped(self, tag_name=None, context=None):
|
||||||
"""Render a tag without escaping it."""
|
"""Render a tag without escaping it."""
|
||||||
if tag_name.startswith("text:"):
|
if tag_name.startswith("text:"):
|
||||||
|
# strip html
|
||||||
tag = tag_name[5:]
|
tag = tag_name[5:]
|
||||||
txt = get_or_attr(context, tag)
|
txt = get_or_attr(context, tag)
|
||||||
if txt:
|
if txt:
|
||||||
|
@ -163,12 +161,14 @@ class Template(object):
|
||||||
return "[[%s]]" % tag_name
|
return "[[%s]]" % tag_name
|
||||||
elif (tag_name.startswith("cq:") or
|
elif (tag_name.startswith("cq:") or
|
||||||
tag_name.startswith("ca:")):
|
tag_name.startswith("ca:")):
|
||||||
|
# cloze deletion
|
||||||
m = re.match("c(.+):(\d+):(.+)", tag_name)
|
m = re.match("c(.+):(\d+):(.+)", tag_name)
|
||||||
(type, ord, tag) = (m.group(1), m.group(2), m.group(3))
|
(type, ord, tag) = (m.group(1), m.group(2), m.group(3))
|
||||||
txt = get_or_attr(context, tag)
|
txt = get_or_attr(context, tag)
|
||||||
if txt:
|
if txt:
|
||||||
return self.clozeText(txt, ord, type)
|
return self.clozeText(txt, ord, type)
|
||||||
return ""
|
return ""
|
||||||
|
# regular field
|
||||||
return get_or_attr(context, tag_name, '{unknown field %s}' % tag_name)
|
return get_or_attr(context, tag_name, '{unknown field %s}' % tag_name)
|
||||||
|
|
||||||
def clozeText(self, txt, ord, type):
|
def clozeText(self, txt, ord, type):
|
||||||
|
|
|
@ -411,7 +411,7 @@ order by ordinal""", mid)):
|
||||||
# q fields now in a
|
# q fields now in a
|
||||||
if not hideq:
|
if not hideq:
|
||||||
conf['afmt'] = (
|
conf['afmt'] = (
|
||||||
conf['qfmt'] + "\n\n<hr id=answerStart>\n\n" + conf['afmt'])
|
conf['qfmt'] + "\n\n<hr id=answer>\n\n" + conf['afmt'])
|
||||||
tmpls.append(conf)
|
tmpls.append(conf)
|
||||||
return tmpls
|
return tmpls
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,7 @@ def test_cloze():
|
||||||
f = d.newNote()
|
f = d.newNote()
|
||||||
f['Text'] = "hello {{c1::world::typical}}"
|
f['Text'] = "hello {{c1::world::typical}}"
|
||||||
assert d.addNote(f) == 1
|
assert d.addNote(f) == 1
|
||||||
assert "<span class=cloze>[...(typical)]</span>" in f.cards()[0].q()
|
assert "<span class=cloze>[...typical]</span>" in f.cards()[0].q()
|
||||||
assert "<span class=cloze>world</span>" in f.cards()[0].a()
|
assert "<span class=cloze>world</span>" in f.cards()[0].a()
|
||||||
# and with 2 clozes
|
# and with 2 clozes
|
||||||
f = d.newNote()
|
f = d.newNote()
|
||||||
|
@ -150,16 +150,6 @@ def test_cloze():
|
||||||
assert d.addNote(f) == 1
|
assert d.addNote(f) == 1
|
||||||
assert "<span class=cloze>b</span> <span class=cloze>c</span>" in (
|
assert "<span class=cloze>b</span> <span class=cloze>c</span>" in (
|
||||||
f.cards()[0].a())
|
f.cards()[0].a())
|
||||||
# clozes should be supported in sections too
|
|
||||||
m = d.models.current()
|
|
||||||
m['tmpls'][0]['qfmt'] = "{{#cloze:1:Text}}{{Notes}}{{/cloze:1:Text}}"
|
|
||||||
d.models.save(m)
|
|
||||||
f = d.newNote()
|
|
||||||
f['Text'] = "hello"
|
|
||||||
f['Notes'] = "world"
|
|
||||||
assert d.addNote(f) == 0
|
|
||||||
f['Text'] = "hello {{c1::foo}}"
|
|
||||||
assert d.addNote(f) == 1
|
|
||||||
# if we add another cloze, a card should be generated
|
# if we add another cloze, a card should be generated
|
||||||
cnt = d.cardCount()
|
cnt = d.cardCount()
|
||||||
f['Text'] = "{{c2::hello}} {{c1::foo}}"
|
f['Text'] = "{{c2::hello}} {{c1::foo}}"
|
||||||
|
@ -179,25 +169,25 @@ def test_modelChange():
|
||||||
mm.save(m)
|
mm.save(m)
|
||||||
f = deck.newNote()
|
f = deck.newNote()
|
||||||
f['Front'] = u'f'
|
f['Front'] = u'f'
|
||||||
f['Back'] = u'b'
|
f['Back'] = u'b123'
|
||||||
deck.addNote(f)
|
deck.addNote(f)
|
||||||
# switch fields
|
# switch fields
|
||||||
map = {0: 1, 1: 0}
|
map = {0: 1, 1: 0}
|
||||||
deck.models.change(basic, [f.id], basic, map, None)
|
deck.models.change(basic, [f.id], basic, map, None)
|
||||||
f.load()
|
f.load()
|
||||||
assert f['Front'] == 'b'
|
assert f['Front'] == 'b123'
|
||||||
assert f['Back'] == 'f'
|
assert f['Back'] == 'f'
|
||||||
# switch cards
|
# switch cards
|
||||||
c0 = f.cards()[0]
|
c0 = f.cards()[0]
|
||||||
c1 = f.cards()[1]
|
c1 = f.cards()[1]
|
||||||
assert ">b<" in c0.q()
|
assert "b123" in c0.q()
|
||||||
assert "f" in c1.q()
|
assert "f" in c1.q()
|
||||||
assert c0.ord == 0
|
assert c0.ord == 0
|
||||||
assert c1.ord == 1
|
assert c1.ord == 1
|
||||||
deck.models.change(basic, [f.id], basic, None, map)
|
deck.models.change(basic, [f.id], basic, None, map)
|
||||||
f.load(); c0.load(); c1.load()
|
f.load(); c0.load(); c1.load()
|
||||||
assert "f" in c0.q()
|
assert "f" in c0.q()
|
||||||
assert ">b<" in c1.q()
|
assert "b123" in c1.q()
|
||||||
assert c0.ord == 1
|
assert c0.ord == 1
|
||||||
assert c1.ord == 0
|
assert c1.ord == 0
|
||||||
# .cards() returns cards in order
|
# .cards() returns cards in order
|
||||||
|
@ -214,7 +204,7 @@ def test_modelChange():
|
||||||
pass
|
pass
|
||||||
assert len(f.cards()) == 1
|
assert len(f.cards()) == 1
|
||||||
# an unmapped field becomes blank
|
# an unmapped field becomes blank
|
||||||
assert f['Front'] == 'b'
|
assert f['Front'] == 'b123'
|
||||||
assert f['Back'] == 'f'
|
assert f['Back'] == 'f'
|
||||||
deck.models.change(basic, [f.id], basic, map, None)
|
deck.models.change(basic, [f.id], basic, map, None)
|
||||||
f.load()
|
f.load()
|
||||||
|
|
Loading…
Reference in a new issue