csv importing

This commit is contained in:
Damien Elmes 2012-02-29 14:05:43 +09:00
parent 562f33635c
commit 03c417daa2
4 changed files with 87 additions and 169 deletions

View file

@ -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()

View file

@ -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

View file

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

View file

@ -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>&amp;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/>