--- /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)