py/gadict_srs_anki.py
changeset 698 5fc9bc68fb9c
parent 697 f1c74335f4ba
child 699 38ff2e2ffd5c
equal deleted inserted replaced
697:f1c74335f4ba 698:5fc9bc68fb9c
     6 import sys
     6 import sys
     7 import tempfile
     7 import tempfile
     8 import shutil
     8 import shutil
     9 import signal
     9 import signal
    10 import regex
    10 import regex
       
    11 
       
    12 import hashlib
       
    13 
       
    14 import anki
       
    15 from anki.exporting import AnkiPackageExporter
    11 
    16 
    12 import gadict
    17 import gadict
    13 import gadict_freq
    18 import gadict_freq
    14 
    19 
    15 
    20 
   106 if FONAME is None:
   111 if FONAME is None:
   107     raise Exception("Output file name is not passed...")
   112     raise Exception("Output file name is not passed...")
   108 # Looks like anki libs change working directory to media directory of current deck
   113 # Looks like anki libs change working directory to media directory of current deck
   109 # Therefore absolute path should be stored before creating temporary deck
   114 # Therefore absolute path should be stored before creating temporary deck
   110 FONAME = os.path.abspath(FONAME)
   115 FONAME = os.path.abspath(FONAME)
   111 TMPDIR = tempfile.mkdtemp(dir = os.path.dirname(FONAME))
   116 TMPDIR = tempfile.mkdtemp(dir=os.path.dirname(FONAME))
   112 
   117 
   113 if not NAME:
   118 if not NAME:
   114     NAME, _ = os.path.splitext(os.path.basename(FINAME))
   119     NAME, _ = os.path.splitext(os.path.basename(FINAME))
   115 
   120 
   116 if FDELNAME:
   121 if FDELNAME:
   117     FDEL = io.open(FDELNAME, mode='r', buffering=1, encoding="utf-8")
   122     FDEL = io.open(FDELNAME, mode='r', buffering=1, encoding="utf-8")
   118 else:
   123 else:
   119     FDEL = None
   124     FDEL = None
   120 
   125 
   121 import hashlib
   126 ################################################################
   122 
       
   123 import anki
       
   124 from anki.exporting import AnkiPackageExporter
       
   125 
       
   126 
   127 
   127 MODEL_CSS = """
   128 MODEL_CSS = """
   128 .card {
   129 .card {
   129   font-family: arial;
   130   font-family: arial;
   130   font-size: 20px;
   131   font-size: 20px;
   207         self.collection = collection = anki.Collection(os.path.join(self.tmpdir, 'collection.anki2'))
   208         self.collection = collection = anki.Collection(os.path.join(self.tmpdir, 'collection.anki2'))
   208 
   209 
   209         deck_id = collection.decks.id(self.name)
   210         deck_id = collection.decks.id(self.name)
   210 
   211 
   211         # It is essential to keep model['id'] unchanged between upgrades!!
   212         # It is essential to keep model['id'] unchanged between upgrades!!
   212         MODEL_ID = int(hashlib.sha1(self.name).hexdigest(), 16) % (2**63)
   213         model_id = int(hashlib.sha1(self.name).hexdigest(), 16) % (2**63)
   213 
   214 
   214         ################################################################
   215         ################################################################
   215         # Regular card model. SafeBack doesn't include examples to not spoil
   216         # Regular card model. SafeBack doesn't include examples to not spoil
   216         # word spelling.
   217         # word spelling.
   217         model = collection.models.new(self.name + "_frontback")
   218         model = collection.models.new(self.name + "_frontback")
   234         collection.models.addTemplate(model, tmpl)
   235         collection.models.addTemplate(model, tmpl)
   235 
   236 
   236         # Equivalent of ``collection.models.add(model)`` without setting
   237         # Equivalent of ``collection.models.add(model)`` without setting
   237         # auto-generated ID.
   238         # auto-generated ID.
   238         # Increment +1 is for keeping model['id'] unique from previous v0.9 release.
   239         # Increment +1 is for keeping model['id'] unique from previous v0.9 release.
   239         model['id'] = MODEL_ID + 1
   240         model['id'] = model_id + 1
   240         collection.models.update(model)
   241         collection.models.update(model)
   241         collection.models.save(model)
   242         collection.models.save(model)
   242         self.model = model
   243         self.model = model
   243         # collection.models.setCurrent(model)
   244         # collection.models.setCurrent(model)
   244 
   245 
   285         tmpl = collection.models.newTemplate('V3alt -> Back')
   286         tmpl = collection.models.newTemplate('V3alt -> Back')
   286         tmpl['qfmt'] = question + '<div class="front">{{V3alt}}</div>'
   287         tmpl['qfmt'] = question + '<div class="front">{{V3alt}}</div>'
   287         tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="freq">{{Freq}}</div><div class="back">{{Back}}</div>'
   288         tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="freq">{{Freq}}</div><div class="back">{{Back}}</div>'
   288         collection.models.addTemplate(model, tmpl)
   289         collection.models.addTemplate(model, tmpl)
   289 
   290 
   290         model['id'] = MODEL_ID + 2          # Keep model['id'] unique.
   291         model['id'] = model_id + 2          # Keep model['id'] unique.
   291         collection.models.update(model)
   292         collection.models.update(model)
   292         collection.models.save(model)
   293         collection.models.save(model)
   293         self.model_irr = model
   294         self.model_irr = model
   294 
   295 
   295         ################################################################
   296         ################################################################
   314         tmpl = collection.models.newTemplate('Plural -> Back')
   315         tmpl = collection.models.newTemplate('Plural -> Back')
   315         tmpl['qfmt'] = question + '<div class="front">{{Plural}}</div>'
   316         tmpl['qfmt'] = question + '<div class="front">{{Plural}}</div>'
   316         tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="freq">{{Freq}}</div><div class="back">{{Back}}</div>'
   317         tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="freq">{{Freq}}</div><div class="back">{{Back}}</div>'
   317         collection.models.addTemplate(model, tmpl)
   318         collection.models.addTemplate(model, tmpl)
   318 
   319 
   319         model['id'] = MODEL_ID + 3          # Keep model['id'] unique.
   320         model['id'] = model_id + 3          # Keep model['id'] unique.
   320         collection.models.update(model)
   321         collection.models.update(model)
   321         collection.models.save(model)
   322         collection.models.save(model)
   322         self.model_pl = model
   323         self.model_pl = model
   323 
   324 
   324 
   325 
   327         :nodetype  used to generate different notes from same headword
   328         :nodetype  used to generate different notes from same headword
   328         """
   329         """
   329         h = hashlib.md5(":".join((self.name, nodetype, headword)))
   330         h = hashlib.md5(":".join((self.name, nodetype, headword)))
   330         return h.hexdigest()
   331         return h.hexdigest()
   331 
   332 
   332     def add_note(self, headword, front, back, safeback, freq, tags = None):
   333     def add_note(self, headword, front, back, safeback, freq, tags=None):
   333         note = anki.notes.Note(self.collection, self.model)
   334         note = anki.notes.Note(self.collection, self.model)
   334         note['Front'] = front
   335         note['Front'] = front
   335         note['Back'] = back
   336         note['Back'] = back
   336         note['SafeBack'] = safeback
   337         note['SafeBack'] = safeback
   337         note['Freq'] = freq
   338         note['Freq'] = freq
   338         note_add_tags(note, tags)
   339         note_add_tags(note, tags)
   339         note.guid = self.guid("front/back", headword)
   340         note.guid = self.guid("front/back", headword)
   340         self.collection.addNote(note)
   341         self.collection.addNote(note)
   341 
   342 
   342     def add_note_irr(self, headword, v1, v2, v2alt, v3, v3alt, front, back, freq, tags = None):
   343     def add_note_irr(self, headword, v1, v2, v2alt, v3, v3alt, front, back, freq, tags=None):
   343         note = anki.notes.Note(self.collection, self.model_irr)
   344         note = anki.notes.Note(self.collection, self.model_irr)
   344         note['V1'] = v1
   345         note['V1'] = v1
   345         note['V2'] = v2
   346         note['V2'] = v2
   346         note['V3'] = v3
   347         note['V3'] = v3
   347         note['V2alt'] = v2alt
   348         note['V2alt'] = v2alt
   351         note['Freq'] = freq
   352         note['Freq'] = freq
   352         note_add_tags(note, tags)
   353         note_add_tags(note, tags)
   353         note.guid = self.guid("irregular verb", headword)
   354         note.guid = self.guid("irregular verb", headword)
   354         self.collection.addNote(note)
   355         self.collection.addNote(note)
   355 
   356 
   356     def add_note_pl(self, headword, singular, plural, front, back, freq, tags = None):
   357     def add_note_pl(self, headword, singular, plural, front, back, freq, tags=None):
   357         note = anki.notes.Note(self.collection, self.model_pl)
   358         note = anki.notes.Note(self.collection, self.model_pl)
   358         note['Singular'] = singular
   359         note['Singular'] = singular
   359         note['Plural'] = plural
   360         note['Plural'] = plural
   360         note['Front'] = front
   361         note['Front'] = front
   361         note['Back'] = back
   362         note['Back'] = back
   369         export.exportInto(fname)
   370         export.exportInto(fname)
   370 
   371 
   371     def close(self):
   372     def close(self):
   372         self.collection.close()
   373         self.collection.close()
   373 
   374 
   374 def write_sense(buf, sense, with_examples = True):
   375 def write_sense(buf, sense, with_examples=True):
   375     buf.append("<div class='sense'>")
   376     buf.append("<div class='sense'>")
   376     if sense.pos:
   377     if sense.pos:
   377         buf.append("<span class='pos'>")
   378         buf.append("<span class='pos'>")
   378         buf.append(sense.pos)
   379         buf.append(sense.pos)
   379         buf.append("</span>")
   380         buf.append("</span>")
   437         buf.append(tr)
   438         buf.append(tr)
   438         buf.append("</span>")
   439         buf.append("</span>")
   439         buf.append("</div>")
   440         buf.append("</div>")
   440 
   441 
   441 try:
   442 try:
   442     builder = AnkiDbBuilder(TMPDIR, NAME)
   443     BUILDER = AnkiDbBuilder(TMPDIR, NAME)
   443 
   444 
   444     for identity in FDEL or []:
   445     for identity in FDEL or []:
   445         identity = identity.strip()
   446         identity = identity.strip()
   446         warnmsg = "<div class='del'>Please delete this note ({})</div>".format(identity)
   447         warnmsg = "<div class='del'>Please delete this note ({})</div>".format(identity)
   447         builder.add_note(identity, warnmsg, warnmsg, warnmsg, "del")
   448         BUILDER.add_note(identity, warnmsg, warnmsg, warnmsg, "del")
   448         builder.add_note_irr(identity, warnmsg, warnmsg, warnmsg, warnmsg, warnmsg, warnmsg, warnmsg, "del")
   449         BUILDER.add_note_irr(identity, warnmsg, warnmsg, warnmsg, warnmsg, warnmsg, warnmsg, warnmsg, "del")
   449         builder.add_note_pl(identity, warnmsg, warnmsg, warnmsg, warnmsg, "del")
   450         BUILDER.add_note_pl(identity, warnmsg, warnmsg, warnmsg, warnmsg, "del")
   450 
   451 
   451     for (headwords, translations) in DOM[1:]:
   452     for (headwords, translations) in DOM[1:]:
   452         identity = headwords[0].headword
   453         identity = headwords[0].headword
   453         if 'rare' in headwords[0].attrs:
   454         if 'rare' in headwords[0].attrs:
   454             continue
   455             continue
   495                 plural = (hw.headword, hw.pron)
   496                 plural = (hw.headword, hw.pron)
   496             buf.append("</div>")
   497             buf.append("</div>")
   497         direct_from = "".join(buf)
   498         direct_from = "".join(buf)
   498         buf = []
   499         buf = []
   499         for sense in translations:
   500         for sense in translations:
   500             write_sense(buf, sense, with_examples = True)
   501             write_sense(buf, sense, with_examples=True)
   501         direct_to = "".join(buf)
   502         direct_to = "".join(buf)
   502         buf = []
   503         buf = []
   503         for sense in translations:
   504         for sense in translations:
   504             write_sense(buf, sense, with_examples = False)
   505             write_sense(buf, sense, with_examples=False)
   505         reverse_from = "".join(buf)         # without examples!!
   506         reverse_from = "".join(buf)         # without examples!!
   506         builder.add_note(identity, direct_from, direct_to, reverse_from, freqmsg, freqtags)
   507         BUILDER.add_note(identity, direct_from, direct_to, reverse_from, freqmsg, freqtags)
   507         if v1 and v2 and v3 and RICH_MODE:
   508         if v1 and v2 and v3 and RICH_MODE:
   508             riddle1 = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v1</span>".format(v1[0], v1[1])
   509             riddle1 = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v1</span>".format(v1[0], v1[1])
   509             riddle2 = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v2</span>".format(v2[0], v2[1])
   510             riddle2 = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v2</span>".format(v2[0], v2[1])
   510             riddle3 = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v3</span>".format(v3[0], v3[1])
   511             riddle3 = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v3</span>".format(v3[0], v3[1])
   511             if v2alt:
   512             if v2alt:
   512                 riddle2alt = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v2</span>".format(v2alt[0], v2alt[1])
   513                 riddle2alt = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v2</span>".format(v2alt[0], v2alt[1])
   513             else:
   514             else:
   514                 riddle2alt = ""
   515                 riddle2alt = u""
   515             if v3alt:
   516             if v3alt:
   516                 riddle3alt = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v2</span>".format(v3alt[0], v3alt[1])
   517                 riddle3alt = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v2</span>".format(v3alt[0], v3alt[1])
   517             else:
   518             else:
   518                 riddle3alt = ""
   519                 riddle3alt = u""
   519             builder.add_note_irr(identity, riddle1, riddle2, riddle2alt, riddle3, riddle3alt, direct_from, direct_to, freqmsg)
   520             BUILDER.add_note_irr(identity, riddle1, riddle2, riddle2alt, riddle3, riddle3alt, direct_from, direct_to, freqmsg)
   520         if singular and plural and RICH_MODE:
   521         if singular and plural and RICH_MODE:
   521             riddle_s = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>s</span>".format(singular[0], singular[1])
   522             riddle_s = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>s</span>".format(singular[0], singular[1])
   522             riddle_pl = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>pl</span>".format(plural[0], plural[1])
   523             riddle_pl = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>pl</span>".format(plural[0], plural[1])
   523             builder.add_note_pl(identity, riddle_s, riddle_pl, direct_from, direct_to, freqmsg)
   524             BUILDER.add_note_pl(identity, riddle_s, riddle_pl, direct_from, direct_to, freqmsg)
   524 
   525 
   525     builder.export(FONAME)
   526     BUILDER.export(FONAME)
   526 finally:
   527 finally:
   527     builder.close()
   528     BUILDER.close()
   528     shutil.rmtree(TMPDIR, ignore_errors=True)
   529     shutil.rmtree(TMPDIR, ignore_errors=True)