mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
190 lines
6.6 KiB
Python
190 lines
6.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright: Damien Elmes <anki@ichi2.net>
|
|
# 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')
|
|
# 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", "<br>", fld)
|
|
# latex differences
|
|
fld = re.sub("(?i)<(/?(\$|\$\$|latex))>", "[\\1]", fld)
|
|
# audio differences
|
|
fld = re.sub("<audio src=\"(.+?)\">(</audio>)?", "[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<hr id=answer>\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<hr id=answer>\n\n\
|
|
{{Pronunciation}}<br>\n{{Meaning}}<br>\n{{Notes}}"""
|
|
mm.addTemplate(m, t)
|
|
t = mm.newTemplate("Production")
|
|
t['qfmt'] = "{{Meaning}}"
|
|
t['afmt'] = t['qfmt'] + """\n\n<hr id=answer>\n\n\
|
|
{{Expression}}<br>\n{{Pronunciation}}<br>\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 = notes.values()
|
|
for orig in notes:
|
|
# create a foreign note object
|
|
n = ForeignNote()
|
|
n.fields = []
|
|
fld = orig.get("text", "")
|
|
fld = re.sub("\r?\n", "<br>", 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)
|