py/gadict_srs_anki.py
changeset 555 4a3188fc8951
child 556 78bf1097106a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/py/gadict_srs_anki.py	Thu Sep 15 19:19:34 2016 +0300
@@ -0,0 +1,208 @@
+# -*- coding: utf-8 -*-
+
+import os
+import io
+import sys
+import codecs
+import tempfile
+import shutil
+import signal
+
+import gadict
+
+
+FINAME = None
+FONAME = None
+if len(sys.argv) >= 2:
+    FINAME = sys.argv[1]
+if len(sys.argv) >= 3:
+    FONAME = sys.argv[2]
+LANGS = None
+if len(sys.argv) >= 4:
+    LANGS = set(sys.argv[3].split(","))
+
+FIN = io.open(FINAME, mode='r', buffering=1, encoding="utf-8")
+
+PARSER = gadict.Parser()
+try:
+    DOM = PARSER.parse(FIN)
+except gadict.ParseException as ex:
+    sys.stdout.write("{:s}{:s}\n".format(FINAME, repr(ex)))
+    if __debug__:
+        import traceback
+        traceback.print_exc()
+    exit(1)
+finally:
+    FIN.close()
+
+def cleanup(collection, tmpdir):
+    if collection:
+        collection.close()
+    if tmpdir:
+        shutil.rmtree(tmpdir, ignore_errors=True)
+
+# signal.signal(signal.SIGINT, lambda signal, frame: cleanup())
+
+
+if FONAME is None:
+    raise Exception('Missing output file name')
+# Looks like anki libs change working directory to media directory of current deck
+# Therefore absolute path should be stored before creating temporary deck
+FONAME = os.path.abspath(FONAME)
+FBASENAME, _ = os.path.splitext(os.path.basename(FONAME))
+TMPDIR = tempfile.mkdtemp(dir = os.path.dirname(FONAME))
+
+import hashlib
+
+import anki
+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['tags'].append(self.name + "_tag")
+        model['did'] = deck_id
+        model['css'] = """
+.card {
+  font-family: arial;
+  font-size: 20px;
+  text-align: center;
+  color: black;
+  background-color: white;
+}
+span.headword {
+  font-style: italic;
+}
+.pron {
+  color: magenta;
+}
+.pos {
+  color: green;
+  font-style: italic;
+}
+.lang {
+  color: red;
+  font-style: italic;
+}
+.ant {
+  color: red;
+}
+.syn {
+  color: blue;
+}
+"""
+
+        collection.models.addField(model, collection.models.newField('From'))
+        collection.models.addField(model, collection.models.newField('To'))
+
+        tmpl = collection.models.newTemplate('From -> To')
+        tmpl['qfmt'] = '<div class="from">{{From}}</div>'
+        tmpl['afmt'] = '{{FrontSide}}\n\n<hr id=answer>\n\n<div class="to">{{To}}</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)
+        collection.models.update(model)
+        collection.models.setCurrent(model)
+        collection.models.save(model)
+
+    def guid(self, type_, headword):
+        """
+        :type_ used to generate different notes from same headword
+        """
+        h = hashlib.md5(":".join((self.name, type_, headword)))
+        return h.hexdigest()
+
+    def add_note(self, type_, id_, from_, to_):
+        note = self.collection.newNote()
+        note['From'] = from_
+        # print(from_)
+        note['To'] = to_
+        # print(to_)
+        note.guid = self.guid(type_, id_)
+        self.collection.addNote(note)
+
+    def export(self, fname):
+        export = AnkiPackageExporter(self.collection)
+        export.exportInto(fname)
+
+    def close(self):
+        self.collection.close()
+
+try:
+    builder = AnkiDbBuilder(TMPDIR, FBASENAME)
+
+    for (headwords, translations) in DOM[1:]:
+        identity = headwords[0].headword
+        buf = []
+        for hw in headwords:
+            buf.append("<div clsas='headword'>")
+            buf.append("<span clsas='headword'>")
+            buf.append(hw.headword)
+            buf.append("</span>")
+            if hw.pron is not None:
+                buf.append(" <span class='pron'>[")
+                buf.append(hw.pron)
+                buf.append("]</span>")
+            if len(hw.attrs) > 0:
+                l = [u"«"+x+u"»" for x in hw.attrs]
+                l.sort()
+                buf.append("<span class='attrs'>")
+                buf.append(", ".join(l))
+                buf.append("</span>")
+            buf.append("</div>")
+        direct_from = "".join(buf)
+        buf = []
+        for sense in translations:
+            buf.append("<div class='sense'>")
+            if sense.pos:
+                buf.append("<span class='pos'>")
+                buf.append(sense.pos)
+                buf.append("</span>")
+            if sense.ant_list and len(sense.ant_list) > 0:
+                buf.append("<span class='ant'>ant: ")
+                buf.append("; ".join(["{"+s+"}" for s in sense.ant_list]))
+                buf.append("</span>")
+            if sense.syn_list and len(sense.syn_list) > 0:
+                buf.append("<span class='syn'>syn: ")
+                buf.append("; ".join(["{"+s+"}" for s in sense.syn_list]))
+                buf.append("</span>")
+            if len(sense.tr_list) > 1:
+                for (lang, tr) in sense.tr_list:
+                    buf.append("<div class='sense'>")
+                    buf.append(" <span class='lang'>")
+                    buf.append(lang)
+                    buf.append("</span>")
+                    buf.append(" <span class='tr'>")
+                    buf.append(tr)
+                    buf.append("</span>")
+                    buf.append("</div>")
+            else:
+                for (lang, tr) in sense.tr_list:
+                    buf.append(" <span class='lang'>")
+                    buf.append(lang)
+                    buf.append("</span>")
+                    buf.append(" <span class='tr'>")
+                    buf.append(tr)
+                    buf.append("</span>")
+        direct_to = "".join(buf)
+        builder.add_note("en->tr", identity, direct_from, direct_to)
+        builder.add_note("tr->en", identity, direct_to, direct_from)
+
+    builder.export(FONAME)
+finally:
+    builder.close()
+    shutil.rmtree(TMPDIR, ignore_errors=True)