class ExploreServerWidget(QtGui.QWidget, FORM_CLASS):
    abstractDbLoaded = pyqtSignal()
    clearWidgets = pyqtSignal()
    def __init__(self, parent = None):
        """Constructor."""
        super(self.__class__, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        self.dbFactory = DbFactory()

    #TODO
    def getServers(self):
        settings = QSettings()
        settings.beginGroup('PostgreSQL/servers')
        currentConnections = settings.childGroups()
        settings.endGroup()
        return currentConnections
    
    #TODO
    def browseServer(self,dbList,host,port,user,password):
        gen = self.factory.createSqlGenerator(False)
        edvgDbList = []
        for database in dbList:
            db = self.getPostGISDatabaseWithParams(database, host, port, user, password)
            if not db.open():
                qgis.utils.iface.messageBar().pushMessage('DB :'+database+'| msg: '+db.lastError().databaseText(), level=QgsMessageBar.CRITICAL)

            query = QSqlQuery(db)
            if query.exec_(gen.getEDGVVersion()):
                while query.next():
                    version = query.value(0)
                    if version:
                        edvgDbList.append((database, version))
        return edvgDbList
    
    #TODO
    def getDbsFromServer(self,name):
        gen = self.factory.createSqlGenerator(False)
        
        (host, port, user, password) = self.getServerConfiguration(name)
        database = 'postgres'
        
        db = self.getPostGISDatabaseWithParams(database, host, port, user, password)
        if not db.open():
            QgsMessageLog.logMessage(db.lastError().text(), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
        
        query = QSqlQuery(gen.getDatabasesFromServer(), db)
        dbList = []
        while query.next():
            dbList.append(query.value(0))
        return self.browseServer(dbList, host, port, user, password)
    
    @pyqtSlot(bool)
    def on_createNewServerPushButton_clicked(self):  
        createNewServer = ViewServers(self)
        createNewServer.exec_()
        self.populateServersCombo()

    def populateServersCombo(self):
        self.serversCombo.clear()
        self.serversCombo.addItem(self.tr('Select Server'))
        currentConnections = self.getServers()
        for connection in currentConnections:
            self.serversCombo.addItem(connection)
    
    @pyqtSlot(int)
    def on_serversCombo_currentIndexChanged(self):
        self.clearWidgets.emit()
        if self.serversCombo.currentIndex() != 0:
            self.abstractDb = self.dbFactory.createDbFactory('QPSQL')
            (host, port, user, password) = self.abstractDb.getServerConfiguration(self.serversCombo.currentText())
            self.abstractDb.connectDatabaseWithParameters(host, port, 'postgres', user, password)
            self.abstractDbLoaded.emit()
Beispiel #2
0
class ViewServers(QtGui.QDialog, FORM_CLASS):
    def __init__(self, iface, parent=None):
        '''Constructor.'''
        super(ViewServers, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        self.iface = iface
        self.abstractDbFactory = DbFactory()
        self.initGui()
    
    def initGui(self):
        header = self.tableWidget.horizontalHeader()
        header.setResizeMode(QHeaderView.Stretch)
        self.populateTable()
        
    def populateTable(self):
        currentConnections = self.getServers()
        self.tableWidget.setRowCount(len(currentConnections))
        for i, connection in enumerate(currentConnections):
            self.tableWidget.setItem(i, 0, QTableWidgetItem(connection))
            (host, port, user, password) = self.getServerConfiguration(connection)
            self.tableWidget.setItem(i, 1, QTableWidgetItem(host))
            self.tableWidget.setItem(i, 2, QTableWidgetItem(port))
            self.tableWidget.setItem(i, 3, QTableWidgetItem(user))
            if not password or len(password) == 0:
                self.tableWidget.setItem(i, 4, QTableWidgetItem(self.tr('Not Saved')))
            else:
                self.tableWidget.setItem(i, 4, QTableWidgetItem(self.tr('Saved')))
        
    @pyqtSlot(bool)
    def on_cancelButton_clicked(self):
        self.done(0)
        
    @pyqtSlot(bool)
    def on_addButton_clicked(self):
        dlg = ServerConfigurator(self)
        result = dlg.exec_()
        if result:
            self.populateTable()
            
    @pyqtSlot(bool)
    def on_editButton_clicked(self):
        selectedItem = self.returnSelectedName()
        if not selectedItem:
            return
        dlg = ServerConfigurator(self)
        dlg.setServerConfiguration(selectedItem.text())
        result = dlg.exec_()
        if result:
            self.populateTable()
        
    @pyqtSlot(bool)
    def on_removeButton_clicked(self):
        selectedItem = self.returnSelectedName()
        if not selectedItem:
            return
        self.removeServerConfiguration(selectedItem.text())
        self.tableWidget.removeRow(selectedItem.row())
        QMessageBox.warning(self, self.tr('Info!'), self.tr('Server removed.'))
        
    @pyqtSlot(bool)
    def on_testButton_clicked(self):
        selectedItem = self.returnSelectedName()
        if not selectedItem:
            return
        name = selectedItem.text()
        try:
            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
            test = self.testServer(name)
            QApplication.restoreOverrideCursor()
        except:
            QApplication.restoreOverrideCursor()
        if test:
            QMessageBox.warning(self, self.tr('Info!'), self.tr('Connection online.'))
        else:
            QMessageBox.warning(self, self.tr('Info!'), self.tr('Connection was not successful. Check log for details.'))
        
    def getServers(self):
        settings = QSettings()
        settings.beginGroup('PostgreSQL/servers')
        currentConnections = settings.childGroups()
        settings.endGroup()
        return currentConnections
    
    def getServerConfiguration(self, name):
        settings = QSettings()
        settings.beginGroup('PostgreSQL/servers/'+name)
        host = settings.value('host')
        port = settings.value('port')
        user = settings.value('username')
        password = settings.value('password')
        settings.endGroup()
        
        return (host, port, user, password)
    
    def removeServerConfiguration(self, name):
        settings = QSettings()
        settings.beginGroup('PostgreSQL/servers/'+name)
        settings.remove('')
        settings.endGroup()
        
    def testServer(self, name):
        abstractDb = self.abstractDbFactory.createDbFactory('QPSQL')
        if not abstractDb:
            return False
        (host, port, user, password) = abstractDb.getServerConfiguration(name)
        abstractDb.connectDatabaseWithParameters(host, port, 'postgres', user, password)
        try:
            abstractDb.checkAndOpenDb()
        except Exception as e:
            QgsMessageLog.logMessage(e.args[0], 'DSG Tools Plugin', QgsMessageLog.CRITICAL)
            return False
        return True
    
    def returnSelectedName(self):
        if len(self.tableWidget.selectedItems()) == 0:
            QMessageBox.warning(self, self.tr('Warning!'), self.tr('Select one server.'))
            return
        return self.tableWidget.selectedItems()[0]
Beispiel #3
0
class BatchDbManager(QtGui.QDialog, FORM_CLASS):
    EDGV213, EDGV_FTer_2a_Ed, Non_EDGV = range(3)
    def __init__(self, parent = None):
        """Constructor."""
        super(self.__class__, self).__init__(parent)
        self.setupUi(self)
        self.utils = Utils()
        self.dbFactory = DbFactory()
        self.factory = SqlGeneratorFactory()
        self.showTabs(show = False)
        #setting the sql generator
        self.serverWidget.populateServersCombo()
        self.serverWidget.abstractDbLoaded.connect(self.checkSuperUser)
        self.serverWidget.abstractDbLoaded.connect(self.populateOtherInterfaces)
        self.dbsCustomSelector.setTitle(self.tr('Server Databases'))
        self.dbsCustomSelector.selectionChanged.connect(self.showTabs)
        self.dbsCustomSelector.selectionChanged.connect(self.populateStylesInterface)
        self.dbsCustomSelector.selectionChanged.connect(self.populateOtherInterfaces)
        self.previousTab = 0
        self.dbDict = {'2.1.3':[], 'FTer_2a_Ed':[],'Non_EDGV':[], '3.0':[]}
        self.correspondenceDict = {self.tr('Load Database Model EDGV Version 2.1.3'):'2.1.3', self.tr('Load Database Model EDGV Version 3.0'):'3.0', self.tr('Load Database Model EDGV Version FTer_2a_Ed'):'FTer_2a_Ed',self.tr('Load Other Database Models'):'Non_EDGV'}

    @pyqtSlot(bool)
    def on_closePushButton_clicked(self):
        self.done(0)
    
    def showTabs(self, show = True):
        if show:
            self.tabWidget.show()
        else:
            self.tabWidget.hide()

    def populateListWithDatabasesFromServer(self):
        try:
            dbList = self.serverWidget.abstractDb.getEDGVDbsFromServer(parentWidget = self)
        except Exception as e:
            QMessageBox.critical(self, self.tr('Critical!'), ':'.join(e.args))

        dbList.sort()
        for (dbname, dbversion) in dbList:
            if dbversion not in self.dbDict.keys():
                dbversion = 'Non_EDGV'
            if dbname not in self.dbDict[dbversion]:
                self.dbDict[dbversion].append(dbname)

    def setDatabases(self):
        self.populateListWithDatabasesFromServer()
    
    @pyqtSlot(int)
    def on_edgvComboFilter_currentIndexChanged(self, idx):
        if idx != -1 and idx != 0:
            self.dbsCustomSelector.setInitialState(self.dbDict[self.correspondenceDict[self.edgvComboFilter.currentText()]])

    def checkSuperUser(self):
        try:
            if self.serverWidget.abstractDb.checkSuperUser():
                self.setDatabases()
            else:
                QMessageBox.warning(self, self.tr('Info!'), self.tr('Connection refused. Connect with a super user to inspect server.'))
        except Exception as e:
            QMessageBox.critical(self, self.tr('Critical!'), ':'.join(e.args))

    def getSelectedDbList(self):
        return self.dbsCustomSelector.toLs
    
    def instantiateAbstractDbs(self, instantiateTemplates = False):
        dbsDict = dict()
        selectedDbNameList = self.getSelectedDbList()
        if instantiateTemplates:
            for templateName in ['template_edgv_213', 'template_edgv_fter_2a_ed', 'template_edgv_3']:
                if templateName not in selectedDbNameList:
                    if templateName != 'dsgtools_admindb':
                        selectedDbNameList.append(templateName)
        for dbName in selectedDbNameList:
            localDb = self.dbFactory.createDbFactory('QPSQL')
            localDb.connectDatabaseWithParameters(self.serverWidget.abstractDb.db.hostName(), self.serverWidget.abstractDb.db.port(), dbName, self.serverWidget.abstractDb.db.userName(), self.serverWidget.abstractDb.db.password())
            dbsDict[dbName] = localDb
        return dbsDict

    def closeAbstractDbs(self, dbsDict):
        exceptionDict = dict()
        for dbName in dbsDict.keys():
            try:
                dbsDict[dbName].db.close()
            except Exception as e:
                exceptionDict[dbName] =  ':'.join(e.args)
        return exceptionDict

    def outputMessage(self, header, successList, exceptionDict):
        msg = header
        if len(successList) > 0:
            msg += self.tr('\nSuccessful databases: ')
            msg +=', '.join(successList)
        msg += self.logInternalError(exceptionDict)
        QMessageBox.warning(self, self.tr('Operation Complete!'), msg)
    
    def logInternalError(self, exceptionDict):
        msg = ''
        errorDbList = exceptionDict.keys()
        if len(errorDbList)> 0:
            msg += self.tr('\nDatabases with error:')
            msg+= ', '.join(errorDbList)
            msg+= self.tr('\nError messages for each database were output in qgis log.')
            for errorDb in errorDbList:
                QgsMessageLog.logMessage(self.tr('Error for database ')+ errorDb + ': ' +exceptionDict[errorDb].decode('utf-8'), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
        return msg 

    @pyqtSlot(bool)
    def on_dropDatabasePushButton_clicked(self):
        selectedDbNameList = self.getSelectedDbList()
        if len(selectedDbNameList) == 0:
            QMessageBox.warning(self, self.tr('Warning'), self.tr('Please select one or more databases to drop!'))
        if QtGui.QMessageBox.question(self, self.tr('Question'), self.tr('Do you really want to drop databases: ')+', '.join(selectedDbNameList), QtGui.QMessageBox.Ok|QtGui.QMessageBox.Cancel) == QtGui.QMessageBox.Cancel:
            return
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        successList, exceptionDict = self.batchDropDbs(selectedDbNameList)
        QApplication.restoreOverrideCursor()
        self.setDatabases()
        header = self.tr('Drop operation complete. \n')
        self.outputMessage(header, successList, exceptionDict)
        self.dbsCustomSelector.setInitialState(self.dbsCustomSelector.fromLs)

    @pyqtSlot(bool)
    def on_upgradePostgisPushButton_clicked(self):
        selectedDbNameList = self.getSelectedDbList()
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        successList, exceptionDict = self.batchUpgradePostgis(selectedDbNameList)
        QApplication.restoreOverrideCursor()
        self.setDatabases()
        header = self.tr('Upgrade Posgtis operation complete. \n')
        self.outputMessage(header, successList, exceptionDict)

    def batchUpgradePostgis(self, dbList):
        exceptionDict = dict()
        successList = []
        dbsDict = self.instantiateAbstractDbs(instantiateTemplates = True)
        self.closeAbstractDbs(dbsDict)
        for dbName in dbsDict.keys():
            try:
                if self.serverWidget.abstractDb.checkIfTemplate(dbName):
                    self.serverWidget.abstractDb.setDbAsTemplate(dbName = dbName, setTemplate = False)
                    dbsDict[dbName].upgradePostgis()
                    self.serverWidget.abstractDb.setDbAsTemplate(dbName = dbName, setTemplate = True)
                    successList.append(dbName)
                else:
                    dbsDict[dbName].upgradePostgis()
                    successList.append(dbName)
            except Exception as e:
                exceptionDict[dbName] =  ':'.join(e.args)
        return successList, exceptionDict

    def batchDropDbs(self, dbList):
        exceptionDict = dict()
        successList = []
        dbsDict = self.instantiateAbstractDbs()
        self.closeAbstractDbs(dbsDict)
        for dbName in dbList:
            try:
                self.serverWidget.abstractDb.dropDatabase(dbName)
                successList.append(dbName)
            except Exception as e:
                exceptionDict[dbName] =  ':'.join(e.args)
        return successList, exceptionDict
    
    @pyqtSlot(bool)
    def on_importStylesPushButton_clicked(self):
        dbsDict = self.instantiateAbstractDbs()
        exceptionDict = dict()
        versionList = []
        
        for dbName in dbsDict.keys():
            try:
                version = dbsDict[dbName].getDatabaseVersion()
                if version not in versionList:
                    versionList.append(version)
            except Exception as e:
                exceptionDict[dbName] = ':'.join(e.args)
        if len(exceptionDict.keys())>0:
            self.logInternalError(exceptionDict)
        if len(versionList) > 1:
            QMessageBox.warning(self, self.tr('Warning'), self.tr('Multiple edgv versions are not allowed!'))
            return
        styleDir = self.getStyleDir(versionList)
        styleList = self.getStyleList(styleDir)
        dlg = SelectStyles(styleList)
        dlg.exec_()
        selectedStyles = dlg.selectedStyles
        if not selectedStyles:
            return
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        successList, exceptionDict = self.batchImportStyles(dbsDict, styleDir, selectedStyles, versionList[0])
        QApplication.restoreOverrideCursor()
        header = self.tr('Import operation complete. \n')
        self.outputMessage(header, successList, exceptionDict)
        self.populateStylesInterface()
        closeExceptionDict = self.closeAbstractDbs(dbsDict)
        self.logInternalError(closeExceptionDict)            
            
    
    def getStyleList(self, styleDir):
        #TODO: Reimplement
        styleList = []
        version = None
        if os.path.basename(styleDir) in ['edgv_213','edgv_FTer_2a_Ed', 'edgv_3']:
            version = os.path.basename(styleDir)
        else:
            parentFolder = os.path.dirname(styleDir)
            version = os.path.basename(parentFolder)
        for style in os.walk(styleDir).next()[1]:
            styleList.append('/'.join([version,style]))
        if len(styleList) == 0:
            styleList = [version+'/'+os.path.basename(styleDir)]
        return styleList
    
    def batchImportStyles(self, dbsDict, styleDir, styleList, version):
        exceptionDict = dict()
        successList = []
        for dbName in dbsDict.keys():
            for style in styleList:
                currentStyleFilesDir = "{0}/{1}".format(styleDir, style.split("/")[1])
                fileList = os.listdir(currentStyleFilesDir)
                # iterate over the list of files and check if there are non-QML files
                onlyQml = bool(sum([int(".qml" in file.lower()) for file in fileList]))
                try:
                    if not onlyQml:
                        raise Exception(self.tr("There are non-QML files in directory {0}.").format(currentStyleFilesDir))
                    dbsDict[dbName].importStylesIntoDb(style)
                    successList.append(dbName)
                except Exception as e:
                    errors = []
                    for arg in e.args:
                        if isinstance(arg, basestring):
                            s = '{}'.format(arg.encode('utf-8'))
                        else:
                            s = str(arg)
                        errors.append(s)
                    exceptionDict[dbName] =  ':'.join(errors)
        return successList, exceptionDict
    
    def getStyleDir(self, versionList):
        currentPath = os.path.join(os.path.dirname(__file__),'..','Styles', self.serverWidget.abstractDb.versionFolderDict[versionList[0]])
        return currentPath
    
    def getStylesFromDbs(self, perspective = 'style'):
        '''
        Returns a dict of styles in a form acording to perspective:
            if perspective = 'style'    : [styleName][dbName][tableName] = timestamp
            if perspective = 'database' : [dbName][styleName][tableName] = timestamp 
        '''
        dbsDict = self.instantiateAbstractDbs()
        allStylesDict = dict()
        exceptionDict = dict()
        for dbName in dbsDict.keys():
            try:
                newDict =dbsDict[dbName].getAllStylesDict(perspective)
                allStylesDict = self.utils.mergeDict(newDict, allStylesDict)
            except Exception as e:
                exceptionDict[dbName] = ':'.join(e.args)
        if len(exceptionDict.keys())>0:
            self.logInternalError(exceptionDict)
        return allStylesDict

    def createItem(self, parent, text, column):
        item = QtGui.QTreeWidgetItem(parent)
        item.setText(column, text)
        return item

    def populateStylesInterface(self):
        self.stylesTreeWidget.clear()
        allStylesDict = self.getStylesFromDbs()
        rootNode = self.stylesTreeWidget.invisibleRootItem()
        for styleName in allStylesDict.keys():
            parentStyleItem = self.createItem(rootNode, styleName, 0)
            dbList = allStylesDict[styleName].keys()
            parentTimeList = []
            for dbName in dbList:
                dbItem = self.createItem(parentStyleItem, dbName, 1)
                tableList = allStylesDict[styleName][dbName].keys()
                tableList.sort()
                timeList = []
                for table in tableList:
                    tableItem = self.createItem(dbItem, table, 2)
                    timeStamp = allStylesDict[styleName][dbName][table].toString()
                    timeList.append(timeStamp)
                    tableItem.setText(3,allStylesDict[styleName][dbName][table].toString())
                parentTimeList.append(max(timeList))
                dbItem.setText(3,max(timeList))
    
    @pyqtSlot(bool)
    def on_deleteStyles_clicked(self):
        dbsDict = self.instantiateAbstractDbs()
        styleDict = self.getStylesFromDbs()
        styleList = styleDict.keys()
        dlg = SelectStyles(styleList)
        dlg.exec_()
        selectedStyles = dlg.selectedStyles
        if not selectedStyles:
            return
        else:
            removeStyleDict = { style : styleDict[style] for style in selectedStyles }
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        successList, exceptionDict = self.batchDeleteStyles(dbsDict, removeStyleDict)
        QApplication.restoreOverrideCursor()
        header = self.tr('Delete operation complete. \n')
        self.outputMessage(header, successList, exceptionDict)
        self.populateStylesInterface()
        closeExceptionDict = self.closeAbstractDbs(dbsDict)
        self.logInternalError(closeExceptionDict)       
    
    def batchDeleteStyles(self, dbsDict, styleDict):
        exceptionDict = dict()
        successList = []
        for style in styleDict.keys():
            for dbName in styleDict[style].keys():
                try:
                    dbsDict[dbName].deleteStyle(style)
                    successList.append(dbName)
                except Exception as e:
                    exceptionDict[dbName] =  ':'.join(e.args)
        return successList, exceptionDict
    
    def getSQLFile(self):
        fd = QFileDialog()
        filename = fd.getOpenFileName(caption=self.tr('Select a SQL file'),filter=self.tr('sql file (*.sql)'))
        return filename
    
    @pyqtSlot(bool)
    def on_customizeFromSQLFilePushButton_clicked(self):
        dbsDict = self.instantiateAbstractDbs()
        sqlFilePath = self.getSQLFile()
        if sqlFilePath == '':
            return
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        successList, exceptionDict = self.batchCustomizeFromSQLFile(dbsDict, sqlFilePath)
        QApplication.restoreOverrideCursor()
        header = self.tr('Customize from SQL file operation complete. \n')
        self.outputMessage(header, successList, exceptionDict)
        closeExceptionDict = self.closeAbstractDbs(dbsDict)
        self.logInternalError(closeExceptionDict)
    
    def batchCustomizeFromSQLFile(self, dbsDict, sqlFilePath):
        exceptionDict = dict()
        successList = []
        for dbName in dbsDict.keys():
            try:
                dbsDict[dbName].runSqlFromFile(sqlFilePath)
                successList.append(dbName)
            except Exception as e:
                exceptionDict[dbName] =  ':'.join(e.args)
        return successList, exceptionDict

    def populateOtherInterfaces(self):
        dbsDict = self.instantiateAbstractDbs()
        if self.edgvComboFilter.currentIndex() != 0:
            edgvVersion = self.correspondenceDict[self.edgvComboFilter.currentText()]
            self.permissionWidget.setParameters(self.serverWidget.abstractDb, dbsDict, edgvVersion)
            # self.customizationManagerWidget.setParameters(self.serverWidget.abstractDb, edgvVersion, dbsDict = dbsDict)
            self.fieldToolBoxConfigManagerWidget.setParameters(self.serverWidget.abstractDb, edgvVersion, dbsDict = dbsDict)
            self.earthCoverageManagerWidget.setParameters(self.serverWidget.abstractDb, edgvVersion, dbsDict = dbsDict)
Beispiel #4
0
class ConnectionWidget(QtGui.QWidget, FORM_CLASS):
    connectionChanged = pyqtSignal()
    problemOccurred = pyqtSignal(str)
    
    def __init__(self, parent = None):
        """Constructor."""
        super(ConnectionWidget, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        
        self.setInitialState()
         
    def __del__(self):
        self.closeDatabase()

    def closeDatabase(self):
        if self.abstractDb:
            del self.abstractDb
            self.abstractDb = None

    def setInitialState(self):
        self.filename = ''
        self.dbLoaded = False
        self.epsg = 0
        self.crs = None
        
        self.abstractDb = None
        self.isSpatialite = True
        self.tabWidget.setCurrentIndex(0)
        self.abstractDbFactory = DbFactory()
        self.utils = Utils()

        #populating the postgis combobox
        self.comboBoxPostgis.setCurrentIndex(0)
        self.populatePostGISConnectionsCombo()
        self.spatialiteFileEdit.setReadOnly(True)   
        self.postGISCrsEdit.setReadOnly(True)
        self.spatialiteCrsEdit.setReadOnly(True)   
        self.edgvSpatialiteVersionEdit.setReadOnly(True)
        self.edgvPostgisVersionEdit.setReadOnly(True)       

    @pyqtSlot(int)
    def on_comboBoxPostgis_currentIndexChanged(self):
        if self.comboBoxPostgis.currentIndex() > 0:
            self.postGISCrsEdit.setText('')
            self.postGISCrsEdit.setReadOnly(True)
            self.edgvPostgisVersionEdit.setText('')
            self.edgvPostgisVersionEdit.setReadOnly(True)  
            self.loadDatabase()
            self.connectionChanged.emit()
        
    @pyqtSlot(bool)
    def on_pushButtonOpenFile_clicked(self):  
        self.loadDatabase()
        if self.isDBConnected():
            self.connectionChanged.emit()
        
    @pyqtSlot(int)
    def on_tabWidget_currentChanged(self):
        self.filename = ''
        self.comboBoxPostgis.setCurrentIndex(0)
        self.dbLoaded = False
        self.epsg = 0
        self.crs = None
        self.dbVersion = ''
        
        self.spatialiteFileEdit.setReadOnly(True)
        self.spatialiteFileEdit.setText(self.filename)
        self.postGISCrsEdit.setText('')
        self.postGISCrsEdit.setReadOnly(True)
        self.spatialiteCrsEdit.setText('')
        self.spatialiteCrsEdit.setReadOnly(True)   
        self.edgvSpatialiteVersionEdit.setText('')
        self.edgvSpatialiteVersionEdit.setReadOnly(True)
        self.edgvPostgisVersionEdit.setText('')
        self.edgvPostgisVersionEdit.setReadOnly(True)     
        
        
        #Setting the database type
        if self.tabWidget.currentIndex() == 0:
            self.isSpatialite = True
        else:
            self.isSpatialite = False

    def loadDatabase(self):
        self.closeDatabase()
        if self.isSpatialite:
            self.abstractDb = self.abstractDbFactory.createDbFactory('QSQLITE')
            self.abstractDb.connectDatabase()
            self.spatialiteFileEdit.setText(self.abstractDb.db.databaseName())
            self.edgvSpatialiteVersionEdit.setText(self.abstractDb.getDatabaseVersion())
                
        else:
            self.abstractDb = self.abstractDbFactory.createDbFactory('QPSQL')
            self.abstractDb.connectDatabase(self.comboBoxPostgis.currentText())
            self.edgvPostgisVersionEdit.setText(self.abstractDb.getDatabaseVersion())
        try:
            self.abstractDb.checkAndOpenDb()
            self.dbLoaded = True
            self.dbVersion = self.abstractDb.getDatabaseVersion()
            if self.dbVersion == '-1':
                self.problemOccurred.emit(self.tr('This is not a valid DsgTools database!'))
            else:
                self.setCRS()
        except Exception as e:
            QgsMessageLog.logMessage(e.args[0], "DSG Tools Plugin", QgsMessageLog.CRITICAL)    

    def setCRS(self):
        try:
            self.epsg = self.abstractDb.findEPSG()
            if self.epsg == -1:
                self.problemOccurred.emit(self.tr('Coordinate Reference System not set or invalid!'))
            else:
                self.crs = QgsCoordinateReferenceSystem(self.epsg, QgsCoordinateReferenceSystem.EpsgCrsId)
                if self.isSpatialite:
                    self.spatialiteCrsEdit.setText(self.crs.description())
                    self.spatialiteCrsEdit.setReadOnly(True)
                else:
                    self.postGISCrsEdit.setText(self.crs.description())
                    self.postGISCrsEdit.setReadOnly(True)
        except:
            pass

    def populatePostGISConnectionsCombo(self):
        self.comboBoxPostgis.clear()
        self.comboBoxPostgis.addItem(self.tr('Select Database'))
        self.comboBoxPostgis.addItems(self.getPostGISConnections())
        
    def isDBConnected(self):
        return self.dbLoaded
        
    def getDBVersion(self):
        return self.abstractDb.getDatabaseVersion()
    
    def getQmlPath(self):
        return self.abstractDb.getQmlDir()
        
    @pyqtSlot(bool)
    def on_addConnectionButton_clicked(self):  
        newConnectionDialog =  ServerDBExplorer(self)
        retvalue = newConnectionDialog.exec_()
        self.populatePostGISConnectionsCombo()
        return retvalue
    
    def getPostGISConnections(self):
        settings = QSettings()
        settings.beginGroup('PostgreSQL/connections')
        currentConnections = settings.childGroups()
        settings.endGroup()
        return currentConnections
class ExploreServerWidget(QtGui.QWidget, FORM_CLASS):
    abstractDbLoaded = pyqtSignal()
    serverAbstractDbLoaded = pyqtSignal(AbstractDb)
    clearWidgets = pyqtSignal()
    def __init__(self, parent = None):
        """Constructor."""
        super(self.__class__, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        self.superNeeded = False
        self.dbFactory = DbFactory()
        self.abstractDb = None

    def getServers(self):
        """
        Gets server from QSettings
        """
        settings = QSettings()
        settings.beginGroup('PostgreSQL/servers')
        currentConnections = settings.childGroups()
        settings.endGroup()
        return currentConnections
    
    def getServerConfiguration(self, name):
        """
        Gets server configuration
        name: server name 
        """
        settings = QSettings()
        settings.beginGroup('PostgreSQL/servers/'+name)
        host = settings.value('host')
        port = settings.value('port')
        user = settings.value('username')
        password = settings.value('password')
        settings.endGroup()
        
        return (host, port, user, password)
    
    def browseServer(self, dbList, host, port, user, password):
        """
        Browses server for EDGV databases
        dbList: databases list
        host: server host ip address
        port: server port
        user: user name
        password: password
        """
        canLoad = True
        if self.superNeeded:
            canLoad = False
            try:
                if self.serverWidget.abstractDb.checkSuperUser():
                    canLoad = True
                else:
                    QMessageBox.warning(self, self.tr('Info!'), self.tr('Connection refused. Connect with a super user to inspect server.'))
                    return []
            except Exception as e:
                QMessageBox.critical(self, self.tr('Critical!'), ':'.join(e.args))
        if canLoad:
            progress = ProgressWidget(1,len(dbList),self.tr('Loading databases from server... '), parent = self)
            progress.initBar()
            gen = self.factory.createSqlGenerator(False)
            edvgDbList = []
            for database in dbList:
                db = self.getPostGISDatabaseWithParams(database, host, port, user, password)
                if not db.open():
                    qgis.utils.iface.messageBar().pushMessage('DB :'+database+'| msg: '+db.lastError().databaseText(), level=QgsMessageBar.CRITICAL)
    
                query = QSqlQuery(db)
                if query.exec_(gen.getEDGVVersion()):
                    while query.next():
                        version = query.value(0)
                        if version:
                            edvgDbList.append((database, version))
                progress.step()
            return edvgDbList
    
    def getDbsFromServer(self, name):
        """
        Gets server databases
        name: server name
        """
        gen = self.factory.createSqlGenerator(False)
        
        (host, port, user, password) = self.getServerConfiguration(name)
        database = 'postgres'
        
        db = self.getPostGISDatabaseWithParams(database, host, port, user, password)
        if not db.open():
            QgsMessageLog.logMessage(db.lastError().text(), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
            QMessageBox.critical(self.iface.mainWindow(), self.tr('Critical'), self.tr('A problem occurred! Check log for details.'))
        
        query = QSqlQuery(gen.getDatabasesFromServer(), db)
        if not query.isActive():
            QMessageBox.critical(self.iface.mainWindow(), self.tr('Critical'), self.tr("Problem executing query: ")+query.lastError().text())
            
        dbList = []
        while query.next():
            dbList.append(query.value(0))
        return self.browseServer(dbList, host, port, user, password)
    
    @pyqtSlot(bool)
    def on_createNewServerPushButton_clicked(self):  
        """
        Opens the View Server dialog
        """
        createNewServer = ViewServers(self)
        result = createNewServer.exec_()
        self.populateServersCombo()

    def populateServersCombo(self):
        """
        Populates the server name combo box
        """
        self.serversCombo.clear()
        self.serversCombo.addItem(self.tr('Select Server'))
        currentConnections = self.getServers()
        for connection in currentConnections:
            (host, port, user, password) = self.getServerConfiguration(connection)
            self.serversCombo.addItem('{3} ({0}@{1}:{2})'.format(user, host, port, connection))
    
    @pyqtSlot(int)
    def on_serversCombo_currentIndexChanged(self):
        """
        Updates the server databases
        """
        self.clearWidgets.emit()
        if self.serversCombo.currentIndex() != 0:
            self.abstractDb = self.dbFactory.createDbFactory('QPSQL')
            if not self.abstractDb:
                QMessageBox.critical(self.iface.mainWindow(), self.tr('Critical'), self.tr('A problem occurred! Check log for details.'))
                return
            (host, port, user, password) = self.abstractDb.getServerConfiguration(self.serversCombo.currentText().split('(')[0][0:-1])
            if host or port or user:
                self.abstractDb.connectDatabaseWithParameters(host, port, 'postgres', user, password)
                if self.superNeeded:
                    try:
                        if not self.abstractDb.checkSuperUser():
                            QMessageBox.warning(self, self.tr('Info!'), self.tr('Connection refused. Connect with a super user to inspect server.'))
                            self.serversCombo.setCurrentIndex(0)
                            return
                    except Exception as e:
                        QMessageBox.critical(self, self.tr('Critical!'), ':'.join(e.args))
                self.abstractDbLoaded.emit()
        else:
            try:
                if self.abstractDb:
                    self.abstractDb.__del__()
                    self.abstractDb = None
            except:
                pass
        self.serverAbstractDbLoaded.emit(self.abstractDb)
    
    def getServerParameters(self):
        """
        Gets postgis server parameters
        """
        if self.serversCombo.currentIndex() != 0:
            return self.abstractDb.getServerConfiguration(self.serversCombo.currentText().split('(')[0][0:-1])
        else:
            return (None, None, None, None)
    
    def clearAll(self):
        """
        Resets the widget
        """
        try:
            if self.abstractDb:
                self.abstractDb.__del__()
                self.abstractDb = None
        except:
            pass
        self.serversCombo.setCurrentIndex(0)
class ConnectionWidget(QtGui.QWidget, FORM_CLASS):
    connectionChanged = pyqtSignal()
    problemOccurred = pyqtSignal(str)
    dbChanged = pyqtSignal(AbstractDb)
    styleChanged = pyqtSignal(dict)
    def __init__(self, parent = None):
        """Constructor."""
        super(ConnectionWidget, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        self.setInitialState()
        self.serverWidget.populateServersCombo()
        self.serverWidget.abstractDbLoaded.connect(self.getDatabasesFromServer)
        self.serverWidget.clearWidgets.connect(self.clearAll)
        
         
    def __del__(self):
        """
        Closes the database
        """
        self.closeDatabase()

    def closeDatabase(self):
        """
        Closes the current database
        """
        if self.abstractDb:
            del self.abstractDb
            self.abstractDb = None
                 
    def clearAll(self):
        """
        Resets the initial state
        """
        self.filename = ''
        self.dbLoaded = False
        self.epsg = 0
        self.crs = None
        
        self.abstractDb = None
        self.isSpatialite = False
        self.abstractDbFactory = DbFactory()
        self.utils = Utils()

        #populating the postgis combobox
        self.comboBoxPostgis.clear()
        self.spatialiteFileEdit.setReadOnly(True)   
        self.postGISCrsEdit.setReadOnly(True)
        self.spatialiteCrsEdit.setReadOnly(True)   
        self.edgvSpatialiteVersionEdit.setReadOnly(True)
        self.edgvPostgisVersionEdit.setReadOnly(True)      

    def setInitialState(self):
        """
        Sets the initial state
        """
        self.filename = ''
        self.dbLoaded = False
        self.epsg = 0
        self.crs = None
        
        self.abstractDb = None
        self.isSpatialite = False
        self.tabWidget.setCurrentIndex(0)
        self.abstractDbFactory = DbFactory()
        self.utils = Utils()
        self.serverWidget.serversCombo.setCurrentIndex(0)

        #populating the postgis combobox
        self.comboBoxPostgis.clear()
        self.spatialiteFileEdit.setReadOnly(True)   
        self.postGISCrsEdit.setReadOnly(True)
        self.spatialiteCrsEdit.setReadOnly(True)   
        self.edgvSpatialiteVersionEdit.setReadOnly(True)
        self.edgvPostgisVersionEdit.setReadOnly(True)      

    @pyqtSlot(int)
    def on_comboBoxPostgis_currentIndexChanged(self):
        """
        Updates database information when the combo box changes
        """
        if self.comboBoxPostgis.currentIndex() > 0:
            self.postGISCrsEdit.setText('')
            self.postGISCrsEdit.setReadOnly(True)
            self.edgvPostgisVersionEdit.setText('')
            self.edgvPostgisVersionEdit.setReadOnly(True)  
            self.loadDatabase()
            self.connectionChanged.emit()
        
    @pyqtSlot(bool)
    def on_pushButtonOpenFile_clicked(self):  
        """
        Loads a spatialite database
        """
        self.loadDatabase()
        if self.isDBConnected():
            self.connectionChanged.emit()
        
    @pyqtSlot(int)
    def on_tabWidget_currentChanged(self):
        """
        Changes the tab to work with spatialite or postgis databases
        """
        self.filename = ''
        self.comboBoxPostgis.clear()
        self.dbLoaded = False
        self.epsg = 0
        self.crs = None
        self.dbVersion = ''
        self.serverWidget.serversCombo.setCurrentIndex(0)
        self.spatialiteFileEdit.setReadOnly(True)
        self.spatialiteFileEdit.setText(self.filename)
        self.postGISCrsEdit.setText('')
        self.postGISCrsEdit.setReadOnly(True)
        self.spatialiteCrsEdit.setText('')
        self.spatialiteCrsEdit.setReadOnly(True)   
        self.edgvSpatialiteVersionEdit.setText('')
        self.edgvSpatialiteVersionEdit.setReadOnly(True)
        self.edgvPostgisVersionEdit.setText('')
        self.edgvPostgisVersionEdit.setReadOnly(True)
        self.mGroupBox.setTitle(self.tr('Database connection'))
        
        #Setting the database type
        if self.tabWidget.currentIndex() == 1:
            self.isSpatialite = True
        else:
            self.isSpatialite = False

    def loadDatabase(self):
        """
        Loads the selected database
        """
        self.closeDatabase()
        try:
            if self.isSpatialite:
                self.abstractDb = self.abstractDbFactory.createDbFactory('QSQLITE')
                self.abstractDb.connectDatabase()
                self.spatialiteFileEdit.setText(self.abstractDb.db.databaseName())
                self.edgvSpatialiteVersionEdit.setText(self.abstractDb.getDatabaseVersion())
                    
            else:
                self.abstractDb = self.abstractDbFactory.createDbFactory('QPSQL')
                (host, port, user, password) = self.serverWidget.getServerParameters()
                dbName = self.comboBoxPostgis.currentText()
                self.abstractDb.connectDatabaseWithParameters(host, port, dbName, user, password)
                self.edgvPostgisVersionEdit.setText(self.abstractDb.getDatabaseVersion())
                serverName = self.serverWidget.serversCombo.currentText()
                newText = dbName + self.tr(' on ') + serverName 
                self.mGroupBox.setToolTip(newText)
                # self.mGroupBox.setTitle(newText)

            self.abstractDb.checkAndOpenDb()
            self.dbLoaded = True
            self.dbVersion = self.abstractDb.getDatabaseVersion()
            self.abstractDb.checkAndCreateStyleTable()
            self.styles = self.abstractDb.getStyleDict(self.dbVersion)
            self.styleChanged.emit(self.styles)
            self.dbChanged.emit(self.abstractDb)
            if self.dbVersion == '-1':
                self.problemOccurred.emit(self.tr('This is not a valid DsgTools database!'))
            else:
                self.setCRS()
        except Exception as e:
            self.problemOccurred.emit(self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)   

    def setCRS(self):
        """
        Sets the CRS information
        """
        try:
            self.epsg = self.abstractDb.findEPSG()
            if self.epsg == -1:
                self.problemOccurred.emit(self.tr('Coordinate Reference System not set or invalid!'))
            else:
                self.crs = QgsCoordinateReferenceSystem(self.epsg, QgsCoordinateReferenceSystem.EpsgCrsId)
                if self.isSpatialite:
                    self.spatialiteCrsEdit.setText(self.crs.description())
                    self.spatialiteCrsEdit.setReadOnly(True)
                else:
                    self.postGISCrsEdit.setText(self.crs.description())
                    self.postGISCrsEdit.setReadOnly(True)
        except Exception as e:
            self.problemOccurred.emit(self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)

        
    def isDBConnected(self):
        """
        Checks if the database is already loaded
        """
        return self.dbLoaded
        
    def getDBVersion(self):
        """
        Gets the database version
        """
        ret = ''
        try:
            ret = self.abstractDb.getDatabaseVersion()
        except Exception as e:
            self.problemOccurred.emit(self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
        return ret
    
    def getQmlPath(self):
        """
        Gets the QML path
        """
        ret = ''
        try:
            ret = self.abstractDb.getQmlDir()
        except Exception as e:
            self.problemOccurred.emit(self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
        return ret
    
    def getDatabasesFromServer(self):
        """
        Gets databases from server
        """  
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            if self.serverWidget.abstractDb:
                dbList = self.serverWidget.abstractDb.getEDGVDbsFromServer(parentWidget = self)
                dbList.sort()
                self.comboBoxPostgis.clear()
                self.comboBoxPostgis.addItem(self.tr('Select Database'))
                for db, version in dbList:
                    self.comboBoxPostgis.addItem(db)
                
            else:
                self.setInitialState()
                return
        except Exception as e:
            QMessageBox.critical(self, self.tr('Critical!'), ':'.join(e.args))
            self.setInitialState()
            self.setInitialState()
        QApplication.restoreOverrideCursor()
Beispiel #7
0
class CreateProfile(QtGui.QDialog, FORM_CLASS):
    profileCreated = pyqtSignal(str)
    
    def __init__(self, parent = None):
        """Constructor."""
        super(CreateProfile, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        self.folder = os.path.join(os.path.dirname(__file__), 'profiles')

        self.abstractDb = None
        self.abstractDbFactory = DbFactory()

        self.populateTreeDict()
        
    def __del__(self):
        if self.abstractDb:
            del self.abstractDb
            self.abstractDb = None
        
    def getDbInfo(self):
        currentPath = os.path.dirname(__file__)
        if self.versionCombo.currentText() == '2.1.3':
            edgvPath = os.path.join(currentPath, '..', 'DbTools', 'SpatialiteTool', 'template', '213', 'seed_edgv213.sqlite')
        elif self.versionCombo.currentText() == 'FTer_2a_Ed':
            edgvPath = os.path.join(currentPath, '..', 'DbTools', 'SpatialiteTool', 'template', 'FTer_2a_Ed', 'seed_edgvfter_2a_ed.sqlite')

        self.abstractDb = self.abstractDbFactory.createDbFactory('QSQLITE')
        self.abstractDb.connectDatabase(edgvPath)

        try:
            self.abstractDb.checkAndOpenDb()
        except Exception as e:
            print e.args[0]

    def populateTreeDict(self):
        self.getDbInfo()

        tables = self.abstractDb.getTablesFromDatabase()
        
        self.profile = dict()
        categories = dict()
        for tableName in tables:
            #proceed only for edgv tables
            if tableName.split("_")[-1] == "p" or tableName.split("_")[-1] == "l" or tableName.split("_")[-1] == "a" or tableName.split("_")[0] == 'complexos':
                layerName = tableName.split('_')[0]+'.'+'_'.join(tableName.split('_')[1::])
                split = tableName.split('_')
                
                if len(split) < 2:
                    continue
                
                schema = split[0]
                category = split[1]
                if schema not in categories.keys():
                    categories[schema] = dict()
                    
                if category not in categories[schema].keys():
                    categories[schema][category] = dict()

                if layerName not in categories[schema][category]:
                    categories[schema][category][layerName] = dict()
                    categories[schema][category][layerName]['read'] = '0'
                    categories[schema][category][layerName]['write'] = '0'
                    
        self.profile['database'+'_'+self.versionCombo.currentText()] = categories

    @pyqtSlot()
    def on_buttonBox_accepted(self):
        if not self.lineEdit.text():
            QtGui.QMessageBox.warning(self, self.tr('Warning!'), self.tr('Fill the profile name!'))
            return
        else:
            profileName = self.lineEdit.text()
            
        path = os.path.join(self.folder, profileName+'.json')
        
        with open(path, 'w') as outfile:
            json.dump(self.profile, outfile, sort_keys=True, indent=4)
        self.profileCreated.emit(profileName)

    @pyqtSlot(int)
    def on_versionCombo_currentIndexChanged(self):
         self.populateTreeDict()
class CustomServerConnectionWidget(QtGui.QWidget, FORM_CLASS):
    selectionChanged = pyqtSignal()
    resetAll = pyqtSignal()
    dbDictChanged = pyqtSignal(str,list)
    styleChanged = pyqtSignal(dict)
    def __init__(self, parent = None):
        """Constructor."""
        super(self.__class__, self).__init__(parent)
        self.setupUi(self)
        self.utils = Utils()
        self.dbFactory = DbFactory()
        self.factory = SqlGeneratorFactory()
        self.serverWidget.populateServersCombo()
        self.serverWidget.abstractDbLoaded.connect(self.populatePostgisSelector)
        self.customFileSelector.filesSelected.connect(self.populateSpatialiteSelector)
        self.comboDict = {self.tr('Load Database Model EDGV Version 2.1.3'):'2.1.3', self.tr('Load Database Model EDGV Version 3.0'):'3.0', self.tr('Load Database Model EDGV Version FTer_2a_Ed'):'FTer_2a_Ed',self.tr('Load Other Database Models'):'Non_EDGV'}
        self.dbDict = {'2.1.3':[], 'FTer_2a_Ed':[],'Non_EDGV':[], '3.0':[]}
        self.selectedDbsDict = dict()
        self.stylesDict = dict()
        self.postgisCustomSelector.selectionChanged.connect(self.selectedDatabases)
        self.spatialiteCustomSelector.selectionChanged.connect(self.selectedFiles)
        self.path = None
        self.customFileSelector.setCaption(self.tr('Select a DSGTools Spatialite file'))
        self.customFileSelector.setFilter(self.tr('Spatialite file databases (*.sqlite)'))
        self.customFileSelector.setType('multi')
        self.edgvType = None
    
    def selectedDatabases(self, dbList, type):
        """
        Selects databases from a name list and database type
        """
        #TODO: build selectedDbsDict and emit dbDictChanged()
        #1- Iterate over dbList and check if all layers on dbList are on dict. If not, add it.
        if type == 'added':
            (host, port, user, password) = self.serverWidget.abstractDb.getParamsFromConectedDb()
            for dbName in dbList:
                if dbName not in self.selectedDbsDict.keys():
                    if host and port and user:
                        localDb = self.dbFactory.createDbFactory('QPSQL')
                        localDb.connectDatabaseWithParameters(host, port, dbName, user, password)
                        self.selectedDbsDict[dbName] = localDb
                        #do get dicts
                        localDict = localDb.getStyleDict(localDb.getDatabaseVersion())
                        for key in localDict.keys():
                            if key not in self.stylesDict.keys():
                                self.stylesDict[key] = dict()
                                self.stylesDict[key]['dbList'] = []
                            self.stylesDict[key]['style'] = localDict[key]
                            if dbName not in self.stylesDict[key]['dbList']:
                                self.stylesDict[key]['dbList'].append(dbName)
            self.dbDictChanged.emit('added', dbList)
            self.styleChanged.emit(self.stylesDict)
        #2- Iterate over selectedDbsDict and if there is a key not in dbList, close db and pop item
        if type == 'removed':
            for dbName in self.selectedDbsDict.keys():
                if dbName in dbList:
                    self.selectedDbsDict.pop(dbName)
            self.dbDictChanged.emit('removed', dbList)
            for key in self.stylesDict.keys():
                for db in self.stylesDict[key]['dbList']:
                    if db in dbList:
                        idx = self.stylesDict[key]['dbList'].index(db)
                        self.stylesDict[key]['dbList'].pop(idx)
                if len(self.stylesDict[key]['dbList']) == 0:
                    self.stylesDict.pop(key)
            self.styleChanged.emit(self.stylesDict)
    
    def selectedFiles(self, dbList, type):
        """
        Selects databases from a name list and database type
        """
        #TODO: build selectedDbsDict and emit dbDictChanged()
        #1- Iterate over dbList and check if all layers on dbList are on dict. If not, add it.
        if type == 'added':
            for dbName in dbList:
                if dbName not in self.selectedDbsDict.keys():
                    localDb = self.dbFactory.createDbFactory('QSQLITE')
                    localDb.connectDatabase(conn = self.spatialiteDict[dbName])
                    self.selectedDbsDict[dbName] = localDb
                    #do get dicts
                    localDict = localDb.getStyleDict(localDb.getDatabaseVersion())
                    for key in localDict.keys():
                        if key not in self.stylesDict.keys():
                            self.stylesDict[key] = dict()
                            self.stylesDict[key]['dbList'] = []
                        self.stylesDict[key]['style'] = localDict[key]
                        if dbName not in self.stylesDict[key]['dbList']:
                            self.stylesDict[key]['dbList'].append(dbName)
            self.dbDictChanged.emit('added', dbList)
            self.styleChanged.emit(self.stylesDict)
        #2- Iterate over selectedDbsDict and if there is a key not in dbList, close db and pop item
        if type == 'removed':
            for dbName in self.selectedDbsDict.keys():
                if dbName in dbList:
                    self.selectedDbsDict.pop(dbName)
            self.dbDictChanged.emit('removed', dbList)
            for key in self.stylesDict.keys():
                for db in self.stylesDict[key]['dbList']:
                    if db in dbList:
                        idx = self.stylesDict[key]['dbList'].index(db)
                        self.stylesDict[key]['dbList'].pop(idx)
                if len(self.stylesDict[key]['dbList']) == 0:
                    self.stylesDict.pop(key)
            self.styleChanged.emit(self.stylesDict)
    
    @pyqtSlot(int)
    def on_serverConnectionTab_currentChanged(self, currentTab):
        """
        Changes the database type (spatialite/postgis)
        """
        if currentTab == 0:
            self.clearSpatialiteTab()
            self.populatePostgisSelector()
        elif currentTab == 1:
            self.clearPostgisTab()
            self.populateSpatialiteSelector()
        pass
    
    def populatePostgisSelector(self):
        """
        Populates the postgis database list according to the database type
        """
        self.dbDict = {'2.1.3':[], 'FTer_2a_Ed':[],'Non_EDGV':[], '3.0':[]}
        dbList = []
        try:
            if self.serverWidget.abstractDb:
                dbList = self.serverWidget.abstractDb.getEDGVDbsFromServer(parentWidget = self)
            else:
                self.clearPostgisTab()
                return
        except Exception as e:
            QMessageBox.critical(self, self.tr('Critical!'), ':'.join(e.args))
            self.clearPostgisTab()
        dbList.sort()
        for (dbname, dbversion) in dbList:
            if dbversion in self.dbDict.keys():
                self.dbDict[dbversion].append(dbname)
            else:
                self.dbDict['Non_EDGV'].append(dbname)
        comboText = self.postgisEdgvComboFilter.currentText()
        self.postgisCustomSelector.setInitialState(self.dbDict[self.comboDict[comboText]]) 
    
    def populateSpatialiteSelector(self):
        """
        Populates the spatialite database list according to the databse type
        """
        self.dbDict = {'2.1.3':[], 'FTer_2a_Ed':[],'Non_EDGV':[], '3.0':[]}
        self.spatialiteDict = dict()
        dbList = []
        try:
            for dbPath in self.customFileSelector.fileNameList:
                auxAbstractDb = self.dbFactory.createDbFactory('QSQLITE')
                dbName = os.path.basename(dbPath).split('.')[0]
                self.path = os.path.dirname(dbPath)
                auxAbstractDb.connectDatabase(conn = dbPath)
                version = auxAbstractDb.getDatabaseVersion()
                dbList.append((dbName,version))
                self.spatialiteDict[dbName] = dbPath
        except Exception as e:
            QMessageBox.critical(self, self.tr('Critical!'), ':'.join(e.args))
            self.clearSpatialiteTab()
        dbList.sort()
        for (dbname, dbversion) in dbList:
            if dbversion in self.dbDict.keys():
                self.dbDict[dbversion].append(dbname)        
#         if len(self.dbDict['2.1.3']) == 0:
#             self.spatialiteEdgvComboFilter.setCurrentIndex(1)
        comboText = self.spatialiteEdgvComboFilter.currentText()
        self.spatialiteCustomSelector.setInitialState(self.dbDict[self.comboDict[comboText]]) 
    
    def clearSpatialiteTab(self):
        """
        Clears the postgis tab, returning it to the original state
        """
        self.spatialiteCustomSelector.clearAll()
        self.serverWidget.clearAll()
        self.dbDict = {'2.1.3':[], 'FTer_2a_Ed':[],'Non_EDGV':[],'3.0':[]}
        self.customFileSelector.resetAll()
        self.edgvType = None
        self.selectedDbsDict = dict()
        self.resetAll.emit()
    
    @pyqtSlot(int)
    def on_postgisEdgvComboFilter_currentIndexChanged(self):
        """
        Updates the postgis databases according to its type
        """
        comboText = self.postgisEdgvComboFilter.currentText()
        self.postgisCustomSelector.resetSelections()
        self.postgisCustomSelector.setInitialState(self.dbDict[self.comboDict[comboText]])
        self.serverConnectionTab
        self.edgvType = self.comboDict[comboText]
        self.resetAll.emit()
    
    @pyqtSlot(int)
    def on_spatialiteEdgvComboFilter_currentIndexChanged(self):
        """
        Updates the postgis databases according to its type
        """
        comboText = self.spatialiteEdgvComboFilter.currentText()
        self.spatialiteCustomSelector.setInitialState(self.dbDict[self.comboDict[comboText]])
        self.edgvType = self.comboDict[comboText]
        self.resetAll.emit()
    
    def clearPostgisTab(self):
        """
        Clears the spatialite tab, returning it to the original state
        """
        self.postgisCustomSelector.clearAll()
        self.serverWidget.clearAll()
        self.dbDict = {'2.1.3':[], 'FTer_2a_Ed':[],'Non_EDGV':[],'3.0':[]}
        self.edgvType = None
        self.selectedDbsDict = dict()
        self.resetAll.emit()
    
    def getStyles(self, type, abstractDb):
        """
        Gets database styles. If the structure to store styles is not yet created, we should create it.
        """
        dbVersion = abstractDb.getDatabaseVersion()
        abstractDb.checkAndCreateStyleTable()
        styles = abstractDb.getStyleDict(dbVersion)
        self.styleChanged.emit(type, styles)
Beispiel #9
0
class ComplexWindow(QtGui.QDockWidget, FORM_CLASS):
    def __init__(self, iface, parent=None):
        """Constructor."""
        super(ComplexWindow, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        self.iface = iface
        
        self.dbButton.clicked.connect(self.getDataSources)
        self.dbCombo.activated.connect(self.updateComplexClass)
        self.complexCombo.activated.connect(self.loadAssociatedFeatures)
        self.iface.newProjectCreated.connect(self.clearDock)

        self.abstractDb = None
        self.databases = None
        self.abstractDbFactory = DbFactory()

    def __del__(self):
        """
        Destructor
        """
        self.renewDb
            
    def renewDb(self):
        """
        Deletes the current abstractDb
        """
        if self.abstractDb:
            del self.abstractDb
            self.abstractDb = None

    def clearDock(self):
        """
        Clears the complex dock widget
        """
        self.treeWidget.clear()
        self.dbCombo.clear()
        self.complexCombo.clear()

    #verificar se é necessario
    def isSpatialiteDatabase(self, dbName):
        """
        Checks if the database in use is a spatialite database
        """
        (dataSourceUri, credentials) = self.databases[dbName]
        if dataSourceUri.host() == "":
            return True
        return False

    def getUserCredentials(self, lyr):
        """
        Gets user credentials to acess the database
        """
        dataSourceUri = QgsDataSourceURI(lyr.dataProvider().dataSourceUri())
        if dataSourceUri.host() == '':
            return (None, None)

        if dataSourceUri.password() != '':
            return (dataSourceUri.username(), dataSourceUri.password())

        connInfo = dataSourceUri.connectionInfo()
        (success, user, passwd ) = QgsCredentials.instance().get(connInfo, dataSourceUri.username(), None)
        # Put the credentials back (for yourself and the provider), as QGIS removes it when you "get" it
        if success:
            QgsCredentials.instance().put(connInfo, user, passwd)
        else:
            return (None, None)

        return (user, passwd)

    def updateComplexClass(self):
        """
        Updates the complex classes in the complex combo box
        """
        self.renewDb()

        if self.dbCombo.currentIndex() == 0:
            return

        dbName = self.dbCombo.currentText()
        
        (dataSourceUri, credentials) = self.databases[dbName]
        #verifying the connection type
        if self.isSpatialiteDatabase(dbName):
            self.abstractDb = self.abstractDbFactory.createDbFactory('QSQLITE')
            self.abstractDb.connectDatabase(dataSourceUri.database())
        else:
            self.abstractDb = self.abstractDbFactory.createDbFactory('QPSQL')
            
            database = dbName
            host = dataSourceUri.host()
            port = int(dataSourceUri.port())
            user = credentials[0]
            password = credentials[1]
            
            self.abstractDb.connectDatabaseWithParameters(host, port, database, user, password)
        try:
            self.abstractDb.checkAndOpenDb()
            self.populateComboBox()
        except Exception as e:
            QMessageBox.critical(self.iface.mainWindow(), self.tr("Critical!"), ':'.join(e.args))

    def populateComboBox(self):
        """
        Fills the complex combo box with complex classes
        """
        #getting all complex tables
        self.complexCombo.clear()
        self.complexCombo.addItem(self.tr("select a complex class"))

        complexClasses = []
        try:
            complexClasses = self.abstractDb.listComplexClassesFromDatabase()
        except Exception as e:
            QMessageBox.critical(self.iface.mainWindow(), self.tr("Critical!"), ':'.join(e.args))
            QgsMessageLog.logMessage(e.args[0], 'DSG Tools Plugin', QgsMessageLog.CRITICAL)

        self.complexCombo.addItems(complexClasses)

    def getDataSources(self):
        """
        Obtains the available databases from the layers loaded in the TOC
        """
        self.dbCombo.clear()
        self.dbCombo.addItem(self.tr("select a database"))

        if self.databases:
            self.databases.clear()

        #dictionary of names and datasourceUri
        self.databases = dict()
        self.layers = self.iface.mapCanvas().layers()
        for layer in self.layers:
            dataSourceUri = QgsDataSourceURI(layer.dataProvider().dataSourceUri())
            dbName = dataSourceUri.database()
            if dbName not in self.databases.keys():
                self.databases[dbName] = (dataSourceUri, self.getUserCredentials(layer))
                #populating the combo
                self.dbCombo.addItem(dbName)

    @pyqtSlot(bool)
    def on_managePushButton_clicked(self):
        """
        Opens the dialog to manage complex features
        """
        #opens a dialog to manage complexes
        if not self.abstractDb:
            QMessageBox.critical(self.iface.mainWindow(), self.tr("Critical!"), self.tr('Select a database before managing a complex!'))
            return
        dlg = ManageComplexDialog(self.iface, self.abstractDb, self.complexCombo.currentText())
        #connects a signal to update the tree widget when done
        QObject.connect(dlg, SIGNAL(("tableUpdated()")), self.loadAssociatedFeatures)
        #connects a signal to disassociate features from complex before removal
        QObject.connect(dlg, SIGNAL(("markedToRemove( PyQt_PyObject )")), self.disassociateFeatures)
        result = dlg.exec_()
        if result:
            pass

    @pyqtSlot(bool)
    def on_associatePushButton_clicked(self):
        """
        Slot used to associate features to a complex
        """
        self.associateFeatures()

    @pyqtSlot(bool)
    def on_zoomButton_clicked(self):
        """
        Slot used to zoom the mapcanvas to the features associated to a complex
        """
        #case no item is selected we should warn the user
        if len(self.treeWidget.selectedItems()) == 0:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("Please, select an item to zoom."))
            return

        item = self.treeWidget.selectedItems()[0]
        #checking if the item is a complex (it should have depth = 2)
        if self.depth(item) == 2:
            bbox = QgsRectangle()
            for i in range(item.childCount()):
                aggregated_item = item.child(i)
                aggregated_class = aggregated_item.text(0)
                #getting the layer the needs to be updated
                aggregated_layer = None
                layers = self.iface.mapCanvas().layers()
                for layer in layers:
                    if layer.name() == aggregated_class:
                        aggregated_layer = layer
                        break

                if not aggregated_layer:
                    QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("The associated classes must be loaded in the table of contents."))
                    return

                for j in range(aggregated_item.childCount()):
                    id = aggregated_item.child(j).text(0)
                    freq = QgsFeatureRequest()
                    freq.setFilterFid(int(id))
                    feature = layer.getFeatures( freq ).next()
                    if j==0 and i == 0:
                        bbox=feature.geometry().boundingBox()
                    bbox.combineExtentWith(feature.geometry().boundingBox())

            self.iface.mapCanvas().setExtent(bbox)
            self.iface.mapCanvas().refresh()
        else:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("Select a complex."))
            return

    def disassociateFeatures(self, toBeRemoved):
        """
        Disassociates features from a complex
        toBeremoved: uuid of the complex that will have all its associated features disassociated
        """
        for uuid in toBeRemoved:
            items = self.treeWidget.findItems(uuid, Qt.MatchRecursive, 1)
            if len(items) == 0:
                return
            complexItem = items[0]
            count = complexItem.childCount()
            for i in range(count):
                self.disassociateAggregatedClass(complexItem.child(i))

    def disassociateAggregatedClass(self, item):
        """
        Disassociates a particular class from a complex
        item: aggregated class to be disassociated
        """
        aggregated_class = item.text(0)
        uuid = item.parent().text(1)
        complex = item.parent().parent().text(0)

        link_column = ''
        try:
            link_column = self.abstractDb.obtainLinkColumn(complex, aggregated_class)
        except Exception as e:
            QMessageBox.critical(self.iface.mainWindow(), self.tr('Critical'), self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), 'DSG Tools Plugin', QgsMessageLog.CRITICAL)

        #getting the layer the needs to be updated
        aggregated_layer = None
        layers = self.iface.mapCanvas().layers()
        for layer in layers:
            #in case of spatialite databases when a complex class is added as a layer it's name has 'complexos_'
            if layer.name() == aggregated_class or layer.name() == 'complexos_'+aggregated_class:
                aggregated_layer = layer
                break

        if not aggregated_layer:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("The class you're trying to disassociate must loaded in the table of contents."))
            return

        for i in range(item.childCount()):
            #feature id that will be updated
            id = item.child(i).text(0)
            self.updateLayerOnDisassociate(layer, aggregated_class, link_column, id)

    def disassociateAggregatedId(self, item):
        """
        Disassociates a particular feature from a complex
        item: aggregated feature to be disassociated
        """        
        aggregated_class = item.parent().text(0)
        uuid = item.parent().parent().text(1)
        complex = item.parent().parent().parent().text(0)

        link_column = ''
        try:
            link_column = self.abstractDb.obtainLinkColumn(complex, aggregated_class)
        except Exception as e:
            QMessageBox.critical(self.iface.mainWindow(), self.tr('Critical'), self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), 'DSG Tools Plugin', QgsMessageLog.CRITICAL)

        #getting the layer the needs to be updated
        aggregated_layer = None
        layers = self.iface.mapCanvas().layers()
        for layer in layers:
            #in case of spatialite databases when a complex class is added as a layer it's name has 'complexos_'
            if layer.name() == aggregated_class or layer.name() == 'complexos_'+aggregated_class:
                aggregated_layer = layer
                break

        if not aggregated_layer:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("The class you're trying to disassociate must loaded in the table of contents."))
            return

        #feature id that will be updated
        id = item.text(0)
        self.updateLayerOnDisassociate(layer, aggregated_class, link_column, id)

    def updateLayerOnDisassociate(self, layer, aggregated_class, link_column, id):
        """
        Updates the layer upon disassociation from complex
        layer: layer that will be afected
        aggregated_class: aggregated class
        link_column: link column between complex and class
        id: feature id
        """
        try:
            if self.abstractDb.isComplexClass(aggregated_class):
                    self.abstractDb.disassociateComplexFromComplex(aggregated_class, link_column, id)
            else:
                #field index that will be set to NULL
                fieldIndex = [i for i in range(len(layer.dataProvider().fields())) if layer.dataProvider().fields()[i].name() == link_column]
                #attribute pair that will be changed
                attrs = {fieldIndex[0]:None}
                #actual update in the database
                layer.dataProvider().changeAttributeValues({int(id):attrs})
        except Exception as e:
            QMessageBox.critical(self.iface.mainWindow(), self.tr("Critical!"), e.args[0])
            QgsMessageLog.logMessage(':'.join(e.args), 'DSG Tools Plugin', QgsMessageLog.CRITICAL)

    @pyqtSlot(bool)
    def on_disassociatePushButton_clicked(self):
        """
        Starts the disassociation process.
        It will firts check the depth of the item that need to be disassociated and them call the correct method to to the job.
        It can be a particular class or a particular feature
        """
        #case no item is selected we should warn the user
        if len(self.treeWidget.selectedItems()) == 0:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("Please, select an aggregated class or aggregated id."))
            return

        item = self.treeWidget.selectedItems()[0]
        #checking if the item is a complex (it should have depth = 2)
        if self.depth(item) == 3:
            self.disassociateAggregatedClass(item)
        elif self.depth(item) == 4:
            self.disassociateAggregatedId(item)
        else:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("Please, select an aggregated class or aggregated id."))
            return

        self.loadAssociatedFeatures()

    def loadAssociatedFeatures(self):
        """
        Loads all features associated to a complex
        """
        self.treeWidget.clear()

        if self.complexCombo.currentIndex() == 0:
            return

        complex = self.complexCombo.currentText()

        associatedDict = dict()
        try:
            associatedDict = self.abstractDb.loadAssociatedFeatures(complex)
        except Exception as e:
            QMessageBox.critical(self.iface.mainWindow(), self.tr('Critical'), self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), 'DSG Tools Plugin', QgsMessageLog.CRITICAL)
            
        for name in associatedDict.keys():
            for complex_uuid in associatedDict[name].keys():
                self.addAssociatedFeature(complex, name, complex_uuid, None, None)
                for aggregated_class in associatedDict[name][complex_uuid]:
                    for ogc_fid in associatedDict[name][complex_uuid][aggregated_class]:
                        self.addAssociatedFeature(complex, name, complex_uuid, aggregated_class, ogc_fid)

    def depth(self, item):
        """
        Calculates the item deth in the tree
        """
        #calculates the depth of the item
        depth = 0
        while item is not None:
            item = item.parent()
            depth += 1
        return depth

    def associateFeatures(self):
        """
        Associates all features selected in the map canvas to a complex.
        """
        #case no item is selected we should warn the user
        if len(self.treeWidget.selectedItems()) == 0:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("Please, select a complex."))
            return

        item = self.treeWidget.selectedItems()[0]
        #checking if the item is a complex (it should have depth = 2)
        if self.depth(item) != 2:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("Please, select a complex."))
            return

        complex = self.complexCombo.currentText()

        #uuid to be adjust on the selected features
        uuid = item.text(1)

        #getting the selected features
        forbiddenLayers = []
        self.layers = self.iface.mapCanvas().layers()
        for layer in self.layers:
            if layer.type() != QgsMapLayer.VectorLayer:
                continue
            #case no fetures selected we proceed to the next one
            selectedFeatures = layer.selectedFeatures()
            if len(selectedFeatures) == 0:
                continue

            #obtaining the link column
            column_name = ''
            try:
                column_name = self.abstractDb.obtainLinkColumn(complex, layer.name())
            except Exception as e:
                QMessageBox.critical(self.iface.mainWindow(), self.tr('Critical'), self.tr('A problem occurred! Check log for details.'))
                QgsMessageLog.logMessage(':'.join(e.args), 'DSG Tools Plugin', QgsMessageLog.CRITICAL)

            #storing the names of the incompatible layers
            if column_name == '':
                forbiddenLayers.append(layer.name())
                continue

            for feature in selectedFeatures:
                fieldIndex = [i for i in range(len(layer.dataProvider().fields())) if layer.dataProvider().fields()[i].name() == column_name]
                #feature id that will be updated
                id = feature.id()
                #attribute pair that will be changed
                attrs = {fieldIndex[0]:uuid}
                #actual update in the database
                layer.dataProvider().changeAttributeValues({id:attrs})

        #show the message of incompatible classes to associate
        if len(forbiddenLayers) > 0:
            message = ""
            message += self.tr("The following layers cannot be associated to complexes from ")+self.complexCombo.currentText()+":\n"
            for text in forbiddenLayers:
                message += text+"\n"
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), message)

        #updating the tree widget
        self.loadAssociatedFeatures()

    def createTreeItem(self, parent, text, uuid = ""):
        """
        Creates tree items
        parent: parent item
        text: item text
        uuid: complex uuid
        """
        count = parent.childCount()
        children = []
        #making a list of item names
        for i in range(count):
            child = parent.child(i)
            children.append(child.text(0))

        #checking if the text is already in the tree widget
        if text not in children:
            #case not it should be created
            item = QTreeWidgetItem(parent)
            item.setExpanded(True)
            item.setText(0,text)
            #adding the complex uuid to the tree widget
            if uuid != "":
                item.setText(1, str(uuid))
        else:
            #case already exists the correspondind item should be returned
            for i in range(count):
                child = parent.child(i)
                if child.text(0) == text:
                    item = child
        return item

    def addAssociatedFeature(self, className, complexName, complexId, associatedClass, associatedId):
        """
        Adds a feature to a complex
        className: class name
        complexName: complex name
        complexId: complex uuid
        associatedClass: associated class
        associatedId: associated id
        """
        #get the corresponding top level item
        classNameItem = self.createTreeItem(self.treeWidget.invisibleRootItem(), className)
        #get the corresponding complex item
        complexNameItem = self.createTreeItem(classNameItem, complexName, complexId)
        if associatedClass and associatedId:
            #get the corresponding class item
            associatedClassItem = self.createTreeItem(complexNameItem, associatedClass)
            #creates the corresponding associated item
            self.createTreeItem(associatedClassItem, str(associatedId))

    def __test(self, x):
        if (x.parent() == None) :
            return True
        else:
            return False
