summaryrefslogtreecommitdiffstats
path: root/kexi/plugins/scripting/scripts
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
commit8362bf63dea22bbf6736609b0f49c152f975eb63 (patch)
tree0eea3928e39e50fae91d4e68b21b1e6cbae25604 /kexi/plugins/scripting/scripts
downloadkoffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz
koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kexi/plugins/scripting/scripts')
-rw-r--r--kexi/plugins/scripting/scripts/Makefile.am1
-rw-r--r--kexi/plugins/scripting/scripts/copycenter/CopyCenter.py644
-rw-r--r--kexi/plugins/scripting/scripts/copycenter/CopyCenter.rc10
-rw-r--r--kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginKexiDB.py646
-rw-r--r--kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginQtSQL.py495
-rw-r--r--kexi/plugins/scripting/scripts/copycenter/Makefile.am4
-rw-r--r--kexi/plugins/scripting/scripts/copycenter/readme.html20
-rw-r--r--kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.py196
-rw-r--r--kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.rc8
-rw-r--r--kexi/plugins/scripting/scripts/exportxhtml/Makefile.am4
-rwxr-xr-xkexi/plugins/scripting/scripts/importxhtml/ImportXHTML.py434
-rw-r--r--kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.rc8
-rw-r--r--kexi/plugins/scripting/scripts/importxhtml/Makefile.am4
-rw-r--r--kexi/plugins/scripting/scripts/projectdocumentor/Makefile.am4
-rwxr-xr-xkexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.py186
-rw-r--r--kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.rc8
-rw-r--r--kexi/plugins/scripting/scripts/python/Makefile.am2
-rw-r--r--kexi/plugins/scripting/scripts/python/kexiapp/Makefile.am2
-rwxr-xr-xkexi/plugins/scripting/scripts/python/kexiapp/__init__.py25
19 files changed, 2701 insertions, 0 deletions
diff --git a/kexi/plugins/scripting/scripts/Makefile.am b/kexi/plugins/scripting/scripts/Makefile.am
new file mode 100644
index 00000000..b63eedee
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = exportxhtml importxhtml projectdocumentor copycenter python
diff --git a/kexi/plugins/scripting/scripts/copycenter/CopyCenter.py b/kexi/plugins/scripting/scripts/copycenter/CopyCenter.py
new file mode 100644
index 00000000..3718512f
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/copycenter/CopyCenter.py
@@ -0,0 +1,644 @@
+"""
+Copy Center
+
+Description:
+Python script to copy data between different datastores.
+
+Author:
+Sebastian Sauer <mail@dipe.org>
+
+Copyright:
+Dual-licensed under LGPL v2+higher and the BSD license.
+"""
+
+class CopyCenter:
+
+ class Plugin:
+ def __init__(self, plugin):
+ self.plugin = plugin
+ self.name = plugin.name
+ self.source = self.load("Source")
+ self.destination = self.load("Destination")
+
+ def load(self, plugintype):
+ instance = None
+ try:
+ if hasattr(self.plugin, plugintype):
+ return getattr(self.plugin, plugintype)(self.plugin)
+ except:
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+ return None
+
+ def __init__(self, scriptpath):
+ self.scriptpath = scriptpath
+ self.homepath = self.getHomePath()
+ self.plugins = {}
+
+ import os
+ import sys
+ if not os.path.exists(scriptpath):
+ print "The Path %s does not exist" % scriptpath
+ else:
+ import re
+ regexp = re.compile('^CopyCenterPlugin(.*)\\.py$')
+ for f in os.listdir(scriptpath):
+ file = os.path.join(scriptpath, f)
+ if not os.path.isfile(file): continue
+ m = regexp.match(f)
+ if not m: continue
+ print "Plugin name=%s file=%s" % (m.group(1),file)
+ mylocals = {}
+ try:
+ execfile(file, globals(), mylocals)
+ if mylocals.has_key("CopyCenterPlugin"):
+ plugin = mylocals.get("CopyCenterPlugin")(self)
+ self.plugins[plugin.name] = self.Plugin(plugin)
+ except:
+ print "Failed to import file=%s" % file
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+
+ def getHomePath(self):
+ """ Return the homedirectory. """
+ import os
+ try:
+ home = os.getenv("HOME")
+ if not home:
+ import pwd
+ user = os.getenv("USER") or os.getenv("LOGNAME")
+ if not user:
+ pwent = pwd.getpwuid(os.getuid())
+ else:
+ pwent = pwd.getpwnam(user)
+ home = pwent[6]
+ return home
+ except (KeyError, ImportError):
+ return os.curdir
+
+class Copierer:
+ def __init__(self): pass
+ def appendProgressMessage(self,messagetext): pass
+ def writeSuccess(self,record,rowcount): pass
+ def writeFailed(self,record): pass
+
+def runGuiApp(copycenter, name):
+ import qt
+ import sys
+
+ #--------------------------------------------------------------------
+
+ class ListViewDialog(qt.QDialog):
+ def __init__(self, parent, caption):
+ qt.QDialog.__init__(self, parent, "ProgressDialog", 1)
+ self.parent = parent
+ self.setCaption(caption)
+ layout = qt.QVBoxLayout(self)
+ box = qt.QVBox(self)
+ box.setMargin(2)
+ layout.addWidget(box)
+ self.listview = qt.QListView(box)
+ self.listview.setAllColumnsShowFocus(True)
+ self.listview.header().setStretchEnabled(True,0)
+ btnbox = qt.QHBox(box)
+ btnbox.setMargin(6)
+ btnbox.setSpacing(6)
+ self.okbtn = qt.QPushButton(btnbox)
+ self.okbtn.setText("Ok")
+ #qt.QObject.connect(okbtn, qt.SIGNAL("clicked()"), self.okClicked)
+ self.cancelbtn = qt.QPushButton(btnbox)
+ self.cancelbtn.setText("Cancel")
+ qt.QObject.connect(self.cancelbtn, qt.SIGNAL("clicked()"), self.close)
+ box.setMinimumSize(qt.QSize(460,380))
+ def addItem(self,valuelist,afteritem = None):
+ if afteritem == None:
+ item = qt.QListViewItem(self.listview)
+ else:
+ item = qt.QListViewItem(self.listview,afteritem)
+ i = 0
+ for value in valuelist:
+ item.setText(i,value)
+ i += 1
+ return item
+
+ #--------------------------------------------------------------------
+
+ class CopyJobWidget(qt.QVBox):
+ def __init__(self,dialog,parent):
+ self.dialog = dialog
+ qt.QVBox.__init__(self,parent)
+ self.setSpacing(6)
+ typebox = qt.QHBox(self)
+ typebox.setSpacing(6)
+ label = qt.QLabel("Job File:",typebox)
+ self.jobfilecombobox = qt.QComboBox(typebox)
+ typebox.setStretchFactor(self.jobfilecombobox,1)
+ self.jobfilecombobox.setEditable(True)
+ self.jobfilecombobox.insertItem("")
+ label.setBuddy(self.jobfilecombobox)
+ qt.QObject.connect(self.jobfilecombobox, qt.SIGNAL("textChanged(const QString&)"), self.jobfilecomboboxChanged)
+
+ import os
+ import re
+ for f in os.listdir(self.dialog.copycenter.homepath):
+ file = os.path.join(self.dialog.copycenter.homepath,f)
+ if os.path.isfile(file) and re.search(".+\\.copycenterjob.xml$",f):
+ self.jobfilecombobox.insertItem(file)
+
+ loadbtn = qt.QPushButton(typebox)
+ loadbtn.setText("Open...")
+ qt.QObject.connect(loadbtn, qt.SIGNAL("clicked()"), self.openClicked)
+ savebtn = qt.QPushButton(typebox)
+ savebtn.setText("Save...")
+ qt.QObject.connect(savebtn, qt.SIGNAL("clicked()"), self.saveClicked)
+
+ self.listview = qt.QListView(self)
+ self.listview.setAllColumnsShowFocus(True)
+ self.listview.setSorting(-1)
+ self.listview.setDefaultRenameAction(qt.QListView.Reject)
+ self.listview.header().setClickEnabled(False)
+ self.listview.addColumn("Name")
+ self.listview.addColumn("Value")
+ qt.QObject.connect(self.listview, qt.SIGNAL("doubleClicked(QListViewItem*, const QPoint&, int)"), self.doubleClicked)
+ #qt.QObject.connect(self.listview, qt.SIGNAL("itemRenamed(QListViewItem*, int, const QString&)"), self.itemRenamed)
+
+ def doubleClicked(self, **args):
+ print "CopyJobWidget.doubleClicked"
+ item = self.listview.selectedItem()
+ if item and item.parent(): item.startRename(1)
+
+ def readOptions(self,domnode,plugininst):
+ print "CopyJobWidget.readOptions plugintype=\"%s\"" % plugininst.plugintype
+ for node in domnode.childNodes:
+ if node.nodeType == node.ELEMENT_NODE:
+ v = node.getAttribute("value")
+ plugininst.options[node.nodeName] = v
+ print "Option \"%s\" has value \"%s\" now." % (node.nodeName, v)
+
+ def jobfilecomboboxChanged(self, **args):
+ print "CopyJobWidget.jobfilecomboboxChanged"
+ import os
+ import xml.dom.minidom
+ filename = str(self.jobfilecombobox.currentText())
+ if not os.path.isfile(filename): return
+ domdoc = xml.dom.minidom.parse(filename)
+ try:
+ elements = domdoc.getElementsByTagName("CopyCenterJob")[0]
+ sourcenode = elements.getElementsByTagName("Source")[0]
+ destinationnode = elements.getElementsByTagName("Destination")[0]
+ except:
+ raise "The XML-file \"%s\" does not contain a valid copy-job." % filename
+
+ sourcepluginname = str(sourcenode.getAttribute('plugin'))
+ if not self.dialog.sourcedata.combobox.listBox().findItem(sourcepluginname,qt.Qt.ExactMatch):
+ raise "There exists no plugin with the name \"%s\"." % sourcepluginname
+ self.dialog.sourcedata.combobox.setCurrentText(sourcepluginname)
+
+ destinationpluginname = str(destinationnode.getAttribute('plugin'))
+ if not self.dialog.destinationdata.combobox.listBox().findItem(destinationpluginname,qt.Qt.ExactMatch):
+ raise "There exists no plugin with the name \"%s\"." % destinationpluginname
+ self.dialog.destinationdata.combobox.setCurrentText(destinationpluginname)
+
+ self.readOptions(sourcenode,self.dialog.getSourcePluginImpl())
+ self.readOptions(destinationnode,self.dialog.getDestinationPluginImpl())
+ self.maybeUpdate()
+
+ def openClicked(self):
+ text = str(self.jobfilecombobox.currentText())
+ if text == "": text = self.dialog.copycenter.homepath
+ filename = str(qt.QFileDialog.getOpenFileName(text,"*.copycenterjob.xml;;*",self.dialog))
+ if filename != "": self.jobfilecombobox.setCurrentText(filename)
+
+ def escape(self,s):
+ return s.replace("&", "&amp;").replace("'", "&apos;").replace("<", "&lt;").replace(">", "&gt;").replace('"', "&quot;")
+
+ def writeOptions(self,writer,pluginname,plugininst):
+ print "CopyJobWidget.writeOptions"
+ writer.write("<%s plugin=\"%s\">\n" % (plugininst.plugintype, pluginname))
+ for optionname in plugininst.options:
+ value = self.escape( unicode(plugininst.options[optionname]).encode("utf-8") )
+ writer.write("\t<%s value=\"%s\" />\n" % (optionname,value))
+ writer.write("</%s>\n" % plugininst.plugintype)
+
+ def saveClicked(self):
+ text = str(self.jobfilecombobox.currentText())
+ if text == "":
+ import os
+ text = os.path.join(self.dialog.copycenter.homepath,"default.copycenterjob.xml")
+ filename = str(qt.QFileDialog.getSaveFileName(text,"*.copycenterjob.xml;;*",self.dialog))
+ if str(filename) == "": return
+ f = open(filename, "w")
+ f.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ f.write("<CopyCenterJob>\n")
+ sourcepluginname = self.dialog.sourcedata.combobox.currentText()
+ self.writeOptions(f, sourcepluginname, self.dialog.getSourcePluginImpl())
+ destinationpluginname = self.dialog.destinationdata.combobox.currentText()
+ self.writeOptions(f, destinationpluginname, self.dialog.getDestinationPluginImpl())
+ f.write("</CopyCenterJob>\n")
+ f.close()
+ print "File \%s\" successfully written." % filename
+
+ def addItem(self, pluginimpl, afteritem = None, parentitem = None):
+ #print "CopyJobWidget.addItem"
+ class ListViewItem(qt.QListViewItem):
+ def __init__(self, pluginimpl, listview, parentitem = None, afteritem = None):
+ self.pluginimpl = pluginimpl
+ if parentitem == None:
+ qt.QListViewItem.__init__(self,listview)
+ self.setOpen(True)
+ else:
+ if afteritem == None:
+ qt.QListViewItem.__init__(self,parentitem)
+ else:
+ qt.QListViewItem.__init__(self,parentitem,afteritem)
+ self.setRenameEnabled(1,True)
+ def startRename(self, columnindex):
+ qt.QListViewItem.startRename(self,columnindex)
+ #lineedit = self.listView().viewport().child("qt_renamebox")
+ #if lineedit:
+ # regexp = qt.QRegExp("^[_A-Z]+[_A-Z0-9]*$", False)
+ # v = qt.QRegExpValidator(regexp, self.listView());
+ # lineedit.setValidator(v)
+ def okRename(self, columnindex):
+ if columnindex == 1:
+ n = str(self.text(0))
+ if not self.pluginimpl.options.has_key(n):
+ raise "No such option \"%s\"" % n
+ qt.QListViewItem.okRename(self,columnindex)
+ v = str(qt.QListViewItem.text(self,1))
+ print "Option \"%s\" has value \"%s\" now." % (n,v)
+ self.pluginimpl.options[n] = v
+
+ def text(self, columnindex):
+ if columnindex == 1:
+ if qt.QListViewItem.text(self,0).contains("password"):
+ return "*" * len(str(qt.QListViewItem.text(self,1)))
+ return qt.QListViewItem.text(self,columnindex)
+ return ListViewItem(pluginimpl, self.listview, parentitem, afteritem)
+
+ def updateItem(self,pluginname,pluginimpl):
+ #print "CopyJobWidget.updateItem"
+ if pluginimpl == None: return
+ #plugin = self.dialog.plugins[pluginname]
+ item = self.addItem(pluginimpl)
+ item.setText(0,"%s: %s" % (pluginimpl.plugintype, pluginname))
+ afteritem = None
+ for i in pluginimpl.options:
+ afteritem = self.addItem(pluginimpl, afteritem, item)
+ afteritem.setText(0,str(i))
+ afteritem.setText(1,str(pluginimpl.options[i]))
+ print "CopyJobWidget.updateItem Added item with name \"%s\" and value \"%s\"" % (str(i),str(pluginimpl.options[i]))
+ pass
+
+ def maybeUpdate(self):
+ print "CopyJobWidget.maybeUpdate"
+ self.listview.clear()
+ try:
+ self.updateItem(self.dialog.getDestinationPluginName(), self.dialog.getDestinationPluginImpl())
+ self.updateItem(self.dialog.getSourcePluginName(), self.dialog.getSourcePluginImpl())
+ except:
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+ self.listview.clear()
+
+ #--------------------------------------------------------------------
+
+ class ProgressDialog(qt.QDialog):
+ def __init__(self, dialog):
+ self.dialog = dialog
+ self.starttime = None
+ qt.QDialog.__init__(self, dialog, "ProgressDialog", 1)
+ self.setCaption("Copying...")
+ layout = qt.QVBoxLayout(self)
+ box = qt.QVBox(self)
+ box.setSpacing(6)
+ box.setMargin(6)
+ layout.addWidget(box)
+ self.textbrowser = qt.QTextBrowser(box)
+ self.textbrowser.setWordWrap(qt.QTextEdit.WidgetWidth)
+ self.textbrowser.setTextFormat(qt.Qt.RichText)
+ statusbox = qt.QFrame(box)
+ layout = qt.QGridLayout(statusbox,4,2,0,2)
+ layout.addWidget(qt.QLabel("Number of records done:",statusbox),0,0)
+ self.donecounter = 0
+ self.donelabel = qt.QLabel("-",statusbox)
+ layout.addWidget(self.donelabel,0,1)
+ layout.addWidget(qt.QLabel("Successfully copied records:",statusbox),1,0)
+ self.successcounter = 0
+ self.successlabel = qt.QLabel("-",statusbox)
+ layout.addWidget(self.successlabel,1,1)
+ layout.addWidget(qt.QLabel("Failed to copy records:",statusbox),2,0)
+ self.failedcounter = 0
+ self.failedlabel = qt.QLabel("-",statusbox)
+ layout.addWidget(self.failedlabel,2,1)
+ layout.addWidget(qt.QLabel("Elapsed time in seconds:",statusbox),3,0)
+ self.elapsedlabel = qt.QLabel("-",statusbox)
+ layout.addWidget(self.elapsedlabel,3,1)
+ btnbox = qt.QHBox(box)
+ btnbox.setSpacing(6)
+ self.donebtn = qt.QPushButton(btnbox)
+ self.donebtn.setText("Done")
+ self.donebtn.setEnabled(False)
+ qt.QObject.connect(self.donebtn,qt.SIGNAL("clicked()"),self.close)
+ self.cancelbtn = qt.QPushButton(btnbox)
+ self.cancelbtn.setText("Cancel")
+ qt.QObject.connect(self.cancelbtn,qt.SIGNAL("clicked()"),self.close)
+ box.setMinimumSize( qt.QSize(500,380) )
+
+ def updateStates(self):
+ if self.starttime != None:
+ self.donelabel.setText(str(self.donecounter))
+ self.failedlabel.setText(str(self.failedcounter))
+ self.successlabel.setText(str(self.successcounter))
+ self.elapsedlabel.setText( str(self.starttime.elapsed() / 1000) )
+ self.donelabel.update()
+ self.failedlabel.update()
+ self.successlabel.update()
+ self.elapsedlabel.update()
+
+ def writeSuccess(self, record, rowcount):
+ self.donecounter += rowcount
+ self.successcounter += rowcount
+ qt.qApp.processEvents()
+ def writeFailed(self, record):
+ self.donecounter += 1
+ self.failedcounter += 1
+ qt.qApp.processEvents()
+
+ def startCopy(self):
+ try:
+ global Copierer
+ copierer = Copierer()
+ copierer.appendProgressMessage = self.textbrowser.append
+ copierer.writeSuccess = self.writeSuccess
+ copierer.writeFailed = self.writeFailed
+
+ self.starttime = qt.QTime()
+ self.updatetimer = qt.QTimer(self)
+ qt.QObject.connect(self.updatetimer,qt.SIGNAL("timeout()"),self.updateStates)
+
+ # Initialize the source
+ sourcename = self.dialog.getSourcePluginName()
+ sourceimpl = self.dialog.getSourcePluginImpl()
+ self.textbrowser.append("Source: %s" % sourcename)
+ if sourceimpl == None:
+ raise "No such source."
+ try:
+ sourceimpl.init(copierer)
+
+ # Initialize the destination
+ destinationname = self.dialog.getDestinationPluginName()
+ destinationimpl = self.dialog.getDestinationPluginImpl()
+ self.textbrowser.append("<hr>Destination: %s" % destinationname)
+ if destinationimpl == None:
+ raise "No such destination."
+ try:
+ destinationimpl.init(copierer)
+
+ self.starttime.start()
+ self.updatetimer.start(500)
+ qt.qApp.processEvents()
+
+ # Copy the records
+ self.textbrowser.append("<hr><i>Copy the records...</i>")
+ while True:
+ record = sourceimpl.read()
+ if record == None: break
+ destinationimpl.write(record)
+
+ self.updateStates()
+ finally:
+ destinationimpl.finish()
+ finally:
+ sourceimpl.finish()
+
+ self.setCaption("Copy done")
+ self.textbrowser.append("<hr><b>Copy done.</b>")
+ except:
+ self.setCaption("Copy failed")
+ self.textbrowser.append("<b>Error: %s</b>" % sys.exc_info()[0])
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+ #self.progressbar.setEnabled(False)
+ self.donebtn.setEnabled(True)
+ self.cancelbtn.setEnabled(False)
+ self.updatetimer.stop()
+ self.starttime = None
+
+ def show(self):
+ qt.QDialog.show(self)
+ qt.QTimer.singleShot(10,self.startCopy)
+ qt.qApp.processEvents()
+
+ def closeEvent(self, closeevent):
+ if not self.dialog.getSourcePluginImpl().isFinished():
+ if qt.QMessageBox.warning(self,"Abort?","Abort the copy?",qt.QMessageBox.Yes,qt.QMessageBox.No) != qt.QMessageBox.Yes:
+ closeevent.ignore()
+ return
+ self.dialog.getSourcePluginImpl().finish()
+ self.dialog.getDestinationPluginImpl().finish()
+ closeevent.accept()
+
+ #--------------------------------------------------------------------
+
+ class DataSelector(qt.QVGroupBox):
+ def __init__(self, plugintype, title, caption, parent, dialog, items):
+ self.plugintype = plugintype
+ self.pluginimpl = None
+ self.dialog = dialog
+ self.mainbox = None
+
+ qt.QVGroupBox.__init__(self,title,parent)
+ self.setInsideMargin(6)
+ self.setInsideSpacing(0)
+
+ typebox = qt.QHBox(self)
+ label = qt.QLabel(caption,typebox)
+ self.combobox = qt.QComboBox(typebox)
+ for item in items:
+ self.combobox.insertItem(str(item))
+ label.setBuddy(self.combobox)
+ typebox.setStretchFactor(self.combobox,1)
+
+ self.scrollview = qt.QScrollView(self)
+ try:
+ self.scrollview.setResizePolicy(qt.QScrollView.AutoOne)
+ self.scrollview.setFrameStyle(qt.QFrame.NoFrame);
+ self.scrollview.setResizePolicy(qt.QScrollView.AutoOneFit);
+ self.scrollview.viewport().setPaletteBackgroundColor(self.paletteBackgroundColor())
+ except:
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+ qt.QObject.connect(self.combobox, qt.SIGNAL("activated(int)"), self.activated)
+
+ def updatePlugin(self):
+ print "DataSelector.updatePlugin"
+ self.pluginimpl = None
+ text = str(self.combobox.currentText())
+ plugin = self.dialog.copycenter.plugins[text]
+ self.pluginimpl = getattr(plugin, self.plugintype)
+
+ def removeMainBox(self):
+ if self.mainbox == None: return
+ try:
+ self.scrollview.removeChild(self.mainbox)
+ self.mainbox.destroy()
+ except:
+ pass
+ self.mainbox = None
+
+ def updateMainBox(self):
+ print "DataSelector.updateMainBox"
+ self.removeMainBox()
+ self.mainbox = qt.QVBox( self.scrollview.viewport() )
+ self.mainbox.setSpacing(2)
+ if self.pluginimpl != None:
+ try:
+ self.pluginimpl.createWidget(self.dialog, self.mainbox)
+ except:
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+ self.mainbox.setStretchFactor(qt.QWidget(self.mainbox), 1)
+ self.mainbox.show()
+ self.scrollview.addChild(self.mainbox)
+
+ def activated(self, **args):
+ self.updatePlugin()
+ self.updateMainBox()
+
+ def maybeUpdate(self):
+ print "DataSelector.maybeUpdate"
+ self.removeMainBox()
+ qt.QTimer.singleShot(50, self.activated)
+
+ def maybeDone(self):
+ print "DataSelector.maybeDone"
+ if self.pluginimpl.widget == None: return
+ for optionname in self.pluginimpl.options:
+ self.pluginimpl.options[optionname] = self.pluginimpl.widget.getOptionValue(optionname)
+
+ #--------------------------------------------------------------------
+
+ class Dialog(qt.QDialog):
+ def __init__(self, copycenter, parent):
+ self.copycenter = copycenter
+
+ import qt
+ import os
+ import sys
+
+ self.ListViewDialog = ListViewDialog
+ qt.QDialog.__init__(self, parent, "Dialog", 1, qt.Qt.WDestructiveClose)
+ self.setCaption("Copy Center")
+ layout = qt.QVBoxLayout(self)
+ box = qt.QVBox(self)
+ box.setMargin(6)
+ box.setSpacing(6)
+ layout.addWidget(box)
+ self.tab = qt.QTabWidget(box)
+ self.tab.setMargin(6)
+ box.setStretchFactor(self.tab,1)
+
+ self.jobsbox = CopyJobWidget(self,self.tab)
+ self.tab.addTab(self.jobsbox,"Jobs")
+
+ self.splitter = qt.QSplitter(self.tab)
+
+ sourceplugins = []
+ destinationplugins = []
+ for pluginname in self.copycenter.plugins:
+ if self.copycenter.plugins[pluginname].source != None:
+ sourceplugins.append(pluginname)
+ if self.copycenter.plugins[pluginname].destination != None:
+ destinationplugins.append(pluginname)
+ sourceplugins.sort()
+ destinationplugins.sort()
+
+ self.sourcedata = DataSelector(
+ "source", # id
+ "Read Data From", # title
+ "Source:", # caption
+ self.splitter, self, sourceplugins)
+ self.destinationdata = DataSelector(
+ "destination", # id
+ "Write Data to", # title
+ "Destination:", # caption
+ self.splitter, self, destinationplugins)
+
+ btnbox = qt.QHBox(box)
+ btnbox.setSpacing(6)
+ okbtn = qt.QPushButton(btnbox)
+ okbtn.setText("Start Copy")
+ okbtn.setDefault(True)
+ qt.QObject.connect(okbtn,qt.SIGNAL("clicked()"),self.startCopy)
+ cancelbtn = qt.QPushButton(btnbox)
+ cancelbtn.setText("Cancel")
+ qt.QObject.connect(cancelbtn,qt.SIGNAL("clicked()"),self.close)
+
+ self.tab.addTab(self.splitter,"Copy")
+ self.tab.setCurrentPage(1)
+
+ self.helpbrowser = qt.QTextBrowser(self.tab)
+ self.helpbrowser.setLinkUnderline(False)
+ self.helpbrowser.setUndoRedoEnabled(False)
+ self.tab.addTab(self.helpbrowser,"Help")
+ qt.QObject.connect(self.tab,qt.SIGNAL("currentChanged(QWidget*)"),self.currentTabChanged)
+
+ box.setMinimumSize( qt.QSize(760,500) )
+
+ defaultfile = os.path.join(self.copycenter.homepath,"default.copycenterjob.xml")
+ if os.path.isfile(defaultfile):
+ print "Reading default copy job file: %s" % defaultfile
+ self.jobsbox.jobfilecombobox.setCurrentText(defaultfile)
+
+ def getSourcePluginName(self):
+ return str(self.sourcedata.combobox.currentText())
+ def getSourcePluginImpl(self):
+ return self.copycenter.plugins[self.getSourcePluginName()].source
+ def getDestinationPluginName(self):
+ return str(self.destinationdata.combobox.currentText())
+ def getDestinationPluginImpl(self):
+ return self.copycenter.plugins[self.getDestinationPluginName()].destination
+
+ def currentTabChanged(self,widget):
+ if self.tab.currentPage() == self.jobsbox:
+ # The "Copy" page is done
+ self.sourcedata.maybeDone()
+ self.destinationdata.maybeDone()
+ # Update the "Jobs" page
+ self.jobsbox.maybeUpdate()
+ elif self.tab.currentPage() == self.splitter:
+ # Update the "Copy" page
+ self.sourcedata.maybeUpdate()
+ self.destinationdata.maybeUpdate()
+ elif self.tab.currentPage() == self.helpbrowser and self.helpbrowser.lines() <= 1:
+ # Update the "Help" page
+ import os
+ file = os.path.join(self.copycenter.scriptpath, "readme.html")
+ if not os.path.isfile(file): return
+ fh = open(file,'r')
+ self.helpbrowser.setText( fh.read() )
+ fh.close()
+
+ def startCopy(self):
+ dlg = ProgressDialog(self)
+ dlg.show()
+
+ #--------------------------------------------------------------------
+
+ if name == "__main__":
+ qtapp = qt.QApplication(sys.argv)
+ else:
+ qtapp = qt.qApp
+ dialog = Dialog(copycenter, qtapp.mainWidget())
+ dialog.exec_loop()
+
+import os
+
+if __name__ == "__main__":
+ scriptpath = os.getcwd()
+else:
+ scriptpath = os.path.dirname(__name__)
+
+copycenter = CopyCenter(scriptpath)
+runGuiApp(copycenter, __name__)
diff --git a/kexi/plugins/scripting/scripts/copycenter/CopyCenter.rc b/kexi/plugins/scripting/scripts/copycenter/CopyCenter.rc
new file mode 100644
index 00000000..e3e758b4
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/copycenter/CopyCenter.rc
@@ -0,0 +1,10 @@
+<KrossScripting>
+ <ScriptAction
+ name="CopyCenter"
+ version="1"
+ text="Copy Center"
+ description="Copy data from one source to another."
+ icon="editcopy"
+ interpreter="python"
+ file="CopyCenter.py" />
+</KrossScripting>
diff --git a/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginKexiDB.py b/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginKexiDB.py
new file mode 100644
index 00000000..e8241405
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginKexiDB.py
@@ -0,0 +1,646 @@
+"""
+CopyCenterPlugin to provide 'KexiDB'.
+
+Description:
+This python-script is a plugin for the CopyCenter.py.
+
+Author:
+Sebastian Sauer <mail@dipe.org>
+
+Copyright:
+GPL v2 or higher.
+"""
+
+class CopyCenterPlugin:
+ """ The CopyCenterPlugin to provide abstract access to the 'KexiDB'
+ framework to CopyCenter.py """
+
+ name = "Kexi Database"
+ """ The name this plugin has. The name should be unique and
+ will be used for displaying a caption. """
+
+ class Plugin:
+ """ The implementation of a plugin which is published to the
+ CopyCenter.py script. While there exists only one instance of
+ the CopyCenterPlugin class, there will be n instances of this
+ Plugin class (one for 'source' aka read-data-from and one for
+ 'destination' aka write-data-to) created from within the
+ CopyCenter.py. The Source and Destination classes are extending
+ this Plugin class with specialized functionality. """
+ def __init__(self,copycenterplugin):
+ """ Constructor. """
+ self.copycenterplugin = copycenterplugin
+ self.widget = None
+ self.options = {
+ 'autoconnect' : False,
+ 'project' : '', #'~/test.kexi',
+ 'driver' : '', #'MySQL', #'SQLite3','MySQL',...
+ 'file' : '', #'/path/to/mysqlite3dbfile.kexi'
+ 'hostname' : '127.0.0.1',
+ 'port' : '',
+ 'usesocketfile' : False,
+ 'socketfile' : '',
+ 'username' : '',
+ 'password' : '',
+ 'database' : '',
+ 'table' : '',
+ 'fields' : '',
+ 'where' : '',
+ }
+ self.connection = copycenterplugin.Connection(self)
+ def isFinished(self):
+ return self.connection.isFinished()
+ def finish(self):
+ """ Called if reading is finished."""
+ self.connection.finish()
+ def createWidget(self, dialog, parent):
+ """ Create and return a widget to modify the plugin settings. """
+ return self.copycenterplugin.createWidget(dialog, self, parent)
+
+ class Source(Plugin):
+ """ Specialization of the Plugin class to implement the
+ 'source' aka read-data-from functionality. """
+ plugintype = "Source"
+ def init(self,copierer):
+ """ Called if reading should be initialize. """
+ self.connection.init(copierer)
+ self.connection.initRead()
+ # Called if a record should be readed.
+ self.read = self.connection.readRecord
+
+ class Destination(Plugin):
+ """ Specialization of the Plugin class to implement the
+ 'destination' aka write-data-to functionality. """
+ plugintype = "Destination"
+ def init(self,copierer):
+ """ Called if writing should be initialize. """
+ self.connection.init(copierer)
+ self.connection.initWrite()
+ # Called if a record should be written.
+ self.write = self.connection.writeRecord
+
+ class Connection:
+ """ Abstract access to work with KexiDB. """
+ def __init__(self,plugin):
+ self.plugin = plugin
+ self.copierer = None
+ self.kexidbconnection = None
+ self.kexidbcursor = None
+ self.tableschema = None
+ self.fieldlist = None
+ def lastError(self): return self.kexidbconnection.lastError()
+ def connect(self): return self.kexidbconnection.connect()
+ def disconnect(self):
+ if self.kexidbconnection == None or self.kexidbconnection.disconnect():
+ self.kexidbconnection = None
+ return True
+ return False
+ def isConnected(self): return self.kexidbconnection != None and self.kexidbconnection.isConnected()
+ def tableNames(self): return self.kexidbconnection.tableNames()
+ def hasTableName(self,tablename): return tablename in self.kexidbconnection.tableNames()
+ def tableSchema(self,tablename): return self.kexidbconnection.tableSchema(tablename)
+
+ def init(self,copierer):
+ self.copierer = copierer
+ if self.kexidbconnection == None:
+ if self.plugin.widget == None:
+ raise "No connection established."
+ self.copierer.appendProgressMessage("<i>Trying to connect...</i>")
+ if self.plugin.widget.driverbox.driver == None:
+ raise "Invalid driver."
+ if not self.plugin.widget.connectClicked():
+ raise "Failed to connect."
+ connectiondata = self.kexidbconnection.data()
+ self.copierer.appendProgressMessage("Connected: %s %s" % (connectiondata.driverName(),connectiondata.serverInfoString()))
+
+ tablename = str(self.plugin.widget.tablebox.tableedit.text())
+ if tablename == "":
+ raise "No table defined"
+ fields = [ f.strip() for f in str(self.plugin.widget.fieldbox.fieldsedit.text()).split(",") if len(f) > 0 ]
+ if len(fields) < 1:
+ raise "No fields defined"
+
+ self.tableschema = self.kexidbconnection.tableSchema(tablename)
+ if not self.tableschema: raise "No such tableschema \"%s\"" % tablename
+ self.copierer.appendProgressMessage("Table: %s" % self.tableschema.name())
+
+ if len(fields) == 1 and fields[0] == "*":
+ self.fieldlist = self.tableschema.fieldlist()
+ else:
+ self.fieldlist = self.tableschema.fieldlist().subList(fields)
+ if not self.fieldlist: raise "No such fields \"%s\"" % fields
+ fieldlistnames = self.fieldlist.names()
+ if len(fieldlistnames) < 1: raise "No valid fields defined for \"%s\"" % fields
+ self.copierer.appendProgressMessage("Fields: %s" % fieldlistnames)
+ def finish(self):
+ if self.plugin.widget == None:
+ self.disconnect()
+ else:
+ self.plugin.widget.disconnectClicked()
+ self.kexidbcursor = None
+ self.kexidbconnection = None
+ self.tableschema = None
+ self.fieldlist = None
+ self.copierer = None
+ def isFinished(self):
+ return self.copierer == None
+
+ def initRead(self):
+ print "Initialize read"
+ #queryschema = self.plugin.copycenterplugin.drivermanager.querySchema()
+ queryschema = self.tableschema.query()
+ queryschema.fieldlist().setFields(self.fieldlist)
+ print "QuerySchema: %s" % queryschema.fieldlist().names()
+
+ whereexpression = str(self.plugin.widget.whereedit.text())
+ if whereexpression != "":
+ print "WHERE-expression: %s" % whereexpression
+ if not queryschema.setWhereExpression(whereexpression):
+ raise "Invalid WHERE-expression."
+
+ #print "QuerySchema statement=%s" % queryschema.statement()
+ self.kexidbcursor = self.kexidbconnection.executeQuerySchema(queryschema)
+ if not self.kexidbcursor:
+ raise "Failed to create cursor."
+ if not self.kexidbcursor.moveFirst():
+ raise "The cursor has no records to read from."
+
+ def readRecord(self):
+ if self.kexidbcursor == None or self.kexidbcursor.eof():
+ return None
+ record = []
+ for i in range( self.kexidbcursor.fieldCount() ):
+ record.append( self.kexidbcursor.value(i) )
+ self.kexidbcursor.moveNext()
+ #print "read record: %s" % record
+ return record
+
+ def initWrite(self):
+ print "Initialize write"
+
+ def writeRecord(self,record):
+ print "write record: %s" % record
+ if self.kexidbconnection.insertRecord(self.fieldlist,record):
+ print "=> insert successfully"
+ self.copierer.writeSuccess(record, 1)
+ else:
+ print "=> insert failed: %s" % self.kexidbconnection.lastError()
+ self.copierer.writeFailed(record)
+ #import time
+ #time.sleep(1)
+ return True
+
+ def __init__(self, copycenter):
+ """ Constructor. """
+ import krosskexidb
+ self.drivermanager = krosskexidb.DriverManager()
+ self.copycenter = copycenter
+
+ def createWidget(self, dialog, plugin, parent):
+ """ Each plugin may provide a qt.QWidget back to the
+ CopyCenter.py. The widget will be used to configure our
+ plugin settings. """
+
+ import qt
+ import os
+ import re
+
+ self.dialog = dialog
+ self.mainbox = None
+ class ProjectBox(qt.QHBox):
+ def __init__(self,main,copycenterplugin,plugin,parent):
+ self.main = main
+ self.copycenterplugin = copycenterplugin
+ self.plugin = plugin
+
+ qt.QHBox.__init__(self,parent)
+ prjlabel = qt.QLabel("Project File:",self)
+ self.prjcombo = qt.QComboBox(self)
+ self.prjcombo.setEditable(True)
+ self.prjcombo.insertItem("")
+
+ path = copycenterplugin.copycenter.homepath
+ for f in os.listdir(path):
+ file = os.path.join(path,f)
+ if os.path.isfile(file) and re.search(".+\\.(kexi|kexis|kexic)$",f):
+ self.prjcombo.insertItem(os.path.join("~",f))
+
+ prjlabel.setBuddy(self.prjcombo)
+ prjsavebtn = qt.QPushButton("...",self)
+ qt.QObject.connect(prjsavebtn, qt.SIGNAL("clicked()"),self.buttonClicked)
+ qt.QObject.connect(self.prjcombo, qt.SIGNAL("textChanged(const QString&)"), self.main.projectChanged)
+ self.setStretchFactor(self.prjcombo,1)
+ def buttonClicked(self):
+ text = str(self.prjcombo.currentText())
+ if text == "":
+ text = self.copycenterplugin.copycenter.homepath
+ elif re.search("^\\~(\\/|\\\\)",text):
+ import os
+ text = os.path.join(self.copycenterplugin.copycenter.homepath,text[2:])
+ if self.plugin.plugintype == "Source":
+ filename = qt.QFileDialog.getOpenFileName(text,"*.kexi *.kexis *.kexic;;*",self.copycenterplugin.dialog)
+ else: # "Destination":
+ filename = qt.QFileDialog.getSaveFileName(text,"*.kexi *.kexis *.kexic;;*",self.copycenterplugin.dialog)
+ if str(filename) != "": self.prjcombo.setCurrentText(str(filename))
+
+ class DriverBox(qt.QVBox):
+ def __init__(self,main,parent):
+ qt.QVBox.__init__(self,parent)
+ self.main = main
+ self.copycenterplugin = main.copycenterplugin
+ self.plugin = main.plugin
+ self.driver = None
+
+ driverbox = qt.QHBox(self)
+ driverlabel = qt.QLabel("Driver:",driverbox)
+ self.drivercombo = qt.QComboBox(driverbox)
+ self.drivercombo.insertItem("")
+ for driver in self.copycenterplugin.drivermanager.driverNames():
+ self.drivercombo.insertItem(driver)
+
+ qt.QObject.connect(self.drivercombo, qt.SIGNAL("activated(int)"), self.activated)
+ driverlabel.setBuddy(self.drivercombo)
+ driverbox.setStretchFactor(self.drivercombo,1)
+
+ self.box = qt.QVBox(self)
+ self.mainbox = None
+
+ def activated(self,index):
+ drivertext = str(self.drivercombo.currentText())
+
+ self.box.hide()
+ if self.mainbox:
+ self.mainbox.hide()
+ self.mainbox.destroy()
+ self.mainbox = None
+ if index == 0 or drivertext == "":
+ self.driver = None
+ self.box.show()
+ self.main.updateConnectButtons()
+ return False
+
+ self.driver = self.copycenterplugin.drivermanager.driver(drivertext)
+
+ mainbox = qt.QVBox(self.box)
+ mainbox.setSpacing(2)
+
+ if self.driver.isFileDriver():
+ filebox = qt.QHBox(mainbox)
+ filelabel = qt.QLabel("File:",filebox)
+ self.fileedit = qt.QLineEdit(self.plugin.options['file'],filebox)
+ filelabel.setBuddy(self.fileedit)
+ filebox.setStretchFactor(self.fileedit,1)
+ filebtn = qt.QPushButton("...",filebox)
+ qt.QObject.connect(filebtn, qt.SIGNAL("clicked()"), self.fileClicked)
+ else:
+ hostbox = qt.QHBox(mainbox)
+ hostlabel = qt.QLabel("Hostname:",hostbox)
+ self.hostedit = qt.QLineEdit(self.plugin.options['hostname'],hostbox)
+ hostlabel.setBuddy(self.hostedit)
+ hostbox.setStretchFactor(self.hostedit,1)
+
+ portbox = qt.QHBox(mainbox)
+ portlabel = qt.QLabel("Port:",portbox)
+ self.portedit = qt.QLineEdit(self.plugin.options['port'],portbox)
+ portlabel.setBuddy(self.portedit)
+ portbox.setStretchFactor(self.portedit,1)
+
+ sockbox = qt.QHBox(mainbox)
+ self.sockfilecheckbox = qt.QCheckBox("Socket File:",sockbox)
+ qt.QObject.connect(self.sockfilecheckbox, qt.SIGNAL("toggled(bool)"), self.sockfilecheckboxClicked)
+ self.sockfilebox = qt.QHBox(sockbox)
+ self.sockfileedit = qt.QLineEdit(self.plugin.options['socketfile'],self.sockfilebox)
+ self.sockfilebox.setEnabled(False)
+ sockfilebtn = qt.QPushButton("...",self.sockfilebox)
+ self.sockfilecheckbox.setChecked( str(self.plugin.options['usesocketfile']) == str(True) )
+ qt.QObject.connect(sockfilebtn, qt.SIGNAL("clicked()"), self.sockfileClicked)
+ self.sockfilebox.setStretchFactor(self.sockfileedit,1)
+ sockbox.setStretchFactor(self.sockfilebox,1)
+
+ userbox = qt.QHBox(mainbox)
+ userlabel = qt.QLabel("Username:",userbox)
+ self.useredit = qt.QLineEdit(self.plugin.options['username'],userbox)
+ userlabel.setBuddy(self.useredit)
+ userbox.setStretchFactor(self.useredit,1)
+
+ passbox = qt.QHBox(mainbox)
+ passlabel = qt.QLabel("Password:",passbox)
+ self.passedit = qt.QLineEdit(self.plugin.options['password'],passbox)
+ self.passedit.setEchoMode(qt.QLineEdit.Password)
+ passlabel.setBuddy(self.passedit)
+ passbox.setStretchFactor(self.passedit,1)
+
+ dbbox = qt.QHBox(mainbox)
+ dblabel = qt.QLabel("Database:",dbbox)
+ self.dbedit = qt.QLineEdit(self.plugin.options['database'],dbbox)
+ dblabel.setBuddy(self.dbedit)
+ dbbox.setStretchFactor(self.dbedit,1)
+ #self.tablecombo.setText("")
+
+ self.mainbox = mainbox
+ self.mainbox.show()
+ self.box.show()
+ self.main.updateConnectButtons()
+ return True
+
+ def fileClicked(self):
+ text = str(self.fileedit.text())
+ if text == "": text = self.copycenterplugin.copycenter.homepath
+ if self.plugin.plugintype == "Source":
+ filename = qt.QFileDialog.getOpenFileName(text,"*",self.copycenterplugin.dialog)
+ else: # "Destination":
+ filename = qt.QFileDialog.getSaveFileName(text,"*",self.copycenterplugin.dialog)
+ if str(filename) != "": self.fileedit.setText(str(filename))
+ def sockfilecheckboxClicked(self,checked):
+ self.sockfilebox.setEnabled(checked)
+
+ def sockfileClicked(self):
+ text = str(self.sockfileedit.text())
+ if text == "": text = self.copycenterplugin.copycenter.homepath
+ if self.plugin.plugintype == "Source":
+ filename = qt.QFileDialog.getOpenFileName(text,"*",self.copycenterplugin.dialog)
+ else: # "Destination":
+ filename = qt.QFileDialog.getSaveFileName(text,"*",self.copycenterplugin.dialog)
+ if str(filename) != "": self.sockfileedit.setText(str(filename))
+
+ class TableBox(qt.QHBox):
+ def __init__(self,copycenterplugin,plugin,parent):
+ qt.QHBox.__init__(self,parent)
+ self.copycenterplugin = copycenterplugin
+ self.plugin = plugin
+ tablelabel = qt.QLabel("Table:",self)
+ self.tableedit = qt.QLineEdit(self.plugin.options['table'],self)
+ self.tablebtn = qt.QPushButton("...",self)
+ self.tablebtn.setEnabled(False)
+ qt.QObject.connect(self.tablebtn, qt.SIGNAL("clicked()"), self.buttonClicked)
+ tablelabel.setBuddy(self.tableedit)
+ self.setStretchFactor(self.tableedit,1)
+ def buttonClicked(self):
+ ListViewDialog = self.copycenterplugin.dialog.ListViewDialog
+ class TableDialog(ListViewDialog):
+ def __init__(self,tablebox):
+ ListViewDialog.__init__(self,tablebox,"Tables")
+ self.mainwidget = tablebox
+ self.listview.addColumn("Name")
+ text = str(self.mainwidget.tableedit.text())
+ item = None
+ for table in self.mainwidget.plugin.connection.tableNames():
+ if item == None:
+ item = qt.QListViewItem(self.listview,table)
+ else:
+ item = qt.QListViewItem(self.listview,item,table)
+ if table == text:
+ self.listview.setSelected(item,True)
+ self.listview.ensureItemVisible(item)
+ qt.QObject.connect(self.listview, qt.SIGNAL("doubleClicked(QListViewItem*, const QPoint&, int)"), self.okClicked)
+ qt.QObject.connect(self.okbtn, qt.SIGNAL("clicked()"), self.okClicked)
+ def okClicked(self):
+ item = self.listview.selectedItem()
+ if item == None:
+ self.mainwidget.tableedit.setText("")
+ else:
+ self.mainwidget.tableedit.setText(item.text(0))
+ self.close()
+ dialog = TableDialog(self)
+ dialog.show()
+
+ class FieldBox(qt.QHBox):
+ def __init__(self,copycenterplugin,plugin,parent):
+ qt.QHBox.__init__(self,parent)
+ self.copycenterplugin = copycenterplugin
+ self.plugin = plugin
+ self.tablename = ""
+ fieldslabel = qt.QLabel("Fields:",self)
+ self.fieldsedit = qt.QLineEdit(self.plugin.options['fields'],self)
+ self.setStretchFactor(self.fieldsedit,1)
+ fieldslabel.setBuddy(self.fieldsedit)
+ self.fieldsbtn = qt.QPushButton("...",self)
+ self.fieldsbtn.setEnabled(False)
+ qt.QObject.connect(self.fieldsbtn, qt.SIGNAL("clicked()"), self.fieldsClicked)
+ def fieldsClicked(self):
+ ListViewDialog = self.copycenterplugin.dialog.ListViewDialog
+ class FieldsDialog(ListViewDialog):
+ def __init__(self, fieldbox):
+ ListViewDialog.__init__(self,fieldbox,"Fields")
+ self.fieldbox = fieldbox
+ self.listview.setSelectionMode(qt.QListView.Multi)
+ self.listview.setSorting(-1)
+ self.listview.header().setClickEnabled(False)
+ self.listview.addColumn("Name")
+ self.listview.addColumn("Type")
+ self.listview.addColumn("Options")
+ fieldslist = str(self.fieldbox.fieldsedit.text()).split(",")
+ allfields = ("*" in fieldslist)
+ tableschema = self.fieldbox.plugin.connection.tableSchema(self.fieldbox.tablename)
+ item = None
+ for field in tableschema.fieldlist().fields():
+ opts = []
+ for opt in ("isAutoInc","isNotNull","isNotEmpty"):
+ if getattr(field,opt)():
+ opts.append(opt[2:])
+ item = self.addItem(( field.name(),field.type(),",".join(opts) ),item)
+ if allfields or field.name() in fieldslist:
+ self.listview.setSelected(item,True)
+ qt.QObject.connect(self.okbtn, qt.SIGNAL("clicked()"), self.okClicked)
+ def okClicked(self):
+ selitems = []
+ item = self.listview.firstChild()
+ while item:
+ if item.isSelected():
+ selitems.append(str(item.text(0)))
+ item = item.nextSibling()
+ self.fieldbox.fieldsedit.setText(",".join(selitems))
+ self.close()
+ dialog = FieldsDialog(self)
+ dialog.show()
+ def tableChanged(self, text):
+ self.tablename = str(text)
+ if self.plugin.connection.isConnected():
+ if self.plugin.connection.hasTableName(self.tablename):
+ self.fieldsbtn.setEnabled(True)
+ return
+ self.fieldsbtn.setEnabled(False)
+
+ class MainBox(qt.QHBox):
+ def __init__(self,copycenterplugin,plugin,parent):
+ qt.QHBox.__init__(self,parent)
+ self.copycenterplugin = copycenterplugin
+ self.plugin = plugin
+
+ self.prjbox = ProjectBox(self,copycenterplugin,plugin,parent)
+ self.driverbox = DriverBox(self,parent)
+
+ statusbar = qt.QHBox(parent)
+ statusbar.setSpacing(2)
+ #self.statuslabel = qt.QLabel("Disconnected",statusbar)
+ #statusbar.setStretchFactor(self.statuslabel,1)
+ statusbar.setStretchFactor(qt.QWidget(statusbar),1)
+ self.connectbtn = qt.QPushButton("Connect",statusbar)
+ self.connectbtn.setEnabled(False)
+ qt.QObject.connect(self.connectbtn, qt.SIGNAL("clicked()"),self.connectClicked)
+ self.disconnectbtn = qt.QPushButton("Disconnect",statusbar)
+ self.disconnectbtn.setEnabled(False)
+ qt.QObject.connect(self.disconnectbtn, qt.SIGNAL("clicked()"),self.disconnectClicked)
+
+ #self.connectionbox = ConnectionBox(copycenterplugin,plugin,parent)
+ self.tablebox = TableBox(copycenterplugin,plugin,parent)
+ self.fieldbox = FieldBox(copycenterplugin,plugin,parent)
+ qt.QObject.connect(self.tablebox.tableedit, qt.SIGNAL("textChanged(const QString&)"), self.fieldbox.tableChanged)
+
+ if self.plugin.options['project'] != '':
+ self.prjbox.prjcombo.setCurrentText(self.plugin.options['project'])
+
+ if self.plugin.options['driver'] != '':
+ try:
+ item = str(self.driverbox.drivercombo.listBox().findItem(self.plugin.options['driver'],qt.Qt.ExactMatch).text())
+ self.driverbox.drivercombo.setCurrentText(item)
+ self.driverbox.activated(item)
+ except:
+ pass
+
+ if self.plugin.plugintype == "Destination":
+ #typebox = qt.QHBox(parent)
+ #label = qt.QLabel("Operation:",typebox)
+ #combobox = qt.QComboBox(typebox)
+ #combobox.insertItem("Append")
+ #combobox.insertItem("Replace")
+ #combobox.insertItem("Update")
+ #combobox.insertItem("Update/Insert")
+ #combobox.insertItem("Insert new")
+ #label.setBuddy(combobox)
+ #typebox.setStretchFactor(combobox,1)
+ pass
+ elif self.plugin.plugintype == "Source":
+ wherebox = qt.QHBox(parent)
+ wherelabel = qt.QLabel("Where:",wherebox)
+ self.whereedit = qt.QLineEdit(self.plugin.options['where'],wherebox)
+
+ #orderbox = qt.QHBox(parent)
+ #orderlabel = qt.QLabel("Order By:",orderbox)
+ #orderedit = qt.QLineEdit("",orderbox)
+
+ #errbox = qt.QHBox(parent)
+ #errlabel = qt.QLabel("On Error:",errbox)
+ #errcombo = qt.QComboBox(errbox)
+ #errcombo.insertItem("Ask")
+ #errcombo.insertItem("Skip")
+ #errcombo.insertItem("Abort")
+ #errlabel.setBuddy(errcombo)
+ #errbox.setStretchFactor(errcombo,1)
+
+ if self.plugin.options['autoconnect']:
+ self.connectClicked()
+
+ def projectChanged(self, text):
+ #if self.driverbox.drivercombo.currentItem() != 0:
+ # self.driverbox.drivercombo.setCurrentItem(0)
+
+ file = str(text)
+ import os
+ if re.search("^\\~(\\/|\\\\)",file):
+ file = os.path.join(self.copycenterplugin.copycenter.homepath,file[2:])
+ if file == "" or not os.path.isfile(file):
+ self.driverbox.drivercombo.setCurrentItem(0)
+ self.driverbox.activated(0)
+ return
+
+ connectiondata = self.copycenterplugin.drivermanager.createConnectionDataByFile(file)
+ if connectiondata == None:
+ raise "Unsupported file."
+
+ drivername = connectiondata.driverName().lower()
+ print "driver: %s" % drivername
+ for i in range(1,self.driverbox.drivercombo.count()):
+ if drivername == self.driverbox.drivercombo.text(i).lower():
+ self.driverbox.drivercombo.setCurrentItem(i)
+ self.driverbox.activated(i)
+ break
+
+ if self.driverbox.driver != None:
+ if self.driverbox.driver.isFileDriver():
+ self.driverbox.fileedit.setText(connectiondata.fileName())
+ else: # server
+ self.driverbox.hostedit.setText(connectiondata.hostName())
+ self.driverbox.portedit.setText(str(connectiondata.port()))
+ self.driverbox.sockfilecheckbox.setChecked(connectiondata.localSocketFileUsed())
+ self.driverbox.sockfileedit.setText(connectiondata.localSocketFileName())
+ self.driverbox.useredit.setText(connectiondata.userName())
+ self.driverbox.passedit.setText(connectiondata.password())
+ self.driverbox.dbedit.setText(connectiondata.databaseName())
+
+ def connectClicked(self):
+ if self.driverbox.driver == None:
+ print "No driver selected."
+ return False
+ connectiondata = self.copycenterplugin.drivermanager.createConnectionData()
+ if self.driverbox.driver.isFileDriver():
+ file = str(self.driverbox.fileedit.text())
+ if file == "" or not os.path.isfile(file):
+ qt.QMessageBox.critical(self,"Failed to connect","There exists no such database file \"%s\"" % file)
+ return False
+ connectiondata.setFileName(file)
+ connectiondata.setDatabaseName(file)
+ else:
+ connectiondata.setHostName(str(self.driverbox.hostedit.text()))
+ connectiondata.setPort(str(self.driverbox.portedit.text()))
+ connectiondata.setLocalSocketFileUsed(self.driverbox.sockfilecheckbox.isChecked())
+ connectiondata.setLocalSocketFileName(str(self.driverbox.sockfileedit.text()))
+ connectiondata.setPassword(str(self.driverbox.passedit.text()))
+ connectiondata.setUserName(str(self.driverbox.useredit.text()))
+ connectiondata.setDatabaseName(str(self.driverbox.dbedit.text()))
+ print "Creating connection"
+ connection = self.driverbox.driver.createConnection(connectiondata)
+ print "Trying to connect"
+ if not connection.connect():
+ qt.QMessageBox.critical(self,"Failed to connect",connection.lastError())
+ return False
+ print "Use database \"%s\"" % connectiondata.databaseName()
+ if not connection.useDatabase( connectiondata.databaseName() ):
+ qt.QMessageBox.critical(self,"Failed to connect",connection.lastError())
+ return False
+ print "dbnames = %s" % connection.databaseNames()
+ print "tablenames = %s" % connection.tableNames()
+ #self.useDatabase(connection, filename)
+
+ self.plugin.connection.kexidbconnection = connection
+ self.updateConnectButtons()
+ return True
+
+ def disconnectClicked(self):
+ if not self.plugin.connection.disconnect():
+ qt.QMessageBox.critical(self,"Failed to disconnect",self.plugin.connection.lastError())
+ return
+ self.updateConnectButtons()
+
+ def updateConnectButtons(self):
+ connected = self.plugin.connection.isConnected()
+ self.prjbox.setEnabled(not connected)
+ self.driverbox.setEnabled(not connected)
+ self.connectbtn.setEnabled( (not connected) and (self.driverbox.driver != None) )
+ self.disconnectbtn.setEnabled(connected)
+ self.tablebox.tablebtn.setEnabled(connected)
+ self.fieldbox.tableChanged(self.tablebox.tableedit.text())
+
+ def getOptionValue(self,optionname):
+ try:
+ if optionname == 'project': return str(self.prjbox.prjcombo.currentText())
+ elif optionname == 'driver': return str(self.driverbox.drivercombo.currentText())
+ elif optionname == 'file': return str(self.driverbox.fileedit.text())
+ elif optionname == 'hostname': return str(self.driverbox.hostedit.text())
+ elif optionname == 'port': return str(self.driverbox.portedit.text())
+ elif optionname == 'usesocketfile': return str(self.driverbox.sockfilecheckbox.isChecked())
+ elif optionname == 'socketfile': return str(self.driverbox.sockfileedit.text())
+ elif optionname == 'username': return str(self.driverbox.useredit.text())
+ elif optionname == 'password': return str(self.driverbox.passedit.text())
+ elif optionname == 'database': return str(self.driverbox.dbedit.text())
+ elif optionname == 'table': return str(self.tablebox.tableedit.text())
+ elif optionname == 'fields': return str(self.fieldbox.fieldsedit.text())
+ elif optionname == 'where': return str(self.whereedit.text())
+ except:
+ pass
+ return ""
+
+ mainbox = MainBox(self,plugin,parent)
+ plugin.widget = mainbox
+ return mainbox
+
diff --git a/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginQtSQL.py b/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginQtSQL.py
new file mode 100644
index 00000000..985d757d
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginQtSQL.py
@@ -0,0 +1,495 @@
+"""
+CopyCenterPlugin to provide 'QtSQL'.
+
+Description:
+This python-script is a plugin for the CopyCenter.py.
+
+Author:
+Sebastian Sauer <mail@dipe.org>
+
+Copyright:
+GPL v2 or higher.
+"""
+
+class CopyCenterPlugin:
+ """ The CopyCenterPlugin to provide 'QtSQL' to CopyCenter.py """
+
+ name = "QtSQL Database"
+ """ The name this plugin has. The name should be unique and
+ will be used for displaying a caption. """
+
+ class Plugin:
+ def _init_(self,copycenterplugin):
+ self.copycenterplugin = copycenterplugin
+ self.widget = None
+ self.database = None
+ self.cursor = None
+ self.isfinished = True
+ def _init(self,copierer):
+ self.copierer = copierer
+ if not self.widget.connectClicked():
+ raise "Failed to connect with database."
+ if self.database == None or not self.database.isOpen():
+ raise "Database is not initialized or not opened."
+ self.copierer.appendProgressMessage("Connected: %s %s@%s:%i %s" %
+ (str(self.database.driverName()),str(self.database.userName()),str(self.database.hostName()),self.database.port(),str(self.database.databaseName())) )
+ self.isfinished = False
+ def isFinished(self):
+ return self.isfinished
+ def finish(self):
+ self.isfinished = True
+ self.widget.disconnectClicked()
+ def createWidget(self,dialog,parent):
+ return self.copycenterplugin.widget(dialog, self, parent)
+
+ class Source(Plugin):
+ plugintype = "Source"
+ def __init__(self,copycenterplugin):
+ self._init_(copycenterplugin)
+ self.options = {
+ 'driver': 'QMYSQL3', #'QMYSQL3','QPSQL7','QODBC3',...
+ 'hostname': '127.0.0.1',
+ 'port': 3306,
+ 'username': 'root', #'MyUsername',
+ 'password': '', #'MySecretPassword',
+ 'database': '', #'MyQtSQLDatabase',
+ 'table': '', #'table1',
+ 'fields': '', #'f1,f2',
+ 'where': '',
+ }
+ def init(self,copierer):
+ self._init(copierer)
+ tablename = str(self.widget.tableedit.text())
+ wherestatement = str(self.widget.whereedit.text())
+ import qt
+ import qtsql
+ self.cursor = qtsql.QSqlCursor(tablename,True,self.database)
+ self.cursor.setFilter(wherestatement)
+ if not self.cursor.select():
+ raise "Select on cursor failed.<br>%s<br>%s" % ( str(self.cursor.lastError().driverText()),str(self.cursor.lastError().databaseText()) )
+ self.fieldlist = []
+ for fieldname in str(self.widget.fieldedit.text()).split(","):
+ fn = fieldname.strip()
+ if fn != "":
+ field = self.cursor.field(fn)
+ if not field:
+ raise "There exists no such field \"%s\" in the table \"%s\"." % (fn,tablename)
+ self.fieldlist.append(str(field.name()))
+ if len(self.fieldlist) < 1:
+ raise "No fields for table \"%s\" defined." % tablename
+ copierer.appendProgressMessage("SQL: %s" % str(self.cursor.executedQuery()))
+
+ def read(self):
+ if not self.cursor.next():
+ return None
+ record = []
+ for fieldname in self.fieldlist:
+ record.append( unicode(self.cursor.value(fieldname).toString()).encode("latin-1") )
+ #print "read record: %s" % record
+ return record
+
+ class Destination(Plugin):
+ plugintype = "Destination"
+ def __init__(self,copycenterplugin):
+ self._init_(copycenterplugin)
+ self.options = {
+ 'driver': 'QMYSQL3', #'QMYSQL3','QPSQL7','QODBC3',...
+ 'hostname': '127.0.0.1',
+ 'port': 3306,
+ 'username': 'root', #'MyUsername',
+ 'password': '', #'MySecretPassword',
+ 'database': '', #'MyQtSQLDatabase',
+ 'table': '', #'table2',
+ 'fields': '', #'field1,field2',
+ 'operation': 'Insert', #'Insert','Update'...
+ 'indexfield': '',
+ }
+ def init(self,copierer):
+ self._init(copierer)
+ import qt
+ import qtsql
+
+ self.fieldlist = []
+ for fieldname in str(self.widget.fieldedit.text()).split(","):
+ fn = fieldname.strip()
+ if fn != "": self.fieldlist.append(fn)
+
+ tablename = str(self.widget.tableedit.text())
+ self.cursor = qtsql.QSqlCursor(tablename,True,self.database)
+ {
+ 0: self.initInsert,
+ 1: self.initUpdate
+ }[ self.widget.operationedit.currentItem() ]()
+
+ def initInsert(self):
+ self.write = self.writeInsert
+ if not self.cursor.select():
+ raise "Select on cursor failed.<br>%s<br>%s" % ( str(self.cursor.lastError().driverText()),str(self.cursor.lastError().databaseText()) )
+ for fieldname in self.fieldlist: # check fieldlist
+ field = self.cursor.field(fieldname)
+ if not field: raise "There exists no such field \"%s\" in the table \"%s\"." % (fieldname, self.cursor.name())
+ self.copierer.appendProgressMessage("Insert SQL: %s" % str(self.cursor.executedQuery()))
+
+ def writeInsert(self, record):
+ print "insert record: %s" % record
+ import qt
+ cursorrecord = self.cursor.primeInsert()
+ count = len(record)
+ for i in range(len(self.fieldlist)):
+ if i == count: break
+ r = record[i]
+ if r == None:
+ v = qt.QVariant()
+ else:
+ v = qt.QVariant(r)
+ cursorrecord.setValue(self.fieldlist[i], v)
+ rowcount = self.cursor.insert()
+ if rowcount < 1:
+ drv = unicode(self.cursor.lastError().driverText()).encode("latin-1")
+ db = unicode(self.cursor.lastError().databaseText()).encode("latin-1")
+ print "failed: %s %s" % (drv,db)
+ self.copierer.writeFailed(record)
+ else:
+ self.copierer.writeSuccess(record,rowcount)
+ #import time
+ #time.sleep(1)
+ return True
+
+ def initUpdate(self):
+ self.write = self.writeUpdate
+ self.indexfieldname = str(self.widget.indexedit.text()).strip()
+ if self.indexfieldname == "": raise "No index-field defined."
+ pkindex = self.cursor.index(self.indexfieldname)
+ if not pkindex: raise "Invalid index-field defined."
+ self.cursor.setPrimaryIndex(pkindex)
+ #self.cursor.setMode( qtsql.QSqlCursor.Insert | qtsql.QSqlCursor.Update )
+ self.copierer.appendProgressMessage("Update SQL: %s" % str(self.cursor.executedQuery()))
+
+ def writeUpdate(self, record):
+ import qt
+ # determinate the primary-index
+ try:
+ idx = self.fieldlist.index(self.indexfieldname)
+ indexvalue = record[idx]
+ except:
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+ raise "Failed to determinate the value for the primary key."
+ # select cursor and go to matching record.
+ wherestatement = "%s = \"%s\"" % (self.indexfieldname, indexvalue)
+ if not self.cursor.select(wherestatement):
+ raise "Select on cursor failed.<br>%s<br>%s" % ( str(self.cursor.lastError().driverText()),str(self.cursor.lastError().databaseText()) )
+ if not self.cursor.next():
+ #print "No such record to update !"
+ return False
+ # Prepare updating the record.
+ cursorrecord = self.cursor.primeUpdate()
+ # Update the fields in the record.
+ count = len(record)
+ for i in range(len(self.fieldlist)):
+ if i == count: break
+ fieldname = self.fieldlist[i]
+ if self.indexfieldname != fieldname: # don't update the indexfield!
+ r = record[i]
+ if r == None:
+ v = qt.QVariant()
+ else:
+ v = qt.QVariant(r)
+ cursorrecord.setValue(fieldname, v)
+ # Write updated record.
+ rowcount = self.cursor.update()
+ if rowcount < 1:
+ self.copierer.writeFailed(record)
+ else:
+ self.copierer.writeSuccess(record,rowcount)
+ print "updated record (rowcount %s): %s" % (rowcount,record)
+ return True
+
+ def __init__(self, copycenter):
+ """ Constructor. """
+ pass
+
+ def widget(self,dialog,plugin,parent):
+ """ Each plugin may provide a qt.QWidget back to the
+ CopyCenter.py. The widget will be used to configure our
+ plugin settings. """
+
+ import qt
+ import os
+
+ self.dialog = dialog
+ ListViewDialog = self.dialog.ListViewDialog
+ class TableDialog(ListViewDialog):
+ def __init__(self, mainwidget):
+ ListViewDialog.__init__(self,mainwidget,"Tables")
+ self.mainwidget = mainwidget
+ self.listview.addColumn("Name")
+ text = str(self.mainwidget.tableedit.text())
+ item = None
+ for table in self.mainwidget.plugin.database.tables():
+ if item == None:
+ item = qt.QListViewItem(self.listview,table)
+ else:
+ item = qt.QListViewItem(self.listview,item,table)
+ if table == text:
+ self.listview.setSelected(item,True)
+ self.listview.ensureItemVisible(item)
+ qt.QObject.connect(self.listview, qt.SIGNAL("doubleClicked(QListViewItem*, const QPoint&, int)"), self.okClicked)
+ qt.QObject.connect(self.okbtn, qt.SIGNAL("clicked()"), self.okClicked)
+ def okClicked(self):
+ item = self.listview.selectedItem()
+ if item == None:
+ self.mainwidget.tableedit.setText("")
+ else:
+ self.mainwidget.tableedit.setText(item.text(0))
+ self.close()
+
+ class FieldsDialog(ListViewDialog):
+ def __init__(self, mainwidget):
+ ListViewDialog.__init__(self,parent,"Fields")
+ self.mainwidget = mainwidget
+ self.listview.setSelectionMode(qt.QListView.Multi)
+ self.listview.setSorting(-1)
+ self.listview.header().setClickEnabled(False)
+ self.listview.addColumn("Name")
+ self.listview.addColumn("Type")
+ self.listview.addColumn("Options")
+ tablename = str(self.mainwidget.tableedit.text())
+ recinfo = self.mainwidget.plugin.database.recordInfo(tablename)
+ if recinfo != None:
+ fieldslist = str(self.mainwidget.fieldedit.text()).split(",")
+ allfields = ("*" in fieldslist)
+ item = None
+ for fieldinfo in recinfo:
+ opts = ""
+ for s in ('Required','Calculated'): #,'Generated'):
+ if getattr(fieldinfo,"is%s" % s)(): opts += "%s " % s
+ item = self.addItem((fieldinfo.name(), qt.QVariant.typeToName(fieldinfo.type()), opts),item)
+ if allfields or fieldinfo.name() in fieldslist:
+ self.listview.setSelected(item,True)
+ qt.QObject.connect(self.okbtn, qt.SIGNAL("clicked()"), self.okClicked)
+ def okClicked(self):
+ selitems = []
+ item = self.listview.firstChild()
+ while item:
+ if item.isSelected():
+ selitems.append(str(item.text(0)))
+ item = item.nextSibling()
+ self.mainwidget.fieldedit.setText(",".join(selitems))
+ self.close()
+
+
+ class MainWidget(qt.QHBox):
+ def __init__(self,plugin,dialog,parent):
+ import qt
+ import qtsql
+ qt.QHBox.__init__(self,parent)
+ self.dialog = dialog
+ self.plugin = plugin
+
+ self.connectionbox = qt.QVBox(parent)
+ self.connectionbox.setSpacing(2)
+
+ driverbox = qt.QHBox(self.connectionbox)
+ driverlabel = qt.QLabel("Driver:",driverbox)
+ self.driveredit = qt.QComboBox(driverbox)
+ for driver in qtsql.QSqlDatabase.drivers():
+ self.driveredit.insertItem(driver)
+ if self.plugin.options['driver'] == driver:
+ self.driveredit.setCurrentItem(self.driveredit.count() - 1)
+ driverlabel.setBuddy(self.driveredit)
+ driverbox.setStretchFactor(self.driveredit,1)
+
+ hostbox = qt.QHBox(self.connectionbox)
+ hostlabel = qt.QLabel("Hostname:",hostbox)
+ self.hostedit = qt.QLineEdit(self.plugin.options['hostname'],hostbox)
+ hostlabel.setBuddy(self.hostedit)
+ hostbox.setStretchFactor(self.hostedit,1)
+
+ portbox = qt.QHBox(self.connectionbox)
+ portlabel = qt.QLabel("Port:",portbox)
+ self.portedit = qt.QLineEdit(str(self.plugin.options['port']),portbox)
+ portlabel.setBuddy(self.portedit)
+ portbox.setStretchFactor(self.portedit,1)
+
+ userbox = qt.QHBox(self.connectionbox)
+ userlabel = qt.QLabel("Username:",userbox)
+ self.useredit = qt.QLineEdit(self.plugin.options['username'],userbox)
+ userlabel.setBuddy(self.useredit)
+ userbox.setStretchFactor(self.useredit,1)
+
+ passbox = qt.QHBox(self.connectionbox)
+ passlabel = qt.QLabel("Password:",passbox)
+ self.passedit = qt.QLineEdit(self.plugin.options['password'],passbox)
+ self.passedit.setEchoMode(qt.QLineEdit.Password)
+ passlabel.setBuddy(self.passedit)
+ passbox.setStretchFactor(self.passedit,1)
+
+ dbbox = qt.QHBox(self.connectionbox)
+ dblabel = qt.QLabel("Database:",dbbox)
+ self.dbedit = qt.QLineEdit(self.plugin.options['database'],dbbox)
+ dblabel.setBuddy(self.dbedit)
+ dbbox.setStretchFactor(self.dbedit,1)
+
+ statusbar = qt.QHBox(parent)
+ statusbar.setSpacing(2)
+ statusbar.setStretchFactor(qt.QWidget(statusbar),1)
+ self.connectbtn = qt.QPushButton("Connect",statusbar)
+ qt.QObject.connect(self.connectbtn, qt.SIGNAL("clicked()"),self.connectClicked)
+ self.disconnectbtn = qt.QPushButton("Disconnect",statusbar)
+ self.disconnectbtn.setEnabled(False)
+ qt.QObject.connect(self.disconnectbtn, qt.SIGNAL("clicked()"),self.disconnectClicked)
+
+ tablebox = qt.QHBox(parent)
+ tablelabel = qt.QLabel("Table:",tablebox)
+ self.tableedit = qt.QLineEdit(self.plugin.options['table'],tablebox)
+ qt.QObject.connect(self.tableedit, qt.SIGNAL("textChanged(const QString&)"), self.tableEditChanged)
+ self.tablebtn = qt.QPushButton("...",tablebox)
+ self.tablebtn.setEnabled(False)
+ qt.QObject.connect(self.tablebtn, qt.SIGNAL("clicked()"), self.tableBtnClicked)
+ tablelabel.setBuddy(self.tableedit)
+ tablebox.setStretchFactor(self.tableedit,1)
+
+ fieldbox = qt.QHBox(parent)
+ fieldlabel = qt.QLabel("Fields:",fieldbox)
+ self.fieldedit = qt.QLineEdit(self.plugin.options['fields'],fieldbox)
+ self.fieldbtn = qt.QPushButton("...",fieldbox)
+ self.fieldbtn.setEnabled(False)
+ qt.QObject.connect(self.fieldbtn, qt.SIGNAL("clicked()"), self.fieldBtnClicked)
+ fieldlabel.setBuddy(self.fieldedit)
+ fieldbox.setStretchFactor(self.fieldedit,1)
+
+ if self.plugin.plugintype == "Source":
+ box = qt.QHBox(parent)
+ wherelabel = qt.QLabel("Where:",box)
+ self.whereedit = qt.QLineEdit(self.plugin.options['where'],box)
+ wherelabel.setBuddy(self.whereedit)
+ box.setStretchFactor(self.whereedit,1)
+ elif self.plugin.plugintype == "Destination":
+
+ class OperationBox(qt.QVBox):
+ def __init__(self, mainwidget, parent):
+ self.mainwidget = mainwidget
+ qt.QVBox.__init__(self, parent)
+ opbox = qt.QHBox(self)
+ operationlabel = qt.QLabel("Operation:",opbox)
+ self.mainwidget.operationedit = qt.QComboBox(opbox)
+ for op in ('Insert','Update'):
+ self.mainwidget.operationedit.insertItem(op)
+ if self.mainwidget.plugin.options['operation'] == op:
+ self.mainwidget.operationedit.setCurrentItem(self.mainwidget.operationedit.count() - 1)
+ operationlabel.setBuddy(self.mainwidget.operationedit)
+ opbox.setStretchFactor(self.mainwidget.operationedit,1)
+ self.box = None
+ qt.QObject.connect(self.mainwidget.operationedit, qt.SIGNAL("activated(int)"), self.operationActivated)
+ self.operationActivated()
+ def operationActivated(self, **args):
+ if self.box:
+ self.box.hide()
+ self.box.destroy()
+ self.box = None
+ def showInsert(self):
+ pass
+ def showUpdate(self):
+ self.box = qt.QHBox(self)
+ indexlabel = qt.QLabel("Indexfield:", self.box)
+ self.mainwidget.indexedit = qt.QLineEdit(self.mainwidget.plugin.options['indexfield'], self.box)
+ indexlabel.setBuddy(self.mainwidget.indexedit)
+ self.box.setStretchFactor(self.mainwidget.indexedit,1)
+ {
+ 0: showInsert,
+ 1: showUpdate,
+ }[ self.mainwidget.operationedit.currentItem() ](self)
+ if self.box != None: self.box.show()
+ OperationBox(self,parent)
+
+ def tableEditChanged(self,text):
+ if self.plugin.database != None and self.plugin.database.isOpen():
+ if str(text) in self.plugin.database.tables():
+ self.fieldbtn.setEnabled(True)
+ return
+ self.fieldbtn.setEnabled(False)
+
+ def tableBtnClicked(self):
+ dialog = TableDialog(self)
+ dialog.show()
+
+ def fieldBtnClicked(self):
+ dialog = FieldsDialog(self)
+ dialog.show()
+
+ def updateConnectState(self):
+ connected = self.plugin.database != None and self.plugin.database.isOpen()
+ self.connectionbox.setEnabled(not connected)
+ self.connectbtn.setEnabled(not connected)
+ self.disconnectbtn.setEnabled(connected)
+ self.tablebtn.setEnabled(connected)
+ self.tableEditChanged(self.tableedit.text())
+
+ def getOptionValue(self,optionname):
+ try:
+ if optionname == 'driver': return str(self.driveredit.currentText())
+ if optionname == 'hostname': return str(self.hostedit.text())
+ if optionname == 'port': return str(self.portedit.text())
+ if optionname == 'username': return str(self.useredit.text())
+ if optionname == 'password': return str(self.passedit.text())
+ if optionname == 'database': return str(self.dbedit.text())
+ if optionname == 'table': return str(self.tableedit.text())
+ if optionname == 'fields': return str(self.fieldedit.text())
+ if optionname == 'where': return str(self.whereedit.text())
+ if optionname == 'operation': return str(self.operationedit.currentText())
+ if optionname == 'indexfield': return str(self.indexedit.text())
+ except:
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+ return ""
+
+ def connectClicked(self):
+ if self.plugin.database != None and self.plugin.database.isOpen():
+ print "already connected. not needed to reconnect..."
+ self.updateConnectState()
+ return True
+ print "trying to connect..."
+
+ import qtsql
+ drivername = str(self.driveredit.currentText())
+ print "drivername: %s" % drivername
+ connectionname = "CopyCenter%s" % self.plugin.plugintype
+ print "connectionname: %s" % connectionname
+ self.plugin.database = qtsql.QSqlDatabase.addDatabase(drivername,connectionname)
+ if not self.plugin.database:
+ qt.QMessageBox.critical(self,"Failed to connect","<qt>Failed to create database for driver \"%s\"</qt>" % drivername)
+ return False
+
+ hostname = str(self.hostedit.text())
+ self.plugin.database.setHostName(hostname)
+
+ portnumber = int(str(self.portedit.text()))
+ self.plugin.database.setPort(portnumber)
+
+ username = str(self.useredit.text())
+ self.plugin.database.setUserName(username)
+
+ password = str(self.passedit.text())
+ self.plugin.database.setPassword(password)
+
+ databasename = str(self.dbedit.text())
+ self.plugin.database.setDatabaseName(databasename)
+
+ if not self.plugin.database.open():
+ qt.QMessageBox.critical(self,"Failed to connect","<qt>%s<br><br>%s</qt>" % (self.plugin.database.lastError().driverText(),self.plugin.database.lastError().databaseText()))
+ return False
+ print "database is opened now!"
+ self.updateConnectState()
+ return True
+
+ def disconnectClicked(self):
+ print "trying to disconnect..."
+ if self.plugin.database:
+ self.plugin.database.close()
+ self.plugin.database = None
+ print "database is closed now!"
+ self.updateConnectState()
+
+ plugin.widget = MainWidget(plugin,self.dialog,parent)
+ return plugin.widget
diff --git a/kexi/plugins/scripting/scripts/copycenter/Makefile.am b/kexi/plugins/scripting/scripts/copycenter/Makefile.am
new file mode 100644
index 00000000..d46928ed
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/copycenter/Makefile.am
@@ -0,0 +1,4 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+scriptsdir = $(kde_datadir)/kexi/scripts/copycenter
+scripts_SCRIPTS = CopyCenter.py CopyCenterPluginQtSQL.py CopyCenterPluginKexiDB.py CopyCenter.rc readme.html
diff --git a/kexi/plugins/scripting/scripts/copycenter/readme.html b/kexi/plugins/scripting/scripts/copycenter/readme.html
new file mode 100644
index 00000000..2aff6152
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/copycenter/readme.html
@@ -0,0 +1,20 @@
+<html><body>
+<h1>Copy Center</h1>
+
+<b>Version 1.2</b>
+
+<p>Python script to copy data between database backends. The flexible
+plugin-architecture allows transparent copies between different backends.</p>
+
+<ul>
+<li>Read+write Kexi Databases. This includes all database backends supported by Kexi (like SQLite, MySQL or PostgreSQL).</li>
+<li>Read+write QtSQL Databases. MySQL, PostgreSQL and UnixODBC are supported. There might even be more like Oracle in the commercial Qt version or 3rd party backends.</li>
+<li>Runs embedded in Kexi (from the tools=>scripts menu) as well as independent of Kexi (use "krossrunner ~/.kde/share/apps/kexi/scripts/copycenter/CopyCenter.py" or python direct).</li>
+<li>Depends only on PyQt. PyKDE is not used at all and Kross (included in KOffice 1.5) is optional.</li>
+</ul>
+
+Author: (C)2006 Sebastian Sauer (mail at dipe dot org)<br>
+Website: http://www.kde-files.org/content/show.php?content=35251<br>
+License: GPL v2 or higher<br>
+
+</body></html>
diff --git a/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.py b/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.py
new file mode 100644
index 00000000..cace0340
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.py
@@ -0,0 +1,196 @@
+"""
+Export table or query data.
+
+Description:
+This script exports a KexiDB table or query to different fileformats.
+
+Author:
+Sebastian Sauer <mail@dipe.org>
+
+Copyright:
+Dual-licensed under LGPL v2+higher and the BSD license.
+"""
+
+class Datasource:
+ def __init__(self):
+ import kexiapp
+ keximainwindow = kexiapp.get("KexiAppMainWindow")
+
+ try:
+ self.connection = keximainwindow.getConnection()
+ except:
+ raise "No connection established. Please open a project before."
+
+ self.schema = None
+
+ def getSources(self):
+ sources = []
+ for table in self.connection.tableNames():
+ sources.append("Tables/%s" % table)
+ for query in self.connection.queryNames():
+ sources.append("Queries/%s" % query)
+ sources.sort()
+ return sources
+
+ def setSource(self, source):
+ s = source.split("/",1)
+ if s[0] == "Tables":
+ self.schema = self.connection.tableSchema( s[1] )
+ self.queryschema = self.schema.query()
+ elif s[0] == "Queries":
+ self.schema = self.connection.querySchema( s[1] )
+ self.queryschema = self.schema
+ self.cursor = None
+ return self.schema != None
+
+ def name(self):
+ return self.schema.name()
+
+ def caption(self):
+ return self.schema.caption()
+
+ def description(self):
+ return self.schema.description()
+
+ def header(self):
+ h = []
+ for field in self.schema.fieldlist().fields():
+ s = field.caption()
+ if s == None or s == "":
+ s = field.name()
+ h.append(s)
+ return h
+
+ def getNext(self):
+ if not self.cursor:
+ self.cursor = self.connection.executeQuerySchema( self.queryschema )
+ if not self.cursor:
+ raise "Failed to execute queryschema."
+ if not self.cursor.moveFirst():
+ raise "Failed to move cursor to first record."
+ if self.cursor.eof():
+ self.cursor = None
+ return None
+ items = []
+ for i in range( self.cursor.fieldCount() ):
+ items.append( self.cursor.value(i) )
+ self.cursor.moveNext()
+ return items
+
+class HtmlExporter:
+ def __init__(self, datasource):
+ self.datasource = datasource
+
+ def htmlescape(self, text):
+ import string
+ return string.replace(string.replace(string.replace(str(text),'&','&amp;'),'<','&lt;'),'>','&gt;')
+
+ def write(self, output, style):
+ name = self.datasource.name()
+
+ output.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n")
+ output.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n")
+ output.write("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n")
+ output.write("<head><title>%s</title>\n" % name)
+ output.write("<style type=\"text/css\">\n<!--\n")
+ if style == "Paper":
+ output.write("html { background-color:#efefef; }")
+ output.write("body { background-color:#fafafa; color:#303030; margin:1em; padding:1em; border:#606060 1px solid; }")
+ elif style == "Blues":
+ output.write("html { background-color:#0000aa; }")
+ output.write("body { background-color:#000066; color:#efefff; margin:1em; padding:1em; border:#00f 1px solid; }")
+ output.write("h1 { color:#0000ff; }")
+ output.write("th { color:#0000aa; }")
+ else:
+ output.write("html { background-color:#ffffff; color:#000; }")
+ output.write("body { margin:1em; }")
+ output.write("\n//-->\n</style>\n")
+ output.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n")
+ output.write("</head><body><h1>%s</h1>\n" % name)
+
+ caption = self.datasource.caption()
+ if caption and caption != name:
+ output.write("caption: %s<br />\n" % caption)
+
+ description = self.datasource.description()
+ if description:
+ output.write("description: %s<br />\n" % description)
+
+ #import datetime
+ #output.write("date: %s<br />" % datetime.datetime.now())
+
+ output.write("<table border='1'>\n")
+
+ output.write("<tr>")
+ for h in self.datasource.header():
+ output.write("<th>%s</th>" % h)
+ output.write("</tr>")
+
+ while 1 == 1:
+ items = self.datasource.getNext()
+ if items == None: break
+ output.write("<tr>")
+ for item in items:
+ u = unicode(str(self.htmlescape(item)),"latin-1")
+ output.write("<td>%s</td>" % u.encode("utf-8"))
+ output.write("</tr>\n")
+ output.write("</table>\n")
+ output.write("</body></html>\n")
+
+class GuiApp:
+ def __init__(self, datasource):
+ self.datasource = datasource
+
+ try:
+ import gui
+ except:
+ raise "Import of the Kross GUI module failed."
+
+ self.dialog = gui.Dialog("Export XHTML")
+ self.dialog.addLabel(self.dialog, "Export a table- or query-datasource to a XHTML-file.")
+
+ datasourceitems = self.datasource.getSources()
+ self.datasourcelist = self.dialog.addList(self.dialog, "Datasource:", datasourceitems)
+
+ styleitems = ["Plain", "Paper", "Blues"]
+ self.stylelist = self.dialog.addList(self.dialog, "Style:", styleitems)
+
+ #queryframe = Tkinter.Frame(frame)
+ #queryframe.pack()
+ #Tkinter.Label(queryframe, text="Table or query to export:").pack(side=Tkinter.LEFT)
+ #self.querycontent = Tkinter.StringVar()
+ #self.query = apply(Tkinter.OptionMenu, (queryframe, self.querycontent) + tuple( self.datasource.getSources() ))
+ #self.query.pack(side=Tkinter.LEFT)
+
+ self.file = self.dialog.addFileChooser(self.dialog,
+ "File:",
+ gui.getHome() + "/kexidata.xhtml",
+ (('XHTML files', '*.xhtml'),('All files', '*')))
+
+ btnframe = self.dialog.addFrame(self.dialog)
+ self.dialog.addButton(btnframe, "Export", self.doExport)
+ self.dialog.addButton(btnframe, "Cancel", self.dialog.close)
+
+ self.dialog.show()
+
+ def doExport(self):
+ file = str( self.file.get() )
+ query = str( self.datasourcelist.get() )
+ print "Exporting '%s' to file '%s' ..." % (query,file)
+
+ if not self.datasource.setSource(query):
+ raise "Invalid datasource selected."
+ #return
+
+ style = str( self.stylelist.get() )
+
+ f = open(file, "w")
+ global HtmlExporter
+ exporter = HtmlExporter(self.datasource)
+ exporter.write(f, style)
+ f.close()
+
+ print "Successfully exported '%s' to file %s" % (query,file)
+ self.dialog.close()
+
+GuiApp( Datasource() )
diff --git a/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.rc b/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.rc
new file mode 100644
index 00000000..11c1dcdf
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.rc
@@ -0,0 +1,8 @@
+<KrossScripting>
+ <ScriptAction
+ name="exportxhtml"
+ text="Export Data to XHTML File"
+ icon="fileexport"
+ interpreter="python"
+ file="ExportXHTML.py" />
+</KrossScripting>
diff --git a/kexi/plugins/scripting/scripts/exportxhtml/Makefile.am b/kexi/plugins/scripting/scripts/exportxhtml/Makefile.am
new file mode 100644
index 00000000..1c7b9ca6
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/exportxhtml/Makefile.am
@@ -0,0 +1,4 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+scriptsdir = $(kde_datadir)/kexi/scripts/exportxhtml
+scripts_SCRIPTS = ExportXHTML.py ExportXHTML.rc
diff --git a/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.py b/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.py
new file mode 100755
index 00000000..200b3dee
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.py
@@ -0,0 +1,434 @@
+"""
+Import data from a XHTML file to a KexiDB table.
+
+Description:
+This script implements import of data from a XHTML file to a KexiDB table. The
+table needs to be an already existing table the data should be added to.
+
+Author:
+Sebastian Sauer <mail@dipe.org>
+
+Copyright:
+Dual-licensed under LGPL v2+higher and the BSD license.
+"""
+
+class SaxInput:
+ """ The inputsource we like to import the data from. This class
+ provides us abstract access to the SAX XML parser we use internaly
+ to import data from the XML-file. """
+
+ xmlfile = None
+ """ The XML file we should read the content from. """
+
+ def __init__(self):
+ """ Constructor. """
+
+ # try to import the xml.sax python module.
+ try:
+ import xml.sax.saxlib
+ import xml.sax.saxexts
+ except:
+ raise "Import of the python xml.sax.saxlib module failed. This module is needed by the ImportXHTML python script."
+
+ def read(self, outputwriter):
+ """ Start reading and parsing the XML-file. """
+
+ import xml.sax.saxlib
+ import xml.sax.saxexts
+
+ class SaxHandler(xml.sax.saxlib.HandlerBase):
+ """ The SaxHandler is our event-handler SAX calls on
+ parsing the XML-file. """
+
+ tablebase = ["html","body","table"]
+ """ The table-base defines where we will find our table-tag
+ that holds all the data we are interessted at. The default
+ is to look at <html><body><table></table></body></html>. """
+
+ def __init__(self, inputreader, outputwriter):
+ """ Constructor. """
+
+ # The to a SaxInput instance pointing inputreader.
+ self.inputreader = inputreader
+ # The to a KexiDBOutput instance pointing outputwriter.
+ self.outputwriter = outputwriter
+ # The hierachy-level in the DOM-tree we are in.
+ self.level = 0
+ # Defines if we are in the with tablebase defined DOM-element.
+ self.intable = False
+
+ # Points to a KexiDBOutput.Record instance if we are in a DOM-element that defines a record.
+ self.record = None
+ # Points to a KexiDBOutput.Field instance if we are in a record's field.
+ self.field = None
+
+ def startDocument(self):
+ sys.stdout.write('=> Starting parsing\n')
+
+ def endDocument(self):
+ sys.stdout.write('=> Fineshed parsing\n')
+
+ def startElement(self, name, attrs):
+ """ This method is called by SAX if a DOM-element starts. """
+
+ if self.level < len(self.tablebase):
+ if self.tablebase[self.level] != name:
+ self.intable = False
+ else:
+ self.intable = True
+ self.level += 1
+ if not self.intable:
+ return
+
+ # Print some debugging-output to stdout.
+ for idx in range(self.level): sys.stdout.write(' ')
+ sys.stdout.write('Element: %s' % name)
+ for attrName in attrs.keys():
+ sys.stdout.write(' %s="%s"' % (attrName,attrs.get(attrName)))
+ sys.stdout.write('\n')
+
+ # handle tr-, th- and td-tags inside the table.
+ if name == "tr" and (self.level == len(self.tablebase) + 1):
+ self.record = self.outputwriter.Record()
+ elif name == "td" and (self.level == len(self.tablebase) + 2):
+ self.field = self.outputwriter.Field()
+ elif name == "th" and (self.level == len(self.tablebase) + 2):
+ self.field = self.outputwriter.Field()
+
+ def endElement(self, name):
+ """ This method is called by SAX if a DOM-Element ends. """
+
+ self.level -= 1
+ #sys.stdout.write('EndElement:%s level:%s len(self.tablebase):%s\n' % (name,self.level,len(self.tablebase)))
+
+ if self.record != None:
+ # a record is defined. so, we are looking for the matching
+ # end-tags to close a record or a field.
+ if name == "tr" and (self.level == len(self.tablebase)):
+ self.outputwriter.write(self.record)
+ self.record = None
+ self.field = None
+ elif name == "td" and (self.level == len(self.tablebase) + 1):
+ #if self.field == None:
+ # raise "Unexpected closing </td>"
+ self.record.setField( self.field )
+ self.field = None
+ elif name == "th" and (self.level == len(self.tablebase) + 1):
+ #if self.field == None:
+ # raise "Unexpected closing </td>"
+ self.record.setHeader( self.field )
+ self.field = None
+
+ def characters(self, chars, offset, length):
+ """ This method is called by SAX if the text-content of a DOM-Element
+ was parsed. """
+
+ if self.field != None:
+ # the xml-data is unicode and we need to encode it
+ # to latin-1 cause KexiDB deals only with latin-1.
+ u = unicode(chars[offset:offset+length])
+ self.field.append(u.encode("latin-1"))
+
+ # start the job
+ outputwriter.begin()
+ # create saxhandler to handle parsing events.
+ handler = SaxHandler(self, outputwriter)
+ # we need a sax-parser and connect it with the handler.
+ parser = xml.sax.saxexts.make_parser()
+ parser.setDocumentHandler(handler)
+ # open the XML-file, parse the content and close the file again.
+ f = file(self.xmlfile, 'r')
+ parser.parseFile(f)
+ f.close()
+ # job is done
+ outputwriter.end()
+
+class KexiDBOutput:
+ """ The destination target we like to import the data to. This class
+ provides abstract access to the KexiDB module. """
+
+ class Result:
+ """ Holds some informations about the import-result. """
+ def __init__(self, outputwriter):
+ self.outputwriter = outputwriter
+ # number of records successfully imported.
+ self.successcount = 0
+ # number of records where import failed.
+ self.failedcount = 0
+
+ def addLog(self, record, state):
+ import datetime
+ date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M.%S")
+ self.outputwriter.logfile.write("%s (%s) %s\n" % (date,state,str(record)))
+
+ def success(self, record):
+ """ Called if a record was written successfully. """
+ print "SUCCESS: %s" % str(record)
+ self.successcount += 1
+ if hasattr(self.outputwriter,"logfile"):
+ self.addLog(record, "Success")
+
+ def failed(self, record):
+ """ Called if we failed to write a record. """
+ print "FAILED: %s" % str(record)
+ self.failedcount += 1
+ if hasattr(self.outputwriter,"logfile"):
+ self.addLog(record, "Failed")
+
+ class Record:
+ """ A Record in the dataset. """
+ def __init__(self):
+ self.fields = []
+ def setHeader(self, headerfield):
+ self.fields.append( headerfield )
+ self.isHeader = True
+ def setField(self, field):
+ self.fields.append( field )
+ def __str__(self):
+ s = "["
+ for f in self.fields:
+ s += "%s, " % str(f)
+ return s + "]"
+
+ class Field:
+ """ A field in a record. """
+ def __init__(self):
+ self.content = []
+ def append(self, content):
+ self.content.append( content )
+ def __str__(self):
+ return "".join(self.content)
+
+ def __init__(self):
+ """ Constructor. """
+ import kexiapp
+ keximainwindow = kexiapp.get("KexiAppMainWindow")
+
+ try:
+ self.connection = keximainwindow.getConnection()
+ except:
+ raise "No connection established. Please open a project before."
+
+ self.fieldlist = None
+ self.headerrecord = None
+ self.mapping = {}
+
+ def begin(self):
+ """ Called before parsing starts. """
+ print "START JOB"
+ if self.fieldlist == None:
+ raise "Invalid tableschema or fieldlist!"
+ global KexiDBOutput
+ self.result = KexiDBOutput.Result(self)
+ if hasattr(self,"logfilename") and self.logfilename != None and self.logfilename != "":
+ self.logfile = open(self.logfilename,'w')
+
+ def end(self):
+ """ Called if parsing is fineshed. """
+ print "END JOB"
+ self.logfile = None
+ self.mapping = {}
+ #self.headerrecord = None
+
+ def getTables(self):
+ """ return a list of avaiable tablenames. """
+ tables = self.connection.tableNames()
+ tables.sort()
+ return tables
+
+ def setTable(self, tablename):
+ """ Set the tablename we like to import the data to. """
+ tableschema = self.connection.tableSchema(tablename)
+ if tableschema == None:
+ raise "There exists no table with the name '%s'!" % tablename
+ self.fieldlist = tableschema.fieldlist()
+ fields = self.fieldlist.fields()
+ for field in fields:
+ print "KexiDBOutput.setTable(%s): %s(%s)" % (tablename,field.name(),field.type())
+ print "names=%s" % self.fieldlist.names()
+
+ def setMapping(self, mapping):
+ """ Set the tablefieldname=xmlcolnr dictonary we should map the data to. """
+ self.mapping = mapping
+
+ def setLogFile(self, logfilename):
+ """ Set the name of the logfile. """
+ self.logfilename = logfilename
+
+ def write(self, record):
+ """ Write the record to the KexiDB table. """
+
+ if hasattr(record, "isHeader"):
+ self.headerrecord = record
+ return
+
+ sys.stdout.write('KexiDBOutput.write:')
+ for f in record.fields:
+ sys.stdout.write(' "%s"' % f)
+ sys.stdout.write('\n')
+
+ if hasattr(self,"onWrite"):
+ if not self.onWrite(record):
+ raise RuntimeError()
+ delattr(self,"onWrite")
+ self.fieldlist = self.fieldlist.subList( list( self.mapping ) )
+
+ # Translate a KexiDBOutput.Record into a list of values.
+ values = []
+ for k in self.fieldlist.names():
+ values.append( str(record.fields[ int(self.mapping[k]) ]) )
+ print "Import values: %s" % values
+
+ try:
+ if self.connection.insertRecord(self.fieldlist, values):
+ self.result.success(record)
+ else:
+ self.result.failed(record)
+ except:
+ err = self.connection.lastError()
+ raise Exception( "Failed to insert the record:\n%s\n\n%s" % (values,err) )
+ #raise Exception( "Failed to insert into table \"%s\" the record:\n%s\n%s" % (self.tableschema.name(),values,self.connection.lastError()) )
+
+class GuiApp:
+ """ The GUI-dialog displayed to let the user define the source
+ XML-file and the destination KexiDB table. """
+
+ class InitialDialog:
+ def __init__(self, guiapp):
+ self.guiapp = guiapp
+ self.ok = False
+
+ import gui
+ self.dialog = gui.Dialog("Import XHTML")
+ self.dialog.addLabel(self.dialog, "Import data from a XHTML-file to a KexiDB table.\n"
+ "The destination table needs to be an existing table the data should be added to.")
+ self.importfile = self.dialog.addFileChooser(self.dialog,
+ "Source File:",
+ gui.getHome() + "/kexidata.xhtml",
+ (('XHTML files', '*.xhtml'),('All files', '*')))
+
+ self.desttable = self.dialog.addList(self.dialog, "Destination Table:", self.guiapp.outputwriter.getTables())
+
+ #self.operation = self.dialog.addList(self.dialog, "Operation:", ("Insert","Update","Insert/Update"))
+ #self.error = self.dialog.addList(self.dialog, "On error:", ("Ask","Skip","Abort"))
+
+ self.logfile = self.dialog.addFileChooser(self.dialog,
+ "Log File:",
+ "",
+ (('Logfiles', '*.log'),('All files', '*')))
+
+ btnframe = self.dialog.addFrame(self.dialog)
+ self.dialog.addButton(btnframe, "Next", self.doNext)
+ self.dialog.addButton(btnframe, "Cancel", self.doCancel)
+ self.dialog.show()
+
+ def doCancel(self):
+ """ Called if the Cancel-button was pressed. """
+ self.dialog.close()
+ self.dialog = None
+ #self.guiapp.InitialDialog
+
+ def doNext(self):
+ """ Start to import the XML-file into the KexiDB table. """
+
+ self.guiapp.inputreader.xmlfile = str(self.importfile.get())
+ self.guiapp.outputwriter.setTable( str(self.desttable.get()) )
+ self.guiapp.outputwriter.setLogFile( str(self.logfile.get()) )
+
+ try:
+ self.guiapp.inputreader.read( self.guiapp.outputwriter )
+
+ msgbox = self.dialog.showMessageBox("info","Import done",
+ "Successfully imported records: %s\nFailed to import records: %s" % (self.guiapp.outputwriter.result.successcount, self.guiapp.outputwriter.result.failedcount) )
+ msgbox.show()
+
+ self.doCancel()
+ except RuntimeError, e:
+ pass
+ #except Exception, e:
+ # import traceback
+ # traceback.print_exc()
+ # msgbox = self.dialog.showMessageBox("error", "Error", e)
+ # msgbox.show()
+
+ class MapperDialog:
+ """ The dialog that provides us a way to map
+ XHTML columns to the destination table. """
+
+ def __init__(self, outputwriter, record):
+ self.outputwriter = outputwriter
+ self.ok = False
+ fieldlist = outputwriter.fieldlist
+
+ import gui
+ self.dlg = gui.Dialog("Import XHTML")
+ self.dlg.addLabel(self.dlg, "Define how the destination table should be mapped to the data from the XHTML file.")
+ values = ["",]
+ for i in range(len(record.fields)):
+ try:
+ values.append( "%s: %s" % (i,str(outputwriter.headerrecord.fields[i])) )
+ except:
+ values.append( "%s: (%s)" % (i,str(record.fields[i])) )
+
+ self.items = []
+ i = 0
+ for field in fieldlist.fields():
+ f = self.dlg.addFrame(self.dlg)
+
+ l = self.dlg.addList(f, "%s:" % field.name(), values)
+ self.items.append( (field,l) )
+
+ details = "%s:" % str( field.type() )
+ if field.isAutoInc(): details += "autoinc,"
+ if field.isUniqueKey(): details += "unique,"
+ if field.isNotNull(): details += "notnull,"
+ if field.isNotEmpty(): details += "notempty,"
+ self.dlg.addLabel(f, "(%s)" % details[:-1])
+
+ try:
+ variable = str( record.fields[i] )
+ try:
+ int(variable)
+ i += 1
+ if not field.isAutoInc():
+ l.set(i)
+ except ValueError, e:
+ if not field.type() in ("Integer","BigInteger","ShortInteger","Float","Double"):
+ i += 1
+ l.set(i)
+ except:
+ pass
+
+ btnframe = self.dlg.addFrame(self.dlg)
+ self.dlg.addButton(btnframe, "Next", self.doNext)
+ self.dlg.addButton(btnframe, "Cancel", self.dlg.close)
+ self.dlg.show()
+
+ def doNext(self):
+ mapping = {}
+ for item in self.items:
+ (field,l) = item
+ fieldname = field.name()
+ colnr = str( l.get() ).split(":",1)[0]
+ if colnr.isdigit():
+ print "Table field '%s' is mapped to XML column '%s'" % (fieldname,colnr)
+ mapping[ fieldname ] = colnr
+ self.outputwriter.setMapping(mapping)
+ self.ok = True
+ self.dlg.close()
+
+ def __init__(self, inputreader, outputwriter):
+ """ Constructor. """
+
+ self.inputreader = inputreader
+ self.outputwriter = outputwriter
+ self.outputwriter.onWrite = self.onWrite
+
+ self.InitialDialog(self)
+
+ def onWrite(self, record):
+ """ This method got called after the first record got
+ readed and before we start to import. """
+ return self.MapperDialog(self.outputwriter, record).ok
+
+GuiApp( SaxInput(), KexiDBOutput() )
diff --git a/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.rc b/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.rc
new file mode 100644
index 00000000..0cfe7718
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.rc
@@ -0,0 +1,8 @@
+<KrossScripting>
+ <ScriptAction
+ name="importxhtml"
+ text="Import Data From XHTML File"
+ icon="fileimport"
+ interpreter="python"
+ file="ImportXHTML.py" />
+</KrossScripting>
diff --git a/kexi/plugins/scripting/scripts/importxhtml/Makefile.am b/kexi/plugins/scripting/scripts/importxhtml/Makefile.am
new file mode 100644
index 00000000..a0a424fa
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/importxhtml/Makefile.am
@@ -0,0 +1,4 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+scriptsdir = $(kde_datadir)/kexi/scripts/importxhtml
+scripts_SCRIPTS = ImportXHTML.py ImportXHTML.rc
diff --git a/kexi/plugins/scripting/scripts/projectdocumentor/Makefile.am b/kexi/plugins/scripting/scripts/projectdocumentor/Makefile.am
new file mode 100644
index 00000000..9d32e165
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/projectdocumentor/Makefile.am
@@ -0,0 +1,4 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+scriptsdir = $(kde_datadir)/kexi/scripts/projectdocumentor
+scripts_SCRIPTS = ProjectDocumentor.py ProjectDocumentor.rc
diff --git a/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.py b/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.py
new file mode 100755
index 00000000..89a60301
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.py
@@ -0,0 +1,186 @@
+"""
+Project Documentor
+
+Description:
+This script collects various informations about a Kexi project
+and exports them to a HTML file.
+
+Author:
+Sebastian Sauer <mail@dipe.org>
+
+Copyright:
+Dual-licensed under LGPL v2+higher and the BSD license.
+"""
+
+class DataProvider:
+ def __init__(self):
+ import kexiapp
+ keximainwindow = kexiapp.get("KexiAppMainWindow")
+
+ try:
+ self.connection = keximainwindow.getConnection()
+ except:
+ raise "No connection established. Please open the project to be documented first."
+
+ def printConnection(self):
+ condata = self.connection.data()
+ infos = []
+ for item in ("caption", "description", "driverName", "hostName", "port", "userName", "fileName", "dbPath", "localSocketFileName", "serverInfoString"):
+ result = getattr(condata, item)()
+ if result != None and result != "" and (item != "port" or result != 0):
+ infos.append( (item, result) )
+ return infos
+
+ def printDriver(self):
+ driver = self.connection.driver()
+ result = [ ("Version", "%s.%s" % (driver.versionMajor(),driver.versionMinor())) ]
+ conlist = driver.connectionsList()
+ if len(conlist) > 0:
+ result.append( ("Connections",str(conlist)) )
+ return result
+
+ def printDatabases(self):
+ result = [ ("Current database", self.connection.currentDatabase()) ]
+ dbnames = self.connection.databaseNames()
+ if len(dbnames) > 0:
+ result.append( ("Databases",str(dbnames)) )
+ return result
+
+ def printTables(self):
+ result = []
+ for t in self.connection.tableNames():
+ tableschema = self.connection.tableSchema(t)
+ ti = []
+ for i in ("name", "caption", "description"):
+ v = getattr(tableschema,i)()
+ if v != None and v != "":
+ ti.append( (i,v) )
+ tf = []
+ for field in tableschema.fieldlist().fields():
+ tfi = []
+ for n in ("caption","description","type","subType","typeGroup","length","defaultValue"):
+ v = getattr(field,n)()
+ if v != None and v != "":
+ tfi.append( (n,v) )
+ props = []
+ for n in ("PrimaryKey","ForeignKey","AutoInc","UniqueKey","NotNull", "NotEmpty","Indexed","Unsigned"):
+ v = getattr(field,"is%s" % n)()
+ if v != None and v != "" and v != False and v != 0:
+ props.append( "%s " % n )
+ if len(props) > 0:
+ tfi.append( ("properties",props) )
+
+ tf.append( (field.name(), tfi) )
+ ti.append( ("fields", tf) )
+ if len(ti) > 0:
+ result.append( (t, ti) )
+ return result
+
+ def printQueries(self):
+ result = []
+ for q in self.connection.queryNames():
+ queryschema = self.connection.querySchema(q)
+ qi = []
+ for i in ("name", "caption", "description", "statement"):
+ v = getattr(queryschema,i)()
+ if v != None and v != "":
+ qi.append( (i,v) )
+ if len(qi) > 0:
+ result.append( (q, qi) )
+ return result
+
+class GuiApp:
+ def __init__(self, dataprovider):
+ self.dataprovider = dataprovider
+
+ try:
+ import gui
+ except:
+ raise "Import of the Kross GUI module failed."
+
+ self.dialog = gui.Dialog("Project Documentor")
+
+ self.dialog.addLabel(self.dialog, "Save information about the project to an HTML file.")
+
+ self.file = self.dialog.addFileChooser(self.dialog,
+ "File:",
+ gui.getHome() + "/projectdoc.html",
+ (('HTML files', '*.html'),('All files', '*')))
+
+ self.printCheckBoxes = {}
+ for d in dir(self.dataprovider):
+ if d.startswith("print"):
+ self.printCheckBoxes[d] = self.dialog.addCheckBox(self.dialog, d[5:], True)
+
+ #value = getattr(self.dataprovider,d)()
+ #if value != None and len(value) > 0:
+ # f.write("<h2>%s</h2>" % d[5:])
+ # f.write( self.toHTML(value) )
+
+ #self.exportProjectdetails =
+ #self.exportTableschemas = self.dialog.addCheckBox(self.dialog, "Table schemas", True)
+ #self.exportQueryschemas = self.dialog.addCheckBox(self.dialog, "Query schemas", True)
+
+ btnframe = self.dialog.addFrame(self.dialog)
+ self.dialog.addButton(btnframe, "Save", self.doSave)
+ self.dialog.addButton(btnframe, "Cancel", self.dialog.close)
+
+ self.dialog.show()
+
+ def toHTML(self, value):
+ import types
+ result = ""
+ if isinstance(value, types.TupleType):
+ result += "<ul>"
+ if len(value) == 1:
+ result += "<li>%s</li>" % value
+ elif len(value) == 2:
+ result += "<li>%s: %s</li>" % (value[0], self.toHTML(value[1]))
+ elif len(value) > 2:
+ for item in value:
+ i = self.toHTML(item)
+ if i != "":
+ result += "<li>%s</li>" % i
+ result += "</ul>"
+ elif isinstance(value, types.ListType):
+ for item in value:
+ result += "%s" % self.toHTML(item)
+ else:
+ result += "%s" % value
+ return result
+
+ def doSave(self):
+ file = str( self.file.get() )
+ print "Attempting to save project documentation to file: %s" % file
+
+ f = open(file, "w")
+
+ f.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>")
+ f.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 4.01 Strict//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11-strict.dtd\">")
+ f.write("<html><head><title>Project information</title>")
+ f.write("<style type=\"text/css\">")
+ f.write(" html { background-color:#fafafa; }")
+ f.write(" body { background-color:#ffffff; margin:1em; padding:1em; border:#99a 1px solid; color:#003; }")
+ f.write("</style>")
+ f.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />")
+ f.write("</head><body><h1>Project information</h1>")
+
+ for d in dir(self.dataprovider):
+ if d.startswith("print"):
+ print "GuiApp.doSave() CHECK %s" % d
+ a = self.printCheckBoxes[d]
+ if a and a.isChecked():
+ print "GuiApp.doSave() BEGIN %s" % d
+ value = getattr(self.dataprovider,d)()
+ if value != None and len(value) > 0:
+ f.write("<h2>%s</h2>" % d[5:])
+ f.write( self.toHTML(value) )
+ print "GuiApp.doSave() END %s" % d
+
+ f.close()
+
+ print "Successfully saved project documentation to file: %s" % file
+ self.dialog.close()
+
+GuiApp( DataProvider() )
+
diff --git a/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.rc b/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.rc
new file mode 100644
index 00000000..bb0f6c69
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.rc
@@ -0,0 +1,8 @@
+<KrossScripting>
+ <ScriptAction
+ name="projectdocumentor"
+ text="Project Documentation Generator"
+ icon="contents"
+ interpreter="python"
+ file="ProjectDocumentor.py" />
+</KrossScripting>
diff --git a/kexi/plugins/scripting/scripts/python/Makefile.am b/kexi/plugins/scripting/scripts/python/Makefile.am
new file mode 100644
index 00000000..4b31c35a
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/python/Makefile.am
@@ -0,0 +1,2 @@
+include $(top_srcdir)/kexi/Makefile.global
+SUBDIRS = kexiapp
diff --git a/kexi/plugins/scripting/scripts/python/kexiapp/Makefile.am b/kexi/plugins/scripting/scripts/python/kexiapp/Makefile.am
new file mode 100644
index 00000000..f0f0492d
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/python/kexiapp/Makefile.am
@@ -0,0 +1,2 @@
+kexiapppythondir = $(kde_datadir)/kexi/kross/python/kexiapp
+kexiapppython_SCRIPTS = __init__.py
diff --git a/kexi/plugins/scripting/scripts/python/kexiapp/__init__.py b/kexi/plugins/scripting/scripts/python/kexiapp/__init__.py
new file mode 100755
index 00000000..b5224304
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/python/kexiapp/__init__.py
@@ -0,0 +1,25 @@
+"""
+Initializer for the krosskexiapp-module.
+
+Description:
+This module provides the entry-point for python scripts
+to work with a running Kexi application instance.
+
+Author:
+Sebastian Sauer <mail@dipe.org>
+
+Copyright:
+Dual-licensed under LGPL v2+higher and the BSD license.
+"""
+
+try:
+ import krosskexiapp
+except ImportError, e:
+ raise "Import of the Kross KexiApp module failed.\n%s" % e
+
+def get(modulename):
+ return krosskexiapp.get(modulename)
+
+def currentConnection():
+ mainwindow = krosskexiapp.get("KexiAppMainWindow")
+ return mainwindow.getConnection()