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 databaseExists(self): """ Checks if database exists. """ _, host, port, user, password = self.viewServers.getDefaultConnectionParameters( ) database = self.currentDb() # get a PostGIS database instance to check if database exists abstractDb = DbFactory().createDbFactory(DsgEnums.DriverPostGIS) return abstractDb.testCredentials(host, port, database, user, password)
def connectToShapefile(self, parameters): """ Stablishes connection to a Shapefile dataset. :param parameters: (dict) a dict containing all connection parameters. :return: (AbstractDb) returns the DSGTools database object. """ abstractDb = DbFactory().createDbFactory( driver=DsgEnums.DriverShapefile) abstractDb.connectDatabase(conn=parameters['path']) return abstractDb if abstractDb.getDatabaseName() != '' else None
def instantiateAbstractDb(self, dbName): """ Instantiates an abstractDb. """ if dbName not in list(self.dbDict.keys()): (host, port, user, password) = self.serverAbstractDb.getParamsFromConectedDb() abstractDb = DbFactory().createDbFactory(DsgEnums.DriverPostGIS) abstractDb.connectDatabaseWithParameters(host, port, dbName, user, password) else: abstractDb = self.dbDict[dbName] return abstractDb
def connectToSpatialite(self, path): """ Stablishes connection to a SpatiaLite database. :param path: (str) path to the SpatiaLite database. # :return: (QSqlDatabase) the database object. :return: (AbstracDb) DSGTools database object. """ db = None if os.path.exists(path): db = DbFactory().createDbFactory(driver=DsgEnums.DriverSpatiaLite) db.connectDatabase(conn=path) return db
def getDefaultPgDb(self, hostName): """ Gets a standard PostGIS database object from a given host. :param hostName: (str) host name. :return: (AbstractDb) PostGIS database object. """ abstractDb = DbFactory().createDbFactory(driver=DsgEnums.DriverPostGIS) (host, port, user, password) = abstractDb.getServerConfiguration(hostName) abstractDb.connectDatabaseWithParameters(host, port, 'postgres', user, password) return abstractDb
def connectToGeopackage(self, parameters): """ Stablishes connection to a Geopackage database. :param parameters: (dict) a dict containing all connection parameters. :return: (AbstractDb) returns the DSGTools database object. """ abstractDb = None if os.path.exists(parameters['path']): abstractDb = DbFactory().createDbFactory( driver=DsgEnums.DriverGeopackage) abstractDb.connectDatabase(conn=parameters['path']) return abstractDb
def connectToPostgis(self, parameters): """ Stablishes connection to a Postgis database. :param parameters: (dict) a dict containing all connection parameters. :return: (AbstractDb) returns the DSGTools database object. """ user, host, port, db = parameters['username'], parameters[ 'host'], parameters['port'], parameters['db'] # initiate abstractDb abstractDb = DbFactory().createDbFactory(driver=DsgEnums.DriverPostGIS) # ignore all info except for the password password = self.userPasswordFromHost(hostname=host, username=user) return abstractDb if abstractDb.testCredentials( host, port, db, user, password) else None
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 __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 __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 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 __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 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)
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' }
def __init__(self, permissionManager, abstractDb, parent = None): """ Constructor """ super(CreateProfileWithProfileManager, 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.permissionManager = permissionManager self.abstractDb = abstractDb self.abstractDbFactory = DbFactory() self.populateTreeDict()
def instantiateAdminDb(self, serverAbstractDb): """ Instantiates dsgtools_admindb in the same server as serverAbstractDb. If dsgtools_admindb does not exists, instantiateAdminDb calls createAdminDb """ (host, port, user, password) = serverAbstractDb.getParamsFromConectedDb() adminDb = DbFactory().createDbFactory(DsgEnums.DriverPostGIS) if not serverAbstractDb.hasAdminDb(): return self.createAdminDb(serverAbstractDb, adminDb, host, port, user, password) adminDb.connectDatabaseWithParameters(host, port, 'dsgtools_admindb', user, password) managerType = self.getManagerType() if not adminDb.checkIfExistsConfigTable(managerType): adminDb.createPropertyTable(managerType, isAdminDb = True) return adminDb
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)
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()
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
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()
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)
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_()
def getAbstractDb(self, host, port, database, user, password): abstractDb = DbFactory().createDbFactory(DsgEnums.DriverPostGIS) abstractDb.connectDatabaseWithParameters(host, port, database, user, password) return abstractDb
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