Beispiel #10
0
class StyleManagerTool(QWidget, FORM_CLASS): 
    def __init__(self, iface, parent = None):
        """
        Constructor
        """
        super(StyleManagerTool, self).__init__(parent)
        self.setupUi(self)
        self.iface = iface
        self.splitter.hide()
        self.refreshDb()
        self.dbFactory = DbFactory()
        self.applyPushButton.setEnabled(False)
        self.utils = Utils()
        
    @pyqtSlot(bool)
    def on_layerPushButton_toggled(self, toggled):
        """
        Shows/Hides the tool bar
        """
        if toggled:
            self.refreshDb()
            self.splitter.show()
        else:
            self.splitter.hide()
    
    @pyqtSlot(bool, name = 'on_refreshPushButton_clicked')
    def refreshDb(self):
        self.dbComboBox.clear()
        self.dbComboBox.addItem(self.tr('Select Database'))
        #populate database list
        for dbName in self.getDatabaseList():
            self.dbComboBox.addItem(dbName)
    
    @pyqtSlot(int, name = 'on_styleComboBox_currentIndexChanged')
    def enableApply(self):
        dbIdx = self.dbComboBox.currentIndex()
        stylesIdx = self.styleComboBox.currentIndex()
        if dbIdx > 0 and stylesIdx > 0:
            self.applyPushButton.setEnabled(True)
        else:
            self.applyPushButton.setEnabled(False)

    @pyqtSlot(bool)
    def on_applyPushButton_clicked(self):
        try:
            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
            dbName = self.dbComboBox.currentText()
            styleName = self.styleComboBox.currentText()
            lyrList = self.getLayers(dbName)
            abstractDb = self.getAbstractDb(dbName)
            dbVersion = abstractDb.getDatabaseVersion()
            stylesDict = abstractDb.getStyleDict(dbVersion)
            selectedStyle = stylesDict[styleName]
            localProgress = ProgressWidget(1, len(lyrList) - 1, self.tr('Loading style {0}').format(styleName), parent=self.iface.mapCanvas())
            for lyr in lyrList:
                try:
                    uri = QgsDataSourceURI(lyr.dataProvider().dataSourceUri())
                    fullPath = self.getStyle(abstractDb, selectedStyle, lyr.name())
                    if fullPath:
                        lyr.applyNamedStyle(fullPath)
                except:
                    pass
                localProgress.step()
            self.iface.mapCanvas().refreshAllLayers()
            QApplication.restoreOverrideCursor()
        except Exception as e:
            QgsMessageLog.logMessage(self.tr('Error setting style ') + styleName + ': ' +':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
            QApplication.restoreOverrideCursor()

    
    def getLayers(self, dbName):
        lyrList = []
        for lyr in self.iface.legendInterface().layers():
            if isinstance(lyr, QgsVectorLayer):
                candidateUri = QgsDataSourceURI(lyr.dataProvider().dataSourceUri())
                if candidateUri.database() == dbName and lyr.providerType() in ['postgres', 'spatialite']:
                    lyrList.append(lyr)
        return lyrList
    
    def getDatabaseList(self):
        dbList = []
        for lyr in self.iface.legendInterface().layers():
            if isinstance(lyr, QgsVectorLayer):
                candidateUri = QgsDataSourceURI(lyr.dataProvider().dataSourceUri())
                dbName = candidateUri.database()
                if dbName not in dbList and lyr.providerType() in ['postgres', 'spatialite']:
                    dbList.append(dbName)
        return dbList
    
    def loadStylesCombo(self, abstractDb):
        dbVersion = abstractDb.getDatabaseVersion()
        styleDict = abstractDb.getStyleDict(dbVersion)
        self.styleComboBox.clear()
        styleList = styleDict.keys()
        numberOfStyles = len(styleList)
        if numberOfStyles > 0:
            self.styleComboBox.addItem(self.tr('Select Style'))
            for i in range(numberOfStyles):
                self.styleComboBox.addItem(styleList[i])
        else:
            self.styleComboBox.addItem(self.tr('No available styles'))
    
    def getParametersFromLyr(self, dbName):
        for lyr in self.iface.legendInterface().layers():
          if isinstance(lyr, QgsVectorLayer):
            candidateUri = QgsDataSourceURI(lyr.dataProvider().dataSourceUri())
            if candidateUri.database() == dbName:
                currLyr = lyr
                break
        dbParameters = dict()
        if currLyr.providerType() == 'postgres':
            dbParameters['host'] = candidateUri.host()
            dbParameters['port'] = candidateUri.port()
            dbParameters['user'] = candidateUri.username()
            dbParameters['password'] = candidateUri.password()
            return dbParameters, 'QPSQL'
        if currLyr.providerType() == 'spatialite':
            dbParameters['dbPath'] = candidateUri.database()
            return dbParameters, 'QSQLITE'
        else:
            raise Exception(self.tr('Feature only implemented for PostGIS and Spatialite'))
    
    def getAbstractDb(self, dbName):
        dbParameters, driverName = self.getParametersFromLyr(dbName)
        abstractDb = self.dbFactory.createDbFactory(driverName)
        if 'host' in dbParameters.keys():
            abstractDb.connectDatabaseWithParameters(dbParameters['host'], dbParameters['port'], dbName, dbParameters['user'], dbParameters['password'])
        else:
            abstractDb.connectDatabase(dbParameters['dbPath'])
        return abstractDb

    @pyqtSlot(int)
    def on_dbComboBox_currentIndexChanged(self, idx):
        if idx <= 0:
            self.styleComboBox.clear()
            self.styleComboBox.addItem(self.tr('Select Style'))
            self.styleComboBox.setEnabled(False)
        elif idx > 0:
            self.styleComboBox.setEnabled(True)
            dbName = self.dbComboBox.currentText()
            abstractDb = self.getAbstractDb(dbName)
            self.loadStylesCombo(abstractDb)
        self.enableApply()
        
    def getStyle(self, abstractDb, stylePath, className):
        if 'db:' in stylePath:
            return abstractDb.getStyle(stylePath.split(':')[-1], className)
        else:
            return self.getStyleFromFile(stylePath, className)
    
    def getStyleFromFile(self, stylePath, className):
        availableStyles = os.walk(stylePath).next()[2]
        styleName = className+'.qml'
        if styleName in availableStyles:
            path = os.path.join(stylePath, styleName)
            qml = self.utils.parseStyle(path)
            return qml
        else:
            return None
Beispiel #11
0
class ExploreServerWidget(QtGui.QWidget, FORM_CLASS):
    abstractDbLoaded = pyqtSignal()
    serverAbstractDbLoaded = pyqtSignal(AbstractDb)
    clearWidgets = pyqtSignal()

    def __init__(self, parent=None):
        """Constructor."""
        super(self.__class__, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        self.superNeeded = False
        self.dbFactory = DbFactory()
        self.abstractDb = None

    def getServers(self):
        """
        Gets server from QSettings
        """
        settings = QSettings()
        settings.beginGroup('PostgreSQL/servers')
        currentConnections = settings.childGroups()
        settings.endGroup()
        return currentConnections

    def getServerConfiguration(self, name):
        """
        Gets server configuration
        name: server name 
        """
        settings = QSettings()
        settings.beginGroup('PostgreSQL/servers/' + name)
        host = settings.value('host')
        port = settings.value('port')
        user = settings.value('username')
        password = settings.value('password')
        settings.endGroup()

        return (host, port, user, password)

    def browseServer(self, dbList, host, port, user, password):
        """
        Browses server for EDGV databases
        dbList: databases list
        host: server host ip address
        port: server port
        user: user name
        password: password
        """
        canLoad = True
        if self.superNeeded:
            canLoad = False
            try:
                if self.serverWidget.abstractDb.checkSuperUser():
                    canLoad = True
                else:
                    QMessageBox.warning(
                        self, self.tr('Info!'),
                        self.
                        tr('Connection refused. Connect with a super user to inspect server.'
                           ))
                    return []
            except Exception as e:
                QMessageBox.critical(self, self.tr('Critical!'),
                                     ':'.join(e.args))
        if canLoad:
            progress = ProgressWidget(
                1,
                len(dbList),
                self.tr('Loading databases from server... '),
                parent=self)
            progress.initBar()
            gen = self.factory.createSqlGenerator(False)
            edvgDbList = []
            for database in dbList:
                db = self.getPostGISDatabaseWithParams(database, host, port,
                                                       user, password)
                if not db.open():
                    qgis.utils.iface.messageBar().pushMessage(
                        'DB :' + database + '| msg: ' +
                        db.lastError().databaseText(),
                        level=QgsMessageBar.CRITICAL)

                query = QSqlQuery(db)
                if query.exec_(gen.getEDGVVersion()):
                    while query.next():
                        version = query.value(0)
                        if version:
                            edvgDbList.append((database, version))
                progress.step()
            return edvgDbList

    def getDbsFromServer(self, name):
        """
        Gets server databases
        name: server name
        """
        gen = self.factory.createSqlGenerator(False)

        (host, port, user, password) = self.getServerConfiguration(name)
        database = 'postgres'

        db = self.getPostGISDatabaseWithParams(database, host, port, user,
                                               password)
        if not db.open():
            QgsMessageLog.logMessage(db.lastError().text(), "DSG Tools Plugin",
                                     QgsMessageLog.CRITICAL)
            QMessageBox.critical(
                self.iface.mainWindow(), self.tr('Critical'),
                self.tr('A problem occurred! Check log for details.'))

        query = QSqlQuery(gen.getDatabasesFromServer(), db)
        if not query.isActive():
            QMessageBox.critical(
                self.iface.mainWindow(), self.tr('Critical'),
                self.tr("Problem executing query: ") +
                query.lastError().text())

        dbList = []
        while query.next():
            dbList.append(query.value(0))
        return self.browseServer(dbList, host, port, user, password)

    @pyqtSlot(bool)
    def on_createNewServerPushButton_clicked(self):
        """
        Opens the View Server dialog
        """
        createNewServer = ViewServers(self)
        result = createNewServer.exec_()
        self.populateServersCombo()

    def populateServersCombo(self):
        """
        Populates the server name combo box
        """
        self.serversCombo.clear()
        self.serversCombo.addItem(self.tr('Select Server'))
        currentConnections = self.getServers()
        for connection in currentConnections:
            (host, port, user,
             password) = self.getServerConfiguration(connection)
            self.serversCombo.addItem('{3} ({0}@{1}:{2})'.format(
                user, host, port, connection))

    @pyqtSlot(int)
    def on_serversCombo_currentIndexChanged(self):
        """
        Updates the server databases
        """
        self.clearWidgets.emit()
        if self.serversCombo.currentIndex() != 0:
            self.abstractDb = self.dbFactory.createDbFactory('QPSQL')
            if not self.abstractDb:
                QMessageBox.critical(
                    self.iface.mainWindow(), self.tr('Critical'),
                    self.tr('A problem occurred! Check log for details.'))
                return
            (host, port, user,
             password) = self.abstractDb.getServerConfiguration(
                 self.serversCombo.currentText().split('(')[0][0:-1])
            if host or port or user:
                self.abstractDb.connectDatabaseWithParameters(
                    host, port, 'postgres', user, password)
                if self.superNeeded:
                    try:
                        if not self.abstractDb.checkSuperUser():
                            QMessageBox.warning(
                                self, self.tr('Info!'),
                                self.
                                tr('Connection refused. Connect with a super user to inspect server.'
                                   ))
                            self.serversCombo.setCurrentIndex(0)
                            return
                    except Exception as e:
                        QMessageBox.critical(self, self.tr('Critical!'),
                                             ':'.join(e.args))
                self.abstractDbLoaded.emit()
        else:
            try:
                if self.abstractDb:
                    self.abstractDb.__del__()
                    self.abstractDb = None
            except:
                pass
        self.serverAbstractDbLoaded.emit(self.abstractDb)

    def getServerParameters(self):
        """
        Gets postgis server parameters
        """
        if self.serversCombo.currentIndex() != 0:
            return self.abstractDb.getServerConfiguration(
                self.serversCombo.currentText().split('(')[0][0:-1])
        else:
            return (None, None, None, None)

    def clearAll(self):
        """
        Resets the widget
        """
        try:
            if self.abstractDb:
                self.abstractDb.__del__()
                self.abstractDb = None
        except:
            pass
        self.serversCombo.setCurrentIndex(0)
Beispiel #12
0
class ComplexWindow(QtGui.QDockWidget, FORM_CLASS):
    def __init__(self, iface, parent=None):
        """Constructor."""
        super(ComplexWindow, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        self.iface = iface
        
        self.dbButton.clicked.connect(self.getDataSources)
        self.dbCombo.activated.connect(self.updateComplexClass)
        self.complexCombo.activated.connect(self.loadAssociatedFeatures)
        self.iface.newProjectCreated.connect(self.clearDock)

        self.abstractDb = None
        self.databases = None
        self.abstractDbFactory = DbFactory()

    def __del__(self):
        self.renewDb
            
    def renewDb(self):
        if self.abstractDb:
            del self.abstractDb
            self.abstractDb = None

    def clearDock(self):
        self.treeWidget.clear()
        self.dbCombo.clear()
        self.complexCombo.clear()

    #verificar se é necessario
    def isSpatialiteDatabase(self, dbName):
        (dataSourceUri, credentials) = self.databases[dbName]
        if dataSourceUri.host() == "":
            return True
        return False

    def getUserCredentials(self, lyr):
        dataSourceUri = QgsDataSourceURI(lyr.dataProvider().dataSourceUri())
        if dataSourceUri.host() == '':
            return (None, None)

        if dataSourceUri.password() != '':
            return (dataSourceUri.username(), dataSourceUri.password())

        connInfo = dataSourceUri.connectionInfo()
        (success, user, passwd ) = QgsCredentials.instance().get(connInfo, dataSourceUri.username(), None)
        # Put the credentials back (for yourself and the provider), as QGIS removes it when you "get" it
        if success:
            QgsCredentials.instance().put(connInfo, user, passwd)
        else:
            return (None, None)

        return (user, passwd)

    def updateComplexClass(self):
        self.renewDb()

        if self.dbCombo.currentIndex() == 0:
            return

        dbName = self.dbCombo.currentText()
        
        (dataSourceUri, credentials) = self.databases[dbName]
        #verifying the connection type
        if self.isSpatialiteDatabase(dbName):
            self.abstractDb = self.abstractDbFactory.createDbFactory('QSQLITE')
            self.abstractDb.connectDatabase(dataSourceUri.database())
        else:
            self.abstractDb = self.abstractDbFactory.createDbFactory('QPSQL')
            
            database = dbName
            host = dataSourceUri.host()
            port = int(dataSourceUri.port())
            user = credentials[0]
            password = credentials[1]
            
            self.abstractDb.connectDatabaseWithParameters(host, port, database, user, password)
        try:
            self.abstractDb.checkAndOpenDb()
            self.populateComboBox()
        except Exception as e:
            QMessageBox.critical(self.iface.mainWindow(), self.tr("Critical!"), e.args[0])

    def populateComboBox(self):
        #getting all complex tables
        self.complexCombo.clear()
        self.complexCombo.addItem(self.tr("select a complex class"))

        complexClasses = self.abstractDb.listComplexClassesFromDatabase()
        self.complexCombo.addItems(complexClasses)

    def getDataSources(self):
        self.dbCombo.clear()
        self.dbCombo.addItem(self.tr("select a database"))

        if self.databases:
            self.databases.clear()

        #dictionary of names and datasourceUri
        self.databases = dict()
        self.layers = self.iface.mapCanvas().layers()
        for layer in self.layers:
            dataSourceUri = QgsDataSourceURI(layer.dataProvider().dataSourceUri())
            dbName = dataSourceUri.database()
            if dbName not in self.databases.keys():
                self.databases[dbName] = (dataSourceUri, self.getUserCredentials(layer))
                #populating the combo
                self.dbCombo.addItem(dbName)

    @pyqtSlot(bool)
    def on_managePushButton_clicked(self):
        #opens a dialog to manage complexes
        dlg = ManageComplexDialog(self.iface, self.abstractDb, self.complexCombo.currentText())
        #connects a signal to update the tree widget when done
        QObject.connect(dlg, SIGNAL(("tableUpdated()")), self.loadAssociatedFeatures)
        #connects a signal to disassociate features from complex before removal
        QObject.connect(dlg, SIGNAL(("markedToRemove( PyQt_PyObject )")), self.disassociateFeatures)
        result = dlg.exec_()
        if result:
            pass

    @pyqtSlot(bool)
    def on_associatePushButton_clicked(self):
        self.associateFeatures()

    @pyqtSlot(bool)
    def on_zoomButton_clicked(self):
        #case no item is selected we should warn the user
        if len(self.treeWidget.selectedItems()) == 0:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("Please, select an item to zoom."))
            return

        item = self.treeWidget.selectedItems()[0]
        #checking if the item is a complex (it should have depth = 2)
        if self.depth(item) == 2:
            bbox = QgsRectangle()
            for i in range(item.childCount()):
                aggregated_item = item.child(i)
                aggregated_class = aggregated_item.text(0)
                #getting the layer the needs to be updated
                aggregated_layer = None
                layers = self.iface.mapCanvas().layers()
                for layer in layers:
                    if layer.name() == aggregated_class:
                        aggregated_layer = layer
                        break

                if not aggregated_layer:
                    QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("The associated classes must be loaded in the table of contents."))
                    return

                for j in range(aggregated_item.childCount()):
                    id = aggregated_item.child(j).text(0)
                    freq = QgsFeatureRequest()
                    freq.setFilterFid(int(id))
                    feature = layer.getFeatures( freq ).next()
                    if j==0 and i == 0:
                        bbox=feature.geometry().boundingBox()
                    bbox.combineExtentWith(feature.geometry().boundingBox())

            self.iface.mapCanvas().setExtent(bbox)
            self.iface.mapCanvas().refresh()
        else:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("Select a complex."))
            return

    def disassociateFeatures(self, toBeRemoved):
        for uuid in toBeRemoved:
            items = self.treeWidget.findItems(uuid, Qt.MatchRecursive, 1)
            if len(items) == 0:
                return
            complexItem = items[0]
            count = complexItem.childCount()
            for i in range(count):
                self.disassociateAggregatedClass(complexItem.child(i))

    def disassociateAggregatedClass(self, item):
        aggregated_class = item.text(0)
        uuid = item.parent().text(1)
        complex = item.parent().parent().text(0)
        link_column = self.abstractDb.obtainLinkColumn(complex, aggregated_class)

        #getting the layer the needs to be updated
        aggregated_layer = None
        layers = self.iface.mapCanvas().layers()
        for layer in layers:
            #in case of spatialite databases when a complex class is added as a layer it's name has 'complexos_'
            if layer.name() == aggregated_class or layer.name() == 'complexos_'+aggregated_class:
                aggregated_layer = layer
                break

        if not aggregated_layer:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("The class you're trying to disassociate must loaded in the table of contents."))
            return

        for i in range(item.childCount()):
            #feature id that will be updated
            id = item.child(i).text(0)
            self.updateLayerOnDisassociate(layer, aggregated_class, link_column, id)

    def disassociateAggregatedId(self, item):
        aggregated_class = item.parent().text(0)
        uuid = item.parent().parent().text(1)
        complex = item.parent().parent().parent().text(0)
        link_column = self.abstractDb.obtainLinkColumn(complex, aggregated_class)

        #getting the layer the needs to be updated
        aggregated_layer = None
        layers = self.iface.mapCanvas().layers()
        for layer in layers:
            #in case of spatialite databases when a complex class is added as a layer it's name has 'complexos_'
            if layer.name() == aggregated_class or layer.name() == 'complexos_'+aggregated_class:
                aggregated_layer = layer
                break

        if not aggregated_layer:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("The class you're trying to disassociate must loaded in the table of contents."))
            return

        #feature id that will be updated
        id = item.text(0)
        self.updateLayerOnDisassociate(layer, aggregated_class, link_column, id)

    def updateLayerOnDisassociate(self, layer, aggregated_class, link_column, id):
        if self.abstractDb.isComplexClass(aggregated_class):
            try:
                self.abstractDb.disassociateComplexFromComplex(aggregated_class, link_column, id)
            except Exception as e:
                QMessageBox.critical(self.iface.mainWindow(), self.tr("Critical!"), e.args[0])
        else:
            #field index that will be set to NULL
            fieldIndex = [i for i in range(len(layer.dataProvider().fields())) if layer.dataProvider().fields()[i].name() == link_column]
            #attribute pair that will be changed
            attrs = {fieldIndex[0]:None}
            #actual update in the database
            layer.dataProvider().changeAttributeValues({int(id):attrs})

    @pyqtSlot(bool)
    def on_disassociatePushButton_clicked(self):
        #case no item is selected we should warn the user
        if len(self.treeWidget.selectedItems()) == 0:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("Please, select an aggregated class or aggregated id."))
            return

        item = self.treeWidget.selectedItems()[0]
        #checking if the item is a complex (it should have depth = 2)
        if self.depth(item) == 3:
            self.disassociateAggregatedClass(item)
        elif self.depth(item) == 4:
            self.disassociateAggregatedId(item)
        else:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("Please, select an aggregated class or aggregated id."))
            return

        self.loadAssociatedFeatures()

    def loadAssociatedFeatures(self):
        self.treeWidget.clear()

        if self.complexCombo.currentIndex() == 0:
            return

        complex = self.complexCombo.currentText()

        associatedDict = self.abstractDb.loadAssociatedFeatures(complex)

        for name in associatedDict.keys():
            for complex_uuid in associatedDict[name].keys():
                self.addAssociatedFeature(str(complex), str(name), complex_uuid, None, None)
                for aggregated_class in associatedDict[name][complex_uuid]:
                    for ogc_fid in associatedDict[name][complex_uuid][aggregated_class]:
                        self.addAssociatedFeature(str(complex), str(name), complex_uuid, str(aggregated_class), ogc_fid)

    def depth(self, item):
        #calculates the depth of the item
        depth = 0
        while item is not None:
            item = item.parent()
            depth += 1
        return depth

    def associateFeatures(self):
        #case no item is selected we should warn the user
        if len(self.treeWidget.selectedItems()) == 0:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("Please, select a complex."))
            return

        item = self.treeWidget.selectedItems()[0]
        #checking if the item is a complex (it should have depth = 2)
        if self.depth(item) != 2:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("Please, select a complex."))
            return

        complex = self.complexCombo.currentText()

        #uuid to be adjust on the selected features
        uuid = item.text(1)

        #getting the selected features
        forbiddenLayers = []
        self.layers = self.iface.mapCanvas().layers()
        for layer in self.layers:
            #case no fetures selected we proceed to the next one
            selectedFeatures = layer.selectedFeatures()
            if len(selectedFeatures) == 0:
                continue

            #obtaining the link column
            column_name = self.abstractDb.obtainLinkColumn(complex, layer.name())

            #storing the names of the incompatible layers
            if column_name == "":
                forbiddenLayers.append(layer.name())
                continue

            for feature in selectedFeatures:
                fieldIndex = [i for i in range(len(layer.dataProvider().fields())) if layer.dataProvider().fields()[i].name() == column_name]
                #feature id that will be updated
                id = feature.id()
                #attribute pair that will be changed
                attrs = {fieldIndex[0]:uuid}
                #actual update in the database
                layer.dataProvider().changeAttributeValues({id:attrs})

        #show the message of incompatible classes to associate
        if len(forbiddenLayers) > 0:
            message = ""
            message += self.tr("The following layers cannot be associated to complexes from ")+self.complexCombo.currentText()+":\n"
            for text in forbiddenLayers:
                message += text+"\n"
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), message)

        #updating the tree widget
        self.loadAssociatedFeatures()

    def createTreeItem(self, parent, text, uuid = ""):
        count = parent.childCount()
        children = []
        #making a list of item names
        for i in range(count):
            child = parent.child(i)
            children.append(child.text(0))

        #checking if the text is already in the tree widget
        if text not in children:
            #case not it should be created
            item = QTreeWidgetItem(parent)
            item.setExpanded(True)
            item.setText(0,text)
            #adding the complex uuid to the tree widget
            if uuid != "":
                item.setText(1, str(uuid))
        else:
            #case already exists the correspondind item should be returned
            for i in range(count):
                child = parent.child(i)
                if child.text(0) == text:
                    item = child
        return item

    def addAssociatedFeature(self, className, complexName, complexId, associatedClass, associatedId):
        #get the corresponding top level item
        classNameItem = self.createTreeItem(self.treeWidget.invisibleRootItem(), className)
        #get the corresponding complex item
        complexNameItem = self.createTreeItem(classNameItem, complexName, complexId)
        if associatedClass and associatedId:
            #get the corresponding class item
            associatedClassItem = self.createTreeItem(complexNameItem, associatedClass)
            #creates the corresponding associated item
            self.createTreeItem(associatedClassItem, str(associatedId))

    def __test(self, x):
        if (x.parent() == None) :
            return True
        else:
            return False
