예제 #1
0
class ComplexWindow(QtWidgets.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(DsgEnums.DriverSpatiaLite)
            self.abstractDb.connectDatabase(dataSourceUri.database())
        else:
            self.abstractDb = self.abstractDbFactory.createDbFactory(DsgEnums.DriverPostGIS)
            
            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], 'DSGTools Plugin', Qgis.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 list(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
        dlg.tableUpdated.connect(self.loadAssociatedFeatures)
        #connects a signal to disassociate features from complex before removal
        dlg.markedToRemove.connect(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 = next(layer.getFeatures( freq ))
                    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), 'DSGTools Plugin', Qgis.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), 'DSGTools Plugin', Qgis.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), 'DSGTools Plugin', Qgis.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), 'DSGTools Plugin', Qgis.Critical)
            
        for name in list(associatedDict.keys()):
            for complex_uuid in list(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), 'DSGTools Plugin', Qgis.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
예제 #2
0
class DatabaseFileLineEdit(QtWidgets.QWidget, FORM_CLASS):
    connectionChanged = pyqtSignal()
    dbChanged = pyqtSignal(AbstractDb)
    problemOccurred = pyqtSignal(str)

    def __init__(self, parent=None):
        """
        Class constructor.
        :param: (QWidget) widget parent to new DatabaseFileLineEdit instance.
        """
        super(DatabaseFileLineEdit, self).__init__(parent)
        self.setupUi(self)
        self.parent = parent
        self.driver = DsgEnums.NoDriver
        self.abstractDb = None
        self.abstractDbFactory = DbFactory()
        self.serverAbstractDb = None
        self.displayDict = {'2.1.3':'EDGV 2.1.3', '2.1.3 Pro':'EDGV 2.1.3 Pro', 'FTer_2a_Ed':'EDGV FTer 2a Ed', 'Non_EDGV':self.tr('Other database model'), '3.0':'EDGV 3.0'}
        self.instantiateAbstractDb = False
        self.connectionSelectorLineEdit.lineEdit.setText(self.tr('Select datasource'))
        self.connectionSelectorLineEdit.lineEdit.setReadOnly(True)

    def closeDatabase(self):
        """
        Unsets any selected database.
        """
        if self.abstractDb is not None:
            self.abstractDb.closeDatabase()

    def clear(self):
        """
        Unsets any selected database and clears db directory, if necessary.
        """
        self.connectionSelectorLineEdit.lineEdit.clear()
        self.connectionSelectorLineEdit.lineEdit.setText(self.tr('Select datasource'))
        self.closeDatabase()
    
    def currentDb(self):
        """
        Returns current loaded datasource name, if any.
        :return: (str) current loaded datasource name; an empty string if no ds is selected.
        """
        text = self.connectionSelectorLineEdit.lineEdit.text()
        if text == self.tr('Select datasource'):
            return None
        else:
            dirSplit = '/' if '/' in text else '\\'
            text = text.split(dirSplit)[-1].split('.')[0] if text else ''
            return text

    def serverIsValid(self):
        """
        Checks if connection to server is valid.
        """
        # for files, server check is not necessary
        return True

    def databaseExists(self):
        """
        Checks if database exists.
        """
        # for files, it is only necessary to check if file exists and is not empty.
        return bool(self.abstractDb)

    @pyqtSlot(str, name = 'on_lineEdit_textChanged')
    def loadDatabase(self, currentText):
        """
        Loads the selected database
        """
        try:
            if not self.currentDb():
                # in case no datasource was selected
                self.closeDatabase()
            elif not self.instantiateAbstractDb:
                self.abstractDb = self.abstractDbFactory.createDbFactory(self.driver)
                self.abstractDb.connectDatabase(conn=currentText)
                self.abstractDb.checkAndOpenDb()
                self.dbChanged.emit(self.abstractDb)
                self.connectionChanged.emit()
        except Exception as e:
            self.closeDatabase()
            self.problemOccurred.emit(self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSGTools Plugin", Qgis.Critical)

    def validate(self):
        """
        Validates current widget. To be validated, it is necessary:
        - a valid datasource selection; and
        - a valid database structure.
        :return: (str) invalidation reason.
        """
        # check a valid server name
        # check if datasource is a valid name and if it already exists into selected server
        if not self.currentDb() or not self.abstractDb:
            return self.tr('Invalid datasource.')
        else:
            # check if the connection is a valid connection
            if not self.serverIsValid():
                return self.tr('Invalid connection to server.')
            # check if it exists
            if not self.databaseExists():
                return self.tr('Database {0} does not exist.').format(self.currentDb())
        # if all tests were positive, widget has a valid selection
        return ''

    def isValid(self):
        """
        Validates selection.
        :return: (bool) validation status.
        """
        return self.validate() == ''

    @pyqtSlot(bool)
    def on_infoPushButton_clicked(self):
        """
        Exhibits information about selected database.
        """
        contents = self.abstractDb.databaseInfo() if self.abstractDb else []
        DatasourceInfoTable(contents=contents).exec_()
예제 #3
0
class BatchDbManager(QtWidgets.QDialog, FORM_CLASS):
    EDGV213, EDGV_FTer_2a_Ed, Non_EDGV = list(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': [],
            '2.1.3 Pro': [],
            'FTer_2a_Ed': [],
            'Non_EDGV': [],
            '3.0': [],
            '3.0 Pro': []
        }
        self.correspondenceDict = {
            self.tr('Load Database Model EDGV Version 2.1.3'): '2.1.3',
            self.tr('Load Database Model EDGV Version 2.1.3 Pro'): '2.1.3 Pro',
            self.tr('Load Database Model EDGV Version 3.0'): '3.0',
            self.tr('Load Database Model EDGV Version 3.0 Pro'): '3.0 Pro',
            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 list(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()
        selectedDbNameList = list(
            set(selectedDbNameList + [
                'template_edgv_213', 'template_edgv_fter_2a_ed',
                'template_edgv_3', 'dsgtools_admindb'
            ])) if instantiateTemplates else selectedDbNameList
        for dbName in selectedDbNameList:
            localDb = self.dbFactory.createDbFactory(DsgEnums.DriverPostGIS)
            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 list(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)
        if exceptionDict != []:
            msg += self.logInternalError(exceptionDict)
        if successList != [] and exceptionDict != []:
            QMessageBox.warning(self, self.tr('Operation Complete!'), msg)

    def logInternalError(self, exceptionDict):
        msg = ''
        errorDbList = list(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:
                msg = self.tr("Error for database {0}: ").format(
                    errorDb, exceptionDict[errorDb])
                QgsMessageLog.logMessage(msg, "DSGTools Plugin", Qgis.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!'))
            return
        if QMessageBox.question(
                self, self.tr('Question'),
                self.tr('Do you really want to drop databases: ') +
                ', '.join(selectedDbNameList),
                QMessageBox.Ok | QMessageBox.Cancel) == 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 = []
        if QMessageBox.question(
                self, self.tr('Question'),
                self.
                tr('This operation will upgrade PostGIS version for templates databases as well as the selected databases. Would you like to continue?'
                   ),
                QMessageBox.Ok | QMessageBox.Cancel) == QMessageBox.Cancel:
            return successList, exceptionDict
        dbsDict = self.instantiateAbstractDbs(instantiateTemplates=True)
        self.closeAbstractDbs(dbsDict)
        for dbName in dbsDict:
            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 = []
        if dbsDict != {}:
            for dbName in list(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(list(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 len(selectedStyles) == 0:
                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)
        styleWalkList = list(os.walk(styleDir))
        if styleWalkList == []:
            return []
        stylePath, styles, files = styleWalkList[0]
        for style in styles:
            if style == []:
                continue
            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 list(dbsDict.keys()):
            for style in styleList:
                try:
                    dbsDict[dbName].importStylesIntoDb(style)
                    successList.append(dbName)
                except Exception as e:
                    errors = []
                    for arg in e.args:
                        if isinstance(arg, str):
                            s = '{}'.format(arg.encode('utf-8'))
                        else:
                            s = str(arg)
                        errors.append(s)
                    exceptionDict[dbName] = ':'.join(errors)
        return successList, exceptionDict

    def getStyleDir(self, versionList):
        if versionList != [] and versionList[
                0] in self.serverWidget.abstractDb.versionFolderDict:
            return os.path.join(
                os.path.dirname(__file__), '..', '..', 'core', 'Styles',
                self.serverWidget.abstractDb.versionFolderDict[versionList[0]])
        elif versionList != []:
            return os.path.join(os.path.dirname(__file__), '..', '..', 'core',
                                'Styles', "Non_EDGV")
        return ""

    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 list(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(list(exceptionDict.keys())) > 0:
            self.logInternalError(exceptionDict)
        return allStylesDict

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

    def populateStylesInterface(self):
        self.stylesTreeWidget.clear()
        allStylesDict = self.getStylesFromDbs()
        rootNode = self.stylesTreeWidget.invisibleRootItem()
        for styleName in list(allStylesDict.keys()):
            parentStyleItem = self.createItem(rootNode, styleName, 0)
            dbList = list(allStylesDict[styleName].keys())
            parentTimeList = []
            for dbName in dbList:
                dbItem = self.createItem(parentStyleItem, dbName, 1)
                tableList = list(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 = list(styleDict.keys())
        dlg = SelectStyles(styleList)
        execStatus = dlg.exec_()
        selectedStyles = dlg.selectedStyles
        if execStatus != 0 and selectedStyles != []:
            selectedStyleDict = {k: styleDict[k] for k in selectedStyles}
            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
            successList, exceptionDict = self.batchDeleteStyles(
                dbsDict, selectedStyleDict)
            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 list(styleDict.keys()):
            for dbName in list(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 list(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)
예제 #4
0
class CreateProfile(QtWidgets.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(
            DsgEnums.DriverSpatiaLite)
        if not self.abstractDb:
            QtWidgets.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:
            QtWidgets.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), 'DSGTools Plugin',
                                     Qgis.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:
            QtWidgets.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), 'DSGTools Plugin',
                                     Qgis.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 list(categories.keys()):
                    categories[schema] = dict()

                if category not in list(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():
            QtWidgets.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()
예제 #5
0
class ConnectionWidget(QtWidgets.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(
                    DsgEnums.DriverSpatiaLite)
                self.abstractDb.connectDatabase()
                self.spatialiteFileEdit.setText(
                    self.abstractDb.db.databaseName())
                self.edgvSpatialiteVersionEdit.setText(
                    self.abstractDb.getDatabaseVersion())

            else:
                self.abstractDb = self.abstractDbFactory.createDbFactory(
                    DsgEnums.DriverPostGIS)
                (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), "DSGTools Plugin",
                                     Qgis.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), "DSGTools Plugin",
                                     Qgis.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), "DSGTools Plugin",
                                     Qgis.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), "DSGTools Plugin",
                                     Qgis.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()
                QApplication.restoreOverrideCursor()
                return
        except Exception as e:
            QApplication.restoreOverrideCursor()
            QMessageBox.critical(self, self.tr('Critical!'), ':'.join(e.args))
            self.setInitialState()
        QApplication.restoreOverrideCursor()
예제 #6
0
class ExploreDb(QtWidgets.QDialog, FORM_CLASS):
    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.utils = Utils()
        self.dbFactory = DbFactory()
        self.localDb = None
        self.serverWidget.populateServersCombo()
        #signal connections
        self.serverWidget.abstractDbLoaded.connect(self.checkSuperUser)
        self.serverWidget.clearWidgets.connect(self.clearAll)
        self.treeWidget.setContextMenuPolicy(Qt.CustomContextMenu)
        self.treeWidget.customContextMenuRequested.connect(
            self.createMenuAssigned)

    def populateListWithDatabasesFromServer(self):
        '''
        Populates databases list from server
        '''
        dbList = []
        try:
            dbList = self.serverWidget.abstractDb.getEDGVDbsFromServer()
        except Exception as e:
            QMessageBox.critical(self, self.tr('Critical!'), ':'.join(e.args))

        dbList.sort()
        for (dbname, dbversion) in dbList:
            item = QListWidgetItem(self.dbListWidget)
            item.setText(dbname + ' (EDGV v. ' + dbversion + ')')
            item.setData(Qt.UserRole, dbname)

    @pyqtSlot(bool)
    def on_closePushButton_clicked(self):
        '''
        Closes the dialog
        '''
        self.done(0)

    def renewDb(self):
        '''
        Renews the database
        '''
        if self.localDb:
            del self.localDb
            self.localDb = None

    def clearAll(self):
        '''
        Clears the database list
        '''
        self.dbListWidget.clear()
        self.treeWidget.clear()
        self.renewDb()

    def checkSuperUser(self):
        '''
        Checks if the user is a super user
        '''
        try:
            if self.serverWidget.abstractDb.checkSuperUser():
                self.populateListWithDatabasesFromServer()
            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 createItem(self, parent, text, column):
        '''
        Creates a tree widget item
        '''
        item = QtWidgets.QTreeWidgetItem(parent)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(column, text)
        return item

    @pyqtSlot(QListWidgetItem, QListWidgetItem)
    def on_dbListWidget_currentItemChanged(self, current, previous):
        '''
        Updates the information related with the database (e.g. users and roles for instance)
        '''
        self.treeWidget.clear()
        if not current:
            return
        self.localDb = self.dbFactory.createDbFactory(DsgEnums.DriverPostGIS)
        originalCon = self.serverWidget.abstractDb.makeOgrConn()
        self.localDb.connectDatabaseWithParameters(
            self.serverWidget.abstractDb.db.hostName(),
            self.serverWidget.abstractDb.db.port(),
            current.text().split(' ')[0],
            self.serverWidget.abstractDb.db.userName(),
            self.serverWidget.abstractDb.db.password())

        candidateUserList = []
        try:
            candidateUserList = self.localDb.getUsers()
        except Exception as e:
            QMessageBox.critical(self, self.tr('Critical!'), ':'.join(e.args))

        for candidate in candidateUserList:
            installed, assigned = self.localDb.getUserRelatedRoles(candidate)
            if len(assigned) > 0:
                userItem = self.createItem(self.treeWidget.invisibleRootItem(),
                                           candidate, 0)
                for perm in assigned:
                    self.createItem(userItem, perm, 1)

    def createMenuAssigned(self, position):
        '''
        Creates a pop up menu
        '''
        menu = QMenu()
        item = self.treeWidget.itemAt(position)
        if item:
            menu.addAction(self.tr('Show properties'),
                           self.showAssignedProperties)
        menu.exec_(self.treeWidget.viewport().mapToGlobal(position))

    def showAssignedProperties(self):
        '''
        Shows information about the selected permissions model 
        '''
        permission = self.treeWidget.currentItem().text(1)
        dbname = self.dbListWidget.currentItem().text().split(' ')[0]

        permissionsDict = dict()
        try:
            permissionsDict = self.localDb.getRolePrivileges(
                permission, dbname)
        except Exception as e:
            QMessageBox.critical(self, self.tr('Critical!'), ':'.join(e.args))

        dlg = PermissionProperties(permissionsDict)
        dlg.exec_()

    @pyqtSlot(bool)
    def on_dropDatabasePushButton_clicked(self):
        '''
        Drops a database and updates QSettings
        '''
        currentItem = self.dbListWidget.currentItem()
        if not currentItem:
            return
        if QMessageBox.question(
                self, self.tr('Question'),
                self.tr('Do you really want to drop database: ') +
                currentItem.text().split(' ')[0],
                QMessageBox.Ok | QMessageBox.Cancel) == QMessageBox.Cancel:
            return
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        localDbName = self.localDb.getDatabaseName()
        self.renewDb()
        try:
            self.serverWidget.abstractDb.dropDatabase(localDbName)
            QApplication.restoreOverrideCursor()
            QMessageBox.warning(
                self, self.tr('Success!'),
                self.tr('Database ') + localDbName +
                self.tr(' dropped successfully!'))
            self.clearQSettings(localDbName)
        except Exception as e:
            QApplication.restoreOverrideCursor()
            QMessageBox.critical(self, self.tr('Critical!'), ':'.join(e.args))
        self.clearAll()
        self.populateListWithDatabasesFromServer()

    @pyqtSlot(bool)
    def on_createViewsPushButton_clicked(self):
        '''
        Creates view button
        '''
        if not self.localDb:
            QMessageBox.critical(self, self.tr('Critical!'),
                                 self.tr('Select a database to create view'))
            return
        dlg = CreateView(self.localDb, self.dbListWidget.currentItem().text())
        dlg.exec_()
        pass

    def clearQSettings(self, database):
        '''
        Clear the database from QSettings
        '''
        name = self.serverWidget.serversCombo.currentText() + '_' + database
        settings = QSettings()
        settings.beginGroup('PostgreSQL/connections/' + name)
        settings.remove('')
        settings.endGroup()

    @pyqtSlot(bool)
    def on_manageAuxStructPushButton_clicked(self):
        '''
        Opens the dialog to manage database auxiliar structure
        '''
        if not self.localDb:
            QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('Select a database to manage auxiliar structure'))
            return
        dlg = ManageDBAuxiliarStructure(self.localDb)
        dlg.exec_()
        pass
예제 #7
0
class ConnectionComboBox(QtWidgets.QWidget, FORM_CLASS):
    connectionChanged = pyqtSignal()
    dbChanged = pyqtSignal(AbstractDb)
    problemOccurred = pyqtSignal(str)

    def __init__(self, parent=None, isStatic=False):
        """
        Class constructor.
        :param parent: (QWidget) widget parent to newly instantiated ConnectionComboBox.
        :param isStatic: (bool) indicates whether server selection will be static (no default).
        """
        super(ConnectionComboBox, self).__init__(parent)
        self.setupUi(self)
        self.parent = parent
        self.abstractDb = None
        self.abstractDbFactory = DbFactory()
        self.serverAbstractDb = None
        self.displayDict = {
            '2.1.3': 'EDGV 2.1.3',
            '2.1.3 Pro': 'EDGV 2.1.3 Pro',
            'FTer_2a_Ed': 'EDGV FTer 2a Ed',
            'Non_EDGV': self.tr('Other database model'),
            '3.0': 'EDGV 3.0'
        }
        self.instantiateAbstractDb = False
        self.isStatic = isStatic
        if self.isStatic:
            from DsgTools.gui.ServerTools.viewServers import ViewServersStatic
            self.viewServers = ViewServersStatic()
        else:
            from DsgTools.gui.ServerTools.viewServers import ViewServers
            self.viewServers = ViewServers()
        self.viewServers.defaultChanged.connect(self.loadServerAbstractDb)
        self.connectionSelectorComboBox.addItem(self.tr('Select database'))
        self.loadServerAbstractDb()

    def __del__(self):
        """
        Destructor
        """
        if self.serverAbstractDb is not None:
            self.serverAbstractDb.closeDatabase()
        del self

    def loadServerAbstractDb(self):
        """
        Checks if there is a default db in self.viewServers . If there isn't one, disables connection combo
        """
        if self.viewServers.connectionParameters() == dict():
            self.connectionSelectorComboBox.setEnabled(False)
        else:
            self.connectionSelectorComboBox.setEnabled(True)
            (_, host, port, user,
             password) = self.viewServers.getDefaultConnectionParameters()
            serverAbstractDb = self.abstractDbFactory.createDbFactory(
                DsgEnums.DriverPostGIS)
            serverAbstractDb.connectDatabaseWithParameters(
                host, port, 'postgres', user, password)
            self.setServerDb(serverAbstractDb)
            serverAbstractDb.closeDatabase()

    def closeDatabase(self):
        if self.abstractDb is not None:
            self.abstractDb.closeDatabase()

    def clear(self):
        self.connectionSelectorComboBox.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, getDatabaseVersions=False)
                dbList.sort()
                self.clear()
                self.connectionSelectorComboBox.addItem(
                    self.tr('Select Database'))
                self.addItems(dbList)
            else:
                self.clear()
                self.abstractDb = None
                QApplication.restoreOverrideCursor()
                return
        except Exception as e:
            QApplication.restoreOverrideCursor()
            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 list(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
        self.connectionSelectorComboBox.addItems(itemList)

    def currentDb(self):
        if self.connectionSelectorComboBox.currentIndex() == 0:
            return ''
        else:
            return self.connectionSelectorComboBox.currentText().split(' (')[0]

    @pyqtSlot(int, name='on_connectionSelectorComboBox_currentIndexChanged')
    def loadDatabase(self, idx):
        """
        Loads the selected database
        """
        try:
            if self.abstractDb is not None:
                self.closeDatabase()
            if self.serverAbstractDb is not None and idx > 0:
                if not self.instantiateAbstractDb:
                    self.abstractDb = self.abstractDbFactory.createDbFactory(
                        DsgEnums.DriverPostGIS)
                    (host, port, user,
                     password) = self.serverAbstractDb.getDatabaseParameters()
                    dbName = self.connectionSelectorComboBox.itemText(
                        idx).split(' (')[0]
                    self.abstractDb.connectDatabaseWithParameters(
                        host, port, dbName, user, password)
                    self.abstractDb.checkAndOpenDb()
                    self.dbChanged.emit(self.abstractDb)
                    self.connectionChanged.emit()
        except Exception as e:
            self.closeDatabase()
            self.problemOccurred.emit(
                self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSGTools Plugin",
                                     Qgis.Critical)

    @pyqtSlot(bool)
    def on_serverPushButton_clicked(self):
        self.viewServers.exec_()

    def serverIsValid(self):
        """
        Checks if connection to server is valid.
        """
        # for files, server check is not necessary
        conn = self.viewServers.getDefaultConnectionParameters()[0]
        return self.viewServers.testServer(conn)

    def databaseExists(self):
        """
        Checks if database exists.
        """
        # for files, it is only necessary to check if file exists and is not empty.
        if self.abstractDb:
            _, host, port, user, password = self.viewServers.getDefaultConnectionParameters(
            )
            database = self.currentDb()
            return self.abstractDb.testCredentials(host, port, database, user,
                                                   password)
        return False

    def validate(self):
        """
        Validates current widget. To be valid, it is necessary:
        - a valid datasource selection; and
        - a valid database structure.
        :return: (str) invalidation reason.
        """
        # check a valid server name
        # check if datasource is a valid name and if it already exists into selected server
        if not self.currentDb() or not self.abstractDb:
            return self.tr('Invalid datasource.')
        else:
            # check if the connection is a valid connection
            if not self.serverIsValid():
                return self.tr('Invalid connection to server.')
            # check if it exists
            if not self.databaseExists():
                return self.tr('Database {0} does not exist.').format(
                    self.currentDb())
        # if all tests were positive, widget has a valid selection
        return ''

    def isValid(self):
        """
        Validates selection.
        :return: (bool) validation status.
        """
        return self.validate() == ''
        # msg = self.validate()
        # if msg:
        #     # if an invalidation reason was given, warn user and nothing else.
        #     iface.messageBar().pushMessage(self.tr('Warning!'), msg, level=Qgis.Warning, duration=5)
        # return msg == ''

    @pyqtSlot(bool)
    def on_infoPushButton_clicked(self):
        """
        Exhibits information about selected database.
        """
        contents = self.abstractDb.databaseInfo() if self.abstractDb else []
        DatasourceInfoTable(contents=contents).exec_()

    def setHost(self, hostname):
        """
        Sets a hostname as selected from its name.
        :param serverName: (str) host to be set as selected.
        """
        # method needs to be ported to 'non-static' view server!
        self.viewServers.setHost(hostname)