# -*- coding: utf-8 -*- # Copyright: Damien Elmes # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import time, re from anki.db import DB from anki.importing.noteimp import NoteImporter, ForeignNote, ForeignCard from anki.stdmodels import addBasicModel, addClozeModel from anki.lang import ngettext class MnemosyneImporter(NoteImporter): needMapper = False update = False allowHTML = True def run(self): db = DB(self.file) ver = db.scalar( "select value from global_variables where key='version'") assert ver.startswith('Mnemosyne SQL 1') or ver == "2" # gather facts into temp objects curid = None notes = {} note = None for _id, id, k, v in db.execute(""" select _id, id, key, value from facts f, data_for_fact d where f._id=d._fact_id"""): if id != curid: if note: notes[note['_id']] = note note = {'_id': _id} curid = id note[k] = v if note: notes[note['_id']] = note # gather cards front = [] frontback = [] vocabulary = [] cloze = {} for row in db.execute(""" select _fact_id, fact_view_id, tags, next_rep, last_rep, easiness, acq_reps+ret_reps, lapses, card_type_id from cards"""): # categorize note note = notes[row[0]] if row[1].endswith(".1"): if row[1].startswith("1.") or row[1].startswith("1::"): front.append(note) elif row[1].startswith("2.") or row[1].startswith("2::"): frontback.append(note) elif row[1].startswith("3.") or row[1].startswith("3::"): vocabulary.append(note) elif row[1].startswith("5.1"): cloze[row[0]] = note # merge tags into note tags = row[2].replace(", ", "\x1f").replace(" ", "_") tags = tags.replace("\x1f", " ") if "tags" not in note: note['tags'] = [] note['tags'] += self.col.tags.split(tags) note['tags'] = self.col.tags.canonify(note['tags']) # if it's a new card we can go with the defaults if row[3] == -1: continue # add the card c = ForeignCard() c.factor = int(row[5]*1000) c.reps = row[6] c.lapses = row[7] # ivl is inferred in mnemosyne next, prev = row[3:5] c.ivl = max(1, (next - prev)/86400) # work out how long we've got left rem = int((next - time.time())/86400) c.due = self.col.sched.today+rem # get ord m = re.search(".(\d+)$", row[1]) ord = int(m.group(1))-1 if 'cards' not in note: note['cards'] = {} note['cards'][ord] = c self._addFronts(front) total = self.total self._addFrontBacks(frontback) total += self.total self._addVocabulary(vocabulary) self.total += total self._addCloze(cloze) self.total += total self.log.append(ngettext("%d note imported.", "%d notes imported.", self.total) % self.total) def fields(self): return self._fields def _mungeField(self, fld): # \n -> br fld = re.sub("\r?\n", "
", fld) # latex differences fld = re.sub("(?i)<(/?(\$|\$\$|latex))>", "[\\1]", fld) # audio differences fld = re.sub(")?", "[sound:\\1]", fld) return fld def _addFronts(self, notes, model=None, fields=("f", "b")): data = [] for orig in notes: # create a foreign note object n = ForeignNote() n.fields = [] for f in fields: fld = self._mungeField(orig.get(f, '')) n.fields.append(fld) n.tags = orig['tags'] n.cards = orig.get('cards', {}) data.append(n) # add a basic model if not model: model = addBasicModel(self.col) model['name'] = "Mnemosyne-FrontOnly" mm = self.col.models mm.save(model) mm.setCurrent(model) self.model = model self._fields = len(model['flds']) self.initMapping() # import self.importNotes(data) def _addFrontBacks(self, notes): m = addBasicModel(self.col) m['name'] = "Mnemosyne-FrontBack" mm = self.col.models t = mm.newTemplate("Back") t['qfmt'] = "{{Back}}" t['afmt'] = t['qfmt'] + "\n\n
\n\n{{Front}}" mm.addTemplate(m, t) self._addFronts(notes, m) def _addVocabulary(self, notes): mm = self.col.models m = mm.new("Mnemosyne-Vocabulary") for f in "Expression", "Pronunciation", "Meaning", "Notes": fm = mm.newField(f) mm.addField(m, fm) t = mm.newTemplate("Recognition") t['qfmt'] = "{{Expression}}" t['afmt'] = t['qfmt'] + """\n\n
\n\n\ {{Pronunciation}}
\n{{Meaning}}
\n{{Notes}}""" mm.addTemplate(m, t) t = mm.newTemplate("Production") t['qfmt'] = "{{Meaning}}" t['afmt'] = t['qfmt'] + """\n\n
\n\n\ {{Expression}}
\n{{Pronunciation}}
\n{{Notes}}""" mm.addTemplate(m, t) mm.add(m) self._addFronts(notes, m, fields=("f", "p_1", "m_1", "n")) def _addCloze(self, notes): data = [] notes = list(notes.values()) for orig in notes: # create a foreign note object n = ForeignNote() n.fields = [] fld = orig.get("text", "") fld = re.sub("\r?\n", "
", fld) state = dict(n=1) def repl(match): # replace [...] with cloze refs res = ("{{c%d::%s}}" % (state['n'], match.group(1))) state['n'] += 1 return res fld = re.sub("\[(.+?)\]", repl, fld) fld = self._mungeField(fld) n.fields.append(fld) n.fields.append("") # extra n.tags = orig['tags'] n.cards = orig.get('cards', {}) data.append(n) # add cloze model model = addClozeModel(self.col) model['name'] = "Mnemosyne-Cloze" mm = self.col.models mm.save(model) mm.setCurrent(model) self.model = model self._fields = len(model['flds']) self.initMapping() self.importNotes(data)