Beispiel #13
0
class PostgisDbThread(GenericThread):
    def __init__(self, parent = None):
        """
        Constructor.
        """
        super(PostgisDbThread, self).__init__()

        self.factory = SqlGeneratorFactory()
        #setting the sql generator
        self.gen = self.factory.createSqlGenerator(False)
        self.messenger = PostgisDbMessages(self)
        self.dbFactory = DbFactory()
        self.parent = parent

    def setParameters(self, abstractDb, dbName, version, epsg, stopped):
        """
        Sets thread parameters
        """
        self.abstractDb = abstractDb #database = postgis
        self.dbName = dbName
        self.db = None  
        self.version = version
        self.epsg = epsg
        self.stopped = stopped

    def run(self):
        """
        Runs the process
        """
        # Processing ending
        (ret, msg) = self.createDatabaseStructure()
        self.signals.processingFinished.emit(ret, msg, self.getId())

    def connectToTemplate(self, setInnerDb = True):
        """
        Connects to the template database to speed up database creation
        :return:
        """
        database = self.abstractDb.getTemplateName(self.version)
        host = self.abstractDb.db.hostName()
        port = self.abstractDb.db.port()
        user = self.abstractDb.db.userName()
        password = self.abstractDb.db.password()
        template = self.dbFactory.createDbFactory('QPSQL')
        template.connectDatabaseWithParameters(host, port, database, user, password)
        template.checkAndOpenDb()
        if setInnerDb:
            self.db = template.db
        return template

    def createDatabaseStructure(self):
        """
        Creates database structure according to the selected edgv version
        """
        currentPath = os.path.dirname(__file__)
        currentPath = os.path.join(currentPath, '..', '..', 'DbTools', 'PostGISTool')
        if self.version == '2.1.3':
            edgvPath = os.path.join(currentPath, 'sqls', '213', 'edgv213.sql')
        elif self.version == '3.0':
            edgvPath = os.path.join(currentPath, 'sqls', '3', 'edgv3.sql')
        elif self.version == 'FTer_2a_Ed':
            edgvPath = os.path.join(currentPath, 'sqls', 'FTer_2a_Ed', 'edgvFter_2a_Ed.sql')
        else:
            pass
        return self.loadDatabaseStructure(edgvPath)

    def loadDatabaseStructure(self, edgvPath):
        """
        Loads the database structure
        edgvPath: path to the databse sql
        """
        commands = []
        hasTemplate = self.abstractDb.checkTemplate(self.version)
        if hasTemplate:
            templateDb = self.connectToTemplate(setInnerDb = False)
            mustUpdateTemplate = templateDb.checkTemplateImplementationVersion()
            if mustUpdateTemplate:
                templateName = templateDb.db.databaseName()
                templateDb.__del__()
                self.abstractDb.dropDatabase(templateName, dropTemplate = True)
                hasTemplate = False
        if not hasTemplate:
            file = codecs.open(edgvPath, encoding='utf-8', mode="r")
            sql = file.read()
            sql = sql.replace('[epsg]', '4674')
            file.close()
            commands = [i for i in sql.split('#') if i != '']
        # Progress bar steps calculated
        self.signals.rangeCalculated.emit(len(commands)+4, self.getId())
        
        if not hasTemplate:
            try:
                self.abstractDb.createTemplateDatabase(self.version)
                self.signals.stepProcessed.emit(self.getId())
                self.connectToTemplate()
                self.signals.stepProcessed.emit(self.getId())
            except Exception as e:
                return (0, self.messenger.getProblemFeedbackMessage()+'\n'+':'.join(e.args))
            self.db.open()
            self.db.transaction()
            query = QSqlQuery(self.db)
    
            for command in commands:
                if not self.stopped[0]:
                    if not query.exec_(command):
                        QgsMessageLog.logMessage(self.messenger.getProblemMessage(command, query), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
                        self.db.rollback()
                        self.db.close()
                        self.dropDatabase(self.db)
                        return (0, self.messenger.getProblemFeedbackMessage())
    
                    # Updating progress
                    self.signals.stepProcessed.emit(self.getId())
                else:
                    self.db.rollback()
                    self.db.close()
                    self.dropDatabase(self.db)                
                    QgsMessageLog.logMessage(self.messenger.getUserCanceledFeedbackMessage(), "DSG Tools Plugin", QgsMessageLog.INFO)
                    return (-1, self.messenger.getUserCanceledFeedbackMessage())
    
            self.db.commit()
            if self.version == '2.1.3':
                sql = 'ALTER DATABASE %s SET search_path = "$user", public, topology,\'cb\',\'complexos\',\'dominios\';' % self.db.databaseName()
            elif self.version == 'FTer_2a_Ed':
                sql = 'ALTER DATABASE %s SET search_path = "$user", public, topology,\'pe\',\'ge\',\'complexos\',\'dominios\';' % self.db.databaseName()
            elif self.version == '3.0':
                sql = 'ALTER DATABASE %s SET search_path = "$user", public, topology,\'edgv\',\'complexos\',\'dominios\';' % self.db.databaseName()
            
            if sql:
                if not query.exec_(sql):
                    QgsMessageLog.logMessage(self.messenger.getProblemMessage(command, query), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
                    return (0, self.messenger.getProblemFeedbackMessage())
            #this commit was missing, so alter database statement was not commited.
            self.db.commit()
            self.db.close()
            self.abstractDb.setDbAsTemplate(self.version)
        #creates from template
        if not self.stopped[0]:
            templateName = self.abstractDb.getTemplateName(self.version)
            self.abstractDb.createDbFromTemplate(self.dbName, templateName, parentWidget = self.parent)
            self.signals.stepProcessed.emit(self.getId())
            #5. alter spatial structure
            createdDb = self.dbFactory.createDbFactory('QPSQL')
            createdDb.connectDatabaseWithParameters(self.abstractDb.db.hostName(), self.abstractDb.db.port(), self.dbName, self.abstractDb.db.userName(), self.abstractDb.db.password())
            errorTuple = createdDb.updateDbSRID(self.epsg, parentWidget = self.parent, threading = True)
            # if an error occur during the thread we should pass the message to the main thread
            if errorTuple:
                QgsMessageLog.logMessage(self.messenger.getProblemMessage(errorTuple[0], errorTuple[1]), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
                return (0, self.messenger.getProblemFeedbackMessage())                
            self.signals.stepProcessed.emit(self.getId())
        else:
            QgsMessageLog.logMessage(self.messenger.getUserCanceledFeedbackMessage(), "DSG Tools Plugin", QgsMessageLog.INFO)
            return (-1, self.messenger.getUserCanceledFeedbackMessage())
        QgsMessageLog.logMessage(self.messenger.getSuccessFeedbackMessage(), "DSG Tools Plugin", QgsMessageLog.INFO)
        return (1, self.messenger.getSuccessFeedbackMessage())

    def dropDatabase(self,db):
        """
        Drops the created database case a problem occurs during database creation
        db: QSqlDatabase to be dropped
        """
        host = db.hostName()
        port = db.port()
        user = db.userName()
        password = db.password()
        database = 'postgres'
        pgDB = QSqlDatabase('QPSQL')
        pgDB.setHostName(host)
        pgDB.setPort(port)
        pgDB.setUserName(user)
        pgDB.setPassword(password)
        pgDB.setDatabaseName(database)
        if not pgDB.open():
            return False
        sql = self.gen.dropDatabase(db.databaseName())
        query = QSqlQuery(pgDB)
        return query.exec_(sql)
Beispiel #14
0
class ConnectionComboBox(DsgCustomComboBox):
    connectionChanged = pyqtSignal()
    dbChanged = pyqtSignal(AbstractDb)
    problemOccurred = pyqtSignal(str)
    def __init__(self, parent=None):
        super(ConnectionComboBox, self).__init__(parent)
        self.parent = parent
        self.abstractDb = None
        self.abstractDbFactory = DbFactory()
        self.serverAbstractDb = None
        self.displayDict = {'2.1.3':'EDGV 2.1.3', 'FTer_2a_Ed':'EDGV FTer 2a Ed', 'Non_EDGV':self.tr('Other database model')}
        self.lineEdit().setPlaceholderText(self.tr('Select a database'))
        self.currentIndexChanged.connect(self.loadDatabase)
        self.instantiateAbstractDb = False
    
    def closeDatabase(self):
        try:
            self.abstractDb.db.close()
            del self.abstractDb
            self.abstractDb = None
        except:
            self.abstractDb = None

    def clear(self):
        super(ConnectionComboBox, self).clear()
        self.closeDatabase()
    
    def setServerDb(self, serverAbstractDb):
        self.serverAbstractDb = serverAbstractDb
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            if self.serverAbstractDb:
                dbList = self.serverAbstractDb.getEDGVDbsFromServer(parentWidget = self.parent)
                dbList.sort()
                self.clear()
                self.addItem(self.tr('Select Database'))
                self.addItems(dbList)
            else:
                self.clear()
                self.abstractDb = None
                return
        except Exception as e:
            QMessageBox.critical(self, self.tr('Critical!'), ':'.join(e.args))
        QApplication.restoreOverrideCursor()
    
    def addItems(self, items):
        itemList = []
        if items == []:
            return
        elif isinstance(items[0], tuple) and len(items[0]) == 2:
            for item in items:
                if item[1] not in self.displayDict.keys():
                    version = item[1]
                else:
                    version = self.displayDict[item[1]]
                newText = item[0] + ' ({0})'.format(version)
                itemList.append(newText)
        if itemList == []:
            itemList = items
        super(ConnectionComboBox, self).addItems(itemList)
    
    def currentDb(self):
        if self.currentIndex() == 0:
            return None
        else:
            return self.currentText().split(' (')[0]
                
    def loadDatabase(self):
        """
        Loads the selected database
        """
        try:
            if self.serverAbstractDb and self.currentIndex() > 0:
                if not self.instantiateAbstractDb:
                    self.abstractDb = self.abstractDbFactory.createDbFactory('QPSQL')
                    (host, port, user, password) = self.serverAbstractDb.getDatabaseParameters()
                    dbName = self.currentText().split(' (')[0]
                    self.abstractDb.connectDatabaseWithParameters(host, port, dbName, user, password)
                    self.abstractDb.checkAndOpenDb()
                    self.dbChanged.emit(self.abstractDb)
        except Exception as e:
            self.closeDatabase()
            self.problemOccurred.emit(self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)   
Beispiel #15
0
class CreateProfile(QtGui.QDialog, FORM_CLASS):
    profileCreated = pyqtSignal(str)

    def __init__(self, parent=None):
        """
        Constructor
        """
        super(CreateProfile, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        self.folder = os.path.join(os.path.dirname(__file__), 'profiles')

        self.abstractDb = None
        self.abstractDbFactory = DbFactory()

        self.populateTreeDict()

    def __del__(self):
        """
        Destructor
        """
        if self.abstractDb:
            del self.abstractDb
            self.abstractDb = None

    def getDbInfo(self):
        """
        Gets database info. This info is used to create a profile model that will be adjusted by the user
        """
        currentPath = os.path.dirname(__file__)
        if self.versionCombo.currentText() == '2.1.3':
            edgvPath = os.path.join(currentPath, '..', 'DbTools',
                                    'SpatialiteTool', 'template', '213',
                                    'seed_edgv213.sqlite')
        elif self.versionCombo.currentText() == 'FTer_2a_Ed':
            edgvPath = os.path.join(currentPath, '..', 'DbTools',
                                    'SpatialiteTool', 'template', 'FTer_2a_Ed',
                                    'seed_edgvfter_2a_ed.sqlite')

        self.abstractDb = self.abstractDbFactory.createDbFactory('QSQLITE')
        if not self.abstractDb:
            QtGui.QMessageBox.warning(
                self, self.tr('Warning!'),
                self.tr('A problem occurred! Check log for details.'))
            return
        self.abstractDb.connectDatabase(edgvPath)

        try:
            self.abstractDb.checkAndOpenDb()
        except Exception as e:
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), 'DSG Tools Plugin',
                                     QgsMessageLog.CRITICAL)

    def populateTreeDict(self):
        """
        Makes a tree widget were the user can define profile properties
        """
        self.getDbInfo()

        tables = []
        try:
            tables = self.abstractDb.getTablesFromDatabase()
        except Exception as e:
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), 'DSG Tools Plugin',
                                     QgsMessageLog.CRITICAL)

        self.profile = dict()
        categories = dict()
        for tableName in tables:
            #proceed only for edgv tables
            if tableName.split("_")[-1] == "p" or tableName.split(
                    "_")[-1] == "l" or tableName.split(
                        "_")[-1] == "a" or tableName.split(
                            "_")[0] == 'complexos' or tableName.split(
                                "_")[0] == 'dominios':
                layerName = tableName.split('_')[0] + '.' + '_'.join(
                    tableName.split('_')[1::])
                split = tableName.split('_')

                if len(split) < 2:
                    continue

                schema = split[0]
                category = split[1]
                if schema not in categories.keys():
                    categories[schema] = dict()

                if category not in categories[schema].keys():
                    categories[schema][category] = dict()

                if layerName not in categories[schema][category]:
                    categories[schema][category][layerName] = dict()
                    categories[schema][category][layerName]['read'] = '0'
                    categories[schema][category][layerName]['write'] = '0'

        self.profile['database' + '_' +
                     self.versionCombo.currentText()] = categories

    @pyqtSlot()
    def on_buttonBox_accepted(self):
        """
        Creates the profile file
        """
        if not self.lineEdit.text():
            QtGui.QMessageBox.warning(self, self.tr('Warning!'),
                                      self.tr('Fill the profile name!'))
            return
        else:
            profileName = self.lineEdit.text()

        path = os.path.join(self.folder, profileName + '.json')

        with open(path, 'w') as outfile:
            json.dump(self.profile, outfile, sort_keys=True, indent=4)
        self.profileCreated.emit(profileName)

    @pyqtSlot(int)
    def on_versionCombo_currentIndexChanged(self):
        """
        Changes the edgv version and updates the tree widget
        """
        self.populateTreeDict()