Skip to content

Commit

Permalink
Add "Back Extra" field to Cloze builtin model (#101)
Browse files Browse the repository at this point in the history
Due to a mistake, the "Back Extra" field was omitted from `genanki.builtin_models.CLOZE_MODEL`. This field shows extra information on the back of the card.

Add this field to `CLOZE_MODEL`. Also, add some code that runs inside Note.write_to_db that checks if `.fields` has only one element and `.model` is `CLOZE_MODEL`, and if so, fixes `.fields` and emits a warning.

This change may cause minor backwards-incompatibility issues, because the ID of `CLOZE_MODEL` has changed. The only visible effect should be that a deck of Cloze notes created before this change and a similar deck created after this change will have different Note Types when both are imported into Anki. (Note Type is the Anki UI's term for what the source code calls Model).
  • Loading branch information
kerrickstaley authored Jan 4, 2022
1 parent e308e37 commit 9ba9392
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 4 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,15 @@ for fields.
may work but has not been tested). See the [`.write_to_collection_from_addon() method`](
https://github.com/kerrickstaley/genanki/blob/0c2cf8fea9c5e382e2fae9cd6d5eb440e267c637/genanki/__init__.py#L275).

## CLOZE_MODEL DeprecationWarning
Due to a mistake, in genanki versions before 0.13.0, `builtin_models.CLOZE_MODEL` only had a single field, whereas the real Cloze model that is built into Anki has two fields. If you get a `DeprecationWarning` when using `CLOZE_MODEL`, simply add another field (it can be an empty string) when creating your `Note`, e.g.

```python
my_note = genanki.Note(
model=genanki.CLOZE_MODEL,
fields=['{{c1::Rome}} is the capital of {{c2::Italy}}', ''])
```

## FAQ
### My field data is getting garbled
If fields in your notes contain literal `<`, `>`, or `&` characters, you need to HTML-encode them: field data is HTML, not plain text. You can use the [`html.escape`](https://docs.python.org/3/library/html.html#html.escape) function.
Expand Down
22 changes: 20 additions & 2 deletions genanki/builtin_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
etc., which is less confusing.
"""

import warnings

from .model import Model


Expand Down Expand Up @@ -120,22 +122,38 @@
)

CLOZE_MODEL = Model(
1122529321,
1550428389,
'Cloze (genanki)',
model_type=Model.CLOZE,
fields=[
{
'name': 'Text',
'font': 'Arial',
},
{
'name': 'Back Extra',
'font': 'Arial',
},
],
templates=[
{
'name': 'Cloze',
'qfmt': '{{cloze:Text}}',
'afmt': '{{cloze:Text}}',
'afmt': '{{cloze:Text}}<br>\n{{Back Extra}}',
},
],
css='.card {\n font-family: arial;\n font-size: 20px;\n text-align: center;\n color: black;\n background-color: white;\n}\n\n'
'.cloze {\n font-weight: bold;\n color: blue;\n}\n.nightMode .cloze {\n color: lightblue;\n}',
)

def _fix_deprecated_builtin_models_and_warn(model, fields):
if model is CLOZE_MODEL and len(fields) == 1:
fixed_fields = fields + ['']
warnings.warn(
'Using CLOZE_MODEL with a single field is deprecated.'
+ ' Please pass two fields, e.g. {} .'.format(repr(fixed_fields))
+ ' See https://github.com/kerrickstaley/genanki#cloze_model-deprecationwarning .',
DeprecationWarning)
return fixed_fields

return fields
2 changes: 2 additions & 0 deletions genanki/note.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import warnings
from cached_property import cached_property

from .builtin_models import _fix_deprecated_builtin_models_and_warn
from .card import Card
from .util import guid_for

Expand Down Expand Up @@ -148,6 +149,7 @@ def _check_invalid_html_tags_in_fields(self):
" your field data isn't already HTML-encoded: {}".format(' '.join(invalid_tags)))

def write_to_db(self, cursor, timestamp: float, deck_id, id_gen):
self.fields = _fix_deprecated_builtin_models_and_warn(self.model, self.fields)
self._check_number_model_fields_matches_num_fields()
self._check_invalid_html_tags_in_fields()
cursor.execute('INSERT INTO notes VALUES(?,?,?,?,?,?,?,?,?,?,?);', (
Expand Down
30 changes: 28 additions & 2 deletions tests/test_builtin_models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import genanki
import os
import pytest
import tempfile
import warnings


def test_builtin_models():
Expand All @@ -24,13 +26,37 @@ def test_builtin_models():
model=genanki.BASIC_TYPE_IN_THE_ANSWER_MODEL,
fields=['Taiwan', 'Taipei']))

my_deck.add_note(genanki.Note(
model=genanki.CLOZE_MODEL,
fields=[
'{{c1::Ottawa}} is the capital of {{c2::Canada}}',
'Ottawa is in Ontario province.']))

# Just try writing the notes to a .apkg file; if there is no Exception and no Warnings, we assume
# things are good.
fnode, fpath = tempfile.mkstemp()
os.close(fnode)

with warnings.catch_warnings(record=True) as warning_list:
my_deck.write_to_file(fpath)

assert not warning_list

os.unlink(fpath)

def test_cloze_with_single_field_warns():
my_deck = genanki.Deck(
1598559905,
'Country Capitals')

my_deck.add_note(genanki.Note(
model=genanki.CLOZE_MODEL,
fields=['{{c1::Rome}} is the capital of {{c2::Italy}}']))

# Just try writing the note to a .apkg file; if there is no Exception, we assume things are good.
fnode, fpath = tempfile.mkstemp()
os.close(fnode)
my_deck.write_to_file(fpath)

with pytest.warns(DeprecationWarning):
my_deck.write_to_file(fpath)

os.unlink(fpath)

0 comments on commit 9ba9392

Please sign in to comment.