From 59f75a2946cd79a923367902da00a9b92529521b Mon Sep 17 00:00:00 2001 From: Austin Siew <17107540+Aquafina-water-bottle@users.noreply.github.com> Date: Sat, 24 Dec 2022 04:10:56 -0700 Subject: [PATCH 1/5] added modelFieldFonts, modelFieldSetFont, modelFieldSetFontSize --- plugin/__init__.py | 131 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 104 insertions(+), 27 deletions(-) diff --git a/plugin/__init__.py b/plugin/__init__.py index 0f6fcbb..739efda 100644 --- a/plugin/__init__.py +++ b/plugin/__init__.py @@ -189,6 +189,22 @@ class AnkiConnect: return media + def getModel(self, modelName): + model = self.collection().models.byName(modelName) + if model is None: + raise Exception('model was not found: {}'.format(modelName)) + return model + + + def getField(self, modelName, fieldName): + model = self.getModel(modelName) + + fieldMap = self.collection().models.fieldMap(model) + if fieldName not in fieldMap: + raise Exception('field was not found in {}: {}'.format(modelName, fieldName)) + return fieldMap[fieldName][1] + + def startEditing(self): self.window().requireReset() @@ -1082,6 +1098,25 @@ class AnkiConnect: return ['' for field in model['flds']] + @util.api() + def modelFieldFonts(self, modelName): + model = self.getModel(modelName) + + fonts = [] + for field in model['flds']: + + #fonts.append([field['font'], field['size']]) + + fieldFont = { + field['name']: { + 'font': field['font'], + 'size': field['size'], + } + } + fonts.append(fieldFont) + + return fonts + @util.api() def modelFieldsOnTemplates(self, modelName): @@ -1201,15 +1236,19 @@ class AnkiConnect: @util.api() def modelFieldRename(self, modelName, oldFieldName, newFieldName): - mm = self.collection().models - model = mm.byName(modelName) - if model is None: - raise Exception('model was not found: {}'.format(modelName)) + #mm = self.collection().models + #model = mm.byName(modelName) + #if model is None: + # raise Exception('model was not found: {}'.format(modelName)) - fieldMap = mm.fieldMap(model) - if oldFieldName not in fieldMap: - raise Exception('field was not found in {}: {}'.format(modelName, oldFieldName)) - field = fieldMap[oldFieldName][1] + #fieldMap = mm.fieldMap(model) + #if oldFieldName not in fieldMap: + # raise Exception('field was not found in {}: {}'.format(modelName, oldFieldName)) + #field = fieldMap[oldFieldName][1] + + mm = self.collection().models + model = self.getModel(modelName) + field = self.getField(modelName, oldFieldName) mm.renameField(model, field, newFieldName) @@ -1218,15 +1257,19 @@ class AnkiConnect: @util.api() def modelFieldReposition(self, modelName, fieldName, index): - mm = self.collection().models - model = mm.byName(modelName) - if model is None: - raise Exception('model was not found: {}'.format(modelName)) + #mm = self.collection().models + #model = mm.byName(modelName) + #if model is None: + # raise Exception('model was not found: {}'.format(modelName)) - fieldMap = mm.fieldMap(model) - if fieldName not in fieldMap: - raise Exception('field was not found in {}: {}'.format(modelName, fieldName)) - field = fieldMap[fieldName][1] + #fieldMap = mm.fieldMap(model) + #if fieldName not in fieldMap: + # raise Exception('field was not found in {}: {}'.format(modelName, fieldName)) + #field = fieldMap[fieldName][1] + + mm = self.collection().models + model = self.getModel(modelName) + field = self.getField(modelName, fieldName) mm.repositionField(model, field, index) @@ -1236,9 +1279,11 @@ class AnkiConnect: @util.api() def modelFieldAdd(self, modelName, fieldName, index=None): mm = self.collection().models - model = mm.byName(modelName) - if model is None: - raise Exception('model was not found: {}'.format(modelName)) + model = self.getModel(modelName) + + #model = mm.byName(modelName) + #if model is None: + # raise Exception('model was not found: {}'.format(modelName)) # only adds the field if it doesn't already exist fieldMap = mm.fieldMap(model) @@ -1257,21 +1302,53 @@ class AnkiConnect: @util.api() def modelFieldRemove(self, modelName, fieldName): - mm = self.collection().models - model = mm.byName(modelName) - if model is None: - raise Exception('model was not found: {}'.format(modelName)) + #mm = self.collection().models + #model = mm.byName(modelName) + #if model is None: + # raise Exception('model was not found: {}'.format(modelName)) - fieldMap = mm.fieldMap(model) - if fieldName not in fieldMap: - raise Exception('field was not found in {}: {}'.format(modelName, fieldName)) - field = fieldMap[fieldName][1] + #fieldMap = mm.fieldMap(model) + #if fieldName not in fieldMap: + # raise Exception('field was not found in {}: {}'.format(modelName, fieldName)) + #field = fieldMap[fieldName][1] + + mm = self.collection().models + model = self.getModel(modelName) + field = self.getField(modelName, fieldName) mm.removeField(model, field) self.save_model(mm, model) + @util.api() + def modelFieldSetFont(self, modelName, fieldName, font): + mm = self.collection().models + model = self.getModel(modelName) + field = self.getField(modelName, fieldName) + + if not isinstance(font, str): + raise Exception("font should be a string: {}".format(font)) + + field["font"] = font + + self.save_model(mm, model) + + + @util.api() + def modelFieldSetFontSize(self, modelName, fieldName, fontSize): + mm = self.collection().models + model = self.getModel(modelName) + field = self.getField(modelName, fieldName) + + if not isinstance(fontSize, int): + raise Exception("fontSize should be an integer: {}".format(fontSize)) + + field["size"] = fontSize + + self.save_model(mm, model) + + @util.api() def deckNameFromId(self, deckId): deck = self.collection().decks.get(deckId) From 2d54eccb6435575fcfbd0715a901c8bf7ded1dd0 Mon Sep 17 00:00:00 2001 From: Austin Siew <17107540+Aquafina-water-bottle@users.noreply.github.com> Date: Sat, 24 Dec 2022 04:18:24 -0700 Subject: [PATCH 2/5] added modelFieldSetDescription, changed double quote strings to single quote strings --- plugin/__init__.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/plugin/__init__.py b/plugin/__init__.py index 739efda..02c7e92 100644 --- a/plugin/__init__.py +++ b/plugin/__init__.py @@ -1328,9 +1328,23 @@ class AnkiConnect: field = self.getField(modelName, fieldName) if not isinstance(font, str): - raise Exception("font should be a string: {}".format(font)) + raise Exception('font should be a string: {}'.format(font)) - field["font"] = font + field['font'] = font + + self.save_model(mm, model) + + + @util.api() + def modelFieldSetDescription(self, modelName, fieldName, description): + mm = self.collection().models + model = self.getModel(modelName) + field = self.getField(modelName, fieldName) + + if not isinstance(description, str): + raise Exception('description should be a string: {}'.format(description)) + + field['description'] = description self.save_model(mm, model) @@ -1342,9 +1356,9 @@ class AnkiConnect: field = self.getField(modelName, fieldName) if not isinstance(fontSize, int): - raise Exception("fontSize should be an integer: {}".format(fontSize)) + raise Exception('fontSize should be an integer: {}'.format(fontSize)) - field["size"] = fontSize + field['size'] = fontSize self.save_model(mm, model) From 69baea296a46bfa5ef28f8cb5bb4a90d71c298d2 Mon Sep 17 00:00:00 2001 From: Austin Siew <17107540+Aquafina-water-bottle@users.noreply.github.com> Date: Wed, 28 Dec 2022 18:37:05 -0700 Subject: [PATCH 3/5] added tests & documentation, removed dead code --- README.md | 93 ++++++++++++++++++++++++++++++++++++++++++-- plugin/__init__.py | 77 ++++++++++-------------------------- tests/test_models.py | 66 +++++++++++++++++++++++++++++++ 3 files changed, 175 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index ac0f351..72aea22 100644 --- a/README.md +++ b/README.md @@ -1865,6 +1865,38 @@ corresponding to when the API was available for use. } ``` +* **modelFieldFonts** + + Gets the complete list of fonts along with their font sizes. + + *Sample request*: + ```json + { + "action": "modelFieldFonts", + "version": 6, + "params": { + "modelName": "Basic" + } + } + ``` + + *Sample result*: + ```json + { + "result": { + "Front": { + "font": "Arial", + "size": 20 + }, + "Back": { + "font": "Arial", + "size": 20 + } + }, + "error": null + } + ``` + * **modelFieldsOnTemplates** Returns an object indicating the fields on the question and answer side of each card template for the given model @@ -2214,18 +2246,19 @@ corresponding to when the API was available for use. } ``` -* **modelFieldRemove** +* **modelFieldSetFont** - Deletes a field within a given model. + Sets the font for a field within a given model. *Sample Request*: ```json { - "action": "modelFieldRemove", + "action": "modelFieldSetFont", "version": 6, "params": { "modelName": "Basic", - "fieldName": "Front" + "fieldName": "Front", + "font": "Courier" } } ``` @@ -2238,6 +2271,58 @@ corresponding to when the API was available for use. } ``` +* **modelFieldSetFontSize** + + Sets the font size for a field within a given model. + + *Sample Request*: + ```json + { + "action": "modelFieldSetFont", + "version": 6, + "params": { + "modelName": "Basic", + "fieldName": "Front", + "fontSize": 10 + } + } + ``` + + *Sample result*: + ```json + { + "result": null, + "error": null + } + ``` + +* **modelFieldSetDescription** + + Sets the description (the text seen in the gui editor when a field is empty) for a field within a given model. + + Older versions of Anki (2.1.49 and below) do not have field descriptions. In that case, this will return with `false`. + + *Sample Request*: + ```json + { + "action": "modelFieldSetDescription", + "version": 6, + "params": { + "modelName": "Basic", + "fieldName": "Front", + "description": "example field description" + } + } + ``` + + *Sample result*: + ```json + { + "result": true, + "error": null + } + ``` + #### Note Actions * **addNote** diff --git a/plugin/__init__.py b/plugin/__init__.py index 02c7e92..38851c5 100644 --- a/plugin/__init__.py +++ b/plugin/__init__.py @@ -1102,18 +1102,13 @@ class AnkiConnect: def modelFieldFonts(self, modelName): model = self.getModel(modelName) - fonts = [] + fonts = {} for field in model['flds']: - #fonts.append([field['font'], field['size']]) - - fieldFont = { - field['name']: { - 'font': field['font'], - 'size': field['size'], - } + fonts[field['name']] = { + 'font': field['font'], + 'size': field['size'], } - fonts.append(fieldFont) return fonts @@ -1236,16 +1231,6 @@ class AnkiConnect: @util.api() def modelFieldRename(self, modelName, oldFieldName, newFieldName): - #mm = self.collection().models - #model = mm.byName(modelName) - #if model is None: - # raise Exception('model was not found: {}'.format(modelName)) - - #fieldMap = mm.fieldMap(model) - #if oldFieldName not in fieldMap: - # raise Exception('field was not found in {}: {}'.format(modelName, oldFieldName)) - #field = fieldMap[oldFieldName][1] - mm = self.collection().models model = self.getModel(modelName) field = self.getField(modelName, oldFieldName) @@ -1257,16 +1242,6 @@ class AnkiConnect: @util.api() def modelFieldReposition(self, modelName, fieldName, index): - #mm = self.collection().models - #model = mm.byName(modelName) - #if model is None: - # raise Exception('model was not found: {}'.format(modelName)) - - #fieldMap = mm.fieldMap(model) - #if fieldName not in fieldMap: - # raise Exception('field was not found in {}: {}'.format(modelName, fieldName)) - #field = fieldMap[fieldName][1] - mm = self.collection().models model = self.getModel(modelName) field = self.getField(modelName, fieldName) @@ -1281,10 +1256,6 @@ class AnkiConnect: mm = self.collection().models model = self.getModel(modelName) - #model = mm.byName(modelName) - #if model is None: - # raise Exception('model was not found: {}'.format(modelName)) - # only adds the field if it doesn't already exist fieldMap = mm.fieldMap(model) if fieldName not in fieldMap: @@ -1302,16 +1273,6 @@ class AnkiConnect: @util.api() def modelFieldRemove(self, modelName, fieldName): - #mm = self.collection().models - #model = mm.byName(modelName) - #if model is None: - # raise Exception('model was not found: {}'.format(modelName)) - - #fieldMap = mm.fieldMap(model) - #if fieldName not in fieldMap: - # raise Exception('field was not found in {}: {}'.format(modelName, fieldName)) - #field = fieldMap[fieldName][1] - mm = self.collection().models model = self.getModel(modelName) field = self.getField(modelName, fieldName) @@ -1335,20 +1296,6 @@ class AnkiConnect: self.save_model(mm, model) - @util.api() - def modelFieldSetDescription(self, modelName, fieldName, description): - mm = self.collection().models - model = self.getModel(modelName) - field = self.getField(modelName, fieldName) - - if not isinstance(description, str): - raise Exception('description should be a string: {}'.format(description)) - - field['description'] = description - - self.save_model(mm, model) - - @util.api() def modelFieldSetFontSize(self, modelName, fieldName, fontSize): mm = self.collection().models @@ -1363,6 +1310,22 @@ class AnkiConnect: self.save_model(mm, model) + @util.api() + def modelFieldSetDescription(self, modelName, fieldName, description): + mm = self.collection().models + model = self.getModel(modelName) + field = self.getField(modelName, fieldName) + + if not isinstance(description, str): + raise Exception('description should be a string: {}'.format(description)) + + if 'description' in field: # older versions do not have the 'description' key + field['description'] = description + self.save_model(mm, model) + return True + return False + + @util.api() def deckNameFromId(self, deckId): deck = self.collection().decks.get(deckId) diff --git a/tests/test_models.py b/tests/test_models.py index bed3b82..801d900 100755 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -21,6 +21,20 @@ def test_modelFieldDescriptions(setup): assert result == ["", ""] +def test_modelFieldFonts(setup): + result = ac.modelFieldFonts(modelName="test_model") + assert result == { + "field1": { + "font": "Arial", + "size": 20, + }, + "field2": { + "font": "Arial", + "size": 20, + }, + } + + def test_modelFieldsOnTemplates(setup): result = ac.modelFieldsOnTemplates(modelName="test_model") assert result == { @@ -172,3 +186,55 @@ class TestModelFieldNames: result = ac.modelFieldNames(modelName="test_model") assert result == ["field2"] + + def test_modelFieldSetFont(self, setup): + ac.modelFieldSetFont( + modelName="test_model", + fieldName="field1", + font="Courier", + ) + + result = ac.modelFieldFonts(modelName="test_model") + assert result == { + "field1": { + "font": "Courier", + "size": 20, + }, + "field2": { + "font": "Arial", + "size": 20, + }, + } + + def test_modelFieldSetFontSize(self, setup): + ac.modelFieldSetFontSize( + modelName="test_model", + fieldName="field2", + fontSize=16, + ) + + result = ac.modelFieldFonts(modelName="test_model") + assert result == { + "field1": { + "font": "Arial", + "size": 20, + }, + "field2": { + "font": "Arial", + "size": 16, + }, + } + + def test_modelFieldSetDescription(self, setup): + set_desc = ac.modelFieldSetDescription( + modelName="test_model", + fieldName="field1", + description="test description", + ) + + result = ac.modelFieldDescriptions(modelName="test_model") + + if set_desc: + assert result == ["test description", ""] + else: + assert result == ["", ""] From a96e10e8402d204d93463b9e6cf7e7f89f10e14b Mon Sep 17 00:00:00 2001 From: Austin Siew <17107540+Aquafina-water-bottle@users.noreply.github.com> Date: Wed, 28 Dec 2022 18:54:22 -0700 Subject: [PATCH 4/5] made the test more precise by checking the anki version --- tests/test_models.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test_models.py b/tests/test_models.py index 801d900..bf8aa97 100755 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,4 +1,5 @@ from conftest import ac +from plugin import anki_version def test_modelNames(setup): @@ -234,7 +235,9 @@ class TestModelFieldNames: result = ac.modelFieldDescriptions(modelName="test_model") - if set_desc: - assert result == ["test description", ""] - else: + if anki_version < (2, 1, 50): + assert not set_desc assert result == ["", ""] + else: + assert set_desc + assert result == ["test description", ""] From 5e527680d93a3ecb0b0dea4ff82a642745b2a4c0 Mon Sep 17 00:00:00 2001 From: Austin Siew <17107540+Aquafina-water-bottle@users.noreply.github.com> Date: Wed, 28 Dec 2022 18:57:38 -0700 Subject: [PATCH 5/5] accidentally removed modelFieldRemove from the README, reverted --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index 72aea22..bd6982e 100644 --- a/README.md +++ b/README.md @@ -2246,6 +2246,30 @@ corresponding to when the API was available for use. } ``` +* **modelFieldRemove** + + Deletes a field within a given model. + + *Sample Request*: + ```json + { + "action": "modelFieldRemove", + "version": 6, + "params": { + "modelName": "Basic", + "fieldName": "Front" + } + } + ``` + + *Sample result*: + ```json + { + "result": null, + "error": null + } + ``` + * **modelFieldSetFont** Sets the font for a field within a given model.