203 color: red; |
190 color: red; |
204 font-weight: bold; |
191 font-weight: bold; |
205 } |
192 } |
206 """ |
193 """ |
207 |
194 |
208 collection.models.addField(model, collection.models.newField('From')) |
195 def note_add_tags(note, tags): |
209 collection.models.addField(model, collection.models.newField('To')) |
196 if isinstance(tags, str): note.tags = [tags] |
210 |
197 elif isinstance(tags, list): note.tags = tags |
211 tmpl = collection.models.newTemplate('From -> To') |
198 elif tags is None: pass |
212 tmpl['qfmt'] = '<div class="from">{{From}}</div>' |
199 else: raise Exception('Expecting string or list of tags...') |
213 tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="to">{{To}}</div>' |
200 |
214 collection.models.addTemplate(model, tmpl) |
201 |
215 |
202 class AnkiDbBuilder: |
216 # Equivalent of: |
203 |
217 # collection.models.add(model) |
204 def __init__(self, tmpdir, name): |
218 # without setting auto-generated ID. It is essential to keep model['id'] |
205 self.tmpdir = tmpdir |
219 # unchanged between upgrades or notes will be skipped!! |
206 self.name = name |
220 model['id'] = int(hashlib.sha1(self.name).hexdigest(), 16) % (2**63) |
207 |
|
208 self.collection = collection = anki.Collection(os.path.join(self.tmpdir, 'collection.anki2')) |
|
209 |
|
210 deck_id = collection.decks.id(self.name) |
|
211 deck = collection.decks.get(deck_id) |
|
212 |
|
213 # It is essential to keep model['id'] unchanged between upgrades!! |
|
214 MODEL_ID = int(hashlib.sha1(self.name).hexdigest(), 16) % (2**63) |
|
215 |
|
216 ################################################################ |
|
217 # Regular card model. SafeBack doesn't include examples to not spoil |
|
218 # word spelling. |
|
219 model = collection.models.new(self.name + "_frontback") |
|
220 model['did'] = deck_id |
|
221 model['css'] = MODEL_CSS |
|
222 |
|
223 collection.models.addField(model, collection.models.newField('Front')) |
|
224 collection.models.addField(model, collection.models.newField('Back')) |
|
225 collection.models.addField(model, collection.models.newField('SafeBack')) |
|
226 |
|
227 tmpl = collection.models.newTemplate('Front -> Back') |
|
228 tmpl['qfmt'] = '<div class="front">{{Front}}</div>' |
|
229 tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Back}}</div>' |
|
230 collection.models.addTemplate(model, tmpl) |
|
231 |
|
232 tmpl = collection.models.newTemplate('SafeBack -> Front') |
|
233 tmpl['qfmt'] = '<div class="safe-back">{{SafeBack}}</div>' |
|
234 tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="front">{{Front}}</div>' |
|
235 collection.models.addTemplate(model, tmpl) |
|
236 |
|
237 # Equivalent of ``collection.models.add(model)`` without setting |
|
238 # auto-generated ID. |
|
239 # Increment +1 is for keeping model['id'] unique from previous v0.9 release. |
|
240 model['id'] = MODEL_ID + 1 |
221 collection.models.update(model) |
241 collection.models.update(model) |
222 collection.models.setCurrent(model) |
|
223 collection.models.save(model) |
242 collection.models.save(model) |
224 |
243 self.model = model |
225 def guid(self, type_, headword): |
244 # collection.models.setCurrent(model) |
|
245 |
|
246 if not RICH_MODE: |
|
247 return |
|
248 |
|
249 ################################################################ |
|
250 # Model for irregular verbs. |
|
251 model = collection.models.new(self.name + "_irrverb") |
|
252 model['did'] = deck_id |
|
253 model['css'] = MODEL_CSS |
|
254 |
|
255 collection.models.addField(model, collection.models.newField('V1')) |
|
256 collection.models.addField(model, collection.models.newField('V2')) |
|
257 collection.models.addField(model, collection.models.newField('V3')) |
|
258 collection.models.addField(model, collection.models.newField('V2alt')) |
|
259 collection.models.addField(model, collection.models.newField('V3alt')) |
|
260 collection.models.addField(model, collection.models.newField('Front')) |
|
261 collection.models.addField(model, collection.models.newField('Back')) |
|
262 |
|
263 question = u"<div class='ask'>Find irregular verb:</div>" |
|
264 |
|
265 tmpl = collection.models.newTemplate('V1 -> Back') |
|
266 tmpl['qfmt'] = question + '<div class="front">{{V1}}</div>' |
|
267 tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="back">{{Back}}</div>' |
|
268 collection.models.addTemplate(model, tmpl) |
|
269 |
|
270 tmpl = collection.models.newTemplate('V2 -> Back') |
|
271 tmpl['qfmt'] = question + '<div class="front">{{V2}}</div>' |
|
272 tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="back">{{Back}}</div>' |
|
273 collection.models.addTemplate(model, tmpl) |
|
274 |
|
275 tmpl = collection.models.newTemplate('V3 -> Back') |
|
276 tmpl['qfmt'] = question + '<div class="front">{{V3}}</div>' |
|
277 tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="back">{{Back}}</div>' |
|
278 collection.models.addTemplate(model, tmpl) |
|
279 |
|
280 tmpl = collection.models.newTemplate('V2alt -> Back') |
|
281 tmpl['qfmt'] = question + '<div class="front">{{V2alt}}</div>' |
|
282 tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="back">{{Back}}</div>' |
|
283 collection.models.addTemplate(model, tmpl) |
|
284 |
|
285 tmpl = collection.models.newTemplate('V3alt -> Back') |
|
286 tmpl['qfmt'] = question + '<div class="front">{{V3alt}}</div>' |
|
287 tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="back">{{Back}}</div>' |
|
288 collection.models.addTemplate(model, tmpl) |
|
289 |
|
290 model['id'] = MODEL_ID + 2 # Keep model['id'] unique. |
|
291 collection.models.update(model) |
|
292 collection.models.save(model) |
|
293 self.model_irr = model |
|
294 |
|
295 ################################################################ |
|
296 # Model for irregular plurals. |
|
297 model = collection.models.new(self.name + "_plural") |
|
298 model['did'] = deck_id |
|
299 model['css'] = MODEL_CSS |
|
300 |
|
301 collection.models.addField(model, collection.models.newField('Singular')) |
|
302 collection.models.addField(model, collection.models.newField('Plural')) |
|
303 collection.models.addField(model, collection.models.newField('Front')) |
|
304 collection.models.addField(model, collection.models.newField('Back')) |
|
305 |
|
306 question = u"<div class='ask'>Find singular/plural form:</div>" |
|
307 |
|
308 tmpl = collection.models.newTemplate('Singular -> Back') |
|
309 tmpl['qfmt'] = question + '<div class="front">{{Singular}}</div>' |
|
310 tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="back">{{Back}}</div>' |
|
311 collection.models.addTemplate(model, tmpl) |
|
312 |
|
313 tmpl = collection.models.newTemplate('Plural -> Back') |
|
314 tmpl['qfmt'] = question + '<div class="front">{{Plural}}</div>' |
|
315 tmpl['afmt'] = '{{FrontSide}}<hr id=answer><div class="back">{{Front}}</div><div class="back">{{Back}}</div>' |
|
316 collection.models.addTemplate(model, tmpl) |
|
317 |
|
318 model['id'] = MODEL_ID + 3 # Keep model['id'] unique. |
|
319 collection.models.update(model) |
|
320 collection.models.save(model) |
|
321 self.model_pl = model |
|
322 |
|
323 |
|
324 def guid(self, nodetype, headword): |
226 """ |
325 """ |
227 :type_ used to generate different notes from same headword |
326 :nodetype used to generate different notes from same headword |
228 """ |
327 """ |
229 h = hashlib.md5(":".join((self.name, type_, headword))) |
328 h = hashlib.md5(":".join((self.name, nodetype, headword))) |
230 return h.hexdigest() |
329 return h.hexdigest() |
231 |
330 |
232 def add_note(self, type_, id_, from_, to_, tags_ = None): |
331 def add_note(self, headword, front, back, safeback, tags = None): |
233 note = self.collection.newNote() |
332 note = anki.notes.Note(self.collection, self.model) |
234 note['From'] = from_ |
333 note['Front'] = front |
235 # print(from_) |
334 note['Back'] = back |
236 note['To'] = to_ |
335 note['SafeBack'] = safeback |
237 # print(to_) |
336 note_add_tags(note, tags) |
238 if isinstance(tags_, str): note.tags = [tags_] |
337 note.guid = self.guid("front/back", headword) |
239 elif isinstance(tags_, list): note.tags = tags_ |
338 self.collection.addNote(note) |
240 elif tags_ is None: pass |
339 |
241 else: raise Exception('Expect string or list of tags...') |
340 def add_note_irr(self, headword, v1, v2, v2alt, v3, v3alt, front, back, tags = None): |
242 note.guid = self.guid(type_, id_) |
341 note = anki.notes.Note(self.collection, self.model_irr) |
|
342 note['V1'] = v1 |
|
343 note['V2'] = v2 |
|
344 note['V3'] = v3 |
|
345 note['V2alt'] = v2alt |
|
346 note['V3alt'] = v3alt |
|
347 note['Front'] = front |
|
348 note['Back'] = back |
|
349 note_add_tags(note, tags) |
|
350 note.guid = self.guid("irregular verb", headword) |
|
351 self.collection.addNote(note) |
|
352 |
|
353 def add_note_pl(self, headword, singular, plural, front, back, tags = None): |
|
354 note = anki.notes.Note(self.collection, self.model_pl) |
|
355 note['Singular'] = singular |
|
356 note['Plural'] = plural |
|
357 note['Front'] = front |
|
358 note['Back'] = back |
|
359 note_add_tags(note, tags) |
|
360 note.guid = self.guid("singular/plural noun", headword) |
243 self.collection.addNote(note) |
361 self.collection.addNote(note) |
244 |
362 |
245 def export(self, fname): |
363 def export(self, fname): |
246 export = AnkiPackageExporter(self.collection) |
364 export = AnkiPackageExporter(self.collection) |
247 export.exportInto(fname) |
365 export.exportInto(fname) |
315 builder = AnkiDbBuilder(TMPDIR, NAME) |
433 builder = AnkiDbBuilder(TMPDIR, NAME) |
316 |
434 |
317 for identity in FDEL or []: |
435 for identity in FDEL or []: |
318 identity = identity.strip() |
436 identity = identity.strip() |
319 warnmsg = "<div class='del'>Please delete this note ({})</div>".format(identity) |
437 warnmsg = "<div class='del'>Please delete this note ({})</div>".format(identity) |
320 builder.add_note("en->tr", identity, warnmsg, warnmsg+" en->tr", "del") |
438 builder.add_note(identity, warnmsg, warnmsg, warnmsg, "del") |
321 builder.add_note("tr->en", identity, warnmsg, warnmsg+" tr->en", "del") |
439 builder.add_note_irr(identity, warnmsg, warnmsg, warnmsg, warnmsg, warnmsg, warnmsg, warnmsg, "del") |
322 builder.add_note("irregular1", identity, warnmsg, warnmsg+" irregular1", "del") |
440 builder.add_note_pl(identity, warnmsg, warnmsg, warnmsg, warnmsg, "del") |
323 builder.add_note("irregular2", identity, warnmsg, warnmsg+" irregular2", "del") |
|
324 builder.add_note("irregular3", identity, warnmsg, warnmsg+" irregular3", "del") |
|
325 builder.add_note("singular", identity, warnmsg, warnmsg+" singular", "del") |
|
326 builder.add_note("plural", identity, warnmsg, warnmsg+" plural", "del") |
|
327 |
441 |
328 for (headwords, translations) in DOM[1:]: |
442 for (headwords, translations) in DOM[1:]: |
329 identity = headwords[0].headword |
443 identity = headwords[0].headword |
330 if 'rare' in headwords[0].attrs: |
444 if 'rare' in headwords[0].attrs: |
331 continue |
445 continue |
373 direct_to = "".join(buf) |
493 direct_to = "".join(buf) |
374 buf = [] |
494 buf = [] |
375 for sense in translations: |
495 for sense in translations: |
376 write_sense(buf, sense, with_examples = False) |
496 write_sense(buf, sense, with_examples = False) |
377 reverse_from = "".join(buf) # without examples!! |
497 reverse_from = "".join(buf) # without examples!! |
378 builder.add_note("en->tr", identity, direct_from + freqmsg, direct_to) |
498 builder.add_note(identity, direct_from, direct_to, reverse_from) |
379 builder.add_note("tr->en", identity, reverse_from, direct_from + freqmsg) |
|
380 if v1 and v2 and v3 and RICH_MODE: |
499 if v1 and v2 and v3 and RICH_MODE: |
381 question = u"<div class='ask'>Find irregular verb:</div>" |
|
382 riddle1 = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v1</span>".format(v1[0], v1[1]) |
500 riddle1 = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v1</span>".format(v1[0], v1[1]) |
383 riddle2 = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v2</span>".format(v2[0], v2[1]) |
501 riddle2 = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v2</span>".format(v2[0], v2[1]) |
384 riddle3 = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v3</span>".format(v3[0], v3[1]) |
502 riddle3 = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v3</span>".format(v3[0], v3[1]) |
385 answer = direct_from + direct_to |
503 if v2alt: |
386 builder.add_note("irregular1", identity, question + riddle1 + freqmsg, answer) |
504 riddle2alt = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v2</span>".format(v2alt[0], v2alt[1]) |
387 builder.add_note("irregular2", identity, question + riddle2 + freqmsg, answer) |
505 else: |
388 builder.add_note("irregular3", identity, question + riddle3 + freqmsg, answer) |
506 riddle2alt = "" |
|
507 if v3alt: |
|
508 riddle3alt = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>v2</span>".format(v3alt[0], v3alt[1]) |
|
509 else: |
|
510 riddle3alt = "" |
|
511 builder.add_note_irr(identity, riddle1 + freqmsg, riddle2 + freqmsg, riddle2alt, riddle3 + freqmsg, riddle3alt, direct_from, direct_to) |
389 if singular and plural and RICH_MODE: |
512 if singular and plural and RICH_MODE: |
390 question = u"<div class='ask'>Find plural:</div>" |
|
391 riddle_s = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>s</span>".format(singular[0], singular[1]) |
513 riddle_s = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>s</span>".format(singular[0], singular[1]) |
392 riddle_pl = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>pl</span>".format(plural[0], plural[1]) |
514 riddle_pl = u"<span class='headword'>{}</span> <span class='pron'>[{}]</span> <span class='attrs'>pl</span>".format(plural[0], plural[1]) |
393 answer = direct_from + direct_to |
515 builder.add_note_pl(identity, riddle_s + freqmsg, riddle_pl + freqmsg, direct_from, direct_to) |
394 builder.add_note("singular", identity, question + riddle_s + freqmsg, answer) |
|
395 builder.add_note("plural", identity, question + riddle_pl + freqmsg, answer) |
|
396 |
516 |
397 builder.export(FONAME) |
517 builder.export(FONAME) |
398 finally: |
518 finally: |
399 builder.close() |
519 builder.close() |
400 shutil.rmtree(TMPDIR, ignore_errors=True) |
520 shutil.rmtree(TMPDIR, ignore_errors=True) |