mirror of
https://github.com/ankitects/anki.git
synced 2025-11-09 14:17:13 -05:00
csv importing
This commit is contained in:
parent
562f33635c
commit
03c417daa2
4 changed files with 87 additions and 169 deletions
201
aqt/importing.py
201
aqt/importing.py
|
|
@ -5,8 +5,9 @@ import os, copy, time, sys, re, traceback
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
import anki
|
import anki
|
||||||
import anki.importing as importing
|
import anki.importing as importing
|
||||||
from aqt.utils import getOnlyText, getFile, showText
|
from aqt.utils import getOnlyText, getFile, showText, showWarning
|
||||||
from anki.errors import *
|
from anki.errors import *
|
||||||
|
from anki.hooks import addHook, remHook
|
||||||
import aqt.forms, aqt.modelchooser
|
import aqt.forms, aqt.modelchooser
|
||||||
|
|
||||||
class ChangeMap(QDialog):
|
class ChangeMap(QDialog):
|
||||||
|
|
@ -18,20 +19,23 @@ class ChangeMap(QDialog):
|
||||||
self.frm.setupUi(self)
|
self.frm.setupUi(self)
|
||||||
n = 0
|
n = 0
|
||||||
setCurrent = False
|
setCurrent = False
|
||||||
for field in self.model.fieldModels:
|
for field in self.model['flds']:
|
||||||
item = QListWidgetItem(_("Map to %s") % field.name)
|
item = QListWidgetItem(_("Map to %s") % field['name'])
|
||||||
self.frm.fields.addItem(item)
|
self.frm.fields.addItem(item)
|
||||||
if current and current.name == field.name:
|
if current == field['name']:
|
||||||
setCurrent = True
|
setCurrent = True
|
||||||
self.frm.fields.setCurrentRow(n)
|
self.frm.fields.setCurrentRow(n)
|
||||||
n += 1
|
n += 1
|
||||||
self.frm.fields.addItem(QListWidgetItem(_("Map to Tags")))
|
self.frm.fields.addItem(QListWidgetItem(_("Map to Tags")))
|
||||||
|
self.frm.fields.addItem(QListWidgetItem(_("Map to Deck")))
|
||||||
self.frm.fields.addItem(QListWidgetItem(_("Discard field")))
|
self.frm.fields.addItem(QListWidgetItem(_("Discard field")))
|
||||||
if not setCurrent:
|
if not setCurrent:
|
||||||
if current == 0:
|
if current == "_tags":
|
||||||
self.frm.fields.setCurrentRow(n)
|
self.frm.fields.setCurrentRow(n)
|
||||||
else:
|
elif current == "_deck":
|
||||||
self.frm.fields.setCurrentRow(n+1)
|
self.frm.fields.setCurrentRow(n+1)
|
||||||
|
else:
|
||||||
|
self.frm.fields.setCurrentRow(n+2)
|
||||||
self.field = None
|
self.field = None
|
||||||
|
|
||||||
def getField(self):
|
def getField(self):
|
||||||
|
|
@ -40,36 +44,14 @@ class ChangeMap(QDialog):
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
row = self.frm.fields.currentRow()
|
row = self.frm.fields.currentRow()
|
||||||
if row < len(self.model.fieldModels):
|
if row < len(self.model['flds']):
|
||||||
self.field = self.model.fieldModels[row]
|
self.field = self.model['flds'][row]['name']
|
||||||
elif row == self.frm.fields.count() - 1:
|
elif row == self.frm.fields.count() - 3:
|
||||||
self.field = None
|
self.field = "_tags"
|
||||||
|
elif row == self.frm.fields.count() - 2:
|
||||||
|
self.field = "_deck"
|
||||||
else:
|
else:
|
||||||
self.field = 0
|
self.field = None
|
||||||
QDialog.accept(self)
|
|
||||||
|
|
||||||
class UpdateMap(QDialog):
|
|
||||||
def __init__(self, mw, numFields, fieldModels):
|
|
||||||
QDialog.__init__(self, mw, Qt.Window)
|
|
||||||
self.mw = mw
|
|
||||||
self.fieldModels = fieldModels
|
|
||||||
self.frm = aqt.forms.importup.Ui_Dialog()
|
|
||||||
self.frm.setupUi(self)
|
|
||||||
self.connect(self.frm.buttonBox.button(QDialogButtonBox.Help),
|
|
||||||
SIGNAL("clicked()"), self.helpRequested)
|
|
||||||
for i in range(numFields):
|
|
||||||
self.frm.fileField.addItem("Field %d" % (i+1))
|
|
||||||
for m in fieldModels:
|
|
||||||
self.frm.colField.addItem(m.name)
|
|
||||||
self.exec_()
|
|
||||||
|
|
||||||
def helpRequested(self):
|
|
||||||
openHelp("importing")
|
|
||||||
|
|
||||||
def accept(self):
|
|
||||||
self.updateKey = (
|
|
||||||
self.frm.fileField.currentIndex(),
|
|
||||||
self.fieldModels[self.frm.colField.currentIndex()].id)
|
|
||||||
QDialog.accept(self)
|
QDialog.accept(self)
|
||||||
|
|
||||||
class ImportDialog(QDialog):
|
class ImportDialog(QDialog):
|
||||||
|
|
@ -77,26 +59,16 @@ class ImportDialog(QDialog):
|
||||||
def __init__(self, mw, importer):
|
def __init__(self, mw, importer):
|
||||||
QDialog.__init__(self, mw, Qt.Window)
|
QDialog.__init__(self, mw, Qt.Window)
|
||||||
self.mw = mw
|
self.mw = mw
|
||||||
|
self.importer = importer
|
||||||
self.frm = aqt.forms.importing.Ui_ImportDialog()
|
self.frm = aqt.forms.importing.Ui_ImportDialog()
|
||||||
self.frm.setupUi(self)
|
self.frm.setupUi(self)
|
||||||
self.connect(self.frm.buttonBox.button(QDialogButtonBox.Help),
|
self.connect(self.frm.buttonBox.button(QDialogButtonBox.Help),
|
||||||
SIGNAL("clicked()"), self.helpRequested)
|
SIGNAL("clicked()"), self.helpRequested)
|
||||||
self.setupMappingFrame()
|
self.setupMappingFrame()
|
||||||
self.setupOptions()
|
self.setupOptions()
|
||||||
|
self.modelChanged()
|
||||||
if self.importer.needMapper:
|
|
||||||
self.modelChooser.show()
|
|
||||||
else:
|
|
||||||
self.modelChooser.hide()
|
|
||||||
self.frm.groupBox.setShown(False)
|
|
||||||
self.frm.mappingGroup.setTitle("")
|
|
||||||
self.frm.autoDetect.setShown(self.importer.needDelimiter)
|
self.frm.autoDetect.setShown(self.importer.needDelimiter)
|
||||||
|
addHook("currentModelChanged", self.modelChanged)
|
||||||
|
|
||||||
if not self.file:
|
|
||||||
return
|
|
||||||
self.frm.groupBox.setTitle(os.path.basename(self.file))
|
|
||||||
self.maybePreview()
|
|
||||||
self.connect(self.frm.autoDetect, SIGNAL("clicked()"),
|
self.connect(self.frm.autoDetect, SIGNAL("clicked()"),
|
||||||
self.onDelimiter)
|
self.onDelimiter)
|
||||||
self.updateDelimiterButtonText()
|
self.updateDelimiterButtonText()
|
||||||
|
|
@ -108,19 +80,12 @@ class ImportDialog(QDialog):
|
||||||
self.mw, self.frm.modelArea)
|
self.mw, self.frm.modelArea)
|
||||||
self.connect(self.frm.importButton, SIGNAL("clicked()"),
|
self.connect(self.frm.importButton, SIGNAL("clicked()"),
|
||||||
self.doImport)
|
self.doImport)
|
||||||
self.connect(self.frm.updateButton, SIGNAL("clicked()"),
|
|
||||||
self.doUpdate)
|
|
||||||
|
|
||||||
def maybePreview(self):
|
def modelChanged(self):
|
||||||
if self.file and self.importer.needMapper:
|
print "model changed"
|
||||||
self.frm.status.setText("")
|
self.importer.model = self.mw.col.models.current()
|
||||||
self.showMapping()
|
self.importer.initMapping()
|
||||||
else:
|
self.showMapping()
|
||||||
self.hideMapping()
|
|
||||||
|
|
||||||
def modelChanged(self, model):
|
|
||||||
self.model = model
|
|
||||||
self.maybePreview()
|
|
||||||
|
|
||||||
def onDelimiter(self):
|
def onDelimiter(self):
|
||||||
str = getOnlyText(_("""\
|
str = getOnlyText(_("""\
|
||||||
|
|
@ -161,46 +126,28 @@ you can enter it here. Use \\t to represent tab."""),
|
||||||
txt = _("Auto-detected &delimiter: %s") % d
|
txt = _("Auto-detected &delimiter: %s") % d
|
||||||
self.frm.autoDetect.setText(txt)
|
self.frm.autoDetect.setText(txt)
|
||||||
|
|
||||||
def doUpdate(self):
|
|
||||||
f = UpdateMap(self,
|
|
||||||
self.importer.fields(),
|
|
||||||
self.model.fieldModels)
|
|
||||||
if not getattr(f, "updateKey", None):
|
|
||||||
# user cancelled
|
|
||||||
return
|
|
||||||
self.importer.updateKey = f.updateKey
|
|
||||||
self.doImport(True)
|
|
||||||
|
|
||||||
def doImport(self, update=False):
|
def doImport(self, update=False):
|
||||||
self.frm.status.setText(_("Importing..."))
|
|
||||||
t = time.time()
|
t = time.time()
|
||||||
self.importer.mapping = self.mapping
|
self.importer.mapping = self.mapping
|
||||||
|
self.mw.progress.start(immediate=True)
|
||||||
|
self.mw.checkpoint(_("Import"))
|
||||||
try:
|
try:
|
||||||
n = _("Import")
|
self.importer.run()
|
||||||
self.mw.col.setUndoStart(n)
|
except Exception, e:
|
||||||
try:
|
msg = _("Import failed.\n")
|
||||||
self.importer.run()
|
msg += unicode(traceback.format_exc(), "ascii", "replace")
|
||||||
except Exception, e:
|
showText(msg)
|
||||||
msg = _("Import failed.\n")
|
return
|
||||||
msg += unicode(traceback.format_exc(), "ascii", "replace")
|
|
||||||
self.frm.status.setText(msg)
|
|
||||||
return
|
|
||||||
finally:
|
finally:
|
||||||
self.mw.col.finishProgress()
|
self.mw.progress.finish()
|
||||||
self.mw.col.setUndoEnd(n)
|
|
||||||
txt = (
|
txt = (
|
||||||
_("Importing complete. %(num)d notes imported from %(file)s.\n") %
|
_("Importing complete. %(num)d notes imported or updated.\n") %
|
||||||
{"num": self.importer.total, "file": os.path.basename(self.file)})
|
{"num": self.importer.total})
|
||||||
self.frm.groupBox.setShown(False)
|
|
||||||
self.frm.buttonBox.button(QDialogButtonBox.Close).setFocus()
|
|
||||||
if self.importer.log:
|
if self.importer.log:
|
||||||
txt += _("Log of import:\n") + "\n".join(self.importer.log)
|
txt += _("Log of import:\n") + "\n".join(self.importer.log)
|
||||||
self.frm.status.setText(txt)
|
self.close()
|
||||||
self.file = None
|
showText(txt)
|
||||||
self.maybePreview()
|
|
||||||
self.mw.col.db.flush()
|
|
||||||
self.mw.reset()
|
self.mw.reset()
|
||||||
self.modelChooser.deinit()
|
|
||||||
|
|
||||||
def setupMappingFrame(self):
|
def setupMappingFrame(self):
|
||||||
# qt seems to have a bug with adding/removing from a grid, so we add
|
# qt seems to have a bug with adding/removing from a grid, so we add
|
||||||
|
|
@ -215,27 +162,21 @@ you can enter it here. Use \\t to represent tab."""),
|
||||||
self.frm.mappingGroup.hide()
|
self.frm.mappingGroup.hide()
|
||||||
|
|
||||||
def showMapping(self, keepMapping=False, hook=None):
|
def showMapping(self, keepMapping=False, hook=None):
|
||||||
# first, check that we can read the file
|
if hook:
|
||||||
try:
|
hook()
|
||||||
self.importer = self.importer(self.mw.col, self.file)
|
if not keepMapping:
|
||||||
if hook:
|
self.mapping = self.importer.mapping
|
||||||
hook()
|
|
||||||
if not keepMapping:
|
# except Exception, e:
|
||||||
self.mapping = self.importer.mapping
|
# self.frm.status.setText(
|
||||||
except Exception, e:
|
# _("Unable to read file.\n\n%s") % unicode(
|
||||||
self.frm.status.setText(
|
# traceback.format_exc(), "ascii", "replace"))
|
||||||
_("Unable to read file.\n\n%s") % unicode(
|
# self.file = None
|
||||||
traceback.format_exc(), "ascii", "replace"))
|
# self.maybePreview()
|
||||||
self.file = None
|
# return
|
||||||
self.maybePreview()
|
|
||||||
return
|
|
||||||
self.frm.mappingGroup.show()
|
self.frm.mappingGroup.show()
|
||||||
if self.importer.fields():
|
assert self.importer.fields()
|
||||||
self.frm.mappingArea.show()
|
|
||||||
else:
|
|
||||||
self.frm.mappingArea.hide()
|
|
||||||
self.frm.updateButton.hide()
|
|
||||||
return
|
|
||||||
# set up the mapping grid
|
# set up the mapping grid
|
||||||
if self.mapwidget:
|
if self.mapwidget:
|
||||||
self.mapbox.removeWidget(self.mapwidget)
|
self.mapbox.removeWidget(self.mapwidget)
|
||||||
|
|
@ -250,10 +191,12 @@ you can enter it here. Use \\t to represent tab."""),
|
||||||
for num in range(len(self.mapping)):
|
for num in range(len(self.mapping)):
|
||||||
text = _("Field <b>%d</b> of file is:") % (num + 1)
|
text = _("Field <b>%d</b> of file is:") % (num + 1)
|
||||||
self.grid.addWidget(QLabel(text), num, 0)
|
self.grid.addWidget(QLabel(text), num, 0)
|
||||||
if self.mapping[num]:
|
if self.mapping[num] == "_tags":
|
||||||
text = _("mapped to <b>%s</b>") % self.mapping[num].name
|
|
||||||
elif self.mapping[num] is 0:
|
|
||||||
text = _("mapped to <b>Tags</b>")
|
text = _("mapped to <b>Tags</b>")
|
||||||
|
elif self.mapping[num] == "_deck":
|
||||||
|
text = _("mapped to <b>Deck</b>")
|
||||||
|
elif self.mapping[num]:
|
||||||
|
text = _("mapped to <b>%s</b>") % self.mapping[num]
|
||||||
else:
|
else:
|
||||||
text = _("<ignored>")
|
text = _("<ignored>")
|
||||||
self.grid.addWidget(QLabel(text), num, 1)
|
self.grid.addWidget(QLabel(text), num, 1)
|
||||||
|
|
@ -280,7 +223,8 @@ you can enter it here. Use \\t to represent tab."""),
|
||||||
self.showMapping(keepMapping=True)
|
self.showMapping(keepMapping=True)
|
||||||
|
|
||||||
def reject(self):
|
def reject(self):
|
||||||
self.modelChooser.deinit()
|
self.modelChooser.cleanup()
|
||||||
|
remHook("currentModelChanged", self.modelChanged)
|
||||||
QDialog.reject(self)
|
QDialog.reject(self)
|
||||||
|
|
||||||
def helpRequested(self):
|
def helpRequested(self):
|
||||||
|
|
@ -310,16 +254,33 @@ def onImport(mw):
|
||||||
importer = importer(mw.col, file)
|
importer = importer(mw.col, file)
|
||||||
# need to show import dialog?
|
# need to show import dialog?
|
||||||
if importer.needMapper:
|
if importer.needMapper:
|
||||||
diag = ImportDialog(mw, importer)
|
# make sure we can load the file first
|
||||||
self.modelChooser.show()
|
mw.progress.start(immediate=True)
|
||||||
else:
|
|
||||||
try:
|
try:
|
||||||
mw.progress.start(immediate=True)
|
importer.open()
|
||||||
importer.run()
|
except UnicodeDecodeError:
|
||||||
|
showWarning(_("Selected file was not in UTF-8 format."))
|
||||||
|
return
|
||||||
|
except Exception, e:
|
||||||
|
if e.message == "unknownFormat":
|
||||||
|
showWarning(_("Unknown file format."))
|
||||||
|
else:
|
||||||
|
msg = _("Import failed. Debugging info:\n")
|
||||||
|
msg += unicode(traceback.format_exc(), "ascii", "replace")
|
||||||
|
showText(msg)
|
||||||
|
return
|
||||||
|
finally:
|
||||||
mw.progress.finish()
|
mw.progress.finish()
|
||||||
|
diag = ImportDialog(mw, importer)
|
||||||
|
else:
|
||||||
|
mw.progress.start(immediate=True)
|
||||||
|
try:
|
||||||
|
importer.run()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
msg = _("Import failed.\n")
|
msg = _("Import failed.\n")
|
||||||
msg += unicode(traceback.format_exc(), "ascii", "replace")
|
msg += unicode(traceback.format_exc(), "ascii", "replace")
|
||||||
showText(msg)
|
showText(msg)
|
||||||
else:
|
else:
|
||||||
showText("\n".join(importer.log))
|
showText("\n".join(importer.log))
|
||||||
|
finally:
|
||||||
|
mw.progress.finish()
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ class ModelChooser(QHBoxLayout):
|
||||||
key=itemgetter("name"))
|
key=itemgetter("name"))
|
||||||
self.models.addItems([m['name'] for m in self._models])
|
self.models.addItems([m['name'] for m in self._models])
|
||||||
for c, m in enumerate(self._models):
|
for c, m in enumerate(self._models):
|
||||||
if m['id'] == str(self.deck.conf['curModel']):
|
if m['id'] == self.deck.conf['curModel']:
|
||||||
|
print "current", c
|
||||||
self.models.setCurrentIndex(c)
|
self.models.setCurrentIndex(c)
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,6 @@ def getFile(parent, title, cb, filter="*.*", dir=None, key=None):
|
||||||
else:
|
else:
|
||||||
dirkey = None
|
dirkey = None
|
||||||
d = QFileDialog(parent)
|
d = QFileDialog(parent)
|
||||||
d.setWindowModality(Qt.WindowModal)
|
|
||||||
d.setFileMode(QFileDialog.ExistingFile)
|
d.setFileMode(QFileDialog.ExistingFile)
|
||||||
d.setDirectory(dir)
|
d.setDirectory(dir)
|
||||||
d.setWindowTitle(title)
|
d.setWindowTitle(title)
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>527</width>
|
<width>553</width>
|
||||||
<height>430</height>
|
<height>466</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
|
@ -20,12 +20,6 @@
|
||||||
<string>Import options</string>
|
<string>Import options</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout">
|
<layout class="QVBoxLayout">
|
||||||
<property name="spacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="margin">
|
|
||||||
<number>9</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="autoDetect">
|
<widget class="QPushButton" name="autoDetect">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
@ -51,12 +45,6 @@
|
||||||
<string>Field mapping</string>
|
<string>Field mapping</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<property name="spacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="margin">
|
|
||||||
<number>9</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
|
|
@ -68,13 +56,6 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="updateButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Update</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
|
|
@ -102,8 +83,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>400</width>
|
<width>402</width>
|
||||||
<height>150</height>
|
<height>271</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
|
@ -114,28 +95,6 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox_2">
|
|
||||||
<property name="title">
|
|
||||||
<string>Status</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="margin">
|
|
||||||
<number>9</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QTextEdit" name="status">
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
|
@ -151,8 +110,6 @@
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>autoDetect</tabstop>
|
<tabstop>autoDetect</tabstop>
|
||||||
<tabstop>importButton</tabstop>
|
<tabstop>importButton</tabstop>
|
||||||
<tabstop>updateButton</tabstop>
|
|
||||||
<tabstop>status</tabstop>
|
|
||||||
<tabstop>buttonBox</tabstop>
|
<tabstop>buttonBox</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue