py/gadict_srs_anki.py
changeset 555 4a3188fc8951
child 556 78bf1097106a
equal deleted inserted replaced
554:59714b9033bc 555:4a3188fc8951
       
     1 # -*- coding: utf-8 -*-
       
     2 
       
     3 import os
       
     4 import io
       
     5 import sys
       
     6 import codecs
       
     7 import tempfile
       
     8 import shutil
       
     9 import signal
       
    10 
       
    11 import gadict
       
    12 
       
    13 
       
    14 FINAME = None
       
    15 FONAME = None
       
    16 if len(sys.argv) >= 2:
       
    17     FINAME = sys.argv[1]
       
    18 if len(sys.argv) >= 3:
       
    19     FONAME = sys.argv[2]
       
    20 LANGS = None
       
    21 if len(sys.argv) >= 4:
       
    22     LANGS = set(sys.argv[3].split(","))
       
    23 
       
    24 FIN = io.open(FINAME, mode='r', buffering=1, encoding="utf-8")
       
    25 
       
    26 PARSER = gadict.Parser()
       
    27 try:
       
    28     DOM = PARSER.parse(FIN)
       
    29 except gadict.ParseException as ex:
       
    30     sys.stdout.write("{:s}{:s}\n".format(FINAME, repr(ex)))
       
    31     if __debug__:
       
    32         import traceback
       
    33         traceback.print_exc()
       
    34     exit(1)
       
    35 finally:
       
    36     FIN.close()
       
    37 
       
    38 def cleanup(collection, tmpdir):
       
    39     if collection:
       
    40         collection.close()
       
    41     if tmpdir:
       
    42         shutil.rmtree(tmpdir, ignore_errors=True)
       
    43 
       
    44 # signal.signal(signal.SIGINT, lambda signal, frame: cleanup())
       
    45 
       
    46 
       
    47 if FONAME is None:
       
    48     raise Exception('Missing output file name')
       
    49 # Looks like anki libs change working directory to media directory of current deck
       
    50 # Therefore absolute path should be stored before creating temporary deck
       
    51 FONAME = os.path.abspath(FONAME)
       
    52 FBASENAME, _ = os.path.splitext(os.path.basename(FONAME))
       
    53 TMPDIR = tempfile.mkdtemp(dir = os.path.dirname(FONAME))
       
    54 
       
    55 import hashlib
       
    56 
       
    57 import anki
       
    58 from anki.exporting import AnkiPackageExporter
       
    59 
       
    60 
       
    61 class AnkiDbBuilder:
       
    62 
       
    63     def __init__(self, tmpdir, name):
       
    64         self.tmpdir = tmpdir
       
    65         self.name = name
       
    66 
       
    67         self.collection = collection = anki.Collection(os.path.join(self.tmpdir, 'collection.anki2'))
       
    68 
       
    69         deck_id = collection.decks.id(self.name + "_deck")
       
    70         deck = collection.decks.get(deck_id)
       
    71 
       
    72         model = collection.models.new(self.name + "_model")
       
    73         model['tags'].append(self.name + "_tag")
       
    74         model['did'] = deck_id
       
    75         model['css'] = """
       
    76 .card {
       
    77   font-family: arial;
       
    78   font-size: 20px;
       
    79   text-align: center;
       
    80   color: black;
       
    81   background-color: white;
       
    82 }
       
    83 span.headword {
       
    84   font-style: italic;
       
    85 }
       
    86 .pron {
       
    87   color: magenta;
       
    88 }
       
    89 .pos {
       
    90   color: green;
       
    91   font-style: italic;
       
    92 }
       
    93 .lang {
       
    94   color: red;
       
    95   font-style: italic;
       
    96 }
       
    97 .ant {
       
    98   color: red;
       
    99 }
       
   100 .syn {
       
   101   color: blue;
       
   102 }
       
   103 """
       
   104 
       
   105         collection.models.addField(model, collection.models.newField('From'))
       
   106         collection.models.addField(model, collection.models.newField('To'))
       
   107 
       
   108         tmpl = collection.models.newTemplate('From -> To')
       
   109         tmpl['qfmt'] = '<div class="from">{{From}}</div>'
       
   110         tmpl['afmt'] = '{{FrontSide}}\n\n<hr id=answer>\n\n<div class="to">{{To}}</div>'
       
   111         collection.models.addTemplate(model, tmpl)
       
   112 
       
   113         # Equivalent of:
       
   114         #   collection.models.add(model)
       
   115         # without setting auto-generated ID. It is essential to keep model['id']
       
   116         # unchanged between upgrades or notes will be skipped!!
       
   117         model['id'] = int(hashlib.sha1(self.name).hexdigest(), 16) % (2**63)
       
   118         collection.models.update(model)
       
   119         collection.models.setCurrent(model)
       
   120         collection.models.save(model)
       
   121 
       
   122     def guid(self, type_, headword):
       
   123         """
       
   124         :type_ used to generate different notes from same headword
       
   125         """
       
   126         h = hashlib.md5(":".join((self.name, type_, headword)))
       
   127         return h.hexdigest()
       
   128 
       
   129     def add_note(self, type_, id_, from_, to_):
       
   130         note = self.collection.newNote()
       
   131         note['From'] = from_
       
   132         # print(from_)
       
   133         note['To'] = to_
       
   134         # print(to_)
       
   135         note.guid = self.guid(type_, id_)
       
   136         self.collection.addNote(note)
       
   137 
       
   138     def export(self, fname):
       
   139         export = AnkiPackageExporter(self.collection)
       
   140         export.exportInto(fname)
       
   141 
       
   142     def close(self):
       
   143         self.collection.close()
       
   144 
       
   145 try:
       
   146     builder = AnkiDbBuilder(TMPDIR, FBASENAME)
       
   147 
       
   148     for (headwords, translations) in DOM[1:]:
       
   149         identity = headwords[0].headword
       
   150         buf = []
       
   151         for hw in headwords:
       
   152             buf.append("<div clsas='headword'>")
       
   153             buf.append("<span clsas='headword'>")
       
   154             buf.append(hw.headword)
       
   155             buf.append("</span>")
       
   156             if hw.pron is not None:
       
   157                 buf.append(" <span class='pron'>[")
       
   158                 buf.append(hw.pron)
       
   159                 buf.append("]</span>")
       
   160             if len(hw.attrs) > 0:
       
   161                 l = [u"«"+x+u"»" for x in hw.attrs]
       
   162                 l.sort()
       
   163                 buf.append("<span class='attrs'>")
       
   164                 buf.append(", ".join(l))
       
   165                 buf.append("</span>")
       
   166             buf.append("</div>")
       
   167         direct_from = "".join(buf)
       
   168         buf = []
       
   169         for sense in translations:
       
   170             buf.append("<div class='sense'>")
       
   171             if sense.pos:
       
   172                 buf.append("<span class='pos'>")
       
   173                 buf.append(sense.pos)
       
   174                 buf.append("</span>")
       
   175             if sense.ant_list and len(sense.ant_list) > 0:
       
   176                 buf.append("<span class='ant'>ant: ")
       
   177                 buf.append("; ".join(["{"+s+"}" for s in sense.ant_list]))
       
   178                 buf.append("</span>")
       
   179             if sense.syn_list and len(sense.syn_list) > 0:
       
   180                 buf.append("<span class='syn'>syn: ")
       
   181                 buf.append("; ".join(["{"+s+"}" for s in sense.syn_list]))
       
   182                 buf.append("</span>")
       
   183             if len(sense.tr_list) > 1:
       
   184                 for (lang, tr) in sense.tr_list:
       
   185                     buf.append("<div class='sense'>")
       
   186                     buf.append(" <span class='lang'>")
       
   187                     buf.append(lang)
       
   188                     buf.append("</span>")
       
   189                     buf.append(" <span class='tr'>")
       
   190                     buf.append(tr)
       
   191                     buf.append("</span>")
       
   192                     buf.append("</div>")
       
   193             else:
       
   194                 for (lang, tr) in sense.tr_list:
       
   195                     buf.append(" <span class='lang'>")
       
   196                     buf.append(lang)
       
   197                     buf.append("</span>")
       
   198                     buf.append(" <span class='tr'>")
       
   199                     buf.append(tr)
       
   200                     buf.append("</span>")
       
   201         direct_to = "".join(buf)
       
   202         builder.add_note("en->tr", identity, direct_from, direct_to)
       
   203         builder.add_note("tr->en", identity, direct_to, direct_from)
       
   204 
       
   205     builder.export(FONAME)
       
   206 finally:
       
   207     builder.close()
       
   208     shutil.rmtree(TMPDIR, ignore_errors=True)