Skip to content

Commit

Permalink
Use standard Go templates instead of custom ones in note type definit…
Browse files Browse the repository at this point in the history
…ion.
  • Loading branch information
lfyuomr-gylo committed Dec 8, 2022
1 parent e1b8ebc commit a285df9
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 210 deletions.
56 changes: 29 additions & 27 deletions anki-helper.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,55 +79,57 @@ actions:
# FieldVoiceover (unless skipVoiceover: true)
- name: Word
vars:
PRONOUN: ""
TENSE: _spanish-infinitive.png
pronoun: ""
tense: _spanish-infinitive.png
- name: Explanation
skipVoiceover: true

- name: IndicativePresentYo
vars:
PRONOUN: yo
TENSE: _spanish-tense-present.jpeg
pronoun: yo
tense: _spanish-tense-present.jpeg
- name: IndicativePresentTu
vars:
PRONOUN:
TENSE: _spanish-tense-present.jpeg
pronoun:
tense: _spanish-tense-present.jpeg
- name: IndicativePresentEl
vars:
PRONOUN: él/ella/usted
TENSE: _spanish-tense-present.jpeg
pronoun: él/ella/usted
tense: _spanish-tense-present.jpeg
- name: IndicativePresentNosotros
vars:
PRONOUN: nosotros
TENSE: _spanish-tense-present.jpeg
pronoun: nosotros
tense: _spanish-tense-present.jpeg
- name: IndicativePresentVosotros
vars:
PRONOUN: vosotros
TENSE: _spanish-tense-present.jpeg
pronoun: vosotros
tense: _spanish-tense-present.jpeg
- name: IndicativePresentEllos
vars:
PRONOUN: ellos/ellas/ustedes
TENSE: _spanish-tense-present.jpeg
pronoun: ellos/ellas/ustedes
tense: _spanish-tense-present.jpeg

- name: ImperativeAffirmativeTu
vars:
PRONOUN:
TENSE: _spanish-tense-imperative.jpeg
pronoun:
tense: _spanish-tense-imperative.jpeg
- name: ImperativeAffirmativeUsted
vars:
PRONOUN: usted
TENSE: _spanish-tense-imperative.jpeg
pronoun: usted
tense: _spanish-tense-imperative.jpeg

