diff -r 59714b9033bc -r 4a3188fc8951 py/gadict_srs_anki.py --- /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'] = '
{{From}}
' + tmpl['afmt'] = '{{FrontSide}}\n\n
\n\n
{{To}}
' + 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("
") + buf.append("") + buf.append(hw.headword) + buf.append("") + if hw.pron is not None: + buf.append(" [") + buf.append(hw.pron) + buf.append("]") + if len(hw.attrs) > 0: + l = [u"«"+x+u"»" for x in hw.attrs] + l.sort() + buf.append("") + buf.append(", ".join(l)) + buf.append("") + buf.append("
") + direct_from = "".join(buf) + buf = [] + for sense in translations: + buf.append("
") + if sense.pos: + buf.append("") + buf.append(sense.pos) + buf.append("") + if sense.ant_list and len(sense.ant_list) > 0: + buf.append("ant: ") + buf.append("; ".join(["{"+s+"}" for s in sense.ant_list])) + buf.append("") + if sense.syn_list and len(sense.syn_list) > 0: + buf.append("syn: ") + buf.append("; ".join(["{"+s+"}" for s in sense.syn_list])) + buf.append("") + if len(sense.tr_list) > 1: + for (lang, tr) in sense.tr_list: + buf.append("
") + buf.append(" ") + buf.append(lang) + buf.append("") + buf.append(" ") + buf.append(tr) + buf.append("") + buf.append("
") + else: + for (lang, tr) in sense.tr_list: + buf.append(" ") + buf.append(lang) + buf.append("") + buf.append(" ") + buf.append(tr) + buf.append("") + 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)