py/gadict_srs_anki.py
changeset 677 e330e7f195b7
parent 676 e3266e096360
child 678 28e04408c0c0
--- a/py/gadict_srs_anki.py	Sat Nov 12 12:39:02 2016 +0200
+++ b/py/gadict_srs_anki.py	Sat Nov 12 13:55:08 2016 +0200
@@ -125,20 +125,7 @@
 from anki.exporting import AnkiPackageExporter
 
 
-class AnkiDbBuilder:
-
-    def __init__(self, tmpdir, name):
-        self.tmpdir = tmpdir
-        self.name = name
-
-        self.collection = collection = anki.Collection(os.path.join(self.tmpdir, 'collection.anki2'))
-
-        deck_id = collection.decks.id(self.name + "_deck")
-        deck = collection.decks.get(deck_id)
-
-        model = collection.models.new(self.name + "_model")
-        model['did'] = deck_id
-        model['css'] = """
+MODEL_CSS = """
 .card {
   font-family: arial;
   font-size: 20px;
@@ -205,41 +192,172 @@
 }
 """
 
-        collection.models.addField(model, collection.models.newField('From'))
-        collection.models.addField(model, collection.models.newField('To'))
+def note_add_tags(note, tags):
+    if isinstance(tags, str):       note.tags = [tags]
+    elif isinstance(tags, list):    note.tags = tags
+    elif tags is None:              pass
+    else:                           raise Exception('Expecting string or list of tags...')
+
+
+class AnkiDbBuilder:
+
+    def __init__(self, tmpdir, name):
+        self.tmpdir = tmpdir
+        self.name = name
+
+        self.collection = collection = anki.Collection(os.path.join(self.tmpdir, 'collection.anki2'))
+
+        deck_id = collection.decks.id(self.name)
+        deck = collection.decks.get(deck_id)
+
+        # It is essential to keep model['id'] unchanged between upgrades!!
+        MODEL_ID = int(hashlib.sha1(self.name).hexdigest(), 16) % (2**63)
+
+        ################################################################
+        # Regular card model. SafeBack doesn't include examples to not spoil
+        # word spelling.
+        model = collection.models.new(self.name + "_frontback")
+        model['did'] = deck_id
+        model['css'] = MODEL_CSS
+
+        collection.models.addField(model, collection.models.newField('Front'))
+        collection.models.addField(model, collection.models.newField('Back'))
+        collection.models.addField(model, collection.models.newField('SafeBack'))
+
+        tmpl = collection.models.newTemplate('Front -> Back')
+        tmpl['qfmt'] = '<div class="front">{{Front}}</div>'
+        tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Back}}</div>'
+        collection.models.addTemplate(model, tmpl)
 
-        tmpl = collection.models.newTemplate('From -> To')
-        tmpl['qfmt'] = '<div class="from">{{From}}</div>'
-        tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="to">{{To}}</div>'
+        tmpl = collection.models.newTemplate('SafeBack -> Front')
+        tmpl['qfmt'] = '<div class="safe-back">{{SafeBack}}</div>'
+        tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="front">{{Front}}</div>'
+        collection.models.addTemplate(model, tmpl)
+
+        # Equivalent of ``collection.models.add(model)`` without setting
+        # auto-generated ID.
+        # Increment +1 is for keeping model['id'] unique from previous v0.9 release.
+        model['id'] = MODEL_ID + 1
+        collection.models.update(model)
+        collection.models.save(model)
+        self.model = model
+        # collection.models.setCurrent(model)
+
+        if not RICH_MODE:
+            return
+
+        ################################################################
+        # Model for irregular verbs.
+        model = collection.models.new(self.name + "_irrverb")
+        model['did'] = deck_id
+        model['css'] = MODEL_CSS
+
+        collection.models.addField(model, collection.models.newField('V1'))
+        collection.models.addField(model, collection.models.newField('V2'))
+        collection.models.addField(model, collection.models.newField('V3'))
+        collection.models.addField(model, collection.models.newField('V2alt'))
+        collection.models.addField(model, collection.models.newField('V3alt'))
+        collection.models.addField(model, collection.models.newField('Front'))
+        collection.models.addField(model, collection.models.newField('Back'))
+
+        question = u"<div class='ask'>Find irregular verb:</div>"
+
+        tmpl = collection.models.newTemplate('V1 -> Back')
+        tmpl['qfmt'] = question + '<div class="front">{{V1}}</div>'
+        tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="back">{{Back}}</div>'
+        collection.models.addTemplate(model, tmpl)
+
+        tmpl = collection.models.newTemplate('V2 -> Back')
+        tmpl['qfmt'] = question + '<div class="front">{{V2}}</div>'
+        tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="back">{{Back}}</div>'
         collection.models.addTemplate(model, tmpl)
 
-        # Equivalent of:
-        #   collection.models.add(model)
-        # without setting auto-generated ID. It is essential to keep model['id']
-        # unchanged between upgrades or notes will be skipped!!
-        model['id'] = int(hashlib.sha1(self.name).hexdigest(), 16) % (2**63)
+        tmpl = collection.models.newTemplate('V3 -> Back')
+        tmpl['qfmt'] = question + '<div class="front">{{V3}}</div>'
+        tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="back">{{Back}}</div>'
+        collection.models.addTemplate(model, tmpl)
+
+        tmpl = collection.models.newTemplate('V2alt -> Back')
+        tmpl['qfmt'] = question + '<div class="front">{{V2alt}}</div>'
+        tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="back">{{Back}}</div>'
+        collection.models.addTemplate(model, tmpl)
+
+        tmpl = collection.models.newTemplate('V3alt -> Back')
+        tmpl['qfmt'] = question + '<div class="front">{{V3alt}}</div>'
+        tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="back">{{Back}}</div>'
+        collection.models.addTemplate(model, tmpl)
+
+        model['id'] = MODEL_ID + 2          # Keep model['id'] unique.
         collection.models.update(model)
-        collection.models.setCurrent(model)
         collection.models.save(model)
+        self.model_irr = model
+
+        ################################################################
+        # Model for irregular plurals.
+        model = collection.models.new(self.name + "_plural")
+        model['did'] = deck_id
+        model['css'] = MODEL_CSS
+
+        collection.models.addField(model, collection.models.newField('Singular'))
+        collection.models.addField(model, collection.models.newField('Plural'))
+        collection.models.addField(model, collection.models.newField('Front'))
+        collection.models.addField(model, collection.models.newField('Back'))
+
+        question = u"<div class='ask'>Find singular/plural form:</div>"
 
-    def guid(self, type_, headword):
+        tmpl = collection.models.newTemplate('Singular -> Back')
+        tmpl['qfmt'] = question + '<div class="front">{{Singular}}</div>'
+        tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="back">{{Back}}</div>'
+        collection.models.addTemplate(model, tmpl)
+
+        tmpl = collection.models.newTemplate('Plural -> Back')
+        tmpl['qfmt'] = question + '<div class="front">{{Plural}}</div>'
+        tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="back">{{Back}}</div>'
+        collection.models.addTemplate(model, tmpl)
+
+        model['id'] = MODEL_ID + 3          # Keep model['id'] unique.
+        collection.models.update(model)
+        collection.models.save(model)
+        self.model_pl = model
+
+
+    def guid(self, nodetype, headword):
         """