- name: AutoConjugationIgnore
skipVoiceover: true
templates:
# For each template a distinct card template is created for each field specified in forFields
# The following meta-variables will be substituted in name, front and back before passing them to Anki:
# $$FIELD$$, $$FIELD_VOICEOVER$$
# $$.Field$$, $$.FieldVoiceover$$
#
# Additionally, variables attached to a field are available in the template as $$.Vars.varName$$.
#
# Front layout is automatically wrapped with {{^$$FIELD$$}}{{/$$FIELD$$}}, so there is no need in
# Front layout is automatically wrapped with {{^$$.Field$$}}{{/$$.Field$$}}, so there is no need in
# adding this manually
- name: "FillIn$$FIELD$$"
- name: "FillIn$$.Field$$"
forFields:
- Word
- IndicativePresentYo
Expand All @@ -139,11 +141,11 @@ actions:
- ImperativeAffirmativeTu
- ImperativeAffirmativeUsted
front: |
<img src="$$TENSE$$"><br><br>
<img src="$$.Vars.tense$$"><br><br>
<i>{{text:Explanation}}</i><br>
<div>$$PRONOUN$$ <b><span class='cloze'>[&#8230;]</span></b></div>
<div>$$.Vars.pronoun$$ <b><span class='cloze'>[&#8230;]</span></b></div>
{{type::$$FIELD$$}}
{{type::$$.Field$$}}
<script>
var clozes = document.querySelectorAll(".mycloze");
Expand All @@ -165,12 +167,12 @@ actions:
});
</script>
back: |
<img src="$$TENSE$$"><br><br>
<img src="$$.Vars.tense$$"><br><br>
<i>{{text:Explanation}}</i><br>
<div>$$PRONOUN$$ <b><span class='cloze'>{{text:$$FIELD$$}}</span></b>{{$$FIELD_VOICEOVER$$}}</div>
<div>$$.Vars.pronoun$$ <b><span class='cloze'>{{text:$$.Field$$}}</span></b>{{$$.FieldVoiceover$$}}</div>
<hr id=answer>
{{type::$$FIELD$$}}
{{type::$$.Field$$}}
<script>
var clozes = document.querySelectorAll(".mycloze");
Expand Down
40 changes: 19 additions & 21 deletions ankihelper/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"anki-rest-enhancer/util/execx"
"anki-rest-enhancer/util/iox"
"anki-rest-enhancer/util/stringx"
"anki-rest-enhancer/util/templatex"
"context"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -255,38 +256,35 @@ func (h Helper) createNoteType(conf ankihelperconf.AnkiNoteType) error {
fieldNames = append(fieldNames, voiceoverFields...)

templates := make([]ankiconnect.CreateModelCardTemplate, 0, len(conf.Templates))
for _, template := range conf.Templates {
for _, field := range template.ForFields {
names := h.fieldNames(field)
substitutions := stringx.RemoveEmptyValuesInPlace(map[string]string{
"FIELD": names.Field,
"FIELD_VOICEOVER": names.FieldVoiceover,
})
for name, val := range field.Vars {
if _, ok := substitutions[name]; ok {
return errorx.IllegalState.New("custom variable %q collides with a default variable in template %q", name, template.Name)
}
substitutions[name] = val
for tmplIdx, cardTemplate := range conf.Templates {
for _, field := range cardTemplate.ForFields {
const voiceoverSuffix = "Voiceover"
substitutions := map[string]any{
"Field": field.Name,
"Vars": field.Vars,
}
if !field.SkipVoiceover {
substitutions["FieldVoiceover"] = field.Name + voiceoverSuffix
}

templateName, err := substituteVariables(template.Name, substitutions)
cardTemplateName, err := templatex.Execute(cardTemplate.Name, substitutions)
if err != nil {
return errorx.Decorate(err, "failed to build card template name for template %q and field %q", template.Name, field.Name)
return errorx.Decorate(err, "failed to build card template name for template #%d and field %q", tmplIdx, field.Name)
}
if err := ankihelperconf.ValidateName(templateName); err != nil {
return errorx.Decorate(err, "got invalid template name after variables substitution: %s", templateName)
if err := ankihelperconf.ValidateName(cardTemplateName); err != nil {
return errorx.Decorate(err, "got invalid template name after variables substitution: %s", cardTemplateName)
}
front, err := substituteVariables(template.Front, substitutions)
front, err := templatex.Execute(cardTemplate.Front, substitutions)
if err != nil {
return errorx.Decorate(err, "failed to build card template front for %q and field %q", template.Name, field.Name)
return errorx.Decorate(err, "failed to build card template front for template #%d and field %q", tmplIdx, field.Name)
}
back, err := substituteVariables(template.Back, substitutions)
back, err := templatex.Execute(cardTemplate.Back, substitutions)
if err != nil {
return errorx.Decorate(err, "failed to build card template back for %q and field %q", template.Name, field.Name)
return errorx.Decorate(err, "failed to build card template back for template #%d and field %q", tmplIdx, field.Name)
}

templates = append(templates, ankiconnect.CreateModelCardTemplate{
Name: templateName,
Name: cardTemplateName,
Front: fmt.Sprintf("{{#%s}}\n%s\n{{/%s}}", field.Name, front, field.Name),
Back: back,
})
Expand Down
21 changes: 14 additions & 7 deletions ankihelper/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"anki-rest-enhancer/azuretts/azurettsmock"
"github.com/stretchr/testify/suite"
"testing"
"text/template"
)

func TestEnhancer(t *testing.T) {
Expand Down Expand Up @@ -50,7 +51,7 @@ func (s *EnhancerSuite) TestNoteTypeCreation_AlreadyExists() {
actions := ankihelperconf.Actions{NoteTypes: []ankihelperconf.AnkiNoteType{{
Name: modelName,
Fields: []ankihelperconf.AnkiNoteField{{Name: "Foo"}, {Name: "Bar"}},
Templates: []ankihelperconf.AnkiCardTemplate{{Name: "Card1", ForFields: []ankihelperconf.AnkiNoteField{{Name: "foo"}}}},
Templates: []ankihelperconf.AnkiCardTemplate{{Name: s.mustParse("Card1"), ForFields: []ankihelperconf.AnkiNoteField{{Name: "foo"}}}},
}}}

// when:
Expand Down Expand Up @@ -82,10 +83,10 @@ func (s *EnhancerSuite) TestNoteTypeCreation_CreateNewWithVoiceover() {
CSS: ".foo { font-size: large; }",
Fields: []ankihelperconf.AnkiNoteField{fieldName},
Templates: []ankihelperconf.AnkiCardTemplate{{
Name: "WordTemplate",
Name: s.mustParse("WordTemplate"),
ForFields: []ankihelperconf.AnkiNoteField{fieldName},
Front: "$TITLE$: {{ $FIELD$ }}$MY_EMPTY_FIELD$",
Back: "{{ $FIELD_VOICEOVER$ }}",
Front: s.mustParse("$$.Vars.TITLE$$: {{ $$.Field$$ }}$$.Vars.MY_EMPTY_FIELD$$"),
Back: s.mustParse("{{ $$.FieldVoiceover$$ }}"),
}},
}}}
expectedModel := ankiconnect.CreateModelParams{
Expand Down Expand Up @@ -131,10 +132,10 @@ func (s *EnhancerSuite) TestNoteTypeCreation_CreateNewWithNoVoiceover() {
CSS: css,
Fields: []ankihelperconf.AnkiNoteField{fieldComment},
Templates: []ankihelperconf.AnkiCardTemplate{{
Name: "CommentTemplate",
Name: s.mustParse("CommentTemplate"),
ForFields: []ankihelperconf.AnkiNoteField{fieldComment},
Front: "Field Name: $FIELD$",
Back: "Field Conent: {{ $FIELD$ }}",
Front: s.mustParse("Field Name: $$.Field$$"),
Back: s.mustParse("Field Conent: {{ $$.Field$$ }}"),
}},
}}}
expectedModel := ankiconnect.CreateModelParams{
Expand Down Expand Up @@ -272,3 +273,9 @@ func (s *EnhancerSuite) TestTTSGeneration_SingleErrorIsIgnored() {
s.Require().NoError(err)
s.Require().Equal(expectedUpdates, noteUpdates)
}

func (s *EnhancerSuite) mustParse(text string) *template.Template {
parsed, err := ankihelperconf.ParseTextTemplate("test", text)
s.Require().NoError(err)
return parsed
}
82 changes: 0 additions & 82 deletions ankihelper/template.go

This file was deleted.

58 changes: 0 additions & 58 deletions ankihelper/template_test.go

This file was deleted.

6 changes: 3 additions & 3 deletions ankihelperconf/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ type AnkiNoteType struct {
type AnkiNoteField YAMLAnkiNoteField

type AnkiCardTemplate struct {
Name string
Name *template.Template
ForFields []AnkiNoteField
Front string
Back string
Front *template.Template
Back *template.Template
}

type NotesOrganizationRule struct {
Expand Down
12 changes: 12 additions & 0 deletions ankihelperconf/templates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ankihelperconf

import "text/template"

const (
templateOpen = "$$"
templateClose = "$$"
)

func ParseTextTemplate(name string, text string) (*template.Template, error) {
return template.New(name).Delims(templateOpen, templateClose).Parse(text)
}
Loading

0 comments on commit a285df9

Please sign in to comment.