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