-        :type_ used to generate different notes from same headword
+        :nodetype  used to generate different notes from same headword
         """
-        h = hashlib.md5(":".join((self.name, type_, headword)))
+        h = hashlib.md5(":".join((self.name, nodetype, headword)))
         return h.hexdigest()
 
-    def add_note(self, type_, id_, from_, to_, tags_ = None):
-        note = self.collection.newNote()
-        note['From'] = from_
-        # print(from_)
-        note['To'] = to_
-        # print(to_)
-        if isinstance(tags_, str):      note.tags = [tags_]
-        elif isinstance(tags_, list):   note.tags = tags_
-        elif tags_ is None:             pass
-        else:                           raise Exception('Expect string or list of tags...')
-        note.guid = self.guid(type_, id_)
+    def add_note(self, headword, front, back, safeback, tags = None):
+        note = anki.notes.Note(self.collection, self.model)
+        note['Front'] = front
+        note['Back'] = back
+        note['SafeBack'] = safeback
+        note_add_tags(note, tags)
+        note.guid = self.guid("front/back", headword)
+        self.collection.addNote(note)
+
+    def add_note_irr(self, headword, v1, v2, v2alt, v3, v3alt, front, back, tags = None):
+        note = anki.notes.Note(self.collection, self.model_irr)
+        note['V1'] = v1
+        note['V2'] = v2
+        note['V3'] = v3
+        note['V2alt'] = v2alt
+        note['V3alt'] = v3alt
+        note['Front'] = front
+        note['Back'] = back
+        note_add_tags(note, tags)
+        note.guid = self.guid("irregular verb", headword)
+        self.collection.addNote(note)
+
+    def add_note_pl(self, headword, singular, plural, front, back, tags = None):
+        note = anki.notes.Note(self.collection, self.model_pl)
+        note['Singular'] = singular
+        note['Plural'] = plural
+        note['Front'] = front
+        note['Back'] = back
+        note_add_tags(note, tags)
+        note.guid = self.guid("singular/plural noun", headword)
         self.collection.addNote(note)
 
     def export(self, fname):
@@ -317,13 +435,9 @@
     for identity in FDEL or []:
         identity = identity.strip()
         warnmsg = "<div class='del'>Please delete this note ({})</div>".format(identity)
-        builder.add_note("en->tr", identity, warnmsg, warnmsg+" en->tr", "del")
-        builder.add_note("tr->en", identity, warnmsg, warnmsg+" tr->en", "del")
-        builder.add_note("irregular1", identity, warnmsg, warnmsg+" irregular1", "del")
-        builder.add_note("irregular2", identity, warnmsg, warnmsg+" irregular2", "del")
-        builder.add_note("irregular3", identity, warnmsg, warnmsg+" irregular3", "del")
-        builder.add_note("singular", identity, warnmsg, warnmsg+" singular", "del")
-        builder.add_note("plural", identity, warnmsg, warnmsg+" plural", "del")
+        builder.add_note(identity, warnmsg, warnmsg, warnmsg, "del")
+        builder.add_note_irr(identity, warnmsg, warnmsg, warnmsg, warnmsg, warnmsg, warnmsg, warnmsg, "del")
+        builder.add_note_pl(identity, warnmsg, warnmsg, warnmsg, warnmsg, "del")
 
     for (headwords, translations) in DOM[1:]:
         identity = headwords[0].headword
@@ -338,7 +452,7 @@
             freqmsg = ",".join(freqtags)
             freqmsg = "<div class='freq'>{:s}</div>".format(freqmsg)
         buf = []
-        v1, v2, v3 = (None, None, None)
+        v1, v2, v3, v2alt, v3alt = (None, None, None, None, None)
         singular, plural = (None, None)
         for hw in headwords:
             buf.append("<div clsas='headword'>")
@@ -358,9 +472,15 @@
             if 'v1' in hw.attrs:
                 v1 = (hw.headword, hw.pron)
             if 'v2' in hw.attrs:
-                v2 = (hw.headword, hw.pron)
+                if v2:
+                    v2alt = (hw.headword, hw.pron)
+                else:
+                    v2 = (hw.headword, hw.pron)
             if 'v3' in hw.attrs:
-                v3 = (hw.headword, hw.pron)
+                if v3:
+                    v3alt = (hw.headword, hw.pron)
+                else:
+                    v3 = (hw.headword, hw.pron)
             if 's' in hw.attrs:
                 singular = (hw.headword, hw.pron)
             if 'pl' in hw.attrs:
@@ -375,24 +495,24 @@
         for sense in translations:
             write_sense(buf, sense, with_examples = False)
         reverse_from = "".join(buf)         # without examples!!
-        builder.add_note("en->tr", identity, direct_from + freqmsg, direct_to)
-        builder.add_note("tr->en", identity, reverse_from, direct_from + freqmsg)
+        builder.add_note(identity, direct_from, direct_to, reverse_from)
         if v1 and v2 and v3 and RICH_MODE:
-            question = u"<div class='ask'>Find irregular verb:</div>"
             riddle1 = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v1</span>".format(v1[0], v1[1])
             riddle2 = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v2</span>".format(v2[0], v2[1])
             riddle3 = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v3</span>".format(v3[0], v3[1])
-            answer = direct_from + direct_to
-            builder.add_note("irregular1", identity, question + riddle1 + freqmsg, answer)
-            builder.add_note("irregular2", identity, question + riddle2 + freqmsg, answer)
-            builder.add_note("irregular3", identity, question + riddle3 + freqmsg, answer)
+            if v2alt:
+                riddle2alt = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v2</span>".format(v2alt[0], v2alt[1])
+            else:
+                riddle2alt = ""
+            if v3alt:
+                riddle3alt = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v2</span>".format(v3alt[0], v3alt[1])
+            else:
+                riddle3alt = ""
+            builder.add_note_irr(identity, riddle1 + freqmsg, riddle2 + freqmsg, riddle2alt, riddle3 + freqmsg, riddle3alt, direct_from, direct_to)
         if singular and plural and RICH_MODE:
-            question = u"<div class='ask'>Find plural:</div>"
             riddle_s = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>s</span>".format(singular[0], singular[1])
             riddle_pl = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>pl</span>".format(plural[0], plural[1])
-            answer = direct_from + direct_to
-            builder.add_note("singular", identity, question + riddle_s + freqmsg, answer)
-            builder.add_note("plural", identity, question + riddle_pl + freqmsg, answer)
+            builder.add_note_pl(identity, riddle_s + freqmsg, riddle_pl + freqmsg, direct_from, direct_to)
 
     builder.export(FONAME)
 finally: